Files
roytam1 bfe2ea5afa import changes from `dev' branch of rmottola/Arctic-Fox:
- reapply  Bug 1574573 - Disambiguate a use of Handle in XPCShellEnvironment.cpp r=Ehsan (a674c4b006)
- re-create --disable-applemedia option (needed for pre-10.7 Mac) in new configure ssytem (28e2fd5fee)
- Bug 1255707 - Part 2. Remove ScreenSizeChanged. r=snorp (3a93e4e768)
- Bug 1250418 - Remove the assertion check of mCanSend in CompositorCh ld::ActorDestroy, r=nical (14bb402a1d)
- Bug 1250718 - Improve layer logging for preserve-3d layers. r=thinker (f373a50040)
- Bug 1232042 - Addendum: Add comment for mLayerManager check. r=jrmuizel (2b69aa784a)
- Bug 1239861. Skip composite if vsync time is before force composite time. r=kats (5ee4038157)
- Bug 1241678 - Fix low-volume null-deref crash. r=BenWa (b28d944615)
- Rename PCompositor to PCompositorBridge. (bug 1258479 part 2, r=mattwoodrow) (dd535a9bdd)
- Bug 1238160 - Set frame type on TabContext. r=billm,mayhemer (54424d792b)
- bits of 1240471 (fb34a97806)
- Bug 1238160 - Test mozbrowser APIs to ensure no content exposure. r=bz (17a0ac611c)
- Bug 1238160 - Enable mozbrowser frames on desktop. r=bz (5b42b5403d)
- Bug 1197461 - Implement Permissions.revoke. r=poiru,baku (306712f9ca)
- Bug 1197461 - e10s support for Permissions.revoke. r=poiru (bbda5df25d)
- Bug 1220184 - Eliminate Gingerbread compatibility. r=froydnj, r=nalexander (dce9e4f9e8)
- Bug 1238160 - Set docshell isolation mode. r=smaug (6ac2c21d1d)
- fix of Bug 1238160 (786a554a6a)
- Bug 1237364 - nsFrameLoader.cpp can set the userContextId directly using nsIDocShell, r=smaug (fea166caa3)
- Bug 1227861 - Add OriginAttributes getter/setter into nsIDocShell. r=smaug, sicking (4f8ac4f8ce)
- Bug 1246956 - Add originURI to Sessionstore. r=bz,Yoric,mfinkle (a1dcd44194)
- Bug 1246956 - Add loadReplace to session history. r=yoric r=mfinkle (662e66ad7e)
- Bug 1252811 - remove mIsInIsolatedMozBrowser from nsDocShell. r=smaug (b5927775b7)
- Bug 1250917 - Remove NS_SUCCESS_I_DID_SOMETHING; r=bholley (9dd6fe351b)
- Bug 1225053 - use null principal in gfxSVGGlyphs.cpp r=edwin (597d10fd4e)
- Bug 1226120 - add test for unknownContentType.xul and file types with a default handler but always ask set, r=mconley (dd95e18fd9)
- Bug 1254118 - Web protocol handlers need more testing coverage, r=felipe. (4fb6949d10)
- Bug 1253307 - Use a better function to load web handler apps in e10s. r=billm/mconley (28695be154)
- Bug 1155241: Check mInstanceOwner for nullptr in nsObjectLoadingContent::PluginDestroyed; r=smaug (ad60991e3e)
- Bug 1229220 - Update the scrollbar visibility prefs when initializing a TabChild; r=smaug (28997e0a6d)
- Bug 1252262 - Don't combine the client offset into the outer rect for the child process. r=jimm (f415c0418e)
- Bug 1249943 - Make test_basic_pan work on Fennec and Linux as well. r=botond (657c940be1)
- bit of bug 1245765 part 5 (82463f7eaa)
- Bug 1207512 - Remove the JS_IsRunning call in nsObjectLoadingContent::ScriptRequestPluginInstance; r=bholley (76047284a6)
- minor cleanup and Telemetry pieces (2765a1408d)
- Bug 1239463 - Do not assert when notifying an inactive document about changed content from the plugin crash notification. r=bz (03bf38a683)
- Bug 1192450 - Remove PlayPreview registration from Shumway. r=jet (9b6e131876)
- Bug 1200602 - Use the alternate content for <applet>. r=kmachulis (843fccf0aa)
- Bug 1236900 - Remove useless null check since mOwnerContent cannot be null. r=jst (a7e2d4b95c)
- bug 1257287 - add nsIDocShell::GetEdItingSession() (abd9022426)
- bug 1257287 - add nsIDocShell.tabChild r=smaug (6fe910c2b8)
- Bug 1256626: P1. Add NotifyBenchmarkResult ipc methods. r=jimm (02ab9cb0f6)
- Bug 1256626: P2. Use NotifyBenchmarkResult to save VP9 result. r=jimm (877277768c)
- Bug 1256626. Workaround Microsoft macro silliness. r=me (070e5a8cf0)
- Bug 1183915 - Put images dragged from content processes in the drag data in the parent. r=smaug (72906fab07)
- Bug 1228652 - Check for window.closed after flushing messages in navigateAndRestore. r=mossop (fd9d2b7d42)
- Bug 1217517 - nullcheck consumers for gKeywordURIFixup, r=jaws (adf6388856)
- Bug 951695 - Rename 'Character Encoding' to 'Text Encoding'. r=jaws (046e9644c2)
- Bug 1088710 - part 2: make it work on e10s, r=mconley (479366cba8)
- Bug 1147720 - Fix intermittent waitForDocLoadAndStopIt() timeouts by keeping a strong reference to the progress listener r=mak (4e9fa3f8c2)
- Bug 1228754, r=mak,bz (a33bc4da9c)
- Bug 1250482 - r=mak (0f69dd5f69)
- Bug 798249 - track when we're 'inside' a loadURI call when dealing with Stop() calls resulting from the same, r=mconley (e06b5b44e9)
- Bug 1254657 - change how we send Content:LoadURIResult to avoid upsetting RemoteWebProgress.jsm, r=mconley (915e7a1be6)
- 1253584 - Fix and enable browser_NetworkPrioritizer.js for e10s. r=mconley (9757c53ab6)
- Bug 1254522 - Make remote-browser.xml and browser.xml not depend on Firefox's browser.js. r=mconley (0a15acf7cc)
- Bug 621158 - make appcache use messaging for quota management, r=mayhemer,jaws (7e69707327)
- Bug 1255511 - Skip beforeunload prompts once nsIAppStartup shuttingDown returns true. r=Gijs (66bb2ce055)
- Bug 884355 - Warn about closing multiple tabs when "Never remember history" is enabled" r=gavin (7e61149b88)
- Bug 1228754 - bustage followup, rs=me,bustage Bug 1228754 - correct comments from bustage fix, rs=me, DONTBUILD (ef4d053a24)
- Bug 985777 - add a whitelist for URLs that we can switch to/from private browsing windows, r=ehsan (dc799a2213)
- Bug 963945 - Add about:addons URI as whitelisted so that it can be refocused instead of opening duplicate tabs in private browsing. r=jdm (8d5770a31c)
- reshuffle misspatch (8d2f7c2924)
- Bug 1236126 - fix oversight in principal handling for dialogs with host-less principals, r=aryx (1827672cae)
- align some stuff (256ffe50fa)
- Bug 1252239: Trap errors thrown by speculativeConnect. r=ttaubert (726d890a7b)
- Bug 1093153: enabled and re-factored browser_aboutHome.js to work correctly in e10s mode. r=mak (90518a9742)
- Bug 1239671 - Don't let session store override persistent cookies, r=ehsan (ee5b04fc97)
- Bug 1255685 - SessionStorage.jsm should use origin attributes from docshell. r=sicking (1f055f6ab2)
- Bug 1234021 - Catch exceptions raised by storage.length in SessionStorage.jsm, r=mconley (85ed6cf819)
- Bug 1192394 - Force an image load whenever the thumbnail file changes. r=adw (c194d868f9)
- let-var (f5e9a53170)
- Bug 529899 - Purge cookies on clean shutdown with "Keep cookies until I close Firefox" r=Yoric (1a15abd5f3)
- Bug 1071104 - Remove legacy "hasWrittenState" flag from SessionWorker r=yoric (484e1ac5b1)
- Bug 1198898 - Determining number of tabs/windows restored by Session Restore;r=mconley (5bda3ecc88)
- Bug 1245891 - Changing Session Restore Talos tests to include the time to restore actual tabs;r=mconley (d7c5957192)
- Bug 1210940 - New Browser Component: Newtab r=Mardak (a59fd6c88e)
- Bug 1219454 - Replace 'show' with 'receive' in about:permissions and control center for consistent messaging around notifications and to account for new Push permission. r=MattN (60cc4211ec)
- align (b7bc52abef)
- Bug 868711 - Remove confusing expression in _trackSlowStartup. r=gavin (b2d5609496)
- Bug 1231112 - work around tab groups migration issue in safe mode, r=ttaubert (e71ceebd3f)
- Bug 1249608 - Don't run UI migration steps with new profiles. r=gijs (b6f92e45ba)
- Bug 910431 - Electrolysis: Permission code followup. f=felipe (1bf556eeea)
- align some e10s (05aa76a560)
- Bug 1250109 - Change DOMEventTargetHelper subclasses to not assume that GetOwner() is non-null, since it can be nulled out by navigation. r=bzbarsky (deb440c2a4)
- Bug 908277 - Prevent permission UI errors when PopupNotifications is not available. r=dolske (dcefc44921)
- Bug 1199805 - Fix displayURI typo on site permission prompts. r=bgrins (78dbd44c98)
- Bug 1246028 Implement chrome.commands.getAll. r=kmag (b263fbe137)
- Bug 1242557 - Import missing commands API schema file. r=kmag (b930a95b2f)
- Bug 1249689 - replace bootstrapScope with an activeAddons Map() that contains it r=mossop (00bef48286)
- Bug 1249689 - generate and provide a Symbol for each add-on on startup r=mossop (bd7b936bcb)
- Bug 685155: Treat symbolic links the same as proxy files in the Add-on Manager. r=Mossop (4f458140df)
- Bug 1244248: Test that the certificate database is cached. r=rhelmer (a5cc6e8d75)
- Bug 1250784: Part 1 - [webext] Add support for options_ui via inline browsers in the Add-on Manager. r=Mossop (62b6efe654)
- Bug 1250784: Part 2 - [webext] Fix some issues that cause noisy tests. r=Mossop (4ada262435)
- Bug 1252250 - Implement browser.bookmarks.removeTree(), r=kmag r=mak (7e67b5ccf1)
- Bug 1253652 - Fix browser.bookmarks.move() and add tests for it. r=kmag, r=mak (151a8bf09a)
- Bug 1250784: Follow-up: Fix merge conflict in bookmarks API. r=me (3db6ea04ea)
- Bug 1251269 - Implement browser.bookmarks.getRecent(), r=kmag r=mak (d4564d6aee)
- Bug 1246614 - Check if system add-ons directory exists before trying to clean it. r=mossop (c2d59f34a8)
- Bug 1041514 - Don't show default browser prompt if a user opts out in the installer. r=jimm (b2ccf151f7)
- use ArcticFoxHTML and ArcticFoxURL keys, not to mix with Firefox (e6d36fdd78)
- Bug 1207784 - skip permission hooks in createOffer when called from hiddenWindow (add-ons). r=mt (b90a4c940f)
- Bug 1209766 - Update validation message before showing notification. r=khuey (8c9a865969)
- Bug 1206560 - Show the site favicon in XUL notifications. r=jaws (e5bad6ea20)
- Bug 1248497 창 Add promise support to the sendMessage APIs. r=billm (e26a805c92)
- Bug 911216 - Part 1: Add tests directly testing content Promise constructor resolved with chrome Promise. r=bz (1b2f1ec6b8)
- Bug 911216 - Part 2: Add self-hosting intrinsic for calling wrapped functions without wrapper security checks. r=efaust,bholley (de086e8422)
- Bug 911216 - Part 3: Allow wrapped self-hosted functions and intrinsics in the callFunction debug check. r=efaust (c02e6337fe)
- Bug 1251921 - Do not call debugger hooks with half-initialized frame if InterpeterFrame::prologue fails. (r=jorendorff) (9873720345)
- Bug 1256342. Fix typed array iteration to work correctly over Xrays. r=till (6a7f5c12c6)
- Bug 1256376. Fix forEach on typed arrays to work over Xrays from web extension sandboxes. r=till (ab19703ab5)
- Bug 1253436 - Add __repr__ to BaseLibrary and BaseProgram; r=glandium (a04a16f2ac)
- Bug 1238064 - Update docs to reflect new workflow. r=mossop (c7670ac086)
- Bug 911216 - Part 4: Add self-hosting intrinsic for creating arrays in other compartments. r=efaust (37b14521fb)
- Bug 1233497 - Temporarily allow unsafe CPOWs in Promise-backend.js and Task.jsm. r=billm (d2672a456a)
- Bug 1225041 - Implement ES6 Annex B.3.5 for direct eval. (r=jorendorff) (daf24f0e34)
- Bug 1254185 - Deal with missing arguments assigned to block bindings. (r=jimb) (3ce53dcd06)
- Bug 1250506 - check if node is acceptable as a child before creating an accessible for it, r=davidb (5960ba726d)
- Bug 1251941 - aria::GetRoleMap should take element, r=davidb (e9ee4e20ea)
- Bug 1251944 - get rid of nsCoreUtils::GetRoleContent, r=davidb (a2bf199bb4)
- Bug 1257030 - Add support for supplying preexisting stack instead of capturing one for use as the async parent stack of CallbackObject. r=bz,tromey (a4ddb41fac)
- Bug 1232291 - Non-used header in MessagePortService.*, r=smaug (1e2398e314)
- Bug 1255655 - Const-ify sWAIRoleMaps. r=tbsaunde. (09653e44af)
- align (24667f7952)
- Bug 1253438 - Expose Push observer notification topics. r=markh (b62a068d4b)
2024-03-14 23:40:15 +08:00

643 lines
18 KiB
JavaScript

/* 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");
loader.lazyRequireGetter(this, "setIgnoreLayoutChanges",
"devtools/server/actors/layout", true);
exports.setIgnoreLayoutChanges = (...args) =>
this.setIgnoreLayoutChanges(...args);
/**
* 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.isMozBrowserOrApp) {
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.isMozBrowserOrApp) {
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;