mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-20 16:55:40 +00:00
Backed out 3 changesets (bug 1572651) for devtools failure at devtools/client/inspector/test/browser_inspector_highlighter-by-type.js. On a CLOSED TREE
Backed out changeset 71db1896c459 (bug 1572651) Backed out changeset fb5863ee4d37 (bug 1572651) Backed out changeset 5ef33867cacb (bug 1572651)
This commit is contained in:
parent
996128fd41
commit
6a27b47313
@ -2010,8 +2010,6 @@ pref("devtools.inspector.showUserAgentShadowRoots", false);
|
||||
pref("devtools.inspector.new-rulesview.enabled", false);
|
||||
// Enable the compatibility tool in the inspector.
|
||||
pref("devtools.inspector.compatibility.enabled", false);
|
||||
// Enable the new Box Model Highlighter with renderer in parent process
|
||||
pref("devtools.inspector.use-new-box-model-highlighter", false);
|
||||
|
||||
// Grid highlighter preferences
|
||||
pref("devtools.gridinspector.gridOutlineMaxColumns", 50);
|
||||
|
@ -1347,24 +1347,3 @@ toolbarpaletteitem > toolbaritem {
|
||||
toolbar[keyNav=true]:not([collapsed=true]):not([customizing=true]) toolbartabstop {
|
||||
-moz-user-focus: normal;
|
||||
}
|
||||
|
||||
/* Frame used for rendering the DevTools inspector highlighters */
|
||||
iframe.devtools-highlighter-renderer {
|
||||
border: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Highlighter for the Browser Toolbox */
|
||||
:root > iframe.devtools-highlighter-renderer {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
/* Highlighter for web content */
|
||||
.browserStack > iframe.devtools-highlighter-renderer {
|
||||
-moz-box-flex: 1;
|
||||
}
|
||||
|
@ -46,12 +46,6 @@ loader.lazyRequireGetter(
|
||||
"devtools/server/actors/highlighters/box-model",
|
||||
true
|
||||
);
|
||||
loader.lazyRequireGetter(
|
||||
this,
|
||||
"BoxModelHighlighterObserver",
|
||||
"devtools/server/actors/highlighters/box-model-observer",
|
||||
true
|
||||
);
|
||||
|
||||
const HIGHLIGHTER_PICKED_TIMER = 1000;
|
||||
const IS_OSX = Services.appinfo.OS === "Darwin";
|
||||
@ -116,7 +110,7 @@ exports.register = register;
|
||||
* The HighlighterActor class
|
||||
*/
|
||||
exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
|
||||
initialize: function(inspector, autohide, useNewBoxModelHighlighter = false) {
|
||||
initialize: function(inspector, autohide) {
|
||||
protocol.Actor.prototype.initialize.call(this, null);
|
||||
|
||||
this._autohide = autohide;
|
||||
@ -125,7 +119,6 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
|
||||
this._targetActor = this._inspector.targetActor;
|
||||
this._highlighterEnv = new HighlighterEnvironment();
|
||||
this._highlighterEnv.initFromTargetActor(this._targetActor);
|
||||
this._useNewBoxModelHighlighter = useNewBoxModelHighlighter;
|
||||
|
||||
this._onNavigate = this._onNavigate.bind(this);
|
||||
|
||||
@ -154,15 +147,6 @@ exports.HighlighterActor = protocol.ActorClassWithSpec(highlighterSpec, {
|
||||
_createHighlighter: function() {
|
||||
this._isPreviousWindowXUL = isXUL(this._targetActor.window);
|
||||
|
||||
if (this._useNewBoxModelHighlighter) {
|
||||
this._highlighter = new BoxModelHighlighterObserver(
|
||||
this._highlighterEnv,
|
||||
this.conn
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this._isPreviousWindowXUL) {
|
||||
this._highlighter = new BoxModelHighlighter(
|
||||
this._highlighterEnv,
|
||||
|
@ -85,7 +85,6 @@ function AutoRefreshHighlighter(highlighterEnv) {
|
||||
|
||||
AutoRefreshHighlighter.prototype = {
|
||||
_ignoreZoom: false,
|
||||
_ignoreScroll: false,
|
||||
|
||||
/**
|
||||
* Window corresponding to the current highlighterEnv. When replaying, this
|
||||
@ -195,7 +194,7 @@ AutoRefreshHighlighter.prototype = {
|
||||
this.contentWindow,
|
||||
this.currentNode,
|
||||
region,
|
||||
{ ignoreScroll: this._ignoreScroll, ignoreZoom: this._ignoreZoom }
|
||||
{ ignoreZoom: this._ignoreZoom }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
@ -1,312 +0,0 @@
|
||||
/* 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 { DebuggerServer } = require("devtools/server/debugger-server");
|
||||
const { AutoRefreshHighlighter } = require("./auto-refresh");
|
||||
const {
|
||||
getBindingElementAndPseudo,
|
||||
hasPseudoClassLock,
|
||||
isNodeValid,
|
||||
} = require("./utils/markup");
|
||||
const { PSEUDO_CLASSES } = require("devtools/shared/css/constants");
|
||||
const { getCurrentZoom } = require("devtools/shared/layout/utils");
|
||||
const {
|
||||
getNodeDisplayName,
|
||||
getNodeGridFlexType,
|
||||
} = require("devtools/server/actors/inspector/utils");
|
||||
const nodeConstants = require("devtools/shared/dom-node-constants");
|
||||
const { LocalizationHelper } = require("devtools/shared/l10n");
|
||||
const STRINGS_URI = "devtools/shared/locales/highlighters.properties";
|
||||
const L10N = new LocalizationHelper(STRINGS_URI);
|
||||
const {
|
||||
BOX_MODEL_REGIONS,
|
||||
BoxModelHighlighterRenderer,
|
||||
} = require("devtools/server/actors/highlighters/box-model-renderer");
|
||||
|
||||
/**
|
||||
* The BoxModelHighlighterObserver observes the coordinates of a node and communicates
|
||||
* with the BoxModelHighlighterRenderer which draws the box model regions on top the a
|
||||
* node.
|
||||
*
|
||||
* When in the context of the content toolbox, the observer lives in
|
||||
* the child process (aka content process) and the renderer is set up in the parent
|
||||
* process. They communicate via messages.
|
||||
*
|
||||
* When in the context of the browser toolbox, both observer and renderer live in the
|
||||
* parent process. They communicate by direct reference.
|
||||
*/
|
||||
class BoxModelHighlighterObserver extends AutoRefreshHighlighter {
|
||||
constructor(highlighterEnv, conn) {
|
||||
super(highlighterEnv);
|
||||
this.conn = conn;
|
||||
this._ignoreScroll = true;
|
||||
this.typeName = this.constructor.name.replace("Observer", "");
|
||||
|
||||
if (DebuggerServer.isInChildProcess) {
|
||||
// eslint-disable-next-line no-restricted-properties
|
||||
this.conn.setupInParent({
|
||||
module: "devtools/server/actors/highlighters/box-model-renderer",
|
||||
setupParent: "setupParentProcess",
|
||||
});
|
||||
} else {
|
||||
this.renderer = new BoxModelHighlighterRenderer();
|
||||
}
|
||||
|
||||
/**
|
||||
* Optionally customize each region's fill color by adding an entry to the
|
||||
* regionFill property: `highlighter.regionFill.margin = "red";
|
||||
*/
|
||||
this.regionFill = {};
|
||||
|
||||
this.onPageHide = this.onPageHide.bind(this);
|
||||
this.onWillNavigate = this.onWillNavigate.bind(this);
|
||||
|
||||
this.highlighterEnv.on("will-navigate", this.onWillNavigate);
|
||||
|
||||
const { pageListenerTarget } = highlighterEnv;
|
||||
pageListenerTarget.addEventListener("pagehide", this.onPageHide);
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroy the nodes. Remove listeners.
|
||||
*/
|
||||
destroy() {
|
||||
this.highlighterEnv.off("will-navigate", this.onWillNavigate);
|
||||
|
||||
const { pageListenerTarget } = this.highlighterEnv;
|
||||
if (pageListenerTarget) {
|
||||
pageListenerTarget.removeEventListener("pagehide", this.onPageHide);
|
||||
}
|
||||
|
||||
if (DebuggerServer.isInChildProcess) {
|
||||
this.postMessage("destroy");
|
||||
} else {
|
||||
this.renderer.destroy();
|
||||
this.renderer = null;
|
||||
}
|
||||
|
||||
AutoRefreshHighlighter.prototype.destroy.call(this);
|
||||
}
|
||||
|
||||
get messageManager() {
|
||||
if (!DebuggerServer.isInChildProcess) {
|
||||
throw new Error(
|
||||
"Message manager should only be used when actor is in child process."
|
||||
);
|
||||
}
|
||||
|
||||
return this.conn.parentMessageManager;
|
||||
}
|
||||
|
||||
postMessage(topic, data = {}) {
|
||||
this._msgName = `debug:${this.conn.prefix}${this.typeName}`;
|
||||
this.messageManager.sendAsyncMessage(this._msgName, { topic, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* Tell the renderer to update the markup of the box model highlighter.
|
||||
*
|
||||
* @param {Object} data
|
||||
* Object with data about the node position, type and its attributes.
|
||||
* @see BoxModelHighlighterRenderer.render()
|
||||
*/
|
||||
render(data) {
|
||||
if (DebuggerServer.isInChildProcess) {
|
||||
this.postMessage("render", data);
|
||||
} else {
|
||||
this.renderer.render(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Override the AutoRefreshHighlighter's _isNodeValid method to also return true for
|
||||
* text nodes since these can also be highlighted.
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*/
|
||||
_isNodeValid(node) {
|
||||
return (
|
||||
node && (isNodeValid(node) || isNodeValid(node, nodeConstants.TEXT_NODE))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the highlighter on a given node
|
||||
*/
|
||||
_show() {
|
||||
if (!BOX_MODEL_REGIONS.includes(this.options.region)) {
|
||||
this.options.region = "content";
|
||||
}
|
||||
|
||||
const shown = this._update();
|
||||
this._trackMutations();
|
||||
return shown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Track the current node markup mutations so that the node info bar can be
|
||||
* updated to reflects the node's attributes
|
||||
*/
|
||||
_trackMutations() {
|
||||
if (isNodeValid(this.currentNode)) {
|
||||
const win = this.currentNode.ownerGlobal;
|
||||
this.currentNodeObserver = new win.MutationObserver(this.update);
|
||||
this.currentNodeObserver.observe(this.currentNode, { attributes: true });
|
||||
}
|
||||
}
|
||||
|
||||
_untrackMutations() {
|
||||
if (isNodeValid(this.currentNode) && this.currentNodeObserver) {
|
||||
this.currentNodeObserver.disconnect();
|
||||
this.currentNodeObserver = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the highlighter on the current highlighted node (the one that was
|
||||
* passed as an argument to show(node)).
|
||||
* Should be called whenever node size or attributes change
|
||||
*/
|
||||
_update() {
|
||||
const node = this.currentNode;
|
||||
let shown = false;
|
||||
|
||||
if (this._nodeNeedsHighlighting()) {
|
||||
// Tell the renderer to update the highlighter markup and provide it
|
||||
// with options, metadata and coordinates of the target node.
|
||||
const data = {
|
||||
...this.options,
|
||||
currentQuads: { ...this.currentQuads },
|
||||
regionFill: { ...this.regionFill },
|
||||
nodeData: this._getNodeData(),
|
||||
|
||||
showBoxModel: true,
|
||||
showInfoBar:
|
||||
!this.options.hideInfoBar &&
|
||||
(node.nodeType === node.ELEMENT_NODE ||
|
||||
node.nodeType === node.TEXT_NODE),
|
||||
};
|
||||
this.render(data);
|
||||
shown = true;
|
||||
} else {
|
||||
// Nothing to highlight (0px rectangle like a <script> tag for instance)
|
||||
this._hide();
|
||||
}
|
||||
|
||||
return shown;
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the highlighter, the outline and the infobar.
|
||||
*/
|
||||
_hide() {
|
||||
this._untrackMutations();
|
||||
|
||||
// Tell the renderer to hide the highlighter markup.
|
||||
this.render({
|
||||
showBoxModel: false,
|
||||
showInfoBar: false,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Can the current node be highlighted? Does it have quads.
|
||||
* @return {Boolean}
|
||||
*/
|
||||
_nodeNeedsHighlighting() {
|
||||
return (
|
||||
this.currentQuads.margin.length ||
|
||||
this.currentQuads.border.length ||
|
||||
this.currentQuads.padding.length ||
|
||||
this.currentQuads.content.length
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get data from the highlighted node to populate the infobar tooltip with
|
||||
* information such as the node's id, class names, grid or flex item type, etc.
|
||||
*
|
||||
* @return {Object|null} Information about the highlighted node
|
||||
*/
|
||||
_getNodeData() {
|
||||
if (!this.currentNode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const { bindingElement: node, pseudo } = getBindingElementAndPseudo(
|
||||
this.currentNode
|
||||
);
|
||||
|
||||
// Update the tag, id, classes, pseudo-classes and dimensions
|
||||
const displayName = getNodeDisplayName(node);
|
||||
|
||||
const id = node.id ? "#" + node.id : "";
|
||||
|
||||
const classList = (node.classList || []).length
|
||||
? "." + [...node.classList].join(".")
|
||||
: "";
|
||||
|
||||
let pseudos = this._getPseudoClasses(node).join("");
|
||||
if (pseudo) {
|
||||
// Display :after as ::after
|
||||
pseudos += ":" + pseudo;
|
||||
}
|
||||
|
||||
const zoom = getCurrentZoom(this.win);
|
||||
|
||||
const { grid: gridType, flex: flexType } = getNodeGridFlexType(node);
|
||||
const gridLayoutTextType = this._getLayoutTextType("gridType", gridType);
|
||||
const flexLayoutTextType = this._getLayoutTextType("flexType", flexType);
|
||||
|
||||
return {
|
||||
classList,
|
||||
displayName,
|
||||
flexLayoutTextType,
|
||||
gridLayoutTextType,
|
||||
id,
|
||||
pseudos,
|
||||
zoom,
|
||||
};
|
||||
}
|
||||
|
||||
_getLayoutTextType(layoutTypeKey, { isContainer, isItem }) {
|
||||
if (!isContainer && !isItem) {
|
||||
return "";
|
||||
}
|
||||
if (isContainer && !isItem) {
|
||||
return L10N.getStr(`${layoutTypeKey}.container`);
|
||||
}
|
||||
if (!isContainer && isItem) {
|
||||
return L10N.getStr(`${layoutTypeKey}.item`);
|
||||
}
|
||||
return L10N.getStr(`${layoutTypeKey}.dual`);
|
||||
}
|
||||
|
||||
_getPseudoClasses(node) {
|
||||
if (node.nodeType !== nodeConstants.ELEMENT_NODE) {
|
||||
// hasPseudoClassLock can only be used on Elements.
|
||||
return [];
|
||||
}
|
||||
|
||||
return PSEUDO_CLASSES.filter(pseudo => hasPseudoClassLock(node, pseudo));
|
||||
}
|
||||
|
||||
onPageHide({ target }) {
|
||||
// If a pagehide event is triggered for current window's highlighter, hide the
|
||||
// highlighter.
|
||||
if (target.defaultView === this.win) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
|
||||
onWillNavigate({ isTopLevel }) {
|
||||
if (isTopLevel) {
|
||||
this.hide();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exports.BoxModelHighlighterObserver = BoxModelHighlighterObserver;
|
@ -1,752 +0,0 @@
|
||||
/* 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 {
|
||||
createNode,
|
||||
createSVGNode,
|
||||
moveInfobar,
|
||||
} = require("devtools/server/actors/highlighters/utils/markup");
|
||||
|
||||
const {
|
||||
HighlighterRenderer,
|
||||
} = require("devtools/server/actors/highlighters/highlighter-renderer");
|
||||
|
||||
// Note that the order of items in this array is important because it is used
|
||||
// for drawing the BoxModelHighlighter's path elements correctly.
|
||||
const BOX_MODEL_REGIONS = ["margin", "border", "padding", "content"];
|
||||
exports.BOX_MODEL_REGIONS = BOX_MODEL_REGIONS;
|
||||
|
||||
const BOX_MODEL_SIDES = ["top", "right", "bottom", "left"];
|
||||
// Width of BoxModelHighlighter guides
|
||||
const GUIDE_STROKE_WIDTH = 1;
|
||||
|
||||
/**
|
||||
* The BoxModelHighlighterRenderer receives node coordinates from the
|
||||
* BoxModelHighlighterObserver and draws the box model regions on top of a node.
|
||||
* If the node is a block box, then each region will be displayed as 1 polygon.
|
||||
* If the node is an inline box though, each region may be represented by 1 or
|
||||
* more polygons, depending on how many line boxes the inline element has.
|
||||
*
|
||||
* Structure:
|
||||
* <div class="highlighter-container">
|
||||
* <div class="box-model-root">
|
||||
* <svg class="box-model-elements" hidden="true">
|
||||
* <g class="box-model-regions">
|
||||
* <path class="box-model-margin" points="..." />
|
||||
* <path class="box-model-border" points="..." />
|
||||
* <path class="box-model-padding" points="..." />
|
||||
* <path class="box-model-content" points="..." />
|
||||
* </g>
|
||||
* <line class="box-model-guide-top" x1="..." y1="..." x2="..." y2="..." />
|
||||
* <line class="box-model-guide-right" x1="..." y1="..." x2="..." y2="..." />
|
||||
* <line class="box-model-guide-bottom" x1="..." y1="..." x2="..." y2="..." />
|
||||
* <line class="box-model-guide-left" x1="..." y1="..." x2="..." y2="..." />
|
||||
* </svg>
|
||||
* <div class="box-model-infobar-container">
|
||||
* <div class="box-model-infobar-arrow highlighter-infobar-arrow-top" />
|
||||
* <div class="box-model-infobar">
|
||||
* <div class="box-model-infobar-text" align="center">
|
||||
* <span class="box-model-infobar-tagname">Node name</span>
|
||||
* <span class="box-model-infobar-id">Node id</span>
|
||||
* <span class="box-model-infobar-classes">.someClass</span>
|
||||
* <span class="box-model-infobar-pseudo-classes">:hover</span>
|
||||
* <span class="box-model-infobar-grid-type">Grid Type</span>
|
||||
* <span class="box-model-infobar-flex-type">Flex Type</span>
|
||||
* </div>
|
||||
* </div>
|
||||
* <div class="box-model-infobar-arrow box-model-infobar-arrow-bottom"/>
|
||||
* </div>
|
||||
* </div>
|
||||
* </div>
|
||||
*/
|
||||
class BoxModelHighlighterRenderer extends HighlighterRenderer {
|
||||
constructor(mm, prefix) {
|
||||
super();
|
||||
// @override Highlighter type name.
|
||||
this.typeName = this.constructor.name.replace("Renderer", "");
|
||||
// String used to prefix ids and classnames of highlighter nodes.
|
||||
this.ID_CLASS_PREFIX = "box-model-";
|
||||
|
||||
// If there is a message manager and connection prefix, it means the observer lives
|
||||
// in the content process so we need to setup a communication system with it.
|
||||
// Otherwise, both renderer and observer live in the parent process and there is no
|
||||
// need for a message-based communication system.
|
||||
if (mm && prefix) {
|
||||
this.setMessageManager(mm, prefix);
|
||||
this.init(false);
|
||||
} else {
|
||||
this.init(true);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the rendering of the box model highlighter, inforbar and guides for a node
|
||||
* using quad coordinates, node information and options from the BoxModelHighlighterObserver.
|
||||
*
|
||||
* @override
|
||||
*
|
||||
* @param {Object} data
|
||||
* Information used for rendering the box model highlighter, infobar and guides.
|
||||
*
|
||||
* @param {Boolean} data.showBoxModel
|
||||
* Whether to show the box model highlighter.
|
||||
* Defaults to false
|
||||
* @param {Boolean} data.showInfoBar
|
||||
* Whether to show the tooltip with the node's dimensions, class names, id, etc.
|
||||
* Defaults to false
|
||||
* @param {Object} data.currentQuads
|
||||
* Collection of quad coordinates for a node's box model regions keyed by region
|
||||
* @param {Object} data.regionFill
|
||||
* Optional collection of custom fill colors for box model regions keyed by
|
||||
* region. Ex: data.regionFill.margin = "red"
|
||||
*
|
||||
* @param {Object} data.nodeData
|
||||
* Collection of information about a node used for the infobar tooltip.
|
||||
* @param {String} data.nodeData.classList
|
||||
* String with list of class names separated by a dot (.) insead of space.
|
||||
* @param {String} data.nodeData.displayName
|
||||
* Node tag name.
|
||||
* @param {String} data.nodeData.flexLayoutTextType
|
||||
* Flex item or flex container.
|
||||
* @param {String} data.nodeData.gridLayoutTextType
|
||||
* Grid idem or flex container.
|
||||
* @param {String} data.nodeData.id
|
||||
* Node id attribute.
|
||||
* @param {String} data.nodeData.pseudos
|
||||
* Pseudo-element type if the node is a ::before/::after pseudo-element
|
||||
* @param {Number} data.nodeData.zoom
|
||||
* Zoom level of the content page where the node exists.
|
||||
*
|
||||
* @param {Object} data.options
|
||||
* Collection of optional overrides in the highlighter rendering.
|
||||
* @param {String} data.options.region
|
||||
* Specifies the region that the guides should outline:
|
||||
* "content" (default), "padding", "border" or "margin".
|
||||
* @param {Boolean} data.options.hideGuides
|
||||
* Defaults to false
|
||||
* @param {Boolean} data.options.hideInfoBar
|
||||
* Defaults to false
|
||||
* @param {String} data.options.showOnly
|
||||
* If set, only this region will be highlighted. Use with onlyRegionArea
|
||||
* to only highlight the area of the region:
|
||||
* "content", "padding", "border" or "margin"
|
||||
* @param {Boolean} data.options.onlyRegionArea
|
||||
* This can be set to true to make each region's box only highlight the
|
||||
* area of the corresponding region rather than the area of nested
|
||||
* regions too. This is useful when used with showOnly.
|
||||
*/
|
||||
render(data = {}) {
|
||||
this.currentQuads = data.currentQuads || {};
|
||||
this.nodeData = data.nodeData || null;
|
||||
this.options = data.options || {};
|
||||
this.regionFill = data.regionFill || {};
|
||||
|
||||
const { showBoxModel = false, showInfoBar = false } = data;
|
||||
|
||||
if (!showBoxModel) {
|
||||
this._hideBoxModel();
|
||||
this._hideInfobar();
|
||||
return;
|
||||
}
|
||||
|
||||
this._updateBoxModel();
|
||||
this._showBoxModel();
|
||||
|
||||
if (!showInfoBar) {
|
||||
this._hideInfobar();
|
||||
} else {
|
||||
this._updateInfobar();
|
||||
this._showInfobar();
|
||||
}
|
||||
}
|
||||
|
||||
getElement(id) {
|
||||
return this.markup.getElement(this.ID_CLASS_PREFIX + id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the infobar
|
||||
*/
|
||||
_hideInfobar() {
|
||||
this.getElement("infobar-container").setAttribute("hidden", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the infobar
|
||||
*/
|
||||
_showInfobar() {
|
||||
this.getElement("infobar-container").removeAttribute("hidden");
|
||||
}
|
||||
|
||||
/**
|
||||
* Hide the box model
|
||||
*/
|
||||
_hideBoxModel() {
|
||||
this.getElement("elements").setAttribute("hidden", "true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the box model
|
||||
*/
|
||||
_showBoxModel() {
|
||||
this.getElement("elements").removeAttribute("hidden");
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the box model
|
||||
*/
|
||||
_updateBoxModel() {
|
||||
const options = this.options;
|
||||
options.region = options.region || "content";
|
||||
|
||||
for (let i = 0; i < BOX_MODEL_REGIONS.length; i++) {
|
||||
const boxType = BOX_MODEL_REGIONS[i];
|
||||
const nextBoxType = BOX_MODEL_REGIONS[i + 1];
|
||||
const box = this.getElement(boxType);
|
||||
|
||||
if (this.regionFill[boxType]) {
|
||||
box.setAttribute("style", "fill:" + this.regionFill[boxType]);
|
||||
} else {
|
||||
box.setAttribute("style", "");
|
||||
}
|
||||
|
||||
// Highlight all quads for this region by setting the "d" attribute of the
|
||||
// corresponding <path>.
|
||||
const path = [];
|
||||
for (let j = 0; j < this.currentQuads[boxType].length; j++) {
|
||||
const boxQuad = this.currentQuads[boxType][j];
|
||||
const nextBoxQuad = this.currentQuads[nextBoxType]
|
||||
? this.currentQuads[nextBoxType][j]
|
||||
: null;
|
||||
path.push(this._getBoxPathCoordinates(boxQuad, nextBoxQuad));
|
||||
}
|
||||
|
||||
box.setAttribute("d", path.join(" "));
|
||||
box.removeAttribute("faded");
|
||||
|
||||
// If showOnly is defined, either hide the other regions, or fade them out
|
||||
// if onlyRegionArea is set too.
|
||||
if (options.showOnly && options.showOnly !== boxType) {
|
||||
if (options.onlyRegionArea) {
|
||||
box.setAttribute("faded", "true");
|
||||
} else {
|
||||
box.removeAttribute("d");
|
||||
}
|
||||
}
|
||||
|
||||
if (boxType === options.region && !options.hideGuides) {
|
||||
this._showGuides(boxType);
|
||||
} else if (options.hideGuides) {
|
||||
this._hideGuides();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate an outer quad based on the quads returned by getAdjustedQuads.
|
||||
* The BoxModelHighlighter may highlight more than one boxes, so in this case
|
||||
* create a new quad that "contains" all of these quads.
|
||||
* This is useful to position the guides and infobar.
|
||||
* This may happen if the BoxModelHighlighter is used to highlight an inline
|
||||
* element that spans line breaks.
|
||||
* @param {String} region The box-model region to get the outer quad for.
|
||||
* @return {Object} A quad-like object {p1,p2,p3,p4,bounds}
|
||||
*/
|
||||
_getOuterQuad(region) {
|
||||
const quads = this.currentQuads[region];
|
||||
if (!quads || !quads.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const quad = {
|
||||
p1: { x: Infinity, y: Infinity },
|
||||
p2: { x: -Infinity, y: Infinity },
|
||||
p3: { x: -Infinity, y: -Infinity },
|
||||
p4: { x: Infinity, y: -Infinity },
|
||||
bounds: {
|
||||
bottom: -Infinity,
|
||||
height: 0,
|
||||
left: Infinity,
|
||||
right: -Infinity,
|
||||
top: Infinity,
|
||||
width: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
};
|
||||
|
||||
for (const q of quads) {
|
||||
quad.p1.x = Math.min(quad.p1.x, q.p1.x);
|
||||
quad.p1.y = Math.min(quad.p1.y, q.p1.y);
|
||||
quad.p2.x = Math.max(quad.p2.x, q.p2.x);
|
||||
quad.p2.y = Math.min(quad.p2.y, q.p2.y);
|
||||
quad.p3.x = Math.max(quad.p3.x, q.p3.x);
|
||||
quad.p3.y = Math.max(quad.p3.y, q.p3.y);
|
||||
quad.p4.x = Math.min(quad.p4.x, q.p4.x);
|
||||
quad.p4.y = Math.max(quad.p4.y, q.p4.y);
|
||||
|
||||
quad.bounds.bottom = Math.max(quad.bounds.bottom, q.bounds.bottom);
|
||||
quad.bounds.top = Math.min(quad.bounds.top, q.bounds.top);
|
||||
quad.bounds.left = Math.min(quad.bounds.left, q.bounds.left);
|
||||
quad.bounds.right = Math.max(quad.bounds.right, q.bounds.right);
|
||||
}
|
||||
quad.bounds.x = quad.bounds.left;
|
||||
quad.bounds.y = quad.bounds.top;
|
||||
quad.bounds.width = quad.bounds.right - quad.bounds.left;
|
||||
quad.bounds.height = quad.bounds.bottom - quad.bounds.top;
|
||||
|
||||
return quad;
|
||||
}
|
||||
|
||||
_getBoxPathCoordinates(boxQuad, nextBoxQuad) {
|
||||
const { p1, p2, p3, p4 } = boxQuad;
|
||||
|
||||
let path;
|
||||
if (!nextBoxQuad || !this.options.onlyRegionArea) {
|
||||
// If this is the content box (inner-most box) or if we're not being asked
|
||||
// to highlight only region areas, then draw a simple rectangle.
|
||||
path =
|
||||
"M" +
|
||||
p1.x +
|
||||
"," +
|
||||
p1.y +
|
||||
" " +
|
||||
"L" +
|
||||
p2.x +
|
||||
"," +
|
||||
p2.y +
|
||||
" " +
|
||||
"L" +
|
||||
p3.x +
|
||||
"," +
|
||||
p3.y +
|
||||
" " +
|
||||
"L" +
|
||||
p4.x +
|
||||
"," +
|
||||
p4.y;
|
||||
} else {
|
||||
// Otherwise, just draw the region itself, not a filled rectangle.
|
||||
const { p1: np1, p2: np2, p3: np3, p4: np4 } = nextBoxQuad;
|
||||
path =
|
||||
"M" +
|
||||
p1.x +
|
||||
"," +
|
||||
p1.y +
|
||||
" " +
|
||||
"L" +
|
||||
p2.x +
|
||||
"," +
|
||||
p2.y +
|
||||
" " +
|
||||
"L" +
|
||||
p3.x +
|
||||
"," +
|
||||
p3.y +
|
||||
" " +
|
||||
"L" +
|
||||
p4.x +
|
||||
"," +
|
||||
p4.y +
|
||||
" " +
|
||||
"L" +
|
||||
p1.x +
|
||||
"," +
|
||||
p1.y +
|
||||
" " +
|
||||
"L" +
|
||||
np1.x +
|
||||
"," +
|
||||
np1.y +
|
||||
" " +
|
||||
"L" +
|
||||
np4.x +
|
||||
"," +
|
||||
np4.y +
|
||||
" " +
|
||||
"L" +
|
||||
np3.x +
|
||||
"," +
|
||||
np3.y +
|
||||
" " +
|
||||
"L" +
|
||||
np2.x +
|
||||
"," +
|
||||
np2.y +
|
||||
" " +
|
||||
"L" +
|
||||
np1.x +
|
||||
"," +
|
||||
np1.y;
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
_getOuterBounds() {
|
||||
for (const region of BOX_MODEL_REGIONS) {
|
||||
const quad = this._getOuterQuad(region);
|
||||
|
||||
if (!quad) {
|
||||
// Invisible element such as a script tag.
|
||||
break;
|
||||
}
|
||||
|
||||
const { bottom, height, left, right, top, width, x, y } = quad.bounds;
|
||||
|
||||
if (width > 0 || height > 0) {
|
||||
return { bottom, height, left, right, top, width, x, y };
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
bottom: 0,
|
||||
height: 0,
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
width: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* We only want to show guides for horizontal and vertical edges as this helps
|
||||
* to line them up. This method finds these edges and displays a guide there.
|
||||
* @param {String} region The region around which the guides should be shown.
|
||||
*/
|
||||
_showGuides(region) {
|
||||
const quad = this._getOuterQuad(region);
|
||||
|
||||
if (!quad) {
|
||||
// Invisible element such as a script tag.
|
||||
return;
|
||||
}
|
||||
|
||||
const { p1, p2, p3, p4 } = quad;
|
||||
|
||||
const allX = [p1.x, p2.x, p3.x, p4.x].sort((a, b) => a - b);
|
||||
const allY = [p1.y, p2.y, p3.y, p4.y].sort((a, b) => a - b);
|
||||
const toShowX = [];
|
||||
const toShowY = [];
|
||||
|
||||
for (const arr of [allX, allY]) {
|
||||
for (let i = 0; i < arr.length; i++) {
|
||||
const val = arr[i];
|
||||
|
||||
if (i !== arr.lastIndexOf(val)) {
|
||||
if (arr === allX) {
|
||||
toShowX.push(val);
|
||||
} else {
|
||||
toShowY.push(val);
|
||||
}
|
||||
arr.splice(arr.lastIndexOf(val), 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Move guide into place or hide it if no valid co-ordinate was found.
|
||||
this._updateGuide("top", Math.round(toShowY[0]));
|
||||
this._updateGuide("right", Math.round(toShowX[1]) - 1);
|
||||
this._updateGuide("bottom", Math.round(toShowY[1] - 1));
|
||||
this._updateGuide("left", Math.round(toShowX[0]));
|
||||
}
|
||||
|
||||
_hideGuides() {
|
||||
for (const side of BOX_MODEL_SIDES) {
|
||||
this.getElement("guide-" + side).setAttribute("hidden", "true");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Move a guide to the appropriate position and display it. If no point is
|
||||
* passed then the guide is hidden.
|
||||
*
|
||||
* @param {String} side
|
||||
* The guide to update
|
||||
* @param {Integer} point
|
||||
* x or y co-ordinate. If this is undefined we hide the guide.
|
||||
*/
|
||||
_updateGuide(side, point = -1) {
|
||||
const guide = this.getElement("guide-" + side);
|
||||
|
||||
if (point <= 0) {
|
||||
guide.setAttribute("hidden", "true");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (side === "top" || side === "bottom") {
|
||||
guide.setAttribute("x1", "0");
|
||||
guide.setAttribute("y1", point + "");
|
||||
guide.setAttribute("x2", "100%");
|
||||
guide.setAttribute("y2", point + "");
|
||||
} else {
|
||||
guide.setAttribute("x1", point + "");
|
||||
guide.setAttribute("y1", "0");
|
||||
guide.setAttribute("x2", point + "");
|
||||
guide.setAttribute("y2", "100%");
|
||||
}
|
||||
|
||||
guide.removeAttribute("hidden");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the Infobar to the right place in the highlighter.
|
||||
*/
|
||||
_moveInfobar() {
|
||||
const bounds = this._getOuterBounds();
|
||||
const container = this.getElement("infobar-container");
|
||||
|
||||
moveInfobar(container, bounds, this.iframe.contentWindow);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update node information (displayName#id.class)
|
||||
*/
|
||||
_updateInfobar() {
|
||||
if (!this.nodeData) {
|
||||
return;
|
||||
}
|
||||
|
||||
const {
|
||||
classList,
|
||||
displayName,
|
||||
flexLayoutTextType,
|
||||
gridLayoutTextType,
|
||||
id,
|
||||
pseudos,
|
||||
zoom,
|
||||
} = this.nodeData;
|
||||
|
||||
// We want to display the original `width` and `height`, instead of the ones affected
|
||||
// by any zoom. Since the infobar can be displayed also for text nodes, we can't
|
||||
// access the computed style for that, and this is why we recalculate them here.
|
||||
const quad = this._getOuterQuad("border");
|
||||
if (!quad) {
|
||||
return;
|
||||
}
|
||||
|
||||
const { width, height } = quad.bounds;
|
||||
const dim =
|
||||
parseFloat((width / zoom).toPrecision(6)) +
|
||||
" \u00D7 " +
|
||||
parseFloat((height / zoom).toPrecision(6));
|
||||
|
||||
this.getElement("infobar-tagname").setTextContent(displayName);
|
||||
this.getElement("infobar-id").setTextContent(id);
|
||||
this.getElement("infobar-classes").setTextContent(classList);
|
||||
this.getElement("infobar-pseudo-classes").setTextContent(pseudos);
|
||||
this.getElement("infobar-dimensions").setTextContent(dim);
|
||||
this.getElement("infobar-grid-type").setTextContent(gridLayoutTextType);
|
||||
this.getElement("infobar-flex-type").setTextContent(flexLayoutTextType);
|
||||
|
||||
this._moveInfobar();
|
||||
}
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
_buildMarkup() {
|
||||
const doc = this.win.document;
|
||||
|
||||
const highlighterContainer = doc.createElement("div");
|
||||
highlighterContainer.setAttribute("role", "presentation");
|
||||
highlighterContainer.className = "highlighter-container box-model";
|
||||
|
||||
// Build the root wrapper, used to adapt to the page zoom.
|
||||
const rootWrapper = createNode(this.win, {
|
||||
parent: highlighterContainer,
|
||||
attributes: {
|
||||
id: "root",
|
||||
class: "root",
|
||||
role: "presentation",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
// Building the nodeinfo bar markup
|
||||
|
||||
const infobarContainer = createNode(this.win, {
|
||||
parent: rootWrapper,
|
||||
attributes: {
|
||||
class: "infobar-container",
|
||||
id: "infobar-container",
|
||||
position: "top",
|
||||
hidden: "true",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
const infobar = createNode(this.win, {
|
||||
parent: infobarContainer,
|
||||
attributes: {
|
||||
class: "infobar",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
const texthbox = createNode(this.win, {
|
||||
parent: infobar,
|
||||
attributes: {
|
||||
class: "infobar-text",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-tagname",
|
||||
id: "infobar-tagname",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-id",
|
||||
id: "infobar-id",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-classes",
|
||||
id: "infobar-classes",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-pseudo-classes",
|
||||
id: "infobar-pseudo-classes",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-dimensions",
|
||||
id: "infobar-dimensions",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-grid-type",
|
||||
id: "infobar-grid-type",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
createNode(this.win, {
|
||||
nodeType: "span",
|
||||
parent: texthbox,
|
||||
attributes: {
|
||||
class: "infobar-flex-type",
|
||||
id: "infobar-flex-type",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
// Building the SVG element with its polygons and lines
|
||||
|
||||
const svg = createSVGNode(this.win, {
|
||||
nodeType: "svg",
|
||||
parent: rootWrapper,
|
||||
attributes: {
|
||||
id: "elements",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
hidden: "true",
|
||||
role: "presentation",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
const regions = createSVGNode(this.win, {
|
||||
nodeType: "g",
|
||||
parent: svg,
|
||||
attributes: {
|
||||
class: "regions",
|
||||
role: "presentation",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
|
||||
for (const region of BOX_MODEL_REGIONS) {
|
||||
createSVGNode(this.win, {
|
||||
nodeType: "path",
|
||||
parent: regions,
|
||||
attributes: {
|
||||
class: region,
|
||||
id: region,
|
||||
role: "presentation",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
}
|
||||
|
||||
for (const side of BOX_MODEL_SIDES) {
|
||||
createSVGNode(this.win, {
|
||||
nodeType: "line",
|
||||
parent: svg,
|
||||
attributes: {
|
||||
class: "guide-" + side,
|
||||
id: "guide-" + side,
|
||||
"stroke-width": GUIDE_STROKE_WIDTH,
|
||||
role: "presentation",
|
||||
},
|
||||
prefix: this.ID_CLASS_PREFIX,
|
||||
});
|
||||
}
|
||||
|
||||
return highlighterContainer;
|
||||
}
|
||||
}
|
||||
|
||||
exports.BoxModelHighlighterRenderer = BoxModelHighlighterRenderer;
|
||||
|
||||
/**
|
||||
* Setup function that runs in parent process and sets up the rendering part of the
|
||||
* box model highlighter and the communication channel with the observer part
|
||||
* of the box model highlighter which lives in the content process.
|
||||
*
|
||||
*
|
||||
* @param {Object} options.mm
|
||||
* Message manager that corresponds to the current content tab.
|
||||
* @param {String} options.prefix
|
||||
* Unique prefix for message manager messages.
|
||||
* This is the debugger-server-connection prefix.
|
||||
* @return {Object}
|
||||
* Defines event listeners for when client disconnects or browser gets
|
||||
* swapped.
|
||||
*/
|
||||
function setupParentProcess({ mm, prefix }) {
|
||||
let renderer = new BoxModelHighlighterRenderer(mm, prefix);
|
||||
|
||||
return {
|
||||
onBrowserSwap: newMM => renderer.setMessageManager(newMM, prefix),
|
||||
onDisconnected: () => {
|
||||
renderer.destroy();
|
||||
renderer = null;
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
exports.setupParentProcess = setupParentProcess;
|
@ -101,6 +101,7 @@ class BoxModelHighlighter extends AutoRefreshHighlighter {
|
||||
super(highlighterEnv);
|
||||
|
||||
this.ID_CLASS_PREFIX = "box-model-";
|
||||
|
||||
this.markup = new CanvasFrameAnonymousContentHelper(
|
||||
this.highlighterEnv,
|
||||
this._buildMarkup.bind(this)
|
||||
|
@ -1,202 +0,0 @@
|
||||
/* 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 Services = require("Services");
|
||||
|
||||
const {
|
||||
HighlighterEnvironment,
|
||||
} = require("devtools/server/actors/highlighters");
|
||||
|
||||
const {
|
||||
CanvasFrameAnonymousContentHelper,
|
||||
} = require("devtools/server/actors/highlighters/utils/markup");
|
||||
|
||||
/**
|
||||
* HighlighterRenderer is the base class that implements the rendering surface for a
|
||||
* highlighter in the parent process.
|
||||
*
|
||||
* It injects an iframe in the browser window which hosts the anonymous canvas
|
||||
* frame where the highlighter's markup is generated and manipulated.
|
||||
*
|
||||
* This is the renderer part of a highlighter. It has a counterpart: the observer part of
|
||||
* a highlighter which lives in the content process so it can observe changes to a node's
|
||||
* position and attributes over time. The observer communicates any changes either through
|
||||
* messages (via message manager) or directly to the renderer which updates the
|
||||
* highlighter markup.
|
||||
*
|
||||
* NOTE: When the highlighter is used in the context of the browser toolbox, for example,
|
||||
* when inspecting the browser UI, both observer and renderer live in the parent process
|
||||
* and communication is done by direct reference, not using messages.
|
||||
*
|
||||
* Classes that extend HighlighterRenderer must implement:
|
||||
* - a `typeName` string to identify the highighter type
|
||||
* - a `_buildMarkup()` method to generate the highlighter markup;
|
||||
* - a `render()` method to update the highlighter markup when given new information
|
||||
* about the observed node.
|
||||
*/
|
||||
class HighlighterRenderer {
|
||||
constructor() {
|
||||
// The highlighter's type name. To be implemented by sub classes.
|
||||
this.typeName = "";
|
||||
this.onMessage = this.onMessage.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an HTML iframe in order to use the anonymous canvas frame within it
|
||||
* for hosting and manipulating the highlighter's markup.
|
||||
*
|
||||
* @param {Boolean} isBrowserToolboxHighlighter
|
||||
* Whether the highlighter is used in the context of the
|
||||
* browser toolbox (as opposed to the content toolbox).
|
||||
* When set to true, this will influence where the
|
||||
* iframe is appended so that it overlaps the browser UI
|
||||
* (as opposed to overlapping just the page content).
|
||||
*/
|
||||
init(isBrowserToolboxHighlighter) {
|
||||
// Get a reference to the parent process window.
|
||||
this.win = Services.wm.getMostRecentBrowserWindow();
|
||||
|
||||
const { gBrowser } = this.win;
|
||||
// Get a reference to the selected <browser> element.
|
||||
const browser = gBrowser.selectedBrowser;
|
||||
const browserContainer = gBrowser.getBrowserContainer(browser);
|
||||
|
||||
// The parent node of the iframe depends on the highlighter context:
|
||||
// - browser toolbox: top-level browser window
|
||||
// - content toolbox: host node of the <browser> element for the selected tab
|
||||
const parent = isBrowserToolboxHighlighter
|
||||
? this.win.document.documentElement
|
||||
: browserContainer.querySelector(".browserStack");
|
||||
|
||||
// Grab the host iframe if it was previously created by another highlighter.
|
||||
const iframe = parent.querySelector(
|
||||
`:scope > .devtools-highlighter-renderer`
|
||||
);
|
||||
|
||||
if (iframe) {
|
||||
this.iframe = iframe;
|
||||
this.setupMarkup();
|
||||
} else {
|
||||
this.iframe = this.win.document.createElement("iframe");
|
||||
this.iframe.classList.add("devtools-highlighter-renderer");
|
||||
|
||||
if (isBrowserToolboxHighlighter) {
|
||||
parent.append(this.iframe);
|
||||
} else {
|
||||
// Ensure highlighters are drawn underneath alerts and dialog boxes.
|
||||
parent.querySelector("browser").after(this.iframe);
|
||||
}
|
||||
|
||||
this.iframe.contentWindow.addEventListener(
|
||||
"DOMContentLoaded",
|
||||
this.setupMarkup.bind(this)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate the highlighter markup and insert it into the anoymous canvas frame.
|
||||
*/
|
||||
setupMarkup() {
|
||||
if (!this.iframe || !this.iframe.contentWindow) {
|
||||
throw Error(
|
||||
"The highlighter renderer's host iframe is missing or not yet ready"
|
||||
);
|
||||
}
|
||||
|
||||
this.highlighterEnv = new HighlighterEnvironment();
|
||||
this.highlighterEnv.initFromWindow(this.iframe.contentWindow);
|
||||
|
||||
this.markup = new CanvasFrameAnonymousContentHelper(
|
||||
this.highlighterEnv,
|
||||
this._buildMarkup.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up message manager listener to listen for messages
|
||||
* coming from the from the child process.
|
||||
*
|
||||
* @param {Object} mm
|
||||
* Message manager that corresponds to the current content tab.
|
||||
* @param {String} prefix
|
||||
* Cross-process connection prefix.
|
||||
*/
|
||||
setMessageManager(mm, prefix) {
|
||||
if (this.messageManager === mm) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Message name used to distinguish between messages over the message manager.
|
||||
this._msgName = `debug:${prefix}${this.typeName}`;
|
||||
|
||||
if (this.messageManager) {
|
||||
// If the browser was swapped we need to reset the message manager.
|
||||
const oldMM = this.messageManager;
|
||||
oldMM.removeMessageListener(this._msgName, this.onMessage);
|
||||
}
|
||||
|
||||
this.messageManager = mm;
|
||||
if (mm) {
|
||||
mm.addMessageListener(this._msgName, this.onMessage);
|
||||
}
|
||||
}
|
||||
|
||||
postMessage(topic, data = {}) {
|
||||
this.messageManager.sendAsyncMessage(`${this._msgName}:event`, {
|
||||
topic,
|
||||
data,
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handler for messages coming from the content process.
|
||||
*
|
||||
* @param {Object} msg
|
||||
* Data payload associated with the message.
|
||||
*/
|
||||
onMessage(msg) {
|
||||
const { topic, data } = msg.json;
|
||||
switch (topic) {
|
||||
case "render":
|
||||
this.render(data);
|
||||
break;
|
||||
|
||||
case "destroy":
|
||||
this.destroy();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
// When called, sub classes should update the highlighter.
|
||||
// To be implemented by sub classes.
|
||||
throw new Error(
|
||||
"Highlighter renderer class had to implement render method"
|
||||
);
|
||||
}
|
||||
|
||||
destroy() {
|
||||
if (this.highlighterEnv) {
|
||||
this.highlighterEnv.destroy();
|
||||
this.highlighterEnv = null;
|
||||
}
|
||||
|
||||
if (this.markup) {
|
||||
this.markup.destroy();
|
||||
this.markup = null;
|
||||
}
|
||||
|
||||
if (this.iframe) {
|
||||
this.iframe.remove();
|
||||
this.iframe = null;
|
||||
}
|
||||
|
||||
this.win = null;
|
||||
this.setMessageManager(null);
|
||||
}
|
||||
}
|
||||
exports.HighlighterRenderer = HighlighterRenderer;
|
@ -11,8 +11,6 @@ DIRS += [
|
||||
DevToolsModules(
|
||||
'accessible.js',
|
||||
'auto-refresh.js',
|
||||
'box-model-observer.js',
|
||||
'box-model-renderer.js',
|
||||
'box-model.js',
|
||||
'css-grid.js',
|
||||
'css-transform.js',
|
||||
@ -20,7 +18,6 @@ DevToolsModules(
|
||||
'flexbox.js',
|
||||
'fonts.js',
|
||||
'geometry-editor.js',
|
||||
'highlighter-renderer.js',
|
||||
'measuring-tool.js',
|
||||
'paused-debugger.js',
|
||||
'rulers.js',
|
||||
|
@ -196,21 +196,15 @@ exports.InspectorActor = protocol.ActorClassWithSpec(inspectorSpec, {
|
||||
*
|
||||
* @param {Boolean} autohide Optionally autohide the highlighter after an
|
||||
* element has been picked
|
||||
* @param {Boolean} useNewBoxModelHighlighter Whether to use the new box model
|
||||
* highlighter that has split renderer and observer parts.
|
||||
* @return {HighlighterActor}
|
||||
*/
|
||||
getHighlighter: function(autohide, useNewBoxModelHighlighter) {
|
||||
getHighlighter: function(autohide) {
|
||||
if (this._highlighterPromise) {
|
||||
return this._highlighterPromise;
|
||||
}
|
||||
|
||||
this._highlighterPromise = this.getWalker().then(walker => {
|
||||
const highlighter = HighlighterActor(
|
||||
this,
|
||||
autohide,
|
||||
useNewBoxModelHighlighter
|
||||
);
|
||||
const highlighter = HighlighterActor(this, autohide);
|
||||
this.manage(highlighter);
|
||||
return highlighter;
|
||||
});
|
||||
|
@ -30,9 +30,7 @@ const TELEMETRY_EYEDROPPER_OPENED_MENU =
|
||||
const SHOW_ALL_ANONYMOUS_CONTENT_PREF =
|
||||
"devtools.inspector.showAllAnonymousContent";
|
||||
const SHOW_UA_SHADOW_ROOTS_PREF = "devtools.inspector.showUserAgentShadowRoots";
|
||||
const FISSION_ENABLED_PREF = "devtools.browsertoolbox.fission";
|
||||
const USE_NEW_BOX_MODEL_HIGHLIGHTER_PREF =
|
||||
"devtools.inspector.use-new-box-model-highlighter";
|
||||
const FISSION_ENABLED = "devtools.browsertoolbox.fission";
|
||||
|
||||
const telemetry = new Telemetry();
|
||||
|
||||
@ -533,10 +531,7 @@ class InspectorFront extends FrontClassWithSpec(inspectorSpec) {
|
||||
|
||||
async _getHighlighter() {
|
||||
const autohide = !flags.testing;
|
||||
this.highlighter = await this.getHighlighter(
|
||||
autohide,
|
||||
Services.prefs.getBoolPref(USE_NEW_BOX_MODEL_HIGHLIGHTER_PREF)
|
||||
);
|
||||
this.highlighter = await this.getHighlighter(autohide);
|
||||
}
|
||||
|
||||
hasHighlighter(type) {
|
||||
@ -609,7 +604,7 @@ class InspectorFront extends FrontClassWithSpec(inspectorSpec) {
|
||||
* @return {Array} The list of InspectorFront instances.
|
||||
*/
|
||||
async getChildInspectors() {
|
||||
const fissionEnabled = Services.prefs.getBoolPref(FISSION_ENABLED_PREF);
|
||||
const fissionEnabled = Services.prefs.getBoolPref(FISSION_ENABLED);
|
||||
const childInspectors = [];
|
||||
const target = this.targetFront;
|
||||
// this line can be removed when we are ready for fission frames
|
||||
|
@ -154,12 +154,7 @@ exports.getFrameOffsets = getFrameOffsets;
|
||||
* An array of objects that have the same structure as quads returned by
|
||||
* getBoxQuads. An empty array if the node has no quads or is invalid.
|
||||
*/
|
||||
function getAdjustedQuads(
|
||||
boundaryWindow,
|
||||
node,
|
||||
region,
|
||||
{ ignoreZoom, ignoreScroll } = {}
|
||||
) {
|
||||
function getAdjustedQuads(boundaryWindow, node, region, { ignoreZoom } = {}) {
|
||||
if (!node || !node.getBoxQuads) {
|
||||
return [];
|
||||
}
|
||||
@ -175,9 +170,7 @@ function getAdjustedQuads(
|
||||
}
|
||||
|
||||
const scale = ignoreZoom ? 1 : getCurrentZoom(node);
|
||||
const { scrollX, scrollY } = ignoreScroll
|
||||
? { scrollX: 0, scrollY: 0 }
|
||||
: boundaryWindow;
|
||||
const { scrollX, scrollY } = boundaryWindow;
|
||||
|
||||
const xOffset = scrollX * scale;
|
||||
const yOffset = scrollY * scale;
|
||||
|
@ -406,7 +406,6 @@ const inspectorSpec = generateActorSpec({
|
||||
getHighlighter: {
|
||||
request: {
|
||||
autohide: Arg(0, "boolean"),
|
||||
useNewBoxModelHighlighter: Arg(1, "boolean"),
|
||||
},
|
||||
response: {
|
||||
highligter: RetVal("highlighter"),
|
||||
|
Loading…
Reference in New Issue
Block a user