From 8bc3aacf6722e184a0b2650fab7e1b399463c56a Mon Sep 17 00:00:00 2001 From: Dave Townsend Date: Mon, 26 Jul 2010 12:42:06 -0700 Subject: [PATCH] Bug 553494: Support undo uninstall for restartless and already disabled add-ons. r=Unfocused --- .../mozapps/extensions/content/extensions.js | 61 +- .../mozapps/extensions/content/extensions.xml | 40 +- .../extensions/test/browser/Makefile.in | 1 + .../test/browser/browser_uninstalling.js | 1008 +++++++++++++++++ .../mozapps/extensions/test/browser/head.js | 96 +- 5 files changed, 1192 insertions(+), 14 deletions(-) create mode 100644 toolkit/mozapps/extensions/test/browser/browser_uninstalling.js diff --git a/toolkit/mozapps/extensions/content/extensions.js b/toolkit/mozapps/extensions/content/extensions.js index d1c88c103cf2..7b92798217d5 100644 --- a/toolkit/mozapps/extensions/content/extensions.js +++ b/toolkit/mozapps/extensions/content/extensions.js @@ -133,8 +133,8 @@ function shutdown() { } // Used by external callers to load a specific view into the manager -function loadView(url) { - gViewController.loadView(url); +function loadView(aViewId, aCallback) { + gViewController.loadView(aViewId, aCallback); } var gEventManager = { @@ -253,6 +253,7 @@ var gViewController = { currentViewRequest: 0, previousViewId: "", viewObjects: {}, + viewChangeCallback: null, initialize: function() { this.viewPort = document.getElementById("view-port"); @@ -269,6 +270,8 @@ var gViewController = { }, shutdown: function() { + if (this.currentViewObj) + this.currentViewObj.hide(); this.currentViewRequest = 0; }, @@ -282,7 +285,7 @@ var gViewController = { return this.currentViewObj.node.hasAttribute("loading"); }, - loadView: function(aViewId) { + loadView: function(aViewId, aCallback) { if (aViewId == this.currentViewId) return; @@ -314,6 +317,8 @@ var gViewController = { this.currentViewId = aViewId; this.currentViewObj = viewObj; + this.viewChangeCallback = aCallback; + this.viewPort.selectedPanel = this.currentViewObj.node; this.viewPort.selectedPanel.setAttribute("loading", "true"); this.currentViewObj.show(view.param, ++this.currentViewRequest); @@ -321,6 +326,10 @@ var gViewController = { notifyViewChanged: function() { this.viewPort.selectedPanel.removeAttribute("loading"); + + if (this.viewChangeCallback) + this.viewChangeCallback(); + var event = document.createEvent("Events"); event.initEvent("ViewChanged", true, true); this.currentViewObj.node.dispatchEvent(event); @@ -527,7 +536,14 @@ var gViewController = { return hasPermission(aAddon, "uninstall"); }, doCommand: function(aAddon) { - aAddon.uninstall(); + if (gViewController.currentViewObj != gDetailView) { + aAddon.uninstall(); + return; + } + + gViewController.loadView(gViewController.previousViewId, function() { + gViewController.currentViewObj.getListItemForID(aAddon.id).uninstall(); + }); } }, @@ -1077,7 +1093,15 @@ var gSearchView = { gViewController.updateCommands(); }, - hide: function() { }, + hide: function() { + var listitem = this._listBox.firstChild; + while (listitem) { + if (listitem.getAttribute("status") == "uninstalled" && + !listitem.isPending("uninstall")) + listitem.mAddon.uninstall(); + listitem = listitem.nextSibling; + } + }, getMatchScore: function(aObj, aQuery) { var score = 0; @@ -1144,8 +1168,16 @@ var gSearchView = { if (item) return item.mAddon; return null; - } + }, + getListItemForID: function(aId) { + var listitem = this._listBox.firstChild; + while (listitem) { + if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId) + return listitem; + listitem = listitem.nextSibling; + } + } }; @@ -1213,6 +1245,14 @@ var gListView = { hide: function() { gEventManager.unregisterInstallListener(this); + + var listitem = this._listBox.firstChild; + while (listitem) { + if (listitem.getAttribute("status") == "uninstalled" && + !listitem.isPending("uninstall")) + listitem.mAddon.uninstall(); + listitem = listitem.nextSibling; + } }, showEmptyNotice: function(aShow) { @@ -1266,6 +1306,15 @@ var gListView = { if (item) return item.mAddon; return null; + }, + + getListItemForID: function(aId) { + var listitem = this._listBox.firstChild; + while (listitem) { + if (listitem.getAttribute("status") == "installed" && listitem.mAddon.id == aId) + return listitem; + listitem = listitem.nextSibling; + } } }; diff --git a/toolkit/mozapps/extensions/content/extensions.xml b/toolkit/mozapps/extensions/content/extensions.xml index 7abbee627bf7..1ed373555bc6 100644 --- a/toolkit/mozapps/extensions/content/extensions.xml +++ b/toolkit/mozapps/extensions/content/extensions.xml @@ -711,6 +711,14 @@ ]]> + + + + + + + + + @@ -1031,8 +1045,18 @@ + // If uninstalling does not require a restart then just disable it + // and show the undo UI. + if (!this.opRequiresRestart("uninstall")) { + // This won't update any other add-on manager views (bug 582002) + this.setAttribute("wasDisabled", this.mAddon.userDisabled); + this.setAttribute("restartrequired", false); + this.setAttribute("status", "uninstalled"); + this.mAddon.userDisabled = true; + } else { + this.mAddon.uninstall(); + } + ]]> @@ -1175,9 +1199,17 @@ diff --git a/toolkit/mozapps/extensions/test/browser/Makefile.in b/toolkit/mozapps/extensions/test/browser/Makefile.in index 5e7ca4c5b0f3..c8b59628b4b9 100644 --- a/toolkit/mozapps/extensions/test/browser/Makefile.in +++ b/toolkit/mozapps/extensions/test/browser/Makefile.in @@ -56,6 +56,7 @@ _TEST_FILES = \ browser_searching.xml \ browser_searching_empty.xml \ browser_sorting.js \ + browser_uninstalling.js \ browser_updatessl.js \ browser_updatessl.rdf \ browser_installssl.js \ diff --git a/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js b/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js new file mode 100644 index 000000000000..97b0b05ec6a6 --- /dev/null +++ b/toolkit/mozapps/extensions/test/browser/browser_uninstalling.js @@ -0,0 +1,1008 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Tests that searching for add-ons works correctly + +var gManagerWindow; +var gDocument; +var gCategoryUtilities; +var gProvider; + +function test() { + waitForExplicitFinish(); + + gProvider = new MockProvider(); + + gProvider.createAddons([{ + id: "addon1@tests.mozilla.org", + name: "Uninstall needs restart", + type: "extension", + operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_UNINSTALL + }, { + id: "addon2@tests.mozilla.org", + name: "Uninstall doesn't need restart 1", + type: "extension", + operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE + }, { + id: "addon3@tests.mozilla.org", + name: "Uninstall doesn't need restart 2", + type: "extension", + operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE + }, { + id: "addon4@tests.mozilla.org", + name: "Uninstall doesn't need restart 3", + type: "extension", + operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE + }, { + id: "addon5@tests.mozilla.org", + name: "Uninstall doesn't need restart 4", + type: "extension", + operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE + }]); + + // Turn off searching + Services.prefs.setIntPref("extensions.getAddons.maxResults", 0); + + open_manager(null, function(aWindow) { + gManagerWindow = aWindow; + gDocument = gManagerWindow.document; + gCategoryUtilities = new CategoryUtilities(gManagerWindow); + run_next_test(); + }); +} + +function end_test() { + close_manager(gManagerWindow, function() { + finish(); + }); +} + +function get_item_in_list(aId, aList) { + var item = aList.firstChild; + while (item) { + if ("mAddon" in item && item.mAddon.id == aId) { + aList.ensureElementIsVisible(item); + return item; + } + item = item.nextSibling; + } + return null; +} + +// Tests that uninstalling a normal add-on from the list view can be undone +add_test(function() { + var ID = "addon1@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); +}); + +// Tests that uninstalling a restartless add-on from the list view can be undone +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(aAddon.isActive, "Add-on should be active"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); +}); + +// Tests that uninstalling a disabled restartless add-on from the list view can +// be undone and doesn't re-enable +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + aAddon.userDisabled = true; + + ok(!aAddon.isActive, "Add-on should be inactive"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(!aAddon.isActive, "Add-on should be inactive"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + aAddon.userDisabled = false; + ok(aAddon.isActive, "Add-on should be active"); + + run_next_test(); + }); + }); +}); + +// Tests that uninstalling a normal add-on from the search view can be undone +add_test(function() { + var ID = "addon1@tests.mozilla.org"; + var list = gDocument.getElementById("search-list"); + + var searchBox = gManagerWindow.document.getElementById("header-search"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); +}); + +// Tests that uninstalling a restartless add-on from the search view can be undone +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("search-list"); + + var searchBox = gManagerWindow.document.getElementById("header-search"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(aAddon.isActive, "Add-on should be active"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); +}); + +// Tests that uninstalling a disabled restartless add-on from the search view can +// be undone and doesn't re-enable +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("search-list"); + + var searchBox = gManagerWindow.document.getElementById("header-search"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + AddonManager.getAddonByID(ID, function(aAddon) { + aAddon.userDisabled = true; + + ok(!aAddon.isActive, "Add-on should be inactive"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(!aAddon.isActive, "Add-on should be inactive"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + aAddon.userDisabled = false; + ok(aAddon.isActive, "Add-on should be active"); + + run_next_test(); + }); + }); +}); + +// Tests that uninstalling a normal add-on from the details view switches back +// to the list view and can be undone +add_test(function() { + var ID = "addon1@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + EventUtils.synthesizeMouse(item, 2, 2, { clickCount: 2 }, gManagerWindow); + wait_for_view_load(gManagerWindow, function() { + is(gDocument.getElementById("view-port").selectedPanel.id, "detail-view", "Should be in the detail view"); + + var button = gDocument.getElementById("detail-uninstall"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + // Force XBL to apply + item.clientTop; + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); + }); + }); +}); + +// Tests that uninstalling a restartless add-on from the details view switches +// back to the list view and can be undone +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + EventUtils.synthesizeMouse(item, 2, 2, { clickCount: 2 }, gManagerWindow); + wait_for_view_load(gManagerWindow, function() { + is(gDocument.getElementById("view-port").selectedPanel.id, "detail-view", "Should be in the detail view"); + + var button = gDocument.getElementById("detail-uninstall"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + // Force XBL to apply + item.clientTop; + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(aAddon.isActive, "Add-on should be active"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); + }); + }); +}); + +// Tests that uninstalling a restartless add-on from the details view switches +// back to the list view and can be undone and doesn't re-enable +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + aAddon.userDisabled = true; + + ok(!aAddon.isActive, "Add-on should be inactive"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + EventUtils.synthesizeMouse(item, 2, 2, { clickCount: 2 }, gManagerWindow); + wait_for_view_load(gManagerWindow, function() { + is(gDocument.getElementById("view-port").selectedPanel.id, "detail-view", "Should be in the detail view"); + + var button = gDocument.getElementById("detail-uninstall"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + // Force XBL to apply + item.clientTop; + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + ok(!aAddon.isActive, "Add-on should be inactive"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + aAddon.userDisabled = false; + ok(aAddon.isActive, "Add-on should be active"); + + run_next_test(); + }); + }); + }); + }); +}); + +// Tests that a normal add-on pending uninstall shows up in the list view +add_test(function() { + var ID = "addon1@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + gCategoryUtilities.openType("plugin", function() { + is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to plugin"); + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); + }); + }); +}); + +// Tests that a normal add-on pending uninstall shows up in the search view +add_test(function() { + var ID = "addon1@tests.mozilla.org"; + var list = gDocument.getElementById("search-list"); + + var searchBox = gManagerWindow.document.getElementById("header-search"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL, "Add-on should require a restart to uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + gCategoryUtilities.openType("plugin", function() { + is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to plugin"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should be pending uninstall"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(!button.hidden, "Restart button should not be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + run_next_test(); + }); + }); + }); + }); +}); + +// Tests that switching away from the list view finalises the uninstall of a +// restartless add-on +add_test(function() { + var ID = "addon2@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + gCategoryUtilities.openType("plugin", function() { + is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + is(aAddon, null, "Add-on should no longer be installed"); + + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + var item = get_item_in_list(ID, list); + is(item, null, "Should not have found the add-on in the list"); + + run_next_test(); + }); + }); + }); + }); + }); +}); + +// Tests that switching away from the search view finalises the uninstall of a +// restartless add-on +add_test(function() { + var ID = "addon3@tests.mozilla.org"; + var list = gDocument.getElementById("search-list"); + + var searchBox = gManagerWindow.document.getElementById("header-search"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + gCategoryUtilities.openType("plugin", function() { + is(gCategoryUtilities.selectedCategory, "plugin", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + is(aAddon, null, "Add-on should no longer be installed"); + + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + var item = get_item_in_list(ID, list); + is(item, null, "Should not have found the add-on in the list"); + + run_next_test(); + }); + }); + }); + }); + }); +}); + +// Tests that closing the manager from the list view finalises the uninstall of +// a restartless add-on +add_test(function() { + var ID = "addon4@tests.mozilla.org"; + var list = gDocument.getElementById("addon-list"); + + // Select the extensions category + gCategoryUtilities.openType("extension", function() { + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + close_manager(gManagerWindow, function() { + AddonManager.getAddonByID(ID, function(aAddon) { + is(aAddon, null, "Add-on should no longer be installed"); + + open_manager(null, function(aWindow) { + gManagerWindow = aWindow; + gDocument = gManagerWindow.document; + gCategoryUtilities = new CategoryUtilities(gManagerWindow); + var list = gDocument.getElementById("addon-list"); + + is(gCategoryUtilities.selectedCategory, "extension", "View should have changed to extension"); + + var item = get_item_in_list(ID, list); + is(item, null, "Should not have found the add-on in the list"); + + run_next_test(); + }); + }); + }); + }); + }); +}); + +// Tests that closing the manager from the search view finalises the uninstall +// of a restartless add-on +add_test(function() { + var ID = "addon5@tests.mozilla.org"; + var list = gDocument.getElementById("search-list"); + + var searchBox = gManagerWindow.document.getElementById("header-search"); + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + AddonManager.getAddonByID(ID, function(aAddon) { + ok(aAddon.isActive, "Add-on should be active"); + ok(!(aAddon.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL), "Add-on should not require a restart to uninstall"); + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + + var item = get_item_in_list(ID, list); + isnot(item, null, "Should have found the add-on in the list"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "remove-btn"); + isnot(button, null, "Should have a remove button"); + ok(!button.disabled, "Button should not be disabled"); + + EventUtils.synthesizeMouse(button, 2, 2, { }, gManagerWindow); + + // Force XBL to apply + item.clientTop; + + is(item.getAttribute("status"), "uninstalled", "Add-on should be uninstalling"); + + ok(!(aAddon.pendingOperations & AddonManager.PENDING_UNINSTALL), "Add-on should not be pending uninstall"); + ok(!aAddon.isActive, "Add-on should be inactive"); + + var button = gDocument.getAnonymousElementByAttribute(item, "anonid", "restart-btn"); + isnot(button, null, "Should have a restart button"); + ok(button.hidden, "Restart button should be hidden"); + button = gDocument.getAnonymousElementByAttribute(item, "anonid", "undo-btn"); + isnot(button, null, "Should have an undo button"); + + close_manager(gManagerWindow, function() { + AddonManager.getAddonByID(ID, function(aAddon) { + is(aAddon, null, "Add-on should no longer be installed"); + + open_manager(null, function(aWindow) { + gManagerWindow = aWindow; + gDocument = gManagerWindow.document; + gCategoryUtilities = new CategoryUtilities(gManagerWindow); + var list = gDocument.getElementById("search-list"); + var searchBox = gManagerWindow.document.getElementById("header-search"); + + searchBox.value = "Uninstall"; + + EventUtils.synthesizeMouse(searchBox, 2, 2, { }, gManagerWindow); + EventUtils.synthesizeKey("VK_RETURN", { }, gManagerWindow); + + wait_for_view_load(gManagerWindow, function() { + is(gCategoryUtilities.selectedCategory, "search", "View should have changed to search"); + + var item = get_item_in_list(ID, list); + is(item, null, "Should not have found the add-on in the list"); + + run_next_test(); + }); + }); + }); + }); + }); + }); +}); diff --git a/toolkit/mozapps/extensions/test/browser/head.js b/toolkit/mozapps/extensions/test/browser/head.js index 9281de9b1a41..aecb37743720 100644 --- a/toolkit/mozapps/extensions/test/browser/head.js +++ b/toolkit/mozapps/extensions/test/browser/head.js @@ -282,6 +282,7 @@ MockProvider.prototype = { */ addAddon: function MP_addAddon(aAddon) { this.addons.push(aAddon); + aAddon._provider = this; if (!this.started) return; @@ -292,6 +293,28 @@ MockProvider.prototype = { null, requiresRestart) }, + /** + * Removes an add-on from the list of add-ons that this provider exposes to + * the AddonManager, dispatching the onUninstalled event in the process. + * + * @param aAddon + * The add-on to add + */ + removeAddon: function MP_removeAddon(aAddon) { + var pos = this.addons.indexOf(aAddon); + if (pos == -1) { + ok(false, "Tried to remove an add-on that wasn't registered with the mock provider"); + return; + } + + this.addons.splice(pos, 1); + + if (!this.started) + return; + + AddonManagerPrivate.callAddonListeners("onUninstalled", aAddon); + }, + /** * Adds an add-on install to the list of installs that this provider exposes * to the AddonManager, dispatching appropriate events in the process. @@ -588,12 +611,15 @@ function MockAddon(aId, aName, aType, aOperationsRequiringRestart) { this.providesUpdatesSecurely = true; this.blocklistState = 0; this.appDisabled = false; - this.userDisabled = false; + this._userDisabled = false; this.scope = AddonManager.SCOPE_PROFILE; this.isActive = true; this.creator = ""; this.pendingOperations = 0; - this.permissions = 0; + this.permissions = AddonManager.PERM_CAN_UNINSTALL | + AddonManager.PERM_CAN_ENABLE | + AddonManager.PERM_CAN_DISABLE | + AddonManager.PERM_CAN_UPGRADE; this.operationsRequiringRestart = aOperationsRequiringRestart || (AddonManager.OP_NEEDS_RESTART_INSTALL | AddonManager.OP_NEEDS_RESTART_UNINSTALL | @@ -602,6 +628,26 @@ function MockAddon(aId, aName, aType, aOperationsRequiringRestart) { } MockAddon.prototype = { + get shouldBeActive() { + return !this.appDisabled && !this._userDisabled; + }, + + get userDisabled() { + return this._userDisabled; + }, + + set userDisabled(val) { + if (val == this._userDisabled) + return val; + + var currentActive = this.shouldBeActive; + this._userDisabled = val; + var newActive = this.shouldBeActive; + this._updateActiveState(currentActive, newActive); + + return val; + }, + isCompatibleWith: function(aAppVersion, aPlatformVersion) { return true; }, @@ -611,11 +657,53 @@ MockAddon.prototype = { }, uninstall: function() { - // To be implemented when needed + if (this.pendingOperations & AddonManager.PENDING_UNINSTALL) + throw new Error("Add-on is already pending uninstall"); + + var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_UNINSTALL); + this.pendingOperations |= AddonManager.PENDING_UNINSTALL; + AddonManagerPrivate.callAddonListeners("onUninstalling", this, needsRestart); + if (!needsRestart) { + this.pendingOperations -= AddonManager.PENDING_UNINSTALL; + this._provider.removeAddon(this); + } }, cancelUninstall: function() { - // To be implemented when needed + if (!(this.pendingOperations & AddonManager.PENDING_UNINSTALL)) + throw new Error("Add-on is not pending uninstall"); + + this.pendingOperations -= AddonManager.PENDING_UNINSTALL; + AddonManagerPrivate.callAddonListeners("onOperationCancelled", this); + }, + + _updateActiveState: function(currentActive, newActive) { + if (currentActive == newActive) + return; + + if (newActive == this.isActive) { + AddonManagerPrivate.callAddonListeners("onOperationCancelled", this); + } + else if (newActive) { + var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_ENABLE); + this.pendingOperations |= AddonManager.PENDING_ENABLE; + AddonManagerPrivate.callAddonListeners("onEnabling", this, needsRestart); + if (!needsRestart) { + this.isActive = newActive; + this.pendingOperations -= AddonManager.PENDING_ENABLE; + AddonManagerPrivate.callAddonListeners("onEnabled", this); + } + } + else { + var needsRestart = !!(this.operationsRequiringRestart & AddonManager.OP_NEEDS_RESTART_DISABLE); + this.pendingOperations |= AddonManager.PENDING_DISABLE; + AddonManagerPrivate.callAddonListeners("onDisabling", this, needsRestart); + if (!needsRestart) { + this.isActive = newActive; + this.pendingOperations -= AddonManager.PENDING_DISABLE; + AddonManagerPrivate.callAddonListeners("onDisabled", this); + } + } } };