Bug 1362623 - Wait for idle callback before executing document_idle content scripts. r?mixedpuppy draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 05 May 2017 21:17:37 -0700
changeset 573672 97498785f751aa319dc8efa7e49f6bf3d87eb967
parent 573671 8f4637881ddc42a948c894e62c8486fe8677a938
child 627361 efa73363b780f111fdb583cb165fd81311124f10
push id57457
push usermaglione.k@gmail.com
push dateSat, 06 May 2017 04:18:01 +0000
reviewersmixedpuppy
bugs1362623
milestone55.0a1
Bug 1362623 - Wait for idle callback before executing document_idle content scripts. r?mixedpuppy MozReview-Commit-ID: 6pOrH1lrTV2
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/test/xpcshell/test_ext_contentscript.js
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -21,16 +21,20 @@ XPCOMUtils.defineLazyModuleGetter(this, 
                                   "resource://gre/modules/Schemas.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "WebNavigationFrames",
                                   "resource://gre/modules/WebNavigationFrames.jsm");
 
 XPCOMUtils.defineLazyServiceGetter(this, "styleSheetService",
                                    "@mozilla.org/content/style-sheet-service;1",
                                    "nsIStyleSheetService");
 
+// xpcshell doesn't handle idle callbacks well.
+XPCOMUtils.defineLazyGetter(this, "idleTimeout",
+                            () => Services.appinfo.name === "XPCShell" ? 500 : undefined);
+
 const DocumentEncoder = Components.Constructor(
   "@mozilla.org/layout/documentEncoder;1?type=text/plain",
   "nsIDocumentEncoder", "init");
 
 const Timer = Components.Constructor("@mozilla.org/timer;1", "nsITimer", "initWithCallback");
 
 Cu.import("resource://gre/modules/ExtensionChild.jsm");
 Cu.import("resource://gre/modules/ExtensionCommon.jsm");
@@ -304,16 +308,22 @@ class Script {
     let {document} = context.contentWindow;
     if (this.runAt === "document_start" && document.readyState !== "complete") {
       document.blockParsing(scriptsPromise);
     }
 
     let scripts = await scriptsPromise;
     let result;
 
+    if (this.runAt === "document_idle") {
+      await new Promise(resolve =>
+          context.contentWindow.requestIdleCallback(resolve,
+                                                    {timeout: idleTimeout}));
+    }
+
     // The evaluations below may throw, in which case the promise will be
     // automatically rejected.
     for (let script of scripts) {
       result = script.executeInGlobal(context.cloneScope);
     }
 
     if (this.options.jsCode) {
       result = Cu.evalInSandbox(this.options.jsCode, context.cloneScope, "latest");
--- a/toolkit/components/extensions/test/xpcshell/test_ext_contentscript.js
+++ b/toolkit/components/extensions/test/xpcshell/test_ext_contentscript.js
@@ -1,15 +1,17 @@
 "use strict";
 
 const server = createHttpServer();
 server.registerDirectory("/data/", do_get_file("data"));
 
 const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`;
 
+ExtensionTestUtils.mockAppInfo();
+
 add_task(function* test_contentscript() {
   function background() {
     browser.runtime.onMessage.addListener(([msg, expectedStates, readyState], sender) => {
       if (msg == "chrome-namespace-ok") {
         browser.test.sendMessage(msg);
         return;
       }