diff options
author | Matt A. Tobin <email@mattatobin.com> | 2016-10-16 19:34:53 -0400 |
---|---|---|
committer | Matt A. Tobin <email@mattatobin.com> | 2016-10-16 19:34:53 -0400 |
commit | 81805ce3f63e2e4a799bd54f174083c58a9b5640 (patch) | |
tree | 6e13374b213ac9b2ae74c25d8aac875faf71fdd0 /toolkit/devtools/shared/DOMHelpers.jsm | |
parent | 28c8da71bf521bb3ee76f27b8a241919e24b7cd5 (diff) | |
download | palemoon-gre-81805ce3f63e2e4a799bd54f174083c58a9b5640.tar.gz |
Move Mozilla DevTools to Platform - Part 3: Merge the browser/devtools and toolkit/devtools adjusting for directory collisions
Diffstat (limited to 'toolkit/devtools/shared/DOMHelpers.jsm')
-rw-r--r-- | toolkit/devtools/shared/DOMHelpers.jsm | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/toolkit/devtools/shared/DOMHelpers.jsm b/toolkit/devtools/shared/DOMHelpers.jsm new file mode 100644 index 000000000..4c2bd522d --- /dev/null +++ b/toolkit/devtools/shared/DOMHelpers.jsm @@ -0,0 +1,156 @@ +/* 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/. */ + +const Ci = Components.interfaces; +const Cu = Components.utils; +Cu.import("resource://gre/modules/Services.jsm"); + +this.EXPORTED_SYMBOLS = ["DOMHelpers"]; + +/** + * DOMHelpers + * Makes DOM traversal easier. Goes through iframes. + * + * @constructor + * @param nsIDOMWindow aWindow + * The content window, owning the document to traverse. + */ +this.DOMHelpers = function DOMHelpers(aWindow) { + if (!aWindow) { + throw new Error("window can't be null or undefined"); + } + this.window = aWindow; +}; + +DOMHelpers.prototype = { + getParentObject: function Helpers_getParentObject(node) + { + let parentNode = node ? node.parentNode : null; + + if (!parentNode) { + // Documents have no parentNode; Attr, Document, DocumentFragment, Entity, + // and Notation. top level windows have no parentNode + if (node && node == this.window.Node.DOCUMENT_NODE) { + // document type + if (node.defaultView) { + let embeddingFrame = node.defaultView.frameElement; + if (embeddingFrame) + return embeddingFrame.parentNode; + } + } + // a Document object without a parentNode or window + return null; // top level has no parent + } + + if (parentNode.nodeType == this.window.Node.DOCUMENT_NODE) { + if (parentNode.defaultView) { + return parentNode.defaultView.frameElement; + } + // parent is document element, but no window at defaultView. + return null; + } + + if (!parentNode.localName) + return null; + + return parentNode; + }, + + getChildObject: function Helpers_getChildObject(node, index, previousSibling, + showTextNodesWithWhitespace) + { + if (!node) + return null; + + if (node.contentDocument) { + // then the node is a frame + if (index == 0) { + return node.contentDocument.documentElement; // the node's HTMLElement + } + return null; + } + + if (node.getSVGDocument) { + let svgDocument = node.getSVGDocument(); + if (svgDocument) { + // then the node is a frame + if (index == 0) { + return svgDocument.documentElement; // the node's SVGElement + } + return null; + } + } + + let child = null; + if (previousSibling) // then we are walking + child = this.getNextSibling(previousSibling); + else + child = this.getFirstChild(node); + + if (showTextNodesWithWhitespace) + return child; + + for (; child; child = this.getNextSibling(child)) { + if (!this.isWhitespaceText(child)) + return child; + } + + return null; // we have no children worth showing. + }, + + getFirstChild: function Helpers_getFirstChild(node) + { + let SHOW_ALL = Components.interfaces.nsIDOMNodeFilter.SHOW_ALL; + this.treeWalker = node.ownerDocument.createTreeWalker(node, + SHOW_ALL, null); + return this.treeWalker.firstChild(); + }, + + getNextSibling: function Helpers_getNextSibling(node) + { + let next = this.treeWalker.nextSibling(); + + if (!next) + delete this.treeWalker; + + return next; + }, + + isWhitespaceText: function Helpers_isWhitespaceText(node) + { + return node.nodeType == this.window.Node.TEXT_NODE && + !/[^\s]/.exec(node.nodeValue); + }, + + destroy: function Helpers_destroy() + { + delete this.window; + delete this.treeWalker; + }, + + /** + * A simple way to be notified (once) when a window becomes + * interactive (DOMContentLoaded). + * + * It is based on the chromeEventHandler. This is useful when + * chrome iframes are loaded in content docshells (in Firefox + * tabs for example). + */ + onceDOMReady: function Helpers_onLocationChange(callback) { + let window = this.window; + let docShell = window.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell); + let onReady = function(event) { + if (event.target == window.document) { + docShell.chromeEventHandler.removeEventListener("DOMContentLoaded", onReady, false); + // If in `callback` the URL of the window is changed and a listener to DOMContentLoaded + // is attached, the event we just received will be also be caught by the new listener. + // We want to avoid that so we execute the callback in the next queue. + Services.tm.mainThread.dispatch(callback, 0); + } + } + docShell.chromeEventHandler.addEventListener("DOMContentLoaded", onReady, false); + } +}; |