Bug 1509339 - Implement a userScript.onBeforeScript API event. r=zombie,robwu
authorLuca Greco <lgreco@mozilla.com>
Fri, 30 Nov 2018 16:09:34 +0000
changeset 449002 39b6008cb9cfb02228f4946463d458d4dee8a8df
parent 449001 7fdaa201c7b3972f9f51af92d935436e6a77b936
child 449003 541cb39b63231ebdd85159013b9f384d39a13b29
push id35139
push userccoroiu@mozilla.com
push dateSat, 01 Dec 2018 02:30:08 +0000
treeherdermozilla-central@22425b629a9d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerszombie, robwu
bugs1509339
milestone65.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 1509339 - Implement a userScript.onBeforeScript API event. r=zombie,robwu Depends on D10061 Differential Revision: https://phabricator.services.mozilla.com/D12676
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/child/ext-toolkit.js
toolkit/components/extensions/child/ext-userScripts-content.js
toolkit/components/extensions/ext-toolkit.json
toolkit/components/extensions/schemas/user_scripts_content.json
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -719,16 +719,18 @@ class UserScript extends Script {
     }
 
     for (let key of Object.keys(userScriptAPIs)) {
       Schemas.exportLazyGetter(userScriptScope, key, () => {
         // Wrap the custom API methods exported to the userScript sandbox.
         return wrapUserScriptAPIMethod(userScriptAPIs[key], key);
       });
     }
+
+    context.userScriptsEvents.emit("on-before-script", clonedMetadata, userScriptScope);
   }
 }
 
 var contentScripts = new DefaultWeakMap(matcher => {
   const extension = processScript.extensions.get(matcher.extension);
 
   if ("userScriptOptions" in matcher) {
     return new UserScript(extension, matcher);
@@ -848,16 +850,21 @@ class ContentScriptContextChild extends 
 
     // A set of exported API methods provided by the extension to the userScripts sandboxes.
     this.userScriptAPIs = null;
 
     // Keep track if the userScript API script has been already executed in this context
     // (e.g. because there are more then one UserScripts that match the related webpage
     // and so the UserScript apiScript has already been executed).
     this.hasUserScriptAPIs = false;
+
+    // A lazy created EventEmitter related to userScripts-specific events.
+    defineLazyGetter(this, "userScriptsEvents", () => {
+      return new ExtensionCommon.EventEmitter();
+    });
   }
 
   injectAPI() {
     if (!this.isExtensionPage) {
       throw new Error("Cannot inject extension API into non-extension window");
     }
 
     // This is an iframe with content script API enabled (See Bug 1214658)
--- a/toolkit/components/extensions/child/ext-toolkit.js
+++ b/toolkit/components/extensions/child/ext-toolkit.js
@@ -72,16 +72,17 @@ extensions.registerModules({
       ["userScripts"],
     ],
   },
   userScriptsContent: {
     url: "chrome://extensions/content/child/ext-userScripts-content.js",
     scopes: ["content_child"],
     paths: [
       ["userScripts", "setScriptAPIs"],
+      ["userScripts", "onBeforeScript"],
     ],
   },
   webRequest: {
     url: "chrome://extensions/content/child/ext-webRequest.js",
     scopes: ["addon_child"],
     paths: [
       ["webRequest"],
     ],
--- a/toolkit/components/extensions/child/ext-userScripts-content.js
+++ b/toolkit/components/extensions/child/ext-userScripts-content.js
@@ -17,12 +17,30 @@ this.userScriptsContent = class extends 
       userScripts: {
         setScriptAPIs(exportedAPIMethods) {
           if (!userScriptsEnabled) {
             throw new ExtensionError(USERSCRIPT_DISABLED_ERRORMSG);
           }
 
           context.setUserScriptAPIs(exportedAPIMethods);
         },
+        onBeforeScript: new EventManager({
+          context,
+          name: "userScripts.onBeforeScript",
+          register: fire => {
+            let handler = (event, userScriptMetadata, userScriptSandbox) => {
+              const apiObj = Cu.createObjectIn(context.cloneScope);
+              apiObj.metadata = userScriptMetadata;
+              apiObj.global = userScriptSandbox;
+
+              fire.raw(apiObj);
+            };
+
+            context.userScriptsEvents.on("on-before-script", handler);
+            return () => {
+              context.userScriptsEvents.off("on-before-script", handler);
+            };
+          },
+        }).api(),
       },
     };
   }
 };
--- a/toolkit/components/extensions/ext-toolkit.json
+++ b/toolkit/components/extensions/ext-toolkit.json
@@ -196,17 +196,18 @@
     "paths": [
       ["userScripts"]
     ]
   },
   "userScriptsContent": {
     "schema": "chrome://extensions/content/schemas/user_scripts_content.json",
     "scopes": ["content_child"],
     "paths": [
-      ["userScripts", "setScriptAPIs"]
+      ["userScripts", "setScriptAPIs"],
+      ["userScripts", "onBeforeScript"]
     ]
   },
   "webNavigation": {
     "url": "chrome://extensions/content/parent/ext-webNavigation.js",
     "schema": "chrome://extensions/content/schemas/web_navigation.json",
     "scopes": ["addon_parent"],
     "paths": [
       ["webNavigation"]
--- a/toolkit/components/extensions/schemas/user_scripts_content.json
+++ b/toolkit/components/extensions/schemas/user_scripts_content.json
@@ -24,11 +24,36 @@
         "description": "Provides a set of custom API methods available to the registered userScripts",
         "parameters": [
           {
             "name": "exportedAPIMethods",
             "$ref": "ExportedAPIMethods"
           }
         ]
       }
+    ],
+    "events": [
+      {
+        "name": "onBeforeScript",
+        "permissions": ["manifest:user_scripts.api_script"],
+        "allowedContexts": ["content", "content_only"],
+        "type": "function",
+        "description": "Event called when a new userScript global has been created",
+        "parameters": [
+          {
+            "type": "object",
+            "name": "userScript",
+            "properties": {
+              "metadata": {
+                "type": "any",
+                "description": "The userScript metadata (as set in userScripts.register)"
+              },
+              "global": {
+                "type": "any",
+                "description": "The userScript global"
+              }
+            }
+          }
+        ]
+      }
     ]
   }
 ]