forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			417 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			417 lines
		
	
	
	
		
			14 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
 | 
						|
# This Source Code Form is subject to the terms of the Mozilla Public
 | 
						|
# 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/.
 | 
						|
 | 
						|
const gXPInstallObserver = {
 | 
						|
  _findChildShell: function (aDocShell, aSoughtShell)
 | 
						|
  {
 | 
						|
    if (aDocShell == aSoughtShell)
 | 
						|
      return aDocShell;
 | 
						|
 | 
						|
    var node = aDocShell.QueryInterface(Components.interfaces.nsIDocShellTreeNode);
 | 
						|
    for (var i = 0; i < node.childCount; ++i) {
 | 
						|
      var docShell = node.getChildAt(i);
 | 
						|
      docShell = this._findChildShell(docShell, aSoughtShell);
 | 
						|
      if (docShell == aSoughtShell)
 | 
						|
        return docShell;
 | 
						|
    }
 | 
						|
    return null;
 | 
						|
  },
 | 
						|
 | 
						|
  _getBrowser: function (aDocShell)
 | 
						|
  {
 | 
						|
    for (let browser of gBrowser.browsers) {
 | 
						|
      if (this._findChildShell(browser.docShell, aDocShell))
 | 
						|
        return browser;
 | 
						|
    }
 | 
						|
    return null;
 | 
						|
  },
 | 
						|
 | 
						|
  observe: function (aSubject, aTopic, aData)
 | 
						|
  {
 | 
						|
    var brandBundle = document.getElementById("bundle_brand");
 | 
						|
    var installInfo = aSubject.QueryInterface(Components.interfaces.amIWebInstallInfo);
 | 
						|
    var win = installInfo.originatingWindow;
 | 
						|
    var shell = win.QueryInterface(Components.interfaces.nsIInterfaceRequestor)
 | 
						|
                   .getInterface(Components.interfaces.nsIWebNavigation)
 | 
						|
                   .QueryInterface(Components.interfaces.nsIDocShell);
 | 
						|
    var browser = this._getBrowser(shell);
 | 
						|
    if (!browser)
 | 
						|
      return;
 | 
						|
    const anchorID = "addons-notification-icon";
 | 
						|
    var messageString, action;
 | 
						|
    var brandShortName = brandBundle.getString("brandShortName");
 | 
						|
 | 
						|
    var notificationID = aTopic;
 | 
						|
    // Make notifications persist a minimum of 30 seconds
 | 
						|
    var options = {
 | 
						|
      timeout: Date.now() + 30000
 | 
						|
    };
 | 
						|
 | 
						|
    switch (aTopic) {
 | 
						|
    case "addon-install-disabled":
 | 
						|
      notificationID = "xpinstall-disabled"
 | 
						|
 | 
						|
      if (gPrefService.prefIsLocked("xpinstall.enabled")) {
 | 
						|
        messageString = gNavigatorBundle.getString("xpinstallDisabledMessageLocked");
 | 
						|
        buttons = [];
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        messageString = gNavigatorBundle.getString("xpinstallDisabledMessage");
 | 
						|
 | 
						|
        action = {
 | 
						|
          label: gNavigatorBundle.getString("xpinstallDisabledButton"),
 | 
						|
          accessKey: gNavigatorBundle.getString("xpinstallDisabledButton.accesskey"),
 | 
						|
          callback: function editPrefs() {
 | 
						|
            gPrefService.setBoolPref("xpinstall.enabled", true);
 | 
						|
          }
 | 
						|
        };
 | 
						|
      }
 | 
						|
 | 
						|
      PopupNotifications.show(browser, notificationID, messageString, anchorID,
 | 
						|
                              action, null, options);
 | 
						|
      break;
 | 
						|
    case "addon-install-blocked":
 | 
						|
      messageString = gNavigatorBundle.getFormattedString("xpinstallPromptWarning",
 | 
						|
                        [brandShortName, installInfo.originatingURI.host]);
 | 
						|
 | 
						|
      let secHistogram = Components.classes["@mozilla.org/base/telemetry;1"].getService(Ci.nsITelemetry).getHistogramById("SECURITY_UI");
 | 
						|
      action = {
 | 
						|
        label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
 | 
						|
        accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
 | 
						|
        callback: function() {
 | 
						|
          secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED_CLICK_THROUGH);
 | 
						|
          installInfo.install();
 | 
						|
        }
 | 
						|
      };
 | 
						|
 | 
						|
      secHistogram.add(Ci.nsISecurityUITelemetry.WARNING_ADDON_ASKING_PREVENTED);
 | 
						|
      PopupNotifications.show(browser, notificationID, messageString, anchorID,
 | 
						|
                              action, null, options);
 | 
						|
      break;
 | 
						|
    case "addon-install-started":
 | 
						|
      var needsDownload = function needsDownload(aInstall) {
 | 
						|
        return aInstall.state != AddonManager.STATE_DOWNLOADED;
 | 
						|
      }
 | 
						|
      // If all installs have already been downloaded then there is no need to
 | 
						|
      // show the download progress
 | 
						|
      if (!installInfo.installs.some(needsDownload))
 | 
						|
        return;
 | 
						|
      notificationID = "addon-progress";
 | 
						|
      messageString = gNavigatorBundle.getString("addonDownloading");
 | 
						|
      messageString = PluralForm.get(installInfo.installs.length, messageString);
 | 
						|
      options.installs = installInfo.installs;
 | 
						|
      options.contentWindow = browser.contentWindow;
 | 
						|
      options.sourceURI = browser.currentURI;
 | 
						|
      options.eventCallback = function(aEvent) {
 | 
						|
        if (aEvent != "removed")
 | 
						|
          return;
 | 
						|
        options.contentWindow = null;
 | 
						|
        options.sourceURI = null;
 | 
						|
      };
 | 
						|
      PopupNotifications.show(browser, notificationID, messageString, anchorID,
 | 
						|
                              null, null, options);
 | 
						|
      break;
 | 
						|
    case "addon-install-failed":
 | 
						|
      // TODO This isn't terribly ideal for the multiple failure case
 | 
						|
      for (let install of installInfo.installs) {
 | 
						|
        let host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
 | 
						|
                   installInfo.originatingURI.host;
 | 
						|
        if (!host)
 | 
						|
          host = (install.sourceURI instanceof Ci.nsIStandardURL) &&
 | 
						|
                 install.sourceURI.host;
 | 
						|
 | 
						|
        let error = (host || install.error == 0) ? "addonError" : "addonLocalError";
 | 
						|
        if (install.error != 0)
 | 
						|
          error += install.error;
 | 
						|
        else if (install.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
 | 
						|
          error += "Blocklisted";
 | 
						|
        else
 | 
						|
          error += "Incompatible";
 | 
						|
 | 
						|
        messageString = gNavigatorBundle.getString(error);
 | 
						|
        messageString = messageString.replace("#1", install.name);
 | 
						|
        if (host)
 | 
						|
          messageString = messageString.replace("#2", host);
 | 
						|
        messageString = messageString.replace("#3", brandShortName);
 | 
						|
        messageString = messageString.replace("#4", Services.appinfo.version);
 | 
						|
 | 
						|
        PopupNotifications.show(browser, notificationID, messageString, anchorID,
 | 
						|
                                action, null, options);
 | 
						|
      }
 | 
						|
      break;
 | 
						|
    case "addon-install-complete":
 | 
						|
      var needsRestart = installInfo.installs.some(function(i) {
 | 
						|
        return i.addon.pendingOperations != AddonManager.PENDING_NONE;
 | 
						|
      });
 | 
						|
 | 
						|
      if (needsRestart) {
 | 
						|
        messageString = gNavigatorBundle.getString("addonsInstalledNeedsRestart");
 | 
						|
        action = {
 | 
						|
          label: gNavigatorBundle.getString("addonInstallRestartButton"),
 | 
						|
          accessKey: gNavigatorBundle.getString("addonInstallRestartButton.accesskey"),
 | 
						|
          callback: function() {
 | 
						|
            Application.restart();
 | 
						|
          }
 | 
						|
        };
 | 
						|
      }
 | 
						|
      else {
 | 
						|
        messageString = gNavigatorBundle.getString("addonsInstalled");
 | 
						|
        action = null;
 | 
						|
      }
 | 
						|
 | 
						|
      messageString = PluralForm.get(installInfo.installs.length, messageString);
 | 
						|
      messageString = messageString.replace("#1", installInfo.installs[0].name);
 | 
						|
      messageString = messageString.replace("#2", installInfo.installs.length);
 | 
						|
      messageString = messageString.replace("#3", brandShortName);
 | 
						|
 | 
						|
      // Remove notificaion on dismissal, since it's possible to cancel the
 | 
						|
      // install through the addons manager UI, making the "restart" prompt
 | 
						|
      // irrelevant.
 | 
						|
      options.removeOnDismissal = true;
 | 
						|
 | 
						|
      PopupNotifications.show(browser, notificationID, messageString, anchorID,
 | 
						|
                              action, null, options);
 | 
						|
      break;
 | 
						|
    }
 | 
						|
  }
 | 
						|
};
 | 
						|
 | 
						|
