Bug 1237820: Track whether a user has been offered a sideloaded add-on or not. r=rhelmer

Previously we just checked every newly sideloaded add-on to decide whether to
offer it to the user for opt-in. This adds a new "seen" property (naming could
be better if you have other suggestions) which tracks whether we've ever shown
the opt-in UI for the add-on. It defaults to true for all add-ons and is only
set to false for sideloaded add-ons that default to being disabled on install.
The seen flag can be set to true through the API but cannot be reset to false
as that would allow add-ons to forcibly re-present themselves to the user when
disabled.

The opt-in UI sets the seen flag to true only when it has focus which fixes a
long-standing bug where if you accept the first add-on you see and restart the
other tabs might not show up.

The one slight downside of this approach is that it now requires loading the
full add-ons database on every startup in order to check the seen flag for all
installed add-ons. There are hacky ways we might get around this but they all
involve overloading prefs with even more object data. The good thing is that
we do the load and check asynchronously after most of startup is complete and
the UI is fully loaded so there shouldn't be any percieved impact to startup
time. I've run multiple talos runs to verify that none of the numbers appear to
regress.

--HG--
extra : commitid : AG6pELCYJDa
extra : rebase_source : b824c1626d0c5a77416fa4349ed3dd4d0e96418b
This commit is contained in:
Dave Townsend 2016-01-26 14:31:33 -08:00
Родитель 5d3da00102
Коммит 5537b5c159
15 изменённых файлов: 399 добавлений и 56 удалений

Просмотреть файл

