From 6bf0a5cb5034a7e684dcc3500e841785237ce2dd Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 19:32:43 +0200 Subject: Adding upstream version 1:115.7.0. Signed-off-by: Daniel Baumann --- .../highlighters/remote-node-picker-notice.js | 188 +++++++++++++++++++++ 1 file changed, 188 insertions(+) create mode 100644 devtools/server/actors/highlighters/remote-node-picker-notice.js (limited to 'devtools/server/actors/highlighters/remote-node-picker-notice.js') diff --git a/devtools/server/actors/highlighters/remote-node-picker-notice.js b/devtools/server/actors/highlighters/remote-node-picker-notice.js new file mode 100644 index 0000000000..64b131d2a2 --- /dev/null +++ b/devtools/server/actors/highlighters/remote-node-picker-notice.js @@ -0,0 +1,188 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +"use strict"; + +const { + CanvasFrameAnonymousContentHelper, +} = require("resource://devtools/server/actors/highlighters/utils/markup.js"); + +loader.lazyGetter(this, "HighlightersBundle", () => { + return new Localization(["devtools/shared/highlighters.ftl"], true); +}); + +loader.lazyGetter(this, "isAndroid", () => { + return Services.appinfo.OS === "Android"; +}); + +/** + * The RemoteNodePickerNotice is a class that displays a notice in a remote debugged page. + * This is used to signal to users they can click/tap an element to select it in the + * about:devtools-toolbox toolbox inspector. + */ +class RemoteNodePickerNotice { + #highlighterEnvironment; + #previousHoveredElement; + + rootElementId = "node-picker-notice-root"; + hideButtonId = "node-picker-notice-hide-button"; + infoNoticeElementId = "node-picker-notice-info"; + + /** + * @param {highlighterEnvironment} highlighterEnvironment + */ + constructor(highlighterEnvironment) { + this.#highlighterEnvironment = highlighterEnvironment; + + this.markup = new CanvasFrameAnonymousContentHelper( + this.#highlighterEnvironment, + this.#buildMarkup + ); + this.isReady = this.markup.initialize(); + } + + #buildMarkup = () => { + const container = this.markup.createNode({ + attributes: { class: "highlighter-container" }, + }); + + // Wrapper element. + const wrapper = this.markup.createNode({ + parent: container, + attributes: { + id: this.rootElementId, + hidden: "true", + overlay: "true", + }, + }); + + const toolbar = this.markup.createNode({ + parent: wrapper, + attributes: { + id: "node-picker-notice-toolbar", + class: "toolbar", + }, + }); + + this.markup.createNode({ + parent: toolbar, + attributes: { + id: "node-picker-notice-icon", + class: isAndroid ? "touch" : "", + }, + }); + + const actionStr = HighlightersBundle.formatValueSync( + isAndroid + ? "remote-node-picker-notice-action-touch" + : "remote-node-picker-notice-action-desktop" + ); + + this.markup.createNode({ + nodeType: "span", + parent: toolbar, + text: HighlightersBundle.formatValueSync("remote-node-picker-notice", { + action: actionStr, + }), + attributes: { + id: this.infoNoticeElementId, + }, + }); + + this.markup.createNode({ + nodeType: "button", + parent: toolbar, + text: HighlightersBundle.formatValueSync( + "remote-node-picker-notice-hide-button" + ), + attributes: { + id: this.hideButtonId, + }, + }); + + return container; + }; + + destroy() { + // hide will nullify take care of this.#abortController. + this.hide(); + this.markup.destroy(); + this.#highlighterEnvironment = null; + this.#previousHoveredElement = null; + } + + /** + * We can't use event listener directly on the anonymous content because they aren't + * working while the page is paused. + * This is called from the NodePicker instance for easier events management. + * + * @param {ClickEvent} + */ + onClick(e) { + const target = e.originalTarget || e.target; + const targetId = target?.id; + + if (targetId === this.hideButtonId) { + this.hide(); + } + } + + /** + * Since we can't use :hover in the CSS for the anonymous content as it wouldn't work + * when the page is paused, we have to roll our own implementation, adding a `.hover` + * class for the element we want to style on hover (e.g. the close button). + * This is called from the NodePicker instance for easier events management. + * + * @param {MouseMoveEvent} + */ + handleHoveredElement(e) { + const hideButton = this.markup.getElement(this.hideButtonId); + + const target = e.originalTarget || e.target; + const targetId = target?.id; + + // If the user didn't change targets, do nothing + if (this.#previousHoveredElement?.id === targetId) { + return; + } + + if (targetId === this.hideButtonId) { + hideButton.classList.add("hover"); + } else { + hideButton.classList.remove("hover"); + } + this.#previousHoveredElement = target; + } + + getMarkupRootElement() { + return this.markup.getElement(this.rootElementId); + } + + async show() { + if (this.#highlighterEnvironment.isXUL) { + return false; + } + await this.isReady; + + // Show the highlighter's root element. + const root = this.getMarkupRootElement(); + root.removeAttribute("hidden"); + root.setAttribute("overlay", "true"); + + return true; + } + + hide() { + if (this.#highlighterEnvironment.isXUL) { + return; + } + + // Hide the overlay. + this.getMarkupRootElement().setAttribute("hidden", "true"); + // Reset the hover state + this.markup.getElement(this.hideButtonId).classList.remove("hover"); + this.#previousHoveredElement = null; + } +} +exports.RemoteNodePickerNotice = RemoteNodePickerNotice; -- cgit v1.2.3