Backed out 5 changesets (bug 1197420) for test_ext_permissions.js and test_chrome_ext_permissions.html timeouts on Fennec
authorPhil Ringnalda <philringnalda@gmail.com>
Fri, 24 Mar 2017 20:43:24 -0700
changeset 397716 0fa8a7ec4b425fbeb6118f6b6b1d98a41f9dc0ac
parent 397715 24b5d95457e90c7859e08817c11769a0de9c0ed7
child 397717 13dd8c2aef35f4410c15a8ba4a7525bf0d3709b1
push id7391
push usermtabara@mozilla.com
push dateMon, 12 Jun 2017 13:08:53 +0000
treeherdermozilla-beta@2191d7f87e2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1197420
milestone55.0a1
backs out8a0125e00903ad10f4ce56a65b9242e5de235002
925e3a9499ee1f8a4f17bea37179a1416846aa37
46e135035f1049d81fabfc8c5c74c1b2d46f26b3
440bab1415095b4574672125d1786ad13766fad2
3e690bbe8b5a74e1eb952843445d0d145ac751ca
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
Backed out 5 changesets (bug 1197420) for test_ext_permissions.js and test_chrome_ext_permissions.html timeouts on Fennec CLOSED TREE Backed out changeset 8a0125e00903 (bug 1197420) Backed out changeset 925e3a9499ee (bug 1197420) Backed out changeset 46e135035f10 (bug 1197420) Backed out changeset 440bab141509 (bug 1197420) Backed out changeset 3e690bbe8b5a (bug 1197420)
browser/base/content/test/webextensions/browser_extension_sideloading.js
browser/components/extensions/schemas/bookmarks.json
browser/components/extensions/schemas/history.json
browser/components/extensions/schemas/tabs.json
browser/locales/en-US/chrome/browser/browser.properties
browser/modules/ExtensionsUI.jsm
mobile/android/components/extensions/schemas/tabs.json
toolkit/components/extensions/Extension.jsm
toolkit/components/extensions/ExtensionChild.jsm
toolkit/components/extensions/ExtensionCommon.jsm
toolkit/components/extensions/ExtensionContent.jsm
toolkit/components/extensions/ExtensionPermissions.jsm
toolkit/components/extensions/ExtensionUtils.jsm
toolkit/components/extensions/ext-c-permissions.js
toolkit/components/extensions/ext-permissions.js
toolkit/components/extensions/extensions-toolkit.manifest
toolkit/components/extensions/jar.mn
toolkit/components/extensions/moz.build
toolkit/components/extensions/schemas/cookies.json
toolkit/components/extensions/schemas/jar.mn
toolkit/components/extensions/schemas/manifest.json
toolkit/components/extensions/schemas/permissions.json
toolkit/components/extensions/schemas/top_sites.json
toolkit/components/extensions/schemas/web_navigation.json
toolkit/components/extensions/schemas/web_request.json
toolkit/components/extensions/test/mochitest/chrome.ini
toolkit/components/extensions/test/mochitest/test_chrome_ext_permissions.html
toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
toolkit/components/extensions/test/xpcshell/xpcshell.ini
toolkit/modules/addons/MatchPattern.jsm
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/content/extensions.js
toolkit/mozapps/extensions/internal/XPIProvider.jsm
toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
--- a/browser/base/content/test/webextensions/browser_extension_sideloading.js
+++ b/browser/base/content/test/webextensions/browser_extension_sideloading.js
@@ -90,56 +90,56 @@ add_task(function* () {
   const ID1 = "addon1@tests.mozilla.org";
   let mock1 = new MockAddon({
     id: ID1,
     name: "Test 1",
     userDisabled: true,
     seen: false,
     userPermissions: {
       permissions: ["history"],
-      origins: ["https://*/*"],
+      hosts: ["https://*/*"],
     },
     iconURL: ICON_URL,
   });
 
   const ID2 = "addon2@tests.mozilla.org";
   let mock2 = new MockAddon({
     id: ID2,
     name: "Test 2",
     userDisabled: true,
     seen: false,
     userPermissions: {
       permissions: [],
-      origins: [],
+      hosts: [],
     },
   });
 
   const ID3 = "addon3@tests.mozilla.org";
   let mock3 = new MockAddon({
     id: ID3,
     name: "Test 3",
     isWebExtension: true,
     userDisabled: true,
     seen: false,
     userPermissions: {
       permissions: [],
-      origins: ["<all_urls>"],
+      hosts: ["<all_urls>"],
     }
   });
 
   const ID4 = "addon4@tests.mozilla.org";
   let mock4 = new MockAddon({
     id: ID4,
     name: "Test 4",
     isWebExtension: true,
     userDisabled: true,
     seen: false,
     userPermissions: {
       permissions: [],
-      origins: ["<all_urls>"],
+      hosts: ["<all_urls>"],
     }
   });
 
   let provider = new MockProvider(mock1, mock2, mock3, mock4);
   AddonManagerPrivate.registerProvider(provider, [{
     id: "extension",
     name: "Extensions",
     uiPriority: 4000,
--- a/browser/components/extensions/schemas/bookmarks.json
+++ b/browser/components/extensions/schemas/bookmarks.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "bookmarks"
           ]
         }]
       }
     ]
--- a/browser/components/extensions/schemas/history.json
+++ b/browser/components/extensions/schemas/history.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "history"
           ]
         }]
       }
     ]
--- a/browser/components/extensions/schemas/tabs.json
+++ b/browser/components/extensions/schemas/tabs.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "activeTab",
             "tabs"
           ]
         }]
       }
--- a/browser/locales/en-US/chrome/browser/browser.properties
+++ b/browser/locales/en-US/chrome/browser/browser.properties
@@ -79,27 +79,16 @@ webextPerms.updateMenuItem=%S requires n
 # LOCALIZATION NOTE (webextPerms.updateText)
 # %S is replaced with the localized name of the updated extension.
 # Note, this string will be used as raw markup. Avoid characters like <, >, &
 webextPerms.updateText=%S has been updated. You must approve new permissions before the updated version will install. Choosing “Cancel” will maintain your current add-on version.
 
 webextPerms.updateAccept.label=Update
 webextPerms.updateAccept.accessKey=U
 
-# LOCALIZATION NOTE (webextPerms.optionalPermsHheader)
-# %S is replace with the localized name of the extension requested new
-# permissions.
-# Note, this string will be used as raw markup. Avoid characters like <, >, &
-webextPerms.optionalPermsHeader=%S requests additional permissions.
-webextPerms.optionalPermsListIntro=It wants to:
-webextPerms.optionalPermsAllow.label=Allow
-webextPerms.optionalPermsAllow.accessKey=A
-webextPerms.optionalPermsDeny.label=Deny
-webextPerms.optionalPermsDeny.accessKey=D
-
 webextPerms.description.bookmarks=Read and modify bookmarks
 webextPerms.description.clipboardRead=Get data from the clipboard
 webextPerms.description.clipboardWrite=Input data to the clipboard
 webextPerms.description.downloads=Download files and read and modify the browser’s download history
 webextPerms.description.geolocation=Access your location
 webextPerms.description.history=Access browsing history
 # LOCALIZATION NOTE (webextPerms.description.nativeMessaging)
 # %S will be replaced with the name of the application
