Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-08-08 12:03:06 +02:00
Родитель 90f6c2d3fd 3db62fe495
Коммит f36d230b1f
8 изменённых файлов: 339 добавлений и 138 удалений

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

@ -43,19 +43,23 @@ CDMCaps::AutoLock::~AutoLock()
mData.Unlock();
}
// Keys with kGMPUsable, kGMPOutputDownscaled, or kGMPOutputRestricted status
// can be used by the CDM to decrypt or decrypt-and-decode samples.
static bool
IsUsableStatus(GMPMediaKeyStatus aStatus)
{
return aStatus == kGMPUsable ||
aStatus == kGMPOutputRestricted ||
aStatus == kGMPOutputDownscaled;
}
bool
CDMCaps::AutoLock::IsKeyUsable(const CencKeyId& aKeyId)
{
mData.mMonitor.AssertCurrentThreadOwns();
const auto& keys = mData.mKeyStatuses;
for (size_t i = 0; i < keys.Length(); i++) {
if (keys[i].mId != aKeyId) {
continue;
}
if (keys[i].mStatus == kGMPUsable ||
keys[i].mStatus == kGMPOutputRestricted ||
keys[i].mStatus == kGMPOutputDownscaled) {
return true;
for (const KeyStatus& keyStatus : mData.mKeyStatuses) {
if (keyStatus.mId == aKeyId) {
return IsUsableStatus(keyStatus.mStatus);
}
}
return false;
@ -68,31 +72,34 @@ CDMCaps::AutoLock::SetKeyStatus(const CencKeyId& aKeyId,
{
mData.mMonitor.AssertCurrentThreadOwns();
KeyStatus key(aKeyId, aSessionId, aStatus);
auto index = mData.mKeyStatuses.IndexOf(key);
if (aStatus == kGMPUnknown) {
// Return true if the element is found to notify key changes.
return mData.mKeyStatuses.RemoveElement(key);
}
auto index = mData.mKeyStatuses.IndexOf(key);
if (index != mData.mKeyStatuses.NoIndex) {
if (mData.mKeyStatuses[index].mStatus == aStatus) {
// No change.
return false;
}
auto oldStatus = mData.mKeyStatuses[index].mStatus;
mData.mKeyStatuses[index].mStatus = aStatus;
if (oldStatus == kGMPUsable || oldStatus == kGMPOutputDownscaled) {
// The old key status was one for which we can decrypt media. We don't
// need to do the "notify usable" step below, as it should be impossible
// for us to have anything waiting on this key to become usable, since it
// was already usable.
if (IsUsableStatus(oldStatus)) {
return true;
}
} else {
mData.mKeyStatuses.AppendElement(key);
}
// Both kGMPUsable and kGMPOutputDownscaled are treated able to decrypt.
// We don't need to notify when transition happens between kGMPUsable and
// kGMPOutputDownscaled. Only call NotifyUsable() when we are going from
// ![kGMPUsable|kGMPOutputDownscaled] to [kGMPUsable|kGMPOutputDownscaled]
if (aStatus != kGMPUsable && aStatus != kGMPOutputDownscaled) {
// Only call NotifyUsable() for a key when we are going from non-usable
// to usable state.
if (!IsUsableStatus(aStatus)) {
return true;
}
@ -124,10 +131,9 @@ void
CDMCaps::AutoLock::GetKeyStatusesForSession(const nsAString& aSessionId,
nsTArray<KeyStatus>& aOutKeyStatuses)
{
for (size_t i = 0; i < mData.mKeyStatuses.Length(); i++) {
const auto& key = mData.mKeyStatuses[i];
if (key.mSessionId.Equals(aSessionId)) {
aOutKeyStatuses.AppendElement(key);
for (const KeyStatus& keyStatus : mData.mKeyStatuses) {
if (keyStatus.mSessionId.Equals(aSessionId)) {
aOutKeyStatuses.AppendElement(keyStatus);
}
}
}
@ -136,7 +142,7 @@ void
CDMCaps::AutoLock::GetSessionIdsForKeyId(const CencKeyId& aKeyId,
nsTArray<nsCString>& aOutSessionIds)
{
for (const auto& keyStatus : mData.mKeyStatuses) {
for (const KeyStatus& keyStatus : mData.mKeyStatuses) {
if (keyStatus.mId == aKeyId) {
aOutSessionIds.AppendElement(NS_ConvertUTF16toUTF8(keyStatus.mSessionId));
}
@ -149,8 +155,8 @@ CDMCaps::AutoLock::RemoveKeysForSession(const nsString& aSessionId)
bool changed = false;
nsTArray<KeyStatus> statuses;
GetKeyStatusesForSession(aSessionId, statuses);
for (const KeyStatus& status : statuses) {
changed |= SetKeyStatus(status.mId, aSessionId, kGMPUnknown);
for (const KeyStatus& keyStatus : statuses) {
changed |= SetKeyStatus(keyStatus.mId, aSessionId, kGMPUnknown);
}
return changed;
}

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

@ -4639,6 +4639,10 @@ pref("extensions.alwaysUnpack", false);
pref("extensions.minCompatiblePlatformVersion", "2.0");
pref("extensions.webExtensionsMinPlatformVersion", "42.0a1");
// Other webextensions prefs
pref("extensions.webextensions.keepStorageOnUninstall", false);
pref("extensions.webextensions.keepUuidOnUninstall", false);
pref("network.buffer.cache.count", 24);
pref("network.buffer.cache.size", 32768);

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

@ -90,7 +90,6 @@ class MozbuildObject(ProcessExecutionMixin):
self._make = None
self._topobjdir = mozpath.normsep(topobjdir) if topobjdir else topobjdir
self._mozconfig = mozconfig
self._config_guess_output = None
self._config_environment = None
self._virtualenv_manager = None
@ -159,32 +158,7 @@ class MozbuildObject(ProcessExecutionMixin):
raise BuildEnvironmentNotFoundException(
'Could not find Mozilla source tree or build environment.')
# Now we try to load the config for this environment. If mozconfig is
# None, read_mozconfig() will attempt to find one in the existing
# environment. If no mozconfig is present, the config will not have
# much defined.
loader = MozconfigLoader(topsrcdir)
current_project = os.environ.get('MOZ_CURRENT_PROJECT')
config = loader.read_mozconfig(mozconfig, moz_build_app=current_project)
config_topobjdir = MozbuildObject.resolve_mozconfig_topobjdir(
topsrcdir, config)
# If we're inside a objdir and the found mozconfig resolves to
# another objdir, we abort. The reasoning here is that if you are
# inside an objdir you probably want to perform actions on that objdir,
# not another one. This prevents accidental usage of the wrong objdir
# when the current objdir is ambiguous.
if topobjdir and config_topobjdir:
if current_project:
config_topobjdir = os.path.join(config_topobjdir, current_project)
_config_topobjdir = config_topobjdir
if not samepath(topobjdir, _config_topobjdir):
raise ObjdirMismatchException(topobjdir, _config_topobjdir)
topsrcdir = mozpath.normsep(topsrcdir)
topobjdir = topobjdir or config_topobjdir
if topobjdir:
topobjdir = mozpath.normsep(os.path.normpath(topobjdir))
@ -193,31 +167,30 @@ class MozbuildObject(ProcessExecutionMixin):
'to be the same as your source directory (%s). This build '
'configuration is not supported.' % topsrcdir)
# If we can't resolve topobjdir, oh well. The constructor will figure
# it out via config.guess.
# If we can't resolve topobjdir, oh well. We'll figure out when we need
# one.
return cls(topsrcdir, None, None, topobjdir=topobjdir,
mozconfig=mozconfig)
@staticmethod
def resolve_mozconfig_topobjdir(topsrcdir, mozconfig, default=None):
topobjdir = mozconfig['topobjdir'] or default
def resolve_mozconfig_topobjdir(self, default=None):
topobjdir = self.mozconfig['topobjdir'] or default
if not topobjdir:
return None
if '@CONFIG_GUESS@' in topobjdir:
topobjdir = topobjdir.replace('@CONFIG_GUESS@',
MozbuildObject.resolve_config_guess(mozconfig, topsrcdir))
self.resolve_config_guess())
if not os.path.isabs(topobjdir):
topobjdir = os.path.abspath(os.path.join(topsrcdir, topobjdir))
topobjdir = os.path.abspath(os.path.join(self.topsrcdir, topobjdir))
return mozpath.normsep(os.path.normpath(topobjdir))
@property
def topobjdir(self):
if self._topobjdir is None:
self._topobjdir = MozbuildObject.resolve_mozconfig_topobjdir(
self.topsrcdir, self.mozconfig, default='obj-@CONFIG_GUESS@')
self._topobjdir = self.resolve_mozconfig_topobjdir(
default='obj-@CONFIG_GUESS@')
return self._topobjdir
@ -237,7 +210,7 @@ class MozbuildObject(ProcessExecutionMixin):
This a dict as returned by MozconfigLoader.read_mozconfig()
"""
if self._mozconfig is MozconfigLoader.AUTODETECT:
if not isinstance(self._mozconfig, dict):
loader = MozconfigLoader(self.topsrcdir)
self._mozconfig = loader.read_mozconfig(path=self._mozconfig,
moz_build_app=os.environ.get('MOZ_CURRENT_PROJECT'))
@ -379,9 +352,8 @@ class MozbuildObject(ProcessExecutionMixin):
return path
@staticmethod
def resolve_config_guess(mozconfig, topsrcdir):
make_extra = mozconfig['make_extra'] or []
def resolve_config_guess(self):
make_extra = self.mozconfig['make_extra'] or []
make_extra = dict(m.split('=', 1) for m in make_extra)
config_guess = make_extra.get('CONFIG_GUESS', None)
@ -394,17 +366,17 @@ class MozbuildObject(ProcessExecutionMixin):
if _config_guess_output:
return _config_guess_output[0]
p = os.path.join(topsrcdir, 'build', 'autoconf', 'config.guess')
p = os.path.join(self.topsrcdir, 'build', 'autoconf', 'config.guess')
# This is a little kludgy. We need access to the normalize_command
# function. However, that's a method of a mach mixin, so we need a
# class instance. Ideally the function should be accessible as a
# standalone function.
o = MozbuildObject(topsrcdir, None, None, None)
o = MozbuildObject(self.topsrcdir, None, None, None)
args = o._normalize_command([p], True)
_config_guess_output.append(
subprocess.check_output(args, cwd=topsrcdir).strip())
subprocess.check_output(args, cwd=self.topsrcdir).strip())
return _config_guess_output[0]
def notify(self, msg):
@ -468,14 +440,6 @@ class MozbuildObject(ProcessExecutionMixin):
self.log(logging.WARNING, 'notifier-failed', {'error':
e.message}, 'Notification center failed: {error}')
@property
def _config_guess(self):
if self._config_guess_output is None:
self._config_guess_output = MozbuildObject.resolve_config_guess(
self.mozconfig, self.topsrcdir)
return self._config_guess_output
def _ensure_objdir_exists(self):
if os.path.isdir(self.statedir):
return
@ -699,6 +663,16 @@ class MachCommandBase(MozbuildObject):
detect_virtualenv_mozinfo=detect_virtualenv_mozinfo)
topsrcdir = dummy.topsrcdir
topobjdir = dummy._topobjdir
if topobjdir:
# If we're inside a objdir and the found mozconfig resolves to
# another objdir, we abort. The reasoning here is that if you
# are inside an objdir you probably want to perform actions on
# that objdir, not another one. This prevents accidental usage
# of the wrong objdir when the current objdir is ambiguous.
config_topobjdir = dummy.resolve_mozconfig_topobjdir()
if config_topobjdir and not samepath(topobjdir,
config_topobjdir):
raise ObjdirMismatchException(topobjdir, config_topobjdir)
except BuildEnvironmentNotFoundException:
pass
except ObjdirMismatchException as e:

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

@ -12,6 +12,7 @@ import sys
import tempfile
import unittest
from cStringIO import StringIO
from mozfile.mozfile import NamedTemporaryFile
from mozunit import main
@ -59,7 +60,8 @@ class TestMozbuildObject(unittest.TestCase):
self.assertIsNotNone(base.topobjdir)
self.assertEqual(len(base.topobjdir.split()), 1)
self.assertTrue(base.topobjdir.endswith(base._config_guess))
config_guess = base.resolve_config_guess()
self.assertTrue(base.topobjdir.endswith(config_guess))
self.assertTrue(os.path.isabs(base.topobjdir))
self.assertTrue(base.topobjdir.startswith(base.topsrcdir))
@ -272,22 +274,31 @@ class TestMozbuildObject(unittest.TestCase):
os.chdir(topobjdir)
with self.assertRaises(ObjdirMismatchException):
MozbuildObject.from_environment(detect_virtualenv_mozinfo=False)
class MockMachContext(object):
pass
context = MockMachContext()
context.cwd = topobjdir
context.topdir = topsrcdir
context.settings = None
context.log_manager = None
context.detect_virtualenv_mozinfo=False
stdout = sys.stdout
sys.stdout = StringIO()
try:
with self.assertRaises(SystemExit):
MachCommandBase(context)
self.assertTrue(sys.stdout.getvalue().startswith(
'Ambiguous object directory detected.'))
finally:
sys.stdout = stdout
finally:
os.chdir(self._old_cwd)
shutil.rmtree(d)
def test_config_guess(self):
# It's difficult to test for exact values from the output of
# config.guess because they vary depending on platform.
base = self.get_base()
result = base._config_guess
self.assertIsNotNone(result)
self.assertGreater(len(result), 0)
def test_config_environment(self):
base = self.get_base(topobjdir=topobjdir)

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

@ -32,6 +32,8 @@ XPCOMUtils.defineLazyModuleGetter(this, "AppConstants",
"resource://gre/modules/AppConstants.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionAPIs",
"resource://gre/modules/ExtensionAPI.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "ExtensionStorage",
"resource://gre/modules/ExtensionStorage.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "FileUtils",
"resource://gre/modules/FileUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Locale",
@ -88,6 +90,9 @@ var {
} = ExtensionUtils;
const LOGGER_ID_BASE = "addons.webextension.";
const UUID_MAP_PREF = "extensions.webextensions.uuids";
const LEAVE_STORAGE_PREF = "extensions.webextensions.keepStorageOnUninstall";
const LEAVE_UUID_PREF = "extensions.webextensions.keepUuidOnUninstall";
const COMMENT_REGEXP = new RegExp(String.raw`
^
@ -475,29 +480,108 @@ let ParentAPIManager = {
ParentAPIManager.init();
// All moz-extension URIs use a machine-specific UUID rather than the
// extension's own ID in the host component. This makes it more
// difficult for web pages to detect whether a user has a given add-on
// installed (by trying to load a moz-extension URI referring to a
// web_accessible_resource from the extension). UUIDMap.get()
// returns the UUID for a given add-on ID.
var UUIDMap = {
_read() {
let pref = Preferences.get(UUID_MAP_PREF, "{}");
try {
return JSON.parse(pref);
} catch (e) {
Cu.reportError(`Error parsing ${UUID_MAP_PREF}.`);
return {};
}
},
_write(map) {
Preferences.set(UUID_MAP_PREF, JSON.stringify(map));
},
get(id, create = true) {
let map = this._read();
if (id in map) {
return map[id];
}
let uuid = null;
if (create) {
let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
uuid = uuidGenerator.generateUUID().number;
uuid = uuid.slice(1, -1); // Strip { and } off the UUID.
map[id] = uuid;
this._write(map);
}
return uuid;
},
remove(id) {
let map = this._read();
delete map[id];
this._write(map);
},
};
// This is the old interface that UUIDMap replaced, to be removed when
// the references listed in bug 1291399 are updated.
/* exported getExtensionUUID */
function getExtensionUUID(id) {
return UUIDMap.get(id, true);
}
// For extensions that have called setUninstallURL(), send an event
// so the browser can display the URL.
var UninstallObserver = {
initialized: false,
init: function() {
init() {
if (!this.initialized) {
AddonManager.addAddonListener(this);
XPCOMUtils.defineLazyPreferenceGetter(this, "leaveStorage", LEAVE_STORAGE_PREF, false);
XPCOMUtils.defineLazyPreferenceGetter(this, "leaveUuid", LEAVE_UUID_PREF, false);
this.initialized = true;
}
},
uninit: function() {
if (this.initialized) {
AddonManager.removeAddonListener(this);
this.initialized = false;
onUninstalling(addon) {
let extension = GlobalManager.extensionMap.get(addon.id);
if (extension) {
// Let any other interested listeners respond
// (e.g., display the uninstall URL)
Management.emit("uninstall", extension);
}
},
onUninstalling: function(addon) {
let extension = GlobalManager.extensionMap.get(addon.id);
if (extension) {
Management.emit("uninstall", extension);
onUninstalled(addon) {
let uuid = UUIDMap.get(addon.id, false);
if (!uuid) {
return;
}
if (!this.leaveStorage) {
// Clear browser.local.storage
ExtensionStorage.clear(addon.id);
// Clear any IndexedDB storage created by the extension
let baseURI = NetUtil.newURI(`moz-extension://${uuid}/`);
let principal = Services.scriptSecurityManager.createCodebasePrincipal(
baseURI, {addonId: addon.id}
);
Services.qms.clearStoragesForPrincipal(principal);
// Clear localStorage created by the extension
let attrs = JSON.stringify({addonId: addon.id});
Services.obs.notifyObservers(null, "clear-origin-data", attrs);
}
if (!this.leaveUuid) {
// Clear the entry in the UUID map
UUIDMap.remove(addon.id);
}
},
};
@ -524,7 +608,6 @@ GlobalManager = {
if (this.extensionMap.size == 0 && this.initialized) {
Services.obs.removeObserver(this, "content-document-global-created");
UninstallObserver.uninit();
this.initialized = false;
}
},
@ -708,36 +791,6 @@ GlobalManager = {
},
};
// All moz-extension URIs use a machine-specific UUID rather than the
// extension's own ID in the host component. This makes it more
// difficult for web pages to detect whether a user has a given add-on
// installed (by trying to load a moz-extension URI referring to a
// web_accessible_resource from the extension). getExtensionUUID
// returns the UUID for a given add-on ID.
function getExtensionUUID(id) {
const PREF_NAME = "extensions.webextensions.uuids";
let pref = Preferences.get(PREF_NAME, "{}");
let map = {};
try {
map = JSON.parse(pref);
} catch (e) {
Cu.reportError(`Error parsing ${PREF_NAME}.`);
}
if (id in map) {
return map[id];
}
let uuidGenerator = Cc["@mozilla.org/uuid-generator;1"].getService(Ci.nsIUUIDGenerator);
let uuid = uuidGenerator.generateUUID().number;
uuid = uuid.slice(1, -1); // Strip { and } off the UUID.
map[id] = uuid;
Preferences.set(PREF_NAME, JSON.stringify(map));
return uuid;
}
// Represents the data contained in an extension, contained either
// in a directory or a zip file, which may or may not be installed.
// This class implements the functionality of the Extension class,
@ -798,7 +851,7 @@ this.ExtensionData = class {
throw new Error("getURL may not be called before an `id` or `uuid` has been set");
}
if (!this.uuid) {
this.uuid = getExtensionUUID(this.id);
this.uuid = UUIDMap.get(this.id);
}
return `moz-extension://${this.uuid}/${path}`;
}
@ -1173,7 +1226,7 @@ class MockExtension {
}
shutdown() {
this.addon.uninstall(true);
this.addon.uninstall();
return this.cleanupGeneratedFile();
}
@ -1189,7 +1242,7 @@ this.Extension = class extends ExtensionData {
constructor(addonData) {
super(addonData.resourceURI);
this.uuid = getExtensionUUID(addonData.id);
this.uuid = UUIDMap.get(addonData.id);
if (addonData.cleanupFile) {
Services.obs.addObserver(this, "xpcom-shutdown", false);

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

@ -24,3 +24,4 @@ skip-if = buildapp == 'b2g'
[test_ext_jsversion.html]
skip-if = buildapp == 'b2g'
[test_ext_schema.html]
[test_chrome_ext_storage_cleanup.html]

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

@ -18,18 +18,13 @@ const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://testing-common/TestUtils.jsm");
const {
GlobalManager,
UninstallObserver,
} = Cu.import("resource://gre/modules/Extension.jsm");
const {GlobalManager} = Cu.import("resource://gre/modules/Extension.jsm");
/* eslint-disable mozilla/balanced-listeners */
add_task(function* testShutdownCleanup() {
is(GlobalManager.initialized, false,
"GlobalManager start as not initialized");
is(UninstallObserver.initialized, false,
"UninstallObserver start as not initialized");
let extension = ExtensionTestUtils.loadExtension({
background: "new " + function() {
@ -43,15 +38,11 @@ add_task(function* testShutdownCleanup() {
is(GlobalManager.initialized, true,
"GlobalManager has been initialized once an extension is started");
is(UninstallObserver.initialized, true,
"UninstallObserver has been initialized once an extension is started");
yield extension.unload();
is(GlobalManager.initialized, false,
"GlobalManager has been uninitialized once all the webextensions have been stopped");
is(UninstallObserver.initialized, false,
"UninstallObserver has been uninitialized once all the webextensions have been stopped");
});
</script>

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

@ -0,0 +1,161 @@
<!DOCTYPE HTML>
<html>
<head>
<title>WebExtension test</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/ExtensionTestUtils.js"></script>
<script type="text/javascript" src="head.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"/>
</head>
<body>
<script type="text/javascript">
"use strict";
// Test that storage used by a webextension (through localStorage,
// indexedDB, and browser.storage.local) gets cleaned up when the
// extension is uninstalled.
add_task(function* test_uninstall() {
function writeData() {
localStorage.setItem("hello", "world");
let idbPromise = new Promise((resolve, reject) => {
let req = indexedDB.open("test");
req.onerror = e => {
reject(new Error(`indexedDB open failed with ${e.errorCode}`));
};
req.onupgradeneeded = e => {
let db = e.target.result;
db.createObjectStore("store", {keyPath: "name"});
};
req.onsuccess = e => {
let db = e.target.result;
let transaction = db.transaction("store", "readwrite");
let addreq = transaction.objectStore("store")
.add({name: "hello", value: "world"});
addreq.onerror = e => {
reject(new Error(`add to indexedDB failed with ${e.errorCode}`));
};
addreq.onsuccess = e => {
resolve();
};
};
});
let browserStoragePromise = browser.storage.local.set({hello: "world"});
Promise.all([idbPromise, browserStoragePromise]).then(() => {
browser.test.sendMessage("finished");
});
}
function readData() {
let matchLocalStorage = (localStorage.getItem("hello") == "world");
let idbPromise = new Promise((resolve, reject) => {
let req = indexedDB.open("test");
req.onerror = e => {
reject(new Error(`indexedDB open failed with ${e.errorCode}`));
};
req.onupgradeneeded = e => {
// no database, data is not present
resolve(false);
};
req.onsuccess = e => {
let db = e.target.result;
let transaction = db.transaction("store", "readwrite");
let addreq = transaction.objectStore("store").get("hello");
addreq.onerror = e => {
reject(new Error(`read from indexedDB failed with ${e.errorCode}`));
};
addreq.onsuccess = e => {
let match = (addreq.result.value == "world");
resolve(match);
};
};
});
let browserStoragePromise = browser.storage.local.get("hello").then(result => {
return (Object.keys(result).length == 1 && result.hello == "world");
});
Promise.all([idbPromise, browserStoragePromise])
.then(([matchIDB, matchBrowserStorage]) => {
let result = {matchLocalStorage, matchIDB, matchBrowserStorage};
browser.test.sendMessage("results", result);
});
}
const ID = "storage.cleanup@tests.mozilla.org";
// Use a test-only pref to leave the addonid->uuid mapping around after
// uninstall so that we can re-attach to the same storage. Also set
// the pref to prevent cleaning up storage on uninstall so we can test
// that the "keep uuid" logic works correctly. Do the storage flag in
// a separate prefEnv so we can pop it below, leaving the uuid flag set.
yield SpecialPowers.pushPrefEnv({
set: [["extensions.webextensions.keepUuidOnUninstall", true]],
});
yield SpecialPowers.pushPrefEnv({
set: [["extensions.webextensions.keepStorageOnUninstall", true]],
});
let extension = ExtensionTestUtils.loadExtension({
background: `(${writeData})()`,
manifest: {
permissions: ["storage"],
},
useAddonManager: "temporary",
}, ID);
yield extension.startup();
yield extension.awaitMessage("finished");
yield extension.unload();
// Check that we can still see data we wrote to storage but clear the
// "leave storage" flag so our storaged gets cleared on uninstall.
// This effectively tests the keepUuidOnUninstall logic, which ensures
// that when we read storage again and check that it is cleared, that
// it is actually a meaningful test!
yield SpecialPowers.popPrefEnv();
extension = ExtensionTestUtils.loadExtension({
background: `(${readData})()`,
manifest: {
permissions: ["storage"],
},
useAddonManager: "temporary",
}, ID);
yield extension.startup();
let results = yield extension.awaitMessage("results");
is(results.matchLocalStorage, true, "localStorage data is still present");
is(results.matchIDB, true, "indexedDB data is still present");
is(results.matchBrowserStorage, true, "browser.storage.local data is still present");
yield extension.unload();
// Read again. This time, our data should be gone.
extension = ExtensionTestUtils.loadExtension({
background: `(${readData})()`,
manifest: {
permissions: ["storage"],
},
useAddonManager: "temporary",
}, ID);
yield extension.startup();
results = yield extension.awaitMessage("results");
is(results.matchLocalStorage, false, "localStorage data was cleared");
is(results.matchIDB, false, "indexedDB data was cleared");
is(results.matchBrowserStorage, false, "browser.storage.local data was cleared");
yield extension.unload();
});
</script>
</body>
</html>