зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1132475 - Refactor LayoutHelpers.jsm to avoid dependencies. r=pbrosset
--HG-- extra : histedit_source : 54d69138146487b378e62bc2cfeb520a85956f43
This commit is contained in:
Родитель
66e694be8a
Коммит
694730e132
|
@ -6,9 +6,10 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const {Cu, Ci} = require("chrome");
|
||||
const { Cu, Ci } = require("chrome");
|
||||
const { getRootBindingParent } = require("devtools/toolkit/layout/utils");
|
||||
|
||||
let EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
let LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
|
||||
/**
|
||||
* API
|
||||
|
@ -228,7 +229,7 @@ Selection.prototype = {
|
|||
let doc = this.document;
|
||||
if (doc && doc.defaultView) {
|
||||
let docEl = doc.documentElement;
|
||||
let bindingParent = LayoutHelpers.getRootBindingParent(rawNode);
|
||||
let bindingParent = getRootBindingParent(rawNode);
|
||||
|
||||
if (docEl.contains(bindingParent)) {
|
||||
return true;
|
||||
|
|
|
@ -9,7 +9,7 @@ Bug 1014547 - CSS transforms highlighter
|
|||
Test that the highlighter elements created have the right size and coordinates.
|
||||
|
||||
Note that instead of hard-coding values here, the assertions are made by
|
||||
comparing with the result of LayoutHelpers.getAdjustedQuads.
|
||||
comparing with the result of getAdjustedQuads.
|
||||
|
||||
There's a separate test for checking that getAdjustedQuads actually returns
|
||||
sensible values
|
||||
|
|
|
@ -32,7 +32,7 @@ const {setTimeout, clearTimeout, setInterval, clearInterval} = require("sdk/time
|
|||
const {parseAttribute} = require("devtools/shared/node-attribute-parser");
|
||||
const ELLIPSIS = Services.prefs.getComplexValue("intl.ellipsis", Ci.nsIPrefLocalizedString).data;
|
||||
const {Task} = require("resource://gre/modules/Task.jsm");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const {scrollIntoViewIfNeeded} = require("devtools/toolkit/layout/utils");
|
||||
|
||||
Cu.import("resource://gre/modules/devtools/Templater.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
@ -75,8 +75,6 @@ function MarkupView(aInspector, aFrame, aControllerWindow) {
|
|||
this._elt = this.doc.querySelector("#root");
|
||||
this.htmlEditor = new HTMLEditor(this.doc);
|
||||
|
||||
this.layoutHelpers = new LayoutHelpers(this.doc.defaultView);
|
||||
|
||||
try {
|
||||
this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
|
||||
} catch(ex) {
|
||||
|
@ -940,7 +938,7 @@ MarkupView.prototype = {
|
|||
}
|
||||
return this._ensureVisible(aNode);
|
||||
}).then(() => {
|
||||
this.layoutHelpers.scrollIntoViewIfNeeded(this.getContainer(aNode).editor.elt, centered);
|
||||
scrollIntoViewIfNeeded(this.getContainer(aNode).editor.elt, centered);
|
||||
}, e => {
|
||||
// Only report this rejection as an error if the panel hasn't been
|
||||
// destroyed in the meantime.
|
||||
|
|
|
@ -1,45 +1,42 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that LayoutHelpers.getAdjustedQuads works properly in a variety of use
|
||||
// cases including iframes, scroll and zoom
|
||||
// Tests getAdjustedQuads works properly in a variety of use cases including
|
||||
// iframes, scroll and zoom
|
||||
|
||||
const {utils: Cu} = Components;
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
let {getAdjustedQuads} = require("devtools/toolkit/layout/utils");
|
||||
|
||||
const TEST_URI = TEST_URI_ROOT + "browser_layoutHelpers-getBoxQuads.html";
|
||||
|
||||
function test() {
|
||||
addTab(TEST_URI, function(browser, tab) {
|
||||
let doc = browser.contentDocument;
|
||||
let win = doc.defaultView;
|
||||
|
||||
info("Creating a new LayoutHelpers instance for the test window");
|
||||
let helper = new LayoutHelpers(win);
|
||||
ok(helper.getAdjustedQuads, "getAdjustedQuads is defined");
|
||||
ok(typeof getAdjustedQuads === "function", "getAdjustedQuads is defined");
|
||||
|
||||
info("Running tests");
|
||||
|
||||
returnsTheRightDataStructure(doc, helper);
|
||||
isEmptyForMissingNode(doc, helper);
|
||||
isEmptyForHiddenNodes(doc, helper);
|
||||
defaultsToBorderBoxIfNoneProvided(doc, helper);
|
||||
returnsLikeGetBoxQuadsInSimpleCase(doc, helper);
|
||||
takesIframesOffsetsIntoAccount(doc, helper);
|
||||
takesScrollingIntoAccount(doc, helper);
|
||||
takesZoomIntoAccount(doc, helper);
|
||||
returnsMultipleItemsForWrappingInlineElements(doc, helper);
|
||||
returnsTheRightDataStructure(doc);
|
||||
isEmptyForMissingNode(doc);
|
||||
isEmptyForHiddenNodes(doc);
|
||||
defaultsToBorderBoxIfNoneProvided(doc);
|
||||
returnsLikeGetBoxQuadsInSimpleCase(doc);
|
||||
takesIframesOffsetsIntoAccount(doc);
|
||||
takesScrollingIntoAccount(doc);
|
||||
takesZoomIntoAccount(doc);
|
||||
returnsMultipleItemsForWrappingInlineElements(doc);
|
||||
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
function returnsTheRightDataStructure(doc, helper) {
|
||||
function returnsTheRightDataStructure(doc) {
|
||||
info("Checks that the returned data contains bounds and 4 points");
|
||||
|
||||
let node = doc.querySelector("body");
|
||||
let [res] = helper.getAdjustedQuads(node, "content");
|
||||
let [res] = getAdjustedQuads(doc.defaultView, node, "content");
|
||||
|
||||
ok("bounds" in res, "The returned data has a bounds property");
|
||||
ok("p1" in res, "The returned data has a p1 property");
|
||||
|
@ -59,33 +56,33 @@ function returnsTheRightDataStructure(doc, helper) {
|
|||
}
|
||||
}
|
||||
|
||||
function isEmptyForMissingNode(doc, helper) {
|
||||
function isEmptyForMissingNode(doc) {
|
||||
info("Checks that null is returned for invalid nodes");
|
||||
|
||||
for (let input of [null, undefined, "", 0]) {
|
||||
is(helper.getAdjustedQuads(input).length, 0, "A 0-length array is returned" +
|
||||
is(getAdjustedQuads(doc.defaultView, input).length, 0, "A 0-length array is returned" +
|
||||
"for input " + input);
|
||||
}
|
||||
}
|
||||
|
||||
function isEmptyForHiddenNodes(doc, helper) {
|
||||
function isEmptyForHiddenNodes(doc) {
|
||||
info("Checks that null is returned for nodes that aren't rendered");
|
||||
|
||||
let style = doc.querySelector("#styles");
|
||||
is(helper.getAdjustedQuads(style).length, 0,
|
||||
is(getAdjustedQuads(doc.defaultView, style).length, 0,
|
||||
"null is returned for a <style> node");
|
||||
|
||||
let hidden = doc.querySelector("#hidden-node");
|
||||
is(helper.getAdjustedQuads(hidden).length, 0,
|
||||
is(getAdjustedQuads(doc.defaultView, hidden).length, 0,
|
||||
"null is returned for a hidden node");
|
||||
}
|
||||
|
||||
function defaultsToBorderBoxIfNoneProvided(doc, helper) {
|
||||
function defaultsToBorderBoxIfNoneProvided(doc) {
|
||||
info("Checks that if no boxtype is passed, then border is the default one");
|
||||
|
||||
let node = doc.querySelector("#simple-node-with-margin-padding-border");
|
||||
let [withBoxType] = helper.getAdjustedQuads(node, "border");
|
||||
let [withoutBoxType] = helper.getAdjustedQuads(node);
|
||||
let [withBoxType] = getAdjustedQuads(doc.defaultView, node, "border");
|
||||
let [withoutBoxType] = getAdjustedQuads(doc.defaultView, node);
|
||||
|
||||
for (let boundProp of
|
||||
["bottom", "top", "right", "left", "width", "height", "x", "y"]) {
|
||||
|
@ -102,7 +99,7 @@ function defaultsToBorderBoxIfNoneProvided(doc, helper) {
|
|||
}
|
||||
}
|
||||
|
||||
function returnsLikeGetBoxQuadsInSimpleCase(doc, helper) {
|
||||
function returnsLikeGetBoxQuadsInSimpleCase(doc) {
|
||||
info("Checks that for an element in the main frame, without scroll nor zoom" +
|
||||
"that the returned value is similar to the returned value of getBoxQuads");
|
||||
|
||||
|
@ -112,7 +109,7 @@ function returnsLikeGetBoxQuadsInSimpleCase(doc, helper) {
|
|||
let expected = node.getBoxQuads({
|
||||
box: region
|
||||
})[0];
|
||||
let [actual] = helper.getAdjustedQuads(node, region);
|
||||
let [actual] = getAdjustedQuads(doc.defaultView, node, region);
|
||||
|
||||
for (let boundProp of
|
||||
["bottom", "top", "right", "left", "width", "height", "x", "y"]) {
|
||||
|
@ -131,7 +128,7 @@ function returnsLikeGetBoxQuadsInSimpleCase(doc, helper) {
|
|||
}
|
||||
}
|
||||
|
||||
function takesIframesOffsetsIntoAccount(doc, helper) {
|
||||
function takesIframesOffsetsIntoAccount(doc) {
|
||||
info("Checks that the quad returned for a node inside iframes that have " +
|
||||
"margins takes those offsets into account");
|
||||
|
||||
|
@ -139,7 +136,7 @@ function takesIframesOffsetsIntoAccount(doc, helper) {
|
|||
let subIframe = rootIframe.contentDocument.querySelector("iframe");
|
||||
let innerNode = subIframe.contentDocument.querySelector("#inner-node");
|
||||
|
||||
let [quad] = helper.getAdjustedQuads(innerNode, "content");
|
||||
let [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
|
||||
|
||||
//rootIframe margin + subIframe margin + node margin + node border + node padding
|
||||
let p1x = 10 + 10 + 10 + 10 + 10;
|
||||
|
@ -150,7 +147,7 @@ function takesIframesOffsetsIntoAccount(doc, helper) {
|
|||
is(quad.p2.x, p2x, "The inner node's p2 x position is correct");
|
||||
}
|
||||
|
||||
function takesScrollingIntoAccount(doc, helper) {
|
||||
function takesScrollingIntoAccount(doc) {
|
||||
info("Checks that the quad returned for a node inside multiple scrolled " +
|
||||
"containers takes the scroll values into account");
|
||||
|
||||
|
@ -164,7 +161,7 @@ function takesScrollingIntoAccount(doc, helper) {
|
|||
subScrolledNode.scrollTop = 200;
|
||||
let innerNode = doc.querySelector("#inner-scrolled-node");
|
||||
|
||||
let [quad] = helper.getAdjustedQuads(innerNode, "content");
|
||||
let [quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
|
||||
is(quad.p1.x, 0, "p1.x of the scrolled node is correct after scrolling down");
|
||||
is(quad.p1.y, -300, "p1.y of the scrolled node is correct after scrolling down");
|
||||
|
||||
|
@ -172,12 +169,12 @@ function takesScrollingIntoAccount(doc, helper) {
|
|||
scrolledNode.scrollTop = 0;
|
||||
subScrolledNode.scrollTop = 0;
|
||||
|
||||
[quad] = helper.getAdjustedQuads(innerNode, "content");
|
||||
[quad] = getAdjustedQuads(doc.defaultView, innerNode, "content");
|
||||
is(quad.p1.x, 0, "p1.x of the scrolled node is correct after scrolling up");
|
||||
is(quad.p1.y, 0, "p1.y of the scrolled node is correct after scrolling up");
|
||||
}
|
||||
|
||||
function takesZoomIntoAccount(doc, helper) {
|
||||
function takesZoomIntoAccount(doc) {
|
||||
info("Checks that if the page is zoomed in/out, the quad returned is correct");
|
||||
|
||||
// Hard-coding coordinates in this zoom test is a bad idea as it can vary
|
||||
|
@ -185,11 +182,11 @@ function takesZoomIntoAccount(doc, helper) {
|
|||
// bigger quad and zooming out produces a smaller quad
|
||||
|
||||
let node = doc.querySelector("#simple-node-with-margin-padding-border");
|
||||
let [defaultQuad] = helper.getAdjustedQuads(node);
|
||||
let [defaultQuad] = getAdjustedQuads(doc.defaultView, node);
|
||||
|
||||
info("Zoom in");
|
||||
window.FullZoom.enlarge();
|
||||
let [zoomedInQuad] = helper.getAdjustedQuads(node);
|
||||
let [zoomedInQuad] = getAdjustedQuads(doc.defaultView, node);
|
||||
|
||||
ok(zoomedInQuad.bounds.width > defaultQuad.bounds.width,
|
||||
"The zoomed in quad is bigger than the default one");
|
||||
|
@ -199,7 +196,7 @@ function takesZoomIntoAccount(doc, helper) {
|
|||
info("Zoom out");
|
||||
window.FullZoom.reset();
|
||||
window.FullZoom.reduce();
|
||||
let [zoomedOutQuad] = helper.getAdjustedQuads(node);
|
||||
let [zoomedOutQuad] = getAdjustedQuads(doc.defaultView, node);
|
||||
|
||||
ok(zoomedOutQuad.bounds.width < defaultQuad.bounds.width,
|
||||
"The zoomed out quad is smaller than the default one");
|
||||
|
@ -209,11 +206,11 @@ function takesZoomIntoAccount(doc, helper) {
|
|||
window.FullZoom.reset();
|
||||
}
|
||||
|
||||
function returnsMultipleItemsForWrappingInlineElements(doc, helper) {
|
||||
function returnsMultipleItemsForWrappingInlineElements(doc) {
|
||||
info("Checks that several quads are returned for inline elements that span line-breaks");
|
||||
|
||||
let node = doc.querySelector("#inline");
|
||||
let quads = helper.getAdjustedQuads(node, "content");
|
||||
let quads = getAdjustedQuads(doc.defaultView, node, "content");
|
||||
// At least 3 because of the 2 <br />, maybe more depending on the window size.
|
||||
ok(quads.length >= 3, "Multiple quads were returned");
|
||||
|
||||
|
|
|
@ -2,8 +2,7 @@
|
|||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Tests that scrollIntoViewIfNeeded works properly.
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
|
||||
let {scrollIntoViewIfNeeded} = require("devtools/toolkit/layout/utils");
|
||||
|
||||
const TEST_URI = TEST_URI_ROOT + "browser_layoutHelpers.html";
|
||||
|
||||
|
@ -14,7 +13,6 @@ add_task(function*() {
|
|||
});
|
||||
|
||||
function runTest(win) {
|
||||
let lh = new LayoutHelpers(win);
|
||||
let some = win.document.getElementById('some');
|
||||
|
||||
some.style.top = win.innerHeight + 'px';
|
||||
|
@ -24,35 +22,35 @@ function runTest(win) {
|
|||
|
||||
let xPos = Math.floor(win.innerWidth / 2);
|
||||
win.scroll(xPos, win.innerHeight + 2); // Above the viewport.
|
||||
lh.scrollIntoViewIfNeeded(some);
|
||||
scrollIntoViewIfNeeded(some);
|
||||
is(win.scrollY, Math.floor(win.innerHeight / 2) + 1,
|
||||
'Element completely hidden above should appear centered.');
|
||||
is(win.scrollX, xPos,
|
||||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, win.innerHeight + 1); // On the top edge.
|
||||
lh.scrollIntoViewIfNeeded(some);
|
||||
scrollIntoViewIfNeeded(some);
|
||||
is(win.scrollY, win.innerHeight,
|
||||
'Element partially visible above should appear above.');
|
||||
is(win.scrollX, xPos,
|
||||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, 0); // Just below the viewport.
|
||||
lh.scrollIntoViewIfNeeded(some);
|
||||
scrollIntoViewIfNeeded(some);
|
||||
is(win.scrollY, Math.floor(win.innerHeight / 2) + 1,
|
||||
'Element completely hidden below should appear centered.');
|
||||
is(win.scrollX, xPos,
|
||||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, 1); // On the bottom edge.
|
||||
lh.scrollIntoViewIfNeeded(some);
|
||||
scrollIntoViewIfNeeded(some);
|
||||
is(win.scrollY, 2,
|
||||
'Element partially visible below should appear below.');
|
||||
is(win.scrollX, xPos,
|
||||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, win.innerHeight + 2); // Above the viewport.
|
||||
lh.scrollIntoViewIfNeeded(some, false);
|
||||
scrollIntoViewIfNeeded(some, false);
|
||||
is(win.scrollY, win.innerHeight,
|
||||
'Element completely hidden above should appear above ' +
|
||||
'if parameter is false.');
|
||||
|
@ -60,7 +58,7 @@ function runTest(win) {
|
|||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, win.innerHeight + 1); // On the top edge.
|
||||
lh.scrollIntoViewIfNeeded(some, false);
|
||||
scrollIntoViewIfNeeded(some, false);
|
||||
is(win.scrollY, win.innerHeight,
|
||||
'Element partially visible above should appear above ' +
|
||||
'if parameter is false.');
|
||||
|
@ -68,7 +66,7 @@ function runTest(win) {
|
|||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, 0); // Below the viewport.
|
||||
lh.scrollIntoViewIfNeeded(some, false);
|
||||
scrollIntoViewIfNeeded(some, false);
|
||||
is(win.scrollY, 2,
|
||||
'Element completely hidden below should appear below ' +
|
||||
'if parameter is false.');
|
||||
|
@ -76,7 +74,7 @@ function runTest(win) {
|
|||
'scrollX position has not changed.');
|
||||
|
||||
win.scroll(win.innerWidth / 2, 1); // On the bottom edge.
|
||||
lh.scrollIntoViewIfNeeded(some, false);
|
||||
scrollIntoViewIfNeeded(some, false);
|
||||
is(win.scrollY, 2,
|
||||
'Element partially visible below should appear below ' +
|
||||
'if parameter is false.');
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
// A helper actor for brower/devtools/inspector tests.
|
||||
|
||||
let { Cc, Ci, Cu, Cr } = require("chrome");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const {getElementFromPoint, getAdjustedQuads} = require("devtools/toolkit/layout/utils");
|
||||
const promise = require("promise");
|
||||
const {Task} = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
let DOMUtils = Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
|
||||
|
@ -239,8 +239,7 @@ const TestActor = exports.TestActor = protocol.ActorClass({
|
|||
}),
|
||||
|
||||
assertElementAtPoint: protocol.method(function (x, y, selector) {
|
||||
let helper = new LayoutHelpers(this.content);
|
||||
let elementAtPoint = helper.getElementFromPoint(this.content.document, x, y);
|
||||
let elementAtPoint = getElementFromPoint(this.content.document, x, y);
|
||||
if (!elementAtPoint) {
|
||||
throw new Error("Unable to find element at (" + x + ", " + y + ")");
|
||||
}
|
||||
|
@ -266,10 +265,9 @@ const TestActor = exports.TestActor = protocol.ActorClass({
|
|||
*/
|
||||
getAllAdjustedQuads: protocol.method(function(selector) {
|
||||
let regions = {};
|
||||
let helper = new LayoutHelpers(this.content);
|
||||
let node = this._querySelector(selector);
|
||||
for (let boxType of ["content", "padding", "border", "margin"]) {
|
||||
regions[boxType] = helper.getAdjustedQuads(node, boxType);
|
||||
regions[boxType] = getAdjustedQuads(this.content, node, boxType);
|
||||
}
|
||||
|
||||
return regions;
|
||||
|
|
|
@ -7,7 +7,7 @@ const { Cc, Ci, Cu, Cr } = require("chrome");
|
|||
|
||||
const { Task } = Cu.import("resource://gre/modules/Task.jsm", {});
|
||||
const { Heritage, setNamedTimeout, clearNamedTimeout } = require("resource:///modules/devtools/ViewHelpers.jsm");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const { getCurrentZoom } = require("devtools/toolkit/layout/utils");
|
||||
|
||||
loader.lazyRequireGetter(this, "promise");
|
||||
loader.lazyRequireGetter(this, "EventEmitter",
|
||||
|
@ -925,7 +925,7 @@ AbstractCanvasGraph.prototype = {
|
|||
|
||||
// The coordinates need to be modified with the current zoom level
|
||||
// to prevent them from being wrong.
|
||||
let zoom = LayoutHelpers.getCurrentZoom(this._canvas);
|
||||
let zoom = getCurrentZoom(this._canvas);
|
||||
mouseX /= zoom;
|
||||
mouseY /= zoom;
|
||||
|
||||
|
|
|
@ -55,11 +55,11 @@ function test() {
|
|||
iframe.contentWindow.innerHeight,
|
||||
"The content window height wasn't calculated correctly.");
|
||||
|
||||
let lh = new LayoutHelpers(gBrowser.contentWindow);
|
||||
let nodeCoordinates = lh.getRect(
|
||||
let nodeCoordinates = getRect(
|
||||
gBrowser.contentWindow,
|
||||
iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
|
||||
|
||||
let frameOffset = lh.getIframeContentOffset(iframe);
|
||||
let frameOffset = getIframeContentOffset(iframe);
|
||||
let frameRect = iframe.getBoundingClientRect();
|
||||
|
||||
is(nodeCoordinates.top, frameRect.top + frameOffset[0] + 98,
|
||||
|
|
|
@ -104,11 +104,11 @@ function test() {
|
|||
iframe.contentWindow.innerHeight,
|
||||
"The content window height wasn't calculated correctly.");
|
||||
|
||||
let lh = new LayoutHelpers(gBrowser.contentWindow);
|
||||
let nodeCoordinates = lh.getRect(
|
||||
let nodeCoordinates = getRect(
|
||||
gBrowser.contentWindow,
|
||||
iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
|
||||
|
||||
let frameOffset = lh.getIframeContentOffset(iframe);
|
||||
let frameOffset = getIframeContentOffset(iframe);
|
||||
let frameRect = iframe.getBoundingClientRect();
|
||||
|
||||
is(nodeCoordinates.top, frameRect.top + frameOffset[0],
|
||||
|
|
|
@ -9,7 +9,7 @@ let {EPSILON, TiltMath, vec3, mat3, mat4, quat4} = require("devtools/tilt/tilt-m
|
|||
let TiltUtils = require("devtools/tilt/tilt-utils");
|
||||
let {TiltVisualizer} = require("devtools/tilt/tilt-visualizer");
|
||||
let DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
let LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
let {getRect, getIframeContentOffset} = require("devtools/toolkit/layout/utils");
|
||||
|
||||
|
||||
const DEFAULT_HTML = "data:text/html," +
|
||||
|
|
|
@ -6,10 +6,10 @@
|
|||
"use strict";
|
||||
|
||||
const {Cc, Ci, Cu} = require("chrome");
|
||||
const {getRect} = require("devtools/toolkit/layout/utils");
|
||||
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
let LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
|
||||
const STACK_THICKNESS = 15;
|
||||
|
||||
|
@ -405,9 +405,8 @@ TiltUtils.DOM = {
|
|||
*/
|
||||
getNodePosition: function TUD_getNodePosition(aContentWindow, aNode,
|
||||
aParentPosition) {
|
||||
let lh = new LayoutHelpers(aContentWindow);
|
||||
// get the x, y, width and height coordinates of the node
|
||||
let coord = lh.getRect(aNode, aContentWindow);
|
||||
let coord = getRect(aContentWindow, aNode, aContentWindow);
|
||||
if (!coord) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
@ -3366,6 +3366,25 @@ nsDocShell::GetSameTypeRootTreeItem(nsIDocShellTreeItem** aRootTreeItem)
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDocShell::GetSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries(nsIDocShell ** aRootTreeItem)
|
||||
{
|
||||
NS_ENSURE_ARG_POINTER(aRootTreeItem);
|
||||
*aRootTreeItem = static_cast<nsIDocShell *>(this);
|
||||
|
||||
nsCOMPtr<nsIDocShell> parent;
|
||||
NS_ENSURE_SUCCESS(GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)),
|
||||
NS_ERROR_FAILURE);
|
||||
while (parent) {
|
||||
*aRootTreeItem = parent;
|
||||
NS_ENSURE_SUCCESS((*aRootTreeItem)->
|
||||
GetSameTypeParentIgnoreBrowserAndAppBoundaries(getter_AddRefs(parent)),
|
||||
NS_ERROR_FAILURE);
|
||||
}
|
||||
NS_ADDREF(*aRootTreeItem);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
/* static */
|
||||
bool
|
||||
nsDocShell::CanAccessItem(nsIDocShellTreeItem* aTargetItem,
|
||||
|
|
|
@ -46,7 +46,7 @@ interface nsITabParent;
|
|||
|
||||
typedef unsigned long nsLoadFlags;
|
||||
|
||||
[scriptable, builtinclass, uuid(35a26f70-dbb9-450d-b634-cd0bbb9b8e13)]
|
||||
[scriptable, builtinclass, uuid(e534b6ee-c35d-4ee2-acce-1bcd08a91230)]
|
||||
interface nsIDocShell : nsIDocShellTreeItem
|
||||
{
|
||||
/**
|
||||
|
@ -841,6 +841,12 @@ interface nsIDocShell : nsIDocShellTreeItem
|
|||
*/
|
||||
nsIDocShell getSameTypeParentIgnoreBrowserAndAppBoundaries();
|
||||
|
||||
/**
|
||||
* Like nsIDocShellTreeItem::GetSameTypeRootTreeItem, except this ignores
|
||||
* <iframe mozbrowser> and <iframe mozapp> boundaries.
|
||||
*/
|
||||
nsIDocShell getSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries();
|
||||
|
||||
/**
|
||||
* True iff asynchronous panning and zooming is enabled for this
|
||||
* docshell.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
const { Cc, Ci, Cu } = require("chrome");
|
||||
const l10n = require("gcli/l10n");
|
||||
const { Services } = require("resource://gre/modules/Services.jsm");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const { getRect } = require("devtools/toolkit/layout/utils");
|
||||
|
||||
loader.lazyImporter(this, "Downloads", "resource://gre/modules/Downloads.jsm");
|
||||
loader.lazyImporter(this, "Task", "resource://gre/modules/Task.jsm");
|
||||
|
@ -274,8 +274,7 @@ function createScreenshotData(document, args) {
|
|||
height = window.innerHeight + window.scrollMaxY;
|
||||
}
|
||||
else if (args.selector) {
|
||||
const lh = new LayoutHelpers(window);
|
||||
({ top, left, width, height } = lh.getRect(args.selector, window));
|
||||
({ top, left, width, height } = getRect(window, args.selector, window));
|
||||
}
|
||||
else {
|
||||
left = window.scrollX;
|
||||
|
|
|
@ -1,584 +0,0 @@
|
|||
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||
/* 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/. */
|
||||
|
||||
let {Ci} = require("chrome")
|
||||
|
||||
let LayoutHelpers = function(aTopLevelWindow) {
|
||||
this._topDocShell = aTopLevelWindow.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
};
|
||||
|
||||
module.exports = LayoutHelpers;
|
||||
|
||||
LayoutHelpers.prototype = {
|
||||
|
||||
/**
|
||||
* Get box quads adjusted for iframes and zoom level.
|
||||
* @param {DOMNode} node The node for which we are to get the box model region
|
||||
* quads.
|
||||
* @param {String} region The box model region to return: "content",
|
||||
* "padding", "border" or "margin".
|
||||
* @return {Array} 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.
|
||||
*/
|
||||
getAdjustedQuads: function(node, region) {
|
||||
if (!node || !node.getBoxQuads) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let quads = node.getBoxQuads({
|
||||
box: region
|
||||
});
|
||||
|
||||
if (!quads.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let [xOffset, yOffset] = this.getFrameOffsets(node);
|
||||
let scale = LayoutHelpers.getCurrentZoom(node);
|
||||
|
||||
let adjustedQuads = [];
|
||||
for (let quad of quads) {
|
||||
adjustedQuads.push({
|
||||
p1: {
|
||||
w: quad.p1.w * scale,
|
||||
x: quad.p1.x * scale + xOffset,
|
||||
y: quad.p1.y * scale + yOffset,
|
||||
z: quad.p1.z * scale
|
||||
},
|
||||
p2: {
|
||||
w: quad.p2.w * scale,
|
||||
x: quad.p2.x * scale + xOffset,
|
||||
y: quad.p2.y * scale + yOffset,
|
||||
z: quad.p2.z * scale
|
||||
},
|
||||
p3: {
|
||||
w: quad.p3.w * scale,
|
||||
x: quad.p3.x * scale + xOffset,
|
||||
y: quad.p3.y * scale + yOffset,
|
||||
z: quad.p3.z * scale
|
||||
},
|
||||
p4: {
|
||||
w: quad.p4.w * scale,
|
||||
x: quad.p4.x * scale + xOffset,
|
||||
y: quad.p4.y * scale + yOffset,
|
||||
z: quad.p4.z * scale
|
||||
},
|
||||
bounds: {
|
||||
bottom: quad.bounds.bottom * scale + yOffset,
|
||||
height: quad.bounds.height * scale,
|
||||
left: quad.bounds.left * scale + xOffset,
|
||||
right: quad.bounds.right * scale + xOffset,
|
||||
top: quad.bounds.top * scale + yOffset,
|
||||
width: quad.bounds.width * scale,
|
||||
x: quad.bounds.x * scale + xOffset,
|
||||
y: quad.bounds.y * scale + yOffset
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return adjustedQuads;
|
||||
},
|
||||
|
||||
/**
|
||||
* Compute the absolute position and the dimensions of a node, relativalely
|
||||
* to the root window.
|
||||
*
|
||||
* @param {DOMNode} aNode
|
||||
* a DOM element to get the bounds for
|
||||
* @param {DOMWindow} aContentWindow
|
||||
* the content window holding the node
|
||||
* @return {Object}
|
||||
* A rect object with the {top, left, width, height} properties
|
||||
*/
|
||||
getRect: function(aNode, aContentWindow) {
|
||||
let frameWin = aNode.ownerDocument.defaultView;
|
||||
let clientRect = aNode.getBoundingClientRect();
|
||||
|
||||
// Go up in the tree of frames to determine the correct rectangle.
|
||||
// clientRect is read-only, we need to be able to change properties.
|
||||
let rect = {top: clientRect.top + aContentWindow.pageYOffset,
|
||||
left: clientRect.left + aContentWindow.pageXOffset,
|
||||
width: clientRect.width,
|
||||
height: clientRect.height};
|
||||
|
||||
// We iterate through all the parent windows.
|
||||
while (true) {
|
||||
// Are we in the top-level window?
|
||||
if (this.isTopLevelWindow(frameWin)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let frameElement = this.getFrameElement(frameWin);
|
||||
if (!frameElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We are in an iframe.
|
||||
// We take into account the parent iframe position and its
|
||||
// offset (borders and padding).
|
||||
let frameRect = frameElement.getBoundingClientRect();
|
||||
|
||||
let [offsetTop, offsetLeft] =
|
||||
this.getIframeContentOffset(frameElement);
|
||||
|
||||
rect.top += frameRect.top + offsetTop;
|
||||
rect.left += frameRect.left + offsetLeft;
|
||||
|
||||
frameWin = this.getParentWindow(frameWin);
|
||||
}
|
||||
|
||||
return rect;
|
||||
},
|
||||
|
||||
/**
|
||||
* Returns iframe content offset (iframe border + padding).
|
||||
* Note: this function shouldn't need to exist, had the platform provided a
|
||||
* suitable API for determining the offset between the iframe's content and
|
||||
* its bounding client rect. Bug 626359 should provide us with such an API.
|
||||
*
|
||||
* @param {DOMNode} aIframe
|
||||
* The iframe.
|
||||
* @return {Array} [offsetTop, offsetLeft]
|
||||
* offsetTop is the distance from the top of the iframe and the top of
|
||||
* the content document.
|
||||
* offsetLeft is the distance from the left of the iframe and the left
|
||||
* of the content document.
|
||||
*/
|
||||
getIframeContentOffset: function(aIframe) {
|
||||
let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
|
||||
|
||||
// In some cases, the computed style is null
|
||||
if (!style) {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
let paddingTop = parseInt(style.getPropertyValue("padding-top"));
|
||||
let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
|
||||
|
||||
let borderTop = parseInt(style.getPropertyValue("border-top-width"));
|
||||
let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
|
||||
|
||||
return [borderTop + paddingTop, borderLeft + paddingLeft];
|
||||
},
|
||||
|
||||
/**
|
||||
* Find an element from the given coordinates. This method descends through
|
||||
* frames to find the element the user clicked inside frames.
|
||||
*
|
||||
* @param {DOMDocument} aDocument the document to look into.
|
||||
* @param {Number} aX
|
||||
* @param {Number} aY
|
||||
* @return {DOMNode}
|
||||
* the element node found at the given coordinates, or null if no node
|
||||
* was found
|
||||
*/
|
||||
getElementFromPoint: function(aDocument, aX, aY) {
|
||||
let node = aDocument.elementFromPoint(aX, aY);
|
||||
if (node && node.contentDocument) {
|
||||
if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
|
||||
let rect = node.getBoundingClientRect();
|
||||
|
||||
// Gap between the iframe and its content window.
|
||||
let [offsetTop, offsetLeft] = this.getIframeContentOffset(node);
|
||||
|
||||
aX -= rect.left + offsetLeft;
|
||||
aY -= rect.top + offsetTop;
|
||||
|
||||
if (aX < 0 || aY < 0) {
|
||||
// Didn't reach the content document, still over the iframe.
|
||||
return node;
|
||||
}
|
||||
}
|
||||
if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
|
||||
node instanceof Ci.nsIDOMHTMLFrameElement) {
|
||||
let subnode = this.getElementFromPoint(node.contentDocument, aX, aY);
|
||||
if (subnode) {
|
||||
node = subnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
},
|
||||
|
||||
/**
|
||||
* Scroll the document so that the element "elem" appears vertically in
|
||||
* the viewport.
|
||||
*
|
||||
* @param {DOMNode} elem
|
||||
* The element that needs to appear in the viewport.
|
||||
* @param {Boolean} centered
|
||||
* true if you want it centered, false if you want it to appear on the
|
||||
* top of the viewport. True by default, and that is usually what
|
||||
* you want.
|
||||
*/
|
||||
scrollIntoViewIfNeeded: function(elem, centered) {
|
||||
// We want to default to centering the element in the page,
|
||||
// so as to keep the context of the element.
|
||||
centered = centered === undefined ? true: !!centered;
|
||||
|
||||
let win = elem.ownerDocument.defaultView;
|
||||
let clientRect = elem.getBoundingClientRect();
|
||||
|
||||
// The following are always from the {top, bottom}
|
||||
// of the viewport, to the {top, …} of the box.
|
||||
// Think of them as geometrical vectors, it helps.
|
||||
// The origin is at the top left.
|
||||
|
||||
let topToBottom = clientRect.bottom;
|
||||
let bottomToTop = clientRect.top - win.innerHeight;
|
||||
let yAllowed = true; // We allow one translation on the y axis.
|
||||
|
||||
// Whatever `centered` is, the behavior is the same if the box is
|
||||
// (even partially) visible.
|
||||
if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
|
||||
win.scrollBy(0, topToBottom - elem.offsetHeight);
|
||||
yAllowed = false;
|
||||
} else
|
||||
if ((bottomToTop < 0 || !centered) && bottomToTop >= -elem.offsetHeight) {
|
||||
win.scrollBy(0, bottomToTop + elem.offsetHeight);
|
||||
yAllowed = false;
|
||||
}
|
||||
|
||||
// If we want it centered, and the box is completely hidden,
|
||||
// then we center it explicitly.
|
||||
if (centered) {
|
||||
if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
|
||||
win.scroll(win.scrollX,
|
||||
win.scrollY + clientRect.top
|
||||
- (win.innerHeight - elem.offsetHeight) / 2);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Check if a node and its document are still alive
|
||||
* and attached to the window.
|
||||
*
|
||||
* @param {DOMNode} aNode
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isNodeConnected: function(aNode) {
|
||||
try {
|
||||
let connected = (aNode.ownerDocument && aNode.ownerDocument.defaultView &&
|
||||
!(aNode.compareDocumentPosition(aNode.ownerDocument.documentElement) &
|
||||
aNode.DOCUMENT_POSITION_DISCONNECTED));
|
||||
return connected;
|
||||
} catch (e) {
|
||||
// "can't access dead object" error
|
||||
return false;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* like win.parent === win, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isTopLevelWindow: function(win) {
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
return docShell === this._topDocShell;
|
||||
},
|
||||
|
||||
/**
|
||||
* Check a window is part of the top level window.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @return {Boolean}
|
||||
*/
|
||||
isIncludedInTopLevelWindow: function LH_isIncludedInTopLevelWindow(win) {
|
||||
if (this.isTopLevelWindow(win)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let parent = this.getParentWindow(win);
|
||||
if (!parent || parent === win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return this.isIncludedInTopLevelWindow(parent);
|
||||
},
|
||||
|
||||
/**
|
||||
* like win.parent, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @return {DOMWindow}
|
||||
*/
|
||||
getParentWindow: function(win) {
|
||||
if (this.isTopLevelWindow(win)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
if (docShell.isBrowserOrApp) {
|
||||
let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
|
||||
return parentDocShell ? parentDocShell.contentViewer.DOMDocument.defaultView : null;
|
||||
} else {
|
||||
return win.parent;
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* like win.frameElement, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* The window to get the frame for
|
||||
* @return {DOMNode}
|
||||
* The element in which the window is embedded.
|
||||
*/
|
||||
getFrameElement: function(win) {
|
||||
if (this.isTopLevelWindow(win)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let winUtils = win.
|
||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
||||
getInterface(Ci.nsIDOMWindowUtils);
|
||||
|
||||
return winUtils.containerElement;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the x/y offsets for of all the parent frames of a given node
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* The node for which we are to get the offset
|
||||
* @return {Array}
|
||||
* The frame offset [x, y]
|
||||
*/
|
||||
getFrameOffsets: function(node) {
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
let frameWin = node.ownerDocument.defaultView;
|
||||
let scale = LayoutHelpers.getCurrentZoom(node);
|
||||
|
||||
while (true) {
|
||||
// Are we in the top-level window?
|
||||
if (this.isTopLevelWindow(frameWin)) {
|
||||
break;
|
||||
}
|
||||
|
||||
let frameElement = this.getFrameElement(frameWin);
|
||||
if (!frameElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We are in an iframe.
|
||||
// We take into account the parent iframe position and its
|
||||
// offset (borders and padding).
|
||||
let frameRect = frameElement.getBoundingClientRect();
|
||||
|
||||
let [offsetTop, offsetLeft] =
|
||||
this.getIframeContentOffset(frameElement);
|
||||
|
||||
xOffset += frameRect.left + offsetLeft;
|
||||
yOffset += frameRect.top + offsetTop;
|
||||
|
||||
frameWin = this.getParentWindow(frameWin);
|
||||
}
|
||||
|
||||
return [xOffset * scale, yOffset * scale];
|
||||
},
|
||||
|
||||
/**
|
||||
* Get the 4 bounding points for a node taking iframes into account.
|
||||
* Note that for transformed nodes, this will return the untransformed bound.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Object}
|
||||
* An object with p1,p2,p3,p4 properties being {x,y} objects
|
||||
*/
|
||||
getNodeBounds: function(node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scale = LayoutHelpers.getCurrentZoom(node);
|
||||
|
||||
// Find out the offset of the node in its current frame
|
||||
let offsetLeft = 0;
|
||||
let offsetTop = 0;
|
||||
let el = node;
|
||||
while (el && el.parentNode) {
|
||||
offsetLeft += el.offsetLeft;
|
||||
offsetTop += el.offsetTop;
|
||||
el = el.offsetParent;
|
||||
}
|
||||
|
||||
// Also take scrolled containers into account
|
||||
el = node;
|
||||
while (el && el.parentNode) {
|
||||
if (el.scrollTop) {
|
||||
offsetTop -= el.scrollTop;
|
||||
}
|
||||
if (el.scrollLeft) {
|
||||
offsetLeft -= el.scrollLeft;
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
|
||||
// And add the potential frame offset if the node is nested
|
||||
let [xOffset, yOffset] = this.getFrameOffsets(node);
|
||||
xOffset += offsetLeft;
|
||||
yOffset += offsetTop;
|
||||
|
||||
xOffset *= scale;
|
||||
yOffset *= scale;
|
||||
|
||||
// Get the width and height
|
||||
let width = node.offsetWidth * scale;
|
||||
let height = node.offsetHeight * scale;
|
||||
|
||||
return {
|
||||
p1: {x: xOffset, y: yOffset},
|
||||
p2: {x: xOffset + width, y: yOffset},
|
||||
p3: {x: xOffset + width, y: yOffset + height},
|
||||
p4: {x: xOffset, y: yOffset + height}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Traverse getBindingParent until arriving upon the bound element
|
||||
* responsible for the generation of the specified node.
|
||||
* See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/DOM_Interfaces#getBindingParent.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {DOMNode}
|
||||
* If node is not anonymous, this will return node. Otherwise,
|
||||
* it will return the bound element
|
||||
*
|
||||
*/
|
||||
LayoutHelpers.getRootBindingParent = function(node) {
|
||||
let parent;
|
||||
let doc = node.ownerDocument;
|
||||
if (!doc) {
|
||||
return node;
|
||||
}
|
||||
while ((parent = doc.getBindingParent(node))) {
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
};
|
||||
|
||||
LayoutHelpers.getBindingParent = function(node) {
|
||||
let doc = node.ownerDocument;
|
||||
if (!doc) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is no binding parent then it is not anonymous.
|
||||
let parent = doc.getBindingParent(node);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
/**
|
||||
* Determine whether a node is anonymous by determining if there
|
||||
* is a bindingParent.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
LayoutHelpers.isAnonymous = function(node) {
|
||||
return LayoutHelpers.getRootBindingParent(node) !== node;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether a node is native anonymous content (as opposed
|
||||
* to XBL anonymous or shadow DOM).
|
||||
* Native anonymous content includes elements like internals to form
|
||||
* controls and ::before/::after.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
LayoutHelpers.isNativeAnonymous = function(node) {
|
||||
if (!LayoutHelpers.getBindingParent(node)) {
|
||||
return false;
|
||||
}
|
||||
return !LayoutHelpers.isXBLAnonymous(node) &&
|
||||
!LayoutHelpers.isShadowAnonymous(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether a node is XBL anonymous content (as opposed
|
||||
* to native anonymous or shadow DOM).
|
||||
* See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
LayoutHelpers.isXBLAnonymous = function(node) {
|
||||
let parent = LayoutHelpers.getBindingParent(node);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shadow nodes also show up in getAnonymousNodes, so return false.
|
||||
if (parent.shadowRoot && parent.shadowRoot.contains(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let anonNodes = [...node.ownerDocument.getAnonymousNodes(parent) || []];
|
||||
return anonNodes.indexOf(node) > -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Determine whether a node is a child of a shadow root.
|
||||
* See https://w3c.github.io/webcomponents/spec/shadow/
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*/
|
||||
LayoutHelpers.isShadowAnonymous = function(node) {
|
||||
let parent = LayoutHelpers.getBindingParent(node);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is a shadowRoot and this is part of it then this
|
||||
// is not native anonymous
|
||||
return parent.shadowRoot && parent.shadowRoot.contains(node);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current zoom factor applied to the container window of a given node.
|
||||
* Container windows are used as a weakmap key to store the corresponding
|
||||
* nsIDOMWindowUtils instance to avoid querying it every time.
|
||||
*
|
||||
* @param {DOMNode|DOMWindow} The node for which the zoom factor should be
|
||||
* calculated, or its owner window.
|
||||
* @return {Number}
|
||||
*/
|
||||
let windowUtils = new WeakMap;
|
||||
LayoutHelpers.getCurrentZoom = function(node) {
|
||||
let win = node.self === node ? node : node.ownerDocument.defaultView;
|
||||
let utils = windowUtils.get(win);
|
||||
if (utils) {
|
||||
return utils.fullZoom;
|
||||
}
|
||||
|
||||
utils = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils);
|
||||
windowUtils.set(win, utils);
|
||||
return utils.fullZoom;
|
||||
};
|
|
@ -0,0 +1,637 @@
|
|||
/* 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 { Ci } = require("chrome");
|
||||
const { memoize } = require("sdk/lang/functional");
|
||||
|
||||
/**
|
||||
* Returns the `DOMWindowUtils` for the window given.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @returns {DOMWindowUtils}
|
||||
*/
|
||||
const utilsFor = memoize(
|
||||
(win) => win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIDOMWindowUtils)
|
||||
);
|
||||
|
||||
/**
|
||||
* like win.top, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @return {DOMWindow}
|
||||
*/
|
||||
function getTopWindow(win) {
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
if (!docShell.isBrowserOrApp) {
|
||||
return win.top;
|
||||
}
|
||||
|
||||
let topDocShell = docShell.getSameTypeRootTreeItemIgnoreBrowserAndAppBoundaries();
|
||||
|
||||
return topDocShell
|
||||
? topDocShell.contentViewer.DOMDocument.defaultView
|
||||
: null;
|
||||
}
|
||||
|
||||
exports.getTopWindow = getTopWindow;
|
||||
|
||||
/**
|
||||
* Returns `true` is the window given is a top level window.
|
||||
* like win.top === win, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @return {Boolean}
|
||||
*/
|
||||
const isTopWindow = win => win && getTopWindow(win) === win;
|
||||
exports.isTopWindow = isTopWindow;
|
||||
|
||||
/**
|
||||
* Check a window is part of the boundary window given.
|
||||
*
|
||||
* @param {DOMWindow} boundaryWindow
|
||||
* @param {DOMWindow} win
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isWindowIncluded(boundaryWindow, win) {
|
||||
if (win === boundaryWindow) {
|
||||
return true;
|
||||
}
|
||||
|
||||
let parent = getParentWindow(win);
|
||||
|
||||
if (!parent || parent === win) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return isWindowIncluded(boundaryWindow, parent);
|
||||
}
|
||||
exports.isWindowIncluded = isWindowIncluded;
|
||||
|
||||
/**
|
||||
* like win.parent, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* @return {DOMWindow}
|
||||
*/
|
||||
function getParentWindow(win) {
|
||||
if (isTopWindow(win)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
|
||||
.getInterface(Ci.nsIWebNavigation)
|
||||
.QueryInterface(Ci.nsIDocShell);
|
||||
|
||||
if (!docShell.isBrowserOrApp) {
|
||||
return win.parent;
|
||||
}
|
||||
|
||||
let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
|
||||
|
||||
return parentDocShell
|
||||
? parentDocShell.contentViewer.DOMDocument.defaultView
|
||||
: null;
|
||||
}
|
||||
|
||||
exports.getParentWindow = getParentWindow;
|
||||
|
||||
/**
|
||||
* like win.frameElement, but goes through mozbrowsers and mozapps iframes.
|
||||
*
|
||||
* @param {DOMWindow} win
|
||||
* The window to get the frame for
|
||||
* @return {DOMNode}
|
||||
* The element in which the window is embedded.
|
||||
*/
|
||||
const getFrameElement = (win) =>
|
||||
isTopWindow(win) ? null : utilsFor(win).containerElement;
|
||||
exports.getFrameElement = getFrameElement;
|
||||
|
||||
/**
|
||||
* Get the x/y offsets for of all the parent frames of a given node, limited to
|
||||
* the boundary window given.
|
||||
*
|
||||
* @param {DOMWindow} boundaryWindow
|
||||
* The window where to stop to iterate. If `null` is given, the top
|
||||
* window is used.
|
||||
* @param {DOMNode} node
|
||||
* The node for which we are to get the offset
|
||||
* @return {Array}
|
||||
* The frame offset [x, y]
|
||||
*/
|
||||
function getFrameOffsets(boundaryWindow, node) {
|
||||
let xOffset = 0;
|
||||
let yOffset = 0;
|
||||
let frameWin = node.ownerDocument.defaultView;
|
||||
let scale = getCurrentZoom(node);
|
||||
|
||||
if (boundaryWindow === null) {
|
||||
boundaryWindow = getTopWindow(frameWin);
|
||||
} else if (typeof boundaryWindow === "undefined") {
|
||||
throw new Error("No `boundaryWindow` given. Use `null` for the default one.");
|
||||
}
|
||||
|
||||
while (frameWin !== boundaryWindow) {
|
||||
|
||||
let frameElement = getFrameElement(frameWin);
|
||||
if (!frameElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We are in an iframe.
|
||||
// We take into account the parent iframe position and its
|
||||
// offset (borders and padding).
|
||||
let frameRect = frameElement.getBoundingClientRect();
|
||||
|
||||
let [offsetTop, offsetLeft] =
|
||||
getIframeContentOffset(frameElement);
|
||||
|
||||
xOffset += frameRect.left + offsetLeft;
|
||||
yOffset += frameRect.top + offsetTop;
|
||||
|
||||
frameWin = getParentWindow(frameWin);
|
||||
}
|
||||
|
||||
return [xOffset * scale, yOffset * scale];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get box quads adjusted for iframes and zoom level.
|
||||
*
|
||||
* @param {DOMWindow} boundaryWindow
|
||||
* The window where to stop to iterate. If `null` is given, the top
|
||||
* window is used.
|
||||
* @param {DOMNode} node
|
||||
* The node for which we are to get the box model region
|
||||
* quads.
|
||||
* @param {String} region
|
||||
* The box model region to return: "content", "padding", "border" or
|
||||
* "margin".
|
||||
* @return {Array}
|
||||
* 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) {
|
||||
if (!node || !node.getBoxQuads) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let quads = node.getBoxQuads({
|
||||
box: region
|
||||
});
|
||||
|
||||
if (!quads.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
|
||||
let scale = getCurrentZoom(node);
|
||||
|
||||
let adjustedQuads = [];
|
||||
for (let quad of quads) {
|
||||
adjustedQuads.push({
|
||||
p1: {
|
||||
w: quad.p1.w * scale,
|
||||
x: quad.p1.x * scale + xOffset,
|
||||
y: quad.p1.y * scale + yOffset,
|
||||
z: quad.p1.z * scale
|
||||
},
|
||||
p2: {
|
||||
w: quad.p2.w * scale,
|
||||
x: quad.p2.x * scale + xOffset,
|
||||
y: quad.p2.y * scale + yOffset,
|
||||
z: quad.p2.z * scale
|
||||
},
|
||||
p3: {
|
||||
w: quad.p3.w * scale,
|
||||
x: quad.p3.x * scale + xOffset,
|
||||
y: quad.p3.y * scale + yOffset,
|
||||
z: quad.p3.z * scale
|
||||
},
|
||||
p4: {
|
||||
w: quad.p4.w * scale,
|
||||
x: quad.p4.x * scale + xOffset,
|
||||
y: quad.p4.y * scale + yOffset,
|
||||
z: quad.p4.z * scale
|
||||
},
|
||||
bounds: {
|
||||
bottom: quad.bounds.bottom * scale + yOffset,
|
||||
height: quad.bounds.height * scale,
|
||||
left: quad.bounds.left * scale + xOffset,
|
||||
right: quad.bounds.right * scale + xOffset,
|
||||
top: quad.bounds.top * scale + yOffset,
|
||||
width: quad.bounds.width * scale,
|
||||
x: quad.bounds.x * scale + xOffset,
|
||||
y: quad.bounds.y * scale + yOffset
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return adjustedQuads;
|
||||
}
|
||||
exports.getAdjustedQuads = getAdjustedQuads;
|
||||
|
||||
/**
|
||||
* Compute the absolute position and the dimensions of a node, relativalely
|
||||
* to the root window.
|
||||
|
||||
* @param {DOMWindow} boundaryWindow
|
||||
* The window where to stop to iterate. If `null` is given, the top
|
||||
* window is used.
|
||||
* @param {DOMNode} aNode
|
||||
* a DOM element to get the bounds for
|
||||
* @param {DOMWindow} aContentWindow
|
||||
* the content window holding the node
|
||||
* @return {Object}
|
||||
* A rect object with the {top, left, width, height} properties
|
||||
*/
|
||||
function getRect(boundaryWindow, aNode, aContentWindow) {
|
||||
let frameWin = aNode.ownerDocument.defaultView;
|
||||
let clientRect = aNode.getBoundingClientRect();
|
||||
|
||||
if (boundaryWindow === null) {
|
||||
boundaryWindow = getTopWindow(frameWin);
|
||||
} else if (typeof boundaryWindow === "undefined") {
|
||||
throw new Error("No `boundaryWindow` given. Use `null` for the default one.");
|
||||
}
|
||||
|
||||
// Go up in the tree of frames to determine the correct rectangle.
|
||||
// clientRect is read-only, we need to be able to change properties.
|
||||
let rect = {
|
||||
top: clientRect.top + aContentWindow.pageYOffset,
|
||||
left: clientRect.left + aContentWindow.pageXOffset,
|
||||
width: clientRect.width,
|
||||
height: clientRect.height
|
||||
};
|
||||
|
||||
// We iterate through all the parent windows.
|
||||
while (frameWin !== boundaryWindow) {
|
||||
let frameElement = getFrameElement(frameWin);
|
||||
if (!frameElement) {
|
||||
break;
|
||||
}
|
||||
|
||||
// We are in an iframe.
|
||||
// We take into account the parent iframe position and its
|
||||
// offset (borders and padding).
|
||||
let frameRect = frameElement.getBoundingClientRect();
|
||||
|
||||
let [offsetTop, offsetLeft] =
|
||||
getIframeContentOffset(frameElement);
|
||||
|
||||
rect.top += frameRect.top + offsetTop;
|
||||
rect.left += frameRect.left + offsetLeft;
|
||||
|
||||
frameWin = getParentWindow(frameWin);
|
||||
}
|
||||
|
||||
return rect;
|
||||
};
|
||||
exports.getRect = getRect;
|
||||
|
||||
/**
|
||||
* Get the 4 bounding points for a node taking iframes into account.
|
||||
* Note that for transformed nodes, this will return the untransformed bound.
|
||||
*
|
||||
* @param {DOMWindow} boundaryWindow
|
||||
* The window where to stop to iterate. If `null` is given, the top
|
||||
* window is used.
|
||||
* @param {DOMNode} node
|
||||
* @return {Object}
|
||||
* An object with p1,p2,p3,p4 properties being {x,y} objects
|
||||
*/
|
||||
function getNodeBounds(boundaryWindow, node) {
|
||||
if (!node) {
|
||||
return;
|
||||
}
|
||||
|
||||
let scale = getCurrentZoom(node);
|
||||
|
||||
// Find out the offset of the node in its current frame
|
||||
let offsetLeft = 0;
|
||||
let offsetTop = 0;
|
||||
let el = node;
|
||||
while (el && el.parentNode) {
|
||||
offsetLeft += el.offsetLeft;
|
||||
offsetTop += el.offsetTop;
|
||||
el = el.offsetParent;
|
||||
}
|
||||
|
||||
// Also take scrolled containers into account
|
||||
el = node;
|
||||
while (el && el.parentNode) {
|
||||
if (el.scrollTop) {
|
||||
offsetTop -= el.scrollTop;
|
||||
}
|
||||
if (el.scrollLeft) {
|
||||
offsetLeft -= el.scrollLeft;
|
||||
}
|
||||
el = el.parentNode;
|
||||
}
|
||||
|
||||
// And add the potential frame offset if the node is nested
|
||||
let [xOffset, yOffset] = getFrameOffsets(boundaryWindow, node);
|
||||
xOffset += offsetLeft;
|
||||
yOffset += offsetTop;
|
||||
|
||||
xOffset *= scale;
|
||||
yOffset *= scale;
|
||||
|
||||
// Get the width and height
|
||||
let width = node.offsetWidth * scale;
|
||||
let height = node.offsetHeight * scale;
|
||||
|
||||
return {
|
||||
p1: {x: xOffset, y: yOffset},
|
||||
p2: {x: xOffset + width, y: yOffset},
|
||||
p3: {x: xOffset + width, y: yOffset + height},
|
||||
p4: {x: xOffset, y: yOffset + height}
|
||||
};
|
||||
}
|
||||
exports.getNodeBounds = getNodeBounds;
|
||||
|
||||
/**
|
||||
* Returns iframe content offset (iframe border + padding).
|
||||
* Note: this function shouldn't need to exist, had the platform provided a
|
||||
* suitable API for determining the offset between the iframe's content and
|
||||
* its bounding client rect. Bug 626359 should provide us with such an API.
|
||||
*
|
||||
* @param {DOMNode} aIframe
|
||||
* The iframe.
|
||||
* @return {Array} [offsetTop, offsetLeft]
|
||||
* offsetTop is the distance from the top of the iframe and the top of
|
||||
* the content document.
|
||||
* offsetLeft is the distance from the left of the iframe and the left
|
||||
* of the content document.
|
||||
*/
|
||||
function getIframeContentOffset(aIframe) {
|
||||
let style = aIframe.contentWindow.getComputedStyle(aIframe, null);
|
||||
|
||||
// In some cases, the computed style is null
|
||||
if (!style) {
|
||||
return [0, 0];
|
||||
}
|
||||
|
||||
let paddingTop = parseInt(style.getPropertyValue("padding-top"));
|
||||
let paddingLeft = parseInt(style.getPropertyValue("padding-left"));
|
||||
|
||||
let borderTop = parseInt(style.getPropertyValue("border-top-width"));
|
||||
let borderLeft = parseInt(style.getPropertyValue("border-left-width"));
|
||||
|
||||
return [borderTop + paddingTop, borderLeft + paddingLeft];
|
||||
}
|
||||
exports.getIframeContentOffset = getIframeContentOffset;
|
||||
|
||||
/**
|
||||
* Find an element from the given coordinates. This method descends through
|
||||
* frames to find the element the user clicked inside frames.
|
||||
*
|
||||
* @param {DOMDocument} aDocument
|
||||
* The document to look into.
|
||||
* @param {Number} aX
|
||||
* @param {Number} aY
|
||||
* @return {DOMNode}
|
||||
* the element node found at the given coordinates, or null if no node
|
||||
* was found
|
||||
*/
|
||||
function getElementFromPoint(aDocument, aX, aY) {
|
||||
let node = aDocument.elementFromPoint(aX, aY);
|
||||
if (node && node.contentDocument) {
|
||||
if (node instanceof Ci.nsIDOMHTMLIFrameElement) {
|
||||
let rect = node.getBoundingClientRect();
|
||||
|
||||
// Gap between the iframe and its content window.
|
||||
let [offsetTop, offsetLeft] = getIframeContentOffset(node);
|
||||
|
||||
aX -= rect.left + offsetLeft;
|
||||
aY -= rect.top + offsetTop;
|
||||
|
||||
if (aX < 0 || aY < 0) {
|
||||
// Didn't reach the content document, still over the iframe.
|
||||
return node;
|
||||
}
|
||||
}
|
||||
if (node instanceof Ci.nsIDOMHTMLIFrameElement ||
|
||||
node instanceof Ci.nsIDOMHTMLFrameElement) {
|
||||
let subnode = getElementFromPoint(node.contentDocument, aX, aY);
|
||||
if (subnode) {
|
||||
node = subnode;
|
||||
}
|
||||
}
|
||||
}
|
||||
return node;
|
||||
}
|
||||
exports.getElementFromPoint = getElementFromPoint;
|
||||
|
||||
/**
|
||||
* Scroll the document so that the element "elem" appears in the viewport.
|
||||
*
|
||||
* @param {DOMNode} elem
|
||||
* The element that needs to appear in the viewport.
|
||||
* @param {Boolean} centered
|
||||
* true if you want it centered, false if you want it to appear on the
|
||||
* top of the viewport. It is true by default, and that is usually what
|
||||
* you want.
|
||||
*/
|
||||
function scrollIntoViewIfNeeded(elem, centered=true) {
|
||||
let win = elem.ownerDocument.defaultView;
|
||||
let clientRect = elem.getBoundingClientRect();
|
||||
|
||||
// The following are always from the {top, bottom}
|
||||
// of the viewport, to the {top, …} of the box.
|
||||
// Think of them as geometrical vectors, it helps.
|
||||
// The origin is at the top left.
|
||||
|
||||
let topToBottom = clientRect.bottom;
|
||||
let bottomToTop = clientRect.top - win.innerHeight;
|
||||
let yAllowed = true; // We allow one translation on the y axis.
|
||||
|
||||
// Whatever `centered` is, the behavior is the same if the box is
|
||||
// (even partially) visible.
|
||||
if ((topToBottom > 0 || !centered) && topToBottom <= elem.offsetHeight) {
|
||||
win.scrollBy(0, topToBottom - elem.offsetHeight);
|
||||
yAllowed = false;
|
||||
} else if ((bottomToTop < 0 || !centered) && bottomToTop >= -elem.offsetHeight) {
|
||||
win.scrollBy(0, bottomToTop + elem.offsetHeight);
|
||||
yAllowed = false;
|
||||
}
|
||||
|
||||
// If we want it centered, and the box is completely hidden,
|
||||
// then we center it explicitly.
|
||||
if (centered) {
|
||||
if (yAllowed && (topToBottom <= 0 || bottomToTop >= 0)) {
|
||||
win.scroll(win.scrollX,
|
||||
win.scrollY + clientRect.top
|
||||
- (win.innerHeight - elem.offsetHeight) / 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
exports.scrollIntoViewIfNeeded = scrollIntoViewIfNeeded;
|
||||
|
||||
/**
|
||||
* Check if a node and its document are still alive
|
||||
* and attached to the window.
|
||||
*
|
||||
* @param {DOMNode} aNode
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isNodeConnected(aNode) {
|
||||
try {
|
||||
let connected = (aNode.ownerDocument && aNode.ownerDocument.defaultView &&
|
||||
!(aNode.compareDocumentPosition(aNode.ownerDocument.documentElement) &
|
||||
aNode.DOCUMENT_POSITION_DISCONNECTED));
|
||||
return connected;
|
||||
} catch (e) {
|
||||
// "can't access dead object" error
|
||||
return false;
|
||||
}
|
||||
}
|
||||
exports.isNodeConnected = isNodeConnected;
|
||||
|
||||
/**
|
||||
* Traverse getBindingParent until arriving upon the bound element
|
||||
* responsible for the generation of the specified node.
|
||||
* See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/DOM_Interfaces#getBindingParent.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {DOMNode}
|
||||
* If node is not anonymous, this will return node. Otherwise,
|
||||
* it will return the bound element
|
||||
*
|
||||
*/
|
||||
function getRootBindingParent(node) {
|
||||
let parent;
|
||||
let doc = node.ownerDocument;
|
||||
if (!doc) {
|
||||
return node;
|
||||
}
|
||||
while ((parent = doc.getBindingParent(node))) {
|
||||
node = parent;
|
||||
}
|
||||
return node;
|
||||
}
|
||||
exports.getRootBindingParent = getRootBindingParent;
|
||||
|
||||
function getBindingParent(node) {
|
||||
let doc = node.ownerDocument;
|
||||
if (!doc) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// If there is no binding parent then it is not anonymous.
|
||||
let parent = doc.getBindingParent(node);
|
||||
if (!parent) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return parent;
|
||||
}
|
||||
exports.getBindingParent = getBindingParent;
|
||||
|
||||
/**
|
||||
* Determine whether a node is anonymous by determining if there
|
||||
* is a bindingParent.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
const isAnonymous = (node) => getRootBindingParent(node) !== node;
|
||||
exports.isAnonymous = isAnonymous;
|
||||
|
||||
/**
|
||||
* Determine whether a node has a bindingParent.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
const hasBindingParent = (node) => !!getBindingParent(node);
|
||||
|
||||
/**
|
||||
* Determine whether a node is native anonymous content (as opposed
|
||||
* to XBL anonymous or shadow DOM).
|
||||
* Native anonymous content includes elements like internals to form
|
||||
* controls and ::before/::after.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
const isNativeAnonymous = (node) =>
|
||||
hasBindingParent(node) && !(isXBLAnonymous(node) || isShadowAnonymous(node));
|
||||
|
||||
exports.isNativeAnonymous = isNativeAnonymous;
|
||||
|
||||
/**
|
||||
* Determine whether a node is XBL anonymous content (as opposed
|
||||
* to native anonymous or shadow DOM).
|
||||
* See https://developer.mozilla.org/en-US/docs/XBL/XBL_1.0_Reference/Anonymous_Content.
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*
|
||||
*/
|
||||
function isXBLAnonymous(node) {
|
||||
let parent = getBindingParent(node);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shadow nodes also show up in getAnonymousNodes, so return false.
|
||||
if (parent.shadowRoot && parent.shadowRoot.contains(node)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let anonNodes = [...node.ownerDocument.getAnonymousNodes(parent) || []];
|
||||
return anonNodes.indexOf(node) > -1;
|
||||
}
|
||||
exports.isXBLAnonymous = isXBLAnonymous;
|
||||
|
||||
/**
|
||||
* Determine whether a node is a child of a shadow root.
|
||||
* See https://w3c.github.io/webcomponents/spec/shadow/
|
||||
*
|
||||
* @param {DOMNode} node
|
||||
* @return {Boolean}
|
||||
*/
|
||||
function isShadowAnonymous(node) {
|
||||
let parent = getBindingParent(node);
|
||||
if (!parent) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// If there is a shadowRoot and this is part of it then this
|
||||
// is not native anonymous
|
||||
return parent.shadowRoot && parent.shadowRoot.contains(node);
|
||||
}
|
||||
exports.isShadowAnonymous = isShadowAnonymous;
|
||||
|
||||
/**
|
||||
* Get the current zoom factor applied to the container window of a given node.
|
||||
* Container windows are used as a weakmap key to store the corresponding
|
||||
* nsIDOMWindowUtils instance to avoid querying it every time.
|
||||
*
|
||||
* @param {DOMNode|DOMWindow}
|
||||
* The node for which the zoom factor should be calculated, or its
|
||||
* owner window.
|
||||
* @return {Number}
|
||||
*/
|
||||
function getCurrentZoom(node) {
|
||||
let win = node instanceof Ci.nsIDOMNode ? node.ownerDocument.defaultView :
|
||||
node instanceof Ci.nsIDOMWindow ? node : null;
|
||||
|
||||
if (!win) {
|
||||
throw new Error("Unable to get the zoom from the given argument.");
|
||||
}
|
||||
|
||||
return utilsFor(win).fullZoom;
|
||||
}
|
||||
exports.getCurrentZoom = getCurrentZoom;
|
|
@ -37,7 +37,6 @@ EXTRA_JS_MODULES.devtools += [
|
|||
'DevToolsUtils.js',
|
||||
'event-emitter.js',
|
||||
'event-parsers.js',
|
||||
'layout-helpers.js',
|
||||
'output-parser.js',
|
||||
'path.js',
|
||||
'worker-loader.js',
|
||||
|
@ -51,3 +50,7 @@ EXTRA_JS_MODULES.devtools += [
|
|||
EXTRA_JS_MODULES.devtools.server.actors += [
|
||||
'server/actors/highlighter.css'
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES.devtools.layout += [
|
||||
'layout/utils.js'
|
||||
]
|
||||
|
|
|
@ -1,17 +1,18 @@
|
|||
/* 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/. */
|
||||
/* globals LayoutHelpers, DOMUtils, CssLogic, setIgnoreLayoutChanges */
|
||||
/* globals DOMUtils, CssLogic, setIgnoreLayoutChanges */
|
||||
|
||||
"use strict";
|
||||
|
||||
const {Cu, Cc, Ci} = require("chrome");
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const {Arg, Option, method, RetVal} = protocol;
|
||||
const events = require("sdk/event/core");
|
||||
const Heritage = require("sdk/core/heritage");
|
||||
const { Cu, Cc, Ci } = require("chrome");
|
||||
const { extend } = require("sdk/core/heritage");
|
||||
const { getCurrentZoom, getAdjustedQuads, getNodeBounds, getRootBindingParent,
|
||||
isWindowIncluded } = require("devtools/toolkit/layout/utils");
|
||||
const EventEmitter = require("devtools/toolkit/event-emitter");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const events = require("sdk/event/core");
|
||||
const protocol = require("devtools/server/protocol");
|
||||
const { Arg, Option, method, RetVal } = protocol;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
loader.lazyRequireGetter(this, "CssLogic",
|
||||
|
@ -145,7 +146,6 @@ let HighlighterActor = exports.HighlighterActor = protocol.ActorClass({
|
|||
this._highlighterHidden = this._highlighterHidden.bind(this);
|
||||
this._onNavigate = this._onNavigate.bind(this);
|
||||
|
||||
this._layoutHelpers = new LayoutHelpers(this._tabActor.window);
|
||||
this._createHighlighter();
|
||||
|
||||
// Listen to navigation events to switch from the BoxModelHighlighter to the
|
||||
|
@ -218,7 +218,6 @@ let HighlighterActor = exports.HighlighterActor = protocol.ActorClass({
|
|||
this._inspector = null;
|
||||
this._walker = null;
|
||||
this._tabActor = null;
|
||||
this._layoutHelpers = null;
|
||||
},
|
||||
|
||||
/**
|
||||
|
@ -257,6 +256,26 @@ let HighlighterActor = exports.HighlighterActor = protocol.ActorClass({
|
|||
request: {}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Returns `true` if the event was dispatched from a window included in
|
||||
* the current highlighter environment; or if the highlighter environment has
|
||||
* chrome privileges
|
||||
*
|
||||
* The method is specifically useful on B2G, where we do not want the events
|
||||
* from the app or main process to be processed when we're inspecting the
|
||||
* content.
|
||||
*
|
||||
* @param {Event} event
|
||||
* The event to allow
|
||||
* @return {Boolean}
|
||||
*/
|
||||
_isEventAllowed: function({view}) {
|
||||
let { window } = this._highlighterEnv;
|
||||
|
||||
return window instanceof Ci.nsIDOMChromeWindow ||
|
||||
isWindowIncluded(window, view);
|
||||
},
|
||||
|
||||
/**
|
||||
* Pick a node on click, and highlight hovered nodes in the process.
|
||||
*
|
||||
|
@ -284,6 +303,11 @@ let HighlighterActor = exports.HighlighterActor = protocol.ActorClass({
|
|||
|
||||
this._onPick = event => {
|
||||
this._preventContentEvent(event);
|
||||
|
||||
if (!this._isEventAllowed(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._stopPickerListeners();
|
||||
this._isPicking = false;
|
||||
if (this._autohide) {
|
||||
|
@ -299,6 +323,11 @@ let HighlighterActor = exports.HighlighterActor = protocol.ActorClass({
|
|||
|
||||
this._onHovered = event => {
|
||||
this._preventContentEvent(event);
|
||||
|
||||
if (!this._isEventAllowed(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this._currentNode = this._findAndAttachElement(event);
|
||||
if (this._hoveredNode !== this._currentNode.node) {
|
||||
this._highlighter.show(this._currentNode.node.rawNode);
|
||||
|
@ -313,6 +342,11 @@ let HighlighterActor = exports.HighlighterActor = protocol.ActorClass({
|
|||
}
|
||||
|
||||
this._preventContentEvent(event);
|
||||
|
||||
if (!this._isEventAllowed(event)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let currentNode = this._currentNode.node.rawNode;
|
||||
|
||||
/**
|
||||
|
@ -842,7 +876,7 @@ CanvasFrameAnonymousContentHelper.prototype = {
|
|||
* @param {String} id The ID of the root element inserted with this API.
|
||||
*/
|
||||
scaleRootElement: function(node, id) {
|
||||
let zoom = LayoutHelpers.getCurrentZoom(node);
|
||||
let zoom = getCurrentZoom(node);
|
||||
let value = "position:absolute;width:100%;height:100%;";
|
||||
|
||||
if (zoom !== 1) {
|
||||
|
@ -884,8 +918,6 @@ function AutoRefreshHighlighter(highlighterEnv) {
|
|||
this.currentNode = null;
|
||||
this.currentQuads = {};
|
||||
|
||||
this.layoutHelpers = new LayoutHelpers(this.win);
|
||||
|
||||
this.update = this.update.bind(this);
|
||||
}
|
||||
|
||||
|
@ -964,7 +996,8 @@ AutoRefreshHighlighter.prototype = {
|
|||
*/
|
||||
_updateAdjustedQuads: function() {
|
||||
for (let region of BOX_MODEL_REGIONS) {
|
||||
this.currentQuads[region] = this.layoutHelpers.getAdjustedQuads(
|
||||
this.currentQuads[region] = getAdjustedQuads(
|
||||
this.win,
|
||||
this.currentNode, region);
|
||||
}
|
||||
},
|
||||
|
@ -1034,7 +1067,6 @@ AutoRefreshHighlighter.prototype = {
|
|||
this.highlighterEnv = null;
|
||||
this.win = null;
|
||||
this.currentNode = null;
|
||||
this.layoutHelpers = null;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -1114,7 +1146,7 @@ function BoxModelHighlighter(highlighterEnv) {
|
|||
this._currentNode = null;
|
||||
}
|
||||
|
||||
BoxModelHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.prototype, {
|
||||
BoxModelHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
typeName: "BoxModelHighlighter",
|
||||
|
||||
ID_CLASS_PREFIX: "box-model-",
|
||||
|
@ -1706,8 +1738,8 @@ BoxModelHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.prototype
|
|||
*/
|
||||
_moveInfobar: function() {
|
||||
let bounds = this._getOuterBounds();
|
||||
let winHeight = this.win.innerHeight * LayoutHelpers.getCurrentZoom(this.win);
|
||||
let winWidth = this.win.innerWidth * LayoutHelpers.getCurrentZoom(this.win);
|
||||
let winHeight = this.win.innerHeight * getCurrentZoom(this.win);
|
||||
let winWidth = this.win.innerWidth * getCurrentZoom(this.win);
|
||||
|
||||
// Ensure that containerBottom and containerTop are at least zero to avoid
|
||||
// showing tooltips outside the viewport.
|
||||
|
@ -1769,7 +1801,7 @@ function CssTransformHighlighter(highlighterEnv) {
|
|||
|
||||
let MARKER_COUNTER = 1;
|
||||
|
||||
CssTransformHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.prototype, {
|
||||
CssTransformHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
typeName: "CssTransformHighlighter",
|
||||
|
||||
ID_CLASS_PREFIX: "css-transform-",
|
||||
|
@ -1946,7 +1978,7 @@ CssTransformHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.proto
|
|||
let [quad] = quads;
|
||||
|
||||
// Getting the points for the untransformed shape
|
||||
let untransformedQuad = this.layoutHelpers.getNodeBounds(this.currentNode);
|
||||
let untransformedQuad = getNodeBounds(this.win, this.currentNode);
|
||||
|
||||
this._setPolygonPoints(quad, "transformed");
|
||||
this._setPolygonPoints(untransformedQuad, "untransformed");
|
||||
|
@ -2064,7 +2096,6 @@ exports.SelectorHighlighter = SelectorHighlighter;
|
|||
*/
|
||||
function RectHighlighter(highlighterEnv) {
|
||||
this.win = highlighterEnv.window;
|
||||
this.layoutHelpers = new LayoutHelpers(this.win);
|
||||
this.markup = new CanvasFrameAnonymousContentHelper(highlighterEnv,
|
||||
this._buildMarkup.bind(this));
|
||||
}
|
||||
|
@ -2085,7 +2116,6 @@ RectHighlighter.prototype = {
|
|||
|
||||
destroy: function() {
|
||||
this.win = null;
|
||||
this.layoutHelpers = null;
|
||||
this.markup.destroy();
|
||||
},
|
||||
|
||||
|
@ -2121,7 +2151,7 @@ RectHighlighter.prototype = {
|
|||
let contextNode = node.ownerDocument.documentElement;
|
||||
|
||||
// Caculate the absolute rect based on the context node's adjusted quads.
|
||||
let quads = this.layoutHelpers.getAdjustedQuads(contextNode);
|
||||
let quads = getAdjustedQuads(this.win, contextNode);
|
||||
if (!quads.length) {
|
||||
this.hide();
|
||||
return false;
|
||||
|
@ -2242,7 +2272,7 @@ function GeometryEditorHighlighter(highlighterEnv) {
|
|||
this._buildMarkup.bind(this));
|
||||
}
|
||||
|
||||
GeometryEditorHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.prototype, {
|
||||
GeometryEditorHighlighter.prototype = extend(AutoRefreshHighlighter.prototype, {
|
||||
typeName: "GeometryEditorHighlighter",
|
||||
|
||||
ID_CLASS_PREFIX: "geometry-editor-",
|
||||
|
@ -2537,8 +2567,8 @@ GeometryEditorHighlighter.prototype = Heritage.extend(AutoRefreshHighlighter.pro
|
|||
// Get the offsetParent, if any.
|
||||
this.offsetParent = getOffsetParent(this.currentNode);
|
||||
// And the offsetParent quads.
|
||||
this.parentQuads = this.layoutHelpers
|
||||
.getAdjustedQuads(this.offsetParent.element, "padding");
|
||||
this.parentQuads = getAdjustedQuads(
|
||||
this.win, this.offsetParent.element, "padding");
|
||||
|
||||
let el = this.getElement("offset-parent");
|
||||
|
||||
|
@ -2939,7 +2969,7 @@ RulersHighlighter.prototype = {
|
|||
|
||||
setIgnoreLayoutChanges(true);
|
||||
|
||||
let zoom = LayoutHelpers.getCurrentZoom(window);
|
||||
let zoom = getCurrentZoom(window);
|
||||
let isZoomChanged = zoom !== this._zoom;
|
||||
|
||||
if (isZoomChanged) {
|
||||
|
@ -3071,7 +3101,7 @@ function isNodeValid(node) {
|
|||
|
||||
// Is the node connected to the document? Using getBindingParent adds
|
||||
// support for anonymous elements generated by a node in the document.
|
||||
let bindingParent = LayoutHelpers.getRootBindingParent(node);
|
||||
let bindingParent = getRootBindingParent(node);
|
||||
if (!doc.documentElement.contains(bindingParent)) {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -68,9 +68,15 @@ const {
|
|||
CustomHighlighterActor,
|
||||
isTypeRegistered,
|
||||
} = require("devtools/server/actors/highlighter");
|
||||
const {
|
||||
isAnonymous,
|
||||
isNativeAnonymous,
|
||||
isXBLAnonymous,
|
||||
isShadowAnonymous,
|
||||
getFrameElement
|
||||
} = require("devtools/toolkit/layout/utils");
|
||||
const {getLayoutChangesObserver, releaseLayoutChangesObserver} =
|
||||
require("devtools/server/actors/layout");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
|
||||
const {EventParsers} = require("devtools/toolkit/event-parsers");
|
||||
|
||||
|
@ -264,10 +270,10 @@ var NodeActor = exports.NodeActor = protocol.ActorClass({
|
|||
attrs: this.writeAttrs(),
|
||||
isBeforePseudoElement: this.isBeforePseudoElement,
|
||||
isAfterPseudoElement: this.isAfterPseudoElement,
|
||||
isAnonymous: LayoutHelpers.isAnonymous(this.rawNode),
|
||||
isNativeAnonymous: LayoutHelpers.isNativeAnonymous(this.rawNode),
|
||||
isXBLAnonymous: LayoutHelpers.isXBLAnonymous(this.rawNode),
|
||||
isShadowAnonymous: LayoutHelpers.isShadowAnonymous(this.rawNode),
|
||||
isAnonymous: isAnonymous(this.rawNode),
|
||||
isNativeAnonymous: isNativeAnonymous(this.rawNode),
|
||||
isXBLAnonymous: isXBLAnonymous(this.rawNode),
|
||||
isShadowAnonymous: isShadowAnonymous(this.rawNode),
|
||||
pseudoClassLocks: this.writePseudoClassLocks(),
|
||||
|
||||
isDisplayed: this.isDisplayed,
|
||||
|
@ -1290,8 +1296,6 @@ var WalkerActor = protocol.ActorClass({
|
|||
this._activePseudoClassLocks = new Set();
|
||||
this.showAllAnonymousContent = options.showAllAnonymousContent;
|
||||
|
||||
this.layoutHelpers = new LayoutHelpers(this.rootWin);
|
||||
|
||||
// Nodes which have been removed from the client's known
|
||||
// ownership tree are considered "orphaned", and stored in
|
||||
// this set.
|
||||
|
@ -1491,7 +1495,7 @@ var WalkerActor = protocol.ActorClass({
|
|||
// If an anonymous node was passed in and we aren't supposed to know
|
||||
// about it, then consult with the document walker as the source of
|
||||
// truth about which elements exist.
|
||||
if (!this.showAllAnonymousContent && LayoutHelpers.isAnonymous(node)) {
|
||||
if (!this.showAllAnonymousContent && isAnonymous(node)) {
|
||||
node = this.getDocumentWalker(node).currentNode;
|
||||
}
|
||||
|
||||
|
@ -2878,7 +2882,7 @@ var WalkerActor = protocol.ActorClass({
|
|||
});
|
||||
return;
|
||||
}
|
||||
let frame = this.layoutHelpers.getFrameElement(window);
|
||||
let frame = getFrameElement(window);
|
||||
let frameActor = this._refMap.get(frame);
|
||||
if (!frameActor) {
|
||||
return;
|
||||
|
@ -2905,7 +2909,7 @@ var WalkerActor = protocol.ActorClass({
|
|||
if (win === window) {
|
||||
return true;
|
||||
}
|
||||
win = this.layoutHelpers.getFrameElement(win);
|
||||
win = getFrameElement(win);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
@ -3906,8 +3910,8 @@ function standardTreeWalkerFilter(aNode) {
|
|||
}
|
||||
|
||||
// Ignore all native and XBL anonymous content inside a non-XUL document
|
||||
if (!isInXULDocument(aNode) && (LayoutHelpers.isXBLAnonymous(aNode) ||
|
||||
LayoutHelpers.isNativeAnonymous(aNode))) {
|
||||
if (!isInXULDocument(aNode) && (isXBLAnonymous(aNode) ||
|
||||
isNativeAnonymous(aNode))) {
|
||||
// Note: this will skip inspecting the contents of feedSubscribeLine since
|
||||
// that's XUL content injected in an HTML document, but we need to because
|
||||
// this also skips many other elements that need to be skipped - like form
|
||||
|
|
|
@ -13,7 +13,7 @@ const {LongStringActor} = require("devtools/server/actors/string");
|
|||
const {DebuggerServer} = require("devtools/server/main");
|
||||
const Services = require("Services");
|
||||
const promise = require("promise");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const {isWindowIncluded} = require("devtools/toolkit/layout/utils");
|
||||
const { setTimeout, clearTimeout } = require("sdk/timers");
|
||||
|
||||
loader.lazyImporter(this, "OS", "resource://gre/modules/osfile.jsm");
|
||||
|
@ -1693,16 +1693,11 @@ let StorageActor = exports.StorageActor = protocol.ActorClass({
|
|||
|
||||
this.destroyed = false;
|
||||
this.boundUpdate = {};
|
||||
|
||||
// Layout helper for window.parent and window.top helper methods that work
|
||||
// accross devices.
|
||||
this.layoutHelper = new LayoutHelpers(this.window);
|
||||
},
|
||||
|
||||
destroy: function() {
|
||||
clearTimeout(this.batchTimer);
|
||||
this.batchTimer = null;
|
||||
this.layoutHelper = null;
|
||||
// Remove observers
|
||||
Services.obs.removeObserver(this, "content-document-global-created", false);
|
||||
Services.obs.removeObserver(this, "inner-window-destroyed", false);
|
||||
|
@ -1751,7 +1746,7 @@ let StorageActor = exports.StorageActor = protocol.ActorClass({
|
|||
},
|
||||
|
||||
isIncludedInTopLevelWindow: function(window) {
|
||||
return this.layoutHelper.isIncludedInTopLevelWindow(window);
|
||||
return isWindowIncluded(this.window, window);
|
||||
},
|
||||
|
||||
getWindowFromInnerWindowID: function(innerID) {
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
const { Cc, Ci, Cu } = require("chrome");
|
||||
const Services = require("Services");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
const { getRootBindingParent } = require("devtools/toolkit/layout/utils");
|
||||
|
||||
let pseudos = new Set([
|
||||
":after",
|
||||
|
@ -76,7 +76,7 @@ exports.PSEUDO_ELEMENT_SET = PSEUDO_ELEMENT_SET;
|
|||
// This should be ok because none of the functions that use this should be used
|
||||
// on the worker thread, where Cu is not available.
|
||||
if (Cu) {
|
||||
Cu.importGlobalProperties(['CSS']);
|
||||
Cu.importGlobalProperties(["CSS"]);
|
||||
}
|
||||
|
||||
function CssLogic()
|
||||
|
@ -945,7 +945,7 @@ function positionInNodeList(element, nodeList) {
|
|||
* and ele.ownerDocument.querySelectorAll(reply).length === 1
|
||||
*/
|
||||
CssLogic.findCssSelector = function CssLogic_findCssSelector(ele) {
|
||||
ele = LayoutHelpers.getRootBindingParent(ele);
|
||||
ele = getRootBindingParent(ele);
|
||||
var document = ele.ownerDocument;
|
||||
if (!document || !document.contains(ele)) {
|
||||
throw new Error('findCssSelector received element not inside document');
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
"use strict";
|
||||
|
||||
const {Cc, Ci, Cu, components} = require("chrome");
|
||||
const {isWindowIncluded} = require("devtools/toolkit/layout/utils");
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
|
||||
|
@ -16,7 +17,6 @@ loader.lazyImporter(this, "Services", "resource://gre/modules/Services.jsm");
|
|||
// Note that these are only used in WebConsoleCommands, see $0 and pprint().
|
||||
loader.lazyImporter(this, "VariablesView", "resource:///modules/devtools/VariablesView.jsm");
|
||||
const DevToolsUtils = require("devtools/toolkit/DevToolsUtils");
|
||||
const LayoutHelpers = require("devtools/toolkit/layout-helpers");
|
||||
|
||||
// Match the function name from the result of toString() or toSource().
|
||||
//
|
||||
|
@ -1201,9 +1201,6 @@ function ConsoleServiceListener(aWindow, aListener)
|
|||
{
|
||||
this.window = aWindow;
|
||||
this.listener = aListener;
|
||||
if (this.window) {
|
||||
this.layoutHelpers = new LayoutHelpers(this.window);
|
||||
}
|
||||
}
|
||||
exports.ConsoleServiceListener = ConsoleServiceListener;
|
||||
|
||||
|
@ -1252,8 +1249,8 @@ ConsoleServiceListener.prototype =
|
|||
return;
|
||||
}
|
||||
|
||||
let errorWindow = Services.wm.getOuterWindowWithId(aMessage.outerWindowID);
|
||||
if (!errorWindow || !this.layoutHelpers.isIncludedInTopLevelWindow(errorWindow)) {
|
||||
let errorWindow = Services.wm.getOuterWindowWithId(aMessage .outerWindowID);
|
||||
if (!errorWindow || !isWindowIncluded(this.window, errorWindow)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -1378,9 +1375,6 @@ function ConsoleAPIListener(aWindow, aOwner, aConsoleID)
|
|||
this.window = aWindow;
|
||||
this.owner = aOwner;
|
||||
this.consoleID = aConsoleID;
|
||||
if (this.window) {
|
||||
this.layoutHelpers = new LayoutHelpers(this.window);
|
||||
}
|
||||
}
|
||||
exports.ConsoleAPIListener = ConsoleAPIListener;
|
||||
|
||||
|
@ -1441,7 +1435,7 @@ ConsoleAPIListener.prototype =
|
|||
let apiMessage = aMessage.wrappedJSObject;
|
||||
if (this.window && CONSOLE_WORKER_IDS.indexOf(apiMessage.innerID) == -1) {
|
||||
let msgWindow = Services.wm.getCurrentInnerWindowWithId(apiMessage.innerID);
|
||||
if (!msgWindow || !this.layoutHelpers.isIncludedInTopLevelWindow(msgWindow)) {
|
||||
if (!msgWindow || !isWindowIncluded(this.window, msgWindow)) {
|
||||
// Not the same window!
|
||||
return;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче