michael@0: // -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: "use strict"; michael@0: michael@0: Components.utils.import("resource://gre/modules/AddonManager.jsm"); michael@0: Components.utils.import("resource://gre/modules/addons/AddonRepository.jsm"); michael@0: Components.utils.import("resource://gre/modules/Services.jsm"); michael@0: michael@0: const Cc = Components.classes; michael@0: const Ci = Components.interfaces; michael@0: michael@0: var gView = null; michael@0: michael@0: function showView(aView) { michael@0: gView = aView; michael@0: michael@0: gView.show(); michael@0: michael@0: // If the view's show method immediately showed a different view then don't michael@0: // do anything else michael@0: if (gView != aView) michael@0: return; michael@0: michael@0: let viewNode = document.getElementById(gView.nodeID); michael@0: viewNode.parentNode.selectedPanel = viewNode; michael@0: michael@0: // For testing dispatch an event when the view changes michael@0: var event = document.createEvent("Events"); michael@0: event.initEvent("ViewChanged", true, true); michael@0: viewNode.dispatchEvent(event); michael@0: } michael@0: michael@0: function showButtons(aCancel, aBack, aNext, aDone) { michael@0: document.getElementById("cancel").hidden = !aCancel; michael@0: document.getElementById("back").hidden = !aBack; michael@0: document.getElementById("next").hidden = !aNext; michael@0: document.getElementById("done").hidden = !aDone; michael@0: } michael@0: michael@0: function isAddonDistroInstalled(aID) { michael@0: let branch = Services.prefs.getBranch("extensions.installedDistroAddon."); michael@0: if (!branch.prefHasUserValue(aID)) michael@0: return false; michael@0: michael@0: return branch.getBoolPref(aID); michael@0: } michael@0: michael@0: function orderForScope(aScope) { michael@0: return aScope == AddonManager.SCOPE_PROFILE ? 1 : 0; michael@0: } michael@0: michael@0: var gAddons = {}; michael@0: michael@0: var gChecking = { michael@0: nodeID: "checking", michael@0: michael@0: _progress: null, michael@0: _addonCount: 0, michael@0: _completeCount: 0, michael@0: michael@0: show: function gChecking_show() { michael@0: showButtons(true, false, false, false); michael@0: this._progress = document.getElementById("checking-progress"); michael@0: michael@0: let self = this; michael@0: AddonManager.getAllAddons(function gChecking_getAllAddons(aAddons) { michael@0: if (aAddons.length == 0) { michael@0: window.close(); michael@0: return; michael@0: } michael@0: michael@0: aAddons = aAddons.filter(function gChecking_filterAddons(aAddon) { michael@0: if (aAddon.type == "plugin" || aAddon.type == "service") michael@0: return false; michael@0: michael@0: if (aAddon.type == "theme") { michael@0: // Don't show application shipped themes michael@0: if (aAddon.scope == AddonManager.SCOPE_APPLICATION) michael@0: return false; michael@0: // Don't show already disabled themes michael@0: if (aAddon.userDisabled) michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: }); michael@0: michael@0: self._addonCount = aAddons.length; michael@0: self._progress.value = 0; michael@0: self._progress.max = aAddons.length; michael@0: self._progress.mode = "determined"; michael@0: michael@0: // Ensure compatibility overrides are up to date before checking for michael@0: // individual addon updates. michael@0: let ids = [addon.id for each (addon in aAddons)]; michael@0: AddonRepository.repopulateCache(ids, function gChecking_repopulateCache() { michael@0: for (let addonItem of aAddons) { michael@0: // Ignore disabled themes michael@0: if (addonItem.type != "theme" || !addonItem.userDisabled) { michael@0: gAddons[addonItem.id] = { michael@0: addon: addonItem, michael@0: install: null, michael@0: wasActive: addonItem.isActive michael@0: } michael@0: } michael@0: michael@0: addonItem.findUpdates(self, AddonManager.UPDATE_WHEN_NEW_APP_INSTALLED); michael@0: } michael@0: }); michael@0: }); michael@0: }, michael@0: michael@0: onUpdateAvailable: function gChecking_onUpdateAvailable(aAddon, aInstall) { michael@0: // If the add-on can be upgraded then remember the new version michael@0: if (aAddon.permissions & AddonManager.PERM_CAN_UPGRADE) michael@0: gAddons[aAddon.id].install = aInstall; michael@0: }, michael@0: michael@0: onUpdateFinished: function gChecking_onUpdateFinished(aAddon, aError) { michael@0: this._completeCount++; michael@0: this._progress.value = this._completeCount; michael@0: michael@0: if (this._completeCount < this._addonCount) michael@0: return; michael@0: michael@0: var addons = [gAddons[id] for (id in gAddons)]; michael@0: michael@0: addons.sort(function sortAddons(a, b) { michael@0: let orderA = orderForScope(a.addon.scope); michael@0: let orderB = orderForScope(b.addon.scope); michael@0: michael@0: if (orderA != orderB) michael@0: return orderA - orderB; michael@0: michael@0: return String.localeCompare(a.addon.name, b.addon.name); michael@0: }); michael@0: michael@0: let rows = document.getElementById("select-rows"); michael@0: let lastAddon = null; michael@0: for (let entry of addons) { michael@0: if (lastAddon && michael@0: orderForScope(entry.addon.scope) != orderForScope(lastAddon.scope)) { michael@0: let separator = document.createElement("separator"); michael@0: rows.appendChild(separator); michael@0: } michael@0: michael@0: let row = document.createElement("row"); michael@0: row.setAttribute("id", entry.addon.id); michael@0: row.setAttribute("class", "addon"); michael@0: rows.appendChild(row); michael@0: row.setAddon(entry.addon, entry.install, entry.wasActive, michael@0: isAddonDistroInstalled(entry.addon.id)); michael@0: michael@0: if (entry.install) michael@0: entry.install.addListener(gUpdate); michael@0: michael@0: lastAddon = entry.addon; michael@0: } michael@0: michael@0: showView(gSelect); michael@0: } michael@0: }; michael@0: michael@0: var gSelect = { michael@0: nodeID: "select", michael@0: michael@0: show: function gSelect_show() { michael@0: this.updateButtons(); michael@0: }, michael@0: michael@0: updateButtons: function gSelect_updateButtons() { michael@0: for (let row = document.getElementById("select-rows").firstChild; michael@0: row; row = row.nextSibling) { michael@0: if (row.localName == "separator") michael@0: continue; michael@0: michael@0: if (row.action) { michael@0: showButtons(false, false, true, false); michael@0: return; michael@0: } michael@0: } michael@0: michael@0: showButtons(false, false, false, true); michael@0: }, michael@0: michael@0: next: function gSelect_next() { michael@0: showView(gConfirm); michael@0: }, michael@0: michael@0: done: function gSelect_done() { michael@0: window.close(); michael@0: } michael@0: }; michael@0: michael@0: var gConfirm = { michael@0: nodeID: "confirm", michael@0: michael@0: show: function gConfirm_show() { michael@0: showButtons(false, true, false, true); michael@0: michael@0: let box = document.getElementById("confirm-scrollbox").firstChild; michael@0: while (box) { michael@0: box.hidden = true; michael@0: while (box.lastChild != box.firstChild) michael@0: box.removeChild(box.lastChild); michael@0: box = box.nextSibling; michael@0: } michael@0: michael@0: for (let row = document.getElementById("select-rows").firstChild; michael@0: row; row = row.nextSibling) { michael@0: if (row.localName == "separator") michael@0: continue; michael@0: michael@0: let action = row.action; michael@0: if (!action) michael@0: continue; michael@0: michael@0: let list = document.getElementById(action + "-list"); michael@0: michael@0: list.hidden = false; michael@0: let item = document.createElement("hbox"); michael@0: item.setAttribute("id", row._addon.id); michael@0: item.setAttribute("class", "addon"); michael@0: item.setAttribute("type", row._addon.type); michael@0: item.setAttribute("name", row._addon.name); michael@0: if (action == "update" || action == "enable") michael@0: item.setAttribute("active", "true"); michael@0: list.appendChild(item); michael@0: michael@0: if (action == "update") michael@0: showButtons(false, true, true, false); michael@0: } michael@0: }, michael@0: michael@0: back: function gConfirm_back() { michael@0: showView(gSelect); michael@0: }, michael@0: michael@0: next: function gConfirm_next() { michael@0: showView(gUpdate); michael@0: }, michael@0: michael@0: done: function gConfirm_done() { michael@0: for (let row = document.getElementById("select-rows").firstChild; michael@0: row; row = row.nextSibling) { michael@0: if (row.localName != "separator") michael@0: row.apply(); michael@0: } michael@0: michael@0: window.close(); michael@0: } michael@0: } michael@0: michael@0: var gUpdate = { michael@0: nodeID: "update", michael@0: michael@0: _progress: null, michael@0: _waitingCount: 0, michael@0: _completeCount: 0, michael@0: _errorCount: 0, michael@0: michael@0: show: function gUpdate_show() { michael@0: showButtons(true, false, false, false); michael@0: michael@0: this._progress = document.getElementById("update-progress"); michael@0: michael@0: for (let row = document.getElementById("select-rows").firstChild; michael@0: row; row = row.nextSibling) { michael@0: if (row.localName != "separator") michael@0: row.apply(); michael@0: } michael@0: michael@0: this._progress.mode = "determined"; michael@0: this._progress.max = this._waitingCount; michael@0: this._progress.value = this._completeCount; michael@0: }, michael@0: michael@0: checkComplete: function gUpdate_checkComplete() { michael@0: this._progress.value = this._completeCount; michael@0: if (this._completeCount < this._waitingCount) michael@0: return; michael@0: michael@0: if (this._errorCount > 0) { michael@0: showView(gErrors); michael@0: return; michael@0: } michael@0: michael@0: window.close(); michael@0: }, michael@0: michael@0: onDownloadStarted: function gUpdate_onDownloadStarted(aInstall) { michael@0: this._waitingCount++; michael@0: }, michael@0: michael@0: onDownloadFailed: function gUpdate_onDownloadFailed(aInstall) { michael@0: this._errorCount++; michael@0: this._completeCount++; michael@0: this.checkComplete(); michael@0: }, michael@0: michael@0: onInstallFailed: function gUpdate_onInstallFailed(aInstall) { michael@0: this._errorCount++; michael@0: this._completeCount++; michael@0: this.checkComplete(); michael@0: }, michael@0: michael@0: onInstallEnded: function gUpdate_onInstallEnded(aInstall) { michael@0: this._completeCount++; michael@0: this.checkComplete(); michael@0: } michael@0: }; michael@0: michael@0: var gErrors = { michael@0: nodeID: "errors", michael@0: michael@0: show: function gErrors_show() { michael@0: showButtons(false, false, false, true); michael@0: }, michael@0: michael@0: done: function gErrors_done() { michael@0: window.close(); michael@0: } michael@0: }; michael@0: michael@0: window.addEventListener("load", function loadEventListener() { michael@0: showView(gChecking); }, false); michael@0: michael@0: // When closing the window cancel any pending or in-progress installs michael@0: window.addEventListener("unload", function unloadEventListener() { michael@0: for (let id in gAddons) { michael@0: let entry = gAddons[id]; michael@0: if (!entry.install) michael@0: return; michael@0: michael@0: aEntry.install.removeListener(gUpdate); michael@0: michael@0: if (entry.install.state != AddonManager.STATE_INSTALLED && michael@0: entry.install.state != AddonManager.STATE_DOWNLOAD_FAILED && michael@0: entry.install.state != AddonManager.STATE_INSTALL_FAILED) { michael@0: entry.install.cancel(); michael@0: } michael@0: } michael@0: }, false);