Merge mozilla-central and fx-team
authorEd Morley <emorley@mozilla.com>
Mon, 09 Sep 2013 13:30:13 +0100
changeset 146207 3d50a4b732af31f5d607be382b26c70bb5092913
parent 146137 218d4334d29e5d55bcb70498bb66228d8b4463c5 (current diff)
parent 146206 f667bfe86c211736d286a75d98774277bec16b1b (diff)
child 146208 7d73a40c6ee92f5bbc9975eee13b046554583135
push id25245
push userryanvm@gmail.com
push dateMon, 09 Sep 2013 20:57:55 +0000
treeherdermozilla-central@a468b2e34b04 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
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
Merge mozilla-central and fx-team
--- a/browser/devtools/commandline/BuiltinCommands.jsm
+++ b/browser/devtools/commandline/BuiltinCommands.jsm
@@ -1742,17 +1742,18 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
       if (!fullpage) {
         if (!node) {
           left = window.scrollX;
           top = window.scrollY;
           width = window.innerWidth;
           height = window.innerHeight;
         } else {
-          let rect = LayoutHelpers.getRect(node, window);
+          let lh = new LayoutHelpers(window);
+          let rect = lh.getRect(node, window);
           top = rect.top;
           left = rect.left;
           width = rect.width;
           height = rect.height;
         }
       } else {
         width = window.innerWidth + window.scrollMaxX;
         height = window.innerHeight + window.scrollMaxY;
--- a/browser/devtools/debugger/debugger-controller.js
+++ b/browser/devtools/debugger/debugger-controller.js
@@ -14,17 +14,16 @@ const FETCH_SOURCE_RESPONSE_DELAY = 50; 
 const FRAME_STEP_CLEAR_DELAY = 100; // ms
 const CALL_STACK_PAGE_SIZE = 25; // frames
 
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
 let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js").Promise;
 Cu.import("resource:///modules/source-editor.jsm");
-Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/BreadcrumbsWidget.jsm");
 Cu.import("resource:///modules/devtools/SideMenuWidget.jsm");
 Cu.import("resource:///modules/devtools/VariablesView.jsm");
 Cu.import("resource:///modules/devtools/VariablesViewController.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Parser",
   "resource:///modules/devtools/Parser.jsm");
@@ -35,16 +34,23 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 Object.defineProperty(this, "NetworkHelper", {
   get: function() {
     return devtools.require("devtools/toolkit/webconsole/network-helper");
   },
   configurable: true,
   enumerable: true
 });
 
+Object.defineProperty(this, "DevtoolsHelpers", {
+  get: function() {
+    return devtools.require("devtools/shared/helpers");
+  },
+  configurable: true,
+  enumerable: true
+});
 
 /**
  * Object defining the debugger controller components.
  */
 let DebuggerController = {
   /**
    * Initializes the debugger controller.
    */
--- a/browser/devtools/debugger/debugger-toolbar.js
+++ b/browser/devtools/debugger/debugger-toolbar.js
@@ -33,20 +33,20 @@ ToolbarView.prototype = {
     this._instrumentsPaneToggleButton = document.getElementById("instruments-pane-toggle");
     this._resumeOrderPanel = document.getElementById("resumption-order-panel");
     this._resumeButton = document.getElementById("resume");
     this._stepOverButton = document.getElementById("step-over");
     this._stepInButton = document.getElementById("step-in");
     this._stepOutButton = document.getElementById("step-out");
     this._chromeGlobals = document.getElementById("chrome-globals");
 
-    let resumeKey = LayoutHelpers.prettyKey(document.getElementById("resumeKey"), true);
-    let stepOverKey = LayoutHelpers.prettyKey(document.getElementById("stepOverKey"), true);
-    let stepInKey = LayoutHelpers.prettyKey(document.getElementById("stepInKey"), true);
-    let stepOutKey = LayoutHelpers.prettyKey(document.getElementById("stepOutKey"), true);
+    let resumeKey = DevtoolsHelpers.prettyKey(document.getElementById("resumeKey"), true);
+    let stepOverKey = DevtoolsHelpers.prettyKey(document.getElementById("stepOverKey"), true);
+    let stepInKey = DevtoolsHelpers.prettyKey(document.getElementById("stepInKey"), true);
+    let stepOutKey = DevtoolsHelpers.prettyKey(document.getElementById("stepOutKey"), true);
     this._resumeTooltip = L10N.getFormatStr("resumeButtonTooltip", resumeKey);
     this._pauseTooltip = L10N.getFormatStr("pauseButtonTooltip", resumeKey);
     this._stepOverTooltip = L10N.getFormatStr("stepOverTooltip", stepOverKey);
     this._stepInTooltip = L10N.getFormatStr("stepInTooltip", stepInKey);
     this._stepOutTooltip = L10N.getFormatStr("stepOutTooltip", stepOutKey);
 
     this._instrumentsPaneToggleButton.addEventListener("mousedown", this._onTogglePanesPressed, false);
     this._resumeButton.addEventListener("mousedown", this._onResumePressed, false);
@@ -724,22 +724,22 @@ FilterView.prototype = {
     this._functionOperatorLabel = document.getElementById("function-operator-label");
     this._tokenOperatorButton = document.getElementById("token-operator-button");
     this._tokenOperatorLabel = document.getElementById("token-operator-label");
     this._lineOperatorButton = document.getElementById("line-operator-button");
     this._lineOperatorLabel = document.getElementById("line-operator-label");
     this._variableOperatorButton = document.getElementById("variable-operator-button");
     this._variableOperatorLabel = document.getElementById("variable-operator-label");
 
-    this._fileSearchKey = LayoutHelpers.prettyKey(document.getElementById("fileSearchKey"), true);
-    this._globalSearchKey = LayoutHelpers.prettyKey(document.getElementById("globalSearchKey"), true);
-    this._filteredFunctionsKey = LayoutHelpers.prettyKey(document.getElementById("functionSearchKey"), true);
-    this._tokenSearchKey = LayoutHelpers.prettyKey(document.getElementById("tokenSearchKey"), true);
-    this._lineSearchKey = LayoutHelpers.prettyKey(document.getElementById("lineSearchKey"), true);
-    this._variableSearchKey = LayoutHelpers.prettyKey(document.getElementById("variableSearchKey"), true);
+    this._fileSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("fileSearchKey"), true);
+    this._globalSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("globalSearchKey"), true);
+    this._filteredFunctionsKey = DevtoolsHelpers.prettyKey(document.getElementById("functionSearchKey"), true);
+    this._tokenSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("tokenSearchKey"), true);
+    this._lineSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("lineSearchKey"), true);
+    this._variableSearchKey = DevtoolsHelpers.prettyKey(document.getElementById("variableSearchKey"), true);
 
     this._searchbox.addEventListener("click", this._onClick, false);
     this._searchbox.addEventListener("select", this._onSearch, false);
     this._searchbox.addEventListener("input", this._onSearch, false);
     this._searchbox.addEventListener("keypress", this._onKeyPress, false);
     this._searchbox.addEventListener("blur", this._onBlur, false);
 
     this._globalOperatorButton.setAttribute("label", SEARCH_GLOBAL_FLAG);