/*
 | 
						|
 * When addons are installed/uninstalled, check and see if the number of items
 | 
						|
 * on the add-on bar changed:
 | 
						|
 * - If an add-on was installed, incrementing the count, show the bar.
 | 
						|
 * - If an add-on was uninstalled, and no more items are left, hide the bar.
 | 
						|
 */
 | 
						|
let AddonsMgrListener = {
 | 
						|
  get addonBar() document.getElementById("addon-bar"),
 | 
						|
  get statusBar() document.getElementById("status-bar"),
 | 
						|
  getAddonBarItemCount: function() {
 | 
						|
    // Take into account the contents of the status bar shim for the count.
 | 
						|
    var itemCount = this.statusBar.childNodes.length;
 | 
						|
 | 
						|
    var defaultOrNoninteractive = this.addonBar.getAttribute("defaultset")
 | 
						|
                                      .split(",")
 | 
						|
                                      .concat(["separator", "spacer", "spring"]);
 | 
						|
    for (let item of this.addonBar.currentSet.split(",")) {
 | 
						|
      if (defaultOrNoninteractive.indexOf(item) == -1)
 | 
						|
        itemCount++;
 | 
						|
    }
 | 
						|
 | 
						|
    return itemCount;
 | 
						|
  },
 | 
						|
  onInstalling: function(aAddon) {
 | 
						|
    this.lastAddonBarCount = this.getAddonBarItemCount();
 | 
						|
  },
 | 
						|
  onInstalled: function(aAddon) {
 | 
						|
    if (this.getAddonBarItemCount() > this.lastAddonBarCount)
 | 
						|
      setToolbarVisibility(this.addonBar, true);
 | 
						|
  },
 | 
						|
  onUninstalling: function(aAddon) {
 | 
						|
    this.lastAddonBarCount = this.getAddonBarItemCount();
 | 
						|
  },
 | 
						|
  onUninstalled: function(aAddon) {
 | 
						|
    if (this.getAddonBarItemCount() == 0)
 | 
						|
      setToolbarVisibility(this.addonBar, false);
 | 
						|
  },
 | 
						|
  onEnabling: function(aAddon) this.onInstalling(),
 | 
						|
  onEnabled: function(aAddon) this.onInstalled(),
 | 
						|
  onDisabling: function(aAddon) this.onUninstalling(),
 | 
						|
  onDisabled: function(aAddon) this.onUninstalled(),
 | 
						|
};
 | 
						|
 | 
						|
 | 
						|
