browser_clipboard.js (8634B)
1 // This test is used to check copy and paste in editable areas to ensure that non-text 2 // types (html and images) are copied to and pasted from the clipboard properly. 3 4 var testPage = 5 "<body style='margin: 0'>" + 6 " <img id='img' tabindex='1' src='http://example.org/browser/browser/base/content/test/general/moz.png'>" + 7 " <div id='main' contenteditable='true'>Test <b>Bold</b> After Text</div>" + 8 "</body>"; 9 10 add_task(async function () { 11 let tab = BrowserTestUtils.addTab(gBrowser); 12 let browser = gBrowser.getBrowserForTab(tab); 13 14 gBrowser.selectedTab = tab; 15 16 await BrowserTestUtils.loadURIString({ 17 browser: tab.linkedBrowser, 18 uriString: "data:text/html," + escape(testPage), 19 }); 20 await SimpleTest.promiseFocus(browser); 21 22 function sendKey(key, code) { 23 return BrowserTestUtils.synthesizeKey( 24 key, 25 { code, accelKey: true }, 26 browser 27 ); 28 } 29 30 // On windows, HTML clipboard includes extra data. 31 // The values are from widget/windows/nsDataObj.cpp. 32 const htmlPrefix = navigator.platform.includes("Win") 33 ? "<html><body>\n<!--StartFragment-->" 34 : ""; 35 const htmlPostfix = navigator.platform.includes("Win") 36 ? "<!--EndFragment-->\n</body>\n</html>" 37 : ""; 38 39 await SpecialPowers.spawn(browser, [], () => { 40 var doc = content.document; 41 var main = doc.getElementById("main"); 42 main.focus(); 43 44 // Select an area of the text. 45 let selection = doc.getSelection(); 46 selection.modify("move", "left", "line"); 47 selection.modify("move", "right", "character"); 48 selection.modify("move", "right", "character"); 49 selection.modify("move", "right", "character"); 50 selection.modify("extend", "right", "word"); 51 selection.modify("extend", "right", "word"); 52 }); 53 54 // The data is empty as the selection was copied during the event default phase. 55 let copyEventPromise = BrowserTestUtils.waitForContentEvent( 56 browser, 57 "copy", 58 false, 59 event => { 60 return event.clipboardData.mozItemCount == 0; 61 } 62 ); 63 await SpecialPowers.spawn(browser, [], () => {}); 64 await sendKey("c"); 65 await copyEventPromise; 66 67 let pastePromise = SpecialPowers.spawn( 68 browser, 69 [htmlPrefix, htmlPostfix], 70 (htmlPrefixChild, htmlPostfixChild) => { 71 let selection = content.document.getSelection(); 72 selection.modify("move", "right", "line"); 73 74 return new Promise(resolve => { 75 content.addEventListener( 76 "paste", 77 event => { 78 let clipboardData = event.clipboardData; 79 Assert.equal( 80 clipboardData.mozItemCount, 81 1, 82 "One item on clipboard" 83 ); 84 Assert.equal( 85 clipboardData.types.length, 86 2, 87 "Two types on clipboard" 88 ); 89 Assert.equal( 90 clipboardData.types[0], 91 "text/html", 92 "text/html on clipboard" 93 ); 94 Assert.equal( 95 clipboardData.types[1], 96 "text/plain", 97 "text/plain on clipboard" 98 ); 99 Assert.equal( 100 clipboardData.getData("text/html"), 101 htmlPrefixChild + "t <b>Bold</b>" + htmlPostfixChild, 102 "text/html value" 103 ); 104 Assert.equal( 105 clipboardData.getData("text/plain"), 106 "t Bold", 107 "text/plain value" 108 ); 109 resolve(); 110 }, 111 { capture: true, once: true } 112 ); 113 }); 114 } 115 ); 116 117 await SpecialPowers.spawn(browser, [], () => {}); 118 119 await sendKey("v"); 120 await pastePromise; 121 122 let copyPromise = SpecialPowers.spawn(browser, [], () => { 123 var main = content.document.getElementById("main"); 124 125 Assert.equal( 126 main.innerHTML, 127 "Test <b>Bold</b> After Textt <b>Bold</b>", 128 "Copy and paste html" 129 ); 130 131 let selection = content.document.getSelection(); 132 selection.modify("extend", "left", "word"); 133 selection.modify("extend", "left", "word"); 134 selection.modify("extend", "left", "character"); 135 136 return new Promise(resolve => { 137 content.addEventListener( 138 "cut", 139 event => { 140 event.clipboardData.setData("text/plain", "Some text"); 141 event.clipboardData.setData("text/html", "<i>Italic</i> "); 142 selection.deleteFromDocument(); 143 event.preventDefault(); 144 resolve(); 145 }, 146 { capture: true, once: true } 147 ); 148 }); 149 }); 150 151 await SpecialPowers.spawn(browser, [], () => {}); 152 153 await sendKey("x"); 154 await copyPromise; 155 156 pastePromise = SpecialPowers.spawn( 157 browser, 158 [htmlPrefix, htmlPostfix], 159 (htmlPrefixChild, htmlPostfixChild) => { 160 let selection = content.document.getSelection(); 161 selection.modify("move", "left", "line"); 162 163 return new Promise(resolve => { 164 content.addEventListener( 165 "paste", 166 event => { 167 let clipboardData = event.clipboardData; 168 Assert.equal( 169 clipboardData.mozItemCount, 170 1, 171 "One item on clipboard 2" 172 ); 173 Assert.equal( 174 clipboardData.types.length, 175 2, 176 "Two types on clipboard 2" 177 ); 178 Assert.equal( 179 clipboardData.types[0], 180 "text/html", 181 "text/html on clipboard 2" 182 ); 183 Assert.equal( 184 clipboardData.types[1], 185 "text/plain", 186 "text/plain on clipboard 2" 187 ); 188 Assert.equal( 189 clipboardData.getData("text/html"), 190 htmlPrefixChild + "<i>Italic</i> " + htmlPostfixChild, 191 "text/html value 2" 192 ); 193 Assert.equal( 194 clipboardData.getData("text/plain"), 195 "Some text", 196 "text/plain value 2" 197 ); 198 resolve(); 199 }, 200 { capture: true, once: true } 201 ); 202 }); 203 } 204 ); 205 206 await SpecialPowers.spawn(browser, [], () => {}); 207 208 await sendKey("v"); 209 await pastePromise; 210 211 await SpecialPowers.spawn(browser, [], () => { 212 var main = content.document.getElementById("main"); 213 Assert.equal( 214 main.innerHTML, 215 "<i>Italic</i> Test <b>Bold</b> After<b></b>", 216 "Copy and paste html 2" 217 ); 218 }); 219 220 // Next, check that the Copy Image command works. 221 222 // The context menu needs to be opened to properly initialize for the copy 223 // image command to run. 224 let contextMenu = document.getElementById("contentAreaContextMenu"); 225 let contextMenuShown = promisePopupShown(contextMenu); 226 BrowserTestUtils.synthesizeMouseAtCenter( 227 "#img", 228 { type: "contextmenu", button: 2 }, 229 gBrowser.selectedBrowser 230 ); 231 await contextMenuShown; 232 233 document.getElementById("context-copyimage-contents").doCommand(); 234 235 contextMenu.hidePopup(); 236 await promisePopupHidden(contextMenu); 237 238 // Focus the content again 239 await SimpleTest.promiseFocus(browser); 240 241 pastePromise = SpecialPowers.spawn( 242 browser, 243 [htmlPrefix, htmlPostfix], 244 (htmlPrefixChild, htmlPostfixChild) => { 245 var doc = content.document; 246 var main = doc.getElementById("main"); 247 main.focus(); 248 249 return new Promise((resolve, reject) => { 250 content.addEventListener( 251 "paste", 252 event => { 253 let clipboardData = event.clipboardData; 254 255 // DataTransfer doesn't support the image types yet, so only text/html 256 // will be present. 257 if ( 258 clipboardData.getData("text/html") !== 259 htmlPrefixChild + 260 '<img id="img" tabindex="1" src="http://example.org/browser/browser/base/content/test/general/moz.png">' + 261 htmlPostfixChild 262 ) { 263 reject( 264 "Clipboard Data did not contain an image, was " + 265 clipboardData.getData("text/html") 266 ); 267 } 268 resolve(); 269 }, 270 { capture: true, once: true } 271 ); 272 }); 273 } 274 ); 275 276 await SpecialPowers.spawn(browser, [], () => {}); 277 await sendKey("v"); 278 await pastePromise; 279 280 // The new content should now include an image. 281 await SpecialPowers.spawn(browser, [], () => { 282 var main = content.document.getElementById("main"); 283 Assert.equal( 284 main.innerHTML, 285 '<i>Italic</i> <img id="img" tabindex="1" ' + 286 'src="http://example.org/browser/browser/base/content/test/general/moz.png">' + 287 "Test <b>Bold</b> After<b></b>", 288 "Paste after copy image" 289 ); 290 }); 291 292 gBrowser.removeCurrentTab(); 293 });