123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167 |
- /* 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 = Components.interfaces;
- const Cu = Components.utils;
- const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
- const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
- const nodeFilterConstants = require("devtools/shared/dom-node-filter-constants");
- 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 = nodeFilterConstants.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, targetURL) {
- 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);
- }
- };
- if ((window.document.readyState == "complete" ||
- window.document.readyState == "interactive") &&
- window.location.href == targetURL) {
- Services.tm.mainThread.dispatch(callback, 0);
- } else {
- docShell.chromeEventHandler.addEventListener("DOMContentLoaded", onReady, false);
- }
- }
- };
|