Bug 897960 - walker should support mozbrowser iframes. r=dcamp
authorPaul Rouget <paul@mozilla.com>
Sun, 08 Sep 2013 11:01:00 +0200
changeset 146127 bbb54cde82c33ad1aa657ff69c9a4557e2908ddf
parent 146126 89a059433037889ef254dd7f587546be126ca3c9
child 146128 bd1111b18be1147eaf1b6222b586481e4a216c35
child 146133 78128ed0ce263b1d51076c15cb5e0f247a59fa36
push id25242
push useremorley@mozilla.com
push dateMon, 09 Sep 2013 12:13:52 +0000
treeherdermozilla-central@218d4334d29e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdcamp
bugs897960
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 897960 - walker should support mozbrowser iframes. r=dcamp
toolkit/devtools/server/actors/inspector.js
--- a/toolkit/devtools/server/actors/inspector.js
+++ b/toolkit/devtools/server/actors/inspector.js
@@ -763,21 +763,24 @@ var WalkerActor = protocol.ActorClass({
 
   /**
    * Create the WalkerActor
    * @param DebuggerServerConnection conn
    *    The server connection.
    */
   initialize: function(conn, tabActor, options) {
     protocol.Actor.prototype.initialize.call(this, conn);
-    this.rootDoc = tabActor.window.document;
+    this.rootWin = tabActor.window;
+    this.rootDoc = this.rootWin.document;
     this._refMap = new Map();
     this._pendingMutations = [];
     this._activePseudoClassLocks = new Set();
 
+    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.
     this._orphaned = new Set();
 
     // The client can tell the walker that it is interested in a node
     // even when it is orphaned with the `retainNode` method.  This
     // list contains orphaned nodes that were so retained.
@@ -924,17 +927,17 @@ var WalkerActor = protocol.ActorClass({
     this._unhighlight();
 
     if (!node ||
         !node.rawNode ||
          node.rawNode.nodeType !== Ci.nsIDOMNode.ELEMENT_NODE) {
       return;
     }
 
-    LayoutHelpers.scrollIntoViewIfNeeded(node.rawNode);
+    this.layoutHelpers.scrollIntoViewIfNeeded(node.rawNode);
     DOMUtils.addPseudoClassLock(node.rawNode, HIGHLIGHTED_PSEUDO_CLASS);
     this._highlightTimeout = setTimeout(this._unhighlight.bind(this), HIGHLIGHTED_TIMEOUT);
 
   }, { request: { node: Arg(0, "nullable:domnode") }}),
 
   /**
    * Watch the given document node for mutations using the DOM observer
    * API.
@@ -988,17 +991,17 @@ var WalkerActor = protocol.ActorClass({
    * @param NodeActor node
    *    The node whose parents are requested.
    * @param object options
    *    Named options, including:
    *    `sameDocument`: If true, parents will be restricted to the same
    *      document as the node.
    */
   parents: method(function(node, options={}) {
-    let walker = documentWalker(node.rawNode);
+    let walker = documentWalker(node.rawNode, this.rootWin);
     let parents = [];
     let cur;
     while((cur = walker.parentNode())) {
       if (options.sameDocument && cur.ownerDocument != node.rawNode.ownerDocument) {
         break;
       }
       parents.push(this._ref(cur));
     }
@@ -1009,17 +1012,17 @@ var WalkerActor = protocol.ActorClass({
       sameDocument: Option(1)
     },
     response: {
       nodes: RetVal("array:domnode")
     },
   }),
 
   parentNode: function(node) {
-    let walker = documentWalker(node.rawNode);
+    let walker = documentWalker(node.rawNode, this.rootWin);
     let parent = walker.parentNode();
     if (parent) {
       return this._ref(parent);
     }
     return null;
   },
 
   /**
@@ -1070,17 +1073,17 @@ var WalkerActor = protocol.ActorClass({
       return;
     }
 
     if (node.retained) {
       // Forcing a retained node to go away.
       this._retainedOrphans.delete(node);
     }
 
-    let walker = documentWalker(node.rawNode);
+    let walker = documentWalker(node.rawNode, this.rootWin);
 
     let child = walker.firstChild();
     while (child) {
       let childActor = this._refMap.get(child);
       if (childActor) {
         this.releaseNode(childActor, options);
       }
       child = walker.nextSibling();
@@ -1097,17 +1100,17 @@ var WalkerActor = protocol.ActorClass({
   /**
    * Add any nodes between `node` and the walker's root node that have not
    * yet been seen by the client.
    */
   ensurePathToRoot: function(node, newParents=new Set()) {
     if (!node) {
       return newParents;
     }
-    let walker = documentWalker(node.rawNode);
+    let walker = documentWalker(node.rawNode, this.rootWin);
     let cur;
     while ((cur = walker.parentNode())) {
       let parent = this._refMap.get(cur);
       if (!parent) {
         // This parent didn't exist, so hasn't been seen by the client yet.
         newParents.add(this._ref(cur));
       } else {
         // This parent did exist, so the client knows about it.
@@ -1147,18 +1150,18 @@ var WalkerActor = protocol.ActorClass({
     }
     let maxNodes = options.maxNodes || -1;
     if (maxNodes == -1) {
       maxNodes = Number.MAX_VALUE;
     }
 
     // We're going to create a few document walkers with the same filter,
     // make it easier.
-    let filteredWalker = function(node) {
-      return documentWalker(node, options.whatToShow);
+    let filteredWalker = (node) => {
+      return documentWalker(node, this.rootWin, options.whatToShow);
     }
 
     // Need to know the first and last child.
     let rawNode = node.rawNode;
     let firstChild = filteredWalker(rawNode).firstChild();
     let lastChild = filteredWalker(rawNode).lastChild();
 
     if (!firstChild) {
@@ -1231,17 +1234,17 @@ var WalkerActor = protocol.ActorClass({
    *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
    *
    * @returns an object with three items:
    *    hasFirst: true if the first child of the node is included in the list.
    *    hasLast: true if the last child of the node is included in the list.
    *    nodes: Child nodes returned by the request.
    */
   siblings: method(function(node, options={}) {
-    let parentNode = documentWalker(node.rawNode).parentNode();
+    let parentNode = documentWalker(node.rawNode, this.rootWin).parentNode();
     if (!parentNode) {
       return {
         hasFirst: true,
         hasLast: true,
         nodes: [node]
       };
     }
 
@@ -1257,32 +1260,32 @@ var WalkerActor = protocol.ActorClass({
    * might be inefficient, be careful.
    *
    * @param object options
    *    Named options:
    *    `whatToShow`: A bitmask of node types that should be included.  See
    *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
    */
   nextSibling: method(function(node, options={}) {
-    let walker = documentWalker(node.rawNode, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
+    let walker = documentWalker(node.rawNode, this.rootWin, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
     let sibling = walker.nextSibling();
     return sibling ? this._ref(sibling) : null;
   }, traversalMethod),
 
   /**
    * Get the previous sibling of a given node.  Getting nodes one at a time
    * might be inefficient, be careful.
    *
    * @param object options
    *    Named options:
    *    `whatToShow`: A bitmask of node types that should be included.  See
    *       https://developer.mozilla.org/en-US/docs/Web/API/NodeFilter.
    */
   previousSibling: method(function(node, options={}) {
-    let walker = documentWalker(node.rawNode, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
+    let walker = documentWalker(node.rawNode, this.rootWin, options.whatToShow || Ci.nsIDOMNodeFilter.SHOW_ALL);
     let sibling = walker.previousSibling();
     return sibling ? this._ref(sibling) : null;
   }, traversalMethod),
 
   /**
    * Helper function for the `children` method: Read forward in the sibling
    * list into an array with `count` items, including the current node.
    */
@@ -1377,17 +1380,17 @@ var WalkerActor = protocol.ActorClass({
    */
   addPseudoClassLock: method(function(node, pseudo, options={}) {
     this._addPseudoClassLock(node, pseudo);
 
     if (!options.parents) {
       return;
     }
 
-    let walker = documentWalker(node.rawNode);
+    let walker = documentWalker(node.rawNode, this.rootWin);
     let cur;
     while ((cur = walker.parentNode())) {
       let curNode = this._ref(cur);
       this._addPseudoClassLock(curNode, pseudo);
     }
   }, {
     request: {
       node: Arg(0, "domnode"),
@@ -1458,17 +1461,17 @@ var WalkerActor = protocol.ActorClass({
    */
   removePseudoClassLock: method(function(node, pseudo, options={}) {
     this._removePseudoClassLock(node, pseudo);
 
     if (!options.parents) {
       return;
     }
 
-    let walker = documentWalker(node.rawNode);
+    let walker = documentWalker(node.rawNode, this.rootWin);
     let cur;
     while ((cur = walker.parentNode())) {
       let curNode = this._ref(cur);
       this._removePseudoClassLock(curNode, pseudo);
     }
   }, {
     request: {
       node: Arg(0, "domnode"),
@@ -1726,17 +1729,17 @@ var WalkerActor = protocol.ActorClass({
         mutation.removed = removedActors;
         mutation.added = addedActors;
       }
       this.queueMutation(mutation);
     }
   },
 
   onFrameLoad: function(window) {
-    let frame = window.frameElement;
+    let frame = this.layoutHelpers.getFrameElement(window);
     if (!frame && !this.rootDoc) {
       this.rootDoc = window.document;
       this.rootNode = this.document();
       this.queueMutation({
         type: "newRoot",
         target: this.rootNode.form()
       });
     }
@@ -1761,17 +1764,17 @@ var WalkerActor = protocol.ActorClass({
 
   // Returns true if domNode is in window or a subframe.
   _childOfWindow: function(window, domNode) {
     let win = nodeDocument(domNode).defaultView;
     while (win) {
       if (win === window) {
         return true;
       }
-      win = win.frameElement;
+      win = this.layoutHelpers.getFrameElement(win);
     }
     return false;
   },
 
   onFrameUnload: function(window) {
     // Any retained orphans that belong to this document
     // or its children need to be released, and a mutation sent
     // to notify of that.
@@ -1805,17 +1808,17 @@ var WalkerActor = protocol.ActorClass({
       this.rootNode = null;
     }
 
     this.queueMutation({
       type: "documentUnload",
       target: documentActor.actorID
     });
 
-    let walker = documentWalker(doc);
+    let walker = documentWalker(doc, this.rootWin);
     let parentNode = walker.parentNode();
     if (parentNode) {
       // Send a childList mutation on the frame so that clients know
       // they should reread the children list.
       this.queueMutation({
         type: "childList",
         target: this._refMap.get(parentNode).actorID,
         added: [],
@@ -2262,36 +2265,37 @@ var InspectorFront = exports.InspectorFr
         return pageStyle;
       });
     });
   }, {
     impl: "_getPageStyle"
   })
 });
 
-function documentWalker(node, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) {
-  return new DocumentWalker(node, whatToShow, whitespaceTextFilter, false);
+function documentWalker(node, rootWin, whatToShow=Ci.nsIDOMNodeFilter.SHOW_ALL) {
+  return new DocumentWalker(node, rootWin, whatToShow, whitespaceTextFilter, false);
 }
 
 // Exported for test purposes.
 exports._documentWalker = documentWalker;
 
 function nodeDocument(node) {
   return node.ownerDocument || (node.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE ? node : null);
 }
 
 /**
  * Similar to a TreeWalker, except will dig in to iframes and it doesn't
  * implement the good methods like previousNode and nextNode.
  *
  * See TreeWalker documentation for explanations of the methods.
  */
-function DocumentWalker(aNode, aShow, aFilter, aExpandEntityReferences)
+function DocumentWalker(aNode, aRootWin, aShow, aFilter, aExpandEntityReferences)
 {
   let doc = nodeDocument(aNode);
+  this.layoutHelpers = new LayoutHelpers(aRootWin);
   this.walker = doc.createTreeWalker(doc,
     aShow, aFilter, aExpandEntityReferences);
   this.walker.currentNode = aNode;
   this.filter = aFilter;
 }
 
 DocumentWalker.prototype = {
   get node() this.walker.node,
@@ -2320,19 +2324,21 @@ DocumentWalker.prototype = {
   parentNode: function DW_parentNode()
   {
     let currentNode = this.walker.currentNode;
     let parentNode = this.walker.parentNode();
 
     if (!parentNode) {
       if (currentNode && currentNode.nodeType == Ci.nsIDOMNode.DOCUMENT_NODE
           && currentNode.defaultView) {
-        let embeddingFrame = currentNode.defaultView.frameElement;
-        if (embeddingFrame) {
-          return this._reparentWalker(embeddingFrame);
+
+        let window = currentNode.defaultView;
+        let frame = this.layoutHelpers.getFrameElement(window);
+        if (frame) {
+          return this._reparentWalker(frame);
         }
       }
       return null;
     }
 
     return parentNode;
   },