--- a/browser/devtools/debugger/test/browser_dbg_pause-resume.js
+++ b/browser/devtools/debugger/test/browser_dbg_pause-resume.js
@@ -3,54 +3,54 @@
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/
  */
 
 var gPane = null;
 var gTab = null;
 var gDebugger = null;
 var gView = null;
-var gLH = null;
+var gDH = null;
 var gL10N = null;
 
 function test() {
   debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) {
     gTab = aTab;
     gPane = aPane;
     gDebugger = gPane.panelWin;
     gView = gDebugger.DebuggerView;
-    gLH = gDebugger.LayoutHelpers;
+    gDH = gDebugger.DevtoolsHelpers;
     gL10N = gDebugger.L10N;
 
     testPause();
   });
 }
 
 function testPause() {
   is(gDebugger.DebuggerController.activeThread.paused, false,
     "Should be running after debug_tab_pane.");
 
   let button = gDebugger.document.getElementById("resume");
   is(button.getAttribute("tooltiptext"),
      gL10N.getFormatStr("pauseButtonTooltip",
-      gLH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
+      gDH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
     "Button tooltip should be pause when running.");
 
   gDebugger.DebuggerController.activeThread.addOneTimeListener("paused", function() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       let frames = gDebugger.DebuggerView.StackFrames.widget._list;
       let childNodes = frames.childNodes;
 
       is(gDebugger.DebuggerController.activeThread.paused, true,
         "Should be paused after an interrupt request.");
 
       is(button.getAttribute("tooltiptext"),
          gL10N.getFormatStr("resumeButtonTooltip",
-          gLH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
+          gDH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
         "Button tooltip should be resume when paused.");
 
       is(frames.querySelectorAll(".dbg-stackframe").length, 0,
         "Should have no frames when paused in the main loop.");
 
       testResume();
     }}, 0);
   });
