toolkit/mozapps/extensions/amWebInstallListener.js

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* This Source Code Form is subject to the terms of the Mozilla Public
     2  * License, v. 2.0. If a copy of the MPL was not distributed with this
     3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 /**
     6  * This is a default implementation of amIWebInstallListener that should work
     7  * for most applications but can be overriden. It notifies the observer service
     8  * about blocked installs. For normal installs it pops up an install
     9  * confirmation when all the add-ons have been downloaded.
    10  */
    12 "use strict";
    14 const Cc = Components.classes;
    15 const Ci = Components.interfaces;
    16 const Cr = Components.results;
    17 const Cu = Components.utils;
    19 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    20 Cu.import("resource://gre/modules/AddonManager.jsm");
    21 Cu.import("resource://gre/modules/Services.jsm");
    23 const URI_XPINSTALL_DIALOG = "chrome://mozapps/content/xpinstall/xpinstallConfirm.xul";
    25 // Installation can begin from any of these states
    26 const READY_STATES = [
    27   AddonManager.STATE_AVAILABLE,
    28   AddonManager.STATE_DOWNLOAD_FAILED,
    29   AddonManager.STATE_INSTALL_FAILED,
    30   AddonManager.STATE_CANCELLED
    31 ];
    33 Cu.import("resource://gre/modules/Log.jsm");
    34 const LOGGER_ID = "addons.weblistener";
    36 // Create a new logger for use by the Addons Web Listener
    37 // (Requires AddonManager.jsm)
    38 let logger = Log.repository.getLogger(LOGGER_ID);
    40 function notifyObservers(aTopic, aWindow, aUri, aInstalls) {
    41   let info = {
    42     originatingWindow: aWindow,
    43     originatingURI: aUri,
    44     installs: aInstalls,
    46     QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
    47   };
    48   Services.obs.notifyObservers(info, aTopic, null);
    49 }
    51 /**
    52  * Creates a new installer to monitor downloads and prompt to install when
    53  * ready
    54  *
    55  * @param  aWindow
    56  *         The window that started the installations
    57  * @param  aUrl
    58  *         The URL that started the installations
    59  * @param  aInstalls
    60  *         An array of AddonInstalls
    61  */
    62 function Installer(aWindow, aUrl, aInstalls) {
    63   this.window = aWindow;
    64   this.url = aUrl;
    65   this.downloads = aInstalls;
    66   this.installed = [];
    68   notifyObservers("addon-install-started", aWindow, aUrl, aInstalls);
    70   aInstalls.forEach(function(aInstall) {
    71     aInstall.addListener(this);
    73     // Start downloading if it hasn't already begun
    74     if (READY_STATES.indexOf(aInstall.state) != -1)
    75       aInstall.install();
    76   }, this);
    78   this.checkAllDownloaded();
    79 }
    81 Installer.prototype = {
    82   window: null,
    83   downloads: null,
    84   installed: null,
    85   isDownloading: true,
    87   /**
    88    * Checks if all downloads are now complete and if so prompts to install.
    89    */
    90   checkAllDownloaded: function Installer_checkAllDownloaded() {
    91     // Prevent re-entrancy caused by the confirmation dialog cancelling unwanted
    92     // installs.
    93     if (!this.isDownloading)
    94       return;
    96     var failed = [];
    97     var installs = [];
    99     for (let install of this.downloads) {
   100       switch (install.state) {
   101       case AddonManager.STATE_AVAILABLE:
   102       case AddonManager.STATE_DOWNLOADING:
   103         // Exit early if any add-ons haven't started downloading yet or are
   104         // still downloading
   105         return;
   106       case AddonManager.STATE_DOWNLOAD_FAILED:
   107         failed.push(install);
   108         break;
   109       case AddonManager.STATE_DOWNLOADED:
   110         // App disabled items are not compatible and so fail to install
   111         if (install.addon.appDisabled)
   112           failed.push(install);
   113         else
   114           installs.push(install);
   116         if (install.linkedInstalls) {
   117           install.linkedInstalls.forEach(function(aInstall) {
   118             aInstall.addListener(this);
   119             // App disabled items are not compatible and so fail to install
   120             if (aInstall.addon.appDisabled)
   121               failed.push(aInstall);
   122             else
   123               installs.push(aInstall);
   124           }, this);
   125         }
   126         break;
   127       case AddonManager.STATE_CANCELLED:
   128         // Just ignore cancelled downloads
   129         break;
   130       default:
   131         logger.warn("Download of " + install.sourceURI.spec + " in unexpected state " +
   132              install.state);
   133       }
   134     }
   136     this.isDownloading = false;
   137     this.downloads = installs;
   139     if (failed.length > 0) {
   140       // Stop listening and cancel any installs that are failed because of
   141       // compatibility reasons.
   142       failed.forEach(function(aInstall) {
   143         if (aInstall.state == AddonManager.STATE_DOWNLOADED) {
   144           aInstall.removeListener(this);
   145           aInstall.cancel();
   146         }
   147       }, this);
   148       notifyObservers("addon-install-failed", this.window, this.url, failed);
   149     }
   151     // If none of the downloads were successful then exit early
   152     if (this.downloads.length == 0)
   153       return;
   155     // Check for a custom installation prompt that may be provided by the
   156     // applicaton
   157     if ("@mozilla.org/addons/web-install-prompt;1" in Cc) {
   158       try {
   159         let prompt = Cc["@mozilla.org/addons/web-install-prompt;1"].
   160                      getService(Ci.amIWebInstallPrompt);
   161         prompt.confirm(this.window, this.url, this.downloads, this.downloads.length);
   162         return;
   163       }
   164       catch (e) {}
   165     }
   167     let args = {};
   168     args.url = this.url;
   169     args.installs = this.downloads;
   170     args.wrappedJSObject = args;
   172     try {
   173       Cc["@mozilla.org/base/telemetry;1"].
   174             getService(Ci.nsITelemetry).
   175             getHistogramById("SECURITY_UI").
   176             add(Ci.nsISecurityUITelemetry.WARNING_CONFIRM_ADDON_INSTALL);
   177       Services.ww.openWindow(this.window, URI_XPINSTALL_DIALOG,
   178                              null, "chrome,modal,centerscreen", args);
   179     } catch (e) {
   180       this.downloads.forEach(function(aInstall) {
   181         aInstall.removeListener(this);
   182         // Cancel the installs, as currently there is no way to make them fail
   183         // from here.
   184         aInstall.cancel();
   185       }, this);
   186       notifyObservers("addon-install-cancelled", this.window, this.url,
   187                       this.downloads);
   188     }
   189   },
   191   /**
   192    * Checks if all installs are now complete and if so notifies observers.
   193    */
   194   checkAllInstalled: function Installer_checkAllInstalled() {
   195     var failed = [];
   197     for (let install of this.downloads) {
   198       switch(install.state) {
   199       case AddonManager.STATE_DOWNLOADED:
   200       case AddonManager.STATE_INSTALLING:
   201         // Exit early if any add-ons haven't started installing yet or are
   202         // still installing
   203         return;
   204       case AddonManager.STATE_INSTALL_FAILED:
   205         failed.push(install);
   206         break;
   207       }
   208     }
   210     this.downloads = null;
   212     if (failed.length > 0)
   213       notifyObservers("addon-install-failed", this.window, this.url, failed);
   215     if (this.installed.length > 0)
   216       notifyObservers("addon-install-complete", this.window, this.url, this.installed);
   217     this.installed = null;
   218   },
   220   onDownloadCancelled: function Installer_onDownloadCancelled(aInstall) {
   221     aInstall.removeListener(this);
   222     this.checkAllDownloaded();
   223   },
   225   onDownloadFailed: function Installer_onDownloadFailed(aInstall) {
   226     aInstall.removeListener(this);
   227     this.checkAllDownloaded();
   228   },
   230   onDownloadEnded: function Installer_onDownloadEnded(aInstall) {
   231     this.checkAllDownloaded();
   232     return false;
   233   },
   235   onInstallCancelled: function Installer_onInstallCancelled(aInstall) {
   236     aInstall.removeListener(this);
   237     this.checkAllInstalled();
   238   },
   240   onInstallFailed: function Installer_onInstallFailed(aInstall) {
   241     aInstall.removeListener(this);
   242     this.checkAllInstalled();
   243   },
   245   onInstallEnded: function Installer_onInstallEnded(aInstall) {
   246     aInstall.removeListener(this);
   247     this.installed.push(aInstall);
   249     // If installing a theme that is disabled and can be enabled then enable it
   250     if (aInstall.addon.type == "theme" &&
   251         aInstall.addon.userDisabled == true &&
   252         aInstall.addon.appDisabled == false) {
   253       aInstall.addon.userDisabled = false;
   254     }
   256     this.checkAllInstalled();
   257   }
   258 };
   260 function extWebInstallListener() {
   261 }
   263 extWebInstallListener.prototype = {
   264   /**
   265    * @see amIWebInstallListener.idl
   266    */
   267   onWebInstallDisabled: function extWebInstallListener_onWebInstallDisabled(aWindow, aUri, aInstalls) {
   268     let info = {
   269       originatingWindow: aWindow,
   270       originatingURI: aUri,
   271       installs: aInstalls,
   273       QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
   274     };
   275     Services.obs.notifyObservers(info, "addon-install-disabled", null);
   276   },
   278   /**
   279    * @see amIWebInstallListener.idl
   280    */
   281   onWebInstallBlocked: function extWebInstallListener_onWebInstallBlocked(aWindow, aUri, aInstalls) {
   282     let info = {
   283       originatingWindow: aWindow,
   284       originatingURI: aUri,
   285       installs: aInstalls,
   287       install: function onWebInstallBlocked_install() {
   288         new Installer(this.originatingWindow, this.originatingURI, this.installs);
   289       },
   291       QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallInfo])
   292     };
   293     Services.obs.notifyObservers(info, "addon-install-blocked", null);
   295     return false;
   296   },
   298   /**
   299    * @see amIWebInstallListener.idl
   300    */
   301   onWebInstallRequested: function extWebInstallListener_onWebInstallRequested(aWindow, aUri, aInstalls) {
   302     new Installer(aWindow, aUri, aInstalls);
   304     // We start the installs ourself
   305     return false;
   306   },
   308   classDescription: "XPI Install Handler",
   309   contractID: "@mozilla.org/addons/web-install-listener;1",
   310   classID: Components.ID("{0f38e086-89a3-40a5-8ffc-9b694de1d04a}"),
   311   QueryInterface: XPCOMUtils.generateQI([Ci.amIWebInstallListener])
   312 };
   314 this.NSGetFactory = XPCOMUtils.generateNSGetFactory([extWebInstallListener]);

mercurial