mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-12-11 16:32:59 +00:00
Merge mozilla-central to autoland. a=merge CLOSED TREE
This commit is contained in:
commit
3c50127358
@ -45,6 +45,12 @@ class LSDatabase final {
|
||||
mActor = nullptr;
|
||||
}
|
||||
|
||||
bool HasActiveSnapshot() const {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
return !!mSnapshot;
|
||||
}
|
||||
|
||||
bool IsAllowedToClose() const {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
|
@ -766,6 +766,24 @@ void LSObject::EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
}
|
||||
}
|
||||
|
||||
bool LSObject::GetHasActiveSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) {
|
||||
AssertIsOnOwningThread();
|
||||
|
||||
if (!CanUseStorage(aSubjectPrincipal)) {
|
||||
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (mDatabase && mDatabase->HasActiveSnapshot()) {
|
||||
MOZ_ASSERT(!mDatabase->IsAllowedToClose());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(LSObject, Storage)
|
||||
NS_IMPL_RELEASE_INHERITED(LSObject, Storage)
|
||||
|
||||
|
@ -173,6 +173,9 @@ class LSObject final : public Storage {
|
||||
void EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
bool GetHasActiveSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aError) override;
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
@ -7,6 +7,8 @@
|
||||
with Files("**"):
|
||||
BUG_COMPONENT = ("Core", "DOM: Web Storage")
|
||||
|
||||
MOCHITEST_MANIFESTS += ['test/mochitest.ini']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += [
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
module.exports = {
|
||||
"extends": [
|
||||
"plugin:mozilla/mochitest-test",
|
||||
"plugin:mozilla/browser-test",
|
||||
],
|
||||
};
|
||||
|
74
dom/localstorage/test/helpers.js
Normal file
74
dom/localstorage/test/helpers.js
Normal file
@ -0,0 +1,74 @@
|
||||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
// testSteps is expected to be defined by the test using this file.
|
||||
/* global testSteps:false */
|
||||
|
||||
function executeSoon(aFun) {
|
||||
SpecialPowers.Services.tm.dispatchToMainThread({
|
||||
run() {
|
||||
aFun();
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
function clearAllDatabases() {
|
||||
let qms = SpecialPowers.Services.qms;
|
||||
let principal = SpecialPowers.wrap(document).nodePrincipal;
|
||||
let request = qms.clearStoragesForPrincipal(principal);
|
||||
return request;
|
||||
}
|
||||
|
||||
if (!window.runTest) {
|
||||
window.runTest = async function() {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
info("Pushing preferences");
|
||||
|
||||
await SpecialPowers.pushPrefEnv(
|
||||
{
|
||||
set: [
|
||||
["dom.storage.testing", true],
|
||||
["dom.quotaManager.testing", true],
|
||||
],
|
||||
});
|
||||
|
||||
info("Clearing old databases");
|
||||
|
||||
await requestFinished(clearAllDatabases());
|
||||
|
||||
ok(typeof testSteps === "function", "There should be a testSteps function");
|
||||
ok(testSteps.constructor.name === "AsyncFunction",
|
||||
"testSteps should be an async function");
|
||||
|
||||
SimpleTest.registerCleanupFunction(async function() {
|
||||
await requestFinished(clearAllDatabases());
|
||||
});
|
||||
|
||||
add_task(testSteps);
|
||||
};
|
||||
}
|
||||
|
||||
function returnToEventLoop() {
|
||||
return new Promise(function(resolve) {
|
||||
executeSoon(resolve);
|
||||
});
|
||||
}
|
||||
|
||||
function getLocalStorage() {
|
||||
return localStorage;
|
||||
}
|
||||
|
||||
function requestFinished(request) {
|
||||
return new Promise(function(resolve, reject) {
|
||||
request.callback = SpecialPowers.wrapCallback(function(requestInner) {
|
||||
if (requestInner.resultCode === SpecialPowers.Cr.NS_OK) {
|
||||
resolve(requestInner.result);
|
||||
} else {
|
||||
reject(requestInner.resultCode);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
10
dom/localstorage/test/mochitest.ini
Normal file
10
dom/localstorage/test/mochitest.ini
Normal file
@ -0,0 +1,10 @@
|
||||
# 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/.
|
||||
|
||||
[DEFAULT]
|
||||
support-files =
|
||||
helpers.js
|
||||
unit/test_largeItems.js
|
||||
|
||||
[test_largeItems.html]
|
19
dom/localstorage/test/test_largeItems.html
Normal file
19
dom/localstorage/test/test_largeItems.html
Normal file
@ -0,0 +1,19 @@
|
||||
<!--
|
||||
Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
-->
|
||||
<html>
|
||||
<head>
|
||||
<title>Large Items Test</title>
|
||||
|
||||
<script src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
|
||||
<script type="text/javascript" src="unit/test_largeItems.js"></script>
|
||||
<script type="text/javascript" src="helpers.js"></script>
|
||||
|
||||
</head>
|
||||
|
||||
<body onload="runTest();"></body>
|
||||
|
||||
</html>
|
@ -28,6 +28,8 @@ if (!this.runTest) {
|
||||
|
||||
enableTesting();
|
||||
|
||||
Cu.importGlobalProperties(["crypto"]);
|
||||
|
||||
Assert.ok(typeof testSteps === "function",
|
||||
"There should be a testSteps function");
|
||||
Assert.ok(testSteps.constructor.name === "AsyncFunction",
|
||||
@ -227,9 +229,13 @@ function getCurrentPrincipal() {
|
||||
return Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
|
||||
}
|
||||
|
||||
function getDefaultPrincipal() {
|
||||
return getPrincipal("http://example.com");
|
||||
}
|
||||
|
||||
function getLocalStorage(principal) {
|
||||
if (!principal) {
|
||||
principal = getCurrentPrincipal();
|
||||
principal = getDefaultPrincipal();
|
||||
}
|
||||
|
||||
return Services.domStorageManager.createStorage(null, principal, principal, "");
|
||||
|
84
dom/localstorage/test/unit/test_largeItems.js
Normal file
84
dom/localstorage/test/unit/test_largeItems.js
Normal file
@ -0,0 +1,84 @@
|
||||
/**
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test repeatedly setting values that are just under the LocalStorage quota
|
||||
* limit without yielding control flow in order to verify that the write
|
||||
* optimizer is present / works. If there was no write optimizer present, the
|
||||
* IPC message size limit would be exceeded, resulting in a crash.
|
||||
*/
|
||||
|
||||
async function testSteps() {
|
||||
const globalLimitKB = 5 * 1024;
|
||||
|
||||
// 18 and more iterations would produce an IPC message with size greater than
|
||||
// 256 MB if write optimizer was not present. This number was determined
|
||||
// experimentally by running the test with disabled write optimizer.
|
||||
const numberOfIterations = 18;
|
||||
|
||||
const randomStringBlockSize = 65536;
|
||||
|
||||
// We need to use a random string because LS internally tries to compress
|
||||
// values.
|
||||
function getRandomString(size) {
|
||||
let crypto = this.window ? this.window.crypto : this.crypto;
|
||||
let decoder = new TextDecoder("ISO-8859-2");
|
||||
|
||||
function getRandomStringBlock(array) {
|
||||
crypto.getRandomValues(array);
|
||||
return decoder.decode(array);
|
||||
}
|
||||
|
||||
let string = "";
|
||||
|
||||
let quotient = size / randomStringBlockSize;
|
||||
if (quotient) {
|
||||
let array = new Uint8Array(randomStringBlockSize);
|
||||
for (let i = 1; i <= quotient; i++) {
|
||||
string += getRandomStringBlock(array);
|
||||
}
|
||||
}
|
||||
|
||||
let remainder = size % randomStringBlockSize;
|
||||
if (remainder) {
|
||||
let array = new Uint8Array(remainder);
|
||||
string += getRandomStringBlock(array);
|
||||
}
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
const data = {};
|
||||
data.key = "foo";
|
||||
data.value = getRandomString(globalLimitKB * 1024 - data.key.length -
|
||||
numberOfIterations.toString().length);
|
||||
|
||||
info("Setting pref");
|
||||
|
||||
// By disabling snapshot reusing, we guarantee that the snapshot will be
|
||||
// checkpointed once control returns to the event loop.
|
||||
if (this.window) {
|
||||
await SpecialPowers.pushPrefEnv(
|
||||
{ set: [["dom.storage.snapshot_reusing", false]] });
|
||||
} else {
|
||||
Services.prefs.setBoolPref("dom.storage.snapshot_reusing", false);
|
||||
}
|
||||
|
||||
info("Getting storage");
|
||||
|
||||
let storage = getLocalStorage();
|
||||
|
||||
info("Adding/updating item");
|
||||
|
||||
for (var i = 0; i < numberOfIterations; i++) {
|
||||
storage.setItem(data.key, data.value + i);
|
||||
}
|
||||
|
||||
info("Returning to event loop");
|
||||
|
||||
await returnToEventLoop();
|
||||
|
||||
ok(!storage.hasActiveSnapshot, "Snapshot successfully finished");
|
||||
}
|
@ -34,6 +34,7 @@ run-sequentially = this test depends on a file produced by test_databaseShadowin
|
||||
[test_eviction.js]
|
||||
[test_groupLimit.js]
|
||||
[test_groupMismatch.js]
|
||||
[test_largeItems.js]
|
||||
[test_migration.js]
|
||||
[test_orderingAfterRemoveAdd.js]
|
||||
[test_originInit.js]
|
||||
|
@ -4120,6 +4120,10 @@ nsresult QuotaManager::InitializeRepository(PersistenceType aPersistenceType) {
|
||||
while (NS_SUCCEEDED(
|
||||
(rv = entries->GetNextFile(getter_AddRefs(childDirectory)))) &&
|
||||
childDirectory) {
|
||||
if (NS_WARN_IF(IsShuttingDown())) {
|
||||
RETURN_STATUS_OR_RESULT(statusKeeper, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
bool isDirectory;
|
||||
rv = childDirectory->IsDirectory(&isDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
@ -4222,6 +4226,10 @@ nsresult QuotaManager::InitializeOrigin(PersistenceType aPersistenceType,
|
||||
nsCOMPtr<nsIFile> file;
|
||||
while (NS_SUCCEEDED((rv = entries->GetNextFile(getter_AddRefs(file)))) &&
|
||||
file) {
|
||||
if (NS_WARN_IF(IsShuttingDown())) {
|
||||
RETURN_STATUS_OR_RESULT(statusKeeper, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
bool isDirectory;
|
||||
rv = file->IsDirectory(&isDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
@ -5808,6 +5816,10 @@ nsresult QuotaManager::EnsureTemporaryStorageIsInitialized() {
|
||||
}
|
||||
});
|
||||
|
||||
if (NS_WARN_IF(IsShuttingDown())) {
|
||||
RETURN_STATUS_OR_RESULT(statusKeeper, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
nsresult rv = InitializeRepository(PERSISTENCE_TYPE_DEFAULT);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
RECORD_IN_NIGHTLY(statusKeeper, rv);
|
||||
@ -5820,6 +5832,10 @@ nsresult QuotaManager::EnsureTemporaryStorageIsInitialized() {
|
||||
#endif
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(IsShuttingDown())) {
|
||||
RETURN_STATUS_OR_RESULT(statusKeeper, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
rv = InitializeRepository(PERSISTENCE_TYPE_TEMPORARY);
|
||||
if (NS_WARN_IF(NS_FAILED(rv)) || NS_FAILED(statusKeeper)) {
|
||||
// We have to cleanup partially initialized quota.
|
||||
|
@ -57,6 +57,9 @@
|
||||
} while (0)
|
||||
|
||||
# define CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(_dummy) continue
|
||||
|
||||
# define RETURN_STATUS_OR_RESULT(_status, _rv) \
|
||||
return NS_FAILED(_status) ? _status : _rv
|
||||
#else
|
||||
# define REPORT_TELEMETRY_INIT_ERR(_key, _label) \
|
||||
{}
|
||||
@ -68,6 +71,8 @@
|
||||
{}
|
||||
|
||||
# define CONTINUE_IN_NIGHTLY_RETURN_IN_OTHERS(_rv) return _rv
|
||||
|
||||
# define RETURN_STATUS_OR_RESULT(_status, _rv) return _rv
|
||||
#endif
|
||||
|
||||
class nsIEventTarget;
|
||||
|
@ -110,6 +110,11 @@ class Storage : public nsISupports, public nsWrapperCache {
|
||||
virtual void EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aRv) {}
|
||||
|
||||
virtual bool GetHasActiveSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||
ErrorResult& aRv) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// Dispatch storage notification events on all impacted pages in the current
|
||||
|
@ -70,4 +70,11 @@ partial interface Storage {
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
void endExplicitSnapshot();
|
||||
|
||||
/**
|
||||
* Returns true if the underlying database has been opened and it has an
|
||||
* active snapshot (initialized implicitly or explicitly).
|
||||
*/
|
||||
[Throws, NeedsSubjectPrincipal, Pref="dom.storage.testing"]
|
||||
readonly attribute boolean hasActiveSnapshot;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user