mobile/android/chrome/content/aboutAddons.js

Wed, 31 Dec 2014 07:22:50 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 07:22:50 +0100
branch
TOR_BUG_3246
changeset 4
fc2d59ddac77
permissions
-rw-r--r--

Correct previous dual key logic pending first delivery installment.

     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 file,
     3  * You can obtain one at http://mozilla.org/MPL/2.0/. */
     5 "use strict";
     7 let Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
     9 Cu.import("resource://gre/modules/Services.jsm")
    10 Cu.import("resource://gre/modules/AddonManager.jsm");
    11 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
    13 const AMO_ICON = "chrome://browser/skin/images/amo-logo.png";
    15 let gStringBundle = Services.strings.createBundle("chrome://browser/locale/aboutAddons.properties");
    17 XPCOMUtils.defineLazyGetter(window, "gChromeWin", function()
    18   window.QueryInterface(Ci.nsIInterfaceRequestor)
    19     .getInterface(Ci.nsIWebNavigation)
    20     .QueryInterface(Ci.nsIDocShellTreeItem)
    21     .rootTreeItem
    22     .QueryInterface(Ci.nsIInterfaceRequestor)
    23     .getInterface(Ci.nsIDOMWindow)
    24     .QueryInterface(Ci.nsIDOMChromeWindow));
    26 var ContextMenus = {
    27   target: null,
    29   init: function() {
    30     document.addEventListener("contextmenu", this, false);
    32     document.getElementById("contextmenu-enable").addEventListener("click", ContextMenus.enable.bind(this), false);
    33     document.getElementById("contextmenu-disable").addEventListener("click", ContextMenus.disable.bind(this), false);
    34     document.getElementById("contextmenu-uninstall").addEventListener("click", ContextMenus.uninstall.bind(this), false);
    36     // XXX - Hack to fix bug 985867 for now
    37     document.addEventListener("touchstart", function() { });
    38   },
    40   handleEvent: function(event) {
    41     // store the target of context menu events so that we know which app to act on
    42     this.target = event.target;
    43     while (!this.target.hasAttribute("contextmenu")) {
    44       this.target = this.target.parentNode;
    45     }
    47     if (!this.target) {
    48       document.getElementById("contextmenu-enable").setAttribute("hidden", "true");
    49       document.getElementById("contextmenu-disable").setAttribute("hidden", "true");
    50       document.getElementById("contextmenu-uninstall").setAttribute("hidden", "true");
    51       return;
    52     }
    54     let addon = this.target.addon;
    55     if (addon.scope == AddonManager.SCOPE_APPLICATION) {
    56       document.getElementById("contextmenu-uninstall").setAttribute("hidden", "true");
    57     } else {
    58       document.getElementById("contextmenu-uninstall").removeAttribute("hidden");
    59     }
    61     let enabled = this.target.getAttribute("isDisabled") != "true";
    62     if (enabled) {
    63       document.getElementById("contextmenu-enable").setAttribute("hidden", "true");
    64       document.getElementById("contextmenu-disable").removeAttribute("hidden");
    65     } else {
    66       document.getElementById("contextmenu-enable").removeAttribute("hidden");
    67       document.getElementById("contextmenu-disable").setAttribute("hidden", "true");
    68     }
    69   },
    71   enable: function(event) {
    72     Addons.setEnabled(true, this.target.addon);
    73     this.target = null;
    74   },
    76   disable: function (event) {
    77     Addons.setEnabled(false, this.target.addon);
    78     this.target = null;
    79   },
    81   uninstall: function (event) {
    82     Addons.uninstall(this.target.addon);
    83     this.target = null;
    84   }
    85 }
    87 function init() {
    88   window.addEventListener("popstate", onPopState, false);
    90   AddonManager.addInstallListener(Addons);
    91   AddonManager.addAddonListener(Addons);
    92   Addons.init();
    93   showList();
    94   ContextMenus.init();
    96   document.getElementById("header-button").addEventListener("click", openLink, false);
    97 }
   100 function uninit() {
   101   AddonManager.removeInstallListener(Addons);
   102   AddonManager.removeAddonListener(Addons);
   103 }
   105 function openLink(aEvent) {
   106   try {
   107     let formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"].getService(Ci.nsIURLFormatter);
   109     let url = formatter.formatURLPref(aEvent.currentTarget.getAttribute("pref"));
   110     let BrowserApp = gChromeWin.BrowserApp;
   111     BrowserApp.addTab(url, { selected: true, parentId: BrowserApp.selectedTab.id });
   112   } catch (ex) {}
   113 }
   115 function onPopState(aEvent) {
   116   // Called when back/forward is used to change the state of the page
   117   if (aEvent.state) {
   118     // Show the detail page for an addon
   119     Addons.showDetails(Addons._getElementForAddon(aEvent.state.id));
   120   } else {
   121     // Clear any previous detail addon
   122     let detailItem = document.querySelector("#addons-details > .addon-item");
   123     detailItem.addon = null;
   125     showList();
   126   }
   127 }
   129 function showList() {
   130   // Hide the detail page and show the list
   131   let details = document.querySelector("#addons-details");
   132   details.style.display = "none";
   133   let list = document.querySelector("#addons-list");
   134   list.style.display = "block";
   135 }
   137 var Addons = {
   138   _restartCount: 0,
   140   _createItem: function _createItem(aAddon) {
   141     let outer = document.createElement("div");
   142     outer.setAttribute("addonID", aAddon.id);
   143     outer.className = "addon-item list-item";
   144     outer.setAttribute("role", "button");
   145     outer.setAttribute("contextmenu", "addonmenu");
   146     outer.addEventListener("click", function() {
   147       this.showDetails(outer);
   148       history.pushState({ id: aAddon.id }, document.title);
   149     }.bind(this), true);
   151     let img = document.createElement("img");
   152     img.className = "icon";
   153     img.setAttribute("src", aAddon.iconURL || AMO_ICON);
   154     outer.appendChild(img);
   156     let inner = document.createElement("div");
   157     inner.className = "inner";
   159     let details = document.createElement("div");
   160     details.className = "details";
   161     inner.appendChild(details);
   163     let titlePart = document.createElement("div");
   164     titlePart.textContent = aAddon.name;
   165     titlePart.className = "title";
   166     details.appendChild(titlePart);
   168     let versionPart = document.createElement("div");
   169     versionPart.textContent = aAddon.version;
   170     versionPart.className = "version";
   171     details.appendChild(versionPart);
   173     if ("description" in aAddon) {
   174       let descPart = document.createElement("div");
   175       descPart.textContent = aAddon.description;
   176       descPart.className = "description";
   177       inner.appendChild(descPart);
   178     }
   180     outer.appendChild(inner);
   181     return outer;
   182   },
   184   _createBrowseItem: function _createBrowseItem() {
   185     let outer = document.createElement("div");
   186     outer.className = "addon-item list-item";
   187     outer.setAttribute("role", "button");
   188     outer.setAttribute("pref", "extensions.getAddons.browseAddons");
   189     outer.addEventListener("click", openLink, true);
   191     let img = document.createElement("img");
   192     img.className = "icon";
   193     img.setAttribute("src", AMO_ICON);
   194     outer.appendChild(img);
   196     let inner = document.createElement("div");
   197     inner.className = "inner";
   199     let title = document.createElement("div");
   200     title.id = "browse-title";
   201     title.className = "title";
   202     title.textContent = gStringBundle.GetStringFromName("addons.browseAll");
   203     inner.appendChild(title);
   205     outer.appendChild(inner);
   206     return outer;
   207   },
   209   _createItemForAddon: function _createItemForAddon(aAddon) {
   210     let appManaged = (aAddon.scope == AddonManager.SCOPE_APPLICATION);
   211     let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
   212     let updateable = (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) > 0;
   213     let uninstallable = (aAddon.permissions & AddonManager.PERM_CAN_UNINSTALL) > 0;
   215     let blocked = "";
   216     switch(aAddon.blocklistState) {
   217       case Ci.nsIBlocklistService.STATE_BLOCKED:
   218         blocked = "blocked";
   219         break;
   220       case Ci.nsIBlocklistService.STATE_SOFTBLOCKED:
   221         blocked = "softBlocked";
   222         break;
   223       case Ci.nsIBlocklistService.STATE_OUTDATED:
   224         blocked = "outdated";
   225         break;
   226     }
   228     let item = this._createItem(aAddon);
   229     item.setAttribute("isDisabled", !aAddon.isActive);
   230     item.setAttribute("opType", opType);
   231     item.setAttribute("updateable", updateable);
   232     if (blocked)
   233       item.setAttribute("blockedStatus", blocked);
   234     item.setAttribute("optionsURL", aAddon.optionsURL || "");
   235     item.addon = aAddon;
   237     return item;
   238   },
   240   _getElementForAddon: function(aKey) {
   241     let list = document.getElementById("addons-list");
   242     let element = list.querySelector("div[addonID=" + aKey.quote() + "]");
   243     return element;
   244   },
   246   init: function init() {
   247     let self = this;
   248     AddonManager.getAddonsByTypes(["extension", "theme", "locale"], function(aAddons) {
   249       // Clear all content before filling the addons
   250       let list = document.getElementById("addons-list");
   251       list.innerHTML = "";
   253       for (let i=0; i<aAddons.length; i++) {
   254         let item = self._createItemForAddon(aAddons[i]);
   255         list.appendChild(item);
   256       }
   258       // Add a "Browse all Firefox Add-ons" item to the bottom of the list.
   259       let browseItem = self._createBrowseItem();
   260       list.appendChild(browseItem);
   261     });
   263     document.getElementById("uninstall-btn").addEventListener("click", Addons.uninstallCurrent.bind(this), false);
   264     document.getElementById("cancel-btn").addEventListener("click", Addons.cancelUninstall.bind(this), false);
   265     document.getElementById("disable-btn").addEventListener("click", Addons.disable.bind(this), false);
   266     document.getElementById("enable-btn").addEventListener("click", Addons.enable.bind(this), false);
   267   },
   269   _getOpTypeForOperations: function _getOpTypeForOperations(aOperations) {
   270     if (aOperations & AddonManager.PENDING_UNINSTALL)
   271       return "needs-uninstall";
   272     if (aOperations & AddonManager.PENDING_ENABLE)
   273       return "needs-enable";
   274     if (aOperations & AddonManager.PENDING_DISABLE)
   275       return "needs-disable";
   276     return "";
   277   },
   279   showDetails: function showDetails(aListItem) {
   280     // This function removes and returns the text content of aNode without
   281     // removing any child elements. Removing the text nodes ensures any XBL
   282     // bindings apply properly.
   283     function stripTextNodes(aNode) {
   284       var text = "";
   285       for (var i = 0; i < aNode.childNodes.length; i++) {
   286         if (aNode.childNodes[i].nodeType != document.ELEMENT_NODE) {
   287           text += aNode.childNodes[i].textContent;
   288           aNode.removeChild(aNode.childNodes[i--]);
   289         } else {
   290           text += stripTextNodes(aNode.childNodes[i]);
   291         }
   292       }
   293       return text;
   294     }
   296     let detailItem = document.querySelector("#addons-details > .addon-item");
   297     detailItem.setAttribute("isDisabled", aListItem.getAttribute("isDisabled"));
   298     detailItem.setAttribute("opType", aListItem.getAttribute("opType"));
   299     detailItem.setAttribute("optionsURL", aListItem.getAttribute("optionsURL"));
   300     let addon = detailItem.addon = aListItem.addon;
   302     let favicon = document.querySelector("#addons-details > .addon-item .icon");
   303     favicon.setAttribute("src", addon.iconURL || AMO_ICON);
   305     detailItem.querySelector(".title").textContent = addon.name;
   306     detailItem.querySelector(".version").textContent = addon.version;
   307     detailItem.querySelector(".description-full").textContent = addon.description;
   308     detailItem.querySelector(".status-uninstalled").textContent =
   309       gStringBundle.formatStringFromName("addonStatus.uninstalled", [addon.name], 1);
   311     let enableBtn = document.getElementById("enable-btn");
   312     if (addon.appDisabled)
   313       enableBtn.setAttribute("disabled", "true");
   314     else
   315       enableBtn.removeAttribute("disabled");
   317     let uninstallBtn = document.getElementById("uninstall-btn");
   318     if (addon.scope == AddonManager.SCOPE_APPLICATION)
   319       uninstallBtn.setAttribute("disabled", "true");
   320     else
   321       uninstallBtn.removeAttribute("disabled");
   323     let box = document.querySelector("#addons-details > .addon-item .options-box");
   324     box.innerHTML = "";
   326     // Retrieve the extensions preferences
   327     try {
   328       let optionsURL = aListItem.getAttribute("optionsURL");
   329       let xhr = new XMLHttpRequest();
   330       xhr.open("GET", optionsURL, false);
   331       xhr.send();
   332       if (xhr.responseXML) {
   333         // Only allow <setting> for now
   334         let settings = xhr.responseXML.querySelectorAll(":root > setting");
   335         if (settings.length > 0) {
   336           for (let i = 0; i < settings.length; i++) {
   337             var setting = settings[i];
   338             var desc = stripTextNodes(setting).trim();
   339             if (!setting.hasAttribute("desc"))
   340               setting.setAttribute("desc", desc);
   341             box.appendChild(setting);
   342           }
   343           // Send an event so add-ons can prepopulate any non-preference based
   344           // settings
   345           let event = document.createEvent("Events");
   346           event.initEvent("AddonOptionsLoad", true, false);
   347           window.dispatchEvent(event);
   349           // Also send a notification to match the behavior of desktop Firefox
   350           let id = aListItem.getAttribute("addonID");
   351           Services.obs.notifyObservers(document, AddonManager.OPTIONS_NOTIFICATION_DISPLAYED, id);
   352         } else {
   353           // No options, so hide the header and reset the list item
   354           detailItem.setAttribute("optionsURL", "");
   355           aListItem.setAttribute("optionsURL", "");
   356         }
   357       }
   358     } catch (e) { }
   360     let list = document.querySelector("#addons-list");
   361     list.style.display = "none";
   362     let details = document.querySelector("#addons-details");
   363     details.style.display = "block";
   364   },
   366   setEnabled: function setEnabled(aValue, aAddon) {
   367     let detailItem = document.querySelector("#addons-details > .addon-item");
   368     let addon = aAddon || detailItem.addon;
   369     if (!addon)
   370       return;
   372     let listItem = this._getElementForAddon(addon.id);
   374     let opType;
   375     if (addon.type == "theme") {
   376       if (aValue) {
   377         // We can have only one theme enabled, so disable the current one if any
   378         let list = document.getElementById("addons-list");
   379         let item = list.firstElementChild;
   380         while (item) {
   381           if (item.addon && (item.addon.type == "theme") && (item.addon.isActive)) {
   382             item.addon.userDisabled = true;
   383             item.setAttribute("isDisabled", true);
   384             break;
   385           }
   386           item = item.nextSibling;
   387         }
   388       }
   389       addon.userDisabled = !aValue;
   390     } else if (addon.type == "locale") {
   391       addon.userDisabled = !aValue;
   392     } else {
   393       addon.userDisabled = !aValue;
   394       opType = this._getOpTypeForOperations(addon.pendingOperations);
   396       if ((addon.pendingOperations & AddonManager.PENDING_ENABLE) ||
   397           (addon.pendingOperations & AddonManager.PENDING_DISABLE)) {
   398         this.showRestart();
   399       } else if (listItem && /needs-(enable|disable)/.test(listItem.getAttribute("opType"))) {
   400         this.hideRestart();
   401       }
   402     }
   404     if (addon == detailItem.addon) {
   405       detailItem.setAttribute("isDisabled", !aValue);
   406       if (opType)
   407         detailItem.setAttribute("opType", opType);
   408       else
   409         detailItem.removeAttribute("opType");
   410     }
   412     // Sync to the list item
   413     if (listItem) {
   414       listItem.setAttribute("isDisabled", !aValue);
   415       if (opType)
   416         listItem.setAttribute("opType", opType);
   417       else
   418         listItem.removeAttribute("opType");
   419     }
   420   },
   422   enable: function enable() {
   423     this.setEnabled(true);
   424   },
   426   disable: function disable() {
   427     this.setEnabled(false);
   428   },
   430   uninstallCurrent: function uninstallCurrent() {
   431     let detailItem = document.querySelector("#addons-details > .addon-item");
   433     let addon = detailItem.addon;
   434     if (!addon)
   435       return;
   437     this.uninstall(addon);
   438   },
   440   uninstall: function uninstall(aAddon) {
   441     let list = document.getElementById("addons-list");
   443     if (!aAddon) {
   444         return;
   445     }
   447     let listItem = this._getElementForAddon(aAddon.id);
   449     aAddon.uninstall();
   450     if (aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL) {
   451       this.showRestart();
   453       // A disabled addon doesn't need a restart so it has no pending ops and
   454       // can't be cancelled
   455       let opType = this._getOpTypeForOperations(aAddon.pendingOperations);
   456       if (!aAddon.isActive && opType == "")
   457         opType = "needs-uninstall";
   459       detailItem.setAttribute("opType", opType);
   460       listItem.setAttribute("opType", opType);
   461     } else {
   462       list.removeChild(listItem);
   463       history.back();
   464     }
   465   },
   467   cancelUninstall: function ev_cancelUninstall() {
   468     let detailItem = document.querySelector("#addons-details > .addon-item");
   469     let addon = detailItem.addon;
   470     if (!addon)
   471       return;
   473     addon.cancelUninstall();
   474     this.hideRestart();
   476     let opType = this._getOpTypeForOperations(addon.pendingOperations);
   477     detailItem.setAttribute("opType", opType);
   479     let listItem = this._getElementForAddon(addon.id);
   480     listItem.setAttribute("opType", opType);
   481   },
   483   showRestart: function showRestart() {
   484     this._restartCount++;
   485     gChromeWin.XPInstallObserver.showRestartPrompt();
   486   },
   488   hideRestart: function hideRestart() {
   489     this._restartCount--;
   490     if (this._restartCount == 0)
   491       gChromeWin.XPInstallObserver.hideRestartPrompt();
   492   },
   494   onEnabled: function(aAddon) {
   495     let listItem = this._getElementForAddon(aAddon.id);
   496     if (!listItem)
   497       return;
   499     // Reload the details to pick up any options now that it's enabled.
   500     listItem.setAttribute("optionsURL", aAddon.optionsURL || "");
   501     let detailItem = document.querySelector("#addons-details > .addon-item");
   502     if (aAddon == detailItem.addon)
   503       this.showDetails(listItem);
   504   },
   506   onInstallEnded: function(aInstall, aAddon) {
   507     let needsRestart = false;
   508     if (aInstall.existingAddon && (aInstall.existingAddon.pendingOperations & AddonManager.PENDING_UPGRADE))
   509       needsRestart = true;
   510     else if (aAddon.pendingOperations & AddonManager.PENDING_INSTALL)
   511       needsRestart = true;
   513     let list = document.getElementById("addons-list");
   514     let element = this._getElementForAddon(aAddon.id);
   515     if (!element) {
   516       element = this._createItemForAddon(aAddon);
   517       list.insertBefore(element, list.firstElementChild);
   518     }
   520     if (needsRestart)
   521       element.setAttribute("opType", "needs-restart");
   522   },
   524   onInstallFailed: function(aInstall) {
   525   },
   527   onDownloadProgress: function xpidm_onDownloadProgress(aInstall) {
   528   },
   530   onDownloadFailed: function(aInstall) {
   531   },
   533   onDownloadCancelled: function(aInstall) {
   534   }
   535 }
   537 window.addEventListener("load", init, false);
   538 window.addEventListener("unload", uninit, false);

mercurial