Bug 1197422 - Part 1: [webext] Use the correct URL for background page contexts. r=billm
authorKris Maglione <maglione.k@gmail.com>
Fri, 09 Oct 2015 04:14:55 -0700
changeset 303495 c39ec146b6a056b15ea157b126934b69c2a6cc94
parent 303494 672eab895605523dd371462b1f1b89ae696d6cf8
child 303496 84df75fc0206c25ef794ffdebf7104c23b30d4de
push id1001
push userraliiev@mozilla.com
push dateMon, 18 Jan 2016 19:06:03 +0000
treeherdermozilla-release@8b89261f3ac4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbillm
bugs1197422
milestone44.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 1197422 - Part 1: [webext] Use the correct URL for background page contexts. r=billm
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ext-backgroundPage.js
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -18,18 +18,21 @@ const Ci = Components.interfaces;
 const Cc = Components.classes;
 const Cu = Components.utils;
 const Cr = Components.results;
 
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/devtools/shared/event-emitter.js");
 
+
 XPCOMUtils.defineLazyModuleGetter(this, "Locale",
                                   "resource://gre/modules/Locale.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "Log",
+                                  "resource://gre/modules/Log.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "MatchPattern",
                                   "resource://gre/modules/MatchPattern.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
                                   "resource://gre/modules/NetUtil.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
                                   "resource://gre/modules/FileUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PrivateBrowsingUtils",
                                   "resource://gre/modules/PrivateBrowsingUtils.jsm");
@@ -54,16 +57,18 @@ ExtensionManagement.registerScript("chro
 Cu.import("resource://gre/modules/ExtensionUtils.jsm");
 var {
   MessageBroker,
   Messenger,
   injectAPI,
   flushJarCache,
 } = ExtensionUtils;
 
+const LOGGER_ID_BASE = "addons.webextension.";
+
 var scriptScope = this;
 
 // This object loads the ext-*.js scripts that define the extension API.
 var Management = {
   initialized: false,
   scopes: [],
   apis: [],
   emitter: new EventEmitter(),
@@ -331,18 +336,21 @@ this.Extension = function(addonData)
     Services.obs.addObserver(this, "xpcom-shutdown", false);
     this.cleanupFile = addonData.cleanupFile || null;
     delete addonData.cleanupFile;
   }
 
   this.addonData = addonData;
   this.id = addonData.id;
   this.baseURI = Services.io.newURI("moz-extension://" + uuid, null, null);
+  this.baseURI.QueryInterface(Ci.nsIURL);
   this.manifest = null;
   this.localeMessages = null;
+  this.logger = Log.repository.getLogger(LOGGER_ID_BASE + this.id.replace(/\./g, "-"));
+  this.principal = this.createPrincipal();
 
   this.views = new Set();
 
   this.onStartup = null;
 
   this.hasShutdown = false;
   this.onShutdown = new Set();
 
@@ -482,16 +490,34 @@ Extension.prototype = {
   emit(...args) {
     return this.emitter.emit(...args);
   },
 
   testMessage(...args) {
     Management.emit("test-message", this, ...args);
   },
 
+  createPrincipal(uri = this.baseURI) {
+    return Services.scriptSecurityManager.createCodebasePrincipal(
+      uri, {addonId: this.id});
+  },
+
+  // Checks that the given URL is a child of our baseURI.
+  isExtensionURL(url) {
+    let uri = Services.io.newURI(url, null, null);
+
+    let common = this.baseURI.getCommonBaseSpec(uri);
+    return common == this.baseURI.spec;
+  },
+
+  // Report an error about the extension's manifest file.
+  manifestError(message) {
+    this.logger.error(`Loading extension '${this.id}': ${message}`);
+  },
+
   // Representation of the extension to send to content
   // processes. This should include anything the content process might
   // need.
   serialize() {
     return {
       id: this.id,
       uuid: this.uuid,
       manifest: this.manifest,
--- a/toolkit/components/extensions/ext-backgroundPage.js
+++ b/toolkit/components/extensions/ext-backgroundPage.js
@@ -16,46 +16,59 @@ function BackgroundPage(options, extensi
   this.context = null;
 }
 
 BackgroundPage.prototype = {
   build() {
     let webNav = Services.appShell.createWindowlessBrowser(false);
     this.webNav = webNav;
 
-    let principal = Services.scriptSecurityManager.createCodebasePrincipal(this.extension.baseURI,
-                                                                           {addonId: this.extension.id});
+    let url;
+    if (this.page) {
+      url = this.extension.baseURI.resolve(this.page);
+    } else {
+      // TODO: Chrome uses "_generated_background_page.html" for this.
+      url = this.extension.baseURI.resolve("_blank.html");
+    }
+
+    if (!this.extension.isExtensionURL(url)) {
+      this.extension.manifestError("Background page must be a file within the extension");
+      url = this.extension.baseURI.resolve("_blank.html");
+    }
+
+    let uri = Services.io.newURI(url, null, null);
+    let principal = this.extension.createPrincipal(uri);
 
     let interfaceRequestor = webNav.QueryInterface(Ci.nsIInterfaceRequestor);
     let docShell = interfaceRequestor.getInterface(Ci.nsIDocShell);
 
-    this.context = new ExtensionPage(this.extension, {type: "background", docShell});
+    this.context = new ExtensionPage(this.extension, {type: "background", docShell, uri});
     GlobalManager.injectInDocShell(docShell, this.extension, this.context);
 
     docShell.createAboutBlankContentViewer(principal);
 
     let window = webNav.document.defaultView;
     this.contentWindow = window;
     this.context.contentWindow = window;
 
-    let url;
-    if (this.page) {
-      url = this.extension.baseURI.resolve(this.page);
-    } else {
-      url = this.extension.baseURI.resolve("_blank.html");
-    }
     webNav.loadURI(url, 0, null, null, null);
 
     // TODO: Right now we run onStartup after the background page
     // finishes. See if this is what Chrome does.
     window.windowRoot.addEventListener("load", () => {
       if (this.scripts) {
         let doc = window.document;
         for (let script of this.scripts) {
           let url = this.extension.baseURI.resolve(script);
+
+          if (!this.extension.isExtensionURL(url)) {
+            this.extension.manifestError("Background scripts must be files within the extension");
+            continue;
+          }
+
           let tag = doc.createElement("script");
           tag.setAttribute("src", url);
           tag.async = false;
           doc.body.appendChild(tag);
         }
       }
 
       if (this.extension.onStartup) {