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:
Daniel Varga 2019-10-10 18:51:17 +03:00
parent 996128fd41
commit 6a27b47313
13 changed files with 10 additions and 1337 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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,

View File

@ -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 }
);
}
},

View File

@ -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;

View File

@ -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;

View File

@ -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)

View File

@ -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;

View File

@ -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',

View File

@ -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;
});

View File

@ -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

View File

@ -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;

View File

@ -406,7 +406,6 @@ const inspectorSpec = generateActorSpec({
getHighlighter: {
request: {
autohide: Arg(0, "boolean"),
useNewBoxModelHighlighter: Arg(1, "boolean"),
},
response: {
highligter: RetVal("highlighter"),