--- a/browser/modules/ExtensionsUI.jsm
+++ b/browser/modules/ExtensionsUI.jsm
@@ -36,17 +36,16 @@ this.ExtensionsUI = {
   histogram: null,
 
   init() {
     this.histogram = Services.telemetry.getHistogramById("EXTENSION_INSTALL_PROMPT_RESULT");
 
     Services.obs.addObserver(this, "webextension-permission-prompt", false);
     Services.obs.addObserver(this, "webextension-update-permissions", false);
     Services.obs.addObserver(this, "webextension-install-notify", false);
-    Services.obs.addObserver(this, "webextension-optional-permission-prompt", false);
 
     this._checkForSideloaded();
   },
 
   _checkForSideloaded() {
     AddonManager.getAllAddons(addons => {
       // Check for any side-loaded addons that the user is allowed
       // to enable.
@@ -199,24 +198,16 @@ this.ExtensionsUI = {
       this.emit("change");
     } else if (topic == "webextension-install-notify") {
       let {target, addon, callback} = subject.wrappedJSObject;
       this.showInstallNotification(target, addon).then(() => {
         if (callback) {
           callback();
         }
       });
-    } else if (topic == "webextension-optional-permission-prompt") {
-      let {browser, name, icon, permissions, resolve} = subject.wrappedJSObject;
-      let strings = this._buildStrings({
-        type: "optional",
-        addon: {name},
-        permissions,
-      });
-      resolve(this.showPermissionsPrompt(browser, strings, icon));
     }
   },
 
   // Escape &, <, and > characters in a string so that it may be
   // injected as part of raw markup.
   _sanitizeName(name) {
     return name.replace(/&/g, "&amp;")
                .replace(/</g, "&lt;")
@@ -224,21 +215,21 @@ this.ExtensionsUI = {
   },
 
   // Create a set of formatted strings for a permission prompt
   _buildStrings(info) {
     let result = {};
 
     let bundle = Services.strings.createBundle(BROWSER_PROPERTIES);
 
-    let perms = info.permissions || {origins: [], permissions: []};
+    let perms = info.permissions || {hosts: [], permissions: []};
 
     // First classify our host permissions
     let allUrls = false, wildcards = [], sites = [];
-    for (let permission of perms.origins) {
+    for (let permission of perms.hosts) {
       if (permission == "<all_urls>") {
         allUrls = true;
         break;
       }
       let match = /^[htps*]+:\/\/([^/]+)\//.exec(permission);
       if (!match) {
         throw new Error("Unparseable host permission");
       }
--- a/mobile/android/components/extensions/schemas/tabs.json
+++ b/mobile/android/components/extensions/schemas/tabs.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "activeTab",
             "tabs"
           ]
         }]
       }
--- a/toolkit/components/extensions/Extension.jsm
+++ b/toolkit/components/extensions/Extension.jsm
@@ -31,18 +31,16 @@ Cu.import("resource://gre/modules/Servic
 XPCOMUtils.defineLazyPreferenceGetter(this, "processCount", "dom.ipc.processCount.extension");
 
 XPCOMUtils.defineLazyModuleGetter(this, "AddonManager",
                                   "resource://gre/modules/AddonManager.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
                                   "resource://gre/modules/AppConstants.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionAPIs",
                                   "resource://gre/modules/ExtensionAPI.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPermissions",
-                                  "resource://gre/modules/ExtensionPermissions.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
                                   "resource://gre/modules/ExtensionStorage.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "ExtensionTestCommon",
                                   "resource://testing-common/ExtensionTestCommon.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Locale",
                                   "resource://gre/modules/Locale.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "Log",
                                   "resource://gre/modules/Log.jsm");
@@ -78,17 +76,16 @@ XPCOMUtils.defineLazyServiceGetter(this,
 
 var {
   GlobalManager,
   ParentAPIManager,
   apiManager: Management,
 } = ExtensionParent;
 
 const {
-  classifyPermission,
   EventEmitter,
   LocaleData,
   StartupCache,
   getUniqueId,
   validateThemeManifest,
 } = ExtensionUtils;
 
 XPCOMUtils.defineLazyGetter(this, "console", ExtensionUtils.getConsole);
@@ -373,43 +370,43 @@ this.ExtensionData = class {
     });
   }
 
   // This method should return a structured representation of any
   // capabilities this extension has access to, as derived from the
   // manifest.  The current implementation just returns the contents
   // of the permissions attribute, if we add things like url_overrides,
   // they should also be added here.
-  get userPermissions() {
+  userPermissions() {
     let result = {
-      origins: this.whiteListedHosts.pat,
+      hosts: this.whiteListedHosts.pat,
       apis: [...this.apiNames],
     };
 
     if (Array.isArray(this.manifest.content_scripts)) {
       for (let entry of this.manifest.content_scripts) {
-        result.origins.push(...entry.matches);
+        result.hosts.push(...entry.matches);
       }
     }
     const EXP_PATTERN = /^experiments\.\w+/;
     result.permissions = [...this.permissions]
-      .filter(p => !result.origins.includes(p) && !EXP_PATTERN.test(p));
+      .filter(p => !result.hosts.includes(p) && !EXP_PATTERN.test(p));
     return result;
   }
 
   // Compute the difference between two sets of permissions, suitable
   // for presenting to the user.
   static comparePermissions(oldPermissions, newPermissions) {
     // See bug 1331769: should we do something more complicated to
     // compare host permissions?
     // e.g., if we go from <all_urls> to a specific host or from
     // a *.domain.com to specific-host.domain.com that's actually a
     // drop in permissions but the simple test below will cause a prompt.
     return {
-      origins: newPermissions.origins.filter(perm => !oldPermissions.origins.includes(perm)),
+      hosts: newPermissions.hosts.filter(perm => !oldPermissions.hosts.includes(perm)),
       permissions: newPermissions.permissions.filter(perm => !oldPermissions.permissions.includes(perm)),
     };
   }
 
   parseManifest() {
     return Promise.all([
       this.readJSON("manifest.json"),
       Management.lazyInit(),
@@ -480,21 +477,22 @@ this.ExtensionData = class {
 
     let whitelist = [];
     for (let perm of this.manifest.permissions) {
       if (perm == "contextualIdentities" && !Preferences.get("privacy.userContext.enabled")) {
         continue;
       }
 
       this.permissions.add(perm);
-      let type = classifyPermission(perm);
-      if (type.origin) {
+
+      let match = /^(\w+)(?:\.(\w+)(?:\.\w+)*)?$/.exec(perm);
+      if (!match) {
         whitelist.push(perm);
-      } else if (type.api) {
-        this.apiNames.add(type.api);
+      } else if (match[1] == "experiments" && match[2]) {
+        this.apiNames.add(match[2]);
       }
     }
     this.whiteListedHosts = new MatchPattern(whitelist);
 
     for (let api of this.apiNames) {
       this.dependencies.add(`${api}@experiments.addons.mozilla.org`);
     }
 
@@ -629,17 +627,17 @@ this.ExtensionData = class {
       this.localeData.selectedLocale = locale;
       return results[0];
     }.bind(this));
   }
 };
 
 let _browserUpdated = false;
 
-const PROXIED_EVENTS = new Set(["test-harness-message", "add-permissions", "remove-permissions"]);
+const PROXIED_EVENTS = new Set(["test-harness-message"]);
 
 // We create one instance of this class per extension. |addonData|
 // comes directly from bootstrap.js when initializing.
 this.Extension = class extends ExtensionData {
   constructor(addonData, startupReason) {
     super(addonData.resourceURI);
 
     this.uuid = UUIDMap.get(addonData.id);
@@ -677,42 +675,19 @@ this.Extension = class extends Extension
 
     this.hasShutdown = false;
     this.onShutdown = new Set();
 
     this.uninstallURL = null;
 
     this.apis = [];
     this.whiteListedHosts = null;
-    this._optionalOrigins = null;
     this.webAccessibleResources = null;
 
     this.emitter = new EventEmitter();
-
-    /* eslint-disable mozilla/balanced-listeners */
-    this.on("add-permissions", (ignoreEvent, permissions) => {
-      for (let perm of permissions.permissions) {
-        this.permissions.add(perm);
-      }
-
-      if (permissions.origins.length > 0) {
-        this.whiteListedHosts = new MatchPattern(this.whiteListedHosts.pat.concat(...permissions.origins));
-      }
-    });
-
-    this.on("remove-permissions", (ignoreEvent, permissions) => {
-      for (let perm of permissions.permissions) {
-        this.permissions.delete(perm);
-      }
-
-      for (let origin of permissions.origins) {
-        this.whiteListedHosts.removeOne(origin);
-      }
-    });
-    /* eslint-enable mozilla/balanced-listeners */
   }
 
   static set browserUpdated(updated) {
     _browserUpdated = updated;
   }
 
   static get browserUpdated() {
     return _browserUpdated;
@@ -816,17 +791,16 @@ this.Extension = class extends Extension
       resourceURL: this.addonData.resourceURI.spec,
       baseURL: this.baseURI.spec,
       content_scripts: this.manifest.content_scripts || [],  // eslint-disable-line camelcase
       webAccessibleResources: this.webAccessibleResources.serialize(),
       whiteListedHosts: this.whiteListedHosts.serialize(),
       localeData: this.localeData.serialize(),
       permissions: this.permissions,
       principal: this.principal,
-      optionalPermissions: this.manifest.optional_permissions,
     };
   }
 
   broadcast(msg, data) {
     return new Promise(resolve => {
       let {ppmm} = Services;
       let children = new Set();
       for (let i = 0; i < ppmm.childCount; i++) {
@@ -914,69 +888,58 @@ this.Extension = class extends Extension
 
       let match = Locale.findClosestLocale(localeList);
       locale = match ? match.name : this.defaultLocale;
     }
 
     return super.initLocale(locale);
   }
 
-  async startup() {
+  startup() {
     let started = false;
-
-    try {
-      let [, perms] = await Promise.all([this.loadManifest(), ExtensionPermissions.get(this)]);
-
+    return this.loadManifest().then(() => {
       ExtensionManagement.startupExtension(this.uuid, this.addonData.resourceURI, this);
       started = true;
 
       if (!this.hasShutdown) {
-        await this.initLocale();
+        return this.initLocale();
       }
-
+    }).then(() => {
       if (this.errors.length) {
         return Promise.reject({errors: this.errors});
       }
 
       if (this.hasShutdown) {
         return;
       }
 
       GlobalManager.init(this);
 
-      // Apply optional permissions
-      for (let perm of perms.permissions) {
-        this.permissions.add(perm);
-      }
-      if (perms.origins.length > 0) {
-        this.whiteListedHosts = new MatchPattern(this.whiteListedHosts.pat.concat(...perms.origins));
-      }
-
       // The "startup" Management event sent on the extension instance itself
       // is emitted just before the Management "startup" event,
       // and it is used to run code that needs to be executed before
       // any of the "startup" listeners.
       this.emit("startup", this);
       Management.emit("startup", this);
 
-      await this.runManifest(this.manifest);
-
+      return this.runManifest(this.manifest);
+    }).then(() => {
       Management.emit("ready", this);
-    } catch (e) {
+    }).catch(e => {
       dump(`Extension error: ${e.message} ${e.filename || e.fileName}:${e.lineNumber} :: ${e.stack || new Error().stack}\n`);
       Cu.reportError(e);
 
       if (started) {
         ExtensionManagement.shutdownExtension(this.uuid);
       }
 
       this.cleanupGeneratedFile();
 
       throw e;
-    }
+    });
   }
 
   cleanupGeneratedFile() {
     if (!this.cleanupFile) {
       return;
     }
 
     let file = this.cleanupFile;
@@ -1038,37 +1001,21 @@ this.Extension = class extends Extension
   }
 
   observe(subject, topic, data) {
     if (topic === "xpcom-shutdown") {
       this.cleanupGeneratedFile();
     }
   }
 
-  hasPermission(perm, includeOptional = false) {
+  hasPermission(perm) {
     let match = /^manifest:(.*)/.exec(perm);
     if (match) {
       return this.manifest[match[1]] != null;
     }
 
-    if (this.permissions.has(perm)) {
-      return true;
-    }
-
-    if (includeOptional && this.manifest.optional_permissions.includes(perm)) {
-      return true;
-    }
-
-    return false;
+    return this.permissions.has(perm);
   }
 
   get name() {
     return this.manifest.name;
   }
-
-  get optionalOrigins() {
-    if (this._optionalOrigins == null) {
-      let origins = this.manifest.optional_permissions.filter(perm => classifyPermission(perm).origin);
-      this._optionalOrigins = new MatchPattern(origins);
-    }
-    return this._optionalOrigins;
-  }
 };
--- a/toolkit/components/extensions/ExtensionChild.jsm
+++ b/toolkit/components/extensions/ExtensionChild.jsm
@@ -622,32 +622,16 @@ class ChildAPIManager {
     let params = {
       childId: this.id,
       extensionId: context.extension.id,
       principal: context.principal,
     };
     Object.assign(params, contextData);
 
     this.messageManager.sendAsyncMessage("API:CreateProxyContext", params);
-
-    this.permissionsChangedCallbacks = new Set();
-    this.updatePermissions = null;
-    if (this.context.extension.optionalPermissions.length > 0) {
-      this.updatePermissions = () => {
-        for (let callback of this.permissionsChangedCallbacks) {
-          try {
-            callback();
-          } catch (err) {
-            Cu.reportError(err);
-          }
-        }
-      };
-      this.context.extension.on("add-permissions", this.updatePermissions);
-      this.context.extension.on("remove-permissions", this.updatePermissions);
-    }
   }
 
   receiveMessage({name, messageName, data}) {
     if (data.childId != this.id) {
       return;
     }
 
     switch (name || messageName) {
@@ -737,20 +721,16 @@ class ChildAPIManager {
       addListener: (listener, ...args) => impl.addListener(listener, args),
       removeListener: (listener) => impl.removeListener(listener),
       hasListener: (listener) => impl.hasListener(listener),
     };
   }
 
   close() {
     this.messageManager.sendAsyncMessage("API:CloseProxyContext", {childId: this.id});
-    if (this.updatePermissions) {
-      this.context.extension.off("add-permissions", this.updatePermissions);
-      this.context.extension.off("remove-permissions", this.updatePermissions);
-    }
   }
 
   get cloneScope() {
     return this.context.cloneScope;
   }
 
   get principal() {
     return this.context.principal;
@@ -796,24 +776,16 @@ class ChildAPIManager {
   getFallbackImplementation(namespace, name) {
     // No local API found, defer implementation to the parent.
     return new ProxyAPIImplementation(namespace, name, this);
   }
 
   hasPermission(permission) {
     return this.context.extension.hasPermission(permission);
   }
-
-  isPermissionRevokable(permission) {
-    return this.context.extension.optionalPermissions.includes(permission);
-  }
-
-  setPermissionsChangedCallback(callback) {
-    this.permissionsChangedCallbacks.add(callback);
-  }
 }
 
 class ExtensionBaseContextChild extends BaseContext {
   /**
    * This ExtensionBaseContextChild represents an addon execution environment
    * that is running in an addon or devtools child process.
    *
    * @param {BrowserExtensionContent} extension This context's owner.
--- a/toolkit/components/extensions/ExtensionCommon.jsm
+++ b/toolkit/components/extensions/ExtensionCommon.jsm
@@ -684,21 +684,18 @@ class SchemaAPIManager extends EventEmit
           }
           copy(dest[prop], source[prop]);
         } else {
           Object.defineProperty(dest, prop, desc);
         }
       }
     }
 
-    function hasPermission(perm) {
-      return context.extension.hasPermission(perm, true);
-    }
     for (let api of apis) {
-      if (Schemas.checkPermissions(api.namespace, {hasPermission})) {
+      if (Schemas.checkPermissions(api.namespace, context.extension)) {
         api = api.getAPI(context);
         copy(obj, api);
       }
     }
   }
 }
 
 const ExtensionCommon = {
--- a/toolkit/components/extensions/ExtensionContent.jsm
+++ b/toolkit/components/extensions/ExtensionContent.jsm
@@ -986,17 +986,16 @@ class BrowserExtensionContent extends Ev
 
     defineLazyGetter(this, "scripts", () => {
       return data.content_scripts.map(scriptData => new Script(this, scriptData));
     });
 
     this.webAccessibleResources = new MatchGlobs(data.webAccessibleResources);
     this.whiteListedHosts = new MatchPattern(data.whiteListedHosts);
     this.permissions = data.permissions;
-    this.optionalPermissions = data.optionalPermissions;
     this.principal = data.principal;
 
     this.localeData = new LocaleData(data.localeData);
 
     this.manifest = data.manifest;
     this.baseURI = Services.io.newURI(data.baseURL);
 
     // Only used in addon processes.
@@ -1006,44 +1005,16 @@ class BrowserExtensionContent extends Ev
     this.devtoolsViews = new Set();
 
     let uri = Services.io.newURI(data.resourceURL);
 
     if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
       // Extension.jsm takes care of this in the parent.
       ExtensionManagement.startupExtension(this.uuid, uri, this);
     }
-
-    /* eslint-disable mozilla/balanced-listeners */
-    this.on("add-permissions", (ignoreEvent, permissions) => {
-      if (permissions.permissions.length > 0) {
-        for (let perm of permissions.permissions) {
-          this.permissions.add(perm);
-        }
-      }
-
-      if (permissions.origins.length > 0) {
-        this.whiteListedHosts = new MatchPattern(this.whiteListedHosts.pat.concat(...permissions.origins));
-      }
-    });
-
-    this.on("remove-permissions", (ignoreEvent, permissions) => {
-      if (permissions.permissions.length > 0) {
-        for (let perm of permissions.permissions) {
-          this.permissions.delete(perm);
-        }
-      }
-
-      if (permissions.origins.length > 0) {
-        for (let origin of permissions.origins) {
-          this.whiteListedHosts.removeOne(origin);
-        }
-      }
-    });
-    /* eslint-enable mozilla/balanced-listeners */
   }
 
   shutdown() {
     Services.cpmm.removeMessageListener(this.MESSAGE_EMIT_EVENT, this);
 
     if (Services.appinfo.processType == Services.appinfo.PROCESS_TYPE_CONTENT) {
       ExtensionManagement.shutdownExtension(this.uuid);
     }
deleted file mode 100644
--- a/toolkit/components/extensions/ExtensionPermissions.jsm
+++ /dev/null
@@ -1,123 +0,0 @@
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "JSONFile",
-                                  "resource://gre/modules/JSONFile.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "OS",
-                                  "resource://gre/modules/osfile.jsm");
-
-this.EXPORTED_SYMBOLS = ["ExtensionPermissions"];
-
-const FILE_NAME = "extension-preferences.json";
-
-let prefs;
-let _initPromise;
-function lazyInit() {
-  if (!_initPromise) {
-    prefs = new JSONFile({path: OS.Path.join(OS.Constants.Path.profileDir, FILE_NAME)});
-
-    _initPromise = prefs.load();
-  }
-  return _initPromise;
-}
-
-function emptyPermissions() {
-  return {permissions: [], origins: []};
-}
-
-this.ExtensionPermissions = {
-  async get(extension) {
-    await lazyInit();
-
-    let perms = emptyPermissions();
-    if (prefs.data[extension.id]) {
-      Object.assign(perms, prefs.data[extension.id]);
-    }
-    return perms;
-  },
-
-  // Add new permissions for the given extension.  `permissions` is
-  // in the format that is passed to browser.permissions.request().
-  async add(extension, perms) {
-    await lazyInit();
-
-    if (!prefs.data[extension.id]) {
-      prefs.data[extension.id] = emptyPermissions();
-    }
-    let {permissions, origins} = prefs.data[extension.id];
-
-    let added = emptyPermissions();
-
-    for (let perm of perms.permissions) {
-      if (!permissions.includes(perm)) {
-        added.permissions.push(perm);
-        permissions.push(perm);
-      }
-    }
-    for (let origin of perms.origins) {
-      if (!origins.includes(origin)) {
-        added.origins.push(origin);
-        origins.push(origin);
-      }
-    }
-
-    if (added.permissions.length > 0 || added.origins.length > 0) {
-      prefs.saveSoon();
-      extension.emit("add-permissions", added);
-    }
-  },
-
-  // Revoke permissions from the given extension.  `permissions` is
-  // in the format that is passed to browser.permissions.remove().
-  async remove(extension, perms) {
-    await lazyInit();
-
-    if (!prefs.data[extension.id]) {
-      return;
-    }
-    let {permissions, origins} = prefs.data[extension.id];
-
-    let removed = emptyPermissions();
-
-    for (let perm of perms.permissions) {
-      let i = permissions.indexOf(perm);
-      if (i >= 0) {
-        removed.permissions.push(perm);
-        permissions.splice(i, 1);
-      }
-    }
-    for (let origin of perms.origins) {
-      let i = origins.indexOf(origin);
-      if (i >= 0) {
-        removed.origins.push(origin);
-        origins.splice(i, 1);
-      }
-    }
-
-    if (removed.permissions.length > 0 || removed.origins.length > 0) {
-      prefs.saveSoon();
-      extension.emit("remove-permissions", removed);
-    }
-  },
-
-  async removeAll(extension) {
-    await lazyInit();
-    delete prefs.data[extension.id];
-    prefs.saveSoon();
-  },
-
-  // This is meant for tests only
-  async _uninit() {
-    if (!_initPromise) {
-      return;
-    }
-
-    await _initPromise;
-    await prefs.finalize();
-    prefs = null;
-    _initPromise = null;
-  },
-};
--- a/toolkit/components/extensions/ExtensionUtils.jsm
+++ b/toolkit/components/extensions/ExtensionUtils.jsm
@@ -1323,41 +1323,17 @@ class MessageManagerProxy {
   handleEvent(event) {
     if (event.type == "SwapDocShells") {
       this.removeListeners(this.eventTarget);
       this.addListeners(event.detail);
     }
   }
 }
 
-/**
- * Classify an individual permission from a webextension manifest
- * as a host/origin permission, an api permission, or a regular permission.
- *
- * @param {string} perm  The permission string to classify
- *
- * @returns {object}
- *          An object with exactly one of the following properties:
- *          "origin" to indicate this is a host/origin permission.
- *          "api" to indicate this is an api permission
- *                (as used for webextensions experiments).
- *          "permission" to indicate this is a regular permission.
- */
-function classifyPermission(perm) {
-  let match = /^(\w+)(?:\.(\w+)(?:\.\w+)*)?$/.exec(perm);
-  if (!match) {
-    return {origin: perm};
-  } else if (match[1] == "experiments" && match[2]) {
-    return {api: match[2]};
-  }
-  return {permission: perm};
-}
-
 this.ExtensionUtils = {
-  classifyPermission,
   defineLazyGetter,
   detectLanguage,
   extend,
   findPathInObject,
   flushJarCache,
   getConsole,
   getInnerWindowID,
   getMessageManager,
deleted file mode 100644
--- a/toolkit/components/extensions/ext-c-permissions.js
+++ /dev/null
@@ -1,19 +0,0 @@
-"use strict";
-
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-const {ExtensionError} = ExtensionUtils;
-
-extensions.registerSchemaAPI("permissions", "addon_child", context => {
-  return {
-    permissions: {
-      async request(perms) {
-        let winUtils = context.contentWindow.getInterface(Ci.nsIDOMWindowUtils);
-        if (!winUtils.isHandlingUserInput) {
-          throw new ExtensionError("May only request permissions from a user input handler");
-        }
-
-        return context.childManager.callParentAsyncFunction("permissions.request_parent", [perms]);
-      },
-    },
-  };
-});
deleted file mode 100644
--- a/toolkit/components/extensions/ext-permissions.js
+++ /dev/null
@@ -1,97 +0,0 @@
-"use strict";
-
-const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
-
-Cu.import("resource://gre/modules/ExtensionUtils.jsm");
-Cu.import("resource://gre/modules/XPCOMUtils.jsm");
-
-XPCOMUtils.defineLazyModuleGetter(this, "ExtensionPermissions",
-                                  "resource://gre/modules/ExtensionPermissions.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "NetUtil",
-                                  "resource://gre/modules/NetUtil.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "Services",
-                                  "resource://gre/modules/Services.jsm");
-
-const {
-  ExtensionError,
-} = ExtensionUtils;
-
-XPCOMUtils.defineLazyPreferenceGetter(this, "promptsEnabled",
-                                      "extensions.webextOptionalPermissionPrompts");
-
-extensions.registerSchemaAPI("permission", "addon_parent", context => {
-  return {
-    permissions: {
-      async request_parent(perms) {
-        let {permissions, origins} = perms;
-
-        let manifestPermissions = context.extension.manifest.optional_permissions;
-        for (let perm of permissions) {
-          if (!manifestPermissions.includes(perm)) {
-            throw new ExtensionError(`Cannot request permission ${perm} since it was not declared in optional_permissions`);
-          }
-        }
-
-        let optionalOrigins = context.extension.optionalOrigins;
-        for (let origin of origins) {
-          if (!optionalOrigins.subsumes(origin)) {
-            throw new ExtensionError(`Cannot request origin permission for ${origin} since it was not declared in optional_permissions`);
-          }
-        }
-
-        if (promptsEnabled) {
-          let allow = await new Promise(resolve => {
-            let subject = {
-              wrappedJSObject: {
-                browser: context.xulBrowser,
-                name: context.extension.name,
-                icon: context.extension.iconURL,
-                permissions: {permissions, origins},
-                resolve,
-              },
-            };
-            Services.obs.notifyObservers(subject, "webextension-optional-permission-prompt", null);
-          });
-          if (!allow) {
-            return false;
-          }
-        }
-
-        await ExtensionPermissions.add(context.extension, perms);
-        return true;
-      },
-
-      async getAll() {
-        let perms = context.extension.userPermissions;
-        delete perms.apis;
-        return perms;
-      },
-
-      async contains(permissions) {
-        for (let perm of permissions.permissions) {
-          if (!context.extension.hasPermission(perm)) {
-            return false;
-          }
-        }
-
-        for (let origin of permissions.origins) {
-          if (!context.extension.whiteListedHosts.subsumes(origin)) {
-            return false;
-          }
-        }
-
-        return true;
-      },
-
-      async remove(permissions) {
-        await ExtensionPermissions.remove(context.extension, permissions);
-        return true;
-      },
-    },
-  };
-});
-
-/* eslint-disable mozilla/balanced-listeners */
-extensions.on("uninstall", extension => {
-  ExtensionPermissions.removeAll(extension);
-});
--- a/toolkit/components/extensions/extensions-toolkit.manifest
+++ b/toolkit/components/extensions/extensions-toolkit.manifest
@@ -6,30 +6,28 @@ category webextension-scripts cookies ch
 category webextension-scripts downloads chrome://extensions/content/ext-downloads.js
 category webextension-scripts extension chrome://extensions/content/ext-extension.js
 category webextension-scripts geolocation chrome://extensions/content/ext-geolocation.js
 category webextension-scripts handlers chrome://extensions/content/ext-protocolHandlers.js
 category webextension-scripts i18n chrome://extensions/content/ext-i18n.js
 category webextension-scripts idle chrome://extensions/content/ext-idle.js
 category webextension-scripts management chrome://extensions/content/ext-management.js
 category webextension-scripts notifications chrome://extensions/content/ext-notifications.js
-category webextension-scripts permissions chrome://extensions/content/ext-permissions.js
 category webextension-scripts privacy chrome://extensions/content/ext-privacy.js
 category webextension-scripts proxy chrome://extensions/content/ext-proxy.js
 category webextension-scripts runtime chrome://extensions/content/ext-runtime.js
 category webextension-scripts storage chrome://extensions/content/ext-storage.js
 category webextension-scripts theme chrome://extensions/content/ext-theme.js
 category webextension-scripts topSites chrome://extensions/content/ext-topSites.js
 category webextension-scripts webNavigation chrome://extensions/content/ext-webNavigation.js
 category webextension-scripts webRequest chrome://extensions/content/ext-webRequest.js
 
 # scripts specific for content process.
 category webextension-scripts-content extension chrome://extensions/content/ext-c-extension.js
 category webextension-scripts-content i18n chrome://extensions/content/ext-i18n.js
-category webextension-scripts-content permissions chrome://extensions/content/ext-c-permissions.js
 category webextension-scripts-content runtime chrome://extensions/content/ext-c-runtime.js
 category webextension-scripts-content storage chrome://extensions/content/ext-c-storage.js
 category webextension-scripts-content test chrome://extensions/content/ext-c-test.js
 
 # scripts specific for devtools extension contexts.
 category webextension-scripts-devtools extension chrome://extensions/content/ext-c-extension.js
 category webextension-scripts-devtools i18n chrome://extensions/content/ext-i18n.js
 category webextension-scripts-devtools runtime chrome://extensions/content/ext-c-runtime.js
@@ -38,17 +36,16 @@ category webextension-scripts-devtools t
 
 # scripts that must run in the same process as addon code.
 category webextension-scripts-addon backgroundPage chrome://extensions/content/ext-c-backgroundPage.js
 category webextension-scripts-addon extension chrome://extensions/content/ext-c-extension.js
 category webextension-scripts-addon i18n chrome://extensions/content/ext-i18n.js
 #ifndef ANDROID
 category webextension-scripts-addon identity chrome://extensions/content/ext-c-identity.js
 #endif
-category webextension-scripts-addon permissions chrome://extensions/content/ext-c-permissions.js
 category webextension-scripts-addon runtime chrome://extensions/content/ext-c-runtime.js
 category webextension-scripts-addon storage chrome://extensions/content/ext-c-storage.js
 category webextension-scripts-addon test chrome://extensions/content/ext-c-test.js
 
 # schemas
 category webextension-schemas alarms chrome://extensions/content/schemas/alarms.json
 category webextension-schemas contextualIdentities chrome://extensions/content/schemas/contextual_identities.json
 category webextension-schemas cookies chrome://extensions/content/schemas/cookies.json
@@ -60,17 +57,16 @@ category webextension-schemas handlers c
 category webextension-schemas i18n chrome://extensions/content/schemas/i18n.json
 #ifndef ANDROID
 category webextension-schemas identity chrome://extensions/content/schemas/identity.json
 #endif
 category webextension-schemas idle chrome://extensions/content/schemas/idle.json
 category webextension-schemas management chrome://extensions/content/schemas/management.json
 category webextension-schemas native_host_manifest chrome://extensions/content/schemas/native_host_manifest.json
 category webextension-schemas notifications chrome://extensions/content/schemas/notifications.json
-category webextension-schemas permissions chrome://extensions/content/schemas/permissions.json
 category webextension-schemas privacy chrome://extensions/content/schemas/privacy.json
 category webextension-schemas proxy chrome://extensions/content/schemas/proxy.json
 category webextension-schemas runtime chrome://extensions/content/schemas/runtime.json
 category webextension-schemas storage chrome://extensions/content/schemas/storage.json
 category webextension-schemas test chrome://extensions/content/schemas/test.json
 category webextension-schemas theme chrome://extensions/content/schemas/theme.json
 category webextension-schemas top_sites chrome://extensions/content/schemas/top_sites.json
 category webextension-schemas types chrome://extensions/content/schemas/types.json
--- a/toolkit/components/extensions/jar.mn
+++ b/toolkit/components/extensions/jar.mn
@@ -11,29 +11,27 @@ toolkit.jar:
     content/extensions/ext-cookies.js
     content/extensions/ext-downloads.js
     content/extensions/ext-extension.js
     content/extensions/ext-geolocation.js
     content/extensions/ext-i18n.js
     content/extensions/ext-idle.js
     content/extensions/ext-management.js
     content/extensions/ext-notifications.js
-    content/extensions/ext-permissions.js
     content/extensions/ext-privacy.js
     content/extensions/ext-protocolHandlers.js
     content/extensions/ext-proxy.js
     content/extensions/ext-runtime.js
     content/extensions/ext-storage.js
     content/extensions/ext-theme.js
     content/extensions/ext-topSites.js
     content/extensions/ext-webRequest.js
     content/extensions/ext-webNavigation.js
     # Below is a separate group using the naming convention ext-c-*.js that run
     # in the child process.
     content/extensions/ext-c-backgroundPage.js
     content/extensions/ext-c-extension.js
 #ifndef ANDROID
     content/extensions/ext-c-identity.js
 #endif
-    content/extensions/ext-c-permissions.js
     content/extensions/ext-c-runtime.js
     content/extensions/ext-c-storage.js
     content/extensions/ext-c-test.js
--- a/toolkit/components/extensions/moz.build
+++ b/toolkit/components/extensions/moz.build
@@ -7,17 +7,16 @@
 EXTRA_JS_MODULES += [
     'Extension.jsm',
     'ExtensionAPI.jsm',
     'ExtensionChild.jsm',
     'ExtensionCommon.jsm',
     'ExtensionContent.jsm',
     'ExtensionManagement.jsm',
     'ExtensionParent.jsm',
-    'ExtensionPermissions.jsm',
     'ExtensionPreferencesManager.jsm',
     'ExtensionSettingsStore.jsm',
     'ExtensionStorage.jsm',
     'ExtensionStorageSync.jsm',
     'ExtensionTabs.jsm',
     'ExtensionUtils.jsm',
     'LegacyExtensionsUtils.jsm',
     'MessageChannel.jsm',
--- a/toolkit/components/extensions/schemas/cookies.json
+++ b/toolkit/components/extensions/schemas/cookies.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "cookies"
           ]
         }]
       }
     ]
--- a/toolkit/components/extensions/schemas/jar.mn
+++ b/toolkit/components/extensions/schemas/jar.mn
@@ -17,17 +17,16 @@ toolkit.jar:
 #ifndef ANDROID
     content/extensions/schemas/identity.json
 #endif
     content/extensions/schemas/idle.json
     content/extensions/schemas/management.json
     content/extensions/schemas/manifest.json
     content/extensions/schemas/native_host_manifest.json
     content/extensions/schemas/notifications.json
-    content/extensions/schemas/permissions.json
     content/extensions/schemas/proxy.json
     content/extensions/schemas/privacy.json
     content/extensions/schemas/runtime.json
     content/extensions/schemas/storage.json
     content/extensions/schemas/test.json
     content/extensions/schemas/theme.json
     content/extensions/schemas/top_sites.json
     content/extensions/schemas/types.json
--- a/toolkit/components/extensions/schemas/manifest.json
+++ b/toolkit/components/extensions/schemas/manifest.json
@@ -174,23 +174,16 @@
                   "type": "string",
                   "deprecated": "Unknown permission ${value}"
                 }
               ]
             },
             "optional": true
           },
 
-          "optional_permissions": {
-            "type": "array",
-            "items": { "$ref": "OptionalPermission" },
-            "optional": true,
-            "default": []
-          },
-
           "web_accessible_resources": {
             "type": "array",
             "items": { "type": "string" },
             "optional": true
           },
 
           "developer": {
             "type": "object",
@@ -209,45 +202,34 @@
             }
           }
 
         },
 
         "additionalProperties": { "$ref": "UnrecognizedProperty" }
       },
       {
-        "id": "OptionalPermission",
+        "id": "Permission",
         "choices": [
           {
             "type": "string",
             "enum": [
+              "alarms",
               "clipboardRead",
               "clipboardWrite",
               "geolocation",
               "idle",
-              "notifications"
+              "notifications",
+              "storage"
             ]
           },
           { "$ref": "MatchPattern" }
         ]
       },
       {
-        "id": "Permission",
-        "choices": [
-          { "$ref": "OptionalPermission" },
-          {
-            "type": "string",
-            "enum": [
-              "alarms",
-              "storage"
-            ]
-          }
-        ]
-      },
-      {
         "id": "HttpURL",
         "type": "string",
         "format": "url",
         "pattern": "^https?://.*$"
       },
       {
         "id": "ExtensionURL",
         "type": "string",
deleted file mode 100644
--- a/toolkit/components/extensions/schemas/permissions.json
+++ /dev/null
@@ -1,153 +0,0 @@
-[
-  {
-    "namespace": "permissions",
-    "permissions": ["manifest:optional_permissions"],
-    "types": [
-      {
-        "id": "Permissions",
-        "type": "object",
-        "properties": {
-          "permissions": {
-            "type": "array",
-            "items": { "$ref": "manifest.OptionalPermission" },
-            "optional": true,
-            "default": []
-          },
-          "origins": {
-            "type": "array",
-            "items": { "$ref": "manifest.MatchPattern" },
-            "optional": true,
-            "default": []
-          }
-        }
-      },
-      {
-        "id": "AnyPermissions",
-        "type": "object",
-        "properties": {
-          "permissions": {
-            "type": "array",
-            "items": { "$ref": "manifest.Permission" },
-            "optional": true,
-            "default": []
-          },
-          "origins": {
-            "type": "array",
-            "items": { "$ref": "manifest.MatchPattern" },
-            "optional": true,
-            "default": []
-          }
-        }
-      }
-    ],
-    "functions": [
-      {
-        "name": "getAll",
-        "type": "function",
-        "async": "callback",
-        "description": "Get a list of all the extension's permissions.",
-        "parameters": [
-          {
-            "name": "callback",
-            "type": "function",
-            "parameters": [
-              {
-                "name": "permissions",
-                "$ref": "AnyPermissions"
-              }
-            ]
-          }
-        ]
-      },
-      {
-        "name": "contains",
-        "type": "function",
-        "async": "callback",
-        "description": "Check if the extension has the given permissions.",
-        "parameters": [
-          {
-            "name": "permissions",
-            "$ref": "AnyPermissions"
-          },
-          {
-            "name": "callback",
-            "type": "function",
-            "parameters": [
-              {
-                "name": "result",
-                "type": "boolean"
-              }
-            ]
-          }
-        ]
-      },
-      {
-        "name": "request",
-        "type": "function",
-        "allowedContexts": ["content"],
-        "async": "callback",
-        "description": "Request the given permissions.",
-        "parameters": [
-          {
-            "name": "permissions",
-            "$ref": "Permissions"
-          },
-          {
-            "name": "callback",
-            "type": "function",
-            "parameters": [
-              {
-                "name": "granted",
-                "type": "boolean"
-              }
-            ]
-          }
-        ]
-      },
-      {
-        "name": "remove",
-        "type": "function",
-        "async": "callback",
-        "description": "Relinquish the given permissions.",
-        "parameters": [
-          {
-            "name": "permissions",
-            "$ref": "Permissions"
-          },
-          {
-            "name": "callback",
-            "type": "function",
-            "parameters": [
-            ]
-          }
-        ]
-      }
-    ],
-    "events": [
-      {
-        "name": "onAdded",
-        "type": "function",
-        "unsupported": true,
-        "description": "Fired when the extension acquires new permissions.",
-        "parameters": [
-          {
-            "name": "permissions",
-            "$ref": "Permissions"
-          }
-        ]
-      },
-      {
-        "name": "onRemoved",
-        "type": "function",
-        "unsupported": true,
-        "description": "Fired when permissions are removed from the extension.",
-        "parameters": [
-          {
-            "name": "permissions",
-            "$ref": "Permissions"
-          }
-        ]
-      }
-    ]
-  }
-]
--- a/toolkit/components/extensions/schemas/top_sites.json
+++ b/toolkit/components/extensions/schemas/top_sites.json
@@ -2,17 +2,17 @@
  * 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/. */
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "topSites"
           ]
         }]
       }
     ]