@ -1141,20 +1141,25 @@ BrowserGlue.prototype = {
// For any add-ons that were installed disabled and can be enabled offer
// them to the user.
let changedIDs = AddonManager.getStartupChanges(AddonManager.STARTUP_CHANGE_INSTALLED);
if (changedIDs.length > 0) {
let win = RecentWindow.getMostRecentBrowserWindow();
AddonManager.getAddonsByIDs(changedIDs, function(aAddons) {
aAddons.forEach(function(aAddon) {
// If the add-on isn't user disabled or can't be enabled then skip it.
if (!aAddon.userDisabled || !(aAddon.permissions & AddonManager.PERM_CAN_ENABLE))
return;
win.openUILinkIn("about:newaddon?id=" + aAddon.id, "tab");
})
});
AddonManager.getAllAddons(addons => {
for (let addon of addons) {
// If this add-on has already seen (or seen is undefined for non-XPI
// add-ons) then skip it.
if (addon.seen !== false) {
continue;
}
// If this add-on cannot be enabled (either already enabled or
// appDisabled) then skip it.
if (!(addon.permissions & AddonManager.PERM_CAN_ENABLE)) {
continue;
}
win.openUILinkIn("about:newaddon?id=" + addon.id, "tab");
}
});
let signingRequired;
if (AppConstants.MOZ_REQUIRE_SIGNING) {
signingRequired = true;

Просмотреть файл

@ -39,10 +39,10 @@ function initialize() {
let bundle = Services.strings.createBundle("chrome://mozapps/locale/extensions/newaddon.properties");
AddonManager.getAddonByID(id, function(aAddon) {
// If the add-on doesn't exist or it is already enabled or it cannot be
// enabled then this UI is useless, just close it. This shouldn't normally
// happen unless session restore restores the tab
if (!aAddon || !aAddon.userDisabled ||
// If the add-on doesn't exist or it is already enabled or it has already
// been seen or it cannot be enabled then this UI is useless, just close it.
// This shouldn't normally happen unless session restore restores the tab.
if (!aAddon || !aAddon.userDisabled || aAddon.seen ||
!(aAddon.permissions & AddonManager.PERM_CAN_ENABLE)) {
window.close();
return;
@ -79,6 +79,14 @@ function initialize() {
document.getElementById("location").hidden = true;
}
// Only mark the add-on as seen if the page actually gets focus
if (document.hasFocus()) {
aAddon.markAsSeen();
}
else {
document.addEventListener("focus", () => aAddon.markAsSeen(), false);
}
var event = document.createEvent("Events");
event.initEvent("AddonDisplayed", true, true);
document.dispatchEvent(event);

Просмотреть файл

@ -6526,6 +6526,7 @@ AddonInternal.prototype = {
sourceURI: null,
releaseNotesURI: null,
foreignInstall: false,
seen: true,
skinnable: false,
get selectedLocale() {
@ -6810,6 +6811,15 @@ AddonWrapper.prototype = {
return AppConstants.DEBUG ? addonFor(this) : undefined;
},
get seen() {
return addonFor(this).seen;
},
markAsSeen: function() {
addonFor(this).seen = true;
XPIDatabase.saveChanges();
},
get type() {
return getExternalType(addonFor(this).type);
},

Просмотреть файл

@ -78,7 +78,8 @@ const PROP_JSON_FIELDS = ["id", "syncGUID", "location", "version", "type",
"skinnable", "size", "sourceURI", "releaseNotesURI",
"softDisabled", "foreignInstall", "hasBinaryComponents",
"strictCompatibility", "locales", "targetApplications",
"targetPlatforms", "multiprocessCompatible", "signedState"];
"targetPlatforms", "multiprocessCompatible", "signedState",
"seen"];
// Properties that should be migrated where possible from an old database. These
// shouldn't include properties that can be read directly from install.rdf files
@ -281,6 +282,7 @@ function copyProperties(aObject, aProperties, aTarget) {
if (!aTarget)
aTarget = {};
aProperties.forEach(function(aProp) {
if (aProp in aObject)
aTarget[aProp] = aObject[aProp];
});
return aTarget;
@ -1302,6 +1304,7 @@ this.XPIDatabase = {
aNewAddon.installDate = aOldAddon.installDate;
aNewAddon.applyBackgroundUpdates = aOldAddon.applyBackgroundUpdates;
aNewAddon.foreignInstall = aOldAddon.foreignInstall;
aNewAddon.seen = aOldAddon.seen;
aNewAddon.active = (aNewAddon.visible && !aNewAddon.disabled && !aNewAddon.pendingUninstall);
// addAddonMetadata does a saveChanges()
@ -1706,6 +1709,7 @@ this.XPIDatabaseReconcile = {
logger.warn("Disabling foreign installed add-on " + aNewAddon.id + " in "
+ aInstallLocation.name);
aNewAddon.userDisabled = true;
aNewAddon.seen = false;
}
}

Просмотреть файл

@ -6,13 +6,16 @@
var gProvider;
function loadPage(aURL, aCallback) {
gBrowser.selectedTab = gBrowser.addTab();
gBrowser.loadURI(aURL);
gBrowser.addEventListener("AddonDisplayed", function(event) {
gBrowser.removeEventListener("AddonDisplayed", arguments.callee, false);
function loadPage(aURL, aCallback, aBackground = false) {
let tab = gBrowser.addTab();
if (!aBackground)
gBrowser.selectedTab = tab;
let browser = tab.linkedBrowser;
browser.loadURI(aURL);
browser.addEventListener("AddonDisplayed", function(event) {
browser.removeEventListener("AddonDisplayed", arguments.callee, false);
aCallback(gBrowser.selectedTab);
aCallback(tab);
});
}
@ -26,13 +29,15 @@ function test() {
name: "Test 1",
version: "5.3",
userDisabled: true,
seen: false,
operationsRequiringRestart: AddonManager.OP_NEEDS_RESTART_NONE
}, {
id: "addon2@tests.mozilla.org",
name: "Test 2",
version: "7.1",
creator: "Dave Townsend",
userDisabled: true
userDisabled: true,
seen: false
}]);
run_next_test();
@ -54,16 +59,19 @@ add_test(function() {
is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
"Should be showing the right buttons");
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
ok(aAddon.seen, "Add-on should have been marked as seen");
EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
{}, aTab.linkedBrowser.contentWindow);
is(gBrowser.tabs.length, 1, "Page should have been closed");
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
ok(aAddon.userDisabled, "Add-on should not have been enabled");
ok(!aAddon.isActive, "Add-on should not be running");
aAddon.seen = false;
run_next_test();
});
});
@ -81,6 +89,9 @@ add_test(function() {
is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
"Should be showing the right buttons");
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
ok(aAddon.seen, "Add-on should have been marked as seen");
EventUtils.synthesizeMouseAtCenter(doc.getElementById("allow"),
{}, aTab.linkedBrowser.contentWindow);
@ -89,11 +100,12 @@ add_test(function() {
is(gBrowser.tabs.length, 1, "Page should have been closed");
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
ok(!aAddon.userDisabled, "Add-on should now have been enabled");
ok(aAddon.isActive, "Add-on should now be running");
aAddon.userDisabled = true;
aAddon.seen = false;
run_next_test();
});
});
@ -112,16 +124,19 @@ add_test(function() {
is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
"Should be showing the right buttons");
AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
ok(aAddon.seen, "Add-on should have been marked as seen");
EventUtils.synthesizeMouseAtCenter(doc.getElementById("continue-button"),
{}, aTab.linkedBrowser.contentWindow);
is(gBrowser.tabs.length, 1, "Page should have been closed");
AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
ok(aAddon.userDisabled, "Add-on should not have been enabled");
ok(!aAddon.isActive, "Add-on should not be running");
aAddon.seen = false;
run_next_test();
});
});
@ -140,6 +155,9 @@ add_test(function() {
is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
"Should be showing the right buttons");
AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
ok(aAddon.seen, "Add-on should have been marked as seen");
EventUtils.synthesizeMouseAtCenter(doc.getElementById("allow"),
{}, aTab.linkedBrowser.contentWindow);
@ -149,7 +167,6 @@ add_test(function() {
is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("restartPanel"),
"Should be showing the right buttons");
AddonManager.getAddonByID("addon2@tests.mozilla.org", function(aAddon) {
ok(!aAddon.userDisabled, "Add-on should now have been enabled");
ok(!aAddon.isActive, "Add-on should not be running");
@ -180,7 +197,36 @@ add_test(function() {
is(gBrowser.tabs.length, 1, "Page should have been closed");
aAddon.seen = false;
run_next_test();
});
});
});
// Tests that opening the page in the background doesn't mark as seen
add_test(function() {
loadPage("about:newaddon?id=addon1@tests.mozilla.org", function(aTab) {
var doc = aTab.linkedBrowser.contentDocument;
is(doc.getElementById("name").value, "Test 1 5.3", "Should say the right name");
is_element_hidden(doc.getElementById("author"), "Should be no author displayed");
is_element_hidden(doc.getElementById("location"), "Should be no location displayed");
is(doc.getElementById("buttonDeck").selectedPanel, doc.getElementById("continuePanel"),
"Should be showing the right buttons");
AddonManager.getAddonByID("addon1@tests.mozilla.org", function(aAddon) {
ok(!aAddon.seen, "Add-on should not have been marked as seen.");
gBrowser.selectedTab = aTab;
waitForFocus(function() {
ok(aAddon.seen, "Add-on should have been marked as seen after focusing the tab.");
gBrowser.removeTab(aTab);
run_next_test();
}, aTab.linkedBrowser.contentWindow);
});
}, true);
});

Просмотреть файл

@ -1227,6 +1227,10 @@ MockAddon.prototype = {
AddonManagerPrivate.callAddonListeners("onOperationCancelled", this);
},
markAsSeen: function() {
this.seen = true;
},
_updateActiveState: function(currentActive, newActive) {
if (currentActive == newActive)
return;

Просмотреть файл

@ -1939,9 +1939,11 @@ function do_exception_wrap(func) {
/**
* Change the schema version of the JSON extensions database
*/
function changeXPIDBVersion(aNewVersion) {
function changeXPIDBVersion(aNewVersion, aMutator = undefined) {
let jData = loadJSON(gExtensionsJSON);
jData.schemaVersion = aNewVersion;
if (aMutator)
aMutator(jData);
saveJSON(jData, gExtensionsJSON);
}

Просмотреть файл

@ -163,6 +163,7 @@ function run_test() {
do_check_true(a1.isActive);
do_check_true(isExtensionInAddonsList(profileDir, a1.id));
do_check_false(a1.hasBinaryComponents);
do_check_true(a1.seen);
// addon2 was user disabled and app enabled in the old extensions.rdf
do_check_neq(a2, null);
@ -171,6 +172,7 @@ function run_test() {
do_check_false(a2.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a2.id));
do_check_false(a2.hasBinaryComponents);
do_check_true(a2.seen);
// addon3 was pending user disable and app disabled in the old extensions.rdf
do_check_neq(a3, null);
@ -179,6 +181,7 @@ function run_test() {
do_check_false(a3.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a3.id));
do_check_false(a3.hasBinaryComponents);
do_check_true(a3.seen);
// addon4 was pending user enable and app disabled in the old extensions.rdf
do_check_neq(a4, null);
@ -187,6 +190,7 @@ function run_test() {
do_check_false(a4.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a4.id));
do_check_false(a4.hasBinaryComponents);
do_check_true(a4.seen);
// addon5 was disabled and compatible but a new version has been installed
// since, it should still be disabled but should be incompatible
@ -196,6 +200,7 @@ function run_test() {
do_check_false(a5.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a5.id));
do_check_false(a5.hasBinaryComponents);
do_check_true(a5.seen);
// addon6, addon7 and addon8 will have been lost as they were staged in the
// pre-Firefox 4.0 directory
@ -210,14 +215,16 @@ function run_test() {
do_check_true(t1.isActive);
do_check_true(isThemeInAddonsList(profileDir, t1.id));
do_check_false(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
do_check_true(t1.seen);
// Theme 2 was previously disabled
do_check_neq(t1, null);
do_check_neq(t2, null);
do_check_true(t2.userDisabled);
do_check_false(t2.appDisabled);
do_check_false(t2.isActive);
do_check_false(isThemeInAddonsList(profileDir, t2.id));
do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
do_check_true(t2.seen);
do_execute_soon(do_test_finished);
});

Просмотреть файл

@ -203,6 +203,7 @@ function run_test() {
do_check_true(a1.isActive);
do_check_false(a1.strictCompatibility);
do_check_false(a1.foreignInstall);
do_check_true(a1.seen);
// addon2 was disabled in the database
do_check_neq(a2, null);
do_check_true(a2.userDisabled);
@ -210,6 +211,7 @@ function run_test() {
do_check_false(a2.isActive);
do_check_false(a2.strictCompatibility);
do_check_false(a2.foreignInstall);
do_check_true(a2.seen);
// addon3 was pending-disable in the database
do_check_neq(a3, null);
do_check_true(a3.userDisabled);
@ -217,6 +219,7 @@ function run_test() {
do_check_false(a3.isActive);
do_check_false(a3.strictCompatibility);
do_check_false(a3.foreignInstall);
do_check_true(a3.seen);
// addon4 was pending-enable in the database
do_check_neq(a4, null);
do_check_false(a4.userDisabled);
@ -224,6 +227,7 @@ function run_test() {
do_check_true(a4.isActive);
do_check_true(a4.strictCompatibility);
do_check_false(a4.foreignInstall);
do_check_true(a4.seen);
// addon5 was enabled in the database but needed a compatibility update
do_check_neq(a5, null);
do_check_false(a5.userDisabled);
@ -231,6 +235,7 @@ function run_test() {
do_check_true(a5.isActive);
do_check_false(a5.strictCompatibility);
do_check_false(a5.foreignInstall);
do_check_true(a5.seen);
// addon6 was disabled and compatible but a new version has been installed
// since, it should still be disabled but should be incompatible
do_check_neq(a6, null);
@ -239,6 +244,7 @@ function run_test() {
do_check_false(a6.isActive);
do_check_false(a6.strictCompatibility);
do_check_false(a6.foreignInstall);
do_check_true(a6.seen);
// addon7 is in the global install location so should be a foreignInstall
do_check_neq(a7, null);
do_check_false(a7.userDisabled);
@ -246,6 +252,7 @@ function run_test() {
do_check_true(a7.isActive);
do_check_false(a7.strictCompatibility);
do_check_true(a7.foreignInstall);
do_check_true(a7.seen);
// addon8 is in the user install location so should be a foreignInstall
do_check_neq(a8, null);
do_check_false(a8.userDisabled);
@ -253,6 +260,7 @@ function run_test() {
do_check_true(a8.isActive);
do_check_false(a8.strictCompatibility);
do_check_true(a8.foreignInstall);
do_check_true(a8.seen);
do_execute_soon(do_test_finished);
});

Просмотреть файл

@ -166,6 +166,7 @@ function run_test() {
do_check_false(a1.appDisabled);
do_check_true(a1.isActive);
do_check_true(isExtensionInAddonsList(profileDir, a1.id));
do_check_true(a1.seen);
// addon2 was user disabled and app enabled in the old extensions.rdf
do_check_neq(a2, null);
@ -173,6 +174,7 @@ function run_test() {
do_check_false(a2.appDisabled);
do_check_false(a2.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a2.id));
do_check_true(a2.seen);
// addon3 was pending user disable and app disabled in the old extensions.rdf
do_check_neq(a3, null);
@ -180,6 +182,7 @@ function run_test() {
do_check_true(a3.appDisabled);
do_check_false(a3.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a3.id));
do_check_true(a3.seen);
// addon4 was pending user enable and app disabled in the old extensions.rdf
do_check_neq(a4, null);
@ -187,6 +190,7 @@ function run_test() {
do_check_true(a4.appDisabled);
do_check_false(a4.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a4.id));
do_check_true(a4.seen);
// addon5 was disabled and compatible but a new version has been installed
// since, it should still be disabled but should be incompatible
@ -195,6 +199,7 @@ function run_test() {
do_check_true(a5.appDisabled);
do_check_false(a5.isActive);
do_check_false(isExtensionInAddonsList(profileDir, a5.id));
do_check_true(a5.seen);
// addon6 and addon7 will have been lost as they were staged in the
// pre-Firefox 4.0 directory
@ -208,14 +213,16 @@ function run_test() {
do_check_false(t1.isActive);
do_check_true(isThemeInAddonsList(profileDir, t1.id));
do_check_true(hasFlag(t1.permissions, AddonManager.PERM_CAN_ENABLE));
do_check_true(t1.seen);
// Theme 2 was previously disabled
do_check_neq(t1, null);
do_check_neq(t2, null);
do_check_true(t2.userDisabled);
do_check_false(t2.appDisabled);
do_check_false(t2.isActive);
do_check_false(isThemeInAddonsList(profileDir, t2.id));
do_check_true(hasFlag(t2.permissions, AddonManager.PERM_CAN_ENABLE));
do_check_true(t2.seen);
do_execute_soon(do_test_finished);
});

Просмотреть файл

@ -172,7 +172,12 @@ function perform_migration() {
// Turn on disabling for all scopes
Services.prefs.setIntPref("extensions.autoDisableScopes", 15);
changeXPIDBVersion(1);
changeXPIDBVersion(1, data => {
// Delete the seen property from all add-ons to make sure it defaults to true
for (let addon of data.addons) {
delete addon.seen;
}
});
Services.prefs.setIntPref("extensions.databaseSchema", 1);
gAppInfo.version = "2"
@ -205,6 +210,7 @@ function test_results() {
do_check_true(a1.isActive);
do_check_eq(a1.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
do_check_true(a1.foreignInstall);
do_check_true(a1.seen);
do_check_false(a1.hasBinaryComponents);
do_check_false(a1.strictCompatibility);
@ -216,6 +222,7 @@ function test_results() {
do_check_false(a2.isActive);
do_check_eq(a2.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DISABLE);
do_check_true(a2.foreignInstall);
do_check_true(a2.seen);
do_check_false(a2.hasBinaryComponents);
do_check_false(a2.strictCompatibility);
@ -227,6 +234,7 @@ function test_results() {
do_check_false(a3.isActive);
do_check_eq(a3.applyBackgroundUpdates, AddonManager.AUTOUPDATE_ENABLE);
do_check_true(a3.foreignInstall);
do_check_true(a3.seen);
do_check_false(a3.hasBinaryComponents);
do_check_false(a3.strictCompatibility);
@ -238,6 +246,7 @@ function test_results() {
do_check_true(a4.isActive);
do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
do_check_true(a4.foreignInstall);
do_check_true(a4.seen);
do_check_false(a4.hasBinaryComponents);
do_check_true(a4.strictCompatibility);
@ -248,6 +257,7 @@ function test_results() {
do_check_true(a5.isActive);
do_check_eq(a4.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
do_check_true(a5.foreignInstall);
do_check_true(a5.seen);
do_check_false(a5.hasBinaryComponents);
do_check_false(a5.strictCompatibility);
@ -260,6 +270,7 @@ function test_results() {
do_check_false(a6.isActive);
do_check_eq(a6.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
do_check_true(a6.foreignInstall);
do_check_true(a6.seen);
do_check_eq(a6.sourceURI.spec, "http://localhost:" + gPort + "/addons/test_migrate4_6.xpi");
do_check_eq(a6.releaseNotesURI.spec, "http://example.com/updateInfo.xhtml");
do_check_false(a6.hasBinaryComponents);
@ -273,6 +284,7 @@ function test_results() {
do_check_true(a7.isActive);
do_check_eq(a7.applyBackgroundUpdates, AddonManager.AUTOUPDATE_DEFAULT);
do_check_false(a7.foreignInstall);
do_check_true(a7.seen);
do_check_eq(a7.sourceURI.spec, "http://localhost:" + gPort + "/addons/test_migrate4_7.xpi");
do_check_eq(a7.releaseNotesURI, null);
do_check_false(a7.hasBinaryComponents);
@ -284,6 +296,7 @@ function test_results() {
do_check_false(a8.appDisabled);
do_check_true(a8.isActive);
do_check_false(a8.foreignInstall);
do_check_true(a8.seen);
do_check_true(a8.hasBinaryComponents);
do_check_false(a8.strictCompatibility);
@ -293,6 +306,7 @@ function test_results() {
do_check_false(a9.appDisabled);
do_check_true(a9.isActive);
do_check_false(a9.foreignInstall);
do_check_true(a9.seen);
do_check_false(a9.hasBinaryComponents);
do_check_true(a9.strictCompatibility);

Просмотреть файл

@ -0,0 +1,211 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
const ID = "bootstrap1@tests.mozilla.org";
let profileDir = gProfD.clone();
profileDir.append("extensions");
createAppInfo("xpcshell@tests.mozilla.org", "XPCShell", "1", "1.9.2");
startupManager();
// By default disable add-ons from the profile
Services.prefs.setIntPref("extensions.autoDisableScopes", AddonManager.SCOPE_PROFILE);
// Installing an add-on through the API should mark it as seen
add_task(function*() {
let install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_1"), resolve));
yield promiseCompleteAllInstalls([install]);
do_check_eq(install.state, AddonManager.STATE_INSTALLED);
do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
let addon = install.addon;
do_check_eq(addon.version, "1.0");
do_check_false(addon.foreignInstall);
do_check_true(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_false(addon.foreignInstall);
do_check_true(addon.seen);
// Installing an update should retain that
install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), resolve));
yield promiseCompleteAllInstalls([install]);
do_check_eq(install.state, AddonManager.STATE_INSTALLED);
do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
addon = install.addon;
do_check_eq(addon.version, "2.0");
do_check_false(addon.foreignInstall);
do_check_true(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_false(addon.foreignInstall);
do_check_true(addon.seen);
addon.uninstall();
yield promiseShutdownManager();
});
// Sideloading an add-on should mark it as unseen
add_task(function*() {
let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
// Make sure the startup code will detect sideloaded updates
setExtensionModifiedTime(path, Date.now() - 10000);
startupManager();
let addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "1.0");
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
yield promiseShutdownManager();
// Sideloading an update shouldn't change the state
manuallyUninstall(profileDir, ID);
manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir, ID);
setExtensionModifiedTime(path, Date.now());
startupManager();
addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "2.0");
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
addon.uninstall();
yield promiseShutdownManager();
});
// Sideloading an add-on should mark it as unseen
add_task(function*() {
let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
// Make sure the startup code will detect sideloaded updates
setExtensionModifiedTime(path, Date.now() - 10000);
startupManager();
let addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "1.0");
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
// Updating through the API shouldn't change the state
install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), resolve));
yield promiseCompleteAllInstalls([install]);
do_check_eq(install.state, AddonManager.STATE_INSTALLED);
do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
addon = install.addon;
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "2.0");
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
addon.uninstall();
yield promiseShutdownManager();
});
// Sideloading an add-on should mark it as unseen
add_task(function*() {
let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
// Make sure the startup code will detect sideloaded updates
setExtensionModifiedTime(path, Date.now() - 10000);
startupManager();
let addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "1.0");
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
addon.markAsSeen();
do_check_true(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_true(addon.foreignInstall);
do_check_true(addon.seen);
yield promiseShutdownManager();
// Sideloading an update shouldn't change the state
manuallyUninstall(profileDir, ID);
manuallyInstall(do_get_addon("test_bootstrap1_2"), profileDir, ID);
setExtensionModifiedTime(path, Date.now());
startupManager();
addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "2.0");
do_check_true(addon.foreignInstall);
do_check_true(addon.seen);
addon.uninstall();
yield promiseShutdownManager();
});
// Sideloading an add-on should mark it as unseen
add_task(function*() {
let path = manuallyInstall(do_get_addon("test_bootstrap1_1"), profileDir, ID);
// Make sure the startup code will detect sideloaded updates
setExtensionModifiedTime(path, Date.now() - 10000);
startupManager();
let addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "1.0");
do_check_true(addon.foreignInstall);
do_check_false(addon.seen);
addon.markAsSeen();
do_check_true(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_true(addon.foreignInstall);
do_check_true(addon.seen);
// Updating through the API shouldn't change the state
install = yield new Promise(resolve => AddonManager.getInstallForFile(do_get_addon("test_bootstrap1_2"), resolve));
yield promiseCompleteAllInstalls([install]);
do_check_eq(install.state, AddonManager.STATE_INSTALLED);
do_check_false(hasFlag(install.addon.pendingOperations, AddonManager.PENDING_INSTALL));
addon = install.addon;
do_check_true(addon.foreignInstall);
do_check_true(addon.seen);
yield promiseRestartManager();
addon = yield promiseAddonByID(ID);
do_check_eq(addon.version, "2.0");
do_check_true(addon.foreignInstall);
do_check_true(addon.seen);
addon.uninstall();
yield promiseShutdownManager();
});

Просмотреть файл

@ -212,6 +212,8 @@ function run_test_1() {
do_check_eq(a1.scope, AddonManager.SCOPE_PROFILE);
do_check_eq(a1.sourceURI, null);
do_check_true(a1.foreignInstall);
do_check_false(a1.userDisabled);
do_check_true(a1.seen);
do_check_neq(a2, null);
do_check_eq(a2.id, "addon2@tests.mozilla.org");
@ -226,6 +228,8 @@ function run_test_1() {
do_check_eq(a2.scope, AddonManager.SCOPE_PROFILE);
do_check_eq(a2.sourceURI, null);
do_check_true(a2.foreignInstall);
do_check_false(a1.userDisabled);
do_check_true(a1.seen);
do_check_neq(a3, null);
do_check_eq(a3.id, "addon3@tests.mozilla.org");
@ -240,6 +244,8 @@ function run_test_1() {
do_check_eq(a3.scope, AddonManager.SCOPE_PROFILE);
do_check_eq(a3.sourceURI, null);
do_check_true(a3.foreignInstall);
do_check_false(a1.userDisabled);
do_check_true(a1.seen);
do_check_eq(a4, null);
do_check_false(isExtensionInAddonsList(profileDir, "addon4@tests.mozilla.org"));
@ -824,14 +830,17 @@ function run_test_12() {
callback_soon(function([a1, a2, a3, a4, a5]) {
do_check_neq(a1, null);
do_check_false(a1.userDisabled);
do_check_true(a1.seen);
do_check_true(a1.isActive);
do_check_neq(a2, null);
do_check_true(a2.userDisabled);
do_check_false(a2.seen);
do_check_false(a2.isActive);
do_check_neq(a3, null);
do_check_false(a3.userDisabled);
do_check_true(a3.seen);
do_check_true(a3.isActive);
var dest = profileDir.clone();
@ -862,14 +871,17 @@ function run_test_12() {
function([a1, a2, a3, a4, a5]) {
do_check_neq(a1, null);
do_check_false(a1.userDisabled);
do_check_true(a1.seen);
do_check_true(a1.isActive);
do_check_neq(a2, null);
do_check_false(a2.userDisabled);
do_check_true(a2.seen);
do_check_true(a2.isActive);
do_check_neq(a3, null);
do_check_true(a3.userDisabled);
do_check_false(a3.seen);
do_check_false(a3.isActive);
var dest = profileDir.clone();
@ -900,14 +912,17 @@ function run_test_12() {
function([a1, a2, a3, a4, a5]) {
do_check_neq(a1, null);
do_check_false(a1.userDisabled);
do_check_true(a1.seen);
do_check_true(a1.isActive);
do_check_neq(a2, null);
do_check_true(a2.userDisabled);
do_check_false(a2.seen);
do_check_false(a2.isActive);
do_check_neq(a3, null);
do_check_true(a3.userDisabled);
do_check_false(a3.seen);
do_check_false(a3.isActive);
do_execute_soon(end_test);

Просмотреть файл

@ -280,6 +280,7 @@ skip-if = os == "android"
skip-if = os == "android"
run-sequentially = Uses hardcoded ports in xpi files.
[test_json_updatecheck.js]
[test_seen.js]
[test_updateid.js]
# Bug 676992: test consistently hangs on Android
skip-if = os == "android"

Просмотреть файл

@ -9,6 +9,7 @@ support-files =
data/**
xpcshell-shared.ini
[test_addon_path_service.js]
[test_asyncBlocklistLoad.js]
[test_cacheflush.js]