author | Tim Taubert <ttaubert@mozilla.com> |
Tue, 25 Sep 2012 09:48:00 +0200 | |
changeset 107926 | 08d435dedc7fc19bfad3d31c62daec9013525c6d |
parent 107922 | 654489f4be25f14be4a92e2ff26e650364558bc8 (current diff) |
parent 107925 | d3089bef49eaba3245d8b4c27ea386926cc0179e (diff) |
child 107984 | 7c7639f797d0299c1d1c74c162e5b5721550422b |
child 108032 | 38a87696db4f76b5c71736c086653ee9a3ad196a |
push id | 23523 |
push user | ttaubert@mozilla.com |
push date | Tue, 25 Sep 2012 07:48:19 +0000 |
treeherder | mozilla-central@08d435dedc7f [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
milestone | 18.0a1 |
first release with | nightly linux32
08d435dedc7f
/
18.0a1
/
20120925030547
/
files
nightly linux64
08d435dedc7f
/
18.0a1
/
20120925030547
/
files
nightly mac
08d435dedc7f
/
18.0a1
/
20120925030547
/
files
nightly win32
08d435dedc7f
/
18.0a1
/
20120925030547
/
files
nightly win64
08d435dedc7f
/
18.0a1
/
20120925030547
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
18.0a1
/
20120925030547
/
pushlog to previous
nightly linux64
18.0a1
/
20120925030547
/
pushlog to previous
nightly mac
18.0a1
/
20120925030547
/
pushlog to previous
nightly win32
18.0a1
/
20120925030547
/
pushlog to previous
nightly win64
18.0a1
/
20120925030547
/
pushlog to previous
|
--- a/browser/devtools/debugger/debugger-controller.js +++ b/browser/devtools/debugger/debugger-controller.js @@ -55,17 +55,18 @@ let DebuggerController = { DebuggerView.initializeKeys(); DebuggerView.initializePanes(); DebuggerView.initializeEditor(function() { DebuggerView.GlobalSearch.initialize(); DebuggerView.Scripts.initialize(); DebuggerView.StackFrames.initialize(); DebuggerView.Breakpoints.initialize(); DebuggerView.Properties.initialize(); - DebuggerView.showCloseButton(!this._isRemoteDebugger && !this._isChromeDebugger); + DebuggerView.toggleCloseButton(!this._isRemoteDebugger && + !this._isChromeDebugger); this.dispatchEvent("Debugger:Loaded"); this._connect(); }.bind(this)); }, /** * Destroys the debugger view, disconnects the debugger client and cleans up
--- a/browser/devtools/debugger/debugger-view.js +++ b/browser/devtools/debugger/debugger-view.js @@ -1,16 +1,17 @@ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ "use strict"; const BREAKPOINT_LINE_TOOLTIP_MAX_SIZE = 1000; // chars +const PANES_APPEARANCE_DELAY = 50; // ms const PROPERTY_VIEW_FLASH_DURATION = 400; // ms const GLOBAL_SEARCH_MATCH_FLASH_DURATION = 100; // ms const GLOBAL_SEARCH_URL_MAX_SIZE = 100; // chars const GLOBAL_SEARCH_LINE_MAX_SIZE = 300; // chars const GLOBAL_SEARCH_ACTION_DELAY = 150; // ms const SEARCH_GLOBAL_FLAG = "!"; const SEARCH_LINE_FLAG = ":"; @@ -98,18 +99,18 @@ let DebuggerView = { * Initializes UI properties for all the displayed panes. */ initializePanes: function DV_initializePanes() { this._togglePanesButton.addEventListener("click", this._onTogglePanesButtonPressed); this._stackframesAndBreakpoints.setAttribute("width", Prefs.stackframesWidth); this._variables.setAttribute("width", Prefs.variablesWidth); - this.showStackframesAndBreakpointsPane(Prefs.stackframesPaneVisible); - this.showVariablesPane(Prefs.variablesPaneVisible); + this.toggleStackframesAndBreakpointsPane({ silent: true }); + this.toggleVariablesPane({ silent: true }); }, /** * Initializes the SourceEditor instance. * * @param function aCallback * Called after the editor finishes initializing. */ @@ -168,79 +169,140 @@ let DebuggerView = { DebuggerController.Breakpoints.initialize(); this.editor.focus(); }, /** * Called when the panes toggle button is clicked. */ _onTogglePanesButtonPressed: function DV__onTogglePanesButtonPressed() { - this.showStackframesAndBreakpointsPane( - this._togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), true); - - this.showVariablesPane( - this._togglePanesButton.getAttribute("variablesHidden"), true); + this.toggleStackframesAndBreakpointsPane({ + visible: !!this._togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), + animated: true + }); + this.toggleVariablesPane({ + visible: !!this._togglePanesButton.getAttribute("variablesHidden"), + animated: true + }); + this._onPanesToggle(); }, /** * Sets the close button hidden or visible. It's hidden by default. * @param boolean aVisibleFlag */ - showCloseButton: function DV_showCloseButton(aVisibleFlag) { + toggleCloseButton: function DV_toggleCloseButton(aVisibleFlag) { document.getElementById("close").setAttribute("hidden", !aVisibleFlag); }, /** * Sets the stackframes and breakpoints pane hidden or visible. - * @param boolean aVisibleFlag - * @param boolean aAnimatedFlag + * + * @param object aFlags [optional] + * An object containing some of the following booleans: + * - visible: true if the pane should be shown, false for hidden + * - animated: true to display an animation on toggle + * - silent: true to not update any designated prefs */ - showStackframesAndBreakpointsPane: - function DV_showStackframesAndBreakpointsPane(aVisibleFlag, aAnimatedFlag) { - if (aAnimatedFlag) { + toggleStackframesAndBreakpointsPane: + function DV_toggleStackframesAndBreakpointsPane(aFlags = {}) { + if (aFlags.animated) { this._stackframesAndBreakpoints.setAttribute("animated", ""); } else { this._stackframesAndBreakpoints.removeAttribute("animated"); } - if (aVisibleFlag) { + if (aFlags.visible) { this._stackframesAndBreakpoints.style.marginLeft = "0"; this._togglePanesButton.removeAttribute("stackframesAndBreakpointsHidden"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes")); } else { let margin = parseInt(this._stackframesAndBreakpoints.getAttribute("width")) + 1; this._stackframesAndBreakpoints.style.marginLeft = -margin + "px"; this._togglePanesButton.setAttribute("stackframesAndBreakpointsHidden", "true"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes")); } - Prefs.stackframesPaneVisible = aVisibleFlag; + if (!aFlags.silent) { + Prefs.stackframesPaneVisible = !!aFlags.visible; + } }, /** * Sets the variable spane hidden or visible. - * @param boolean aVisibleFlag - * @param boolean aAnimatedFlag + * + * @param object aFlags [optional] + * An object containing some of the following booleans: + * - visible: true if the pane should be shown, false for hidden + * - animated: true to display an animation on toggle + * - silent: true to not update any designated prefs */ - showVariablesPane: - function DV_showVariablesPane(aVisibleFlag, aAnimatedFlag) { - if (aAnimatedFlag) { + toggleVariablesPane: + function DV_toggleVariablesPane(aFlags = {}) { + if (aFlags.animated) { this._variables.setAttribute("animated", ""); } else { this._variables.removeAttribute("animated"); } - if (aVisibleFlag) { + if (aFlags.visible) { this._variables.style.marginRight = "0"; this._togglePanesButton.removeAttribute("variablesHidden"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("collapsePanes")); } else { let margin = parseInt(this._variables.getAttribute("width")) + 1; this._variables.style.marginRight = -margin + "px"; this._togglePanesButton.setAttribute("variablesHidden", "true"); this._togglePanesButton.setAttribute("tooltiptext", L10N.getStr("expandPanes")); } - Prefs.variablesPaneVisible = aVisibleFlag; + if (!aFlags.silent) { + Prefs.variablesPaneVisible = !!aFlags.visible; + } + }, + + /** + * Shows the stackframes, breakpoints and variable panes if currently hidden + * and the preferences dictate otherwise. + */ + showPanesIfAllowed: function DV_showPanesIfAllowed() { + // Try to keep animations as smooth as possible, so wait a few cycles. + window.setTimeout(function() { + let shown; + + if (Prefs.stackframesPaneVisible && + this._togglePanesButton.getAttribute("stackframesAndBreakpointsHidden")) { + this.toggleStackframesAndBreakpointsPane({ + visible: true, + animated: true, + silent: true + }); + shown = true; + } + if (Prefs.variablesPaneVisible && + this._togglePanesButton.getAttribute("variablesHidden")) { + this.toggleVariablesPane({ + visible: true, + animated: true, + silent: true + }); + shown = true; + } + if (shown) { + this._onPanesToggle(); + } + }.bind(this), PANES_APPEARANCE_DELAY); + }, + + /** + * Displaying the panes may have the effect of triggering scrollbars to + * appear in the source editor, which would render the currently highlighted + * line to appear behind them in some cases. + */ + _onPanesToggle: function DV__onPanesToggle() { + document.addEventListener("transitionend", function onEvent() { + document.removeEventListener("transitionend", onEvent); + DebuggerController.StackFrames.updateEditorLocation(); + }); }, /** * The cached global view elements. */ _togglePanesButton: null, _stackframesAndBreakpoints: null, _stackframes: null, @@ -1620,16 +1682,18 @@ StackFramesView.prototype = { * @return object * The newly created html node representing the added frame. */ addFrame: function DVF_addFrame(aDepth, aFrameNameText, aFrameDetailsText) { // Make sure we don't duplicate anything. if (document.getElementById("stackframe-" + aDepth)) { return null; } + // Stackframes are UI elements which benefit from visible panes. + DebuggerView.showPanesIfAllowed(); let frame = document.createElement("box"); let frameName = document.createElement("label"); let frameDetails = document.createElement("label"); // Create a list item to be added to the stackframes container. frame.id = "stackframe-" + aDepth; frame.className = "dbg-stackframe list-item";
--- a/browser/devtools/debugger/test/browser_dbg_pane-collapse.js +++ b/browser/devtools/debugger/test/browser_dbg_pane-collapse.js @@ -14,42 +14,62 @@ var gView = null; function test() { debug_tab_pane(STACK_URL, function(aTab, aDebuggee, aPane) { gTab = aTab; gDebuggee = aDebuggee; gPane = aPane; gDebugger = gPane.contentWindow; gView = gDebugger.DebuggerView; + testPanesState(); + + gView.toggleStackframesAndBreakpointsPane({ visible: true }); + gView.toggleVariablesPane({ visible: true }); testPaneCollapse1(); testPaneCollapse2(); + closeDebuggerAndFinish(); }); } +function testPanesState() { + let togglePanesButton = + gDebugger.document.getElementById("toggle-panes"); + + ok(togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), + "The stackframes and breakpoints pane should initially be invisible."); + is(gDebugger.Prefs.stackframesPaneVisible, true, + "The stackframes and breakpoints pane should initially be preffed as visible."); + + ok(togglePanesButton.getAttribute("variablesHidden"), + "The stackframes and breakpoints pane should initially be invisible."); + is(gDebugger.Prefs.variablesPaneVisible, true, + "The stackframes and breakpoints pane should initially be preffed as visible."); +} + function testPaneCollapse1() { let stackframesAndBrekpoints = gDebugger.document.getElementById("stackframes+breakpoints"); let togglePanesButton = gDebugger.document.getElementById("toggle-panes"); let width = parseInt(stackframesAndBrekpoints.getAttribute("width")); is(width, gDebugger.Prefs.stackframesWidth, "The stackframes and breakpoints pane has an incorrect width."); is(stackframesAndBrekpoints.style.marginLeft, "0px", - "The stackframes and breakpoints pane has an incorrect initial left margin."); + "The stackframes and breakpoints pane has an incorrect left margin."); ok(!stackframesAndBrekpoints.hasAttribute("animated"), - "The stackframes and breakpoints pane has an incorrect initial animated attribute."); + "The stackframes and breakpoints pane has an incorrect animated attribute."); ok(!togglePanesButton.getAttribute("stackframesAndBreakpointsHidden"), - "The stackframes and breakpoints pane should initially be visible."); + "The stackframes and breakpoints pane should at this point be visible."); is(gDebugger.Prefs.stackframesPaneVisible, true, - "The stackframes and breakpoints pane should initially be visible."); + "The stackframes and breakpoints pane should at this point be visible."); - gView.showStackframesAndBreakpointsPane(false, true); + gView.toggleStackframesAndBreakpointsPane({ visible: false, animated: true }); is(gDebugger.Prefs.stackframesPaneVisible, false, "The stackframes and breakpoints pane should be hidden after collapsing."); let margin = -(width + 1) + "px"; is(width, gDebugger.Prefs.stackframesWidth, "The stackframes and breakpoints pane has an incorrect width after collapsing."); is(stackframesAndBrekpoints.style.marginLeft, margin, @@ -57,17 +77,17 @@ function testPaneCollapse1() { ok(stackframesAndBrekpoints.hasAttribute("animated"), "The stackframes and breakpoints pane has an incorrect attribute after an animated collapsing."); ok(togglePanesButton.hasAttribute("stackframesAndBreakpointsHidden"), "The stackframes and breakpoints pane should not be visible after collapsing."); is(gDebugger.Prefs.stackframesPaneVisible, false, "The stackframes and breakpoints pane should be hidden before uncollapsing."); - gView.showStackframesAndBreakpointsPane(true, false); + gView.toggleStackframesAndBreakpointsPane({ visible: true, animated: false }); is(gDebugger.Prefs.stackframesPaneVisible, true, "The stackframes and breakpoints pane should be visible after uncollapsing."); is(width, gDebugger.Prefs.stackframesWidth, "The stackframes and breakpoints pane has an incorrect width after uncollapsing."); is(stackframesAndBrekpoints.style.marginLeft, "0px", "The stackframes and breakpoints pane has an incorrect left margin after uncollapsing."); @@ -82,26 +102,26 @@ function testPaneCollapse2() { gDebugger.document.getElementById("variables"); let togglePanesButton = gDebugger.document.getElementById("toggle-panes"); let width = parseInt(variables.getAttribute("width")); is(width, gDebugger.Prefs.variablesWidth, "The variables pane has an incorrect width."); is(variables.style.marginRight, "0px", - "The variables pane has an incorrect initial right margin."); + "The variables pane has an incorrect right margin."); ok(!variables.hasAttribute("animated"), - "The variables pane has an incorrect initial animated attribute."); + "The variables pane has an incorrect animated attribute."); ok(!togglePanesButton.getAttribute("variablesHidden"), - "The variables pane should initially be visible."); + "The variables pane should at this point be visible."); is(gDebugger.Prefs.variablesPaneVisible, true, - "The variables pane should initially be visible."); + "The variables pane should at this point be visible."); - gView.showVariablesPane(false, true); + gView.toggleVariablesPane({ visible: false, animated: true }); is(gDebugger.Prefs.variablesPaneVisible, false, "The variables pane should be hidden after collapsing."); let margin = -(width + 1) + "px"; is(width, gDebugger.Prefs.variablesWidth, "The variables pane has an incorrect width after collapsing."); is(variables.style.marginRight, margin, @@ -109,17 +129,17 @@ function testPaneCollapse2() { ok(variables.hasAttribute("animated"), "The variables pane has an incorrect attribute after an animated collapsing."); ok(togglePanesButton.hasAttribute("variablesHidden"), "The variables pane should not be visible after collapsing."); is(gDebugger.Prefs.variablesPaneVisible, false, "The variables pane should be hidden before uncollapsing."); - gView.showVariablesPane(true, false); + gView.toggleVariablesPane({ visible: true, animated: false }); is(gDebugger.Prefs.variablesPaneVisible, true, "The variables pane should be visible after uncollapsing."); is(width, gDebugger.Prefs.variablesWidth, "The variables pane has an incorrect width after uncollapsing."); is(variables.style.marginRight, "0px", "The variables pane has an incorrect right margin after uncollapsing.");
--- a/browser/themes/gnomestripe/devtools/debugger.css +++ b/browser/themes/gnomestripe/devtools/debugger.css @@ -1,16 +1,16 @@ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ #body { - background: -moz-dialog; + background-color: white; } /** * Debugger content */ #dbg-content { padding: 0;
--- a/browser/themes/pinstripe/devtools/debugger.css +++ b/browser/themes/pinstripe/devtools/debugger.css @@ -2,17 +2,17 @@ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ %include ../shared.inc #body { - background: -moz-dialog; + background-color: white; } /** * Debugger content */ #dbg-content { padding: 0;
--- a/browser/themes/winstripe/devtools/debugger.css +++ b/browser/themes/winstripe/devtools/debugger.css @@ -1,16 +1,16 @@ /* -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ft=javascript ts=2 et sw=2 tw=80: */ /* 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/. */ #body { - background: -moz-dialog; + background-color: white; } /** * Debugger content */ #dbg-content { padding: 0;
--- a/toolkit/mozapps/extensions/XPIProvider.jsm +++ b/toolkit/mozapps/extensions/XPIProvider.jsm @@ -2254,33 +2254,23 @@ var XPIProvider = { * true to update add-ons appDisabled property when the application * version has changed * @param aOldAppVersion * The version of the application last run with this profile or null * if it is a new profile or the version is unknown * @param aOldPlatformVersion * The version of the platform last run with this profile or null * if it is a new profile or the version is unknown - * @param aMigrateData - * an object generated from a previous version of the database - * holding information about what add-ons were previously userDisabled - * and updated compatibility information if present - * @param aActiveBundles - * When performing recovery after startup this will be an array of - * persistent descriptors of add-ons that are known to be active, - * otherwise it will be null * @return a boolean indicating if a change requiring flushing the caches was * detected */ processFileChanges: function XPI_processFileChanges(aState, aManifests, aUpdateCompatibility, aOldAppVersion, - aOldPlatformVersion, - aMigrateData, - aActiveBundles) { + aOldPlatformVersion) { let visibleAddons = {}; let oldBootstrappedAddons = this.bootstrappedAddons; this.bootstrappedAddons = {}; /** * Updates an add-on's metadata and determines if a restart of the * application is necessary. This is called when either the add-on's * install directory path or last modified time has changed. @@ -2596,20 +2586,21 @@ var XPIProvider = { let newAddon = null; let sameVersion = false; // Check the updated manifests lists for the install location, If there // is no manifest for the add-on ID then newAddon will be undefined if (aInstallLocation.name in aManifests) newAddon = aManifests[aInstallLocation.name][aId]; - // If we aren't recovering from a corrupt database or we don't have - // migration data for this add-on then this must be a new install. - let isNewInstall = !aActiveBundles && !aMigrateData; - + // If we had staged data for this add-on or we aren't recovering from a + // corrupt database and we don't have migration data for this add-on then + // this must be a new install. + let isNewInstall = (!!newAddon) || (!XPIDatabase.activeBundles && !aMigrateData); + // If it's a new install and we haven't yet loaded the manifest then it // must be something dropped directly into the install location let isDetectedInstall = isNewInstall && !newAddon; // Load the manifest if necessary and sanity check the add-on ID try { if (!newAddon) { // Load the manifest from the add-on. @@ -2679,24 +2670,24 @@ var XPIProvider = { if (isDetectedInstall && newAddon.foreignInstall) { // If the add-on is a foreign install and is in a scope where add-ons // that were dropped in should default to disabled then disable it let disablingScopes = Prefs.getIntPref(PREF_EM_AUTO_DISABLED_SCOPES, 0); if (aInstallLocation.scope & disablingScopes) newAddon.userDisabled = true; } - if (aActiveBundles) { - // If we have a list of what add-ons should be marked as active then use - // it to guess at migration data + // If we have a list of what add-ons should be marked as active then use + // it to guess at migration data. + if (!isNewInstall && XPIDatabase.activeBundles) { // For themes we know which is active by the current skin setting if (newAddon.type == "theme") newAddon.active = newAddon.internalName == XPIProvider.currentSkin; else - newAddon.active = aActiveBundles.indexOf(aAddonState.descriptor) != -1; + newAddon.active = XPIDatabase.activeBundles.indexOf(aAddonState.descriptor) != -1; // If the add-on wasn't active and it isn't already disabled in some way // then it was probably either softDisabled or userDisabled if (!newAddon.active && newAddon.visible && !isAddonDisabled(newAddon)) { // If the add-on is softblocked then assume it is softDisabled if (newAddon.blocklistState == Ci.nsIBlocklistService.STATE_SOFTBLOCKED) newAddon.softDisabled = true; else @@ -2744,19 +2735,20 @@ var XPIProvider = { let installReason = BOOTSTRAP_REASONS.ADDON_INSTALL; // If we're hiding a bootstrapped add-on then call its uninstall method if (newAddon.id in oldBootstrappedAddons) { let oldBootstrap = oldBootstrappedAddons[newAddon.id]; XPIProvider.bootstrappedAddons[newAddon.id] = oldBootstrap; - // If the old version is the same as the new version, don't call - // uninstall and install methods. - if (sameVersion) + // If the old version is the same as the new version, or we're + // recovering from a corrupt DB, don't call uninstall and install + // methods. + if (sameVersion || !isNewInstall) return false; installReason = Services.vc.compare(oldBootstrap.version, newAddon.version) < 0 ? BOOTSTRAP_REASONS.ADDON_UPGRADE : BOOTSTRAP_REASONS.ADDON_DOWNGRADE; let oldAddonFile = Cc["@mozilla.org/file/local;1"]. createInstance(Ci.nsIFile); @@ -2853,18 +2845,18 @@ var XPIProvider = { } }, this); } // All the remaining add-ons in this install location must be new. // Get the migration data for this install location. let locMigrateData = {}; - if (aMigrateData && installLocation.name in aMigrateData) - locMigrateData = aMigrateData[installLocation.name]; + if (XPIDatabase.migrateData && installLocation.name in XPIDatabase.migrateData) + locMigrateData = XPIDatabase.migrateData[installLocation.name]; for (let id in addonStates) { changed = addMetadata(installLocation, id, addonStates[id], locMigrateData[id]) || changed; } }, this); // The remaining locations that had add-ons installed in them no longer // have any add-ons installed in them, or the locations no longer exist. @@ -2876,16 +2868,19 @@ var XPIProvider = { changed = removeMetadata(aLocation, aOldAddon) || changed; }, this); }, this); // Cache the new install location states let cache = JSON.stringify(this.getInstallLocationStates()); Services.prefs.setCharPref(PREF_INSTALL_CACHE, cache); + // Clear out any cached migration data. + XPIDatabase.migrateData = null; + return changed; }, /** * Imports the xpinstall permissions from preferences into the permissions * manager for the user to change later. */ importPermissions: function XPI_importPermissions() { @@ -2938,44 +2933,48 @@ var XPIProvider = { this.importPermissions(); // If the application version has changed then the database information // needs to be updated let updateDatabase = aAppChanged; // Load the list of bootstrapped add-ons first so processFileChanges can // modify it - this.bootstrappedAddons = JSON.parse(Prefs.getCharPref(PREF_BOOTSTRAP_ADDONS, - "{}")); + try { + this.bootstrappedAddons = JSON.parse(Prefs.getCharPref(PREF_BOOTSTRAP_ADDONS, + "{}")); + } catch (e) { + WARN("Error parsing enabled bootstrapped extensions cache", e); + } // First install any new add-ons into the locations, if there are any // changes then we must update the database with the information in the // install locations let manifests = {}; - updateDatabase = this.processPendingFileChanges(manifests) | updateDatabase; + updateDatabase = this.processPendingFileChanges(manifests) || updateDatabase; // This will be true if the previous session made changes that affect the // active state of add-ons but didn't commit them properly (normally due // to the application crashing) let hasPendingChanges = Prefs.getBoolPref(PREF_PENDING_OPERATIONS); // If the schema appears to have changed then we should update the database - updateDatabase |= DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0); + updateDatabase = updateDatabase || DB_SCHEMA != Prefs.getIntPref(PREF_DB_SCHEMA, 0); // If the application has changed then check for new distribution add-ons if (aAppChanged !== false && Prefs.getBoolPref(PREF_INSTALL_DISTRO_ADDONS, true)) - updateDatabase = this.installDistributionAddons(manifests) | updateDatabase; + updateDatabase = this.installDistributionAddons(manifests) || updateDatabase; let state = this.getInstallLocationStates(); if (!updateDatabase) { // If the state has changed then we must update the database let cache = Prefs.getCharPref(PREF_INSTALL_CACHE, null); - updateDatabase |= cache != JSON.stringify(state); + updateDatabase = cache != JSON.stringify(state); } // If the database doesn't exist and there are add-ons installed then we // must update the database however if there are no add-ons then there is // no need to update the database. // Avoid using XPIDatabase.dbFileExists, as that code is lazy-loaded, // and we want to avoid loading it until absolutely necessary. let dbFile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true); @@ -3004,24 +3003,23 @@ var XPIProvider = { let transationBegun = false; try { let extensionListChanged = false; // If the database needs to be updated then open it and then update it // from the filesystem if (updateDatabase || hasPendingChanges) { XPIDatabase.beginTransaction(); transationBegun = true; - let migrateData = XPIDatabase.openConnection(false, true); + XPIDatabase.openConnection(false, true); try { extensionListChanged = this.processFileChanges(state, manifests, aAppChanged, aOldAppVersion, - aOldPlatformVersion, - migrateData, null); + aOldPlatformVersion); } catch (e) { ERROR("Error processing file changes", e); } } if (aAppChanged) { // When upgrading the app and using a custom skin make sure it is still
--- a/toolkit/mozapps/extensions/XPIProviderUtils.js +++ b/toolkit/mozapps/extensions/XPIProviderUtils.js @@ -304,16 +304,20 @@ var XPIDatabase = { statementCache: {}, // A cache of weak referenced DBAddonInternals so we can reuse objects where // possible addonCache: [], // The nested transaction count transactionCount: 0, // The database file dbfile: FileUtils.getFile(KEY_PROFILEDIR, [FILE_DATABASE], true), + // Migration data loaded from an old version of the database. + migrateData: null, + // Active add-on directories loaded from extensions.ini and prefs at startup. + activeBundles: null, // The statements used by the database statements: { _getDefaultLocale: "SELECT id, name, description, creator, homepageURL " + "FROM locale WHERE id=:id", _getLocales: "SELECT addon_locale.locale, locale.id, locale.name, " + "locale.description, locale.creator, locale.homepageURL " + "FROM addon_locale JOIN locale ON " + @@ -475,31 +479,38 @@ var XPIDatabase = { // Attempt to open the database try { connection = Services.storage.openUnsharedDatabase(aDBFile); this.dbfileExists = true; } catch (e) { ERROR("Failed to open database (1st attempt)", e); - try { - aDBFile.remove(true); - } - catch (e) { - ERROR("Failed to remove database that could not be opened", e); + // If the database was locked for some reason then assume it still + // has some good data and we should try to load it the next time around. + if (e.result != Cr.NS_ERROR_STORAGE_BUSY) { + try { + aDBFile.remove(true); + } + catch (e) { + ERROR("Failed to remove database that could not be opened", e); + } + try { + connection = Services.storage.openUnsharedDatabase(aDBFile); + } + catch (e) { + ERROR("Failed to open database (2nd attempt)", e); + + // If we have got here there seems to be no way to open the real + // database, instead open a temporary memory database so things will + // work for this session. + return Services.storage.openSpecialDatabase("memory"); + } } - try { - connection = Services.storage.openUnsharedDatabase(aDBFile); - } - catch (e) { - ERROR("Failed to open database (2nd attempt)", e); - - // If we have got here there seems to be no way to open the real - // database, instead open a temporary memory database so things will - // work for this session + else { return Services.storage.openSpecialDatabase("memory"); } } connection.executeSimpleSQL("PRAGMA synchronous = FULL"); connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); return connection; @@ -518,30 +529,30 @@ var XPIDatabase = { delete this.connection; if (!aForceOpen && !this.dbfileExists) { this.connection = null; return {}; } this.initialized = true; + this.migrateData = null; this.connection = this.openDatabaseFile(this.dbfile); - let migrateData = null; // If the database was corrupt or missing then the new blank database will // have a schema version of 0. let schemaVersion = this.connection.schemaVersion; if (schemaVersion != DB_SCHEMA) { // A non-zero schema version means that a schema has been successfully // created in the database in the past so we might be able to get useful // information from it if (schemaVersion != 0) { LOG("Migrating data from schema " + schemaVersion); - migrateData = this.getMigrateDataFromDatabase(); + this.migrateData = this.getMigrateDataFromDatabase(); // Delete the existing database this.connection.close(); try { if (this.dbfileExists) this.dbfile.remove(true); // Reopen an empty database @@ -557,64 +568,62 @@ var XPIDatabase = { else { let dbSchema = 0; try { dbSchema = Services.prefs.getIntPref(PREF_DB_SCHEMA); } catch (e) {} if (dbSchema == 0) { // Only migrate data from the RDF if we haven't done it before - migrateData = this.getMigrateDataFromRDF(); + this.migrateData = this.getMigrateDataFromRDF(); } } // At this point the database should be completely empty - this.createSchema(); + try { + this.createSchema(); + } + catch (e) { + // If creating the schema fails, then the database is unusable, + // fall back to an in-memory database. + this.connection = Services.storage.openSpecialDatabase("memory"); + } + + // If there is no migration data then load the list of add-on directories + // that were active during the last run + if (!this.migrateData) + this.activeBundles = this.getActiveBundles(); if (aRebuildOnError) { - let activeBundles = this.getActiveBundles(); WARN("Rebuilding add-ons database from installed extensions."); this.beginTransaction(); try { let state = XPIProvider.getInstallLocationStates(); - XPIProvider.processFileChanges(state, {}, false, undefined, undefined, - migrateData, activeBundles) + XPIProvider.processFileChanges(state, {}, false); // Make sure to update the active add-ons and add-ons list on shutdown Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true); this.commitTransaction(); } catch (e) { ERROR("Error processing file changes", e); this.rollbackTransaction(); } } } // If the database connection has a file open then it has the right schema - // by now so make sure the preferences reflect that. If not then there is - // an in-memory database open which means a problem opening and deleting the - // real database, clear the schema preference to force trying to load the - // database on the next startup + // by now so make sure the preferences reflect that. if (this.connection.databaseFile) { Services.prefs.setIntPref(PREF_DB_SCHEMA, DB_SCHEMA); + Services.prefs.savePrefFile(null); } - else { - try { - Services.prefs.clearUserPref(PREF_DB_SCHEMA); - } - catch (e) { - // The preference may not be defined - } - } - Services.prefs.savePrefFile(null); // Begin any pending transactions for (let i = 0; i < this.transactionCount; i++) this.connection.executeSimpleSQL("SAVEPOINT 'default'"); - return migrateData; }, /** * A lazy getter for the database connection. */ get connection() { this.openConnection(true); return this.connection; @@ -629,50 +638,63 @@ var XPIDatabase = { * @return an array of persisitent descriptors for the directories */ getActiveBundles: function XPIDB_getActiveBundles() { let bundles = []; let addonsList = FileUtils.getFile(KEY_PROFILEDIR, [FILE_XPI_ADDONS_LIST], true); - let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"]. - getService(Ci.nsIINIParserFactory); - let parser = iniFactory.createINIParser(addonsList); + if (!addonsList.exists()) + return null; - let keys = parser.getKeys("ExtensionDirs"); + try { + let iniFactory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"] + .getService(Ci.nsIINIParserFactory); + let parser = iniFactory.createINIParser(addonsList); + let keys = parser.getKeys("ExtensionDirs"); - while (keys.hasMore()) - bundles.push(parser.getString("ExtensionDirs", keys.getNext())); + while (keys.hasMore()) + bundles.push(parser.getString("ExtensionDirs", keys.getNext())); + } + catch (e) { + WARN("Failed to parse extensions.ini", e); + return null; + } // Also include the list of active bootstrapped extensions for (let id in XPIProvider.bootstrappedAddons) bundles.push(XPIProvider.bootstrappedAddons[id].descriptor); return bundles; }, /** * Retrieves migration data from the old extensions.rdf database. * * @return an object holding information about what add-ons were previously * userDisabled and any updated compatibility information */ getMigrateDataFromRDF: function XPIDB_getMigrateDataFromRDF(aDbWasMissing) { - let migrateData = {}; // Migrate data from extensions.rdf let rdffile = FileUtils.getFile(KEY_PROFILEDIR, [FILE_OLD_DATABASE], true); - if (rdffile.exists()) { - LOG("Migrating data from extensions.rdf"); + if (!rdffile.exists()) + return null; + + LOG("Migrating data from " + FILE_OLD_DATABASE); + let migrateData = {}; + + try { let ds = gRDF.GetDataSourceBlocking(Services.io.newFileURI(rdffile).spec); let root = Cc["@mozilla.org/rdf/container;1"]. createInstance(Ci.nsIRDFContainer); root.Init(ds, gRDF.GetResource(RDFURI_ITEM_ROOT)); let elements = root.GetElements(); + while (elements.hasMoreElements()) { let source = elements.getNext().QueryInterface(Ci.nsIRDFResource); let location = getRDFProperty(ds, source, "installLocation"); if (location) { if (!(location in migrateData)) migrateData[location] = {}; let id = source.ValueUTF8.substring(PREFIX_ITEM_URI.length); @@ -704,16 +726,20 @@ var XPIDatabase = { appInfo.minVersion = getRDFProperty(ds, targetApp, "minVersion"); appInfo.maxVersion = getRDFProperty(ds, targetApp, "maxVersion"); } migrateData[location][id].targetApplications.push(appInfo); } } } } + catch (e) { + WARN("Error reading " + FILE_OLD_DATABASE, e); + migrateData = null; + } return migrateData; }, /** * Retrieves migration data from a database that has an older or newer schema. * * @return an object holding information about what add-ons were previously @@ -742,17 +768,17 @@ var XPIDatabase = { } else if (DB_BOOL_METADATA.indexOf(row.name) != -1) { props.push(row.name); } } if (reqCount < REQUIRED.length) { ERROR("Unable to read anything useful from the database"); - return migrateData; + return null; } stmt.finalize(); stmt = this.connection.createStatement("SELECT " + props.join(",") + " FROM addon"); for (let row in resultRows(stmt)) { if (!(row.location in migrateData)) migrateData[row.location] = {}; let addonData = { @@ -785,16 +811,17 @@ var XPIDatabase = { }); } } } } catch (e) { // An error here means the schema is too different to read ERROR("Error migrating data", e); + return null; } finally { if (taStmt) taStmt.finalize(); if (stmt) stmt.finalize(); } @@ -813,16 +840,21 @@ var XPIDatabase = { this.addonCache = []; if (this.transactionCount > 0) { ERROR(this.transactionCount + " outstanding transactions, rolling back."); while (this.transactionCount > 0) this.rollbackTransaction(); } + // If we are running with an in-memory database then force a new + // extensions.ini to be written to disk on the next startup + if (!this.connection.databaseFile) + Services.prefs.setBoolPref(PREF_PENDING_OPERATIONS, true); + this.initialized = false; let connection = this.connection; delete this.connection; // Re-create the connection smart getter to allow the database to be // re-loaded during testing. this.__defineGetter__("connection", function() { this.openConnection(true);
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/addons/test_locked2_5/install.rdf @@ -0,0 +1,23 @@ +<?xml version="1.0"?> + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + <em:id>addon5@tests.mozilla.org</em:id> + <em:version>2.0</em:version> + + <!-- Front End MetaData --> + <em:name>Test 5</em:name> + <em:description>Test Description</em:description> + + <em:targetApplication> + <Description> + <em:id>xpcshell@tests.mozilla.org</em:id> + <em:minVersion>2</em:minVersion> + <em:maxVersion>2</em:maxVersion> + </Description> + </em:targetApplication> + + </Description> +</RDF>
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/addons/test_locked2_6/install.rdf @@ -0,0 +1,23 @@ +<?xml version="1.0"?> + +<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#" + xmlns:em="http://www.mozilla.org/2004/em-rdf#"> + + <Description about="urn:mozilla:install-manifest"> + <em:id>addon6@tests.mozilla.org</em:id> + <em:version>1.0</em:version> + + <!-- Front End MetaData --> + <em:name>Test 6</em:name> + <em:description>Test Description</em:description> + + <em:targetApplication> + <Description> + <em:id>xpcshell@tests.mozilla.org</em:id> + <em:minVersion>2</em:minVersion> + <em:maxVersion>2</em:maxVersion> + </Description> + </em:targetApplication> + + </Description> +</RDF>
--- a/toolkit/mozapps/extensions/test/xpcshell/test_locked.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked.js @@ -147,16 +147,19 @@ function run_test() { testserver = new HttpServer(); testserver.registerDirectory("/addons/", do_get_file("addons")); testserver.registerDirectory("/data/", do_get_file("data")); testserver.start(4444); // Startup the profile and setup the initial state startupManager(); + // New profile so new add-ons are ignored + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", "addon7@tests.mozilla.org", "theme2@tests.mozilla.org"], function([a2, a3, a4, a7, t2]) { // Set up the initial state a2.userDisabled = true; @@ -193,40 +196,45 @@ function run_test_1() { "theme2@tests.mozilla.org"], function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) { do_check_neq(a1, null); do_check_true(a1.isActive); do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_true(a5.isActive); do_check_false(a5.userDisabled); do_check_false(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); do_check_false(a6.userDisabled); do_check_false(a6.appDisabled); do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(a7, null); @@ -235,30 +243,40 @@ function run_test_1() { do_check_false(a7.appDisabled); do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(t1, null); do_check_false(t1.isActive); do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - // After restarting the database won't be open so lock the file for writing - restartManager(); + // After shutting down the database won't be open so we can lock it + shutdownManager(); var dbfile = gProfD.clone(); dbfile.append("extensions.sqlite"); - var fstream = AM_Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(AM_Ci.nsIFileOutputStream); - fstream.init(dbfile, FileUtils.MODE_TRUNCATE | FileUtils.MODE_WRONLY, FileUtils.PERMS_FILE, 0); + let connection = Services.storage.openUnsharedDatabase(dbfile); + connection.executeSimpleSQL("PRAGMA synchronous = FULL"); + connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); + // Force the DB to become locked + connection.beginTransactionAs(connection.TRANSACTION_EXCLUSIVE); + connection.commitTransaction(); + + startupManager(false); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); // Accessing the add-ons should open and recover the database AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", "addon5@tests.mozilla.org", "addon6@tests.mozilla.org", @@ -268,47 +286,52 @@ function run_test_1() { a4, a5, a6, a7, t1, t2]) { // Should be correctly recovered do_check_neq(a1, null); do_check_true(a1.isActive); do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); // Should be correctly recovered do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); // The compatibility update won't be recovered but it should still be // active for this session do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); // The compatibility update won't be recovered and with strict // compatibility it would not have been able to tell that it was // previously userDisabled. However, without strict compat, it wasn't // appDisabled, so it knows it must have been userDisabled. do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_true(a5.isActive); do_check_false(a5.userDisabled); do_check_false(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); do_check_false(a6.userDisabled); do_check_false(a6.appDisabled); do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(a7, null); @@ -318,69 +341,79 @@ function run_test_1() { do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); // Should be correctly recovered do_check_neq(t1, null); do_check_false(t1.isActive); do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); // Should be correctly recovered do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); // Restarting will actually apply changes to extensions.ini which will // then be put into the in-memory database when we next fail to load the // real thing restartManager(); + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", "addon5@tests.mozilla.org", "addon6@tests.mozilla.org", "addon7@tests.mozilla.org", "theme1@tests.mozilla.org", "theme2@tests.mozilla.org"], function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) { do_check_neq(a1, null); do_check_true(a1.isActive); do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_true(a5.isActive); do_check_false(a5.userDisabled); do_check_false(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); do_check_false(a6.userDisabled); do_check_false(a6.appDisabled); do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(a7, null); @@ -389,21 +422,104 @@ function run_test_1() { do_check_false(a7.appDisabled); do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(t1, null); do_check_false(t1.isActive); do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - fstream.close(); - end_test(); + connection.close(); + + // After allowing access to the original DB things should go back to as + // they were previously + restartManager(); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org", + "addon5@tests.mozilla.org", + "addon6@tests.mozilla.org", + "addon7@tests.mozilla.org", + "theme1@tests.mozilla.org", + "theme2@tests.mozilla.org"], function([a1, a2, a3, + a4, a5, a6, + a7, t1, t2]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_true(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_true(a3.isActive); + do_check_false(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_neq(a4, null); + do_check_false(a4.isActive); + do_check_true(a4.userDisabled); + do_check_false(a4.appDisabled); + do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); + + do_check_neq(a5, null); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(a7, null); + do_check_false(a7.isActive); + do_check_true(a7.userDisabled); + do_check_false(a7.appDisabled); + do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(t1, null); + do_check_false(t1.isActive); + do_check_true(t1.userDisabled); + do_check_false(t1.appDisabled); + do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); + + do_check_neq(t2, null); + do_check_true(t2.isActive); + do_check_false(t2.userDisabled); + do_check_false(t2.appDisabled); + do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); + + end_test(); + }); }); }); }); }
new file mode 100644 --- /dev/null +++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked2.js @@ -0,0 +1,272 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + */ + +// Checks that we handle a locked database when there are extension changes +// in progress + +// Will be left alone +var addon1 = { + id: "addon1@tests.mozilla.org", + version: "1.0", + name: "Test 1", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +// Will be enabled +var addon2 = { + id: "addon2@tests.mozilla.org", + version: "1.0", + name: "Test 2", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +// Will be disabled +var addon3 = { + id: "addon3@tests.mozilla.org", + version: "1.0", + name: "Test 3", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +// Will be uninstalled +var addon4 = { + id: "addon4@tests.mozilla.org", + version: "1.0", + name: "Test 4", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + + +// Will be updated +var addon5 = { + id: "addon5@tests.mozilla.org", + version: "1.0", + name: "Test 5", + targetApplications: [{ + id: "xpcshell@tests.mozilla.org", + minVersion: "2", + maxVersion: "2" + }] +}; + +const profileDir = gProfD.clone(); +profileDir.append("extensions"); + +function run_test() { + do_test_pending(); + createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "2", "2"); + + writeInstallRDFForExtension(addon1, profileDir); + writeInstallRDFForExtension(addon2, profileDir); + writeInstallRDFForExtension(addon3, profileDir); + writeInstallRDFForExtension(addon4, profileDir); + writeInstallRDFForExtension(addon5, profileDir); + + // Make it look like add-on 5 was installed some time in the past so the update is + // detected + setExtensionModifiedTime(getFileForAddon(profileDir, addon5.id), Date.now() - (60000)); + + // Startup the profile and setup the initial state + startupManager(); + + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []); + + AddonManager.getAddonByID("addon2@tests.mozilla.org", function(a2) { + a2.userDisabled = true; + + restartManager(); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org", + "addon5@tests.mozilla.org"], + function([a1, a2, a3, a4, a5]) { + a2.userDisabled = false; + a3.userDisabled = true; + a4.uninstall(); + + installAllFiles([do_get_addon("test_locked2_5"), + do_get_addon("test_locked2_6")], function() { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_false(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_ENABLE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_true(a3.isActive); + do_check_true(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_neq(a4, null); + do_check_true(a4.isActive); + do_check_false(a4.userDisabled); + do_check_false(a4.appDisabled); + do_check_eq(a4.pendingOperations, AddonManager.PENDING_UNINSTALL); + do_check_true(isExtensionInAddonsList(profileDir, a4.id)); + + do_check_neq(a5, null); + do_check_eq(a5.version, "1.0"); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_UPGRADE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + // After shutting down the database won't be open so we can lock it + shutdownManager(); + var dbfile = gProfD.clone(); + dbfile.append("extensions.sqlite"); + let connection = Services.storage.openUnsharedDatabase(dbfile); + connection.executeSimpleSQL("PRAGMA synchronous = FULL"); + connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); + // Force the DB to become locked + connection.beginTransactionAs(connection.TRANSACTION_EXCLUSIVE); + connection.commitTransaction(); + + startupManager(false); + + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, []); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org", + "addon5@tests.mozilla.org", + "addon6@tests.mozilla.org"], + function([a1, a2, a3, a4, a5, a6]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_true(a2.isActive); + do_check_false(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_false(a3.isActive); + do_check_true(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_eq(a4, null); + + do_check_neq(a5, null); + do_check_eq(a5.version, "2.0"); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a6.id)); + + connection.close(); + + // After allowing access to the original DB things should still be + // applied correctly + restartManager(); + + // These things happened when we had no access to the database so + // they are seen as external changes when we get the database back :( + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, ["addon6@tests.mozilla.org"]); + check_startup_changes(AddonManager.STARTUP_CHANGE_UNINSTALLED, ["addon4@tests.mozilla.org"]); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org", + "addon5@tests.mozilla.org", + "addon6@tests.mozilla.org"], + function([a1, a2, a3, a4, a5, a6]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_true(a2.isActive); + do_check_false(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_false(a3.isActive); + do_check_true(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_eq(a4, null); + + do_check_neq(a5, null); + do_check_eq(a5.version, "2.0"); + do_check_true(a5.isActive); + do_check_false(a5.userDisabled); + do_check_false(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a6.id)); + + end_test(); + }); + }); + }); + }); + }); +} + +function end_test() { + do_test_finished(); +}
--- a/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js +++ b/toolkit/mozapps/extensions/test/xpcshell/test_locked_strictcompat.js @@ -147,16 +147,19 @@ function run_test() { testserver = new HttpServer(); testserver.registerDirectory("/addons/", do_get_file("addons")); testserver.registerDirectory("/data/", do_get_file("data")); testserver.start(4444); // Startup the profile and setup the initial state startupManager(); + // New profile so new add-ons are ignored + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", "addon7@tests.mozilla.org", "theme2@tests.mozilla.org"], function([a2, a3, a4, a7, t2]) { // Set up the initial state a2.userDisabled = true; @@ -193,40 +196,45 @@ function run_test_1() { "theme2@tests.mozilla.org"], function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) { do_check_neq(a1, null); do_check_true(a1.isActive); do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_false(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_true(a4.userDisabled); do_check_false(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_false(a5.isActive); do_check_false(a5.userDisabled); do_check_true(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); do_check_false(a6.userDisabled); do_check_false(a6.appDisabled); do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(a7, null); @@ -235,30 +243,40 @@ function run_test_1() { do_check_false(a7.appDisabled); do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(t1, null); do_check_false(t1.isActive); do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - // After restarting the database won't be open so lock the file for writing - restartManager(); + // After shutting down the database won't be open so we can lock it + shutdownManager(); var dbfile = gProfD.clone(); dbfile.append("extensions.sqlite"); - var fstream = AM_Cc["@mozilla.org/network/file-output-stream;1"]. - createInstance(AM_Ci.nsIFileOutputStream); - fstream.init(dbfile, FileUtils.MODE_TRUNCATE | FileUtils.MODE_WRONLY, FileUtils.PERMS_FILE, 0); + let connection = Services.storage.openUnsharedDatabase(dbfile); + connection.executeSimpleSQL("PRAGMA synchronous = FULL"); + connection.executeSimpleSQL("PRAGMA locking_mode = EXCLUSIVE"); + // Force the DB to become locked + connection.beginTransactionAs(connection.TRANSACTION_EXCLUSIVE); + connection.commitTransaction(); + + startupManager(false); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); // Accessing the add-ons should open and recover the database AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", "addon5@tests.mozilla.org", "addon6@tests.mozilla.org", @@ -268,45 +286,50 @@ function run_test_1() { a4, a5, a6, a7, t1, t2]) { // Should be correctly recovered do_check_neq(a1, null); do_check_true(a1.isActive); do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); // Should be correctly recovered do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); // The compatibility update won't be recovered but it should still be // active for this session do_check_neq(a3, null); do_check_true(a3.isActive); do_check_false(a3.userDisabled); do_check_true(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_DISABLE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); // The compatibility update won't be recovered and it will not have been // able to tell that it was previously userDisabled do_check_neq(a4, null); do_check_false(a4.isActive); do_check_false(a4.userDisabled); do_check_true(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_false(a5.isActive); do_check_false(a5.userDisabled); do_check_true(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); do_check_false(a6.userDisabled); do_check_false(a6.appDisabled); do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(a7, null); @@ -316,69 +339,79 @@ function run_test_1() { do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); // Should be correctly recovered do_check_neq(t1, null); do_check_false(t1.isActive); do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); // Should be correctly recovered do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); // Restarting will actually apply changes to extensions.ini which will // then be put into the in-memory database when we next fail to load the // real thing restartManager(); + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", "addon2@tests.mozilla.org", "addon3@tests.mozilla.org", "addon4@tests.mozilla.org", "addon5@tests.mozilla.org", "addon6@tests.mozilla.org", "addon7@tests.mozilla.org", "theme1@tests.mozilla.org", "theme2@tests.mozilla.org"], function([a1, a2, a3, a4, a5, a6, a7, t1, t2]) { do_check_neq(a1, null); do_check_true(a1.isActive); do_check_false(a1.userDisabled); do_check_false(a1.appDisabled); do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); do_check_neq(a2, null); do_check_false(a2.isActive); do_check_true(a2.userDisabled); do_check_false(a2.appDisabled); do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); do_check_neq(a3, null); do_check_false(a3.isActive); do_check_false(a3.userDisabled); do_check_true(a3.appDisabled); do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a3.id)); do_check_neq(a4, null); do_check_false(a4.isActive); do_check_false(a4.userDisabled); do_check_true(a4.appDisabled); do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); do_check_neq(a5, null); do_check_false(a5.isActive); do_check_false(a5.userDisabled); do_check_true(a5.appDisabled); do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); do_check_neq(a6, null); do_check_true(a6.isActive); do_check_false(a6.userDisabled); do_check_false(a6.appDisabled); do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(a7, null); @@ -387,21 +420,104 @@ function run_test_1() { do_check_false(a7.appDisabled); do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); do_check_neq(t1, null); do_check_false(t1.isActive); do_check_true(t1.userDisabled); do_check_false(t1.appDisabled); do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); do_check_neq(t2, null); do_check_true(t2.isActive); do_check_false(t2.userDisabled); do_check_false(t2.appDisabled); do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); - fstream.close(); - end_test(); + connection.close(); + + // After allowing access to the original DB things should go back to as + // they were previously + restartManager(); + + // Shouldn't have seen any startup changes + check_startup_changes(AddonManager.STARTUP_CHANGE_INSTALLED, []); + + AddonManager.getAddonsByIDs(["addon1@tests.mozilla.org", + "addon2@tests.mozilla.org", + "addon3@tests.mozilla.org", + "addon4@tests.mozilla.org", + "addon5@tests.mozilla.org", + "addon6@tests.mozilla.org", + "addon7@tests.mozilla.org", + "theme1@tests.mozilla.org", + "theme2@tests.mozilla.org"], function([a1, a2, a3, + a4, a5, a6, + a7, t1, t2]) { + do_check_neq(a1, null); + do_check_true(a1.isActive); + do_check_false(a1.userDisabled); + do_check_false(a1.appDisabled); + do_check_eq(a1.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a1.id)); + + do_check_neq(a2, null); + do_check_false(a2.isActive); + do_check_true(a2.userDisabled); + do_check_false(a2.appDisabled); + do_check_eq(a2.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a2.id)); + + do_check_neq(a3, null); + do_check_true(a3.isActive); + do_check_false(a3.userDisabled); + do_check_false(a3.appDisabled); + do_check_eq(a3.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isExtensionInAddonsList(profileDir, a3.id)); + + do_check_neq(a4, null); + do_check_false(a4.isActive); + do_check_true(a4.userDisabled); + do_check_false(a4.appDisabled); + do_check_eq(a4.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a4.id)); + + do_check_neq(a5, null); + do_check_false(a5.isActive); + do_check_false(a5.userDisabled); + do_check_true(a5.appDisabled); + do_check_eq(a5.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isExtensionInAddonsList(profileDir, a5.id)); + + do_check_neq(a6, null); + do_check_true(a6.isActive); + do_check_false(a6.userDisabled); + do_check_false(a6.appDisabled); + do_check_eq(a6.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(a7, null); + do_check_false(a7.isActive); + do_check_true(a7.userDisabled); + do_check_false(a7.appDisabled); + do_check_eq(a7.pendingOperations, AddonManager.PENDING_NONE); + + do_check_neq(t1, null); + do_check_false(t1.isActive); + do_check_true(t1.userDisabled); + do_check_false(t1.appDisabled); + do_check_eq(t1.pendingOperations, AddonManager.PENDING_NONE); + do_check_false(isThemeInAddonsList(profileDir, t1.id)); + + do_check_neq(t2, null); + do_check_true(t2.isActive); + do_check_false(t2.userDisabled); + do_check_false(t2.appDisabled); + do_check_eq(t2.pendingOperations, AddonManager.PENDING_NONE); + do_check_true(isThemeInAddonsList(profileDir, t2.id)); + + end_test(); + }); }); }); }); }
--- a/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/mozapps/extensions/test/xpcshell/xpcshell.ini @@ -175,16 +175,17 @@ skip-if = os == "android" [test_install_icons.js] # Bug 676992: test consistently hangs on Android skip-if = os == "android" [test_install_strictcompat.js] # Bug 676992: test consistently hangs on Android skip-if = os == "android" [test_locale.js] [test_locked.js] +[test_locked2.js] [test_locked_strictcompat.js] [test_manifest.js] [test_migrate1.js] [test_migrate2.js] [test_migrate3.js] [test_migrate4.js] [test_migrate5.js] [test_migrateAddonRepository.js]