diff options
Diffstat (limited to 'devtools/client/inspector')
7 files changed, 246 insertions, 13 deletions
diff --git a/devtools/client/inspector/rules/test/browser_part2.toml b/devtools/client/inspector/rules/test/browser_part2.toml index 1cffe88e6e..19d7e2997b 100644 --- a/devtools/client/inspector/rules/test/browser_part2.toml +++ b/devtools/client/inspector/rules/test/browser_part2.toml @@ -181,6 +181,8 @@ skip-if = [ ["browser_rules_keyframeLineNumbers.js"] +["browser_rules_keyframes-rule-nested.js"] + ["browser_rules_keyframes-rule-shadowdom.js"] ["browser_rules_keyframes-rule_01.js"] diff --git a/devtools/client/inspector/rules/test/browser_rules_keyframes-rule-nested.js b/devtools/client/inspector/rules/test/browser_rules_keyframes-rule-nested.js new file mode 100644 index 0000000000..97b4b21a65 --- /dev/null +++ b/devtools/client/inspector/rules/test/browser_rules_keyframes-rule-nested.js @@ -0,0 +1,89 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +// Test that nested @keyframes rules are displayed correctly in the rule view + +const TEST_URI = `data:text/html,${encodeURIComponent(` + <style> + body { + animation-name: in-layer, + in-starting-style, + in-media, + in-container; + animation-duration: 1s, 2s, 3s, 4s; + border: 4px solid; + outline: 4px solid; + } + + @layer { + @keyframes in-layer { + from { color: red; } + to { color: blue; } + } + } + + @starting-style { + /* keyframes is considered as being outside of @starting-style */ + @keyframes in-starting-style { + from { border-color: tomato; } + to { border-color: gold; } + } + } + + @media screen { + @keyframes in-media { + from { outline-color: purple; } + to { outline-color: pink; } + } + } + + @container (width > 0px) { + /* keyframes is considered as being outside of @container */ + @keyframes in-container { + from { background-color: green; } + to { background-color: lime; } + } + } + </style> + <h1>Nested <code>@keyframes</code></h1> +`)}`; + +add_task(async function () { + await pushPref("layout.css.starting-style-at-rules.enabled", true); + await addTab(TEST_URI); + const { inspector, view } = await openRuleView(); + + await selectNode("body", inspector); + const headers = Array.from(view.element.querySelectorAll(".ruleview-header")); + Assert.deepEqual( + headers.map(el => el.textContent), + [ + "Keyframes in-layer", + "Keyframes in-starting-style", + "Keyframes in-media", + "Keyframes in-container", + ], + "Got expected keyframes sections" + ); + + info("Check that keyframes' keyframe ancestor rules are not displayed"); + for (const headerEl of headers) { + const keyframeContainerId = headerEl + .querySelector("button") + .getAttribute("aria-controls"); + const keyframeContainer = view.element.querySelector( + `#${keyframeContainerId}` + ); + ok( + !!keyframeContainer, + `Got keyframe container for "${headerEl.textContent}"` + ); + is( + keyframeContainer.querySelector(".ruleview-rule-ancestor"), + null, + `ancestor data are not displayed for "${headerEl.textContent}" keyframe rules` + ); + } +}); diff --git a/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js index fcd0302624..d1919c6f36 100644 --- a/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js +++ b/devtools/client/inspector/rules/test/browser_rules_pseudo-element_01.js @@ -5,13 +5,16 @@ // Test that pseudoelements are displayed correctly in the rule view -const TEST_URI = URL_ROOT + "doc_pseudoelement.html"; +const TEST_URI = URL_ROOT + "doc_pseudoelement.html?#:~:text=fox"; const PSEUDO_PREF = "devtools.inspector.show_pseudo_elements"; add_task(async function () { await pushPref(PSEUDO_PREF, true); await pushPref("dom.customHighlightAPI.enabled", true); + await pushPref("dom.text_fragments.enabled", true); await pushPref("layout.css.modern-range-pseudos.enabled", true); + await pushPref("full-screen-api.transition-duration.enter", "0 0"); + await pushPref("full-screen-api.transition-duration.leave", "0 0"); await addTab(TEST_URI); const { inspector, view } = await openRuleView(); @@ -23,9 +26,11 @@ add_task(async function () { await testParagraph(inspector, view); await testBody(inspector, view); await testList(inspector, view); - await testDialogBackdrop(inspector, view); await testCustomHighlight(inspector, view); await testSlider(inspector, view); + await testUrlFragmentTextDirective(inspector, view); + // keep this one last as it makes the browser go fullscreen and seem to impact other tests + await testBackdrop(inspector, view); }); async function testTopLeft(inspector, view) { @@ -288,13 +293,80 @@ async function testList(inspector, view) { assertGutters(view); } -async function testDialogBackdrop(inspector, view) { +async function testBackdrop(inspector, view) { + info("Test ::backdrop for dialog element"); await assertPseudoElementRulesNumbers("dialog", inspector, view, { elementRulesNb: 3, backdropRules: 1, }); + info("Test ::backdrop for popover element"); + await assertPseudoElementRulesNumbers( + "#in-dialog[popover]", + inspector, + view, + { + elementRulesNb: 3, + backdropRules: 1, + } + ); + assertGutters(view); + + info("Test ::backdrop rules are displayed when elements is fullscreen"); + + // Wait for the document being activated, so that + // fullscreen request won't be denied. + const onTabFocused = SpecialPowers.spawn(gBrowser.selectedBrowser, [], () => { + return ContentTaskUtils.waitForCondition( + () => content.browsingContext.isActive && content.document.hasFocus(), + "document is active" + ); + }); + gBrowser.selectedBrowser.focus(); + await onTabFocused; + + info("Request fullscreen"); + // Entering fullscreen is triggering an update, wait for it so it doesn't impact + // the rest of the test + let onInspectorUpdated = view.once("ruleview-refreshed"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + const canvas = content.document.querySelector("canvas"); + canvas.requestFullscreen(); + + await ContentTaskUtils.waitForCondition( + () => content.document.fullscreenElement === canvas, + "canvas is fullscreen" + ); + }); + await onInspectorUpdated; + + await assertPseudoElementRulesNumbers("canvas", inspector, view, { + elementRulesNb: 3, + backdropRules: 1, + }); + + assertGutters(view); + + // Exiting fullscreen is triggering an update, wait for it so it doesn't impact + // the rest of the test + onInspectorUpdated = view.once("ruleview-refreshed"); + await SpecialPowers.spawn(gBrowser.selectedBrowser, [], async () => { + content.document.exitFullscreen(); + await ContentTaskUtils.waitForCondition( + () => content.document.fullscreenElement === null, + "canvas is no longer fullscreen" + ); + }); + await onInspectorUpdated; + + info( + "Test ::backdrop rules are not displayed when elements are not fullscreen" + ); + await assertPseudoElementRulesNumbers("canvas", inspector, view, { + elementRulesNb: 3, + backdropRules: 0, + }); } async function testCustomHighlight(inspector, view) { @@ -376,6 +448,19 @@ async function testSlider(inspector, view) { ); } +async function testUrlFragmentTextDirective(inspector, view) { + await assertPseudoElementRulesNumbers( + ".url-fragment-text-directives", + inspector, + view, + { + elementRulesNb: 3, + targetTextRulesNb: 1, + } + ); + assertGutters(view); +} + function convertTextPropsToString(textProps) { return textProps .map( @@ -436,6 +521,9 @@ async function assertPseudoElementRulesNumbers( sliderTrackRules: elementStyle.rules.filter( rule => rule.pseudoElement === "::slider-track" ), + targetTextRules: elementStyle.rules.filter( + rule => rule.pseudoElement === "::target-text" + ), }; is( @@ -493,6 +581,11 @@ async function assertPseudoElementRulesNumbers( ruleNbs.sliderTrackRulesNb || 0, selector + " has the correct number of ::slider-track rules" ); + is( + rules.targetTextRules.length, + ruleNbs.targetTextRulesNb || 0, + selector + " has the correct number of ::target-text rules" + ); // If we do have pseudo element rules displayed, ensure we don't mark their selectors // as matched or unmatched diff --git a/devtools/client/inspector/rules/test/doc_pseudoelement.html b/devtools/client/inspector/rules/test/doc_pseudoelement.html index 8e077e220b..88611d6633 100644 --- a/devtools/client/inspector/rules/test/doc_pseudoelement.html +++ b/devtools/client/inspector/rules/test/doc_pseudoelement.html @@ -110,7 +110,11 @@ p:first-letter { color: purple; } -dialog::backdrop { +:is( + dialog, + [popover], + :fullscreen, +)::backdrop { background-color: transparent; } @@ -142,6 +146,10 @@ input::slider-thumb { input::slider-track { background: seagreen; } + +.url-fragment-text-directives::target-text { + background-color: salmon; +} </style> </head> <body> @@ -167,7 +175,10 @@ input::slider-track { <li id="list" class="box">List element</li> </ol> - <dialog>In dialog</dialog> + <dialog> + In dialog + <div id="in-dialog" popover>hello</div> + </dialog> <label>Range <input type="range" class="slider"></label> <label>Not range <input type="text" class="slider"></label> @@ -177,10 +188,15 @@ input::slider-track { You can use them to examine, edit, and debug HTML, CSS, and JavaScript. </section> + <section class="url-fragment-text-directives">May the fox be with you</section> + + <canvas></canvas> + <script> "use strict"; // This is the only way to have the ::backdrop style to be applied document.querySelector("dialog").showModal() + document.querySelector("#in-dialog").showPopover() // Register highlights for ::highlight pseudo elements const highlightsContainer = document.querySelector(".highlights-container"); diff --git a/devtools/client/inspector/shared/utils.js b/devtools/client/inspector/shared/utils.js index 542f9897b1..655f143978 100644 --- a/devtools/client/inspector/shared/utils.js +++ b/devtools/client/inspector/shared/utils.js @@ -12,7 +12,7 @@ loader.lazyRequireGetter( ); loader.lazyRequireGetter( this, - "getCSSLexer", + "InspectorCSSParserWrapper", "resource://devtools/shared/css/lexer.js", true ); @@ -51,11 +51,11 @@ function advanceValidate(keyCode, value, insertionPoint) { // value. Otherwise it's been inserted in some spot where it has a // valid meaning, like a comment or string. value = value.slice(0, insertionPoint) + ";" + value.slice(insertionPoint); - const lexer = getCSSLexer(value); + const lexer = new InspectorCSSParserWrapper(value); while (true) { const token = lexer.nextToken(); if (token.endOffset > insertionPoint) { - if (token.tokenType === "symbol" && token.text === ";") { + if (token.tokenType === "Semicolon") { // The ";" is a terminator. return true; } diff --git a/devtools/client/inspector/test/browser_inspector_picker-shift-key.js b/devtools/client/inspector/test/browser_inspector_picker-shift-key.js index 2eb1c04709..db1d5510e7 100644 --- a/devtools/client/inspector/test/browser_inspector_picker-shift-key.js +++ b/devtools/client/inspector/test/browser_inspector_picker-shift-key.js @@ -13,12 +13,18 @@ const TEST_URI = `data:text/html;charset=utf-8, <div id="nopointer" style="pointer-events: none">Element with pointer-events: none</div> <div id="transluscent" style="pointer-events: none;opacity: 0.1">Element with opacity of 0.1</div> <div id="invisible" style="pointer-events: none;opacity: 0">Element with opacity of 0</div> + <div> + <header>Hello</header> + <div style="z-index:-1;position:relative;"> + <span id="negative-z-index-child">ZZ</span> + </div> + </div> </main>`; const IS_OSX = Services.appinfo.OS === "Darwin"; add_task(async function () { - const { inspector, toolbox } = await openInspectorForURL(TEST_URI); - + const { inspector, toolbox, highlighterTestFront } = + await openInspectorForURL(TEST_URI); const body = await getNodeFront("body", inspector); is( inspector.selection.nodeFront, @@ -72,6 +78,31 @@ add_task(async function () { shiftKey: true, }); await checkElementSelected("main", inspector); + + info("Shift-clicking element with negative z-index parent works"); + await hoverElement( + inspector, + "#negative-z-index-child", + undefined, + undefined, + { + shiftKey: true, + } + ); + is( + await highlighterTestFront.getHighlighterNodeTextContent( + "box-model-infobar-id" + ), + "#negative-z-index-child", + "The highlighter is shown on #negative-z-index-child" + ); + + await clickElement({ + inspector, + selector: "#negative-z-index-child", + shiftKey: true, + }); + await checkElementSelected("#negative-z-index-child", inspector); }); async function clickElement({ selector, inspector, shiftKey }) { diff --git a/devtools/client/inspector/test/head.js b/devtools/client/inspector/test/head.js index cb1b00030c..73c8902c6b 100644 --- a/devtools/client/inspector/test/head.js +++ b/devtools/client/inspector/test/head.js @@ -131,10 +131,12 @@ function pickElement(inspector, selector, x, y) { * X-offset from the top-left corner of the element matching the provided selector * @param {Number} y * Y-offset from the top-left corner of the element matching the provided selector + * @param {Object} eventOptions + * Options that will be passed to synthesizeMouse * @return {Promise} promise that resolves when both the "picker-node-hovered" and * "highlighter-shown" events are emitted. */ -async function hoverElement(inspector, selector, x, y) { +async function hoverElement(inspector, selector, x, y, eventOptions = {}) { const { waitForHighlighterTypeShown } = getHighlighterTestHelpers(inspector); info(`Waiting for element "${selector}" to be hovered`); const onHovered = inspector.toolbox.nodePicker.once("picker-node-hovered"); @@ -159,7 +161,7 @@ async function hoverElement(inspector, selector, x, y) { if (isNaN(x) || isNaN(y)) { BrowserTestUtils.synthesizeMouseAtCenter( selector, - { type: "mousemove" }, + { ...eventOptions, type: "mousemove" }, browsingContext ); } else { @@ -167,7 +169,7 @@ async function hoverElement(inspector, selector, x, y) { selector, x, y, - { type: "mousemove" }, + { ...eventOptions, type: "mousemove" }, browsingContext ); } |