--- a/toolkit/components/extensions/schemas/web_navigation.json
+++ b/toolkit/components/extensions/schemas/web_navigation.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "webNavigation"
           ]
         }]
       }
     ]
--- a/toolkit/components/extensions/schemas/web_request.json
+++ b/toolkit/components/extensions/schemas/web_request.json
@@ -2,17 +2,17 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
 [
   {
     "namespace": "manifest",
     "types": [
       {
-        "$extend": "OptionalPermission",
+        "$extend": "Permission",
         "choices": [{
           "type": "string",
           "enum": [
             "webRequest",
             "webRequestBlocking"
           ]
         }]
       }
--- a/toolkit/components/extensions/test/mochitest/chrome.ini
+++ b/toolkit/components/extensions/test/mochitest/chrome.ini
@@ -12,33 +12,32 @@ support-files =
   oauth.html
   redirect_auto.sjs
 tags = webextensions in-process-webextensions
 
 [test_chrome_ext_background_debug_global.html]
 skip-if = (os == 'android') # android doesn't have devtools
 [test_chrome_ext_background_page.html]
 skip-if = (toolkit == 'android') # android doesn't have devtools
+[test_chrome_ext_eventpage_warning.html]
 [test_chrome_ext_contentscript_data_uri.html]
 [test_chrome_ext_contentscript_unrecognizedprop_warning.html]
-[test_chrome_ext_downloads_saveAs.html]
-[test_chrome_ext_eventpage_warning.html]
 [test_chrome_ext_hybrid_addons.html]
-[test_chrome_ext_idle.html]
-[test_chrome_ext_identity.html]
-skip-if = os == 'android' # unsupported.
-[test_chrome_ext_permissions.html]
-[test_chrome_ext_storage_cleanup.html]
-[test_chrome_ext_trackingprotection.html]
 [test_chrome_ext_trustworthy_origin.html]
 [test_chrome_ext_webnavigation_resolved_urls.html]
-[test_chrome_ext_webrequest_background_events.html]
-[test_chrome_ext_webrequest_host_permissions.html]
 [test_chrome_native_messaging_paths.html]
 skip-if = os != "mac" && os != "linux"
 [test_ext_cookies_expiry.html]
 [test_ext_cookies_permissions_bad.html]
 [test_ext_cookies_permissions_good.html]
 [test_ext_cookies_containers.html]
 [test_ext_jsversion.html]
 [test_ext_schema.html]
 [test_ext_protocolHandlers.html]
 skip-if = (toolkit == 'android') # bug 1342577
+[test_chrome_ext_storage_cleanup.html]
+[test_chrome_ext_idle.html]
+[test_chrome_ext_identity.html]
+skip-if = os == 'android' # unsupported.
+[test_chrome_ext_downloads_saveAs.html]
+[test_chrome_ext_webrequest_background_events.html]
+[test_chrome_ext_webrequest_host_permissions.html]
+[test_chrome_ext_trackingprotection.html]
deleted file mode 100644
--- a/toolkit/components/extensions/test/mochitest/test_chrome_ext_permissions.html
+++ /dev/null
@@ -1,170 +0,0 @@
-<!DOCTYPE HTML>
-<html>
-<head>
-  <title>Test for permissions</title>
-  <script src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
-  <script src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
-  <link rel="stylesheet" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
-</head>
-<body>
-
-<script type="text/javascript">
-"use strict";
-
-function makeTest(manifestPermissions, optionalPermissions, checkFetch = true) {
-  return async function() {
-    function pageScript() {
-      /* global PERMISSIONS */
-      /* eslint-disable mozilla/balanced-listeners */
-      window.addEventListener("keypress", () => {
-        browser.permissions.request(PERMISSIONS).then(result => {
-          browser.test.sendMessage("request.result", result);
-        }, {once: true});
-      });
-      /* eslint-enable mozilla/balanced-listeners */
-
-      browser.test.onMessage.addListener(async msg => {
-        if (msg == "set-cookie") {
-          try {
-            await browser.cookies.set({
-              url: "http://example.com/",
-              name: "COOKIE",
-              value: "NOM NOM",
-            });
-            browser.test.sendMessage("set-cookie.result", {success: true});
-          } catch (err) {
-            dump(`set cookie failed with ${err.message}\n`);
-            browser.test.sendMessage("set-cookie.result",
-                                     {success: false, message: err.message});
-          }
-        } else if (msg == "remove") {
-          browser.permissions.remove(PERMISSIONS).then(result => {
-            browser.test.sendMessage("remove.result", result);
-          });
-        }
-      });
-
-      browser.test.sendMessage("page-ready");
-    }
-
-    let extension = ExtensionTestUtils.loadExtension({
-      background() {
-        browser.test.sendMessage("ready", browser.runtime.getURL("page.html"));
-      },
-
-      manifest: {
-        permissions: manifestPermissions,
-        optional_permissions: [...(optionalPermissions.permissions || []),
-                               ...(optionalPermissions.origins || [])],
-
-        content_scripts: [{
-          matches: ["http://mochi.test/*/file_sample.html"],
-          js: ["content_script.js"],
-        }],
-      },
-
-      files: {
-        "content_script.js": async () => {
-          let url = new URL(window.location.pathname, "http://example.com/");
-          fetch(url, {}).then(response => {
-            browser.test.sendMessage("fetch.result", response.ok);
-          }).catch(err => {
-            browser.test.sendMessage("fetch.result", false);
-          });
-        },
-
-        "page.html": `<html><head>
-          <script src="page.js"><\/script>
-        </head></html>`,
-
-        "page.js": `const PERMISSIONS = ${JSON.stringify(optionalPermissions)}; (${pageScript})();`,
-      },
-    });
-
-    await extension.startup();
-
-    function call(method) {
-      extension.sendMessage(method);
-      return extension.awaitMessage(`${method}.result`);
-    }
-
-    let base = window.location.href.replace(/^chrome:\/\/mochitests\/content/,
-                                            "http://mochi.test:8888");
-    let file = new URL("file_sample.html", base);
-
-    async function testContentScript() {
-      let win = window.open(file);
-      let result = await extension.awaitMessage("fetch.result");
-      win.close();
-      return result;
-    }
-
-    let url = await extension.awaitMessage("ready");
-    let win = window.open(url);
-    await extension.awaitMessage("page-ready");
-
-    // Using the cookies API from an extension page should fail
-    let result = await call("set-cookie");
-    is(result.success, false, "setting cookie failed");
-    if (manifestPermissions.includes("cookies")) {
-      ok(/^Permission denied/.test(result.message),
-         "setting cookie failed with an appropriate error due to missing host permission");
-    } else {
-      ok(/browser\.cookies is undefined/.test(result.message),
-         "setting cookie failed since cookies API is not present");
-    }
-
-    // Making a cross-origin request from a content script should fail
-    if (checkFetch) {
-      result = await testContentScript();
-      is(result, false, "fetch() failed from content script due to lack of host permission");
-    }
-
-    // Request some permissions
-    let winutils = SpecialPowers.getDOMWindowUtils(win);
-    winutils.sendKeyEvent("keypress", KeyEvent.DOM_VK_A, 0, 0);
-    result = await extension.awaitMessage("request.result");
-    is(result, true, "permissions.request() succeeded");
-
-    // Using the cookies API from an extension page should succeed
-    result = await call("set-cookie");
-    is(result.success, true, "setting cookie succeeded");
-
-    // Making a cross-origin request from a content script should succeed
-    if (checkFetch) {
-      result = await testContentScript();
-      is(result, true, "fetch() succeeded from content script due to lack of host permission");
-    }
-
-    // Now revoke our permissions
-    result = await call("remove");
-
-    // The cookies API should once again fail
-    result = await call("set-cookie");
-    is(result.success, false, "setting cookie failed");
-
-    // As should the cross-origin request from a content script
-    if (checkFetch) {
-      result = await testContentScript();
-      is(result, false, "fetch() failed from content script due to lack of host permission");
-    }
-
-    await extension.unload();
-  };
-}
-
-const ORIGIN = "*://example.com/";
-add_task(makeTest([], {
-  permissions: ["cookies"],
-  origins: [ORIGIN],
-}));
-
-add_task(makeTest(["cookies"], {origins: [ORIGIN]}));
-add_task(makeTest([ORIGIN], {permissions: ["cookies"]}, false));
-
-</script>
-
-</body>
-</html>
-
--- a/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
+++ b/toolkit/components/extensions/test/mochitest/test_ext_all_apis.js
@@ -68,20 +68,16 @@ let expectedBackgroundApis = [
   "extensionTypes.CSSOrigin",
   "extensionTypes.ImageFormat",
   "extensionTypes.RunAt",
   "management.ExtensionDisabledReason",
   "management.ExtensionInstallType",
   "management.ExtensionType",
   "management.getSelf",
   "management.uninstallSelf",
-  "permissions.getAll",
-  "permissions.contains",
-  "permissions.request",
-  "permissions.remove",
   "runtime.getBackgroundPage",
   "runtime.getBrowserInfo",
   "runtime.getPlatformInfo",
   "runtime.onConnectExternal",
   "runtime.onInstalled",
   "runtime.onMessageExternal",
   "runtime.onStartup",
   "runtime.onUpdateAvailable",
deleted file mode 100644
--- a/toolkit/components/extensions/test/xpcshell/test_ext_permissions.js
+++ /dev/null
@@ -1,262 +0,0 @@
-"use strict";
-
-XPCOMUtils.defineLazyGetter(this, "ExtensionManager", () => {
-  const {ExtensionManager}
-    = Cu.import("resource://gre/modules/ExtensionContent.jsm", {});
-  return ExtensionManager;
-});
-Cu.import("resource://gre/modules/ExtensionPermissions.jsm");
-
-AddonTestUtils.init(this);
-AddonTestUtils.overrideCertDB();
-AddonTestUtils.createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "42");
-
-// Find the DOMWindowUtils for the background page for the given
-// extension (wrapper)
-function findWinUtils(extension) {
-  let extensionChild = ExtensionManager.extensions.get(extension.extension.id);
-  let bgwin = null;
-  for (let view of extensionChild.views) {
-    if (view.viewType == "background") {
-      bgwin = view.contentWindow;
-    }
-  }
-  notEqual(bgwin, null, "Found background window for the test extension");
-  return bgwin.QueryInterface(Ci.nsIInterfaceRequestor)
-              .getInterface(Ci.nsIDOMWindowUtils);
-}
-
-add_task(async function test_permissions() {
-  const REQUIRED_PERMISSIONS = ["downloads"];
-  const REQUIRED_ORIGINS = ["*://site.com/", "*://*.domain.com/"];
-  const OPTIONAL_PERMISSIONS = ["bookmarks", "history"];
-  const OPTIONAL_ORIGINS = ["http://optionalsite.com/", "https://*.optionaldomain.com/"];
-
-  let acceptPrompt = false;
-  const observer = {
-    observe(subject, topic, data) {
-      if (topic == "webextension-optional-permission-prompt") {
-        let {resolve} = subject.wrappedJSObject;
-        resolve(acceptPrompt);
-      }
-    },
-  };
-
-  Services.prefs.setBoolPref("extensions.webextOptionalPermissionPrompts", true);
-  Services.obs.addObserver(observer, "webextension-optional-permission-prompt", false);
-  do_register_cleanup(() => {
-    Services.obs.removeObserver(observer, "webextension-optional-permission-prompt");
-    Services.prefs.clearUserPref("extensions.webextOptionalPermissionPrompts");
-  });
-
-  await AddonTestUtils.promiseStartupManager();
-
-  function background() {
-    browser.test.onMessage.addListener(async (method, arg) => {
-      if (method == "getAll") {
-        let perms = await browser.permissions.getAll();
-        browser.test.sendMessage("getAll.result", perms);
-      } else if (method == "contains") {
-        let result = await browser.permissions.contains(arg);
-        browser.test.sendMessage("contains.result", result);
-      } else if (method == "request") {
-        try {
-          let result = await browser.permissions.request(arg);
-          browser.test.sendMessage("request.result", {status: "success", result});
-        } catch (err) {
-          browser.test.sendMessage("request.result", {status: "error", message: err.message});
-        }
-      } else if (method == "remove") {
-        let result = await browser.permissions.remove(arg);
-        browser.test.sendMessage("remove.result", result);
-      }
-    });
-  }
-
-  let extension = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {
-      permissions: [...REQUIRED_PERMISSIONS, ...REQUIRED_ORIGINS],
-      optional_permissions: [...OPTIONAL_PERMISSIONS, ...OPTIONAL_ORIGINS],
-    },
-    useAddonManager: "permanent",
-  });
-
-  await extension.startup();
-  let winUtils = findWinUtils(extension);
-
-  function call(method, arg) {
-    extension.sendMessage(method, arg);
-    return extension.awaitMessage(`${method}.result`);
-  }
-
-  let result = await call("getAll");
-  deepEqual(result.permissions, REQUIRED_PERMISSIONS);
-  deepEqual(result.origins, REQUIRED_ORIGINS);
-
-  for (let perm of REQUIRED_PERMISSIONS) {
-    result = await call("contains", {permissions: [perm]});
-    equal(result, true, `contains() returns true for fixed permission ${perm}`);
-  }
-  for (let origin of REQUIRED_ORIGINS) {
-    result = await call("contains", {origins: [origin]});
-    equal(result, true, `contains() returns true for fixed origin ${origin}`);
-  }
-
-  // None of the optional permissions should be available yet
-  for (let perm of OPTIONAL_PERMISSIONS) {
-    result = await call("contains", {permissions: [perm]});
-    equal(result, false, `contains() returns false for permission ${perm}`);
-  }
-  for (let origin of OPTIONAL_ORIGINS) {
-    result = await call("contains", {origins: [origin]});
-    equal(result, false, `conains() returns false for origin ${origin}`);
-  }
-
-  result = await call("contains", {
-    permissions: [...REQUIRED_PERMISSIONS, ...OPTIONAL_PERMISSIONS],
-  });
-  equal(result, false, "contains() returns false for a mix of available and unavailable permissions");
-
-  let perm = OPTIONAL_PERMISSIONS[0];
-  result = await call("request", {permissions: [perm]});
-  equal(result.status, "error", "request() fails if not called from an event handler");
-  ok(/May only request permissions from a user input handler/.test(result.message),
-     "error message for calling request() outside an event handler is reasonable");
-  result = await call("contains", {permissions: [perm]});
-  equal(result, false, "Permission requested outside an event handler was not granted");
-
-  let userInputHandle = winUtils.setHandlingUserInput(true);
-
-  result = await call("request", {permissions: ["notifications"]});
-  equal(result.status, "error", "request() for permission not in optional_permissions should fail");
-  ok(/since it was not declared in optional_permissions/.test(result.message),
-     "error message for undeclared optional_permission is reasonable");
-
-  // Check request() when the prompt is canceled.
-  acceptPrompt = false;
-  result = await call("request", {permissions: [perm]});
-  equal(result.status, "success", "request() returned cleanly");
-  equal(result.result, false, "request() returned false for rejected permission");
-
-  result = await call("contains", {permissions: [perm]});
-  equal(result, false, "Rejected permission was not granted");
-
-  // Call request() and accept the prompt
-  acceptPrompt = true;
-  let allOptional = {
-    permissions: OPTIONAL_PERMISSIONS,
-    origins: OPTIONAL_ORIGINS,
-  };
-  result = await call("request", allOptional);
-  equal(result.status, "success", "request() returned cleanly");
-  equal(result.result, true, "request() returned true for accepted permissions");
-  userInputHandle.destruct();
-
-  let allPermissions = {
-    permissions: [...REQUIRED_PERMISSIONS, ...OPTIONAL_PERMISSIONS],
-    origins: [...REQUIRED_ORIGINS, ...OPTIONAL_ORIGINS],
-  };
-
-  result = await call("getAll");
-  deepEqual(result, allPermissions, "getAll() returns required and runtime requested permissions");
-
-  result = await call("contains", allPermissions);
-  equal(result, true, "contains() returns true for runtime requested permissions");
-
-  // Restart, verify permissions are still present
-  await AddonTestUtils.promiseRestartManager();
-  await extension.awaitStartup();
-
-  result = await call("getAll");
-  deepEqual(result, allPermissions, "Runtime requested permissions are still present after restart");
-
-  // Check remove()
-  result = await call("remove", {permissions: OPTIONAL_PERMISSIONS});
-  equal(result, true, "remove() succeeded");
-
-  let perms = {
-    permissions: REQUIRED_PERMISSIONS,
-    origins: [...REQUIRED_ORIGINS, ...OPTIONAL_ORIGINS],
-  };
-  result = await call("getAll");
-  deepEqual(result, perms, "Expected permissions remain after removing some");
-
-  result = await call("remove", {origins: OPTIONAL_ORIGINS});
-  equal(result, true, "remove() succeeded");
-
-  perms.origins = REQUIRED_ORIGINS;
-  result = await call("getAll");
-  deepEqual(result, perms, "Back to default permissions after removing more");
-
-  await extension.unload();
-});
-
-add_task(async function test_startup() {
-  async function background() {
-    browser.test.onMessage.addListener(async (perms) => {
-      await browser.permissions.request(perms);
-      browser.test.sendMessage("requested");
-    });
-
-    let all = await browser.permissions.getAll();
-    browser.test.sendMessage("perms", all);
-  }
-
-  const PERMS1 = {
-    permissions: ["clipboardRead", "tabs"],
-  };
-  const PERMS2 = {
-    origins: ["https://site2.com/"],
-  };
-
-  let extension1 = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {optional_permissions: PERMS1.permissions},
-    useAddonManager: "permanent",
-  });
-  let extension2 = ExtensionTestUtils.loadExtension({
-    background,
-    manifest: {optional_permissions: PERMS2.origins},
-    useAddonManager: "permanent",
-  });
-
-  await extension1.startup();
-  await extension2.startup();
-
-  let perms = await extension1.awaitMessage("perms");
-  dump(`perms1 ${JSON.stringify(perms)}\n`);
-  perms = await extension2.awaitMessage("perms");
-  dump(`perms2 ${JSON.stringify(perms)}\n`);
-
-  let winUtils = findWinUtils(extension1);
-  let handle = winUtils.setHandlingUserInput(true);
-  extension1.sendMessage(PERMS1);
-  await extension1.awaitMessage("requested");
-  handle.destruct();
-
-  winUtils = findWinUtils(extension2);
-  handle = winUtils.setHandlingUserInput(true);
-  extension2.sendMessage(PERMS2);
-  await extension2.awaitMessage("requested");
-  handle.destruct();
-
-  // Restart everything, and force the permissions store to be
-  // re-read on startup
-  ExtensionPermissions._uninit();
-  await AddonTestUtils.promiseRestartManager();
-  await extension1.awaitStartup();
-  await extension2.awaitStartup();
-
-  async function checkPermissions(extension, permissions) {
-    perms = await extension.awaitMessage("perms");
-    let expect = Object.assign({permissions: [], origins: []}, permissions);
-    deepEqual(perms, expect, "Extension got correct permissions on startup");
-  }
-
-  await checkPermissions(extension1, PERMS1);
-  await checkPermissions(extension2, PERMS2);
-
-  await extension1.unload();
-  await extension2.unload();
-});
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini
+++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini
@@ -47,17 +47,16 @@ skip-if = release_or_beta
 [test_ext_management.js]
 [test_ext_management_uninstall_self.js]
 [test_ext_manifest_content_security_policy.js]
 [test_ext_manifest_incognito.js]
 [test_ext_manifest_minimum_chrome_version.js]
 [test_ext_manifest_themes.js]
 [test_ext_onmessage_removelistener.js]
 skip-if = true # This test no longer tests what it is meant to test.
