browser_inspector_menu-01-sensitivity.js (10764B)
1 /* Any copyright is dedicated to the Public Domain. 2 http://creativecommons.org/publicdomain/zero/1.0/ */ 3 "use strict"; 4 5 // Test that context menu items are enabled / disabled correctly. 6 7 const TEST_URL = URL_ROOT + "doc_inspector_menu.html"; 8 9 const PASTE_MENU_ITEMS = [ 10 "node-menu-pasteinnerhtml", 11 "node-menu-pasteouterhtml", 12 "node-menu-pastebefore", 13 "node-menu-pasteafter", 14 "node-menu-pastefirstchild", 15 "node-menu-pastelastchild", 16 ]; 17 18 const ACTIVE_ON_DOCTYPE_ITEMS = [ 19 "node-menu-showdomproperties", 20 "node-menu-useinconsole", 21 ]; 22 23 const ACTIVE_ON_SHADOW_ROOT_ITEMS = [ 24 "node-menu-pasteinnerhtml", 25 "node-menu-copyinner", 26 "node-menu-edithtml", 27 ].concat(ACTIVE_ON_DOCTYPE_ITEMS); 28 29 const ALL_MENU_ITEMS = [ 30 "node-menu-edithtml", 31 "node-menu-copyinner", 32 "node-menu-copyouter", 33 "node-menu-copyuniqueselector", 34 "node-menu-copycsspath", 35 "node-menu-copyxpath", 36 "node-menu-copyimagedatauri", 37 "node-menu-delete", 38 "node-menu-pseudo-hover", 39 "node-menu-pseudo-active", 40 "node-menu-pseudo-focus", 41 "node-menu-pseudo-focus-within", 42 "node-menu-scrollnodeintoview", 43 "node-menu-screenshotnode", 44 "node-menu-add-attribute", 45 "node-menu-copy-attribute", 46 "node-menu-edit-attribute", 47 "node-menu-remove-attribute", 48 ].concat(PASTE_MENU_ITEMS, ACTIVE_ON_DOCTYPE_ITEMS); 49 50 const INACTIVE_ON_DOCTYPE_ITEMS = ALL_MENU_ITEMS.filter( 51 item => !ACTIVE_ON_DOCTYPE_ITEMS.includes(item) 52 ); 53 54 const INACTIVE_ON_DOCUMENT_ITEMS = INACTIVE_ON_DOCTYPE_ITEMS; 55 56 const INACTIVE_ON_SHADOW_ROOT_ITEMS = ALL_MENU_ITEMS.filter( 57 item => !ACTIVE_ON_SHADOW_ROOT_ITEMS.includes(item) 58 ); 59 60 /** 61 * Test cases, each item of this array may define the following properties: 62 * desc: string that will be logged 63 * selector: selector of the node to be selected 64 * disabled: items that should have disabled state 65 * clipboardData: clipboard content 66 * clipboardDataType: clipboard content type 67 * attributeTrigger: attribute that will be used as context menu trigger 68 * shadowRoot: if true, selects the shadow root from the node, rather than 69 * the node itself. 70 */ 71 const TEST_CASES = [ 72 { 73 desc: "doctype node with empty clipboard", 74 selector: null, 75 disabled: INACTIVE_ON_DOCTYPE_ITEMS, 76 }, 77 { 78 desc: "doctype node with html on clipboard", 79 clipboardData: "<p>some text</p>", 80 clipboardDataType: "text", 81 selector: null, 82 disabled: INACTIVE_ON_DOCTYPE_ITEMS, 83 }, 84 { 85 desc: "element node HTML on the clipboard", 86 clipboardData: "<p>some text</p>", 87 clipboardDataType: "text", 88 disabled: [ 89 "node-menu-copyimagedatauri", 90 "node-menu-copy-attribute", 91 "node-menu-edit-attribute", 92 "node-menu-remove-attribute", 93 ], 94 selector: "#sensitivity", 95 }, 96 { 97 desc: "<html> element", 98 clipboardData: "<p>some text</p>", 99 clipboardDataType: "text", 100 selector: "html", 101 disabled: [ 102 "node-menu-copyimagedatauri", 103 "node-menu-pastebefore", 104 "node-menu-pasteafter", 105 "node-menu-pastefirstchild", 106 "node-menu-pastelastchild", 107 "node-menu-copy-attribute", 108 "node-menu-edit-attribute", 109 "node-menu-remove-attribute", 110 "node-menu-delete", 111 ], 112 }, 113 { 114 desc: "<body> with HTML on clipboard", 115 clipboardData: "<p>some text</p>", 116 clipboardDataType: "text", 117 selector: "body", 118 disabled: [ 119 "node-menu-copyimagedatauri", 120 "node-menu-pastebefore", 121 "node-menu-pasteafter", 122 "node-menu-copy-attribute", 123 "node-menu-edit-attribute", 124 "node-menu-remove-attribute", 125 ], 126 }, 127 { 128 desc: "<img> with HTML on clipboard", 129 clipboardData: "<p>some text</p>", 130 clipboardDataType: "text", 131 selector: "img", 132 disabled: [ 133 "node-menu-copy-attribute", 134 "node-menu-edit-attribute", 135 "node-menu-remove-attribute", 136 ], 137 }, 138 { 139 desc: "<head> with HTML on clipboard", 140 clipboardData: "<p>some text</p>", 141 clipboardDataType: "text", 142 selector: "head", 143 disabled: [ 144 "node-menu-copyimagedatauri", 145 "node-menu-pastebefore", 146 "node-menu-pasteafter", 147 "node-menu-screenshotnode", 148 "node-menu-copy-attribute", 149 "node-menu-edit-attribute", 150 "node-menu-remove-attribute", 151 ], 152 }, 153 { 154 desc: "<head> with no html on clipboard", 155 selector: "head", 156 disabled: PASTE_MENU_ITEMS.concat([ 157 "node-menu-copyimagedatauri", 158 "node-menu-screenshotnode", 159 "node-menu-copy-attribute", 160 "node-menu-edit-attribute", 161 "node-menu-remove-attribute", 162 ]), 163 }, 164 { 165 desc: "<element> with text on clipboard", 166 clipboardData: "some text", 167 clipboardDataType: "text", 168 selector: "#paste-area", 169 disabled: [ 170 "node-menu-copyimagedatauri", 171 "node-menu-copy-attribute", 172 "node-menu-edit-attribute", 173 "node-menu-remove-attribute", 174 ], 175 }, 176 { 177 desc: "<element> with base64 encoded image data uri on clipboard", 178 clipboardData: 179 "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABC" + 180 "AAAAAA6fptVAAAACklEQVQYV2P4DwABAQEAWk1v8QAAAABJRU5ErkJggg==", 181 clipboardDataType: "image", 182 selector: "#paste-area", 183 disabled: PASTE_MENU_ITEMS.concat([ 184 "node-menu-copyimagedatauri", 185 "node-menu-copy-attribute", 186 "node-menu-edit-attribute", 187 "node-menu-remove-attribute", 188 ]), 189 }, 190 { 191 desc: "<element> with empty string on clipboard", 192 clipboardData: "", 193 clipboardDataType: "text", 194 selector: "#paste-area", 195 disabled: PASTE_MENU_ITEMS.concat([ 196 "node-menu-copyimagedatauri", 197 "node-menu-copy-attribute", 198 "node-menu-edit-attribute", 199 "node-menu-remove-attribute", 200 ]), 201 }, 202 { 203 desc: "<element> with whitespace only on clipboard", 204 clipboardData: " \n\n\t\n\n \n", 205 clipboardDataType: "text", 206 selector: "#paste-area", 207 disabled: PASTE_MENU_ITEMS.concat([ 208 "node-menu-copyimagedatauri", 209 "node-menu-copy-attribute", 210 "node-menu-edit-attribute", 211 "node-menu-remove-attribute", 212 ]), 213 }, 214 { 215 desc: "<element> that isn't visible on the page, empty clipboard", 216 selector: "#hiddenElement", 217 disabled: PASTE_MENU_ITEMS.concat([ 218 "node-menu-copyimagedatauri", 219 "node-menu-screenshotnode", 220 "node-menu-copy-attribute", 221 "node-menu-edit-attribute", 222 "node-menu-remove-attribute", 223 ]), 224 }, 225 { 226 desc: "<element> nested in another hidden element, empty clipboard", 227 selector: "#nestedHiddenElement", 228 disabled: PASTE_MENU_ITEMS.concat([ 229 "node-menu-copyimagedatauri", 230 "node-menu-screenshotnode", 231 "node-menu-copy-attribute", 232 "node-menu-edit-attribute", 233 "node-menu-remove-attribute", 234 ]), 235 }, 236 { 237 desc: "<element> with context menu triggered on attribute, empty clipboard", 238 selector: "#attributes", 239 disabled: PASTE_MENU_ITEMS.concat(["node-menu-copyimagedatauri"]), 240 attributeTrigger: "data-edit", 241 }, 242 { 243 desc: "Shadow Root", 244 clipboardData: "<p>some text</p>", 245 clipboardDataType: "text", 246 disabled: INACTIVE_ON_SHADOW_ROOT_ITEMS, 247 selector: "#host", 248 shadowRoot: true, 249 }, 250 { 251 desc: "Document node in iFrame", 252 disabled: INACTIVE_ON_DOCUMENT_ITEMS, 253 selector: "iframe", 254 documentNode: true, 255 }, 256 ]; 257 258 var clipboard = require("resource://devtools/shared/platform/clipboard.js"); 259 registerCleanupFunction(() => { 260 clipboard.copyString(""); 261 clipboard = null; 262 }); 263 264 add_task(async function () { 265 const { inspector } = await openInspectorForURL(TEST_URL); 266 for (const test of TEST_CASES) { 267 const { 268 desc, 269 disabled, 270 selector, 271 attributeTrigger, 272 documentNode = false, 273 shadowRoot = false, 274 } = test; 275 276 info(`Test ${desc}`); 277 setupClipboard(test.clipboardData, test.clipboardDataType); 278 279 const front = await getNodeFrontForSelector( 280 selector, 281 inspector, 282 documentNode, 283 shadowRoot 284 ); 285 286 info("Selecting the specified node."); 287 await selectNode(front, inspector); 288 289 info("Simulating context menu click on the selected node container."); 290 const nodeFrontContainer = getContainerForNodeFront(front, inspector); 291 const contextMenuTrigger = attributeTrigger 292 ? nodeFrontContainer.tagLine.querySelector( 293 `[data-attr="${attributeTrigger}"]` 294 ) 295 : nodeFrontContainer.tagLine; 296 297 const allMenuItems = openContextMenuAndGetAllItems(inspector, { 298 target: contextMenuTrigger, 299 }); 300 301 for (const id of ALL_MENU_ITEMS) { 302 const menuItem = allMenuItems.find(item => item.id === id); 303 const shouldBeDisabled = disabled.includes(id); 304 const shouldBeDisabledText = shouldBeDisabled ? "disabled" : "enabled"; 305 is( 306 menuItem.disabled, 307 shouldBeDisabled, 308 `#${id} should be ${shouldBeDisabledText} for test case ${desc}` 309 ); 310 } 311 } 312 }); 313 314 /** 315 * A helper that fetches a front for a node that matches the given selector or 316 * doctype node if the selector is falsy. 317 */ 318 async function getNodeFrontForSelector( 319 selector, 320 inspector, 321 documentNode, 322 shadowRoot 323 ) { 324 if (selector) { 325 info("Retrieving front for selector " + selector); 326 const node = await getNodeFront(selector, inspector); 327 if (shadowRoot) { 328 return getShadowRoot(node, inspector); 329 } 330 if (documentNode) { 331 return getFrameDocument(node, inspector); 332 } 333 return node; 334 } 335 336 info("Retrieving front for doctype node"); 337 const { nodes } = await inspector.walker.children(inspector.walker.rootNode); 338 return nodes[0]; 339 } 340 341 /** 342 * A helper that populates the clipboard with data of given type. Clears the 343 * clipboard if data is falsy. 344 */ 345 function setupClipboard(data, type) { 346 if (!data) { 347 info("Clearing the clipboard."); 348 clipboard.copyString(""); 349 } else if (type === "text") { 350 info("Populating clipboard with text."); 351 clipboard.copyString(data); 352 } else if (type === "image") { 353 info("Populating clipboard with image content"); 354 copyImageToClipboard(data); 355 } 356 } 357 358 /** 359 * The code below is a simplified version of the sdk/clipboard helper set() method. 360 */ 361 function copyImageToClipboard(data) { 362 const imageTools = Cc["@mozilla.org/image/tools;1"].getService(Ci.imgITools); 363 364 // Image data is stored as base64 in the test. 365 const image = atob(data); 366 367 const imgPtr = Cc["@mozilla.org/supports-interface-pointer;1"].createInstance( 368 Ci.nsISupportsInterfacePointer 369 ); 370 imgPtr.data = imageTools.decodeImageFromBuffer( 371 image, 372 image.length, 373 "image/png" 374 ); 375 376 const xferable = Cc["@mozilla.org/widget/transferable;1"].createInstance( 377 Ci.nsITransferable 378 ); 379 xferable.init(null); 380 xferable.addDataFlavor("image/png"); 381 xferable.setTransferData("image/png", imgPtr); 382 383 Services.clipboard.setData( 384 xferable, 385 null, 386 Services.clipboard.kGlobalClipboard 387 ); 388 }