@@ -65,17 +65,17 @@ function testResume() {
     Services.tm.currentThread.dispatch({ run: function() {
 
       is(gDebugger.DebuggerController.activeThread.paused, false,
         "Should be paused after an interrupt request.");
 
       let button = gDebugger.document.getElementById("resume");
       is(button.getAttribute("tooltiptext"),
          gL10N.getFormatStr("pauseButtonTooltip",
-          gLH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
+          gDH.prettyKey(gDebugger.document.getElementById("resumeKey"))),
         "Button tooltip should be pause when running.");
 
       closeDebuggerAndFinish();
     }}, 0);
   });
 
   EventUtils.sendMouseEvent({ type: "mousedown" },
     gDebugger.document.getElementById("resume"),
@@ -83,11 +83,11 @@ function testResume() {
 }
 
 registerCleanupFunction(function() {
   removeTab(gTab);
   gPane = null;
   gTab = null;
   gDebugger = null;
   gView = null;
-  gLH = null;
+  gDH = null;
   gL10N = null;
 });
--- a/browser/devtools/inspector/breadcrumbs.js
+++ b/browser/devtools/inspector/breadcrumbs.js
@@ -6,17 +6,16 @@
 
 const {Cc, Cu, Ci} = require("chrome");
 
 const PSEUDO_CLASSES = [":hover", ":active", ":focus"];
 const ENSURE_SELECTION_VISIBLE_DELAY = 50; // ms
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
-Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 
 let promise = require("sdk/core/promise");
 
 const LOW_PRIORITY_ELEMENTS = {
   "HEAD": true,
   "BASE": true,
   "BASEFONT": true,
--- a/browser/devtools/inspector/highlighter.js
+++ b/browser/devtools/inspector/highlighter.js
@@ -88,16 +88,17 @@ function LocalHighlighter(aTarget, aInsp
 {
   this.target = aTarget;
   this.tab = aTarget.tab;
   this.toolbox = aToolbox;
   this.browser = this.tab.linkedBrowser;
   this.chromeDoc = this.tab.ownerDocument;
   this.chromeWin = this.chromeDoc.defaultView;
   this.inspector = aInspector
+  this.layoutHelpers = new LayoutHelpers(this.browser.contentWindow);
 
   EventEmitter.decorate(this);
 
   this._init();
 }
 
 LocalHighlighter.prototype = {
   get selection() {
@@ -220,17 +221,17 @@ LocalHighlighter.prototype = {
       if (this.selection.reason != "navigateaway") {
         this.disabled = false;
       }
       this.show();
       this.updateInfobar();
       this.invalidateSize();
       if (!this._highlighting &&
           this.selection.reason != "highlighter") {
-        LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
+        this.layoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
       }
     } else {
       this.disabled = true;
       this.hide();
     }
   },
 
   /**
@@ -250,17 +251,17 @@ LocalHighlighter.prototype = {
     // here, do the check manually.
     if (!this.selection.node ||
         !this.selection.node.ownerDocument ||
         !this.selection.node.ownerDocument.defaultView) {
       return;
     }
 
     let clientRect = this.selection.node.getBoundingClientRect();
-    let rect = LayoutHelpers.getDirtyRect(this.selection.node);
+    let rect = this.layoutHelpers.getDirtyRect(this.selection.node);
     this.highlightRectangle(rect);
 
     this.moveInfobar();
 
     if (this._highlighting) {
       this.showOutline();
       this.emit("highlighting");
     }
@@ -456,17 +457,17 @@ LocalHighlighter.prototype = {
     let texthbox = this.chromeDoc.createElement("hbox");
     texthbox.className = "highlighter-nodeinfobar-text";
     texthbox.setAttribute("align", "center");
     texthbox.setAttribute("flex", "1");
 
     texthbox.addEventListener("mousedown", function(aEvent) {
       // On click, show the node:
       if (this.selection.isElementNode()) {
-        LayoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
+        this.layoutHelpers.scrollIntoViewIfNeeded(this.selection.node);
       }
     }.bind(this), true);
 
     texthbox.appendChild(tagNameLabel);
     texthbox.appendChild(idLabel);
     texthbox.appendChild(classesBox);
     texthbox.appendChild(pseudoClassesBox);
 
@@ -509,17 +510,17 @@ LocalHighlighter.prototype = {
 
     let oldRect = this._contentRect;
 
     if (oldRect && aRect.top == oldRect.top && aRect.left == oldRect.left &&
         aRect.width == oldRect.width && aRect.height == oldRect.height) {
       return; // same rectangle
     }
 
-    let aRectScaled = LayoutHelpers.getZoomedRect(this.win, aRect);
+    let aRectScaled = this.layoutHelpers.getZoomedRect(this.win, aRect);
 
     if (aRectScaled.left >= 0 && aRectScaled.top >= 0 &&
         aRectScaled.width > 0 && aRectScaled.height > 0) {
 
       this.showOutline();
 
       // The bottom div and the right div are flexibles (flex=1).
       // We don't need to resize them.
@@ -804,17 +805,17 @@ LocalHighlighter.prototype = {
    */
   handleMouseMove: function LocalHighlighter_handleMouseMove(aEvent)
   {
     let doc = aEvent.target.ownerDocument;
 
     // This should never happen, but just in case, we don't let the
     // highlighter highlight browser nodes.
     if (doc && doc != this.chromeDoc) {
-      let element = LayoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
+      let element = this.layoutHelpers.getElementFromPoint(aEvent.target.ownerDocument,
         aEvent.clientX, aEvent.clientY);
       if (element && element != this.selection.node) {
         this.selection.setNode(element, "highlighter");
       }
     }
   },
 };
 
--- a/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
+++ b/browser/devtools/inspector/test/browser_inspector_bug_817558_delete_node.js
@@ -28,17 +28,18 @@ function test()
     inspector = aInspector;
     inspector.selection.setNode(node);
     inspector.once("inspector-updated", () => {
       let parentNode = node.parentNode;
       parentNode.removeChild(node);
 
       let tmp = {};
       Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
-      ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
+      let lh = new tmp.LayoutHelpers(window.content);
+      ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
 
       // Wait for the inspector to process the mutation
       inspector.once("inspector-updated", () => {
         is(inspector.selection.node, parentNode, "parent of selection got selected");
         finishUp();
       });
     });
   };
--- a/browser/devtools/inspector/test/browser_inspector_destroyselection.js
+++ b/browser/devtools/inspector/test/browser_inspector_destroyselection.js
@@ -28,17 +28,18 @@ function test()
     inspector = aInspector;
     inspector.selection.setNode(node);
 
     iframe.parentNode.removeChild(iframe);
     iframe = null;
 
     let tmp = {};
     Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm", tmp);
-    ok(!tmp.LayoutHelpers.isNodeConnected(node), "Node considered as disconnected.");
+    let lh = new tmp.LayoutHelpers(window.content);
+    ok(!lh.isNodeConnected(node), "Node considered as disconnected.");
     ok(!inspector.selection.isConnected(), "Selection considered as disconnected");
 
     finishUp();
   }
 
   function finishUp() {
     node = null;
     gBrowser.removeCurrentTab();
--- a/browser/devtools/inspector/test/head.js
+++ b/browser/devtools/inspector/test/head.js
@@ -72,17 +72,18 @@ function getHighlitNode()
   let b = {
     x: a.x + h._contentRect.width,
     y: a.y + h._contentRect.height
   };
 
   // Get midpoint of diagonal line.
   let midpoint = midPoint(a, b);
 
-  return LayoutHelpers.getElementFromPoint(h.win.document, midpoint.x,
+  let lh = new LayoutHelpers(window.content);
+  return lh.getElementFromPoint(h.win.document, midpoint.x,
     midpoint.y);
 }
 
 
 function midPoint(aPointA, aPointB)
 {
   let pointC = { };
   pointC.x = (aPointB.x - aPointA.x) / 2 + aPointA.x;
--- a/browser/devtools/layoutview/view.js
+++ b/browser/devtools/layoutview/view.js
@@ -3,17 +3,16 @@
 /* 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 Cu = Components.utils;
 Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource://gre/modules/devtools/Loader.jsm");
 Cu.import("resource://gre/modules/devtools/Console.jsm");
 
 const promise = devtools.require("sdk/core/promise");
 
 function LayoutView(aInspector, aWindow)
 {
   this.inspector = aInspector;
--- a/browser/devtools/markupview/markup-view.js
+++ b/browser/devtools/markupview/markup-view.js
@@ -48,16 +48,18 @@ loader.lazyGetter(this, "AutocompletePop
 function MarkupView(aInspector, aFrame, aControllerWindow)
 {
   this._inspector = aInspector;
   this.walker = this._inspector.walker;
   this._frame = aFrame;
   this.doc = this._frame.contentDocument;
   this._elt = this.doc.querySelector("#root");
 
+  this.layoutHelpers = new LayoutHelpers(this.doc.defaultView);
+
   try {
     this.maxChildren = Services.prefs.getIntPref("devtools.markup.pagesize");
   } catch(ex) {
     this.maxChildren = DEFAULT_MAX_CHILDREN;
   }
 
   // Creating the popup to be used to show CSS suggestions.
   let options = {
@@ -401,17 +403,17 @@ MarkupView.prototype = {
       this.importNode(parent);
       this.expandNode(parent);
     }
 
     return this._waitForChildren().then(() => {
       return this._ensureVisible(aNode);
     }).then(() => {
       // Why is this not working?
-      LayoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
+      this.layoutHelpers.scrollIntoViewIfNeeded(this._containers.get(aNode).editor.elt, centered);
     });
   },
 
   /**
    * Expand the container's children.
    */
   _expandContainer: function MT__expandContainer(aContainer)
   {
@@ -1226,17 +1228,17 @@ ElementEditor.prototype = {
       attrName: aAttr.name,
     };
     this.template("attribute", data);
     var {attr, inner, name, val} = data;
 
     // Double quotes need to be handled specially to prevent DOMParser failing.
     // name="v"a"l"u"e" when editing -> name='v"a"l"u"e"'
     // name="v'a"l'u"e" when editing -> name="v'a&quot;l'u&quot;e"
-    let editValueDisplayed = aAttr.value;
+    let editValueDisplayed = aAttr.value || "";
     let hasDoubleQuote = editValueDisplayed.contains('"');
     let hasSingleQuote = editValueDisplayed.contains("'");
     let initial = aAttr.name + '="' + editValueDisplayed + '"';
 
     // Can't just wrap value with ' since the value contains both " and '.
     if (hasDoubleQuote && hasSingleQuote) {
         editValueDisplayed = editValueDisplayed.replace(/\"/g, "&quot;");
         initial = aAttr.name + '="' + editValueDisplayed + '"';
--- a/browser/devtools/scratchpad/scratchpad.js
+++ b/browser/devtools/scratchpad/scratchpad.js
@@ -14,24 +14,24 @@
 
 "use strict";
 
 let require = Components.utils.import("resource://gre/modules/devtools/Loader.jsm", {}).devtools.require;
 
 let { Cc, Ci, Cu } = require("chrome");
 let promise = require("sdk/core/promise");
 let Telemetry = require("devtools/shared/telemetry");
+let DevtoolsHelpers = require("devtools/shared/helpers");
 let TargetFactory = require("devtools/framework/target").TargetFactory;
 const escodegen = require("escodegen/escodegen");
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/NetUtil.jsm");
 Cu.import("resource:///modules/source-editor.jsm");
-Cu.import("resource://gre/modules/devtools/LayoutHelpers.jsm");
 Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
 Cu.import("resource://gre/modules/jsdebugger.jsm");
 Cu.import("resource:///modules/devtools/gDevTools.jsm");
 Cu.import("resource://gre/modules/osfile.jsm");
 Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
 Cu.import("resource://gre/modules/reflect.jsm");
 Cu.import("resource://gre/modules/devtools/DevToolsUtils.jsm");
 
@@ -1279,19 +1279,19 @@ var Scratchpad = {
       let chromeContextCommand = document.getElementById("sp-cmd-browserContext");
       environmentMenu.removeAttribute("hidden");
       chromeContextCommand.removeAttribute("disabled");
       errorConsoleCommand.removeAttribute("disabled");
     }
 
     let initialText = this.strings.formatStringFromName(
       "scratchpadIntro1",
-      [LayoutHelpers.prettyKey(document.getElementById("sp-key-run")),
-       LayoutHelpers.prettyKey(document.getElementById("sp-key-inspect")),
-       LayoutHelpers.prettyKey(document.getElementById("sp-key-display"))],
+      [DevtoolsHelpers.prettyKey(document.getElementById("sp-key-run")),
+       DevtoolsHelpers.prettyKey(document.getElementById("sp-key-inspect")),
+       DevtoolsHelpers.prettyKey(document.getElementById("sp-key-display"))],
       3);
 
     let args = window.arguments;
 
     if (args && args[0] instanceof Ci.nsIDialogParamBlock) {
       args = args[0];
     } else {
       // If this Scratchpad window doesn't have any arguments, horrible
new file mode 100644
--- /dev/null
+++ b/browser/devtools/shared/helpers.js
@@ -0,0 +1,72 @@
+/* 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 {Cu} = require("chrome");
+Cu.import("resource://gre/modules/Services.jsm");
+
+let { XPCOMUtils } = Cu.import("resource://gre/modules/XPCOMUtils.jsm", {});
+XPCOMUtils.defineLazyGetter(this, "PlatformKeys", function() {
+  return Services.strings.createBundle(
+    "chrome://global-platform/locale/platformKeys.properties");
+});
+
+/**
+  * Prettifies the modifier keys for an element.
+  *
+  * @param Node aElemKey
+  *        The key element to get the modifiers from.
+  * @param boolean aAllowCloverleaf
+  *        Pass true to use the cloverleaf symbol instead of a descriptive string.
+  * @return string
+  *         A prettified and properly separated modifier keys string.
+  */
+exports.prettyKey = function Helpers_prettyKey(aElemKey, aAllowCloverleaf) {
+  let elemString = "";
+  let elemMod = aElemKey.getAttribute("modifiers");
+
+  if (elemMod.match("accel")) {
+    if (Services.appinfo.OS == "Darwin") {
+      // XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
+      // Orion adds variable height lines.
+      if (!aAllowCloverleaf) {
+        elemString += "Cmd-";
+      } else {
+        elemString += PlatformKeys.GetStringFromName("VK_META") +
+          PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+      }
+    } else {
+      elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
+        PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+    }
+  }
+  if (elemMod.match("access")) {
+    if (Services.appinfo.OS == "Darwin") {
+      elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
+        PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+    } else {
+      elemString += PlatformKeys.GetStringFromName("VK_ALT") +
+        PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+    }
+  }
+  if (elemMod.match("shift")) {
+    elemString += PlatformKeys.GetStringFromName("VK_SHIFT") +
+      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+  }
+  if (elemMod.match("alt")) {
+    elemString += PlatformKeys.GetStringFromName("VK_ALT") +
+      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+  }
+  if (elemMod.match("ctrl") || elemMod.match("control")) {
+    elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
+      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+  }
+  if (elemMod.match("meta")) {
+    elemString += PlatformKeys.GetStringFromName("VK_META") +
+      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+  }
+
+  return elemString +
+    (aElemKey.getAttribute("keycode").replace(/^.*VK_/, "") ||
+     aElemKey.getAttribute("key")).toUpperCase();
+}
--- a/browser/devtools/shared/test/browser_layoutHelpers.js
+++ b/browser/devtools/shared/test/browser_layoutHelpers.js
@@ -20,78 +20,80 @@ function test() {
     let doc = browser.contentDocument;
     runTest(doc.defaultView, doc.getElementById('some'));
     gBrowser.removeCurrentTab();
     finish();
   });
 }
 
 function runTest(win, some) {
+  let lh = new LayoutHelpers(win);
+
   some.style.top = win.innerHeight + 'px';
   some.style.left = win.innerWidth + 'px';
   // The tests start with a black 2x2 pixels square below bottom right.
   // Do not resize the window during the tests.
 
   win.scroll(win.innerWidth / 2, win.innerHeight + 2);  // Above the viewport.
-  LayoutHelpers.scrollIntoViewIfNeeded(some);
+  lh.scrollIntoViewIfNeeded(some);
   is(win.scrollY, Math.floor(win.innerHeight / 2) + 1,
      'Element completely hidden above should appear centered.');
 
   win.scroll(win.innerWidth / 2, win.innerHeight + 1);  // On the top edge.
-  LayoutHelpers.scrollIntoViewIfNeeded(some);
+  lh.scrollIntoViewIfNeeded(some);
   is(win.scrollY, win.innerHeight,
      'Element partially visible above should appear above.');
 
   win.scroll(win.innerWidth / 2, 0);  // Just below the viewport.
-  LayoutHelpers.scrollIntoViewIfNeeded(some);
+  lh.scrollIntoViewIfNeeded(some);
   is(win.scrollY, Math.floor(win.innerHeight / 2) + 1,
      'Element completely hidden below should appear centered.');
 
   win.scroll(win.innerWidth / 2, 1);  // On the bottom edge.
-  LayoutHelpers.scrollIntoViewIfNeeded(some);
+  lh.scrollIntoViewIfNeeded(some);
   is(win.scrollY, 2,
      'Element partially visible below should appear below.');
 
 
   win.scroll(win.innerWidth / 2, win.innerHeight + 2);  // Above the viewport.
-  LayoutHelpers.scrollIntoViewIfNeeded(some, false);
+  lh.scrollIntoViewIfNeeded(some, false);
   is(win.scrollY, win.innerHeight,
      'Element completely hidden above should appear above ' +
      'if parameter is false.');
 
   win.scroll(win.innerWidth / 2, win.innerHeight + 1);  // On the top edge.
-  LayoutHelpers.scrollIntoViewIfNeeded(some, false);
+  lh.scrollIntoViewIfNeeded(some, false);
   is(win.scrollY, win.innerHeight,
      'Element partially visible above should appear above ' +
      'if parameter is false.');
 
   win.scroll(win.innerWidth / 2, 0);  // Below the viewport.
-  LayoutHelpers.scrollIntoViewIfNeeded(some, false);
+  lh.scrollIntoViewIfNeeded(some, false);
   is(win.scrollY, 2,
      'Element completely hidden below should appear below ' +
      'if parameter is false.');
 
   win.scroll(win.innerWidth / 2, 1);  // On the bottom edge.
-  LayoutHelpers.scrollIntoViewIfNeeded(some, false);
+  lh.scrollIntoViewIfNeeded(some, false);
   is(win.scrollY, 2,
      'Element partially visible below should appear below ' +
      'if parameter is false.');
 
   // The case of iframes.
   win.scroll(0, 0);
 
   let frame = win.document.getElementById('frame');
   let fwin = frame.contentWindow;
 
   frame.style.top = win.innerHeight + 'px';
   frame.style.left = win.innerWidth + 'px';
 
   fwin.addEventListener('load', function frameLoad() {
     let some = fwin.document.getElementById('some');
-    LayoutHelpers.scrollIntoViewIfNeeded(some);
+    lh.scrollIntoViewIfNeeded(some);
     is(win.scrollX, Math.floor(win.innerWidth / 2) + 20,
        'Scrolling from an iframe should center the iframe vertically.');
     is(win.scrollY, Math.floor(win.innerHeight / 2) + 20,
        'Scrolling from an iframe should center the iframe horizontally.');
     is(fwin.scrollX, Math.floor(fwin.innerWidth / 2) + 1,
        'Scrolling from an iframe should center the element vertically.');
     is(fwin.scrollY, Math.floor(fwin.innerHeight / 2) + 1,
        'Scrolling from an iframe should center the element horizontally.');
--- a/browser/devtools/tilt/test/browser_tilt_utils05.js
+++ b/browser/devtools/tilt/test/browser_tilt_utils05.js
@@ -50,20 +50,21 @@ function test() {
 
     is(cwDimensions.width - iframe.contentWindow.scrollMaxX,
       iframe.contentWindow.innerWidth,
       "The content window width wasn't calculated correctly.");
     is(cwDimensions.height - iframe.contentWindow.scrollMaxY,
       iframe.contentWindow.innerHeight,
       "The content window height wasn't calculated correctly.");
 
-    let nodeCoordinates = LayoutHelpers.getRect(
+    let lh = new LayoutHelpers(gBrowser.contentWindow);
+    let nodeCoordinates = lh.getRect(
       iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
 
-    let frameOffset = LayoutHelpers.getIframeContentOffset(iframe);
+    let frameOffset = lh.getIframeContentOffset(iframe);
     let frameRect = iframe.getBoundingClientRect();
 
     is(nodeCoordinates.top, frameRect.top + frameOffset[0] + 98,
       "The node coordinates top value wasn't calculated correctly.");
     is(nodeCoordinates.left, frameRect.left + frameOffset[1] + 76,
       "The node coordinates left value wasn't calculated correctly.");
     is(nodeCoordinates.width, 123,
       "The node coordinates width value wasn't calculated correctly.");
--- a/browser/devtools/tilt/test/browser_tilt_utils07.js
+++ b/browser/devtools/tilt/test/browser_tilt_utils07.js
@@ -99,20 +99,21 @@ function test() {
 
     is(cwDimensions.width - iframe.contentWindow.scrollMaxX,
       iframe.contentWindow.innerWidth,
       "The content window width wasn't calculated correctly.");
     is(cwDimensions.height - iframe.contentWindow.scrollMaxY,
       iframe.contentWindow.innerHeight,
       "The content window height wasn't calculated correctly.");
 
-    let nodeCoordinates = LayoutHelpers.getRect(
+    let lh = new LayoutHelpers(gBrowser.contentWindow);
+    let nodeCoordinates = lh.getRect(
       iframe.contentDocument.getElementById("test-div"), iframe.contentWindow);
 
-    let frameOffset = LayoutHelpers.getIframeContentOffset(iframe);
+    let frameOffset = lh.getIframeContentOffset(iframe);
     let frameRect = iframe.getBoundingClientRect();
 
     is(nodeCoordinates.top, frameRect.top + frameOffset[0],
       "The node coordinates top value wasn't calculated correctly.");
     is(nodeCoordinates.left, frameRect.left + frameOffset[1],
       "The node coordinates left value wasn't calculated correctly.");
     is(nodeCoordinates.width, 123,
       "The node coordinates width value wasn't calculated correctly.");
--- a/browser/devtools/tilt/tilt-utils.js
+++ b/browser/devtools/tilt/tilt-utils.js
@@ -400,18 +400,19 @@ TiltUtils.DOM = {
    *                  width of the node
    *         {Number} height
    *                  height of the node
    *         {Number} thickness
    *                  thickness of the node
    */
   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 = LayoutHelpers.getRect(aNode, aContentWindow);
+    let coord = lh.getRect(aNode, aContentWindow);
     if (!coord) {
       return null;
     }
 
     coord.depth = aParentPosition ? (aParentPosition.depth + aParentPosition.thickness) : 0;
     coord.thickness = STACK_THICKNESS;
 
     return coord;
--- a/toolkit/devtools/LayoutHelpers.jsm
+++ b/toolkit/devtools/LayoutHelpers.jsm
@@ -8,24 +8,25 @@ const Cu = Components.utils;
 const Ci = Components.interfaces;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "Services",
   "resource://gre/modules/Services.jsm");
 
-XPCOMUtils.defineLazyGetter(this, "PlatformKeys", function() {
-  return Services.strings.createBundle(
-    "chrome://global-platform/locale/platformKeys.properties");
-});
-
 this.EXPORTED_SYMBOLS = ["LayoutHelpers"];
 
-this.LayoutHelpers = LayoutHelpers = {
+this.LayoutHelpers = LayoutHelpers = function(aTopLevelWindow) {
+  this._topDocShell = aTopLevelWindow.QueryInterface(Ci.nsIInterfaceRequestor)
+                                     .getInterface(Ci.nsIWebNavigation)
+                                     .QueryInterface(Ci.nsIDocShell);
+}
+
+LayoutHelpers.prototype = {
 
   /**
    * Compute the position and the dimensions for the visible portion
    * of a node, relativalely to the root window.
    *
    * @param nsIDOMNode aNode
    *        a DOM element to be highlighted
    */
@@ -65,32 +66,37 @@ this.LayoutHelpers = LayoutHelpers = {
       if (rect.top < 0) {
         rect.height += rect.top;
         rect.top = 0;
       }
 
       // Selection has been clipped to fit in its own window.
 
       // Are we in the top-level window?
-      if (frameWin.parent === frameWin || !frameWin.frameElement) {
+      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 = frameWin.frameElement.getBoundingClientRect();
+      let frameRect = frameElement.getBoundingClientRect();
 
       let [offsetTop, offsetLeft] =
-        this.getIframeContentOffset(frameWin.frameElement);
+        this.getIframeContentOffset(frameElement);
 
       rect.top += frameRect.top + offsetTop;
       rect.left += frameRect.left + offsetLeft;
 
-      frameWin = frameWin.parent;
+      frameWin = this.getParentWindow(frameWin);
     }
 
     return rect;
   },
 
   /**
    * Compute the absolute position and the dimensions of a node, relativalely
    * to the root window.
@@ -110,32 +116,37 @@ this.LayoutHelpers = LayoutHelpers = {
             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 (frameWin.parent === frameWin || !frameWin.frameElement) {
+      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 = frameWin.frameElement.getBoundingClientRect();
+      let frameRect = frameElement.getBoundingClientRect();
 
       let [offsetTop, offsetLeft] =
-        this.getIframeContentOffset(frameWin.frameElement);
+        this.getIframeContentOffset(frameElement);
 
       rect.top += frameRect.top + offsetTop;
       rect.left += frameRect.left + offsetLeft;
 
-      frameWin = frameWin.parent;
+      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
@@ -198,17 +209,17 @@ this.LayoutHelpers = LayoutHelpers = {
    */
   getElementFromPoint: function LH_elementFromPoint(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] = LayoutHelpers.getIframeContentOffset(node);
+        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;
         }
@@ -291,19 +302,20 @@ this.LayoutHelpers = LayoutHelpers = {
 
       if (xAllowed && (leftToRight <= 0 || rightToLeft <= 0)) {
         win.scroll(win.scrollX + clientRect.left
                    - (win.innerWidth - elem.offsetWidth) / 2,
                    win.scrollY);
       }
     }
 
-    if (win.parent !== win) {
+    if (!this.isTopLevelWindow(win)) {
       // We are inside an iframe.
-      LH_scrollIntoViewIfNeeded(win.frameElement, centered);
+      let frameElement = this.getFrameElement(win);
+      this.scrollIntoViewIfNeeded(frameElement, centered);
     }
   },
 
   /**
    * Check if a node and its document are still alive
    * and attached to the window.
    *
    * @param aNode
@@ -317,68 +329,65 @@ this.LayoutHelpers = LayoutHelpers = {
       return connected;
     } catch (e) {
       // "can't access dead object" error
       return false;
     }
   },
 
   /**
-   * Prettifies the modifier keys for an element.
-   *
-   * @param Node aElemKey
-   *        The key element to get the modifiers from.
-   * @param boolean aAllowCloverleaf
-   *        Pass true to use the cloverleaf symbol instead of a descriptive string.
-   * @return string
-   *         A prettified and properly separated modifier keys string.
+   * like win.parent === win, but goes through mozbrowsers and mozapps iframes.
    */
-  prettyKey: function LH_prettyKey(aElemKey, aAllowCloverleaf)
-  {
-    let elemString = "";
-    let elemMod = aElemKey.getAttribute("modifiers");
+  isTopLevelWindow: function LH_isTopLevelWindow(win) {
+    let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIWebNavigation)
+                   .QueryInterface(Ci.nsIDocShell);
 
-    if (elemMod.match("accel")) {
-      if (Services.appinfo.OS == "Darwin") {
-        // XXX bug 779642 Use "Cmd-" literal vs. cloverleaf meta-key until
-        // Orion adds variable height lines.
-        if (!aAllowCloverleaf) {
-          elemString += "Cmd-";
-        } else {
-          elemString += PlatformKeys.GetStringFromName("VK_META") +
-                        PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-        }
-      } else {
-        elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
-                      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-      }
-    }
-    if (elemMod.match("access")) {
-      if (Services.appinfo.OS == "Darwin") {
-        elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
-                      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-      } else {
-        elemString += PlatformKeys.GetStringFromName("VK_ALT") +
-                      PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-      }
-    }
-    if (elemMod.match("shift")) {
-      elemString += PlatformKeys.GetStringFromName("VK_SHIFT") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-    if (elemMod.match("alt")) {
-      elemString += PlatformKeys.GetStringFromName("VK_ALT") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-    if (elemMod.match("ctrl") || elemMod.match("control")) {
-      elemString += PlatformKeys.GetStringFromName("VK_CONTROL") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
-    }
-    if (elemMod.match("meta")) {
-      elemString += PlatformKeys.GetStringFromName("VK_META") +
-                    PlatformKeys.GetStringFromName("MODIFIER_SEPARATOR");
+    return docShell === this._topDocShell;
+  },
+
+  /**
+   * like win.parent, but goes through mozbrowsers and mozapps iframes.
+   */
+  getParentWindow: function LH_getParentWindow(win) {
+    if (this.isTopLevelWindow(win)) {
+      return null;
     }
 
-    return elemString +
-      (aElemKey.getAttribute("keycode").replace(/^.*VK_/, "") ||
-       aElemKey.getAttribute("key")).toUpperCase();
-  }
+    let docShell = win.QueryInterface(Ci.nsIInterfaceRequestor)
+                   .getInterface(Ci.nsIWebNavigation)
+                   .QueryInterface(Ci.nsIDocShell);
+
+    if (docShell.isBrowserOrApp) {
+      let parentDocShell = docShell.getSameTypeParentIgnoreBrowserAndAppBoundaries();
+      return parentDocShell.contentViewer.DOMDocument.defaultView;
+    } else {
+      return win.parent;
+    }
+  },
+
+  /**
+   * like win.frameElement, but goes through mozbrowsers and mozapps iframes.
+   */
+  getFrameElement: function LH_getFrameElement(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();
+      let parentDoc = parentDocShell.contentViewer.DOMDocument;
+      let allIframes = parentDoc.querySelectorAll("iframe");
+      for (let f of allIframes) {
+        if (f.contentWindow === win) {
+          return f;
+        }
+      }
+      return null;
+    } else {
+      return win.frameElement;
+    }
+  },
 };
--- 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;
   },
 
--- a/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
+++ b/toolkit/devtools/server/tests/mochitest/inspector-helpers.js
@@ -96,17 +96,17 @@ function sortOwnershipChildren(children)
 
 function serverOwnershipSubtree(walker, node) {
   let actor = walker._refMap.get(node);
   if (!actor) {
     return undefined;
   }
 
   let children = [];
-  let docwalker = _documentWalker(node);
+  let docwalker = _documentWalker(node, window);
   let child = docwalker.firstChild();
   while (child) {
     let item = serverOwnershipSubtree(walker, child);
     if (item) {
       children.push(item);
     }
     child = docwalker.nextSibling();
   }
--- a/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html
+++ b/toolkit/devtools/server/tests/mochitest/test_inspector-insert.html
@@ -60,17 +60,17 @@ addTest(function testRearrange() {
     ok(!gInspectee.querySelector("#a").nextSibling, "a should now be at the end of the list.");
     return gWalker.children(longlist);
   }).then(response => {
     is(nodeA, response.nodes[response.nodes.length - 1], "a should now be the last returned child.");
     // Now move it to the middle of the list.
     nextNode = response.nodes[13];
     return gWalker.insertBefore(nodeA, longlist, nextNode);
   }).then(response => {
-    let sibling = inspector._documentWalker(gInspectee.querySelector("#a")).nextSibling();
+    let sibling = inspector._documentWalker(gInspectee.querySelector("#a"), window).nextSibling();
     is(sibling, nextNode.rawNode(), "Node should match the expected next node.");
     return gWalker.children(longlist);
   }).then(response => {
     is(nodeA, response.nodes[13], "a should be where we expect it.");
     is(nextNode, response.nodes[14], "next node should be where we expect it.");
   }).then(runNextTest));
 });