-[test_ext_permissions.js]
 [test_ext_privacy.js]
 [test_ext_privacy_disable.js]
 [test_ext_privacy_update.js]
 [test_ext_runtime_connect_no_receiver.js]
 [test_ext_runtime_getBrowserInfo.js]
 [test_ext_runtime_getPlatformInfo.js]
 [test_ext_runtime_onInstalled_and_onStartup.js]
 [test_ext_runtime_sendMessage.js]
--- a/toolkit/modules/addons/MatchPattern.jsm
+++ b/toolkit/modules/addons/MatchPattern.jsm
@@ -16,22 +16,16 @@ XPCOMUtils.defineLazyModuleGetter(this, 
 
 this.EXPORTED_SYMBOLS = ["MatchPattern", "MatchGlobs", "MatchURLFilters"];
 
 /* globals MatchPattern, MatchGlobs */
 
 const PERMITTED_SCHEMES = ["http", "https", "file", "ftp", "data"];
 const PERMITTED_SCHEMES_REGEXP = PERMITTED_SCHEMES.join("|");
 
-// The basic RE for matching patterns
-const PATTERN_REGEXP = new RegExp(`^(${PERMITTED_SCHEMES_REGEXP}|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$`);
-
-// The schemes/protocols implied by a pattern that starts with *://
-const WILDCARD_SCHEMES = ["http", "https"];
-
 // This function converts a glob pattern (containing * and possibly ?
 // as wildcards) to a regular expression.
 function globToRegexp(pat, allowQuestion) {
   // Escape everything except ? and *.
   pat = pat.replace(/[.+^${}()|[\]\\]/g, "\\$&");
 
   if (allowQuestion) {
     pat = pat.replace(/\?/g, ".");
@@ -48,25 +42,26 @@ function SingleMatchPattern(pat) {
   this.pat = pat;
   if (pat == "<all_urls>") {
     this.schemes = PERMITTED_SCHEMES;
     this.hostMatch = () => true;
     this.pathMatch = () => true;
   } else if (!pat) {
     this.schemes = [];
   } else {
-    let match = PATTERN_REGEXP.exec(pat);
+    let re = new RegExp(`^(${PERMITTED_SCHEMES_REGEXP}|\\*)://(\\*|\\*\\.[^*/]+|[^*/]+|)(/.*)$`);
+    let match = re.exec(pat);
     if (!match) {
       Cu.reportError(`Invalid match pattern: '${pat}'`);
       this.schemes = [];
       return;
     }
 
     if (match[1] == "*") {
-      this.schemes = WILDCARD_SCHEMES;
+      this.schemes = ["http", "https"];
     } else {
       this.schemes = [match[1]];
     }
 
     // We allow the host to be empty for file URLs.
     if (match[2] == "" && this.schemes[0] != "file") {
       Cu.reportError(`Invalid match pattern: '${pat}'`);
       this.schemes = [];
@@ -174,51 +169,19 @@ MatchPattern.prototype = {
           return true;
         }
       }
     }
 
     return false;
   },
 
-  // Test if this MatchPattern subsumes the given pattern (i.e., whether
-  // this pattern matches everything the given pattern does).
-  // Note, this method considers only to protocols and hosts/domains,
-  // paths are ignored.
-  subsumes(pattern) {
-    let match = PATTERN_REGEXP.exec(pattern);
-    if (!match) {
-      throw new Error("Invalid match pattern");
-    }
-
-    if (match[1] == "*") {
-      return WILDCARD_SCHEMES.every(scheme => this.matchesIgnoringPath({scheme, host: match[2]}));
-    }
-
-    return this.matchesIgnoringPath({scheme: match[1], host: match[2]});
-  },
-
   serialize() {
     return this.pat;
   },
-
-  removeOne(pattern) {
-    if (!Array.isArray(this.pat)) {
-      return;
-    }
-
-    let index = this.pat.indexOf(pattern);
-    if (index >= 0) {
-      if (this.matchers[index].pat != pattern) {
-        throw new Error("pat/matcher mismatch in removeOne()");
-      }
-      this.pat.splice(index, 1);
-      this.matchers.splice(index, 1);
-    }
-  },
 };
 
 // Globs can match everything. Be careful, this DOES NOT filter by allowed schemes!
 this.MatchGlobs = function(globs) {
   this.original = globs;
   if (globs) {
     this.regexps = Array.from(globs, (glob) => globToRegexp(glob, true));
   } else {
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -1343,17 +1343,17 @@ var AddonManagerInternal = {
       return Promise.resolve();
     }
 
     let newPerms = info.addon.userPermissions;
 
     let difference = Extension.comparePermissions(oldPerms, newPerms);
 
     // If there are no new permissions, just go ahead with the update
-    if (difference.origins.length == 0 && difference.permissions.length == 0) {
+    if (difference.hosts.length == 0 && difference.permissions.length == 0) {
       return Promise.resolve();
     }
 
     return new Promise((resolve, reject) => {
       let subject = {wrappedJSObject: {
         addon: info.addon,
         permissions: difference,
         resolve, reject
--- a/toolkit/mozapps/extensions/content/extensions.js
+++ b/toolkit/mozapps/extensions/content/extensions.js
@@ -720,17 +720,17 @@ function attachUpdateHandler(install) {
       return Promise.resolve();
     }
 
     let newPerms = info.addon.userPermissions;
 
     let difference = Extension.comparePermissions(oldPerms, newPerms);
 
     // If there are no new permissions, just proceed
-    if (difference.origins.length == 0 && difference.permissions.length == 0) {
+    if (difference.hosts.length == 0 && difference.permissions.length == 0) {
       return Promise.resolve();
     }
 
     return new Promise((resolve, reject) => {
       let subject = {
         wrappedJSObject: {
           target: getBrowserElement(),
           info: {
@@ -1286,17 +1286,17 @@ var gViewController = {
           return false;
         let addonType = AddonManager.addonTypes[aAddon.type];
         return (!(addonType.flags & AddonManager.TYPE_SUPPORTS_ASK_TO_ACTIVATE) &&
                 hasPermission(aAddon, "enable"));
       },
       doCommand(aAddon) {
         if (aAddon.isWebExtension && !aAddon.seen && WEBEXT_PERMISSION_PROMPTS) {
           let perms = aAddon.userPermissions;
-          if (perms.origins.length > 0 || perms.permissions.length > 0) {
+          if (perms.hosts.length > 0 || perms.permissions.length > 0) {
             let subject = {
               wrappedJSObject: {
                 target: getBrowserElement(),
                 info: {
                   type: "sideload",
                   addon: aAddon,
                   icon: aAddon.iconURL,
                   permissions: perms,
--- a/toolkit/mozapps/extensions/internal/XPIProvider.jsm
+++ b/toolkit/mozapps/extensions/internal/XPIProvider.jsm
@@ -1017,17 +1017,17 @@ var loadManifestFromWebManifest = Task.a
     else
       addon.optionsBrowserStyle = manifest.options_ui.browser_style;
   }
 
   // WebExtensions don't use iconURLs
   addon.iconURL = null;
   addon.icon64URL = null;
   addon.icons = manifest.icons || {};
-  addon.userPermissions = extension.userPermissions;
+  addon.userPermissions = extension.userPermissions();
 
   addon.applyBackgroundUpdates = AddonManager.AUTOUPDATE_DEFAULT;
 
   function getLocale(aLocale) {
     // Use the raw manifest, here, since we need values with their
     // localization placeholders still in place.
     let rawManifest = extension.rawManifest;
 
--- a/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
+++ b/toolkit/mozapps/extensions/test/xpcshell/test_webextension_install.js
@@ -515,17 +515,17 @@ add_task(function* test_permissions_prom
 
   yield promiseCompleteInstall(install);
 
   notEqual(perminfo, undefined, "Permission handler was invoked");
   equal(perminfo.existingAddon, null, "Permission info does not include an existing addon");
   notEqual(perminfo.addon, null, "Permission info includes the new addon");
   let perms = perminfo.addon.userPermissions;
   deepEqual(perms.permissions, ["tabs", "storage"], "API permissions are correct");
-  deepEqual(perms.origins, ["https://*.example.com/*", "<all_urls>"], "Host permissions are correct");
+  deepEqual(perms.hosts, ["https://*.example.com/*", "<all_urls>"], "Host permissions are correct");
   deepEqual(perms.apis, ["test"], "Experiments permissions are correct");
 
   let addon = yield promiseAddonByID(perminfo.addon.id);
   notEqual(addon, null, "Extension was installed");
 
   addon.uninstall();
   yield OS.File.remove(xpi.path);
 });