Source code

Revision control

Copy as Markdown

Other Tools

Test Info: Errors

/* Any copyright is dedicated to the Public Domain.
"use strict";
// Test that hovering over a simple selector in the style-editor requests the
// highlighting of the corresponding nodes, even in remote iframes.
<style>h2{color:cyan}</style>
<h2>highlighter test</h2>`;
<style>h1{color:red}</style>
<h1>highlighter test</h1>
<iframe src='${REMOTE_IFRAME_URL}'></iframe>`;
add_task(async function () {
const { ui } = await openStyleEditorForURL(TOP_LEVEL_URL);
info(
"Wait until both stylesheet are loaded and ready to handle mouse events"
);
await waitFor(() => ui.editors.length == 2);
const topLevelStylesheetEditor = ui.editors.find(e =>
e._resource.nodeHref.startsWith("https://example.com")
);
const iframeStylesheetEditor = ui.editors.find(e =>
e._resource.nodeHref.startsWith("https://example.org")
);
await ui.selectStyleSheet(topLevelStylesheetEditor.styleSheet);
await waitFor(() => topLevelStylesheetEditor.highlighter);
info("Check that highlighting works on the top-level document");
const topLevelHighlighterTestFront =
await topLevelStylesheetEditor._resource.targetFront.getFront(
"highlighterTest"
);
topLevelHighlighterTestFront.highlighter =
topLevelStylesheetEditor.highlighter;
info("Expecting a node-highlighted event");
let onHighlighted = topLevelStylesheetEditor.once("node-highlighted");
info("Simulate a mousemove event on the h1 selector");
// mousemove event listeners is set on editor.sourceEditor, which is not defined right away.
await waitFor(() => !!topLevelStylesheetEditor.sourceEditor);
let selectorEl = querySelectorCodeMirrorCssRuleSelectorToken(
topLevelStylesheetEditor
);
EventUtils.synthesizeMouseAtCenter(
selectorEl,
{ type: "mousemove" },
selectorEl.ownerDocument.defaultView
);
await onHighlighted;
ok(
await topLevelHighlighterTestFront.isNodeRectHighlighted(
await getElementNodeRectWithinTarget(["h1"])
),
"The highlighter's outline corresponds to the h1 node"
);
info(
"Simulate a mousemove event on the property name to hide the highlighter"
);
EventUtils.synthesizeMouseAtCenter(
querySelectorCodeMirrorCssPropertyNameToken(topLevelStylesheetEditor),
{ type: "mousemove" },
selectorEl.ownerDocument.defaultView
);
await waitFor(async () => !topLevelStylesheetEditor.highlighter.isShown());
let isVisible = await topLevelHighlighterTestFront.isHighlighting();
is(isVisible, false, "The highlighter is now hidden");
// It looks like we need to show the same highlighter again to trigger Bug 1847747
info("Show and hide the highlighter again");
onHighlighted = topLevelStylesheetEditor.once("node-highlighted");
EventUtils.synthesizeMouseAtCenter(
selectorEl,
{ type: "mousemove" },
selectorEl.ownerDocument.defaultView
);
await onHighlighted;
EventUtils.synthesizeMouseAtCenter(
querySelectorCodeMirrorCssPropertyNameToken(topLevelStylesheetEditor),
{ type: "mousemove" },
selectorEl.ownerDocument.defaultView
);
await waitFor(async () => !topLevelStylesheetEditor.highlighter.isShown());
// wait for a bit so the style editor would have had the time to process
// any unwanted stylesheets
await wait(1000);
ok(
!ui.editors.find(e => e._resource.href?.includes("highlighters.css")),
"highlighters.css isn't displayed in StyleEditor"
);
is(ui.editors.length, 2, "No other stylesheet was displayed");
info("Check that highlighting works on the iframe document");
await ui.selectStyleSheet(iframeStylesheetEditor.styleSheet);
await waitFor(() => iframeStylesheetEditor.highlighter);
const iframeHighlighterTestFront =
await iframeStylesheetEditor._resource.targetFront.getFront(
"highlighterTest"
);
iframeHighlighterTestFront.highlighter = iframeStylesheetEditor.highlighter;
info("Expecting a node-highlighted event");
onHighlighted = iframeStylesheetEditor.once("node-highlighted");
info("Simulate a mousemove event on the h2 selector");
// mousemove event listeners is set on editor.sourceEditor, which is not defined right away.
await waitFor(() => !!iframeStylesheetEditor.sourceEditor);
selectorEl = querySelectorCodeMirrorCssRuleSelectorToken(
iframeStylesheetEditor
);
EventUtils.synthesizeMouseAtCenter(
selectorEl,
{ type: "mousemove" },
selectorEl.ownerDocument.defaultView
);
await onHighlighted;
isVisible = await iframeHighlighterTestFront.isHighlighting();
ok(isVisible, "The highlighter is shown");
ok(
await iframeHighlighterTestFront.isNodeRectHighlighted(
await getElementNodeRectWithinTarget(["iframe", "h2"])
),
"The highlighter's outline corresponds to the h2 node"
);
info("Simulate a mousemove event elsewhere in the editor");
EventUtils.synthesizeMouseAtCenter(
querySelectorCodeMirrorCssPropertyNameToken(iframeStylesheetEditor),
{ type: "mousemove" },
selectorEl.ownerDocument.defaultView
);
await waitFor(async () => !topLevelStylesheetEditor.highlighter.isShown());
isVisible = await iframeHighlighterTestFront.isHighlighting();
is(isVisible, false, "The highlighter is now hidden");
});
function querySelectorCodeMirrorCssRuleSelectorToken(stylesheetEditor) {
// CSS Rules selector (e.g. `h1`) are displayed in a .cm-tag span
return querySelectorCodeMirror(stylesheetEditor, ".cm-tag");
}
function querySelectorCodeMirrorCssPropertyNameToken(stylesheetEditor) {
// properties name (e.g. `color`) are displayed in a .cm-property span
return querySelectorCodeMirror(stylesheetEditor, ".cm-property");
}
function querySelectorCodeMirror(stylesheetEditor, selector) {
return stylesheetEditor.sourceEditor.codeMirror
.getWrapperElement()
.querySelector(selector);
}
/**
* Return the bounds of the element matching the selector, relatively to the target bounds
* (e.g. if Fission is enabled, it's related to the iframe bound, if Fission is disabled,
* it's related to the top level document).
*
* @param {Array<string>} selectors: Arrays of CSS selectors from the root document to the node.
* The last CSS selector of the array is for the node in its frame doc.
* The before-last CSS selector is for the frame in its parent frame, etc...
* Ex: ["frame.first-frame", ..., "frame.last-frame", ".target-node"]
* @returns {Object} with left/top/width/height properties representing the node bounds
*/
async function getElementNodeRectWithinTarget(selectors) {
// Retrieve the browsing context in which the element is
const inBCSelector = selectors.pop();
const frameSelectors = selectors;
const bc = frameSelectors.length
? await getBrowsingContextInFrames(
gBrowser.selectedBrowser.browsingContext,
frameSelectors
)
: gBrowser.selectedBrowser.browsingContext;
// Get the element bounds within the Firefox window
const elementBounds = await SpecialPowers.spawn(
bc,
[inBCSelector],
_selector => {
const el = content.document.querySelector(_selector);
const { left, top, width, height } = el
.getBoxQuadsFromWindowOrigin()[0]
.getBounds();
return { left, top, width, height };
}
);
// Then we need to offset the element bounds from a frame bounds
// When fission/EFT is enabled, the highlighter is only shown within the iframe bounds.
// So we only need to retrieve the element bounds within the iframe.
// Otherwise, we retrieve the top frame bounds
const relativeBrowsingContext =
isFissionEnabled() || isEveryFrameTargetEnabled()
? bc
: gBrowser.selectedBrowser.browsingContext;
const relativeDocumentBounds = await SpecialPowers.spawn(
relativeBrowsingContext,
[],
() =>
content.document.documentElement
.getBoxQuadsFromWindowOrigin()[0]
.getBounds()
);
// Adjust the element bounds based on the relative document bounds
elementBounds.left = elementBounds.left - relativeDocumentBounds.left;
elementBounds.top = elementBounds.top - relativeDocumentBounds.top;
return elementBounds;
}