Bug 1365075 - waiting until the document is loaded if CustomContentContainer is not available; r=pbro draft
authorMatteo Ferretti <mferretti@mozilla.com>
Tue, 16 May 2017 18:18:46 +0200
changeset 580486 12190cef72f2cc62677ba8b6aa6fc6357c8bf8c0
parent 578825 8f89d291e303a3d637a268c5abad9c819aad9285
child 629304 0b2658e90c6743140b2462d4e4e7e54b1f845075
push id59575
push userbmo:zer0@mozilla.com
push dateThu, 18 May 2017 16:29:24 +0000
reviewerspbro
bugs1365075
milestone55.0a1
Bug 1365075 - waiting until the document is loaded if CustomContentContainer is not available; r=pbro MozReview-Commit-ID: Cuv6diP08RT
devtools/client/inspector/test/browser.ini
devtools/client/inspector/test/browser_inspector_highlighter-05.js
devtools/server/actors/highlighters/utils/markup.js
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -63,16 +63,17 @@ skip-if = os == "mac" # Full keyboard na
 [browser_inspector_destroy-after-navigation.js]
 [browser_inspector_destroy-before-ready.js]
 [browser_inspector_expand-collapse.js]
 [browser_inspector_gcli-inspect-command.js]
 [browser_inspector_highlighter-01.js]
 [browser_inspector_highlighter-02.js]
 [browser_inspector_highlighter-03.js]
 [browser_inspector_highlighter-04.js]
+[browser_inspector_highlighter-05.js]
 [browser_inspector_highlighter-by-type.js]
 [browser_inspector_highlighter-cancel.js]
 [browser_inspector_highlighter-comments.js]
 [browser_inspector_highlighter-cssgrid_01.js]
 [browser_inspector_highlighter-cssgrid_02.js]
 [browser_inspector_highlighter-csstransform_01.js]
 [browser_inspector_highlighter-csstransform_02.js]
 [browser_inspector_highlighter-embed.js]
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/test/browser_inspector_highlighter-05.js
@@ -0,0 +1,69 @@
+/* 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";
+
+// This is testing that the Anonymous Content is properly inserted into the document.
+// Usually that is happening during the "interactive" state of the document, to have them
+// ready as soon as possible.
+// However, in some conditions, that's not possible since we don't have access yet to
+// the `CustomContentContainer`, that is used to add the Anonymous Content.
+// That can happen if the page has some external resource, as <link>, that takes time
+// to load and / or returns the wrong content. This is not happening, for instance, with
+// images.
+//
+// In those case, we want to be sure that if we're not able to insert the Anonymous
+// Content at the "interactive" state, we're doing so when the document is loaded.
+//
+// See: https://bugzilla.mozilla.org/show_bug.cgi?id=1365075
+
+const server = createTestHTTPServer();
+const filepath = "/slow.css";
+const cssuri = `http://localhost:${server.identity.primaryPort}${filepath}`;
+
+// Register a slow css file handler so we can simulate a long loading time.
+server.registerContentType("css", "text/css");
+server.registerPathHandler(filepath, (metadata, response) => {
+  info("CSS has been requested");
+  response.processAsync();
+  setTimeout(() => {
+    info("CSS is responding");
+    response.finish();
+  }, 2000);
+});
+
+const TEST_URL = "data:text/html," + encodeURIComponent(`
+  <!DOCTYPE html>
+  <html>
+    <head>
+      <link href="${cssuri}" rel="stylesheet" />
+    </head>
+    <body>
+      <p>Slow page</p>
+     </body>
+  </html>
+`);
+
+add_task(function* () {
+  info("Open the inspector to a blank page.");
+  let { inspector, tab, testActor } = yield openInspectorForURL("about:blank");
+
+  let pageLoaded = waitForPageLoad(tab);
+
+  info("Navigate to the test url and waiting for the page to be loaded.");
+  yield navigateTo(inspector, TEST_URL);
+  yield pageLoaded;
+
+  info("Shows the box model highligher for the <p> node.");
+  let divFront = yield getNodeFront("p", inspector);
+  yield inspector.highlighter.showBoxModel(divFront);
+
+  info("Check the node is highlighted.");
+  is(yield testActor.isHighlighting(), true,
+    "Box Model highlighter is working as expected.");
+});
+
+const waitForPageLoad = (tab) => new Promise(resolve => {
+  tab.linkedBrowser.addEventListener("load", resolve, {capture: true, once: true});
+});
--- a/devtools/server/actors/highlighters/utils/markup.js
+++ b/devtools/server/actors/highlighters/utils/markup.js
@@ -1,15 +1,15 @@
 /* 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 { Cc, Ci, Cu } = require("chrome");
+const { Cc, Ci, Cu, Cr } = require("chrome");
 const { getCurrentZoom, getWindowDimensions, getViewportDimensions,
   getRootBindingParent, loadSheet } = require("devtools/shared/layout/utils");
 const { on, emit } = require("sdk/event/core");
 
 const lazyContainer = {};
 
 loader.lazyRequireGetter(lazyContainer, "CssLogic",
   "devtools/server/css-logic", true);
@@ -265,17 +265,33 @@ CanvasFrameAnonymousContentHelper.protot
 
     let node = this.nodeBuilder();
 
     // It was stated that hidden documents don't accept
     // `insertAnonymousContent` calls yet. That doesn't seems the case anymore,
     // at least on desktop. Therefore, removing the code that was dealing with
     // that scenario, fixes when we're adding anonymous content in a tab that
     // is not the active one (see bug 1260043 and bug 1260044)
-    this._content = doc.insertAnonymousContent(node);
+    try {
+      this._content = doc.insertAnonymousContent(node);
+    } catch (e) {
+      // If the `insertAnonymousContent` fails throwing a `NS_ERROR_UNEXPECTED`, it means
+      // we don't have access to a `CustomContentContainer` yet (see bug 1365075).
+      // At this point, it could only happen on document's interactive state, and we
+      // need to wait until the `complete` state before inserting the anonymous content
+      // again.
+      if (e.result === Cr.NS_ERROR_UNEXPECTED && doc.readyState === "interactive") {
+        // The next state change will be "complete" since the current is "interactive"
+        doc.addEventListener("readystatechange", () => {
+          this._content = doc.insertAnonymousContent(node);
+        }, { once: true });
+      } else {
+        throw e;
+      }
+    }
   },
 
   _remove() {
     try {
       let doc = this.anonymousContentDocument;
       doc.removeAnonymousContent(this._content);
     } catch (e) {
       // If the current window isn't the one the content was inserted into, this