Bug 1539598 Require pre-install confirmation on all addons installs from outside about:addons r=kmag
authorAndrew Swan <aswan@mozilla.com>
Wed, 03 Apr 2019 15:30:38 -0700
changeset 469418 b7e2018b550c9e0cb80644b23791e673d152ac3f
parent 469331 d9ee5458ce46d43e760fe81dffe866673080b78e
child 469419 0cf7ad6c1a23ea598cf72c00d06daeb2607ae039
push id35866
push userapavel@mozilla.com
push dateSat, 13 Apr 2019 21:46:23 +0000
treeherdermozilla-central@0cf7ad6c1a23 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskmag
bugs1539598
milestone68.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 1539598 Require pre-install confirmation on all addons installs from outside about:addons r=kmag Differential Revision: https://phabricator.services.mozilla.com/D26024
mobile/android/tests/browser/robocop/testThemeInstall.js
toolkit/mozapps/extensions/AddonManager.jsm
toolkit/mozapps/extensions/test/browser/browser_webapi_theme.js
--- a/mobile/android/tests/browser/robocop/testThemeInstall.js
+++ b/mobile/android/tests/browser/robocop/testThemeInstall.js
@@ -1,16 +1,17 @@
 // -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 "use strict";
 
 const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
 const {EventDispatcher} = ChromeUtils.import("resource://gre/modules/Messaging.jsm");
+const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
 
 // The chrome window and friends.
 let chromeWin = Services.wm.getMostRecentWindow("navigator:browser");
 let BrowserApp = chromeWin.BrowserApp;
 
 const BASE = "https://example.com:443/tests/robocop/";
 const XPI = BASE + "browser_theme_image_file.xpi";
 const PAGE = BASE + "robocop_blank_04.html";
@@ -20,17 +21,35 @@ let tabTest;
 
 function cleanupTabs() {
   if (tabTest) {
     BrowserApp.closeTab(tabTest);
     tabTest = null;
   }
 }
 
+// A user prompt is displayed during theme installation and responding to
+// the prompt from test code is tricky.  Instead, just override the prompt
+// implementation and let installation proceed.
+const PROMPT_CLASSID = Components.ID("{85325f87-03f8-d142-b3a0-d2a0b8f2d4e0}");
+const PROMPT_CONTRACTID = "@mozilla.org/addons/web-install-prompt;1";
+function NullPrompt() {}
+NullPrompt.prototype = {
+  QueryInterface: ChromeUtils.generateQI([Ci.amIWebInstallPrompt]),
+  confirm(browser, url, installs) {
+    installs[0].install();
+  },
+};
+
 add_task(async function testThemeInstall() {
+  let factory = XPCOMUtils.generateSingletonFactory(NullPrompt);
+  let registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
+  let originalCID = registrar.contractIDToCID(PROMPT_CONTRACTID);
+  registrar.registerFactory(PROMPT_CLASSID, "", PROMPT_CONTRACTID, factory);
+
   await SpecialPowers.pushPrefEnv({
     set: [["extensions.webapi.testing", true],
           ["extensions.install.requireBuiltInCerts", false]],
   });
 
   tabTest = BrowserApp.addTab(PAGE);
   let browser = tabTest.browser;
   let content = browser.contentWindow;
@@ -56,11 +75,14 @@ add_task(async function testThemeInstall
      "headerURL references resource inside extension file");
 
   await EventDispatcher.instance.sendRequestForResult({
     type: "Robocop:WaitOnUI",
   });
 
   Services.obs.removeObserver(observer, "lightweight-theme-styling-update");
   cleanupTabs();
+
+  registrar.unregisterFactory(PROMPT_CLASSID, factory);
+  registrar.registerFactory(originalCID, "", PROMPT_CONTRACTID, null);
 });
 
 run_next_test();
--- a/toolkit/mozapps/extensions/AddonManager.jsm
+++ b/toolkit/mozapps/extensions/AddonManager.jsm
@@ -2535,17 +2535,17 @@ var AddonManagerInternal = {
 
       // All installs end up in this callback when the add-on is available
       // for installation.  There are numerous different things that can
       // happen from here though.  For webextensions, if the application
       // implements webextension permission prompts, those always take
       // precedence.
       // If this add-on is not a webextension or if the application does not
       // implement permission prompts, no confirmation is displayed for
-      // installs created with mozAddonManager (in which case requireConfirm
+      // installs created from about:addons (in which case requireConfirm
       // is false).
       // In the remaining cases, a confirmation prompt is displayed but the
       // application may override it either by implementing the
       // "@mozilla.org/addons/web-install-prompt;1" contract or by setting
       // the customConfirmationUI preference and responding to the
       // "addon-install-confirmation" notification.  If the application
       // does not implement its own prompt, use the built-in xul dialog.
       if (info.addon.userPermissions && WEBEXT_PERMISSION_PROMPTS) {
@@ -2700,17 +2700,22 @@ var AddonManagerInternal = {
         browser: target,
         triggeringPrincipal: options.triggeringPrincipal,
         hash: options.hash,
         telemetryInfo: {
           source: AddonManager.getInstallSourceFromHost(options.sourceHost),
           method: "amWebAPI",
         },
       }).then(install => {
-        AddonManagerInternal.setupPromptHandler(target, null, install, false, "AMO");
+        let requireConfirm = true;
+        if (target.contentDocument &&
+            target.contentDocument.nodePrincipal.isSystemPrincipal) {
+          requireConfirm = false;
+        }
+        AddonManagerInternal.setupPromptHandler(target, null, install, requireConfirm, "AMO");
 
         let id = this.nextInstall++;
         let {listener, installPromise} = makeListener(id, target.messageManager);
         install.addListener(listener);
 
         this.installs.set(id, {install, target, listener, installPromise});
 
         let result = {id};
--- a/toolkit/mozapps/extensions/test/browser/browser_webapi_theme.js
+++ b/toolkit/mozapps/extensions/test/browser/browser_webapi_theme.js
@@ -16,24 +16,31 @@ add_task(async function test_theme_insta
     function observer(subject, topic, data) {
       updates.push(JSON.stringify(subject.wrappedJSObject));
     }
     Services.obs.addObserver(observer, "lightweight-theme-styling-update");
     registerCleanupFunction(() => {
       Services.obs.removeObserver(observer, "lightweight-theme-styling-update");
     });
 
+    let sawConfirm = false;
+    promisePopupNotificationShown("addon-install-confirmation").then(panel => {
+      sawConfirm = true;
+      panel.button.click();
+    });
 
     let prompt1 = waitAppMenuNotificationShown("addon-installed", "theme@tests.mozilla.org", false);
     let installPromise = ContentTask.spawn(browser, URL, async (url) => {
       let install = await content.navigator.mozAddonManager.createInstall({url});
       return install.install();
     });
     await prompt1;
 
+    ok(sawConfirm, "Confirm notification was displayed before installation");
+
     // Open a new window and test the app menu panel from there.  This verifies the
     // incognito checkbox as well as finishing install in this case.
     let newWin = await BrowserTestUtils.openNewBrowserWindow();
     await waitAppMenuNotificationShown("addon-installed", "theme@tests.mozilla.org", true, newWin);
     await installPromise;
     ok(true, "Theme install completed");
 
     await BrowserTestUtils.closeWindow(newWin);