var LightWeightThemeWebInstaller = {
 | 
						|
  handleEvent: function (event) {
 | 
						|
    switch (event.type) {
 | 
						|
      case "InstallBrowserTheme":
 | 
						|
      case "PreviewBrowserTheme":
 | 
						|
      case "ResetBrowserThemePreview":
 | 
						|
        // ignore requests from background tabs
 | 
						|
        if (event.target.ownerDocument.defaultView.top != content)
 | 
						|
          return;
 | 
						|
    }
 | 
						|
    switch (event.type) {
 | 
						|
      case "InstallBrowserTheme":
 | 
						|
        this._installRequest(event);
 | 
						|
        break;
 | 
						|
      case "PreviewBrowserTheme":
 | 
						|
        this._preview(event);
 | 
						|
        break;
 | 
						|
      case "ResetBrowserThemePreview":
 | 
						|
        this._resetPreview(event);
 | 
						|
        break;
 | 
						|
      case "pagehide":
 | 
						|
      case "TabSelect":
 | 
						|
        this._resetPreview();
 | 
						|
        break;
 | 
						|
    }
 | 
						|
  },
 | 
						|
 | 
						|
  get _manager () {
 | 
						|
    var temp = {};
 | 
						|
    Cu.import("resource://gre/modules/LightweightThemeManager.jsm", temp);
 | 
						|
    delete this._manager;
 | 
						|
    return this._manager = temp.LightweightThemeManager;
 | 
						|
  },
 | 
						|
 | 
						|
  _installRequest: function (event) {
 | 
						|
    var node = event.target;
 | 
						|
    var data = this._getThemeFromNode(node);
 | 
						|
    if (!data)
 | 
						|
      return;
 | 
						|
 | 
						|
    if (this._isAllowed(node)) {
 | 
						|
      this._install(data);
 | 
						|
      return;
 | 
						|
    }
 | 
						|
 | 
						|
    var allowButtonText =
 | 
						|
      gNavigatorBundle.getString("lwthemeInstallRequest.allowButton");
 | 
						|
    var allowButtonAccesskey =
 | 
						|
      gNavigatorBundle.getString("lwthemeInstallRequest.allowButton.accesskey");
 | 
						|
    var message =
 | 
						|
      gNavigatorBundle.getFormattedString("lwthemeInstallRequest.message",
 | 
						|
                                          [node.ownerDocument.location.host]);
 | 
						|
    var buttons = [{
 | 
						|
      label: allowButtonText,
 | 
						|
      accessKey: allowButtonAccesskey,
 | 
						|
      callback: function () {
 | 
						|
        LightWeightThemeWebInstaller._install(data);
 | 
						|
      }
 | 
						|
    }];
 | 
						|
 | 
						|
    this._removePreviousNotifications();
 | 
						|
 | 
						|
    var notificationBox = gBrowser.getNotificationBox();
 | 
						|
    var notificationBar =
 | 
						|
      notificationBox.appendNotification(message, "lwtheme-install-request", "",
 | 
						|
                                         notificationBox.PRIORITY_INFO_MEDIUM,
 | 
						|
                                         buttons);
 | 
						|
    notificationBar.persistence = 1;
 | 
						|
  },
 | 
						|
 | 
						|
  _install: function (newLWTheme) {
 | 
						|
    var previousLWTheme = this._manager.currentTheme;
 | 
						|
 | 
						|
    var listener = {
 | 
						|
      onEnabling: function(aAddon, aRequiresRestart) {
 | 
						|
        if (!aRequiresRestart)
 | 
						|
          return;
 | 
						|
 | 
						|
        let messageString = gNavigatorBundle.getFormattedString("lwthemeNeedsRestart.message",
 | 
						|
          [aAddon.name], 1);
 | 
						|
 | 
						|
        let action = {
 | 
						|
          label: gNavigatorBundle.getString("lwthemeNeedsRestart.button"),
 | 
						|
          accessKey: gNavigatorBundle.getString("lwthemeNeedsRestart.accesskey"),
 | 
						|
          callback: function () {
 | 
						|
            Application.restart();
 | 
						|
          }
 | 
						|
        };
 | 
						|
 | 
						|
        let options = {
 | 
						|
          timeout: Date.now() + 30000
 | 
						|
        };
 | 
						|
 | 
						|
        PopupNotifications.show(gBrowser.selectedBrowser, "addon-theme-change",
 | 
						|
                                messageString, "addons-notification-icon",
 | 
						|
                                action, null, options);
 | 
						|
      },
 | 
						|
 | 
						|
      onEnabled: function(aAddon) {
 | 
						|
        LightWeightThemeWebInstaller._postInstallNotification(newLWTheme, previousLWTheme);
 | 
						|
      }
 | 
						|
    };
 | 
						|
 | 
						|
    AddonManager.addAddonListener(listener);
 | 
						|
    this._manager.currentTheme = newLWTheme;
 | 
						|
    AddonManager.removeAddonListener(listener);
 | 
						|
  },
 | 
						|
 | 
						|
  _postInstallNotification: function (newTheme, previousTheme) {
 | 
						|
    function text(id) {
 | 
						|
      return gNavigatorBundle.getString("lwthemePostInstallNotification." + id);
 | 
						|
    }
 | 
						|
 | 
						|
    var buttons = [{
 | 
						|
      label: text("undoButton"),
 | 
						|
      accessKey: text("undoButton.accesskey"),
 | 
						|
      callback: function () {
 | 
						|
        LightWeightThemeWebInstaller._manager.forgetUsedTheme(newTheme.id);
 | 
						|
        LightWeightThemeWebInstaller._manager.currentTheme = previousTheme;
 | 
						|
      }
 | 
						|
    }, {
 | 
						|
      label: text("manageButton"),
 | 
						|
      accessKey: text("manageButton.accesskey"),
 | 
						|
      callback: function () {
 | 
						|
        BrowserOpenAddonsMgr("addons://list/theme");
 | 
						|
      }
 | 
						|
    }];
 | 
						|
 | 
						|
    this._removePreviousNotifications();
 | 
						|
 | 
						|
    var notificationBox = gBrowser.getNotificationBox();
 | 
						|
    var notificationBar =
 | 
						|
      notificationBox.appendNotification(text("message"),
 | 
						|
                                         "lwtheme-install-notification", "",
 | 
						|
                                         notificationBox.PRIORITY_INFO_MEDIUM,
 | 
						|
                                         buttons);
 | 
						|
    notificationBar.persistence = 1;
 | 
						|
    notificationBar.timeout = Date.now() + 20000; // 20 seconds
 | 
						|
  },
 | 
						|
 | 
						|
  _removePreviousNotifications: function () {
 | 
						|
    var box = gBrowser.getNotificationBox();
 | 
						|
 | 
						|
    ["lwtheme-install-request",
 | 
						|
     "lwtheme-install-notification"].forEach(function (value) {
 | 
						|
        var notification = box.getNotificationWithValue(value);
 | 
						|
        if (notification)
 | 
						|
          box.removeNotification(notification);
 | 
						|
      });
 | 
						|
  },
 | 
						|
 | 
						|
  _previewWindow: null,
 | 
						|
  _preview: function (event) {
 | 
						|
    if (!this._isAllowed(event.target))
 | 
						|
      return;
 | 
						|
 | 
						|
    var data = this._getThemeFromNode(event.target);
 | 
						|
    if (!data)
 | 
						|
      return;
 | 
						|
 | 
						|
    this._resetPreview();
 | 
						|
 | 
						|
    this._previewWindow = event.target.ownerDocument.defaultView;
 | 
						|
    this._previewWindow.addEventListener("pagehide", this, true);
 | 
						|
    gBrowser.tabContainer.addEventListener("TabSelect", this, false);
 | 
						|
 | 
						|
    this._manager.previewTheme(data);
 | 
						|
  },
 | 
						|
 | 
						|
  _resetPreview: function (event) {
 | 
						|
    if (!this._previewWindow ||
 | 
						|
        event && !this._isAllowed(event.target))
 | 
						|
      return;
 | 
						|
 | 
						|
    this._previewWindow.removeEventListener("pagehide", this, true);
 | 
						|
    this._previewWindow = null;
 | 
						|
    gBrowser.tabContainer.removeEventListener("TabSelect", this, false);
 | 
						|
 | 
						|
    this._manager.resetPreview();
 | 
						|
  },
 | 
						|
 | 
						|
  _isAllowed: function (node) {
 | 
						|
    var pm = Services.perms;
 | 
						|
 | 
						|
    var uri = node.ownerDocument.documentURIObject;
 | 
						|
    return pm.testPermission(uri, "install") == pm.ALLOW_ACTION;
 | 
						|
  },
 | 
						|
 | 
						|
  _getThemeFromNode: function (node) {
 | 
						|
    return this._manager.parseTheme(node.getAttribute("data-browsertheme"),
 | 
						|
                                    node.baseURI);
 | 
						|
  }
 | 
						|
}
 |