forked from mirrors/gecko-dev
		
	
		
			
				
	
	
		
			434 lines
		
	
	
	
		
			15 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			434 lines
		
	
	
	
		
			15 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 (var i = 0; i < gBrowser.browsers.length; ++i) {
 | |
|       var browser = gBrowser.getBrowserAtIndex(i);
 | |
|       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]);
 | |
| 
 | |
|       action = {
 | |
|         label: gNavigatorBundle.getString("xpinstallPromptAllowButton"),
 | |
|         accessKey: gNavigatorBundle.getString("xpinstallPromptAllowButton.accesskey"),
 | |
|         callback: function() {
 | |
|           installInfo.install();
 | |
|         }
 | |
|       };
 | |
| 
 | |
|       PopupNotifications.show(browser, notificationID, messageString, anchorID,
 | |
|                               action, null, options);
 | |
|       break;
 | |
|     case "addon-install-started":
 | |
|       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
 | |
|       installInfo.installs.forEach(function(aInstall) {
 | |
|         var host = (installInfo.originatingURI instanceof Ci.nsIStandardURL) &&
 | |
|                    installInfo.originatingURI.host;
 | |
|         if (!host)
 | |
|           host = (aInstall.sourceURI instanceof Ci.nsIStandardURL) &&
 | |
|                  aInstall.sourceURI.host;
 | |
| 
 | |
|         var error = (host || aInstall.error == 0) ? "addonError" : "addonLocalError";
 | |
|         if (aInstall.error != 0)
 | |
|           error += aInstall.error;
 | |
|         else if (aInstall.addon.blocklistState == Ci.nsIBlocklistService.STATE_BLOCKED)
 | |
|           error += "Blocklisted";
 | |
|         else
 | |
|           error += "Incompatible";
 | |
| 
 | |
|         messageString = gNavigatorBundle.getString(error);
 | |
|         messageString = messageString.replace("#1", aInstall.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 = {
 | |
|           label: gNavigatorBundle.getString("addonInstallManage"),
 | |
|           accessKey: gNavigatorBundle.getString("addonInstallManage.accesskey"),
 | |
|           callback: function() {
 | |
|             // Calculate the add-on type that is most popular in the list of
 | |
|             // installs
 | |
|             var types = {};
 | |
|             var bestType = null;
 | |
|             installInfo.installs.forEach(function(aInstall) {
 | |
|               if (aInstall.type in types)
 | |
|                 types[aInstall.type]++;
 | |
|               else
 | |
|                 types[aInstall.type] = 1;
 | |
|               if (!bestType || types[aInstall.type] > types[bestType])
 | |
|                 bestType = aInstall.type;
 | |
|             });
 | |
| 
 | |
|             BrowserOpenAddonsMgr("addons://list/" + bestType);
 | |
|           }
 | |
|         };
 | |
|       }
 | |
| 
 | |
|       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"]);
 | |
|     this.addonBar.currentSet.split(",").forEach(function (item) {
 | |
|       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);
 | |
|   }
 | |
| }
 | 
