Bug 1484373: Part 5 - Minimize the amount of content script options processing we do in child processes r=mixedpuppy
authorKris Maglione <maglione.k@gmail.com>
Fri, 17 Aug 2018 20:02:52 -0700
changeset 481489 03cdd81e991d9675c5166e39a825b99fabded292
parent 481488 a7848dcb339e399ee108590e413a548dfea4a1c3
child 481490 4cc1736698ad36957e04996a5b087f6f1e5bad88
push id232
push userfmarier@mozilla.com
push dateWed, 05 Sep 2018 20:45:54 +0000
reviewersmixedpuppy
bugs1484373
milestone63.0a1
Bug 1484373: Part 5 - Minimize the amount of content script options processing we do in child processes r=mixedpuppy Differential Revision: https://phabricator.services.mozilla.com/D3695
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/extension-process-script.js
toolkit/components/extensions/parent/ext-contentScripts.js
toolkit/components/extensions/parent/ext-tabs-base.js
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -675,16 +675,34 @@ class ExtensionData {
         scopes: data.scopes,
       });
 
       let computeModuleInit = (scope, modules) => {
         let manager = new ExtensionCommon.SchemaAPIManager(scope);
         return manager.initModuleJSON([modules]);
       };
 
+      result.contentScripts = [];
+      for (let options of manifest.content_scripts || []) {
+        result.contentScripts.push({
+          allFrames: options.all_frames,
+          matchAboutBlank: options.match_about_blank,
+          frameID: options.frame_id,
+          runAt: options.run_at,
+
+          matches: options.matches,
+          excludeMatches: options.exclude_matches || [],
+          includeGlobs: options.include_globs,
+          excludeGlobs: options.exclude_globs,
+
+          jsPaths: options.js || [],
+          cssPaths: options.css || [],
+        });
+      }
+
       if (this.canUseExperiment(manifest)) {
         let parentModules = {};
         let childModules = {};
 
         for (let [name, data] of Object.entries(manifest.experiment_apis)) {
           let schema = this.getURL(data.schema);
 
           if (!schemaPromises.has(schema)) {
@@ -815,16 +833,17 @@ class ExtensionData {
 
     // Do not override the add-on id that has been already assigned.
     if (!this.id) {
       this.id = manifestData.id;
     }
 
     this.manifest = manifestData.manifest;
     this.apiNames = manifestData.apiNames;
+    this.contentScripts = manifestData.contentScripts;
     this.dependencies = manifestData.dependencies;
     this.permissions = manifestData.permissions;
     this.schemaURLs = manifestData.schemaURLs;
     this.type = manifestData.type;
 
     this.modules = manifestData.modules;
 
     this.apiManager = this.getAPIManager();
@@ -1558,20 +1577,16 @@ class Extension extends ExtensionData {
     return {
       backgroundScripts: this.backgroundScripts,
       childModules: this.modules && this.modules.child,
       dependencies: this.dependencies,
       schemaURLs: this.schemaURLs,
     };
   }
 
-  get contentScripts() {
-    return this.manifest.content_scripts || [];
-  }
-
   broadcast(msg, data) {
     return new Promise(resolve => {
       let {ppmm} = Services;
       let children = new Set();
       for (let i = 0; i < ppmm.childCount; i++) {
         children.add(ppmm.getChildAt(i));
       }
 
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -875,36 +875,36 @@ var ExtensionContent = {
       promises = Array.from(this.enumerateWindows(global.docShell), executeInWin)
                       .filter(promise => promise);
     } catch (e) {
       Cu.reportError(e);
       return Promise.reject({message: "An unexpected error occurred"});
     }
 
     if (!promises.length) {
-      if (options.frame_id) {
+      if (options.frameID) {
         return Promise.reject({message: `Frame not found, or missing host permission`});
       }
 
-      let frames = options.all_frames ? ", and any iframes" : "";
+      let frames = options.allFrames ? ", and any iframes" : "";
       return Promise.reject({message: `Missing host permission for the tab${frames}`});
     }
-    if (!options.all_frames && promises.length > 1) {
+    if (!options.allFrames && promises.length > 1) {
       return Promise.reject({message: `Internal error: Script matched multiple windows`});
     }
 
     let result = await Promise.all(promises);
 
     try {
       // Make sure we can structured-clone the result value before
       // we try to send it back over the message manager.
       Cu.cloneInto(result, target);
     } catch (e) {
-      const {js} = options;
-      const fileName = js.length ? js[js.length - 1] : "<anonymous code>";
+      const {jsPaths} = options;
+      const fileName = jsPaths.length ? jsPaths[jsPaths.length - 1] : "<anonymous code>";
       const message = `Script '${fileName}' result is non-structured-clonable data`;
       return Promise.reject({message, fileName});
     }
 
     return result;
   },
 
   handleWebNavigationGetFrame(global, {frameId}) {
--- a/toolkit/components/extensions/extension-process-script.js
+++ b/toolkit/components/extensions/extension-process-script.js
@@ -48,29 +48,22 @@ function tryMatchPatternSet(patterns, op
   } catch (e) {
     Cu.reportError(e);
     return new MatchPatternSet([]);
   }
 }
 
 function parseScriptOptions(options, restrictSchemes = true) {
   return {
-    allFrames: options.all_frames,
-    matchAboutBlank: options.match_about_blank,
-    frameID: options.frame_id,
-    runAt: options.run_at,
-    hasActiveTabPermission: options.hasActiveTabPermission,
+    ...options,
 
     matches: tryMatchPatternSet(options.matches, {restrictSchemes}),
-    excludeMatches: tryMatchPatternSet(options.exclude_matches || [], {restrictSchemes}),
-    includeGlobs: options.include_globs && options.include_globs.map(glob => new MatchGlob(glob)),
-    excludeGlobs: options.exclude_globs && options.exclude_globs.map(glob => new MatchGlob(glob)),
-
-    jsPaths: options.js || [],
-    cssPaths: options.css || [],
+    excludeMatches: tryMatchPatternSet(options.excludeMatches || [], {restrictSchemes}),
+    includeGlobs: options.includeGlobs && options.includeGlobs.map(glob => new MatchGlob(glob)),
+    excludeGlobs: options.excludeGlobs && options.excludeGlobs.map(glob => new MatchGlob(glob)),
   };
 }
 
 var extensions = new DefaultWeakMap(policy => {
   let data = policy.initData;
   if (data.serialize) {
     // We have an actual Extension rather than serialized extension
     // data, so serialize it now to make sure we have consistent inputs
@@ -139,18 +132,18 @@ class ExtensionGlobal {
         return ExtensionContent.handleDetectLanguage(this.global, target);
       case "Extension:Execute":
         let policy = WebExtensionPolicy.getByID(recipient.extensionId);
 
         let matcher = new WebExtensionContentScript(policy, parseScriptOptions(data.options, !policy.hasPermission("mozillaAddons")));
 
         Object.assign(matcher, {
           wantReturnValue: data.options.wantReturnValue,
-          removeCSS: data.options.remove_css,
-          cssOrigin: data.options.css_origin,
+          removeCSS: data.options.removeCSS,
+          cssOrigin: data.options.cssOrigin,
           jsCode: data.options.jsCode,
         });
 
         let script = contentScripts.get(matcher);
 
         // Add the cssCode to the script, so that it can be converted into a cached URL.
         await script.addCSSCode(data.options.cssCode);
         delete data.options.cssCode;
--- a/toolkit/components/extensions/parent/ext-contentScripts.js
+++ b/toolkit/components/extensions/parent/ext-contentScripts.js
@@ -58,47 +58,47 @@ class ContentScriptParent {
     this.options = null;
   }
 
   _convertOptions(details) {
     const {context} = this;
 
     const options = {
       matches: details.matches,
-      exclude_matches: details.excludeMatches,
-      include_globs: details.includeGlobs,
-      exclude_globs: details.excludeGlobs,
-      all_frames: details.allFrames,
-      match_about_blank: details.matchAboutBlank,
-      run_at: details.runAt || "document_idle",
-      js: [],
-      css: [],
+      excludeMatches: details.excludeMatches,
+      includeGlobs: details.includeGlobs,
+      excludeGlobs: details.excludeGlobs,
+      allFrames: details.allFrames,
+      matchAboutBlank: details.matchAboutBlank,
+      runAt: details.runAt || "document_idle",
+      jsPaths: [],
+      cssPaths: [],
     };
 
     const convertCodeToURL = (data, mime) => {
       const blob = new context.cloneScope.Blob(data, {type: mime});
       const blobURL = context.cloneScope.URL.createObjectURL(blob);
 
       this.blobURLs.add(blobURL);
 
       return blobURL;
     };
 
     if (details.js && details.js.length > 0) {
-      options.js = details.js.map(data => {
+      options.jsPaths = details.js.map(data => {
         if (data.file) {
           return data.file;
         }
 
         return convertCodeToURL([data.code], "text/javascript");
       });
     }
 
     if (details.css && details.css.length > 0) {
-      options.css = details.css.map(data => {
+      options.cssPaths = details.css.map(data => {
         if (data.file) {
           return data.file;
         }
 
         return convertCodeToURL([data.code], "text/css");
       });
     }
 
--- a/toolkit/components/extensions/parent/ext-tabs-base.js
+++ b/toolkit/components/extensions/parent/ext-tabs-base.js
@@ -643,19 +643,19 @@ class TabBase {
    *        Used to generate appropriate error messages on failure.
    *
    * @returns {Promise}
    *        Resolves to the result of the execution, once it has completed.
    * @private
    */
   _execute(context, details, kind, method) {
     let options = {
-      js: [],
-      css: [],
-      remove_css: method == "removeCSS",
+      jsPaths: [],
+      cssPaths: [],
+      removeCSS: method == "removeCSS",
     };
 
     // We require a `code` or a `file` property, but we can't accept both.
     if ((details.code === null) == (details.file === null)) {
       return Promise.reject({message: `${method} requires either a 'code' or a 'file' property, but not both`});
     }
 
     if (details.frameId !== null && details.allFrames) {
@@ -671,33 +671,33 @@ class TabBase {
     if (details.file !== null) {
       let url = context.uri.resolve(details.file);
       if (!this.extension.isExtensionURL(url)) {
         return Promise.reject({message: "Files to be injected must be within the extension"});
       }
       options[kind].push(url);
     }
     if (details.allFrames) {
-      options.all_frames = details.allFrames;
+      options.allFrames = details.allFrames;
     }
     if (details.frameId !== null) {
-      options.frame_id = details.frameId;
+      options.frameID = details.frameId;
     }
     if (details.matchAboutBlank) {
-      options.match_about_blank = details.matchAboutBlank;
+      options.matchAboutBlank = details.matchAboutBlank;
     }
     if (details.runAt !== null) {
-      options.run_at = details.runAt;
+      options.runAt = details.runAt;
     } else {
-      options.run_at = "document_idle";
+      options.runAt = "document_idle";
     }
     if (details.cssOrigin !== null) {
-      options.css_origin = details.cssOrigin;
+      options.cssOrigin = details.cssOrigin;
     } else {
-      options.css_origin = "author";
+      options.cssOrigin = "author";
     }
 
     options.wantReturnValue = true;
 
     return this.sendMessage(context, "Extension:Execute", {options});
   }
 
   /**
@@ -712,33 +712,33 @@ class TabBase {
    *        when.
    *
    * @returns {Promise}
    *        Resolves to the result of the evaluation of the given script, once
    *        it has completed, or rejects with any error the evaluation
    *        generates.
    */
   executeScript(context, details) {
-    return this._execute(context, details, "js", "executeScript");
+    return this._execute(context, details, "jsPaths", "executeScript");
   }
 
   /**
    * Injects CSS into the tab's content window, and returns a Promise which
    * resolves when the injection is complete.
    *
    * @param {BaseContext} context
    *        The extension context for which to inject the script.
    * @param {InjectDetails} details
    *        The InjectDetails object, specifying what to inject, and where.
    *
    * @returns {Promise}
    *        Resolves when the injection has completed.
    */
   insertCSS(context, details) {
-    return this._execute(context, details, "css", "insertCSS").then(() => {});
+    return this._execute(context, details, "cssPaths", "insertCSS").then(() => {});
   }
 
 
   /**
    * Removes CSS which was previously into the tab's content window via
    * `insertCSS`, and returns a Promise which resolves when the operation is
    * complete.
    *