зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-inbound to mozilla-central a=merge
This commit is contained in:
Коммит
530163105d
|
@ -190,6 +190,7 @@ dom/grid/**
|
||||||
dom/html/**
|
dom/html/**
|
||||||
dom/ipc/**
|
dom/ipc/**
|
||||||
dom/jsurl/**
|
dom/jsurl/**
|
||||||
|
dom/localstorage/**
|
||||||
dom/manifest/**
|
dom/manifest/**
|
||||||
dom/media/test/**
|
dom/media/test/**
|
||||||
dom/media/tests/**
|
dom/media/tests/**
|
||||||
|
|
|
@ -277,7 +277,7 @@ add_task(async function deleteStorageInAboutURL() {
|
||||||
|
|
||||||
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("about:newtab");
|
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("about:newtab");
|
||||||
await new Promise(aResolve => {
|
await new Promise(aResolve => {
|
||||||
let req = Services.qms.clearStoragesForPrincipal(principal, null, false);
|
let req = Services.qms.clearStoragesForPrincipal(principal);
|
||||||
req.callback = () => { aResolve(); };
|
req.callback = () => { aResolve(); };
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -313,7 +313,7 @@ add_task(async function deleteStorageOnlyCustomPermissionInAboutURL() {
|
||||||
|
|
||||||
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("about:newtab");
|
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin("about:newtab");
|
||||||
await new Promise(aResolve => {
|
await new Promise(aResolve => {
|
||||||
let req = Services.qms.clearStoragesForPrincipal(principal, null, false);
|
let req = Services.qms.clearStoragesForPrincipal(principal);
|
||||||
req.callback = () => { aResolve(); };
|
req.callback = () => { aResolve(); };
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
support-files =
|
support-files =
|
||||||
head.js
|
head.js
|
||||||
file_install_extensions.html
|
file_install_extensions.html
|
||||||
browser_legacy.xpi
|
|
||||||
browser_legacy_webext.xpi
|
browser_legacy_webext.xpi
|
||||||
browser_webext_permissions.xpi
|
browser_webext_permissions.xpi
|
||||||
browser_webext_nopermissions.xpi
|
browser_webext_nopermissions.xpi
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
const {AddonManagerPrivate} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
|
const {AddonManagerPrivate} = ChromeUtils.import("resource://gre/modules/AddonManager.jsm", {});
|
||||||
|
|
||||||
const ID_PERMS = "update_perms@tests.mozilla.org";
|
const ID_PERMS = "update_perms@tests.mozilla.org";
|
||||||
const ID_LEGACY = "legacy_update@tests.mozilla.org";
|
|
||||||
const ID_ORIGINS = "update_origins@tests.mozilla.org";
|
const ID_ORIGINS = "update_origins@tests.mozilla.org";
|
||||||
|
|
||||||
function getBadgeStatus() {
|
function getBadgeStatus() {
|
||||||
|
@ -80,11 +79,6 @@ async function testNoPrompt(origUrl, id) {
|
||||||
add_task(() => testNoPrompt(`${BASE}/browser_webext_update_perms1.xpi`,
|
add_task(() => testNoPrompt(`${BASE}/browser_webext_update_perms1.xpi`,
|
||||||
ID_PERMS));
|
ID_PERMS));
|
||||||
|
|
||||||
// Test that an update from a legacy extension to a webextension
|
|
||||||
// doesn't show a prompt even when the webextension uses
|
|
||||||
// promptable required permissions.
|
|
||||||
add_task(() => testNoPrompt(`${BASE}/browser_legacy.xpi`, ID_LEGACY));
|
|
||||||
|
|
||||||
// Test that an update that narrows origin permissions is just applied without
|
// Test that an update that narrows origin permissions is just applied without
|
||||||
// showing a notification promt
|
// showing a notification promt
|
||||||
add_task(() => testNoPrompt(`${BASE}/browser_webext_update_origins1.xpi`,
|
add_task(() => testNoPrompt(`${BASE}/browser_webext_update_origins1.xpi`,
|
||||||
|
|
Двоичные данные
browser/base/content/test/webextensions/browser_legacy.xpi
Двоичные данные
browser/base/content/test/webextensions/browser_legacy.xpi
Двоичный файл не отображается.
|
@ -51,11 +51,6 @@ async function testUpdateNoPrompt(filename, id,
|
||||||
await addon.uninstall();
|
await addon.uninstall();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test that we don't see a prompt when updating from a legacy
|
|
||||||
// extension to a webextension.
|
|
||||||
add_task(() => testUpdateNoPrompt("browser_legacy.xpi",
|
|
||||||
"legacy_update@tests.mozilla.org", "1.1"));
|
|
||||||
|
|
||||||
// Test that we don't see a prompt when no new promptable permissions
|
// Test that we don't see a prompt when no new promptable permissions
|
||||||
// are added.
|
// are added.
|
||||||
add_task(() => testUpdateNoPrompt("browser_webext_update_perms1.xpi",
|
add_task(() => testUpdateNoPrompt("browser_webext_update_perms1.xpi",
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
background-position: 9px center;
|
background-position: 9px center;
|
||||||
background-size: 12px 12px;
|
background-size: 12px 12px;
|
||||||
padding-left: 30px;
|
padding-left: 30px;
|
||||||
|
z-index: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#prefs {
|
#prefs {
|
||||||
|
@ -39,11 +40,23 @@
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#prefs > tr.locked {
|
||||||
|
opacity: 0.4;
|
||||||
|
background-image: url("chrome://browser/skin/preferences/in-content/privacy-security.svg");
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-position: 9px center;
|
||||||
|
background-size: 16px 16px;
|
||||||
|
}
|
||||||
|
|
||||||
#prefs > tr > td {
|
#prefs > tr > td {
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
width: 50%;
|
width: 50%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#prefs > tr > td.cell-name {
|
||||||
|
padding-inline-start: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
.cell-value {
|
.cell-value {
|
||||||
word-break: break-all;
|
word-break: break-all;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,14 +19,16 @@ function onLoad() {
|
||||||
let hasUserValue = Services.prefs.prefHasUserValue(name);
|
let hasUserValue = Services.prefs.prefHasUserValue(name);
|
||||||
let pref = {
|
let pref = {
|
||||||
name,
|
name,
|
||||||
value: Preferences.get(name),
|
|
||||||
hasUserValue,
|
hasUserValue,
|
||||||
hasDefaultValue: hasUserValue ? prefHasDefaultValue(name) : true,
|
hasDefaultValue: hasUserValue ? prefHasDefaultValue(name) : true,
|
||||||
|
isLocked: Services.prefs.prefIsLocked(name),
|
||||||
};
|
};
|
||||||
// Try in case it's a localized string.
|
// Try in case it's a localized string or locked user added pref
|
||||||
// Throws an exception if there is no equivalent value in the localized files for the pref.
|
|
||||||
// If an execption is thrown the pref value is set to the empty string.
|
// If an execption is thrown the pref value is set to the empty string.
|
||||||
try {
|
try {
|
||||||
|
// Throws an exception in case locked user added pref without default value
|
||||||
|
pref.value = Preferences.get(name);
|
||||||
|
// Throws an exception if there is no equivalent value in the localized files for the pref.
|
||||||
if (!pref.hasUserValue && /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value)) {
|
if (!pref.hasUserValue && /^chrome:\/\/.+\/locale\/.+\.properties/.test(pref.value)) {
|
||||||
pref.value = Services.prefs.getComplexValue(name, Ci.nsIPrefLocalizedString).data;
|
pref.value = Services.prefs.getComplexValue(name, Ci.nsIPrefLocalizedString).data;
|
||||||
}
|
}
|
||||||
|
@ -121,6 +123,9 @@ function createPrefsFragment(prefArray) {
|
||||||
if (pref.hasUserValue) {
|
if (pref.hasUserValue) {
|
||||||
row.classList.add("has-user-value");
|
row.classList.add("has-user-value");
|
||||||
}
|
}
|
||||||
|
if (pref.isLocked) {
|
||||||
|
row.classList.add("locked");
|
||||||
|
}
|
||||||
row.setAttribute("aria-label", pref.name);
|
row.setAttribute("aria-label", pref.name);
|
||||||
|
|
||||||
row.appendChild(getPrefRow(pref));
|
row.appendChild(getPrefRow(pref));
|
||||||
|
@ -135,6 +140,7 @@ function createNewPrefFragment(name) {
|
||||||
row.classList.add("has-user-value");
|
row.classList.add("has-user-value");
|
||||||
row.setAttribute("aria-label", name);
|
row.setAttribute("aria-label", name);
|
||||||
let nameCell = document.createElement("td");
|
let nameCell = document.createElement("td");
|
||||||
|
nameCell.className = "cell-name";
|
||||||
nameCell.append(name);
|
nameCell.append(name);
|
||||||
row.appendChild(nameCell);
|
row.appendChild(nameCell);
|
||||||
|
|
||||||
|
@ -164,6 +170,7 @@ function createNewPrefFragment(name) {
|
||||||
function getPrefRow(pref) {
|
function getPrefRow(pref) {
|
||||||
let rowFragment = document.createDocumentFragment();
|
let rowFragment = document.createDocumentFragment();
|
||||||
let nameCell = document.createElement("td");
|
let nameCell = document.createElement("td");
|
||||||
|
nameCell.className = "cell-name";
|
||||||
// Add <wbr> behind dots to prevent line breaking in random mid-word places.
|
// Add <wbr> behind dots to prevent line breaking in random mid-word places.
|
||||||
let parts = pref.name.split(".");
|
let parts = pref.name.split(".");
|
||||||
for (let i = 0; i < parts.length - 1; i++) {
|
for (let i = 0; i < parts.length - 1; i++) {
|
||||||
|
@ -187,11 +194,14 @@ function getPrefRow(pref) {
|
||||||
document.l10n.setAttributes(button, "about-config-pref-edit");
|
document.l10n.setAttributes(button, "about-config-pref-edit");
|
||||||
button.className = "button-edit";
|
button.className = "button-edit";
|
||||||
}
|
}
|
||||||
|
if (pref.isLocked) {
|
||||||
|
button.disabled = true;
|
||||||
|
}
|
||||||
editCell.appendChild(button);
|
editCell.appendChild(button);
|
||||||
rowFragment.appendChild(editCell);
|
rowFragment.appendChild(editCell);
|
||||||
|
|
||||||
let buttonCell = document.createElement("td");
|
let buttonCell = document.createElement("td");
|
||||||
if (pref.hasUserValue) {
|
if (!pref.isLocked && pref.hasUserValue) {
|
||||||
let resetButton = document.createElement("button");
|
let resetButton = document.createElement("button");
|
||||||
if (!pref.hasDefaultValue) {
|
if (!pref.hasDefaultValue) {
|
||||||
document.l10n.setAttributes(resetButton, "about-config-pref-delete");
|
document.l10n.setAttributes(resetButton, "about-config-pref-delete");
|
||||||
|
@ -201,6 +211,7 @@ function getPrefRow(pref) {
|
||||||
}
|
}
|
||||||
buttonCell.appendChild(resetButton);
|
buttonCell.appendChild(resetButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
rowFragment.appendChild(buttonCell);
|
rowFragment.appendChild(buttonCell);
|
||||||
return rowFragment;
|
return rowFragment;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,3 +5,5 @@
|
||||||
skip-if = debug # Bug 1507747
|
skip-if = debug # Bug 1507747
|
||||||
[browser_search.js]
|
[browser_search.js]
|
||||||
skip-if = debug # Bug 1507747
|
skip-if = debug # Bug 1507747
|
||||||
|
[browser_locked.js]
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const PAGE_URL = "chrome://browser/content/aboutconfig/aboutconfig.html";
|
||||||
|
|
||||||
|
add_task(async function setup() {
|
||||||
|
await SpecialPowers.pushPrefEnv({
|
||||||
|
set: [
|
||||||
|
["test.aboutconfig.a", "some value"],
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_locked() {
|
||||||
|
registerCleanupFunction(() => {
|
||||||
|
Services.prefs.unlockPref("browser.search.searchEnginesURL");
|
||||||
|
Services.prefs.unlockPref("test.aboutconfig.a");
|
||||||
|
Services.prefs.unlockPref("accessibility.AOM.enabled");
|
||||||
|
});
|
||||||
|
|
||||||
|
Services.prefs.lockPref("browser.search.searchEnginesURL");
|
||||||
|
Services.prefs.lockPref("test.aboutconfig.a");
|
||||||
|
Services.prefs.lockPref("accessibility.AOM.enabled");
|
||||||
|
await BrowserTestUtils.withNewTab({
|
||||||
|
gBrowser,
|
||||||
|
url: PAGE_URL,
|
||||||
|
}, async browser => {
|
||||||
|
await ContentTask.spawn(browser, null, () => {
|
||||||
|
let list = [...content.document.getElementById("prefs")
|
||||||
|
.getElementsByTagName("tr")];
|
||||||
|
function getRow(name) {
|
||||||
|
return list.find(row => row.querySelector("td").textContent == name);
|
||||||
|
}
|
||||||
|
function getValue(name) {
|
||||||
|
return getRow(name).querySelector("td.cell-value").textContent;
|
||||||
|
}
|
||||||
|
function getButton(name) {
|
||||||
|
return getRow(name).querySelector("button");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test locked default string pref.
|
||||||
|
let lockedPref = getRow("browser.search.searchEnginesURL");
|
||||||
|
Assert.ok(lockedPref.classList.contains("locked"));
|
||||||
|
Assert.equal(getValue("browser.search.searchEnginesURL"), "https://addons.mozilla.org/%LOCALE%/firefox/search-engines/");
|
||||||
|
Assert.ok(getButton("browser.search.searchEnginesURL").classList.contains("button-edit"));
|
||||||
|
Assert.equal(getButton("browser.search.searchEnginesURL").disabled, true);
|
||||||
|
|
||||||
|
// Test locked default boolean pref.
|
||||||
|
lockedPref = getRow("accessibility.AOM.enabled");
|
||||||
|
Assert.ok(lockedPref.classList.contains("locked"));
|
||||||
|
Assert.equal(getValue("accessibility.AOM.enabled"), "false");
|
||||||
|
Assert.ok(getButton("accessibility.AOM.enabled").classList.contains("button-toggle"));
|
||||||
|
Assert.equal(getButton("accessibility.AOM.enabled").disabled, true);
|
||||||
|
|
||||||
|
// Test locked user added pref.
|
||||||
|
lockedPref = getRow("test.aboutconfig.a");
|
||||||
|
Assert.ok(lockedPref.classList.contains("locked"));
|
||||||
|
Assert.equal(getValue("test.aboutconfig.a"), "");
|
||||||
|
Assert.ok(getButton("test.aboutconfig.a").classList.contains("button-edit"));
|
||||||
|
Assert.equal(getButton("test.aboutconfig.a").disabled, true);
|
||||||
|
|
||||||
|
// Test pref not locked
|
||||||
|
let unlockedPref = getRow("accessibility.indicator.enabled");
|
||||||
|
Assert.equal(unlockedPref.classList.contains("locked"), false);
|
||||||
|
Assert.equal(getValue("accessibility.indicator.enabled"), "false");
|
||||||
|
Assert.ok(getButton("accessibility.indicator.enabled").classList.contains("button-toggle"));
|
||||||
|
Assert.equal(getButton("accessibility.indicator.enabled").disabled, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -92,6 +92,10 @@ async function checkIndexedDB(browser) {
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
is(e.name, "NotFoundError", "The indexedDB does not exist as expected");
|
is(e.name, "NotFoundError", "The indexedDB does not exist as expected");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
db.close();
|
||||||
|
|
||||||
|
content.indexedDB.deleteDatabase("idb");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -127,7 +131,12 @@ add_task(async function test_quota_clearStoragesForPrincipal() {
|
||||||
let httpURI = caUtils.makeURI("http://" + TEST_HOST);
|
let httpURI = caUtils.makeURI("http://" + TEST_HOST);
|
||||||
let httpPrincipal = Services.scriptSecurityManager
|
let httpPrincipal = Services.scriptSecurityManager
|
||||||
.createCodebasePrincipal(httpURI, {});
|
.createCodebasePrincipal(httpURI, {});
|
||||||
Services.qms.clearStoragesForPrincipal(httpPrincipal, null, true);
|
let clearRequest = Services.qms.clearStoragesForPrincipal(httpPrincipal, null, null, true);
|
||||||
|
await new Promise(resolve => {
|
||||||
|
clearRequest.callback = () => {
|
||||||
|
resolve();
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
for (let userContextId of Object.keys(USER_CONTEXTS)) {
|
for (let userContextId of Object.keys(USER_CONTEXTS)) {
|
||||||
// Open our tab in the given user context.
|
// Open our tab in the given user context.
|
||||||
|
|
|
@ -77,22 +77,26 @@ const clearHistory = options => {
|
||||||
const clearIndexedDB = async function(options) {
|
const clearIndexedDB = async function(options) {
|
||||||
let promises = [];
|
let promises = [];
|
||||||
|
|
||||||
await new Promise(resolve => {
|
await new Promise((resolve, reject) => {
|
||||||
quotaManagerService.getUsage(request => {
|
quotaManagerService.getUsage(request => {
|
||||||
if (request.resultCode != Cr.NS_OK) {
|
if (request.resultCode != Cr.NS_OK) {
|
||||||
// We are probably shutting down. We don't want to propagate the error,
|
reject({message: "Clear indexedDB failed"});
|
||||||
// rejecting the promise.
|
|
||||||
resolve();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let item of request.result) {
|
for (let item of request.result) {
|
||||||
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
|
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
|
||||||
let uri = principal.URI;
|
let scheme = principal.URI.scheme;
|
||||||
if (uri.scheme == "http" || uri.scheme == "https" || uri.scheme == "file") {
|
if (scheme == "http" || scheme == "https" || scheme == "file") {
|
||||||
promises.push(new Promise(r => {
|
promises.push(new Promise((resolve, reject) => {
|
||||||
let req = quotaManagerService.clearStoragesForPrincipal(principal, null, false);
|
let clearRequest = quotaManagerService.clearStoragesForPrincipal(principal, null, "idb");
|
||||||
req.callback = () => { r(); };
|
clearRequest.callback = () => {
|
||||||
|
if (clearRequest.resultCode == Cr.NS_OK) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject({message: "Clear indexedDB failed"});
|
||||||
|
}
|
||||||
|
};
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -109,6 +113,46 @@ const clearLocalStorage = async function(options) {
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
{message: "Firefox does not support clearing localStorage with 'since'."});
|
{message: "Firefox does not support clearing localStorage with 'since'."});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (Services.lsm.nextGenLocalStorageEnabled) {
|
||||||
|
// Ideally we could reuse the logic in Sanitizer.jsm or nsIClearDataService,
|
||||||
|
// but this API exposes an ability to wipe data at a much finger granularity
|
||||||
|
// than those APIs. So custom logic is used here to wipe only the QM
|
||||||
|
// localStorage client (when in use).
|
||||||
|
|
||||||
|
let promises = [];
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
quotaManagerService.getUsage(request => {
|
||||||
|
if (request.resultCode != Cr.NS_OK) {
|
||||||
|
reject({message: "Clear localStorage failed"});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let item of request.result) {
|
||||||
|
let principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(item.origin);
|
||||||
|
let host = principal.URI.hostPort;
|
||||||
|
if (!options.hostnames || options.hostnames.includes(host)) {
|
||||||
|
promises.push(new Promise((resolve, reject) => {
|
||||||
|
let clearRequest = quotaManagerService.clearStoragesForPrincipal(principal, "default", "ls");
|
||||||
|
clearRequest.callback = () => {
|
||||||
|
if (clearRequest.resultCode == Cr.NS_OK) {
|
||||||
|
resolve();
|
||||||
|
} else {
|
||||||
|
reject({message: "Clear localStorage failed"});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolve();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return Promise.all(promises);
|
||||||
|
}
|
||||||
|
|
||||||
if (options.hostnames) {
|
if (options.hostnames) {
|
||||||
for (let hostname of options.hostnames) {
|
for (let hostname of options.hostnames) {
|
||||||
Services.obs.notifyObservers(null, "extension:purge-localStorage", hostname);
|
Services.obs.notifyObservers(null, "extension:purge-localStorage", hostname);
|
||||||
|
|
|
@ -53,6 +53,9 @@ add_task(async function testIndexedDB() {
|
||||||
let origins = [];
|
let origins = [];
|
||||||
Services.qms.getUsage(request => {
|
Services.qms.getUsage(request => {
|
||||||
for (let i = 0; i < request.result.length; ++i) {
|
for (let i = 0; i < request.result.length; ++i) {
|
||||||
|
if (request.result[i].usage === 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (request.result[i].origin.startsWith("http://mochi.test") ||
|
if (request.result[i].origin.startsWith("http://mochi.test") ||
|
||||||
request.result[i].origin.startsWith("http://example.com")) {
|
request.result[i].origin.startsWith("http://example.com")) {
|
||||||
origins.push(request.result[i].origin);
|
origins.push(request.result[i].origin);
|
||||||
|
|
|
@ -60,6 +60,9 @@ add_task(async function testLocalStorage() {
|
||||||
await browser.browsingData.remove({}, {localStorage: true});
|
await browser.browsingData.remove({}, {localStorage: true});
|
||||||
await sendMessageToTabs(tabs, "checkLocalStorageCleared");
|
await sendMessageToTabs(tabs, "checkLocalStorageCleared");
|
||||||
|
|
||||||
|
// Cleanup (checkLocalStorageCleared creates empty LS databases).
|
||||||
|
await browser.browsingData.removeLocalStorage({});
|
||||||
|
|
||||||
browser.tabs.remove(tabs.map(tab => tab.id));
|
browser.tabs.remove(tabs.map(tab => tab.id));
|
||||||
|
|
||||||
browser.test.notifyPass("done");
|
browser.test.notifyPass("done");
|
||||||
|
|
|
@ -9,7 +9,7 @@ support-files =
|
||||||
|
|
||||||
[browser_clearSiteData.js]
|
[browser_clearSiteData.js]
|
||||||
[browser_siteData.js]
|
[browser_siteData.js]
|
||||||
skip-if = (os == 'linux' && debug) # Bug 1439332
|
skip-if = (os == 'linux' && debug) || verify # Bug 1439332 and bug 1436395
|
||||||
[browser_siteData2.js]
|
[browser_siteData2.js]
|
||||||
[browser_siteData3.js]
|
[browser_siteData3.js]
|
||||||
[browser_siteData_multi_select.js]
|
[browser_siteData_multi_select.js]
|
||||||
|
|
|
@ -44,7 +44,7 @@ add_task(async function() {
|
||||||
await new Promise(resolve => {
|
await new Promise(resolve => {
|
||||||
let principal = Services.scriptSecurityManager
|
let principal = Services.scriptSecurityManager
|
||||||
.createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
|
.createCodebasePrincipalFromOrigin(TEST_QUOTA_USAGE_ORIGIN);
|
||||||
let request = Services.qms.clearStoragesForPrincipal(principal, null, true);
|
let request = Services.qms.clearStoragesForPrincipal(principal, null, null, true);
|
||||||
request.callback = resolve;
|
request.callback = resolve;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -304,7 +304,7 @@ var SiteDataManager = {
|
||||||
// We are clearing *All* across OAs so need to ensure a principal without suffix here,
|
// We are clearing *All* across OAs so need to ensure a principal without suffix here,
|
||||||
// or the call of `clearStoragesForPrincipal` would fail.
|
// or the call of `clearStoragesForPrincipal` would fail.
|
||||||
principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(originNoSuffix);
|
principal = Services.scriptSecurityManager.createCodebasePrincipalFromOrigin(originNoSuffix);
|
||||||
let request = this._qms.clearStoragesForPrincipal(principal, null, true);
|
let request = this._qms.clearStoragesForPrincipal(principal, null, null, true);
|
||||||
request.callback = resolve;
|
request.callback = resolve;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
|
@ -396,8 +396,17 @@ GetSpecialBaseDomain(const nsCOMPtr<nsIURI>& aCodebase,
|
||||||
{
|
{
|
||||||
*aHandled = false;
|
*aHandled = false;
|
||||||
|
|
||||||
// For a file URI, we return the file path.
|
// Special handling for a file URI.
|
||||||
if (NS_URIIsLocalFile(aCodebase)) {
|
if (NS_URIIsLocalFile(aCodebase)) {
|
||||||
|
// If strict file origin policy is not in effect, all local files are
|
||||||
|
// considered to be same-origin, so return a known dummy domain here.
|
||||||
|
if (!nsScriptSecurityManager::GetStrictFileOriginPolicy()) {
|
||||||
|
*aHandled = true;
|
||||||
|
aBaseDomain.AssignLiteral("UNIVERSAL_FILE_URI_ORIGIN");
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise, we return the file path.
|
||||||
nsCOMPtr<nsIURL> url = do_QueryInterface(aCodebase);
|
nsCOMPtr<nsIURL> url = do_QueryInterface(aCodebase);
|
||||||
|
|
||||||
if (url) {
|
if (url) {
|
||||||
|
|
|
@ -48,6 +48,8 @@ def get_normalized_signatures(signature, fileAnnot=None):
|
||||||
signature = signature.replace(';', ' ')
|
signature = signature.replace(';', ' ')
|
||||||
# Normalize spaces.
|
# Normalize spaces.
|
||||||
signature = re.sub(r'\s+', ' ', signature).strip()
|
signature = re.sub(r'\s+', ' ', signature).strip()
|
||||||
|
# Remove new-line induced spaces after opening braces.
|
||||||
|
signature = re.sub(r'\(\s+', '(', signature).strip()
|
||||||
# Match arguments, and keep only the type.
|
# Match arguments, and keep only the type.
|
||||||
signature = reMatchArg.sub('\g<type>', signature)
|
signature = reMatchArg.sub('\g<type>', signature)
|
||||||
# Remove class name
|
# Remove class name
|
||||||
|
@ -144,45 +146,62 @@ def get_macroassembler_definitions(filename):
|
||||||
return []
|
return []
|
||||||
|
|
||||||
style_section = False
|
style_section = False
|
||||||
code_section = False
|
|
||||||
lines = ''
|
lines = ''
|
||||||
signatures = []
|
signatures = []
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
for line in f:
|
for line in f:
|
||||||
if '//{{{ check_macroassembler_style' in line:
|
if '//{{{ check_macroassembler_style' in line:
|
||||||
|
if style_section:
|
||||||
|
raise 'check_macroassembler_style section already opened.'
|
||||||
style_section = True
|
style_section = True
|
||||||
|
braces_depth = 0
|
||||||
elif '//}}} check_macroassembler_style' in line:
|
elif '//}}} check_macroassembler_style' in line:
|
||||||
style_section = False
|
style_section = False
|
||||||
if not style_section:
|
if not style_section:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
# Remove comments from the processed line.
|
||||||
line = re.sub(r'//.*', '', line)
|
line = re.sub(r'//.*', '', line)
|
||||||
if line.startswith('{') or line.strip() == "{}":
|
|
||||||
|
# Locate and count curly braces.
|
||||||
|
open_curly_brace = line.find('{')
|
||||||
|
was_braces_depth = braces_depth
|
||||||
|
braces_depth = braces_depth + line.count('{') - line.count('}')
|
||||||
|
|
||||||
|
# Raise an error if the check_macroassembler_style macro is used
|
||||||
|
# across namespaces / classes scopes.
|
||||||
|
if braces_depth < 0:
|
||||||
|
raise 'check_macroassembler_style annotations are not well scoped.'
|
||||||
|
|
||||||
|
# If the current line contains an opening curly brace, check if
|
||||||
|
# this line combines with the previous one can be identified as a
|
||||||
|
# MacroAssembler function signature.
|
||||||
|
if open_curly_brace != -1 and was_braces_depth == 0:
|
||||||
|
lines = lines + line[:open_curly_brace]
|
||||||
if 'MacroAssembler::' in lines:
|
if 'MacroAssembler::' in lines:
|
||||||
signatures.extend(
|
signatures.extend(
|
||||||
get_normalized_signatures(lines, fileAnnot))
|
get_normalized_signatures(lines, fileAnnot))
|
||||||
if line.strip() != "{}": # Empty declaration, no need to declare
|
|
||||||
# a new code section
|
|
||||||
code_section = True
|
|
||||||
continue
|
|
||||||
if line.startswith('}'):
|
|
||||||
code_section = False
|
|
||||||
lines = ''
|
lines = ''
|
||||||
continue
|
continue
|
||||||
if code_section:
|
|
||||||
continue
|
|
||||||
|
|
||||||
if len(line.strip()) == 0:
|
# We do not aggregate any lines if we are scanning lines which are
|
||||||
lines = ''
|
# in-between a set of curly braces.
|
||||||
|
if braces_depth > 0:
|
||||||
continue
|
continue
|
||||||
|
if was_braces_depth != 0:
|
||||||
|
line = line[line.rfind('}') + 1:]
|
||||||
|
|
||||||
|
# This logic is used to remove template instantiation, static
|
||||||
|
# variable definitions and function declaration from the next
|
||||||
|
# function definition.
|
||||||
|
last_semi_colon = line.rfind(';')
|
||||||
|
if last_semi_colon != -1:
|
||||||
|
lines = ''
|
||||||
|
line = line[last_semi_colon + 1:]
|
||||||
|
|
||||||
|
# Aggregate lines of non-braced text, which corresponds to the space
|
||||||
|
# where we are expecting to find function definitions.
|
||||||
lines = lines + line
|
lines = lines + line
|
||||||
# Continue until we have a complete declaration
|
|
||||||
if '{' not in lines:
|
|
||||||
continue
|
|
||||||
# Skip variable declarations
|
|
||||||
if ')' not in lines:
|
|
||||||
lines = ''
|
|
||||||
continue
|
|
||||||
|
|
||||||
return signatures
|
return signatures
|
||||||
|
|
||||||
|
@ -201,14 +220,17 @@ def get_macroassembler_declaration(filename):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
line = re.sub(r'//.*', '', line)
|
line = re.sub(r'//.*', '', line)
|
||||||
if len(line.strip()) == 0:
|
if len(line.strip()) == 0 or 'public:' in line or 'private:' in line:
|
||||||
lines = ''
|
lines = ''
|
||||||
continue
|
continue
|
||||||
lines = lines + line
|
lines = lines + line
|
||||||
|
|
||||||
# Continue until we have a complete declaration
|
# Continue until we have a complete declaration
|
||||||
if ';' not in lines:
|
if ';' not in lines:
|
||||||
continue
|
continue
|
||||||
# Skip variable declarations
|
|
||||||
|
# Skip member declarations: which are lines ending with a
|
||||||
|
# semi-colon without any list of arguments.
|
||||||
if ')' not in lines:
|
if ')' not in lines:
|
||||||
lines = ''
|
lines = ''
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -23,6 +23,8 @@ support-files =
|
||||||
!/devtools/client/shared/test/telemetry-test-helpers.js
|
!/devtools/client/shared/test/telemetry-test-helpers.js
|
||||||
|
|
||||||
[browser_addons_debug_bootstrapped.js]
|
[browser_addons_debug_bootstrapped.js]
|
||||||
|
# To be removed in bug 1497264
|
||||||
|
skip-if = true
|
||||||
[browser_addons_debug_info.js]
|
[browser_addons_debug_info.js]
|
||||||
[browser_addons_debug_webextension.js]
|
[browser_addons_debug_webextension.js]
|
||||||
tags = webextensions
|
tags = webextensions
|
||||||
|
@ -34,11 +36,16 @@ tags = webextensions
|
||||||
skip-if = (verify && debug) || (debug && os == "linux" && bits == 64) # verify: crashes on shutdown, timeouts linux debug Bug 1299001
|
skip-if = (verify && debug) || (debug && os == "linux" && bits == 64) # verify: crashes on shutdown, timeouts linux debug Bug 1299001
|
||||||
tags = webextensions
|
tags = webextensions
|
||||||
[browser_addons_debugging_initial_state.js]
|
[browser_addons_debugging_initial_state.js]
|
||||||
|
# To be removed or updated in bug 1497264
|
||||||
|
skip-if = true
|
||||||
[browser_addons_install.js]
|
[browser_addons_install.js]
|
||||||
skip-if = verify && debug
|
# To be updated in bug 1497264 (was "verify && debug")
|
||||||
|
skip-if = true
|
||||||
[browser_addons_reload.js]
|
[browser_addons_reload.js]
|
||||||
[browser_addons_remove.js]
|
[browser_addons_remove.js]
|
||||||
[browser_addons_toggle_debug.js]
|
[browser_addons_toggle_debug.js]
|
||||||
|
# To be removed or updated in bug 1497264
|
||||||
|
skip-if = true
|
||||||
[browser_page_not_found.js]
|
[browser_page_not_found.js]
|
||||||
[browser_service_workers.js]
|
[browser_service_workers.js]
|
||||||
[browser_service_workers_fetch_flag.js]
|
[browser_service_workers_fetch_flag.js]
|
||||||
|
|
|
@ -13,6 +13,8 @@ function testFilePath(container, expectedFilePath) {
|
||||||
is(filePath.previousElementSibling.textContent, "Location", "file path has label");
|
is(filePath.previousElementSibling.textContent, "Location", "file path has label");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove in Bug 1497264
|
||||||
|
/*
|
||||||
add_task(async function testLegacyAddon() {
|
add_task(async function testLegacyAddon() {
|
||||||
const addonId = "test-devtools@mozilla.org";
|
const addonId = "test-devtools@mozilla.org";
|
||||||
const addonName = "test-devtools";
|
const addonName = "test-devtools";
|
||||||
|
@ -32,6 +34,7 @@ add_task(async function testLegacyAddon() {
|
||||||
|
|
||||||
await closeAboutDebugging(tab);
|
await closeAboutDebugging(tab);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
add_task(async function testWebExtension() {
|
add_task(async function testWebExtension() {
|
||||||
const addonId = "test-devtools-webextension-nobg@mozilla.org";
|
const addonId = "test-devtools-webextension-nobg@mozilla.org";
|
||||||
|
|
|
@ -7,9 +7,6 @@ loader.lazyImporter(this, "AddonTestUtils",
|
||||||
|
|
||||||
AddonTestUtils.initMochitest(this);
|
AddonTestUtils.initMochitest(this);
|
||||||
|
|
||||||
const ADDON_ID = "test-devtools@mozilla.org";
|
|
||||||
const ADDON_NAME = "test-devtools";
|
|
||||||
|
|
||||||
function mockFilePicker(window, file) {
|
function mockFilePicker(window, file) {
|
||||||
// Mock the file picker to select a test addon
|
// Mock the file picker to select a test addon
|
||||||
const MockFilePicker = SpecialPowers.MockFilePicker;
|
const MockFilePicker = SpecialPowers.MockFilePicker;
|
||||||
|
@ -35,6 +32,9 @@ function promiseWriteWebManifestForExtension(manifest, dir) {
|
||||||
}
|
}
|
||||||
|
|
||||||
add_task(async function testLegacyInstallSuccess() {
|
add_task(async function testLegacyInstallSuccess() {
|
||||||
|
const ADDON_ID = "test-devtools@mozilla.org";
|
||||||
|
const ADDON_NAME = "test-devtools";
|
||||||
|
|
||||||
const { tab, document } = await openAboutDebugging("addons");
|
const { tab, document } = await openAboutDebugging("addons");
|
||||||
await waitForInitialAddonList(document);
|
await waitForInitialAddonList(document);
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
const ADDON_ID = "test-devtools@mozilla.org";
|
const ADDON_ID = "test-devtools@mozilla.org";
|
||||||
const ADDON_NAME = "test-devtools";
|
|
||||||
|
|
||||||
const PACKAGED_ADDON_ID = "bug1273184@tests";
|
const PACKAGED_ADDON_ID = "bug1273184@tests";
|
||||||
const PACKAGED_ADDON_NAME = "bug 1273184";
|
const PACKAGED_ADDON_NAME = "bug 1273184";
|
||||||
|
@ -60,7 +59,10 @@ class TempWebExt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove in Bug 1497264
|
||||||
|
/*
|
||||||
add_task(async function reloadButtonReloadsAddon() {
|
add_task(async function reloadButtonReloadsAddon() {
|
||||||
|
const ADDON_NAME = "test-devtools";
|
||||||
const { tab, document, window } = await openAboutDebugging("addons");
|
const { tab, document, window } = await openAboutDebugging("addons");
|
||||||
const { AboutDebugging } = window;
|
const { AboutDebugging } = window;
|
||||||
await waitForInitialAddonList(document);
|
await waitForInitialAddonList(document);
|
||||||
|
@ -96,6 +98,7 @@ add_task(async function reloadButtonReloadsAddon() {
|
||||||
await tearDownAddon(AboutDebugging, reloadedAddon);
|
await tearDownAddon(AboutDebugging, reloadedAddon);
|
||||||
await closeAboutDebugging(tab);
|
await closeAboutDebugging(tab);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
add_task(async function reloadButtonRefreshesMetadata() {
|
add_task(async function reloadButtonRefreshesMetadata() {
|
||||||
const { tab, document, window } = await openAboutDebugging("addons");
|
const { tab, document, window } = await openAboutDebugging("addons");
|
||||||
|
|
|
@ -12,6 +12,8 @@ function getRemoveButton(document, id) {
|
||||||
return document.querySelector(`[data-addon-id="${id}"] .uninstall-button`);
|
return document.querySelector(`[data-addon-id="${id}"] .uninstall-button`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove in Bug 1497264
|
||||||
|
/*
|
||||||
add_task(async function removeLegacyExtension() {
|
add_task(async function removeLegacyExtension() {
|
||||||
const addonID = "test-devtools@mozilla.org";
|
const addonID = "test-devtools@mozilla.org";
|
||||||
const addonName = "test-devtools";
|
const addonName = "test-devtools";
|
||||||
|
@ -36,6 +38,7 @@ add_task(async function removeLegacyExtension() {
|
||||||
|
|
||||||
await closeAboutDebugging(tab);
|
await closeAboutDebugging(tab);
|
||||||
});
|
});
|
||||||
|
*/
|
||||||
|
|
||||||
add_task(async function removeWebextension() {
|
add_task(async function removeWebextension() {
|
||||||
const addonID = "test-devtools-webextension@mozilla.org";
|
const addonID = "test-devtools-webextension@mozilla.org";
|
||||||
|
|
|
@ -401,7 +401,7 @@ class FlexboxInspector {
|
||||||
* highlighter is toggled on/off for.
|
* highlighter is toggled on/off for.
|
||||||
*/
|
*/
|
||||||
onToggleFlexboxHighlighter(node) {
|
onToggleFlexboxHighlighter(node) {
|
||||||
this.highlighters.toggleFlexboxHighlighter(node);
|
this.highlighters.toggleFlexboxHighlighter(node, "layout");
|
||||||
this.store.dispatch(updateFlexboxHighlighted(node !==
|
this.store.dispatch(updateFlexboxHighlighted(node !==
|
||||||
this.highlighters.flexboxHighlighterShow));
|
this.highlighters.flexboxHighlighterShow));
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ support-files =
|
||||||
[browser_flexbox_empty_state.js]
|
[browser_flexbox_empty_state.js]
|
||||||
[browser_flexbox_highlighter_color_picker_on_ESC.js]
|
[browser_flexbox_highlighter_color_picker_on_ESC.js]
|
||||||
[browser_flexbox_highlighter_color_picker_on_RETURN.js]
|
[browser_flexbox_highlighter_color_picker_on_RETURN.js]
|
||||||
|
[browser_flexbox_highlighter_opened_telemetry.js]
|
||||||
[browser_flexbox_item_list_01.js]
|
[browser_flexbox_item_list_01.js]
|
||||||
[browser_flexbox_item_list_02.js]
|
[browser_flexbox_item_list_02.js]
|
||||||
[browser_flexbox_item_list_updates_on_change.js]
|
[browser_flexbox_item_list_updates_on_change.js]
|
||||||
|
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that the telemetry is correct when the flexbox highlighter is activated from
|
||||||
|
// the layout view.
|
||||||
|
|
||||||
|
const TEST_URI = URL_ROOT + "doc_flexbox_specific_cases.html";
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab(TEST_URI);
|
||||||
|
startTelemetry();
|
||||||
|
const { inspector, flexboxInspector } = await openLayoutView();
|
||||||
|
const { document: doc } = flexboxInspector;
|
||||||
|
const { highlighters, store } = inspector;
|
||||||
|
|
||||||
|
const onFlexHighlighterToggleRendered = waitForDOM(doc, "#flexbox-checkbox-toggle");
|
||||||
|
await selectNode("#container", inspector);
|
||||||
|
const [flexHighlighterToggle] = await onFlexHighlighterToggleRendered;
|
||||||
|
|
||||||
|
await toggleHighlighterON(flexHighlighterToggle, highlighters, store);
|
||||||
|
await toggleHighlighterOFF(flexHighlighterToggle, highlighters, store);
|
||||||
|
|
||||||
|
checkResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
checkTelemetry("devtools.layout.flexboxhighlighter.opened", "", 1, "scalar");
|
||||||
|
checkTelemetry("DEVTOOLS_FLEXBOX_HIGHLIGHTER_TIME_ACTIVE_SECONDS", "", null,
|
||||||
|
"hasentries");
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||||
|
/* import-globals-from ../../../shared/test/telemetry-test-helpers.js */
|
||||||
/* import-globals-from ../../test/head.js */
|
/* import-globals-from ../../test/head.js */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
|
@ -39,6 +39,7 @@ skip-if = (verify && (os == 'win'))
|
||||||
[browser_grids_grid-outline-writing-mode.js]
|
[browser_grids_grid-outline-writing-mode.js]
|
||||||
skip-if = (verify && (os == 'win'))
|
skip-if = (verify && (os == 'win'))
|
||||||
[browser_grids_highlighter-setting-rules-grid-toggle.js]
|
[browser_grids_highlighter-setting-rules-grid-toggle.js]
|
||||||
|
[browser_grids_highlighter-toggle-telemetry.js]
|
||||||
[browser_grids_number-of-css-grids-telemetry.js]
|
[browser_grids_number-of-css-grids-telemetry.js]
|
||||||
[browser_grids_persist-color-palette.js]
|
[browser_grids_persist-color-palette.js]
|
||||||
[browser_grids_restored-after-reload.js]
|
[browser_grids_restored-after-reload.js]
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that the telemetry count is correct when the grid highlighter is activated from
|
||||||
|
// the layout view.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
#grid {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="grid">
|
||||||
|
<div id="cell1">cell1</div>
|
||||||
|
<div id="cell2">cell2</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
startTelemetry();
|
||||||
|
const { gridInspector, inspector } = await openLayoutView();
|
||||||
|
const { document: doc } = gridInspector;
|
||||||
|
const { highlighters, store } = inspector;
|
||||||
|
|
||||||
|
await selectNode("#grid", inspector);
|
||||||
|
const gridList = doc.getElementById("grid-list");
|
||||||
|
const checkbox = gridList.children[0].querySelector("input");
|
||||||
|
|
||||||
|
info("Toggling ON the CSS grid highlighter from the layout panel.");
|
||||||
|
const onHighlighterShown = highlighters.once("grid-highlighter-shown");
|
||||||
|
const onCheckboxChange = waitUntilState(store, state =>
|
||||||
|
state.grids.length == 1 &&
|
||||||
|
state.grids[0].highlighted);
|
||||||
|
checkbox.click();
|
||||||
|
await onHighlighterShown;
|
||||||
|
await onCheckboxChange;
|
||||||
|
|
||||||
|
checkResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
checkTelemetry("devtools.grid.gridinspector.opened", "", 1, "scalar");
|
||||||
|
}
|
|
@ -135,8 +135,10 @@ skip-if = true # Bug 1177550
|
||||||
[browser_markup_events_source_map.js]
|
[browser_markup_events_source_map.js]
|
||||||
[browser_markup_events-windowed-host.js]
|
[browser_markup_events-windowed-host.js]
|
||||||
[browser_markup_flex_display_badge.js]
|
[browser_markup_flex_display_badge.js]
|
||||||
|
[browser_markup_flex_display_badge_telemetry.js]
|
||||||
[browser_markup_grid_display_badge_01.js]
|
[browser_markup_grid_display_badge_01.js]
|
||||||
[browser_markup_grid_display_badge_02.js]
|
[browser_markup_grid_display_badge_02.js]
|
||||||
|
[browser_markup_grid_display_badge_telemetry.js]
|
||||||
[browser_markup_links_01.js]
|
[browser_markup_links_01.js]
|
||||||
[browser_markup_links_02.js]
|
[browser_markup_links_02.js]
|
||||||
[browser_markup_links_03.js]
|
[browser_markup_links_03.js]
|
||||||
|
|
|
@ -0,0 +1,48 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that the telemetry is correct when the flexbox highlighter is activated from
|
||||||
|
// the markup view.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type="text/css">
|
||||||
|
#flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="flex"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await pushPref("devtools.inspector.flexboxHighlighter.enabled", true);
|
||||||
|
await pushPref("devtools.flexboxinspector.enabled", true);
|
||||||
|
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
startTelemetry();
|
||||||
|
const { inspector } = await openLayoutView();
|
||||||
|
const { highlighters } = inspector;
|
||||||
|
|
||||||
|
await selectNode("#flex", inspector);
|
||||||
|
const flexContainer = await getContainerForSelector("#flex", inspector);
|
||||||
|
const flexDisplayBadge = flexContainer.elt.querySelector(
|
||||||
|
".inspector-badge.interactive[data-display]");
|
||||||
|
|
||||||
|
info("Toggling ON the flexbox highlighter from the flex display badge.");
|
||||||
|
const onHighlighterShown = highlighters.once("flexbox-highlighter-shown");
|
||||||
|
flexDisplayBadge.click();
|
||||||
|
await onHighlighterShown;
|
||||||
|
|
||||||
|
info("Toggling OFF the flexbox highlighter from the flex display badge.");
|
||||||
|
const onHighlighterHidden = highlighters.once("flexbox-highlighter-hidden");
|
||||||
|
flexDisplayBadge.click();
|
||||||
|
await onHighlighterHidden;
|
||||||
|
|
||||||
|
checkResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
checkTelemetry("devtools.markup.flexboxhighlighter.opened", "", 1, "scalar");
|
||||||
|
checkTelemetry("DEVTOOLS_FLEXBOX_HIGHLIGHTER_TIME_ACTIVE_SECONDS", "", null,
|
||||||
|
"hasentries");
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that the telemetry count is correct when the grid highlighter is activated from
|
||||||
|
// the markup view.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type="text/css">
|
||||||
|
#grid {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="grid"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
startTelemetry();
|
||||||
|
const { inspector } = await openLayoutView();
|
||||||
|
const { highlighters, store } = inspector;
|
||||||
|
|
||||||
|
await selectNode("#grid", inspector);
|
||||||
|
const gridContainer = await getContainerForSelector("#grid", inspector);
|
||||||
|
const gridDisplayBadge = gridContainer.elt.querySelector(
|
||||||
|
".inspector-badge.interactive[data-display]");
|
||||||
|
|
||||||
|
info("Toggling ON the CSS grid highlighter from the grid display badge.");
|
||||||
|
const onHighlighterShown = highlighters.once("grid-highlighter-shown");
|
||||||
|
const onCheckboxChange = waitUntilState(store, state =>
|
||||||
|
state.grids.length === 1 &&
|
||||||
|
state.grids[0].highlighted);
|
||||||
|
gridDisplayBadge.click();
|
||||||
|
await onHighlighterShown;
|
||||||
|
await onCheckboxChange;
|
||||||
|
|
||||||
|
checkResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
checkTelemetry("devtools.markup.gridinspector.opened", "", 1, "scalar");
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* 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/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||||
|
/* import-globals-from ../../../shared/test/telemetry-test-helpers.js */
|
||||||
/* import-globals-from ../../test/head.js */
|
/* import-globals-from ../../test/head.js */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
|
@ -749,7 +749,7 @@ ElementEditor.prototype = {
|
||||||
this.stopTrackingFlexboxHighlighterEvents();
|
this.stopTrackingFlexboxHighlighterEvents();
|
||||||
|
|
||||||
this._displayBadge.classList.toggle("active");
|
this._displayBadge.classList.toggle("active");
|
||||||
await this.highlighters.toggleFlexboxHighlighter(this.node);
|
await this.highlighters.toggleFlexboxHighlighter(this.node, "markup");
|
||||||
|
|
||||||
this.startTrackingFlexboxHighlighterEvents();
|
this.startTrackingFlexboxHighlighterEvents();
|
||||||
}
|
}
|
||||||
|
|
|
@ -166,6 +166,7 @@ skip-if = (os == "win" && debug) # bug 963492: win.
|
||||||
[browser_rules_flexbox-highlighter-on-navigate.js]
|
[browser_rules_flexbox-highlighter-on-navigate.js]
|
||||||
[browser_rules_flexbox-highlighter-on-reload.js]
|
[browser_rules_flexbox-highlighter-on-reload.js]
|
||||||
[browser_rules_flexbox-highlighter-restored-after-reload.js]
|
[browser_rules_flexbox-highlighter-restored-after-reload.js]
|
||||||
|
[browser_rules_flexbox-toggle-telemetry.js]
|
||||||
[browser_rules_flexbox-toggle_01.js]
|
[browser_rules_flexbox-toggle_01.js]
|
||||||
[browser_rules_flexbox-toggle_01b.js]
|
[browser_rules_flexbox-toggle_01b.js]
|
||||||
[browser_rules_flexbox-toggle_02.js]
|
[browser_rules_flexbox-toggle_02.js]
|
||||||
|
@ -176,6 +177,7 @@ skip-if = (os == "win" && debug) # bug 963492: win.
|
||||||
[browser_rules_grid-highlighter-on-navigate.js]
|
[browser_rules_grid-highlighter-on-navigate.js]
|
||||||
[browser_rules_grid-highlighter-on-reload.js]
|
[browser_rules_grid-highlighter-on-reload.js]
|
||||||
[browser_rules_grid-highlighter-restored-after-reload.js]
|
[browser_rules_grid-highlighter-restored-after-reload.js]
|
||||||
|
[browser_rules_grid-toggle-telemetry.js]
|
||||||
[browser_rules_grid-toggle_01.js]
|
[browser_rules_grid-toggle_01.js]
|
||||||
[browser_rules_grid-toggle_01b.js]
|
[browser_rules_grid-toggle_01b.js]
|
||||||
[browser_rules_grid-toggle_02.js]
|
[browser_rules_grid-toggle_02.js]
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that the telemetry is correct when the flexbox highlighter is activated from
|
||||||
|
// the rules view.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
#flex {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="flex"></div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
startTelemetry();
|
||||||
|
const {inspector, view} = await openRuleView();
|
||||||
|
const {highlighters} = view;
|
||||||
|
|
||||||
|
await selectNode("#flex", inspector);
|
||||||
|
const container = getRuleViewProperty(view, "#flex", "display").valueSpan;
|
||||||
|
const flexboxToggle = container.querySelector(".ruleview-flex");
|
||||||
|
|
||||||
|
info("Toggling ON the flexbox highlighter from the rule-view.");
|
||||||
|
const onHighlighterShown = highlighters.once("flexbox-highlighter-shown");
|
||||||
|
flexboxToggle.click();
|
||||||
|
await onHighlighterShown;
|
||||||
|
|
||||||
|
info("Toggling OFF the flexbox highlighter from the rule-view.");
|
||||||
|
const onHighlighterHidden = highlighters.once("flexbox-highlighter-hidden");
|
||||||
|
flexboxToggle.click();
|
||||||
|
await onHighlighterHidden;
|
||||||
|
|
||||||
|
checkResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
checkTelemetry("devtools.rules.flexboxhighlighter.opened", "", 1, "scalar");
|
||||||
|
checkTelemetry("DEVTOOLS_FLEXBOX_HIGHLIGHTER_TIME_ACTIVE_SECONDS", "", null,
|
||||||
|
"hasentries");
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
// Test that the telemetry count is correct when the grid highlighter is activated from
|
||||||
|
// the rules view.
|
||||||
|
|
||||||
|
const TEST_URI = `
|
||||||
|
<style type='text/css'>
|
||||||
|
#grid {
|
||||||
|
display: grid;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
<div id="grid">
|
||||||
|
<div id="cell1">cell1</div>
|
||||||
|
<div id="cell2">cell2</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
|
||||||
|
add_task(async function() {
|
||||||
|
await addTab("data:text/html;charset=utf-8," + encodeURIComponent(TEST_URI));
|
||||||
|
startTelemetry();
|
||||||
|
const {inspector, view} = await openRuleView();
|
||||||
|
const highlighters = view.highlighters;
|
||||||
|
|
||||||
|
await selectNode("#grid", inspector);
|
||||||
|
const container = getRuleViewProperty(view, "#grid", "display").valueSpan;
|
||||||
|
const gridToggle = container.querySelector(".ruleview-grid");
|
||||||
|
|
||||||
|
info("Toggling ON the CSS grid highlighter from the rule-view.");
|
||||||
|
const onHighlighterShown = highlighters.once("grid-highlighter-shown");
|
||||||
|
gridToggle.click();
|
||||||
|
await onHighlighterShown;
|
||||||
|
|
||||||
|
checkResults();
|
||||||
|
});
|
||||||
|
|
||||||
|
function checkResults() {
|
||||||
|
checkTelemetry("devtools.rules.gridinspector.opened", "", 1, "scalar");
|
||||||
|
}
|
|
@ -2,6 +2,7 @@
|
||||||
/* Any copyright is dedicated to the Public Domain.
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
/* eslint no-unused-vars: [2, {"vars": "local"}] */
|
||||||
|
/* import-globals-from ../../../shared/test/telemetry-test-helpers.js */
|
||||||
/* import-globals-from ../../test/head.js */
|
/* import-globals-from ../../test/head.js */
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
|
|
@ -258,16 +258,18 @@ class HighlightersOverlay {
|
||||||
*
|
*
|
||||||
* @param {NodeFront} node
|
* @param {NodeFront} node
|
||||||
* The NodeFront of the flexbox container element to highlight.
|
* The NodeFront of the flexbox container element to highlight.
|
||||||
* @param {Object} options
|
* @param. {String} trigger
|
||||||
* Object used for passing options to the flexbox highlighter.
|
* String name matching "layout", "markup" or "rule" to indicate where the
|
||||||
|
* flexbox highlighter was toggled on from. "layout" represents the layout view.
|
||||||
|
* "markup" represents the markup view. "rule" represents the rule view.
|
||||||
*/
|
*/
|
||||||
async toggleFlexboxHighlighter(node, options = {}) {
|
async toggleFlexboxHighlighter(node, trigger) {
|
||||||
if (node == this.flexboxHighlighterShown) {
|
if (node == this.flexboxHighlighterShown) {
|
||||||
await this.hideFlexboxHighlighter(node);
|
await this.hideFlexboxHighlighter(node);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
await this.showFlexboxHighlighter(node, options);
|
await this.showFlexboxHighlighter(node, {}, trigger);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -277,8 +279,12 @@ class HighlightersOverlay {
|
||||||
* The NodeFront of the flexbox container element to highlight.
|
* The NodeFront of the flexbox container element to highlight.
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* Object used for passing options to the flexbox highlighter.
|
* Object used for passing options to the flexbox highlighter.
|
||||||
|
* @param. {String} trigger
|
||||||
|
* String name matching "layout", "markup" or "rule" to indicate where the
|
||||||
|
* flexbox highlighter was toggled on from. "layout" represents the layout view.
|
||||||
|
* "markup" represents the markup view. "rule" represents the rule view.
|
||||||
*/
|
*/
|
||||||
async showFlexboxHighlighter(node, options) {
|
async showFlexboxHighlighter(node, options, trigger) {
|
||||||
const highlighter = await this._getHighlighter("FlexboxHighlighter");
|
const highlighter = await this._getHighlighter("FlexboxHighlighter");
|
||||||
if (!highlighter) {
|
if (!highlighter) {
|
||||||
return;
|
return;
|
||||||
|
@ -293,6 +299,17 @@ class HighlightersOverlay {
|
||||||
|
|
||||||
this._toggleRuleViewIcon(node, true, ".ruleview-flex");
|
this._toggleRuleViewIcon(node, true, ".ruleview-flex");
|
||||||
|
|
||||||
|
this.telemetry.toolOpened("flexbox_highlighter", this.inspector.toolbox.sessionId,
|
||||||
|
this);
|
||||||
|
|
||||||
|
if (trigger === "layout") {
|
||||||
|
this.telemetry.scalarAdd("devtools.layout.flexboxhighlighter.opened", 1);
|
||||||
|
} else if (trigger === "markup") {
|
||||||
|
this.telemetry.scalarAdd("devtools.markup.flexboxhighlighter.opened", 1);
|
||||||
|
} else if (trigger === "rule") {
|
||||||
|
this.telemetry.scalarAdd("devtools.rules.flexboxhighlighter.opened", 1);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Save flexbox highlighter state.
|
// Save flexbox highlighter state.
|
||||||
const { url } = this.inspector.target;
|
const { url } = this.inspector.target;
|
||||||
|
@ -319,6 +336,9 @@ class HighlightersOverlay {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.telemetry.toolClosed("flexbox_highlighter", this.inspector.toolbox.sessionId,
|
||||||
|
this);
|
||||||
|
|
||||||
this._toggleRuleViewIcon(node, false, ".ruleview-flex");
|
this._toggleRuleViewIcon(node, false, ".ruleview-flex");
|
||||||
|
|
||||||
await this.highlighters.FlexboxHighlighter.hide();
|
await this.highlighters.FlexboxHighlighter.hide();
|
||||||
|
@ -407,10 +427,10 @@ class HighlightersOverlay {
|
||||||
*
|
*
|
||||||
* @param {NodeFront} node
|
* @param {NodeFront} node
|
||||||
* The NodeFront of the grid container element to highlight.
|
* The NodeFront of the grid container element to highlight.
|
||||||
* @param. {String|null} trigger
|
* @param. {String} trigger
|
||||||
* String name matching "grid" or "rule" to indicate where the
|
* String name matching "grid", "markup" or "rule" to indicate where the
|
||||||
* grid highlighter was toggled on from. "grid" represents the grid view
|
* grid highlighter was toggled on from. "grid" represents the grid view.
|
||||||
* "rule" represents the rule view.
|
* "markup" represents the markup view. "rule" represents the rule view.
|
||||||
*/
|
*/
|
||||||
async toggleGridHighlighter(node, trigger) {
|
async toggleGridHighlighter(node, trigger) {
|
||||||
if (this.gridHighlighters.has(node)) {
|
if (this.gridHighlighters.has(node)) {
|
||||||
|
@ -428,10 +448,10 @@ class HighlightersOverlay {
|
||||||
* The NodeFront of the grid container element to highlight.
|
* The NodeFront of the grid container element to highlight.
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* Object used for passing options to the grid highlighter.
|
* Object used for passing options to the grid highlighter.
|
||||||
* @param. {String|null} trigger
|
* @param. {String} trigger
|
||||||
* String name matching "grid" or "rule" to indicate where the
|
* String name matching "grid", "markup" or "rule" to indicate where the
|
||||||
* grid highlighter was toggled on from. "grid" represents the grid view
|
* grid highlighter was toggled on from. "grid" represents the grid view.
|
||||||
* "rule" represents the rule view.
|
* "markup" represents the markup view. "rule" represents the rule view.
|
||||||
*/
|
*/
|
||||||
async showGridHighlighter(node, options, trigger) {
|
async showGridHighlighter(node, options, trigger) {
|
||||||
// When the grid highlighter has the given node, it is probably called with new
|
// When the grid highlighter has the given node, it is probably called with new
|
||||||
|
@ -464,9 +484,11 @@ class HighlightersOverlay {
|
||||||
|
|
||||||
this._toggleRuleViewIcon(node, true, ".ruleview-grid");
|
this._toggleRuleViewIcon(node, true, ".ruleview-grid");
|
||||||
|
|
||||||
if (trigger == "grid") {
|
if (trigger === "grid") {
|
||||||
this.telemetry.scalarAdd("devtools.grid.gridinspector.opened", 1);
|
this.telemetry.scalarAdd("devtools.grid.gridinspector.opened", 1);
|
||||||
} else if (trigger == "rule") {
|
} else if (trigger === "markup") {
|
||||||
|
this.telemetry.scalarAdd("devtools.markup.gridinspector.opened", 1);
|
||||||
|
} else if (trigger === "rule") {
|
||||||
this.telemetry.scalarAdd("devtools.rules.gridinspector.opened", 1);
|
this.telemetry.scalarAdd("devtools.rules.gridinspector.opened", 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -968,7 +990,7 @@ class HighlightersOverlay {
|
||||||
|
|
||||||
if (this._isRuleViewDisplayFlex(event.target)) {
|
if (this._isRuleViewDisplayFlex(event.target)) {
|
||||||
event.stopPropagation();
|
event.stopPropagation();
|
||||||
this.toggleFlexboxHighlighter(this.inspector.selection.nodeFront);
|
this.toggleFlexboxHighlighter(this.inspector.selection.nodeFront, "rule");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._isRuleViewShapeSwatch(event.target)) {
|
if (this._isRuleViewShapeSwatch(event.target)) {
|
||||||
|
|
|
@ -732,16 +732,14 @@ function getChartsFromToolId(id) {
|
||||||
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
||||||
countHist = `DEVTOOLS_${id}_OPENED_COUNT`;
|
countHist = `DEVTOOLS_${id}_OPENED_COUNT`;
|
||||||
break;
|
break;
|
||||||
|
case "FLEXBOX_HIGHLIGHTER":
|
||||||
|
timerHist = `DEVTOOLS_${id}_TIME_ACTIVE_SECONDS`;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
timerHist = `DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS`;
|
timerHist = `DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS`;
|
||||||
countHist = `DEVTOOLS_CUSTOM_OPENED_COUNT`;
|
countHist = `DEVTOOLS_CUSTOM_OPENED_COUNT`;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!timerHist || (!countHist && !countScalar)) {
|
|
||||||
throw new Error(`getChartsFromToolId cannot be called without a timer ` +
|
|
||||||
`histogram and either a count histogram or count scalar.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
useTimedEvent: useTimedEvent,
|
useTimedEvent: useTimedEvent,
|
||||||
timerHist: timerHist,
|
timerHist: timerHist,
|
||||||
|
|
|
@ -77,7 +77,9 @@ support-files =
|
||||||
[browser_cubic-bezier-06.js]
|
[browser_cubic-bezier-06.js]
|
||||||
[browser_cubic-bezier-07.js]
|
[browser_cubic-bezier-07.js]
|
||||||
[browser_dbg_addon-console.js]
|
[browser_dbg_addon-console.js]
|
||||||
skip-if = (e10s && debug || os == 'win' || verify)
|
# To be removed or updated in bug 1497264
|
||||||
|
# previously was: (e10s && debug || os == 'win' || verify)
|
||||||
|
skip-if = true
|
||||||
tags = addons
|
tags = addons
|
||||||
[browser_dbg_debugger-statement.js]
|
[browser_dbg_debugger-statement.js]
|
||||||
skip-if = e10s && debug
|
skip-if = e10s && debug
|
||||||
|
@ -245,7 +247,9 @@ skip-if = !e10s || os == "win" # RDM only works for remote tabs, Win: bug 140419
|
||||||
skip-if = verify
|
skip-if = verify
|
||||||
[browser_theme_switching.js]
|
[browser_theme_switching.js]
|
||||||
[browser_dbg_listaddons.js]
|
[browser_dbg_listaddons.js]
|
||||||
skip-if = e10s && debug
|
# To be removed or updated in bug 1497264
|
||||||
|
# previously was: e10s && debug
|
||||||
|
skip-if = true
|
||||||
tags = addons
|
tags = addons
|
||||||
[browser_dbg_listtabs-01.js]
|
[browser_dbg_listtabs-01.js]
|
||||||
[browser_dbg_listtabs-02.js]
|
[browser_dbg_listtabs-02.js]
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
#include "nsAboutRedirector.h"
|
#include "nsAboutRedirector.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
#include "nsAboutProtocolUtils.h"
|
#include "nsAboutProtocolUtils.h"
|
||||||
|
#include "nsBaseChannel.h"
|
||||||
#include "mozilla/ArrayUtils.h"
|
#include "mozilla/ArrayUtils.h"
|
||||||
#include "nsIProtocolHandler.h"
|
#include "nsIProtocolHandler.h"
|
||||||
|
|
||||||
|
@ -27,6 +28,36 @@ struct RedirEntry
|
||||||
uint32_t flags;
|
uint32_t flags;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class CrashChannel final : public nsBaseChannel
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit CrashChannel(nsIURI* aURI)
|
||||||
|
{
|
||||||
|
SetURI(aURI);
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult OpenContentStream(bool async, nsIInputStream **stream,
|
||||||
|
nsIChannel** channel) override
|
||||||
|
{
|
||||||
|
nsAutoCString spec;
|
||||||
|
mURI->GetSpec(spec);
|
||||||
|
|
||||||
|
if (spec.EqualsASCII("about:crashparent") && XRE_IsParentProcess()) {
|
||||||
|
MOZ_CRASH("Crash via about:crashparent");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (spec.EqualsASCII("about:crashcontent") && XRE_IsContentProcess()) {
|
||||||
|
MOZ_CRASH("Crash via about:crashcontent");
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_WARNING("Unhandled about:crash* URI or wrong process");
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~CrashChannel() = default;
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome
|
Entries which do not have URI_SAFE_FOR_UNTRUSTED_CONTENT will run with chrome
|
||||||
privileges. This is potentially dangerous. Please use
|
privileges. This is potentially dangerous. Please use
|
||||||
|
@ -145,12 +176,10 @@ static const RedirEntry kRedirMap[] = {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"crashparent", "about:blank",
|
"crashparent", "about:blank",
|
||||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
|
||||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT
|
nsIAboutModule::HIDE_FROM_ABOUTABOUT
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"crashcontent", "about:blank",
|
"crashcontent", "about:blank",
|
||||||
nsIAboutModule::URI_SAFE_FOR_UNTRUSTED_CONTENT |
|
|
||||||
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
|
nsIAboutModule::HIDE_FROM_ABOUTABOUT |
|
||||||
nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
|
nsIAboutModule::URI_CAN_LOAD_IN_CHILD |
|
||||||
nsIAboutModule::URI_MUST_LOAD_IN_CHILD
|
nsIAboutModule::URI_MUST_LOAD_IN_CHILD
|
||||||
|
@ -174,12 +203,10 @@ nsAboutRedirector::NewChannel(nsIURI* aURI,
|
||||||
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
|
nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
|
||||||
NS_ENSURE_SUCCESS(rv, rv);
|
NS_ENSURE_SUCCESS(rv, rv);
|
||||||
|
|
||||||
if (XRE_IsParentProcess() && path.EqualsASCII("crashparent")) {
|
if (path.EqualsASCII("crashparent") || path.EqualsASCII("crashcontent")) {
|
||||||
MOZ_CRASH("Crash via about:crashparent");
|
nsCOMPtr<nsIChannel> channel = new CrashChannel(aURI);
|
||||||
}
|
channel.forget(aResult);
|
||||||
|
return NS_OK;
|
||||||
if (XRE_IsContentProcess() && path.EqualsASCII("crashcontent")) {
|
|
||||||
MOZ_CRASH("Crash via about:crashcontent");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef ABOUT_CONFIG_BLOCKED_GV
|
#ifdef ABOUT_CONFIG_BLOCKED_GV
|
||||||
|
|
|
@ -115,3 +115,4 @@ support-files = file_framedhistoryframes.html
|
||||||
[test_pushState_after_document_open.html]
|
[test_pushState_after_document_open.html]
|
||||||
[test_windowedhistoryframes.html]
|
[test_windowedhistoryframes.html]
|
||||||
[test_triggeringprincipal_location_seturi.html]
|
[test_triggeringprincipal_location_seturi.html]
|
||||||
|
[test_bug1507702.html]
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html>
|
||||||
|
<!--
|
||||||
|
https://bugzilla.mozilla.org/show_bug.cgi?id=1507702
|
||||||
|
-->
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test for Bug 1507702</title>
|
||||||
|
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||||
|
<link rel="icon" href="about:crashparent"/>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1507702">Mozilla Bug 1507702</a>
|
||||||
|
<img src="about:crashparent">
|
||||||
|
<img src="about:crashcontent">
|
||||||
|
<iframe src="about:crashparent"></iframe>
|
||||||
|
<iframe src="about:crashcontent"></iframe>
|
||||||
|
<script>
|
||||||
|
let urls = ["about:crashparent", "about:crashcontent"];
|
||||||
|
async function testFetch() {
|
||||||
|
const url = urls.shift();
|
||||||
|
if (!url) {
|
||||||
|
return Promise.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
let threw;
|
||||||
|
try {
|
||||||
|
await fetch(url);
|
||||||
|
threw = false;
|
||||||
|
} catch (e) {
|
||||||
|
threw = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
ok(threw === true, "fetch should reject");
|
||||||
|
return testFetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
document.body.onload = async () => {
|
||||||
|
for (const url of ["about:crashparent", "about:crashcontent"]) {
|
||||||
|
SimpleTest.doesThrow(() => {
|
||||||
|
top.location.href = url;
|
||||||
|
}, "navigation should throw");
|
||||||
|
|
||||||
|
SimpleTest.doesThrow(() => {
|
||||||
|
location.href = url;
|
||||||
|
}, "navigation should throw");
|
||||||
|
}
|
||||||
|
|
||||||
|
await testFetch();
|
||||||
|
SimpleTest.finish();
|
||||||
|
};
|
||||||
|
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -792,7 +792,9 @@ ParentRunnable::ReadMetadata()
|
||||||
|
|
||||||
nsresult rv =
|
nsresult rv =
|
||||||
qm->EnsureOriginIsInitialized(quota::PERSISTENCE_TYPE_TEMPORARY, mSuffix,
|
qm->EnsureOriginIsInitialized(quota::PERSISTENCE_TYPE_TEMPORARY, mSuffix,
|
||||||
mGroup, mOrigin, getter_AddRefs(mDirectory));
|
mGroup, mOrigin,
|
||||||
|
/* aCreateIfNotExists */ true,
|
||||||
|
getter_AddRefs(mDirectory));
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
mResult = JS::AsmJSCache_StorageInitFailure;
|
mResult = JS::AsmJSCache_StorageInitFailure;
|
||||||
return rv;
|
return rv;
|
||||||
|
|
|
@ -619,10 +619,7 @@ ChromeUtils::IsOriginAttributesEqual(dom::GlobalObject& aGlobal,
|
||||||
ChromeUtils::IsOriginAttributesEqual(const dom::OriginAttributesDictionary& aA,
|
ChromeUtils::IsOriginAttributesEqual(const dom::OriginAttributesDictionary& aA,
|
||||||
const dom::OriginAttributesDictionary& aB)
|
const dom::OriginAttributesDictionary& aB)
|
||||||
{
|
{
|
||||||
return aA.mAppId == aB.mAppId &&
|
return aA == aB;
|
||||||
aA.mInIsolatedMozBrowser == aB.mInIsolatedMozBrowser &&
|
|
||||||
aA.mUserContextId == aB.mUserContextId &&
|
|
||||||
aA.mPrivateBrowsingId == aB.mPrivateBrowsingId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NIGHTLY_BUILD
|
#ifdef NIGHTLY_BUILD
|
||||||
|
|
|
@ -23,6 +23,8 @@
|
||||||
#include "mozilla/dom/DOMPrefs.h"
|
#include "mozilla/dom/DOMPrefs.h"
|
||||||
#include "mozilla/dom/EventTarget.h"
|
#include "mozilla/dom/EventTarget.h"
|
||||||
#include "mozilla/dom/LocalStorage.h"
|
#include "mozilla/dom/LocalStorage.h"
|
||||||
|
#include "mozilla/dom/LocalStorageCommon.h"
|
||||||
|
#include "mozilla/dom/LSObject.h"
|
||||||
#include "mozilla/dom/PartitionedLocalStorage.h"
|
#include "mozilla/dom/PartitionedLocalStorage.h"
|
||||||
#include "mozilla/dom/Storage.h"
|
#include "mozilla/dom/Storage.h"
|
||||||
#include "mozilla/dom/IdleRequest.h"
|
#include "mozilla/dom/IdleRequest.h"
|
||||||
|
@ -4908,32 +4910,38 @@ nsGlobalWindowInner::GetLocalStorage(ErrorResult& aError)
|
||||||
if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny &&
|
if (access != nsContentUtils::StorageAccess::ePartitionedOrDeny &&
|
||||||
(!mLocalStorage ||
|
(!mLocalStorage ||
|
||||||
mLocalStorage->Type() == Storage::ePartitionedLocalStorage)) {
|
mLocalStorage->Type() == Storage::ePartitionedLocalStorage)) {
|
||||||
nsresult rv;
|
RefPtr<Storage> storage;
|
||||||
nsCOMPtr<nsIDOMStorageManager> storageManager =
|
|
||||||
do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
aError.Throw(rv);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
nsString documentURI;
|
if (NextGenLocalStorageEnabled()) {
|
||||||
if (mDoc) {
|
aError = LSObject::CreateForWindow(this, getter_AddRefs(storage));
|
||||||
aError = mDoc->GetDocumentURI(documentURI);
|
} else {
|
||||||
if (NS_WARN_IF(aError.Failed())) {
|
nsresult rv;
|
||||||
|
nsCOMPtr<nsIDOMStorageManager> storageManager =
|
||||||
|
do_GetService("@mozilla.org/dom/localStorage-manager;1", &rv);
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
aError.Throw(rv);
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
nsString documentURI;
|
||||||
|
if (mDoc) {
|
||||||
|
aError = mDoc->GetDocumentURI(documentURI);
|
||||||
|
if (NS_WARN_IF(aError.Failed())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIPrincipal *principal = GetPrincipal();
|
||||||
|
if (!principal) {
|
||||||
|
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
aError = storageManager->CreateStorage(this, principal, documentURI,
|
||||||
|
IsPrivateBrowsing(),
|
||||||
|
getter_AddRefs(storage));
|
||||||
}
|
}
|
||||||
|
|
||||||
nsIPrincipal *principal = GetPrincipal();
|
|
||||||
if (!principal) {
|
|
||||||
aError.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
RefPtr<Storage> storage;
|
|
||||||
aError = storageManager->CreateStorage(this, principal, documentURI,
|
|
||||||
IsPrivateBrowsing(),
|
|
||||||
getter_AddRefs(storage));
|
|
||||||
if (aError.Failed()) {
|
if (aError.Failed()) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
@ -5809,7 +5817,8 @@ nsGlobalWindowInner::ObserveStorageNotification(StorageEvent* aEvent,
|
||||||
MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
|
MOZ_DIAGNOSTIC_ASSERT(StorageUtils::PrincipalsEqual(aEvent->GetPrincipal(),
|
||||||
principal));
|
principal));
|
||||||
|
|
||||||
fireMozStorageChanged = mLocalStorage == aEvent->GetStorageArea();
|
fireMozStorageChanged =
|
||||||
|
mLocalStorage && mLocalStorage == aEvent->GetStorageArea();
|
||||||
|
|
||||||
if (fireMozStorageChanged) {
|
if (fireMozStorageChanged) {
|
||||||
eventType.AssignLiteral("MozLocalStorageChanged");
|
eventType.AssignLiteral("MozLocalStorageChanged");
|
||||||
|
@ -5821,7 +5830,7 @@ nsGlobalWindowInner::ObserveStorageNotification(StorageEvent* aEvent,
|
||||||
IgnoredErrorResult error;
|
IgnoredErrorResult error;
|
||||||
RefPtr<StorageEvent> clonedEvent =
|
RefPtr<StorageEvent> clonedEvent =
|
||||||
CloneStorageEvent(eventType, aEvent, error);
|
CloneStorageEvent(eventType, aEvent, error);
|
||||||
if (error.Failed()) {
|
if (error.Failed() || !clonedEvent) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5856,16 +5865,18 @@ nsGlobalWindowInner::CloneStorageEvent(const nsAString& aType,
|
||||||
// If null, this is a localStorage event received by IPC.
|
// If null, this is a localStorage event received by IPC.
|
||||||
if (!storageArea) {
|
if (!storageArea) {
|
||||||
storage = GetLocalStorage(aRv);
|
storage = GetLocalStorage(aRv);
|
||||||
if (aRv.Failed() || !storage) {
|
if (!NextGenLocalStorageEnabled()) {
|
||||||
return nullptr;
|
if (aRv.Failed() || !storage) {
|
||||||
}
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (storage->Type() == Storage::eLocalStorage) {
|
if (storage->Type() == Storage::eLocalStorage) {
|
||||||
RefPtr<LocalStorage> localStorage =
|
RefPtr<LocalStorage> localStorage =
|
||||||
static_cast<LocalStorage*>(storage.get());
|
static_cast<LocalStorage*>(storage.get());
|
||||||
|
|
||||||
// We must apply the current change to the 'local' localStorage.
|
// We must apply the current change to the 'local' localStorage.
|
||||||
localStorage->ApplyEvent(aEvent);
|
localStorage->ApplyEvent(aEvent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else if (storageArea->Type() == Storage::eSessionStorage) {
|
} else if (storageArea->Type() == Storage::eSessionStorage) {
|
||||||
storage = GetSessionStorage(aRv);
|
storage = GetSessionStorage(aRv);
|
||||||
|
@ -6790,6 +6801,13 @@ nsGlobalWindowInner::EventListenerAdded(nsAtom* aType)
|
||||||
ErrorResult rv;
|
ErrorResult rv;
|
||||||
GetLocalStorage(rv);
|
GetLocalStorage(rv);
|
||||||
rv.SuppressException();
|
rv.SuppressException();
|
||||||
|
|
||||||
|
if (NextGenLocalStorageEnabled() &&
|
||||||
|
mLocalStorage && mLocalStorage->Type() == Storage::eLocalStorage) {
|
||||||
|
auto object = static_cast<LSObject*>(mLocalStorage.get());
|
||||||
|
|
||||||
|
Unused << NS_WARN_IF(NS_FAILED(object->EnsureObserver()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6803,6 +6821,20 @@ nsGlobalWindowInner::EventListenerRemoved(nsAtom* aType)
|
||||||
MOZ_ASSERT(mBeforeUnloadListenerCount >= 0);
|
MOZ_ASSERT(mBeforeUnloadListenerCount >= 0);
|
||||||
mTabChild->BeforeUnloadRemoved();
|
mTabChild->BeforeUnloadRemoved();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (aType == nsGkAtoms::onstorage) {
|
||||||
|
if (NextGenLocalStorageEnabled() &&
|
||||||
|
mLocalStorage &&
|
||||||
|
mLocalStorage->Type() == Storage::eLocalStorage &&
|
||||||
|
// The remove event is fired even if this isn't the last listener, so
|
||||||
|
// only remove if there are no other listeners left.
|
||||||
|
mListenerManager &&
|
||||||
|
!mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
|
||||||
|
auto object = static_cast<LSObject*>(mLocalStorage.get());
|
||||||
|
|
||||||
|
object->DropObserver();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -7964,6 +7996,14 @@ nsGlobalWindowInner::StorageAccessGranted()
|
||||||
|
|
||||||
MOZ_ASSERT(mLocalStorage &&
|
MOZ_ASSERT(mLocalStorage &&
|
||||||
mLocalStorage->Type() == Storage::eLocalStorage);
|
mLocalStorage->Type() == Storage::eLocalStorage);
|
||||||
|
|
||||||
|
if (NextGenLocalStorageEnabled() &&
|
||||||
|
mListenerManager &&
|
||||||
|
mListenerManager->HasListenersFor(nsGkAtoms::onstorage)) {
|
||||||
|
auto object = static_cast<LSObject*>(mLocalStorage.get());
|
||||||
|
|
||||||
|
object->EnsureObserver();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#include "mozilla/dom/ContentFrameMessageManager.h"
|
#include "mozilla/dom/ContentFrameMessageManager.h"
|
||||||
#include "mozilla/dom/EventTarget.h"
|
#include "mozilla/dom/EventTarget.h"
|
||||||
#include "mozilla/dom/LocalStorage.h"
|
#include "mozilla/dom/LocalStorage.h"
|
||||||
|
#include "mozilla/dom/LSObject.h"
|
||||||
#include "mozilla/dom/Storage.h"
|
#include "mozilla/dom/Storage.h"
|
||||||
#include "mozilla/dom/IdleRequest.h"
|
#include "mozilla/dom/IdleRequest.h"
|
||||||
#include "mozilla/dom/Performance.h"
|
#include "mozilla/dom/Performance.h"
|
||||||
|
|
|
@ -632,7 +632,7 @@ subsuite = clipboard
|
||||||
skip-if = toolkit == 'android' #bug 904183
|
skip-if = toolkit == 'android' #bug 904183
|
||||||
[test_copypaste.xhtml]
|
[test_copypaste.xhtml]
|
||||||
subsuite = clipboard
|
subsuite = clipboard
|
||||||
skip-if = toolkit == 'android' #bug 904183
|
skip-if = toolkit == 'android' && !e10s #bug 904183
|
||||||
[test_createHTMLDocument.html]
|
[test_createHTMLDocument.html]
|
||||||
[test_data_uri.html]
|
[test_data_uri.html]
|
||||||
skip-if = verify
|
skip-if = verify
|
||||||
|
|
|
@ -163,6 +163,11 @@ public:
|
||||||
return mImpl == aOther.mImpl;
|
return mImpl == aOther.mImpl;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool operator!=(const Optional_base<T, InternalType>& aOther) const
|
||||||
|
{
|
||||||
|
return mImpl != aOther.mImpl;
|
||||||
|
}
|
||||||
|
|
||||||
template<typename T1, typename T2>
|
template<typename T1, typename T2>
|
||||||
explicit Optional_base(const T1& aValue1, const T2& aValue2)
|
explicit Optional_base(const T1& aValue1, const T2& aValue2)
|
||||||
{
|
{
|
||||||
|
|
|
@ -12996,6 +12996,30 @@ class CGDictionary(CGThing):
|
||||||
"aOther")],
|
"aOther")],
|
||||||
body=body.define())
|
body=body.define())
|
||||||
|
|
||||||
|
def canHaveEqualsOperator(self):
|
||||||
|
return all(m.type.isString() or m.type.isPrimitive() for (m,_) in
|
||||||
|
self.memberInfo)
|
||||||
|
|
||||||
|
def equalsOperator(self):
|
||||||
|
body = CGList([])
|
||||||
|
|
||||||
|
for m, _ in self.memberInfo:
|
||||||
|
memberName = self.makeMemberName(m.identifier.name)
|
||||||
|
memberTest = CGGeneric(fill(
|
||||||
|
"""
|
||||||
|
if (${memberName} != aOther.${memberName}) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
""",
|
||||||
|
memberName=memberName))
|
||||||
|
body.append(memberTest)
|
||||||
|
body.append(CGGeneric("return true;\n"))
|
||||||
|
return ClassMethod(
|
||||||
|
"operator==", "bool",
|
||||||
|
[Argument("const %s&" % self.makeClassName(self.dictionary),
|
||||||
|
"aOther")
|
||||||
|
], const=True, body=body.define())
|
||||||
|
|
||||||
def getStructs(self):
|
def getStructs(self):
|
||||||
d = self.dictionary
|
d = self.dictionary
|
||||||
selfName = self.makeClassName(d)
|
selfName = self.makeClassName(d)
|
||||||
|
@ -13076,6 +13100,9 @@ class CGDictionary(CGThing):
|
||||||
else:
|
else:
|
||||||
disallowCopyConstruction = True
|
disallowCopyConstruction = True
|
||||||
|
|
||||||
|
if self.canHaveEqualsOperator():
|
||||||
|
methods.append(self.equalsOperator())
|
||||||
|
|
||||||
struct = CGClass(selfName,
|
struct = CGClass(selfName,
|
||||||
bases=[ClassBase(self.base())],
|
bases=[ClassBase(self.base())],
|
||||||
members=members,
|
members=members,
|
||||||
|
|
|
@ -107,7 +107,7 @@ support-files =
|
||||||
[test_browserElement_inproc_CookiesNotThirdParty.html]
|
[test_browserElement_inproc_CookiesNotThirdParty.html]
|
||||||
[test_browserElement_inproc_CopyPaste.html]
|
[test_browserElement_inproc_CopyPaste.html]
|
||||||
subsuite = clipboard
|
subsuite = clipboard
|
||||||
skip-if = (os == "android") # Disabled on Android, see bug 1230421
|
skip-if = (os == "android" && !e10s) # Disabled on Android, see bug 1230421
|
||||||
[test_browserElement_inproc_DataURI.html]
|
[test_browserElement_inproc_DataURI.html]
|
||||||
[test_browserElement_inproc_ExposableURI.html]
|
[test_browserElement_inproc_ExposableURI.html]
|
||||||
[test_browserElement_inproc_FirstPaint.html]
|
[test_browserElement_inproc_FirstPaint.html]
|
||||||
|
|
|
@ -435,6 +435,7 @@ Context::QuotaInitRunnable::Run()
|
||||||
mQuotaInfo.mSuffix,
|
mQuotaInfo.mSuffix,
|
||||||
mQuotaInfo.mGroup,
|
mQuotaInfo.mGroup,
|
||||||
mQuotaInfo.mOrigin,
|
mQuotaInfo.mOrigin,
|
||||||
|
/* aCreateIfNotExists */ true,
|
||||||
getter_AddRefs(mQuotaInfo.mDir));
|
getter_AddRefs(mQuotaInfo.mDir));
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
resolver->Resolve(rv);
|
resolver->Resolve(rv);
|
||||||
|
|
|
@ -294,7 +294,7 @@ BodyMaybeUpdatePaddingSize(const QuotaInfo& aQuotaInfo, nsIFile* aBaseDir,
|
||||||
int64_t fileSize = 0;
|
int64_t fileSize = 0;
|
||||||
RefPtr<QuotaObject> quotaObject =
|
RefPtr<QuotaObject> quotaObject =
|
||||||
quotaManager->GetQuotaObject(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
|
quotaManager->GetQuotaObject(PERSISTENCE_TYPE_DEFAULT, aQuotaInfo.mGroup,
|
||||||
aQuotaInfo.mOrigin, bodyFile, &fileSize);
|
aQuotaInfo.mOrigin, bodyFile, -1, &fileSize);
|
||||||
MOZ_DIAGNOSTIC_ASSERT(quotaObject);
|
MOZ_DIAGNOSTIC_ASSERT(quotaObject);
|
||||||
MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
|
MOZ_DIAGNOSTIC_ASSERT(fileSize >= 0);
|
||||||
// XXXtt: bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1422815
|
// XXXtt: bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1422815
|
||||||
|
|
|
@ -247,5 +247,22 @@ IPCBlobInputStreamThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_
|
||||||
return NS_ERROR_NOT_IMPLEMENTED;
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IsOnDOMFileThread()
|
||||||
|
{
|
||||||
|
mozilla::StaticMutexAutoLock lock(gIPCBlobThreadMutex);
|
||||||
|
|
||||||
|
MOZ_ASSERT(!gShutdownHasStarted);
|
||||||
|
MOZ_ASSERT(gIPCBlobThread);
|
||||||
|
|
||||||
|
return gIPCBlobThread->IsOnCurrentThreadInfallible();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
AssertIsOnDOMFileThread()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsOnDOMFileThread());
|
||||||
|
}
|
||||||
|
|
||||||
} // dom namespace
|
} // dom namespace
|
||||||
} // mozilla namespace
|
} // mozilla namespace
|
||||||
|
|
|
@ -54,6 +54,12 @@ private:
|
||||||
nsTArray<RefPtr<IPCBlobInputStreamChild>> mPendingActors;
|
nsTArray<RefPtr<IPCBlobInputStreamChild>> mPendingActors;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool
|
||||||
|
IsOnDOMFileThread();
|
||||||
|
|
||||||
|
void
|
||||||
|
AssertIsOnDOMFileThread();
|
||||||
|
|
||||||
} // dom namespace
|
} // dom namespace
|
||||||
} // mozilla namespace
|
} // mozilla namespace
|
||||||
|
|
||||||
|
|
|
@ -18419,11 +18419,13 @@ Maintenance::DirectoryWork()
|
||||||
// Idle maintenance may occur before origin is initailized.
|
// Idle maintenance may occur before origin is initailized.
|
||||||
// Ensure origin is initialized first. It will initialize all origins
|
// Ensure origin is initialized first. It will initialize all origins
|
||||||
// for temporary storage including IDB origins.
|
// for temporary storage including IDB origins.
|
||||||
rv = quotaManager->EnsureOriginIsInitialized(persistenceType,
|
rv = quotaManager->EnsureOriginIsInitialized(
|
||||||
suffix,
|
persistenceType,
|
||||||
group,
|
suffix,
|
||||||
origin,
|
group,
|
||||||
getter_AddRefs(directory));
|
origin,
|
||||||
|
/* aCreateIfNotExists */ true,
|
||||||
|
getter_AddRefs(directory));
|
||||||
|
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return rv;
|
return rv;
|
||||||
|
@ -21319,6 +21321,7 @@ OpenDatabaseOp::DoDatabaseWork()
|
||||||
mSuffix,
|
mSuffix,
|
||||||
mGroup,
|
mGroup,
|
||||||
mOrigin,
|
mOrigin,
|
||||||
|
/* aCreateIfNotExists */ true,
|
||||||
getter_AddRefs(dbDirectory));
|
getter_AddRefs(dbDirectory));
|
||||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
return rv;
|
return rv;
|
||||||
|
|
|
@ -49,6 +49,11 @@ interface nsIDOMStorageManager : nsISupports
|
||||||
in AString aDocumentURI,
|
in AString aDocumentURI,
|
||||||
[optional] in bool aPrivate);
|
[optional] in bool aPrivate);
|
||||||
/**
|
/**
|
||||||
|
* DEPRECATED. The only good reason to use this was if you were writing a
|
||||||
|
* test and wanted to hackily determine if a preload happened. That's now
|
||||||
|
* covered by `nsILocalStorageManager.isPreloaded` and you should use that if
|
||||||
|
* that's what you want. If LSNG is in use, this will throw.
|
||||||
|
*
|
||||||
* Returns instance of DOM storage object for given principal.
|
* Returns instance of DOM storage object for given principal.
|
||||||
* If there is no storage managed for the scope, then null is returned and
|
* If there is no storage managed for the scope, then null is returned and
|
||||||
* no object is created. Otherwise, an object (new) for the existing storage
|
* no object is created. Otherwise, an object (new) for the existing storage
|
||||||
|
|
|
@ -41,6 +41,7 @@
|
||||||
#include "mozilla/dom/FileCreatorHelper.h"
|
#include "mozilla/dom/FileCreatorHelper.h"
|
||||||
#include "mozilla/dom/GetFilesHelper.h"
|
#include "mozilla/dom/GetFilesHelper.h"
|
||||||
#include "mozilla/dom/IPCBlobUtils.h"
|
#include "mozilla/dom/IPCBlobUtils.h"
|
||||||
|
#include "mozilla/dom/LSObject.h"
|
||||||
#include "mozilla/dom/MemoryReportRequest.h"
|
#include "mozilla/dom/MemoryReportRequest.h"
|
||||||
#include "mozilla/dom/PLoginReputationChild.h"
|
#include "mozilla/dom/PLoginReputationChild.h"
|
||||||
#include "mozilla/dom/PushNotifier.h"
|
#include "mozilla/dom/PushNotifier.h"
|
||||||
|
@ -3811,15 +3812,21 @@ ContentChild::GetSpecificMessageEventTarget(const Message& aMsg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef NIGHTLY_BUILD
|
|
||||||
void
|
void
|
||||||
ContentChild::OnChannelReceivedMessage(const Message& aMsg)
|
ContentChild::OnChannelReceivedMessage(const Message& aMsg)
|
||||||
{
|
{
|
||||||
|
if (aMsg.is_sync()) {
|
||||||
|
LSObject::CancelSyncLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
if (nsContentUtils::IsMessageInputEvent(aMsg)) {
|
if (nsContentUtils::IsMessageInputEvent(aMsg)) {
|
||||||
mPendingInputEvents++;
|
mPendingInputEvents++;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
PContentChild::Result
|
PContentChild::Result
|
||||||
ContentChild::OnMessageReceived(const Message& aMsg)
|
ContentChild::OnMessageReceived(const Message& aMsg)
|
||||||
{
|
{
|
||||||
|
|
|
@ -768,10 +768,10 @@ private:
|
||||||
virtual already_AddRefed<nsIEventTarget>
|
virtual already_AddRefed<nsIEventTarget>
|
||||||
GetSpecificMessageEventTarget(const Message& aMsg) override;
|
GetSpecificMessageEventTarget(const Message& aMsg) override;
|
||||||
|
|
||||||
#ifdef NIGHTLY_BUILD
|
|
||||||
virtual void
|
virtual void
|
||||||
OnChannelReceivedMessage(const Message& aMsg) override;
|
OnChannelReceivedMessage(const Message& aMsg) override;
|
||||||
|
|
||||||
|
#ifdef NIGHTLY_BUILD
|
||||||
virtual PContentChild::Result
|
virtual PContentChild::Result
|
||||||
OnMessageReceived(const Message& aMsg) override;
|
OnMessageReceived(const Message& aMsg) override;
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,7 @@
|
||||||
#include "mozilla/dom/ExternalHelperAppParent.h"
|
#include "mozilla/dom/ExternalHelperAppParent.h"
|
||||||
#include "mozilla/dom/GetFilesHelper.h"
|
#include "mozilla/dom/GetFilesHelper.h"
|
||||||
#include "mozilla/dom/GeolocationBinding.h"
|
#include "mozilla/dom/GeolocationBinding.h"
|
||||||
|
#include "mozilla/dom/LocalStorageCommon.h"
|
||||||
#include "mozilla/dom/MemoryReportRequest.h"
|
#include "mozilla/dom/MemoryReportRequest.h"
|
||||||
#include "mozilla/dom/Notification.h"
|
#include "mozilla/dom/Notification.h"
|
||||||
#include "mozilla/dom/PContentBridgeParent.h"
|
#include "mozilla/dom/PContentBridgeParent.h"
|
||||||
|
@ -141,6 +142,7 @@
|
||||||
#include "nsIGfxInfo.h"
|
#include "nsIGfxInfo.h"
|
||||||
#include "nsIIdleService.h"
|
#include "nsIIdleService.h"
|
||||||
#include "nsIInterfaceRequestorUtils.h"
|
#include "nsIInterfaceRequestorUtils.h"
|
||||||
|
#include "nsILocalStorageManager.h"
|
||||||
#include "nsIMemoryInfoDumper.h"
|
#include "nsIMemoryInfoDumper.h"
|
||||||
#include "nsIMemoryReporter.h"
|
#include "nsIMemoryReporter.h"
|
||||||
#include "nsIMozBrowserFrame.h"
|
#include "nsIMozBrowserFrame.h"
|
||||||
|
@ -5707,6 +5709,24 @@ ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild(nsIChannel* aChannel)
|
||||||
UpdateCookieStatus(aChannel);
|
UpdateCookieStatus(aChannel);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!NextGenLocalStorageEnabled()) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (principal->GetIsCodebasePrincipal()) {
|
||||||
|
nsCOMPtr<nsILocalStorageManager> lsm =
|
||||||
|
do_GetService("@mozilla.org/dom/localStorage-manager;1");
|
||||||
|
if (NS_WARN_IF(!lsm)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsCOMPtr<nsISupports> dummy;
|
||||||
|
rv = lsm->Preload(principal, nullptr, getter_AddRefs(dummy));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return NS_OK;
|
return NS_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,336 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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 "ActorsChild.h"
|
||||||
|
|
||||||
|
#include "LocalStorageCommon.h"
|
||||||
|
#include "LSDatabase.h"
|
||||||
|
#include "LSObject.h"
|
||||||
|
#include "LSObserver.h"
|
||||||
|
#include "LSSnapshot.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* LSDatabaseChild
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
LSDatabaseChild::LSDatabaseChild(LSDatabase* aDatabase)
|
||||||
|
: mDatabase(aDatabase)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aDatabase);
|
||||||
|
|
||||||
|
MOZ_COUNT_CTOR(LSDatabaseChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSDatabaseChild::~LSDatabaseChild()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
MOZ_COUNT_DTOR(LSDatabaseChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSDatabaseChild::SendDeleteMeInternal()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mDatabase) {
|
||||||
|
mDatabase->ClearActor();
|
||||||
|
mDatabase = nullptr;
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(PBackgroundLSDatabaseChild::SendDeleteMe());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSDatabaseChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mDatabase) {
|
||||||
|
mDatabase->ClearActor();
|
||||||
|
#ifdef DEBUG
|
||||||
|
mDatabase = nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
LSDatabaseChild::RecvRequestAllowToClose()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mDatabase) {
|
||||||
|
mDatabase->RequestAllowToClose();
|
||||||
|
|
||||||
|
// TODO: A new datastore will be prepared at first LocalStorage API
|
||||||
|
// synchronous call. It would be better to start preparing a new
|
||||||
|
// datastore right here, but asynchronously.
|
||||||
|
// However, we probably shouldn't do that if we are shutting down.
|
||||||
|
}
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
PBackgroundLSSnapshotChild*
|
||||||
|
LSDatabaseChild::AllocPBackgroundLSSnapshotChild(const nsString& aDocumentURI,
|
||||||
|
const bool& aIncreasePeakUsage,
|
||||||
|
const int64_t& aRequestedSize,
|
||||||
|
const int64_t& aMinSize,
|
||||||
|
LSSnapshotInitInfo* aInitInfo)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("PBackgroundLSSnapshotChild actor should be manually constructed!");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LSDatabaseChild::DeallocPBackgroundLSSnapshotChild(
|
||||||
|
PBackgroundLSSnapshotChild* aActor)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aActor);
|
||||||
|
|
||||||
|
delete aActor;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* LSObserverChild
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
LSObserverChild::LSObserverChild(LSObserver* aObserver)
|
||||||
|
: mObserver(aObserver)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObserver);
|
||||||
|
|
||||||
|
MOZ_COUNT_CTOR(LSObserverChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSObserverChild::~LSObserverChild()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
MOZ_COUNT_DTOR(LSObserverChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSObserverChild::SendDeleteMeInternal()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mObserver) {
|
||||||
|
mObserver->ClearActor();
|
||||||
|
mObserver = nullptr;
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(PBackgroundLSObserverChild::SendDeleteMe());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSObserverChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mObserver) {
|
||||||
|
mObserver->ClearActor();
|
||||||
|
#ifdef DEBUG
|
||||||
|
mObserver = nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
LSObserverChild::RecvObserve(const PrincipalInfo& aPrincipalInfo,
|
||||||
|
const uint32_t& aPrivateBrowsingId,
|
||||||
|
const nsString& aDocumentURI,
|
||||||
|
const nsString& aKey,
|
||||||
|
const nsString& aOldValue,
|
||||||
|
const nsString& aNewValue)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (!mObserver) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
nsCOMPtr<nsIPrincipal> principal =
|
||||||
|
PrincipalInfoToPrincipal(aPrincipalInfo, &rv);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return IPC_FAIL_NO_REASON(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Storage::NotifyChange(/* aStorage */ nullptr,
|
||||||
|
principal,
|
||||||
|
aKey,
|
||||||
|
aOldValue,
|
||||||
|
aNewValue,
|
||||||
|
/* aStorageType */ kLocalStorageType,
|
||||||
|
aDocumentURI,
|
||||||
|
/* aIsPrivate */ !!aPrivateBrowsingId,
|
||||||
|
/* aImmediateDispatch */ true);
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* LocalStorageRequestChild
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
LSRequestChild::LSRequestChild(LSRequestChildCallback* aCallback)
|
||||||
|
: mCallback(aCallback)
|
||||||
|
, mFinishing(false)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
MOZ_COUNT_CTOR(LSRequestChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSRequestChild::~LSRequestChild()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
MOZ_COUNT_DTOR(LSRequestChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
LSRequestChild::Finishing() const
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
return mFinishing;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSRequestChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
LSRequestChild::Recv__delete__(const LSRequestResponse& aResponse)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mCallback);
|
||||||
|
|
||||||
|
mCallback->OnResponse(aResponse);
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
LSRequestChild::RecvReady()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
mFinishing = true;
|
||||||
|
|
||||||
|
SendFinish();
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* LSSimpleRequestChild
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
LSSimpleRequestChild::LSSimpleRequestChild(
|
||||||
|
LSSimpleRequestChildCallback* aCallback)
|
||||||
|
: mCallback(aCallback)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aCallback);
|
||||||
|
|
||||||
|
MOZ_COUNT_CTOR(LSSimpleRequestChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSSimpleRequestChild::~LSSimpleRequestChild()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
MOZ_COUNT_DTOR(LSSimpleRequestChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSimpleRequestChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
LSSimpleRequestChild::Recv__delete__(const LSSimpleRequestResponse& aResponse)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
mCallback->OnResponse(aResponse);
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
/*******************************************************************************
|
||||||
|
* LSSnapshotChild
|
||||||
|
******************************************************************************/
|
||||||
|
|
||||||
|
LSSnapshotChild::LSSnapshotChild(LSSnapshot* aSnapshot)
|
||||||
|
: mSnapshot(aSnapshot)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aSnapshot);
|
||||||
|
|
||||||
|
MOZ_COUNT_CTOR(LSSnapshotChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSSnapshotChild::~LSSnapshotChild()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
MOZ_COUNT_DTOR(LSSnapshotChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshotChild::SendDeleteMeInternal()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mSnapshot) {
|
||||||
|
mSnapshot->ClearActor();
|
||||||
|
mSnapshot = nullptr;
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(PBackgroundLSSnapshotChild::SendDeleteMe());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshotChild::ActorDestroy(ActorDestroyReason aWhy)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mSnapshot) {
|
||||||
|
mSnapshot->ClearActor();
|
||||||
|
#ifdef DEBUG
|
||||||
|
mSnapshot = nullptr;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
LSSnapshotChild::RecvMarkDirty()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (!mSnapshot) {
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
mSnapshot->MarkDirty();
|
||||||
|
|
||||||
|
return IPC_OK();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,313 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_ActorsChild_h
|
||||||
|
#define mozilla_dom_localstorage_ActorsChild_h
|
||||||
|
|
||||||
|
#include "mozilla/dom/PBackgroundLSDatabaseChild.h"
|
||||||
|
#include "mozilla/dom/PBackgroundLSObserverChild.h"
|
||||||
|
#include "mozilla/dom/PBackgroundLSRequestChild.h"
|
||||||
|
#include "mozilla/dom/PBackgroundLSSimpleRequestChild.h"
|
||||||
|
#include "mozilla/dom/PBackgroundLSSnapshotChild.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
class BackgroundChildImpl;
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LocalStorageManager2;
|
||||||
|
class LSDatabase;
|
||||||
|
class LSObject;
|
||||||
|
class LSObserver;
|
||||||
|
class LSRequestChildCallback;
|
||||||
|
class LSSimpleRequestChildCallback;
|
||||||
|
class LSSnapshot;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal glue actor with standard IPC-managed new/delete existence that exists
|
||||||
|
* primarily to track the continued existence of the LSDatabase in the child.
|
||||||
|
* Most of the interesting bits happen via PBackgroundLSSnapshot.
|
||||||
|
*
|
||||||
|
* Mutual raw pointers are maintained between LSDatabase and this class that are
|
||||||
|
* cleared at either (expected) when the child starts the deletion process
|
||||||
|
* (SendDeleteMeInternal) or unexpected actor death (ActorDestroy).
|
||||||
|
*
|
||||||
|
* See `PBackgroundLSDatabase.ipdl` for more information.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ## Low-Level Lifecycle ##
|
||||||
|
* - Created by LSObject::EnsureDatabase if it had to create a database.
|
||||||
|
* - Deletion begun by LSDatabase's destructor invoking SendDeleteMeInternal
|
||||||
|
* which will result in the parent sending __delete__ which destroys the
|
||||||
|
* actor.
|
||||||
|
*/
|
||||||
|
class LSDatabaseChild final
|
||||||
|
: public PBackgroundLSDatabaseChild
|
||||||
|
{
|
||||||
|
friend class mozilla::ipc::BackgroundChildImpl;
|
||||||
|
friend class LSDatabase;
|
||||||
|
friend class LSObject;
|
||||||
|
|
||||||
|
LSDatabase* mDatabase;
|
||||||
|
|
||||||
|
NS_DECL_OWNINGTHREAD
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSDatabaseChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Only created by LSObject.
|
||||||
|
explicit LSDatabaseChild(LSDatabase* aDatabase);
|
||||||
|
|
||||||
|
// Only destroyed by mozilla::ipc::BackgroundChildImpl.
|
||||||
|
~LSDatabaseChild();
|
||||||
|
|
||||||
|
void
|
||||||
|
SendDeleteMeInternal();
|
||||||
|
|
||||||
|
// IPDL methods are only called by IPDL.
|
||||||
|
void
|
||||||
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
RecvRequestAllowToClose() override;
|
||||||
|
|
||||||
|
PBackgroundLSSnapshotChild*
|
||||||
|
AllocPBackgroundLSSnapshotChild(const nsString& aDocumentURI,
|
||||||
|
const bool& aIncreasePeakUsage,
|
||||||
|
const int64_t& aRequestedSize,
|
||||||
|
const int64_t& aMinSize,
|
||||||
|
LSSnapshotInitInfo* aInitInfo) override;
|
||||||
|
|
||||||
|
bool
|
||||||
|
DeallocPBackgroundLSSnapshotChild(PBackgroundLSSnapshotChild* aActor)
|
||||||
|
override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal IPC-managed (new/delete) actor that exists to receive and relay
|
||||||
|
* "storage" events from changes to LocalStorage that take place in other
|
||||||
|
* processes as their Snapshots are checkpointed to the canonical Datastore in
|
||||||
|
* the parent process.
|
||||||
|
*
|
||||||
|
* See `PBackgroundLSObserver.ipdl` for more info.
|
||||||
|
*/
|
||||||
|
class LSObserverChild final
|
||||||
|
: public PBackgroundLSObserverChild
|
||||||
|
{
|
||||||
|
friend class mozilla::ipc::BackgroundChildImpl;
|
||||||
|
friend class LSObserver;
|
||||||
|
friend class LSObject;
|
||||||
|
|
||||||
|
LSObserver* mObserver;
|
||||||
|
|
||||||
|
NS_DECL_OWNINGTHREAD
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSObserverChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Only created by LSObject.
|
||||||
|
explicit LSObserverChild(LSObserver* aObserver);
|
||||||
|
|
||||||
|
// Only destroyed by mozilla::ipc::BackgroundChildImpl.
|
||||||
|
~LSObserverChild();
|
||||||
|
|
||||||
|
void
|
||||||
|
SendDeleteMeInternal();
|
||||||
|
|
||||||
|
// IPDL methods are only called by IPDL.
|
||||||
|
void
|
||||||
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
RecvObserve(const PrincipalInfo& aPrinciplaInfo,
|
||||||
|
const uint32_t& aPrivateBrowsingId,
|
||||||
|
const nsString& aDocumentURI,
|
||||||
|
const nsString& aKey,
|
||||||
|
const nsString& aOldValue,
|
||||||
|
const nsString& aNewValue) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal glue IPC-managed (new/delete) actor that is used by LSObject and its
|
||||||
|
* RequestHelper to perform synchronous requests on top of an asynchronous
|
||||||
|
* protocol.
|
||||||
|
*
|
||||||
|
* Takes an `LSReuestChildCallback` to be invoked when a response is received
|
||||||
|
* via __delete__.
|
||||||
|
*
|
||||||
|
* See `PBackgroundLSRequest.ipdl`, `LSObject`, and `RequestHelper` for more
|
||||||
|
* info.
|
||||||
|
*/
|
||||||
|
class LSRequestChild final
|
||||||
|
: public PBackgroundLSRequestChild
|
||||||
|
{
|
||||||
|
friend class LSObject;
|
||||||
|
friend class LocalStorageManager2;
|
||||||
|
|
||||||
|
RefPtr<LSRequestChildCallback> mCallback;
|
||||||
|
|
||||||
|
bool mFinishing;
|
||||||
|
|
||||||
|
NS_DECL_OWNINGTHREAD
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSReqeustChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Finishing() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Only created by LSObject.
|
||||||
|
explicit LSRequestChild(LSRequestChildCallback* aCallback);
|
||||||
|
|
||||||
|
// Only destroyed by mozilla::ipc::BackgroundChildImpl.
|
||||||
|
~LSRequestChild();
|
||||||
|
|
||||||
|
// IPDL methods are only called by IPDL.
|
||||||
|
void
|
||||||
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
Recv__delete__(const LSRequestResponse& aResponse) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
RecvReady() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NS_NO_VTABLE LSRequestChildCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
OnResponse(const LSRequestResponse& aResponse) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~LSRequestChildCallback()
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal glue IPC-managed (new/delete) actor used by `LocalStorageManager2` to
|
||||||
|
* issue asynchronous requests in an asynchronous fashion.
|
||||||
|
*
|
||||||
|
* Takes an `LSSimpleRequestChildCallback` to be invoked when a response is
|
||||||
|
* received via __delete__.
|
||||||
|
*
|
||||||
|
* See `PBackgroundLSSimpleRequest.ipdl` for more info.
|
||||||
|
*/
|
||||||
|
class LSSimpleRequestChild final
|
||||||
|
: public PBackgroundLSSimpleRequestChild
|
||||||
|
{
|
||||||
|
friend class LocalStorageManager2;
|
||||||
|
|
||||||
|
RefPtr<LSSimpleRequestChildCallback> mCallback;
|
||||||
|
|
||||||
|
NS_DECL_OWNINGTHREAD
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSSimpleReqeustChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Only created by LocalStorageManager2.
|
||||||
|
explicit LSSimpleRequestChild(LSSimpleRequestChildCallback* aCallback);
|
||||||
|
|
||||||
|
// Only destroyed by mozilla::ipc::BackgroundChildImpl.
|
||||||
|
~LSSimpleRequestChild();
|
||||||
|
|
||||||
|
// IPDL methods are only called by IPDL.
|
||||||
|
void
|
||||||
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
Recv__delete__(const LSSimpleRequestResponse& aResponse) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class NS_NO_VTABLE LSSimpleRequestChildCallback
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
|
||||||
|
|
||||||
|
virtual void
|
||||||
|
OnResponse(const LSSimpleRequestResponse& aResponse) = 0;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual ~LSSimpleRequestChildCallback()
|
||||||
|
{ }
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Minimal IPC-managed (new/delete) actor that lasts as long as its owning
|
||||||
|
* LSSnapshot.
|
||||||
|
*
|
||||||
|
* Mutual raw pointers are maintained between LSSnapshot and this class that are
|
||||||
|
* cleared at either (expected) when the child starts the deletion process
|
||||||
|
* (SendDeleteMeInternal) or unexpected actor death (ActorDestroy).
|
||||||
|
*
|
||||||
|
* See `PBackgroundLSSnapshot.ipdl` and `LSSnapshot` for more info.
|
||||||
|
*/
|
||||||
|
class LSSnapshotChild final
|
||||||
|
: public PBackgroundLSSnapshotChild
|
||||||
|
{
|
||||||
|
friend class LSDatabase;
|
||||||
|
friend class LSSnapshot;
|
||||||
|
|
||||||
|
LSSnapshot* mSnapshot;
|
||||||
|
|
||||||
|
NS_DECL_OWNINGTHREAD
|
||||||
|
|
||||||
|
public:
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSSnapshotChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Only created by LSDatabase.
|
||||||
|
explicit LSSnapshotChild(LSSnapshot* aSnapshot);
|
||||||
|
|
||||||
|
// Only destroyed by LSDatabaseChild.
|
||||||
|
~LSSnapshotChild();
|
||||||
|
|
||||||
|
void
|
||||||
|
SendDeleteMeInternal();
|
||||||
|
|
||||||
|
// IPDL methods are only called by IPDL.
|
||||||
|
void
|
||||||
|
ActorDestroy(ActorDestroyReason aWhy) override;
|
||||||
|
|
||||||
|
mozilla::ipc::IPCResult
|
||||||
|
RecvMarkDirty() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_ActorsChild_h
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,94 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_ActorsParent_h
|
||||||
|
#define mozilla_dom_localstorage_ActorsParent_h
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
class PBackgroundParent;
|
||||||
|
class PrincipalInfo;
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LSRequestParams;
|
||||||
|
class LSSimpleRequestParams;
|
||||||
|
class PBackgroundLSDatabaseParent;
|
||||||
|
class PBackgroundLSObserverParent;
|
||||||
|
class PBackgroundLSRequestParent;
|
||||||
|
class PBackgroundLSSimpleRequestParent;
|
||||||
|
|
||||||
|
namespace quota {
|
||||||
|
|
||||||
|
class Client;
|
||||||
|
|
||||||
|
} // namespace quota
|
||||||
|
|
||||||
|
PBackgroundLSDatabaseParent*
|
||||||
|
AllocPBackgroundLSDatabaseParent(
|
||||||
|
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||||
|
const uint32_t& aPrivateBrowsingId,
|
||||||
|
const uint64_t& aDatastoreId);
|
||||||
|
|
||||||
|
bool
|
||||||
|
RecvPBackgroundLSDatabaseConstructor(
|
||||||
|
PBackgroundLSDatabaseParent* aActor,
|
||||||
|
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||||
|
const uint32_t& aPrivateBrowsingId,
|
||||||
|
const uint64_t& aDatastoreId);
|
||||||
|
|
||||||
|
bool
|
||||||
|
DeallocPBackgroundLSDatabaseParent(PBackgroundLSDatabaseParent* aActor);
|
||||||
|
|
||||||
|
PBackgroundLSObserverParent*
|
||||||
|
AllocPBackgroundLSObserverParent(const uint64_t& aObserverId);
|
||||||
|
|
||||||
|
bool
|
||||||
|
RecvPBackgroundLSObserverConstructor(PBackgroundLSObserverParent* aActor,
|
||||||
|
const uint64_t& aObservereId);
|
||||||
|
|
||||||
|
bool
|
||||||
|
DeallocPBackgroundLSObserverParent(PBackgroundLSObserverParent* aActor);
|
||||||
|
|
||||||
|
PBackgroundLSRequestParent*
|
||||||
|
AllocPBackgroundLSRequestParent(
|
||||||
|
mozilla::ipc::PBackgroundParent* aBackgroundActor,
|
||||||
|
const LSRequestParams& aParams);
|
||||||
|
|
||||||
|
bool
|
||||||
|
RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
|
||||||
|
const LSRequestParams& aParams);
|
||||||
|
|
||||||
|
bool
|
||||||
|
DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor);
|
||||||
|
|
||||||
|
PBackgroundLSSimpleRequestParent*
|
||||||
|
AllocPBackgroundLSSimpleRequestParent(const LSSimpleRequestParams& aParams);
|
||||||
|
|
||||||
|
bool
|
||||||
|
RecvPBackgroundLSSimpleRequestConstructor(
|
||||||
|
PBackgroundLSSimpleRequestParent* aActor,
|
||||||
|
const LSSimpleRequestParams& aParams);
|
||||||
|
|
||||||
|
bool
|
||||||
|
DeallocPBackgroundLSSimpleRequestParent(
|
||||||
|
PBackgroundLSSimpleRequestParent* aActor);
|
||||||
|
|
||||||
|
namespace localstorage {
|
||||||
|
|
||||||
|
already_AddRefed<mozilla::dom::quota::Client>
|
||||||
|
CreateQuotaClient();
|
||||||
|
|
||||||
|
} // namespace localstorage
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_ActorsParent_h
|
|
@ -0,0 +1,364 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set 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 "LSDatabase.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
typedef nsDataHashtable<nsCStringHashKey, LSDatabase*> LSDatabaseHashtable;
|
||||||
|
|
||||||
|
StaticAutoPtr<LSDatabaseHashtable> gLSDatabases;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
LSDatabase::LSDatabase(const nsACString& aOrigin)
|
||||||
|
: mActor(nullptr)
|
||||||
|
, mSnapshot(nullptr)
|
||||||
|
, mOrigin(aOrigin)
|
||||||
|
, mAllowedToClose(false)
|
||||||
|
, mRequestedAllowToClose(false)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (!gLSDatabases) {
|
||||||
|
gLSDatabases = new LSDatabaseHashtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(!gLSDatabases->Get(mOrigin));
|
||||||
|
gLSDatabases->Put(mOrigin, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSDatabase::~LSDatabase()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(!mSnapshot);
|
||||||
|
|
||||||
|
if (!mAllowedToClose) {
|
||||||
|
AllowToClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mActor) {
|
||||||
|
mActor->SendDeleteMeInternal();
|
||||||
|
MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
LSDatabase*
|
||||||
|
LSDatabase::Get(const nsACString& aOrigin)
|
||||||
|
{
|
||||||
|
return gLSDatabases ? gLSDatabases->Get(aOrigin) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSDatabase::SetActor(LSDatabaseChild* aActor)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aActor);
|
||||||
|
MOZ_ASSERT(!mActor);
|
||||||
|
|
||||||
|
mActor = aActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSDatabase::RequestAllowToClose()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(!mRequestedAllowToClose);
|
||||||
|
|
||||||
|
mRequestedAllowToClose = true;
|
||||||
|
|
||||||
|
if (mSnapshot) {
|
||||||
|
mSnapshot->MarkDirty();
|
||||||
|
} else {
|
||||||
|
AllowToClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSDatabase::NoteFinishedSnapshot(LSSnapshot* aSnapshot)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aSnapshot == mSnapshot);
|
||||||
|
|
||||||
|
mSnapshot = nullptr;
|
||||||
|
|
||||||
|
if (mRequestedAllowToClose) {
|
||||||
|
AllowToClose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::GetLength(LSObject* aObject,
|
||||||
|
uint32_t* aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->GetLength(aResult);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::GetKey(LSObject* aObject,
|
||||||
|
uint32_t aIndex,
|
||||||
|
nsAString& aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->GetKey(aIndex, aResult);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::GetItem(LSObject* aObject,
|
||||||
|
const nsAString& aKey,
|
||||||
|
nsAString& aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->GetItem(aKey, aResult);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::GetKeys(LSObject* aObject,
|
||||||
|
nsTArray<nsString>& aKeys)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->GetKeys(aKeys);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::SetItem(LSObject* aObject,
|
||||||
|
const nsAString& aKey,
|
||||||
|
const nsAString& aValue,
|
||||||
|
LSNotifyInfo& aNotifyInfo)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->SetItem(aKey, aValue, aNotifyInfo);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::RemoveItem(LSObject* aObject,
|
||||||
|
const nsAString& aKey,
|
||||||
|
LSNotifyInfo& aNotifyInfo)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->RemoveItem(aKey, aNotifyInfo);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::Clear(LSObject* aObject,
|
||||||
|
LSNotifyInfo& aNotifyInfo)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mSnapshot->Clear(aNotifyInfo);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::BeginExplicitSnapshot(LSObject* aObject)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
if (mSnapshot) {
|
||||||
|
return NS_ERROR_ALREADY_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult rv = EnsureSnapshot(aObject, /* aExplicit */ true);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::EndExplicitSnapshot(LSObject* aObject)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
if (!mSnapshot) {
|
||||||
|
return NS_ERROR_NOT_INITIALIZED;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(mSnapshot->Explicit());
|
||||||
|
|
||||||
|
nsresult rv = mSnapshot->End();
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSDatabase::EnsureSnapshot(LSObject* aObject,
|
||||||
|
bool aExplicit)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aObject);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT_IF(mSnapshot, !aExplicit);
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
|
||||||
|
if (mSnapshot) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<LSSnapshot> snapshot = new LSSnapshot(this);
|
||||||
|
|
||||||
|
LSSnapshotChild* actor = new LSSnapshotChild(snapshot);
|
||||||
|
|
||||||
|
LSSnapshotInitInfo initInfo;
|
||||||
|
bool ok =
|
||||||
|
mActor->SendPBackgroundLSSnapshotConstructor(actor,
|
||||||
|
aObject->DocumentURI(),
|
||||||
|
/* increasePeakUsage */ true,
|
||||||
|
/* requestedSize */ 131072,
|
||||||
|
/* minSize */ 4096,
|
||||||
|
&initInfo);
|
||||||
|
if (NS_WARN_IF(!ok)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
snapshot->SetActor(actor);
|
||||||
|
|
||||||
|
// This add refs snapshot.
|
||||||
|
nsresult rv = snapshot->Init(initInfo, aExplicit);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is cleared in LSSnapshot::Run() before the snapshot is destroyed.
|
||||||
|
mSnapshot = snapshot;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSDatabase::AllowToClose()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(!mAllowedToClose);
|
||||||
|
MOZ_ASSERT(!mSnapshot);
|
||||||
|
|
||||||
|
mAllowedToClose = true;
|
||||||
|
|
||||||
|
if (mActor) {
|
||||||
|
mActor->SendAllowToClose();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(gLSDatabases);
|
||||||
|
MOZ_ASSERT(gLSDatabases->Get(mOrigin));
|
||||||
|
gLSDatabases->Remove(mOrigin);
|
||||||
|
|
||||||
|
if (!gLSDatabases->Count()) {
|
||||||
|
gLSDatabases = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,120 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_LSDatabase_h
|
||||||
|
#define mozilla_dom_localstorage_LSDatabase_h
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LSDatabaseChild;
|
||||||
|
class LSSnapshot;
|
||||||
|
|
||||||
|
class LSDatabase final
|
||||||
|
{
|
||||||
|
LSDatabaseChild* mActor;
|
||||||
|
|
||||||
|
LSSnapshot* mSnapshot;
|
||||||
|
|
||||||
|
const nsCString mOrigin;
|
||||||
|
|
||||||
|
bool mAllowedToClose;
|
||||||
|
bool mRequestedAllowToClose;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LSDatabase(const nsACString& aOrigin);
|
||||||
|
|
||||||
|
static LSDatabase*
|
||||||
|
Get(const nsACString& aOrigin);
|
||||||
|
|
||||||
|
NS_INLINE_DECL_REFCOUNTING(LSDatabase)
|
||||||
|
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetActor(LSDatabaseChild* aActor);
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearActor()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
|
||||||
|
mActor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IsAllowedToClose() const
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
return mAllowedToClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RequestAllowToClose();
|
||||||
|
|
||||||
|
void
|
||||||
|
NoteFinishedSnapshot(LSSnapshot* aSnapshot);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetLength(LSObject* aObject,
|
||||||
|
uint32_t* aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetKey(LSObject* aObject,
|
||||||
|
uint32_t aIndex,
|
||||||
|
nsAString& aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetItem(LSObject* aObject,
|
||||||
|
const nsAString& aKey,
|
||||||
|
nsAString& aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetKeys(LSObject* aObject,
|
||||||
|
nsTArray<nsString>& aKeys);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
SetItem(LSObject* aObject,
|
||||||
|
const nsAString& aKey,
|
||||||
|
const nsAString& aValue,
|
||||||
|
LSNotifyInfo& aNotifyInfo);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
RemoveItem(LSObject* aObject,
|
||||||
|
const nsAString& aKey,
|
||||||
|
LSNotifyInfo& aNotifyInfo);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Clear(LSObject* aObject,
|
||||||
|
LSNotifyInfo& aNotifyInfo);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
BeginExplicitSnapshot(LSObject* aObject);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
EndExplicitSnapshot(LSObject* aObject);
|
||||||
|
|
||||||
|
private:
|
||||||
|
~LSDatabase();
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
EnsureSnapshot(LSObject* aObject,
|
||||||
|
bool aExplicit = false);
|
||||||
|
|
||||||
|
void
|
||||||
|
AllowToClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_LSDatabase_h
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,262 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_LSObject_h
|
||||||
|
#define mozilla_dom_localstorage_LSObject_h
|
||||||
|
|
||||||
|
#include "mozilla/dom/Storage.h"
|
||||||
|
|
||||||
|
class nsGlobalWindowInner;
|
||||||
|
class nsIPrincipal;
|
||||||
|
class nsPIDOMWindowInner;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
|
||||||
|
class ErrorResult;
|
||||||
|
|
||||||
|
namespace ipc {
|
||||||
|
|
||||||
|
class PrincipalInfo;
|
||||||
|
|
||||||
|
} // namespace ipc
|
||||||
|
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LSDatabase;
|
||||||
|
class LSObjectChild;
|
||||||
|
class LSObserver;
|
||||||
|
class LSRequestChild;
|
||||||
|
class LSRequestChildCallback;
|
||||||
|
class LSRequestParams;
|
||||||
|
class LSRequestResponse;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Backs the WebIDL `Storage` binding; all content LocalStorage calls are
|
||||||
|
* handled by this class.
|
||||||
|
*
|
||||||
|
* ## Semantics under e10s / multi-process ##
|
||||||
|
*
|
||||||
|
* A snapshot mechanism used in conjuction with stable points ensures that JS
|
||||||
|
* run-to-completion semantics are experienced even if the same origin is
|
||||||
|
* concurrently accessing LocalStorage across multiple content processes.
|
||||||
|
*
|
||||||
|
* ### Snapshot Consistency ###
|
||||||
|
*
|
||||||
|
* An LSSnapshot is created locally whenever the contents of LocalStorage are
|
||||||
|
* about to be read or written (including length). This synchronously
|
||||||
|
* establishes a corresponding Snapshot in PBackground in the parent process.
|
||||||
|
* An effort is made to send as much data from the parent process as possible,
|
||||||
|
* so sites using a small/reasonable amount of LocalStorage data will have it
|
||||||
|
* sent to the content process for immediate access. Sites with greater
|
||||||
|
* LocalStorage usage may only have some of the information relayed. In that
|
||||||
|
* case, the parent Snapshot will ensure that it retains the exact state of the
|
||||||
|
* parent Datastore at the moment the Snapshot was created.
|
||||||
|
*/
|
||||||
|
class LSObject final
|
||||||
|
: public Storage
|
||||||
|
{
|
||||||
|
typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
|
||||||
|
|
||||||
|
friend nsGlobalWindowInner;
|
||||||
|
|
||||||
|
nsAutoPtr<PrincipalInfo> mPrincipalInfo;
|
||||||
|
|
||||||
|
RefPtr<LSDatabase> mDatabase;
|
||||||
|
RefPtr<LSObserver> mObserver;
|
||||||
|
|
||||||
|
uint32_t mPrivateBrowsingId;
|
||||||
|
nsCString mOrigin;
|
||||||
|
nsString mDocumentURI;
|
||||||
|
|
||||||
|
bool mInExplicitSnapshot;
|
||||||
|
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The normal creation path invoked by nsGlobalWindowInner.
|
||||||
|
*/
|
||||||
|
static nsresult
|
||||||
|
CreateForWindow(nsPIDOMWindowInner* aWindow,
|
||||||
|
Storage** aStorage);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* nsIDOMStorageManager creation path for use in testing logic. Supports the
|
||||||
|
* system principal where CreateForWindow does not. This is also why aPrivate
|
||||||
|
* exists separate from the principal; because the system principal can never
|
||||||
|
* be mutated to have a private browsing id even though it can be used in a
|
||||||
|
* window/document marked as private browsing. That's a legacy issue that is
|
||||||
|
* being dealt with, but it's why it exists here.
|
||||||
|
*/
|
||||||
|
static nsresult
|
||||||
|
CreateForPrincipal(nsPIDOMWindowInner* aWindow,
|
||||||
|
nsIPrincipal* aPrincipal,
|
||||||
|
const nsAString& aDocumentURI,
|
||||||
|
bool aPrivate,
|
||||||
|
LSObject** aObject);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for requests from the parent process to the parent process; in that
|
||||||
|
* case we want ActorsParent to know our event-target and this is better than
|
||||||
|
* trying to tunnel the pointer through IPC.
|
||||||
|
*/
|
||||||
|
static already_AddRefed<nsIEventTarget>
|
||||||
|
GetSyncLoopEventTarget();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper invoked by ContentChild::OnChannelReceivedMessage when a sync IPC
|
||||||
|
* message is received. This will be invoked on the IPC I/O thread and it's
|
||||||
|
* necessary to unblock the main thread when this happens to avoid the
|
||||||
|
* potential for browser deadlock. This should only occur in (ugly) testing
|
||||||
|
* scenarios where CPOWs are in use.
|
||||||
|
*
|
||||||
|
* Cancellation will result in the underlying LSRequest being explicitly
|
||||||
|
* canceled, resulting in the parent sending an NS_ERROR_FAILURE result.
|
||||||
|
*/
|
||||||
|
static void
|
||||||
|
CancelSyncLoop();
|
||||||
|
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsString&
|
||||||
|
DocumentURI() const
|
||||||
|
{
|
||||||
|
return mDocumentURI;
|
||||||
|
}
|
||||||
|
|
||||||
|
LSRequestChild*
|
||||||
|
StartRequest(nsIEventTarget* aMainEventTarget,
|
||||||
|
const LSRequestParams& aParams,
|
||||||
|
LSRequestChildCallback* aCallback);
|
||||||
|
|
||||||
|
// Storage overrides.
|
||||||
|
StorageType
|
||||||
|
Type() const override;
|
||||||
|
|
||||||
|
bool
|
||||||
|
IsForkOf(const Storage* aStorage) const override;
|
||||||
|
|
||||||
|
int64_t
|
||||||
|
GetOriginQuotaUsage() const override;
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
GetLength(nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
Key(uint32_t aIndex,
|
||||||
|
nsAString& aResult,
|
||||||
|
nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
GetItem(const nsAString& aKey,
|
||||||
|
nsAString& aResult,
|
||||||
|
nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
GetSupportedNames(nsTArray<nsString>& aNames) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
SetItem(const nsAString& aKey,
|
||||||
|
const nsAString& aValue,
|
||||||
|
nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
RemoveItem(const nsAString& aKey,
|
||||||
|
nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
Clear(nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Testing Methods: See Storage.h
|
||||||
|
void
|
||||||
|
Open(nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
Close(nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
BeginExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
void
|
||||||
|
EndExplicitSnapshot(nsIPrincipal& aSubjectPrincipal,
|
||||||
|
ErrorResult& aError) override;
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS_INHERITED
|
||||||
|
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(LSObject, Storage)
|
||||||
|
|
||||||
|
private:
|
||||||
|
LSObject(nsPIDOMWindowInner* aWindow,
|
||||||
|
nsIPrincipal* aPrincipal);
|
||||||
|
|
||||||
|
~LSObject();
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
DoRequestSynchronously(const LSRequestParams& aParams,
|
||||||
|
LSRequestResponse& aResponse);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
EnsureDatabase();
|
||||||
|
|
||||||
|
void
|
||||||
|
DropDatabase();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked by nsGlobalWindowInner whenever a new "storage" event listener is
|
||||||
|
* added to the window in order to ensure that "storage" events are received
|
||||||
|
* from other processes. (`LSObject::OnChange` directly invokes
|
||||||
|
* `Storage::NotifyChange` to notify in-process listeners.)
|
||||||
|
*
|
||||||
|
* If this is the first request in the process for an observer for this
|
||||||
|
* origin, this will trigger a RequestHelper-mediated synchronous LSRequest
|
||||||
|
* to prepare a new observer in the parent process and also construction of
|
||||||
|
* corresponding actors, which will result in the observer being fully
|
||||||
|
* registered in the parent process.
|
||||||
|
*/
|
||||||
|
nsresult
|
||||||
|
EnsureObserver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked by nsGlobalWindowInner whenever its last "storage" event listener
|
||||||
|
* is removed.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
DropObserver();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal helper method used by mutation methods that wraps the call to
|
||||||
|
* Storage::NotifyChange to generate same-process "storage" events.
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
OnChange(const nsAString& aKey,
|
||||||
|
const nsAString& aOldValue,
|
||||||
|
const nsAString& aNewValue);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
EndExplicitSnapshotInternal();
|
||||||
|
|
||||||
|
// Storage overrides.
|
||||||
|
void
|
||||||
|
LastRelease() override;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_LSObject_h
|
|
@ -0,0 +1,76 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set 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 "LSObserver.h"
|
||||||
|
|
||||||
|
#include "mozilla/ipc/BackgroundChild.h"
|
||||||
|
#include "mozilla/ipc/BackgroundUtils.h"
|
||||||
|
#include "mozilla/ipc/PBackgroundChild.h"
|
||||||
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsIScriptObjectPrincipal.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
typedef nsDataHashtable<nsCStringHashKey, LSObserver*> LSObserverHashtable;
|
||||||
|
|
||||||
|
StaticAutoPtr<LSObserverHashtable> gLSObservers;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
LSObserver::LSObserver(const nsACString& aOrigin)
|
||||||
|
: mActor(nullptr)
|
||||||
|
, mOrigin(aOrigin)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (!gLSObservers) {
|
||||||
|
gLSObservers = new LSObserverHashtable();
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(!gLSObservers->Get(mOrigin));
|
||||||
|
gLSObservers->Put(mOrigin, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
LSObserver::~LSObserver()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (mActor) {
|
||||||
|
mActor->SendDeleteMeInternal();
|
||||||
|
MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(gLSObservers);
|
||||||
|
MOZ_ASSERT(gLSObservers->Get(mOrigin));
|
||||||
|
gLSObservers->Remove(mOrigin);
|
||||||
|
|
||||||
|
if (!gLSObservers->Count()) {
|
||||||
|
gLSObservers = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
LSObserver*
|
||||||
|
LSObserver::Get(const nsACString& aOrigin)
|
||||||
|
{
|
||||||
|
return gLSObservers ? gLSObservers->Get(aOrigin) : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSObserver::SetActor(LSObserverChild* aActor)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aActor);
|
||||||
|
MOZ_ASSERT(!mActor);
|
||||||
|
|
||||||
|
mActor = aActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,74 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_LSObserver_h
|
||||||
|
#define mozilla_dom_localstorage_LSObserver_h
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LSObserverChild;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Effectively just a refcounted life-cycle management wrapper around
|
||||||
|
* LSObserverChild which exists to receive "storage" event information from
|
||||||
|
* other processes. (Same-process events are handled within the process, see
|
||||||
|
* `LSObject::OnChange`.)
|
||||||
|
*
|
||||||
|
* ## Lifecycle ##
|
||||||
|
* - Created by LSObject::EnsureObserver via synchronous LSRequest idiom
|
||||||
|
* whenever the first window's origin adds a "storage" event. Placed in the
|
||||||
|
* gLSObservers LSObserverHashtable for subsequent LSObject's via
|
||||||
|
* LSObserver::Get lookup.
|
||||||
|
* - The LSObserverChild directly handles "Observe" messages, shunting them
|
||||||
|
* directly to Storage::NotifyChange which does all the legwork of notifying
|
||||||
|
* windows about "storage" events.
|
||||||
|
* - Destroyed when refcount goes to zero due to all owning LSObjects being
|
||||||
|
* destroyed or having their `LSObject::DropObserver` methods invoked due to
|
||||||
|
* the last "storage" event listener being removed from the owning window.
|
||||||
|
*/
|
||||||
|
class LSObserver final
|
||||||
|
{
|
||||||
|
friend class LSObject;
|
||||||
|
|
||||||
|
LSObserverChild* mActor;
|
||||||
|
|
||||||
|
const nsCString mOrigin;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LSObserver(const nsACString& aOrigin);
|
||||||
|
|
||||||
|
static LSObserver*
|
||||||
|
Get(const nsACString& aOrigin);
|
||||||
|
|
||||||
|
NS_INLINE_DECL_REFCOUNTING(LSObserver)
|
||||||
|
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSDatabase);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetActor(LSObserverChild* aActor);
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearActor()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
|
||||||
|
mActor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
~LSObserver();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_LSObserver_h
|
|
@ -0,0 +1,785 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set 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 "LSSnapshot.h"
|
||||||
|
|
||||||
|
#include "nsContentUtils.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
const uint32_t kSnapshotTimeoutMs = 20000;
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
LSSnapshot::LSSnapshot(LSDatabase* aDatabase)
|
||||||
|
: mDatabase(aDatabase)
|
||||||
|
, mActor(nullptr)
|
||||||
|
, mInitLength(0)
|
||||||
|
, mLength(0)
|
||||||
|
, mExactUsage(0)
|
||||||
|
, mPeakUsage(0)
|
||||||
|
, mLoadState(LoadState::Initial)
|
||||||
|
, mExplicit(false)
|
||||||
|
, mHasPendingStableStateCallback(false)
|
||||||
|
, mHasPendingTimerCallback(false)
|
||||||
|
, mDirty(false)
|
||||||
|
#ifdef DEBUG
|
||||||
|
, mInitialized(false)
|
||||||
|
, mSentFinish(false)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
LSSnapshot::~LSSnapshot()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mDatabase);
|
||||||
|
MOZ_ASSERT(!mHasPendingStableStateCallback);
|
||||||
|
MOZ_ASSERT(!mHasPendingTimerCallback);
|
||||||
|
MOZ_ASSERT_IF(mInitialized, mSentFinish);
|
||||||
|
|
||||||
|
if (mActor) {
|
||||||
|
mActor->SendDeleteMeInternal();
|
||||||
|
MOZ_ASSERT(!mActor, "SendDeleteMeInternal should have cleared!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshot::SetActor(LSSnapshotChild* aActor)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(aActor);
|
||||||
|
MOZ_ASSERT(!mActor);
|
||||||
|
|
||||||
|
mActor = aActor;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::Init(const LSSnapshotInitInfo& aInitInfo,
|
||||||
|
bool aExplicit)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(!mSelfRef);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mLoadState == LoadState::Initial);
|
||||||
|
MOZ_ASSERT(!mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
mSelfRef = this;
|
||||||
|
|
||||||
|
LoadState loadState = aInitInfo.loadState();
|
||||||
|
|
||||||
|
const nsTArray<LSItemInfo>& itemInfos = aInitInfo.itemInfos();
|
||||||
|
for (uint32_t i = 0; i < itemInfos.Length(); i++) {
|
||||||
|
const LSItemInfo& itemInfo = itemInfos[i];
|
||||||
|
|
||||||
|
const nsString& value = itemInfo.value();
|
||||||
|
|
||||||
|
if (loadState != LoadState::AllOrderedItems && !value.IsVoid()) {
|
||||||
|
mLoadedItems.PutEntry(itemInfo.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
mValues.Put(itemInfo.key(), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loadState == LoadState::Partial) {
|
||||||
|
mInitLength = aInitInfo.totalLength();
|
||||||
|
mLength = mInitLength;
|
||||||
|
} else if (loadState == LoadState::AllOrderedKeys) {
|
||||||
|
mInitLength = aInitInfo.totalLength();
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(loadState == LoadState::AllOrderedItems);
|
||||||
|
}
|
||||||
|
|
||||||
|
mExactUsage = aInitInfo.initialUsage();
|
||||||
|
mPeakUsage = aInitInfo.peakUsage();
|
||||||
|
|
||||||
|
mLoadState = aInitInfo.loadState();
|
||||||
|
|
||||||
|
mExplicit = aExplicit;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
mInitialized = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (!mExplicit) {
|
||||||
|
mTimer = NS_NewTimer();
|
||||||
|
MOZ_ASSERT(mTimer);
|
||||||
|
|
||||||
|
ScheduleStableStateCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::GetLength(uint32_t* aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
if (mLoadState == LoadState::Partial) {
|
||||||
|
*aResult = mLength;
|
||||||
|
} else {
|
||||||
|
*aResult = mValues.Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::GetKey(uint32_t aIndex,
|
||||||
|
nsAString& aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
nsresult rv = EnsureAllKeys();
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
aResult.SetIsVoid(true);
|
||||||
|
for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) {
|
||||||
|
if (aIndex == 0) {
|
||||||
|
aResult = iter.Key();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
aIndex--;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::GetItem(const nsAString& aKey,
|
||||||
|
nsAString& aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
nsString result;
|
||||||
|
nsresult rv = GetItemInternal(aKey, Optional<nsString>(), result);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
aResult = result;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::GetKeys(nsTArray<nsString>& aKeys)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
nsresult rv = EnsureAllKeys();
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (auto iter = mValues.ConstIter(); !iter.Done(); iter.Next()) {
|
||||||
|
aKeys.AppendElement(iter.Key());
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::SetItem(const nsAString& aKey,
|
||||||
|
const nsAString& aValue,
|
||||||
|
LSNotifyInfo& aNotifyInfo)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
nsString oldValue;
|
||||||
|
nsresult rv =
|
||||||
|
GetItemInternal(aKey, Optional<nsString>(nsString(aValue)), oldValue);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed;
|
||||||
|
if (oldValue == aValue && oldValue.IsVoid() == aValue.IsVoid()) {
|
||||||
|
changed = false;
|
||||||
|
} else {
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
int64_t delta = static_cast<int64_t>(aValue.Length()) -
|
||||||
|
static_cast<int64_t>(oldValue.Length());
|
||||||
|
|
||||||
|
if (oldValue.IsVoid()) {
|
||||||
|
delta += static_cast<int64_t>(aKey.Length());
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = UpdateUsage(delta);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
if (oldValue.IsVoid()) {
|
||||||
|
mValues.Remove(aKey);
|
||||||
|
} else {
|
||||||
|
mValues.Put(aKey, oldValue);
|
||||||
|
}
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (oldValue.IsVoid() && mLoadState == LoadState::Partial) {
|
||||||
|
mLength++;
|
||||||
|
}
|
||||||
|
|
||||||
|
LSSetItemInfo setItemInfo;
|
||||||
|
setItemInfo.key() = aKey;
|
||||||
|
setItemInfo.oldValue() = oldValue;
|
||||||
|
setItemInfo.value() = aValue;
|
||||||
|
|
||||||
|
mWriteInfos.AppendElement(std::move(setItemInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
aNotifyInfo.changed() = changed;
|
||||||
|
aNotifyInfo.oldValue() = oldValue;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::RemoveItem(const nsAString& aKey,
|
||||||
|
LSNotifyInfo& aNotifyInfo)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
nsString oldValue;
|
||||||
|
nsresult rv =
|
||||||
|
GetItemInternal(aKey, Optional<nsString>(VoidString()), oldValue);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed;
|
||||||
|
if (oldValue.IsVoid()) {
|
||||||
|
changed = false;
|
||||||
|
} else {
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
int64_t delta = -(static_cast<int64_t>(aKey.Length()) +
|
||||||
|
static_cast<int64_t>(oldValue.Length()));
|
||||||
|
|
||||||
|
DebugOnly<nsresult> rv = UpdateUsage(delta);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
if (mLoadState == LoadState::Partial) {
|
||||||
|
mLength--;
|
||||||
|
}
|
||||||
|
|
||||||
|
LSRemoveItemInfo removeItemInfo;
|
||||||
|
removeItemInfo.key() = aKey;
|
||||||
|
removeItemInfo.oldValue() = oldValue;
|
||||||
|
|
||||||
|
mWriteInfos.AppendElement(std::move(removeItemInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
aNotifyInfo.changed() = changed;
|
||||||
|
aNotifyInfo.oldValue() = oldValue;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::Clear(LSNotifyInfo& aNotifyInfo)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
uint32_t length;
|
||||||
|
if (mLoadState == LoadState::Partial) {
|
||||||
|
length = mLength;
|
||||||
|
MOZ_ASSERT(length);
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(mActor->SendLoaded());
|
||||||
|
|
||||||
|
mLoadedItems.Clear();
|
||||||
|
mUnknownItems.Clear();
|
||||||
|
mLength = 0;
|
||||||
|
mLoadState = LoadState::AllOrderedItems;
|
||||||
|
} else {
|
||||||
|
length = mValues.Count();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool changed;
|
||||||
|
if (!length) {
|
||||||
|
changed = false;
|
||||||
|
} else {
|
||||||
|
changed = true;
|
||||||
|
|
||||||
|
DebugOnly<nsresult> rv = UpdateUsage(-mExactUsage);
|
||||||
|
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||||
|
|
||||||
|
mValues.Clear();
|
||||||
|
|
||||||
|
LSClearInfo clearInfo;
|
||||||
|
|
||||||
|
mWriteInfos.AppendElement(std::move(clearInfo));
|
||||||
|
}
|
||||||
|
|
||||||
|
aNotifyInfo.changed() = changed;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshot::MarkDirty()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
if (mDirty) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mDirty = true;
|
||||||
|
|
||||||
|
if (!mExplicit && !mHasPendingStableStateCallback) {
|
||||||
|
CancelTimer();
|
||||||
|
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(Checkpoint());
|
||||||
|
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(Finish());
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(!mHasPendingTimerCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::End()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mExplicit);
|
||||||
|
MOZ_ASSERT(!mHasPendingStableStateCallback);
|
||||||
|
MOZ_ASSERT(!mHasPendingTimerCallback);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
nsresult rv = Checkpoint();
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<LSSnapshot> kungFuDeathGrip = this;
|
||||||
|
|
||||||
|
rv = Finish();
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NS_WARN_IF(!mActor->SendPing())) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshot::ScheduleStableStateCallback()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mTimer);
|
||||||
|
MOZ_ASSERT(!mExplicit);
|
||||||
|
MOZ_ASSERT(!mHasPendingStableStateCallback);
|
||||||
|
|
||||||
|
CancelTimer();
|
||||||
|
|
||||||
|
nsCOMPtr<nsIRunnable> runnable = this;
|
||||||
|
nsContentUtils::RunInStableState(runnable.forget());
|
||||||
|
|
||||||
|
mHasPendingStableStateCallback = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshot::MaybeScheduleStableStateCallback()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
|
||||||
|
if (!mExplicit && !mHasPendingStableStateCallback) {
|
||||||
|
ScheduleStableStateCallback();
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(!mHasPendingTimerCallback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::GetItemInternal(const nsAString& aKey,
|
||||||
|
const Optional<nsString>& aValue,
|
||||||
|
nsAString& aResult)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
nsString result;
|
||||||
|
|
||||||
|
switch (mLoadState) {
|
||||||
|
case LoadState::Partial: {
|
||||||
|
if (mValues.Get(aKey, &result)) {
|
||||||
|
MOZ_ASSERT(!result.IsVoid());
|
||||||
|
} else if (mLoadedItems.GetEntry(aKey) || mUnknownItems.GetEntry(aKey)) {
|
||||||
|
result.SetIsVoid(true);
|
||||||
|
} else {
|
||||||
|
if (NS_WARN_IF(!mActor->SendLoadItem(nsString(aKey), &result))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (result.IsVoid()) {
|
||||||
|
mUnknownItems.PutEntry(aKey);
|
||||||
|
} else {
|
||||||
|
mLoadedItems.PutEntry(aKey);
|
||||||
|
mValues.Put(aKey, result);
|
||||||
|
|
||||||
|
if (mLoadedItems.Count() == mInitLength) {
|
||||||
|
mLoadedItems.Clear();
|
||||||
|
mUnknownItems.Clear();
|
||||||
|
mLength = 0;
|
||||||
|
mLoadState = LoadState::AllUnorderedItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aValue.WasPassed()) {
|
||||||
|
const nsString& value = aValue.Value();
|
||||||
|
if (!value.IsVoid()) {
|
||||||
|
mValues.Put(aKey, value);
|
||||||
|
} else if (!result.IsVoid()) {
|
||||||
|
mValues.Remove(aKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LoadState::AllOrderedKeys: {
|
||||||
|
if (mValues.Get(aKey, &result)) {
|
||||||
|
if (result.IsVoid()) {
|
||||||
|
if (NS_WARN_IF(!mActor->SendLoadItem(nsString(aKey), &result))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(!result.IsVoid());
|
||||||
|
|
||||||
|
mLoadedItems.PutEntry(aKey);
|
||||||
|
mValues.Put(aKey, result);
|
||||||
|
|
||||||
|
if (mLoadedItems.Count() == mInitLength) {
|
||||||
|
mLoadedItems.Clear();
|
||||||
|
MOZ_ASSERT(mLength == 0);
|
||||||
|
mLoadState = LoadState::AllOrderedItems;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
result.SetIsVoid(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aValue.WasPassed()) {
|
||||||
|
const nsString& value = aValue.Value();
|
||||||
|
if (!value.IsVoid()) {
|
||||||
|
mValues.Put(aKey, value);
|
||||||
|
} else if (!result.IsVoid()) {
|
||||||
|
mValues.Remove(aKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case LoadState::AllUnorderedItems:
|
||||||
|
case LoadState::AllOrderedItems: {
|
||||||
|
if (aValue.WasPassed()) {
|
||||||
|
const nsString& value = aValue.Value();
|
||||||
|
if (!value.IsVoid()) {
|
||||||
|
auto entry = mValues.LookupForAdd(aKey);
|
||||||
|
if (entry) {
|
||||||
|
result = entry.Data();
|
||||||
|
entry.Data() = value;
|
||||||
|
} else {
|
||||||
|
result.SetIsVoid(true);
|
||||||
|
entry.OrInsert([value]() { return value; });
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (auto entry = mValues.Lookup(aKey)) {
|
||||||
|
result = entry.Data();
|
||||||
|
MOZ_ASSERT(!result.IsVoid());
|
||||||
|
entry.Remove();
|
||||||
|
} else {
|
||||||
|
result.SetIsVoid(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (mValues.Get(aKey, &result)) {
|
||||||
|
MOZ_ASSERT(!result.IsVoid());
|
||||||
|
} else {
|
||||||
|
result.SetIsVoid(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Bad state!");
|
||||||
|
}
|
||||||
|
|
||||||
|
aResult = result;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::EnsureAllKeys()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
MOZ_ASSERT(mLoadState != LoadState::Initial);
|
||||||
|
|
||||||
|
if (mLoadState == LoadState::AllOrderedKeys ||
|
||||||
|
mLoadState == LoadState::AllOrderedItems) {
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsTArray<nsString> keys;
|
||||||
|
if (NS_WARN_IF(!mActor->SendLoadKeys(&keys))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsDataHashtable<nsStringHashKey, nsString> newValues;
|
||||||
|
|
||||||
|
for (auto key : keys) {
|
||||||
|
newValues.Put(key, VoidString());
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint32_t index = 0; index < mWriteInfos.Length(); index++) {
|
||||||
|
const LSWriteInfo& writeInfo = mWriteInfos[index];
|
||||||
|
|
||||||
|
switch (writeInfo.type()) {
|
||||||
|
case LSWriteInfo::TLSSetItemInfo: {
|
||||||
|
newValues.Put(writeInfo.get_LSSetItemInfo().key(), VoidString());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LSWriteInfo::TLSRemoveItemInfo: {
|
||||||
|
newValues.Remove(writeInfo.get_LSRemoveItemInfo().key());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case LSWriteInfo::TLSClearInfo: {
|
||||||
|
newValues.Clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Should never get here!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT_IF(mLoadState == LoadState::AllUnorderedItems,
|
||||||
|
newValues.Count() == mValues.Count());
|
||||||
|
|
||||||
|
for (auto iter = newValues.Iter(); !iter.Done(); iter.Next()) {
|
||||||
|
nsString value;
|
||||||
|
if (mValues.Get(iter.Key(), &value)) {
|
||||||
|
iter.Data() = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mValues.SwapElements(newValues);
|
||||||
|
|
||||||
|
if (mLoadState == LoadState::Partial) {
|
||||||
|
mUnknownItems.Clear();
|
||||||
|
mLength = 0;
|
||||||
|
mLoadState = LoadState::AllOrderedKeys;
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(mLoadState == LoadState::AllUnorderedItems);
|
||||||
|
|
||||||
|
MOZ_ASSERT(mUnknownItems.Count() == 0);
|
||||||
|
MOZ_ASSERT(mLength == 0);
|
||||||
|
mLoadState = LoadState::AllOrderedItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::UpdateUsage(int64_t aDelta)
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mDatabase);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mPeakUsage >= mExactUsage);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
int64_t newExactUsage = mExactUsage + aDelta;
|
||||||
|
if (newExactUsage > mPeakUsage) {
|
||||||
|
int64_t minSize = newExactUsage - mPeakUsage;
|
||||||
|
int64_t requestedSize = minSize + 4096;
|
||||||
|
int64_t size;
|
||||||
|
if (NS_WARN_IF(!mActor->SendIncreasePeakUsage(requestedSize,
|
||||||
|
minSize,
|
||||||
|
&size))) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(size >= 0);
|
||||||
|
|
||||||
|
if (size == 0) {
|
||||||
|
return NS_ERROR_FILE_NO_DEVICE_SPACE;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPeakUsage += size;
|
||||||
|
}
|
||||||
|
|
||||||
|
mExactUsage = newExactUsage;
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::Checkpoint()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
if (!mWriteInfos.IsEmpty()) {
|
||||||
|
MOZ_ALWAYS_TRUE(mActor->SendCheckpoint(mWriteInfos));
|
||||||
|
|
||||||
|
mWriteInfos.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LSSnapshot::Finish()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mDatabase);
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
MOZ_ASSERT(mInitialized);
|
||||||
|
MOZ_ASSERT(!mSentFinish);
|
||||||
|
|
||||||
|
MOZ_ALWAYS_TRUE(mActor->SendFinish());
|
||||||
|
|
||||||
|
mDatabase->NoteFinishedSnapshot(this);
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
mSentFinish = true;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Clear the self reference added in Init method.
|
||||||
|
MOZ_ASSERT(mSelfRef);
|
||||||
|
mSelfRef = nullptr;
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LSSnapshot::CancelTimer()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mTimer);
|
||||||
|
|
||||||
|
if (mHasPendingTimerCallback) {
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(mTimer->Cancel());
|
||||||
|
mHasPendingTimerCallback = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// static
|
||||||
|
void
|
||||||
|
LSSnapshot::TimerCallback(nsITimer* aTimer, void* aClosure)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(aTimer);
|
||||||
|
|
||||||
|
auto* self = static_cast<LSSnapshot*>(aClosure);
|
||||||
|
MOZ_ASSERT(self);
|
||||||
|
MOZ_ASSERT(self->mTimer);
|
||||||
|
MOZ_ASSERT(SameCOMIdentity(self->mTimer, aTimer));
|
||||||
|
MOZ_ASSERT(!self->mHasPendingStableStateCallback);
|
||||||
|
MOZ_ASSERT(self->mHasPendingTimerCallback);
|
||||||
|
|
||||||
|
self->mHasPendingTimerCallback = false;
|
||||||
|
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(self->Finish());
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(LSSnapshot, nsIRunnable)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LSSnapshot::Run()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(!mExplicit);
|
||||||
|
MOZ_ASSERT(mHasPendingStableStateCallback);
|
||||||
|
MOZ_ASSERT(!mHasPendingTimerCallback);
|
||||||
|
|
||||||
|
mHasPendingStableStateCallback = false;
|
||||||
|
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(Checkpoint());
|
||||||
|
|
||||||
|
if (mDirty || !Preferences::GetBool("dom.storage.snapshot_reusing")) {
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(Finish());
|
||||||
|
} else if (!mExplicit) {
|
||||||
|
MOZ_ASSERT(mTimer);
|
||||||
|
|
||||||
|
MOZ_ALWAYS_SUCCEEDS(
|
||||||
|
mTimer->InitWithNamedFuncCallback(TimerCallback,
|
||||||
|
this,
|
||||||
|
kSnapshotTimeoutMs,
|
||||||
|
nsITimer::TYPE_ONE_SHOT,
|
||||||
|
"LSSnapshot::TimerCallback"));
|
||||||
|
|
||||||
|
mHasPendingTimerCallback = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,204 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_LSSnapshot_h
|
||||||
|
#define mozilla_dom_localstorage_LSSnapshot_h
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LSDatabase;
|
||||||
|
class LSNotifyInfo;
|
||||||
|
class LSSnapshotChild;
|
||||||
|
class LSSnapshotInitInfo;
|
||||||
|
class LSWriteInfo;
|
||||||
|
|
||||||
|
class LSSnapshot final
|
||||||
|
: public nsIRunnable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* The LoadState expresses what subset of information a snapshot has from the
|
||||||
|
* authoritative Datastore in the parent process. The initial snapshot is
|
||||||
|
* populated heuristically based on the size of the keys and size of the items
|
||||||
|
* (inclusive of the key value; item is key+value, not just value) of the
|
||||||
|
* entire datastore relative to the configured prefill limit (via pref
|
||||||
|
* "dom.storage.snapshot_prefill" exposed as gSnapshotPrefill in bytes).
|
||||||
|
*
|
||||||
|
* If there's less data than the limit, we send both keys and values and end
|
||||||
|
* up as AllOrderedItems. If there's enough room for all the keys but not
|
||||||
|
* all the values, we end up as AllOrderedKeys with as many values present as
|
||||||
|
* would fit. If there's not enough room for all the keys, then we end up as
|
||||||
|
* Partial with as many key-value pairs as will fit.
|
||||||
|
*
|
||||||
|
* The state AllUnorderedItems can only be reached by code getting items one
|
||||||
|
* by one.
|
||||||
|
*/
|
||||||
|
enum class LoadState
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Class constructed, Init(LSSnapshotInitInfo) has not been invoked yet.
|
||||||
|
*/
|
||||||
|
Initial,
|
||||||
|
/**
|
||||||
|
* Some keys and their values are known.
|
||||||
|
*/
|
||||||
|
Partial,
|
||||||
|
/**
|
||||||
|
* All the keys are known in order, but some values are unknown.
|
||||||
|
*/
|
||||||
|
AllOrderedKeys,
|
||||||
|
/**
|
||||||
|
* All keys and their values are known, but in an arbitrary order.
|
||||||
|
*/
|
||||||
|
AllUnorderedItems,
|
||||||
|
/**
|
||||||
|
* All keys and their values are known and are present in their canonical
|
||||||
|
* order. This is everything, and is the preferred case. The initial
|
||||||
|
* population will send this info when the size of all items is less than
|
||||||
|
* the prefill threshold.
|
||||||
|
*
|
||||||
|
* mValues will contain all keys and values, mLoadedItems and mUnknownItems
|
||||||
|
* are unused.
|
||||||
|
*/
|
||||||
|
AllOrderedItems,
|
||||||
|
EndGuard
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<LSSnapshot> mSelfRef;
|
||||||
|
|
||||||
|
RefPtr<LSDatabase> mDatabase;
|
||||||
|
|
||||||
|
nsCOMPtr<nsITimer> mTimer;
|
||||||
|
|
||||||
|
LSSnapshotChild* mActor;
|
||||||
|
|
||||||
|
nsTHashtable<nsStringHashKey> mLoadedItems;
|
||||||
|
nsTHashtable<nsStringHashKey> mUnknownItems;
|
||||||
|
nsDataHashtable<nsStringHashKey, nsString> mValues;
|
||||||
|
nsTArray<LSWriteInfo> mWriteInfos;
|
||||||
|
|
||||||
|
uint32_t mInitLength;
|
||||||
|
uint32_t mLength;
|
||||||
|
int64_t mExactUsage;
|
||||||
|
int64_t mPeakUsage;
|
||||||
|
|
||||||
|
LoadState mLoadState;
|
||||||
|
|
||||||
|
bool mExplicit;
|
||||||
|
bool mHasPendingStableStateCallback;
|
||||||
|
bool mHasPendingTimerCallback;
|
||||||
|
bool mDirty;
|
||||||
|
|
||||||
|
#ifdef DEBUG
|
||||||
|
bool mInitialized;
|
||||||
|
bool mSentFinish;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit LSSnapshot(LSDatabase* aDatabase);
|
||||||
|
|
||||||
|
void
|
||||||
|
AssertIsOnOwningThread() const
|
||||||
|
{
|
||||||
|
NS_ASSERT_OWNINGTHREAD(LSSnapshot);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SetActor(LSSnapshotChild* aActor);
|
||||||
|
|
||||||
|
void
|
||||||
|
ClearActor()
|
||||||
|
{
|
||||||
|
AssertIsOnOwningThread();
|
||||||
|
MOZ_ASSERT(mActor);
|
||||||
|
|
||||||
|
mActor = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
Explicit() const
|
||||||
|
{
|
||||||
|
return mExplicit;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Init(const LSSnapshotInitInfo& aInitInfo,
|
||||||
|
bool aExplicit);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetLength(uint32_t* aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetKey(uint32_t aIndex,
|
||||||
|
nsAString& aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetItem(const nsAString& aKey,
|
||||||
|
nsAString& aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetKeys(nsTArray<nsString>& aKeys);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
SetItem(const nsAString& aKey,
|
||||||
|
const nsAString& aValue,
|
||||||
|
LSNotifyInfo& aNotifyInfo);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
RemoveItem(const nsAString& aKey,
|
||||||
|
LSNotifyInfo& aNotifyInfo);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Clear(LSNotifyInfo& aNotifyInfo);
|
||||||
|
|
||||||
|
void
|
||||||
|
MarkDirty();
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
End();
|
||||||
|
|
||||||
|
private:
|
||||||
|
~LSSnapshot();
|
||||||
|
|
||||||
|
void
|
||||||
|
ScheduleStableStateCallback();
|
||||||
|
|
||||||
|
void
|
||||||
|
MaybeScheduleStableStateCallback();
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
GetItemInternal(const nsAString& aKey,
|
||||||
|
const Optional<nsString>& aValue,
|
||||||
|
nsAString& aResult);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
EnsureAllKeys();
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
UpdateUsage(int64_t aDelta);
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Checkpoint();
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
Finish();
|
||||||
|
|
||||||
|
void
|
||||||
|
CancelTimer();
|
||||||
|
|
||||||
|
static void
|
||||||
|
TimerCallback(nsITimer* aTimer, void* aClosure);
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIRUNNABLE
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_LSSnapshot_h
|
|
@ -0,0 +1,42 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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 "LocalStorageCommon.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
Atomic<int32_t> gNextGenLocalStorageEnabled(-1);
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
const char16_t* kLocalStorageType = u"localStorage";
|
||||||
|
|
||||||
|
bool
|
||||||
|
NextGenLocalStorageEnabled()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (gNextGenLocalStorageEnabled == -1) {
|
||||||
|
bool enabled = Preferences::GetBool("dom.storage.next_gen", false);
|
||||||
|
gNextGenLocalStorageEnabled = enabled ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !!gNextGenLocalStorageEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
CachedNextGenLocalStorageEnabled()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(gNextGenLocalStorageEnabled != -1);
|
||||||
|
|
||||||
|
return !!gNextGenLocalStorageEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,247 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_LocalStorageCommon_h
|
||||||
|
#define mozilla_dom_localstorage_LocalStorageCommon_h
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Local storage
|
||||||
|
* ~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* Implementation overview
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* The implementation is based on a per principal/origin cache (datastore)
|
||||||
|
* living in the main process and synchronous calls initiated from content
|
||||||
|
* processes.
|
||||||
|
* The IPC communication is managed by database actors which link to the
|
||||||
|
* datastore.
|
||||||
|
* The synchronous blocking of the main thread is done by using a special
|
||||||
|
* technique or by using standard synchronous IPC calls.
|
||||||
|
*
|
||||||
|
* General architecture
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~
|
||||||
|
* The current browser architecture consists of one main process and multiple
|
||||||
|
* content processes (there are other processes but for simplicity's sake, they
|
||||||
|
* are not mentioned here). The processes use the IPC communication to talk to
|
||||||
|
* each other. Local storage implementation uses the client-server model, so
|
||||||
|
* the main process manages all the data and content processes then request
|
||||||
|
* particular data from the main process. The main process is also called the
|
||||||
|
* parent or the parent side, the content process is then called the child or
|
||||||
|
* the child side.
|
||||||
|
*
|
||||||
|
* Datastores
|
||||||
|
* ~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* A datastore provides a convenient way to access data for given origin. The
|
||||||
|
* data is always preloaded into memory and indexed using a hash table. This
|
||||||
|
* enables very fast access to particular stored items. There can be only one
|
||||||
|
* datastore per origin and exists solely on the parent side. It is represented
|
||||||
|
* by the "Datastore" class. A datastore instance is a ref counted object and
|
||||||
|
* lives on the PBackground thread, it is kept alive by database objects. When
|
||||||
|
* the last database object for given origin is destroyed, the associated
|
||||||
|
* datastore object is destroyed too.
|
||||||
|
*
|
||||||
|
* Databases
|
||||||
|
* ~~~~~~~~~
|
||||||
|
*
|
||||||
|
* A database allows direct access to a datastore from a content process. There
|
||||||
|
* can be multiple databases for the same origin, but they all share the same
|
||||||
|
* datastore.
|
||||||
|
* Databases use the PBackgroundLSDatabase IPDL protocol for IPC communication.
|
||||||
|
* Given the nature of local storage, most of PBackgroundLSDatabase messages
|
||||||
|
* are synchronous.
|
||||||
|
*
|
||||||
|
* On the parent side, the database is represented by the "Database" class that
|
||||||
|
* is a parent actor as well (implements the "PBackgroundLSDatabaseParent"
|
||||||
|
* interface). A database instance is a ref counted object and lives on the
|
||||||
|
* PBackground thread.
|
||||||
|
* All live database actors are tracked in an array.
|
||||||
|
*
|
||||||
|
* On the child side, the database is represented by the "LSDatabase" class
|
||||||
|
* that provides indirect access to a child actor. An LSDatabase instance is a
|
||||||
|
* ref counted object and lives on the main thread.
|
||||||
|
* The actual child actor is represented by the "LSDatabaseChild" class that
|
||||||
|
* implements the "PBackgroundLSDatabaseChild" interface. An "LSDatabaseChild"
|
||||||
|
* instance is not ref counted and lives on the main thread too.
|
||||||
|
*
|
||||||
|
* Synchronous blocking
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* Local storage is synchronous in nature which means the execution can't move
|
||||||
|
* forward until there's a reply for given method call.
|
||||||
|
* Since we have to use IPC anyway, we could just always use synchronous IPC
|
||||||
|
* messages for all local storage method calls. Well, there's a problem with
|
||||||
|
* that approach.
|
||||||
|
* If the main process needs to do some off PBackground thread stuff like
|
||||||
|
* getting info from principals on the main thread or some asynchronous stuff
|
||||||
|
* like directory locking before sending a reply to a synchronous message, then
|
||||||
|
* we would have to block the thread or spin the event loop which is usually a
|
||||||
|
* bad idea, especially in the main process.
|
||||||
|
* Instead, we can use a special thread in the content process called DOM File
|
||||||
|
* thread for communication with the main process using asynchronous messages
|
||||||
|
* and synchronously block the main thread until the DOM File thread is done
|
||||||
|
* (the main thread blocking is a bit more complicated, see the comment in
|
||||||
|
* RequestHelper::StartAndReturnResponse for more details).
|
||||||
|
* Anyway, the extra hop to the DOM File thread brings another overhead and
|
||||||
|
* latency. The final solution is to use a combination of the special thread
|
||||||
|
* for complex stuff like datastore preparation and synchronous IPC messages
|
||||||
|
* sent directly from the main thread for database access when data is already
|
||||||
|
* loaded from disk into memory.
|
||||||
|
*
|
||||||
|
* Requests
|
||||||
|
* ~~~~~~~~
|
||||||
|
*
|
||||||
|
* Requests are used to handle asynchronous high level datastore operations
|
||||||
|
* which are initiated in a content process and then processed in the parent
|
||||||
|
* process (for example, preparation of a datastore).
|
||||||
|
* Requests use the "PBackgroundLSRequest" IPDL protocol for IPC communication.
|
||||||
|
*
|
||||||
|
* On the parent side, the request is represented by the "LSRequestBase" class
|
||||||
|
* that is a parent actor as well (implements the "PBackgroundLSRequestParent"
|
||||||
|
* interface). It's an abstract class (contains pure virtual functions) so it
|
||||||
|
* can't be used to create instances.
|
||||||
|
* It also inherits from the "DatastoreOperationBase" class which is a generic
|
||||||
|
* base class for all datastore operations. The "DatastoreOperationsBase" class
|
||||||
|
* inherits from the "Runnable" class, so derived class instances are ref
|
||||||
|
* counted, can be dispatched to multiple threads and thus they are used on
|
||||||
|
* multiple threads. However, derived class instances can be created on the
|
||||||
|
* PBackground thread only.
|
||||||
|
*
|
||||||
|
* On the child side, the request is represented by the "RequestHelper" class
|
||||||
|
* that covers all the complexity needed to start a new request, handle
|
||||||
|
* responses and do safe main thread blocking at the same time.
|
||||||
|
* It inherits from the "Runnable" class, so instances are ref counted and
|
||||||
|
* they are internally used on multiple threads (specifically on the main
|
||||||
|
* thread and on the DOM File thread). Anyway, users should create and use
|
||||||
|
* instances of this class only on the main thread (apart from a special case
|
||||||
|
* when we need to cancel the request from an internal chromium IPC thread to
|
||||||
|
* prevent a dead lock involving CPOWs).
|
||||||
|
* The actual child actor is represented by the "LSRequestChild" class that
|
||||||
|
* implements the "PBackgroundLSRequestChild" interface. An "LSRequestChild"
|
||||||
|
* instance is not ref counted and lives on the DOM File thread.
|
||||||
|
* Request responses are passed using the "LSRequestChildCallback" interface.
|
||||||
|
*
|
||||||
|
* Preparation of a datastore
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* The datastore preparation is needed to make sure a datastore is fully loaded
|
||||||
|
* into memory. Every datastore preparation produces a unique id (even if the
|
||||||
|
* datastore for given origin already exists).
|
||||||
|
* On the parent side, the preparation is handled by the "PrepareDatastoreOp"
|
||||||
|
* class which inherits from the "LSRequestBase" class. The preparation process
|
||||||
|
* on the parent side is quite complicated, it happens sequentially on multiple
|
||||||
|
* threads and is managed by a state machine.
|
||||||
|
* On the child side, the preparation is done in the LSObject::EnsureDatabase
|
||||||
|
* method using the "RequestHelper" class. The method starts a new preparation
|
||||||
|
* request and obtains a unique id produced by the parent (or an error code if
|
||||||
|
* the requested failed to complete).
|
||||||
|
*
|
||||||
|
* Linking databases to a datastore
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* A datastore exists only on the parent side, but it can be accessed from the
|
||||||
|
* content via database actors. Database actors are initiated on the child side
|
||||||
|
* and they need to be linked to a datastore on the parent side via an id. The
|
||||||
|
* datastore preparation process gives us the required id.
|
||||||
|
* The linking is initiated on the child side in the LSObject::EnsureDatabase
|
||||||
|
* method by calling SendPBackgroundLSDatabaseConstructor and finished in
|
||||||
|
* RecvPBackgroundLSDatabaseConstructor on the parent side.
|
||||||
|
*
|
||||||
|
* Actor migration
|
||||||
|
* ~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* In theory, the datastore preparation request could return a database actor
|
||||||
|
* directly (instead of returning an id intended for database linking to a
|
||||||
|
* datastore). However, as it was explained above, the preparation must be done
|
||||||
|
* on the DOM File thread and database objects are used on the main thread. The
|
||||||
|
* returned actor would have to be migrated from the DOM File thread to the
|
||||||
|
* main thread and that's something which our IPDL doesn't support yet.
|
||||||
|
*
|
||||||
|
* Exposing local storage
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* The implementation is exposed to the DOM via window.localStorage attribute.
|
||||||
|
* Local storage's sibling, session storage shares the same WebIDL interface
|
||||||
|
* for exposing it to web content, therefore there's an abstract class called
|
||||||
|
* "Storage" that handles some of the common DOM bindings stuff. Local storage
|
||||||
|
* specific functionality is defined in the "LSObject" derived class.
|
||||||
|
* The "LSObject" class is also a starting point for the datastore preparation
|
||||||
|
* and database linking.
|
||||||
|
*
|
||||||
|
* Local storage manager
|
||||||
|
* ~~~~~~~~~~~~~~~~~~~~~
|
||||||
|
*
|
||||||
|
* The local storage manager exposes some of the features that need to be
|
||||||
|
* available only in the chrome code or tests. The manager is represented by
|
||||||
|
* the "LocalStorageManager2" class that implements the "nsIDOMStorageManager"
|
||||||
|
* interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
extern const char16_t* kLocalStorageType;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Convenience data-structure to make it easier to track whether a value has
|
||||||
|
* changed and what its previous value was for notification purposes. Instances
|
||||||
|
* are created on the stack by LSObject and passed to LSDatabase which in turn
|
||||||
|
* passes them onto LSSnapshot for final updating/population. LSObject then
|
||||||
|
* generates an event, if appropriate.
|
||||||
|
*/
|
||||||
|
class MOZ_STACK_CLASS LSNotifyInfo
|
||||||
|
{
|
||||||
|
bool mChanged;
|
||||||
|
nsString mOldValue;
|
||||||
|
|
||||||
|
public:
|
||||||
|
LSNotifyInfo()
|
||||||
|
: mChanged(false)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
bool
|
||||||
|
changed() const
|
||||||
|
{
|
||||||
|
return mChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool&
|
||||||
|
changed()
|
||||||
|
{
|
||||||
|
return mChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
const nsString&
|
||||||
|
oldValue() const
|
||||||
|
{
|
||||||
|
return mOldValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsString&
|
||||||
|
oldValue()
|
||||||
|
{
|
||||||
|
return mOldValue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main-thread-only check of LSNG being enabled, the value is latched once
|
||||||
|
* initialized so changing the preference during runtime has no effect.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
NextGenLocalStorageEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cached any-thread version of NextGenLocalStorageEnabled().
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
CachedNextGenLocalStorageEnabled();
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_LocalStorageCommon_h
|
|
@ -0,0 +1,430 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set 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 "LocalStorageManager2.h"
|
||||||
|
|
||||||
|
#include "LSObject.h"
|
||||||
|
#include "mozilla/dom/Promise.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
class RequestResolver final
|
||||||
|
: public LSRequestChildCallback
|
||||||
|
{
|
||||||
|
RefPtr<Promise> mPromise;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit RequestResolver(Promise* aPromise)
|
||||||
|
: mPromise(aPromise)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
NS_INLINE_DECL_REFCOUNTING(mozilla::dom::RequestResolver, override);
|
||||||
|
|
||||||
|
private:
|
||||||
|
~RequestResolver() = default;
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleResponse(nsresult aResponse);
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleResponse(const NullableDatastoreId& aDatastoreId);
|
||||||
|
|
||||||
|
// LSRequestChildCallback
|
||||||
|
void
|
||||||
|
OnResponse(const LSRequestResponse& aResponse) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimpleRequestResolver final
|
||||||
|
: public LSSimpleRequestChildCallback
|
||||||
|
{
|
||||||
|
RefPtr<Promise> mPromise;
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit SimpleRequestResolver(Promise* aPromise)
|
||||||
|
: mPromise(aPromise)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
NS_INLINE_DECL_REFCOUNTING(SimpleRequestResolver, override);
|
||||||
|
|
||||||
|
private:
|
||||||
|
~SimpleRequestResolver() = default;
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleResponse(nsresult aResponse);
|
||||||
|
|
||||||
|
void
|
||||||
|
HandleResponse(bool aResponse);
|
||||||
|
|
||||||
|
// LSRequestChildCallback
|
||||||
|
void
|
||||||
|
OnResponse(const LSSimpleRequestResponse& aResponse) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
CreatePromise(JSContext* aContext, Promise** aPromise)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aContext);
|
||||||
|
|
||||||
|
nsIGlobalObject* global =
|
||||||
|
xpc::NativeGlobal(JS::CurrentGlobalOrNull(aContext));
|
||||||
|
if (NS_WARN_IF(!global)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ErrorResult result;
|
||||||
|
RefPtr<Promise> promise = Promise::Create(global, result);
|
||||||
|
if (result.Failed()) {
|
||||||
|
return result.StealNSResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.forget(aPromise);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
|
||||||
|
PrincipalInfo& aPrincipalInfo)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
|
||||||
|
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &aPrincipalInfo);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
|
||||||
|
aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
|
||||||
|
return NS_ERROR_UNEXPECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
LocalStorageManager2::LocalStorageManager2()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(NextGenLocalStorageEnabled());
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalStorageManager2::~LocalStorageManager2()
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMPL_ISUPPORTS(LocalStorageManager2,
|
||||||
|
nsIDOMStorageManager,
|
||||||
|
nsILocalStorageManager)
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::PrecacheStorage(nsIPrincipal* aPrincipal,
|
||||||
|
Storage** _retval)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
MOZ_ASSERT(_retval);
|
||||||
|
|
||||||
|
// This method was created as part of the e10s-ification of the old LS
|
||||||
|
// implementation to perform a preload in the content/current process. That's
|
||||||
|
// not how things work in LSNG. Instead everything happens in the parent
|
||||||
|
// process, triggered by the official preloading spot,
|
||||||
|
// ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild.
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::CreateStorage(mozIDOMWindow* aWindow,
|
||||||
|
nsIPrincipal* aPrincipal,
|
||||||
|
const nsAString& aDocumentURI,
|
||||||
|
bool aPrivate,
|
||||||
|
Storage** _retval)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
MOZ_ASSERT(_retval);
|
||||||
|
|
||||||
|
nsCOMPtr<nsPIDOMWindowInner> inner = nsPIDOMWindowInner::From(aWindow);
|
||||||
|
|
||||||
|
RefPtr<LSObject> object;
|
||||||
|
nsresult rv = LSObject::CreateForPrincipal(inner,
|
||||||
|
aPrincipal,
|
||||||
|
aDocumentURI,
|
||||||
|
aPrivate,
|
||||||
|
getter_AddRefs(object));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
object.forget(_retval);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::GetStorage(mozIDOMWindow* aWindow,
|
||||||
|
nsIPrincipal* aPrincipal,
|
||||||
|
bool aPrivate,
|
||||||
|
Storage** _retval)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
MOZ_ASSERT(_retval);
|
||||||
|
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::CloneStorage(Storage* aStorageToCloneFrom)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aStorageToCloneFrom);
|
||||||
|
|
||||||
|
// Cloning is specific to sessionStorage; state is forked when a new tab is
|
||||||
|
// opened from an existing tab.
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::CheckStorage(nsIPrincipal* aPrincipal,
|
||||||
|
Storage *aStorage,
|
||||||
|
bool* _retval)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
MOZ_ASSERT(aStorage);
|
||||||
|
MOZ_ASSERT(_retval);
|
||||||
|
|
||||||
|
// Only used by sessionStorage.
|
||||||
|
return NS_ERROR_NOT_IMPLEMENTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::GetNextGenLocalStorageEnabled(bool* aResult)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aResult);
|
||||||
|
|
||||||
|
*aResult = NextGenLocalStorageEnabled();
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::Preload(nsIPrincipal* aPrincipal,
|
||||||
|
JSContext* aContext,
|
||||||
|
nsISupports** _retval)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
MOZ_ASSERT(_retval);
|
||||||
|
|
||||||
|
nsresult rv;
|
||||||
|
|
||||||
|
RefPtr<Promise> promise;
|
||||||
|
|
||||||
|
if (aContext) {
|
||||||
|
rv = CreatePromise(aContext, getter_AddRefs(promise));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LSRequestPrepareDatastoreParams params;
|
||||||
|
params.createIfNotExists() = false;
|
||||||
|
|
||||||
|
rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||||
|
params.principalInfo());
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = StartRequest(promise, params);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.forget(_retval);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_IMETHODIMP
|
||||||
|
LocalStorageManager2::IsPreloaded(nsIPrincipal* aPrincipal,
|
||||||
|
JSContext* aContext,
|
||||||
|
nsISupports** _retval)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPrincipal);
|
||||||
|
MOZ_ASSERT(_retval);
|
||||||
|
|
||||||
|
RefPtr<Promise> promise;
|
||||||
|
nsresult rv = CreatePromise(aContext, getter_AddRefs(promise));
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
LSSimpleRequestPreloadedParams params;
|
||||||
|
|
||||||
|
rv = CheckedPrincipalToPrincipalInfo(aPrincipal,
|
||||||
|
params.principalInfo());
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = StartSimpleRequest(promise, params);
|
||||||
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
|
promise.forget(_retval);
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LocalStorageManager2::StartRequest(Promise* aPromise,
|
||||||
|
const LSRequestParams& aParams)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
PBackgroundChild* backgroundActor =
|
||||||
|
BackgroundChild::GetOrCreateForCurrentThread();
|
||||||
|
if (NS_WARN_IF(!backgroundActor)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<RequestResolver> resolver = new RequestResolver(aPromise);
|
||||||
|
|
||||||
|
auto actor = new LSRequestChild(resolver);
|
||||||
|
|
||||||
|
if (!backgroundActor->SendPBackgroundLSRequestConstructor(actor, aParams)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
nsresult
|
||||||
|
LocalStorageManager2::StartSimpleRequest(Promise* aPromise,
|
||||||
|
const LSSimpleRequestParams& aParams)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(aPromise);
|
||||||
|
|
||||||
|
PBackgroundChild* backgroundActor =
|
||||||
|
BackgroundChild::GetOrCreateForCurrentThread();
|
||||||
|
if (NS_WARN_IF(!backgroundActor)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<SimpleRequestResolver> resolver = new SimpleRequestResolver(aPromise);
|
||||||
|
|
||||||
|
auto actor = new LSSimpleRequestChild(resolver);
|
||||||
|
|
||||||
|
if (!backgroundActor->SendPBackgroundLSSimpleRequestConstructor(actor,
|
||||||
|
aParams)) {
|
||||||
|
return NS_ERROR_FAILURE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NS_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RequestResolver::HandleResponse(nsresult aResponse)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (!mPromise) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mPromise->MaybeReject(aResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RequestResolver::HandleResponse(const NullableDatastoreId& aDatastoreId)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
if (!mPromise) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (aDatastoreId.type()) {
|
||||||
|
case NullableDatastoreId::Tnull_t:
|
||||||
|
mPromise->MaybeResolve(JS::NullHandleValue);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NullableDatastoreId::Tuint64_t:
|
||||||
|
mPromise->MaybeResolve(aDatastoreId.get_uint64_t());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown datastore id type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
RequestResolver::OnResponse(const LSRequestResponse& aResponse)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
switch (aResponse.type()) {
|
||||||
|
case LSRequestResponse::Tnsresult:
|
||||||
|
HandleResponse(aResponse.get_nsresult());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSRequestResponse::TLSRequestPrepareDatastoreResponse:
|
||||||
|
HandleResponse(
|
||||||
|
aResponse.get_LSRequestPrepareDatastoreResponse().datastoreId());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown response type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleRequestResolver::HandleResponse(nsresult aResponse)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(mPromise);
|
||||||
|
|
||||||
|
mPromise->MaybeReject(aResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleRequestResolver::HandleResponse(bool aResponse)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
MOZ_ASSERT(mPromise);
|
||||||
|
|
||||||
|
mPromise->MaybeResolve(aResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
SimpleRequestResolver::OnResponse(const LSSimpleRequestResponse& aResponse)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(NS_IsMainThread());
|
||||||
|
|
||||||
|
switch (aResponse.type()) {
|
||||||
|
case LSSimpleRequestResponse::Tnsresult:
|
||||||
|
HandleResponse(aResponse.get_nsresult());
|
||||||
|
break;
|
||||||
|
|
||||||
|
case LSSimpleRequestResponse::TLSSimpleRequestPreloadedResponse:
|
||||||
|
HandleResponse(
|
||||||
|
aResponse.get_LSSimpleRequestPreloadedResponse().preloaded());
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown response type!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,69 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_LocalStorageManager2_h
|
||||||
|
#define mozilla_dom_localstorage_LocalStorageManager2_h
|
||||||
|
|
||||||
|
#include "nsIDOMStorageManager.h"
|
||||||
|
#include "nsILocalStorageManager.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
class LSRequestParams;
|
||||||
|
class LSSimpleRequestParams;
|
||||||
|
class Promise;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Under LSNG this exposes nsILocalStorageManager::Preload to ContentParent to
|
||||||
|
* trigger preloading. Otherwise, this is basically just a place for test logic
|
||||||
|
* that doesn't make sense to put directly on the Storage WebIDL interface.
|
||||||
|
*
|
||||||
|
* Previously, the nsIDOMStorageManager XPCOM interface was also used by
|
||||||
|
* nsGlobalWindowInner to interact with LocalStorage, but in these de-XPCOM
|
||||||
|
* days, we've moved to just directly reference the relevant concrete classes
|
||||||
|
* (ex: LSObject) directly.
|
||||||
|
*
|
||||||
|
* Note that testing methods are now also directly exposed on the Storage WebIDL
|
||||||
|
* interface for simplicity/sanity.
|
||||||
|
*/
|
||||||
|
class LocalStorageManager2 final
|
||||||
|
: public nsIDOMStorageManager
|
||||||
|
, public nsILocalStorageManager
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
LocalStorageManager2();
|
||||||
|
|
||||||
|
NS_DECL_ISUPPORTS
|
||||||
|
NS_DECL_NSIDOMSTORAGEMANAGER
|
||||||
|
NS_DECL_NSILOCALSTORAGEMANAGER
|
||||||
|
|
||||||
|
private:
|
||||||
|
~LocalStorageManager2();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to trigger an LSRequest and resolve/reject the provided promise when
|
||||||
|
* the result comes in. This routine is notable because the LSRequest
|
||||||
|
* mechanism is normally used synchronously from content, but here it's
|
||||||
|
* exposed asynchronously.
|
||||||
|
*/
|
||||||
|
nsresult
|
||||||
|
StartRequest(Promise* aPromise,
|
||||||
|
const LSRequestParams& aParams);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper to trigger an LSSimpleRequst and resolve/reject the provided promise
|
||||||
|
* when the result comes in.
|
||||||
|
*/
|
||||||
|
nsresult
|
||||||
|
StartSimpleRequest(Promise* aPromise,
|
||||||
|
const LSSimpleRequestParams& aParams);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_LocalStorageManager2_h
|
|
@ -0,0 +1,148 @@
|
||||||
|
/* 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 protocol PBackground;
|
||||||
|
include protocol PBackgroundLSSnapshot;
|
||||||
|
|
||||||
|
include "mozilla/dom/localstorage/SerializationHelpers.h";
|
||||||
|
|
||||||
|
using mozilla::dom::LSSnapshot::LoadState
|
||||||
|
from "mozilla/dom/LSSnapshot.h";
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* LocalStorage key/value pair wire representations. `value` may be void in
|
||||||
|
* cases where there is a value but it is not being sent for memory/bandwidth
|
||||||
|
* conservation purposes. (It's not possible to have a null/undefined `value`
|
||||||
|
* as Storage is defined explicitly as a String store.)
|
||||||
|
*/
|
||||||
|
struct LSItemInfo
|
||||||
|
{
|
||||||
|
nsString key;
|
||||||
|
nsString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Initial LSSnapshot state as produced by Datastore::GetSnapshotInitInfo. See
|
||||||
|
* `LSSnapshot::LoadState` for more details about the possible states and a
|
||||||
|
* high level overview.
|
||||||
|
*/
|
||||||
|
struct LSSnapshotInitInfo
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* As many key/value or key/void pairs as the snapshot prefill byte budget
|
||||||
|
* allowed.
|
||||||
|
*/
|
||||||
|
LSItemInfo[] itemInfos;
|
||||||
|
/**
|
||||||
|
* The total number of key/value pairs in LocalStorage for this origin at the
|
||||||
|
* time the snapshot was created. (And the point of the snapshot is to
|
||||||
|
* conceptually freeze the state of the Datastore in time, so this value does
|
||||||
|
* not change despite what other LSDatabase objects get up to in other
|
||||||
|
* processes.)
|
||||||
|
*/
|
||||||
|
uint32_t totalLength;
|
||||||
|
/**
|
||||||
|
* The current amount of LocalStorage usage as measured by the summing the
|
||||||
|
* nsString Length() of both the key and the value over all stored pairs.
|
||||||
|
*/
|
||||||
|
int64_t initialUsage;
|
||||||
|
/**
|
||||||
|
* The amount of storage allowed to be used by the Snapshot without requesting
|
||||||
|
* more storage space via IncreasePeakUsage. This is the `initialUsage` plus
|
||||||
|
* 0 or more bytes of space. If space was available, the increase will be the
|
||||||
|
* `requestedSize` from the PBackgroundLSSnapshot constructor. If the
|
||||||
|
* LocalStorage usage was already close to the limit, then the fallback is the
|
||||||
|
* `minSize` requested, or 0 if there wasn't space for that.
|
||||||
|
*/
|
||||||
|
int64_t peakUsage;
|
||||||
|
// See `LSSnapshot::LoadState` in `LSSnapshot.h`
|
||||||
|
LoadState loadState;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This protocol is asynchronously created via constructor on PBackground but
|
||||||
|
* has synchronous semantics from the perspective of content on the main thread.
|
||||||
|
* The construction potentially involves waiting for disk I/O to load the
|
||||||
|
* LocalStorage data from disk as well as related QuotaManager checks, so async
|
||||||
|
* calls to PBackground are the only viable mechanism because blocking
|
||||||
|
* PBackground is not acceptable. (Note that an attempt is made to minimize any
|
||||||
|
* I/O latency by triggering preloading from
|
||||||
|
* ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild, the central place
|
||||||
|
* for pre-loading.)
|
||||||
|
*/
|
||||||
|
sync protocol PBackgroundLSDatabase
|
||||||
|
{
|
||||||
|
manager PBackground;
|
||||||
|
manages PBackgroundLSSnapshot;
|
||||||
|
|
||||||
|
parent:
|
||||||
|
// The DeleteMe message is used to avoid a race condition between the parent
|
||||||
|
// actor and the child actor. The PBackgroundLSDatabase protocol could be
|
||||||
|
// simply destroyed by sending the __delete__ message from the child side.
|
||||||
|
// However, that would destroy the child actor immediatelly and the parent
|
||||||
|
// could be sending a message to the child at the same time resulting in a
|
||||||
|
// routing error since the child actor wouldn't exist anymore. A routing
|
||||||
|
// error typically causes a crash. The race can be prevented by doing the
|
||||||
|
// teardown in two steps. First, we send the DeleteMe message to the parent
|
||||||
|
// and the parent then sends the __delete__ message to the child.
|
||||||
|
async DeleteMe();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent in response to a `RequestAllowToClose` message once the snapshot
|
||||||
|
* cleanup has happened OR from LSDatabase's destructor if AllowToClose has
|
||||||
|
* not already been reported.
|
||||||
|
*/
|
||||||
|
async AllowToClose();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked to create an LSSnapshot backed by a Snapshot in PBackground that
|
||||||
|
* presents an atomic and consistent view of the state of the authoritative
|
||||||
|
* Datastore state in the parent.
|
||||||
|
*
|
||||||
|
* This needs to be synchronous because LocalStorage's semantics are
|
||||||
|
* synchronous. Note that the Datastore in the PBackground parent already
|
||||||
|
* has the answers to this request immediately available without needing to
|
||||||
|
* consult any other threads or perform any I/O. Additionally, the response
|
||||||
|
* is explicitly bounded in size by the tunable snapshot prefill byte limit.
|
||||||
|
*
|
||||||
|
* @param increasePeakUsage
|
||||||
|
* Whether the parent should attempt to pre-allocate some amount of quota
|
||||||
|
* usage to the Snapshot.
|
||||||
|
*/
|
||||||
|
sync PBackgroundLSSnapshot(nsString documentURI,
|
||||||
|
bool increasePeakUsage,
|
||||||
|
int64_t requestedSize,
|
||||||
|
int64_t minSize)
|
||||||
|
returns (LSSnapshotInitInfo initInfo);
|
||||||
|
|
||||||
|
child:
|
||||||
|
/**
|
||||||
|
* Only sent by the parent in response to the child's DeleteMe request.
|
||||||
|
*/
|
||||||
|
async __delete__();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Request to close the LSDatabase, checkpointing and finishing any
|
||||||
|
* outstanding snapshots so no state is lost. This request is issued when
|
||||||
|
* QuotaManager is shutting down or is aborting operations for an origin or
|
||||||
|
* process. Once the snapshot has cleaned up, AllowToClose will be sent to
|
||||||
|
* the parent.
|
||||||
|
*
|
||||||
|
* Note that the QuotaManager shutdown process is more likely to happen in
|
||||||
|
* unit tests where we explicitly reset the QuotaManager. At runtime, we
|
||||||
|
* expect windows to be closed and content processes terminated well before
|
||||||
|
* QuotaManager shutdown would actually occur.
|
||||||
|
*
|
||||||
|
* Also, Operations are usually aborted for an origin due to privacy API's
|
||||||
|
* clearing data for an origin. Operations are aborted for a process by
|
||||||
|
* ContentParent::ShutDownProcess.
|
||||||
|
*/
|
||||||
|
async RequestAllowToClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,57 @@
|
||||||
|
/* 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 protocol PBackground;
|
||||||
|
|
||||||
|
include PBackgroundSharedTypes;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The observer protocol sends "storage" event notifications for changes to
|
||||||
|
* LocalStorage that take place in other processes as their Snapshots are
|
||||||
|
* Checkpointed to the canonical Datastore in the parent process. Same-process
|
||||||
|
* notifications are generated as mutations happen.
|
||||||
|
*
|
||||||
|
* Note that mutations are never generated for redundant mutations. Setting the
|
||||||
|
* key "foo" to have value "bar" when it already has value "bar" will never
|
||||||
|
* result in a "storage" event.
|
||||||
|
*/
|
||||||
|
async protocol PBackgroundLSObserver
|
||||||
|
{
|
||||||
|
manager PBackground;
|
||||||
|
|
||||||
|
parent:
|
||||||
|
/**
|
||||||
|
* Sent by the LSObserver's destructor when it's going away. Any Observe
|
||||||
|
* messages received after this is sent will be ignored. Which is fine,
|
||||||
|
* because there should be nothing around left to hear. In the event a new
|
||||||
|
* page came into existence, its Observer creation will happen (effectively)
|
||||||
|
* synchronously.
|
||||||
|
*/
|
||||||
|
async DeleteMe();
|
||||||
|
|
||||||
|
child:
|
||||||
|
/**
|
||||||
|
* Only sent by the parent in response to a deletion request.
|
||||||
|
*/
|
||||||
|
async __delete__();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent by the parent process as Snapshots from other processes are
|
||||||
|
* Checkpointed, applying their mutations. The child actor currently directly
|
||||||
|
* shunts these to Storage::NotifyChange to generate "storage" events for
|
||||||
|
* immediate dispatch.
|
||||||
|
*/
|
||||||
|
async Observe(PrincipalInfo principalInfo,
|
||||||
|
uint32_t privateBrowsingId,
|
||||||
|
nsString documentURI,
|
||||||
|
nsString key,
|
||||||
|
nsString oldValue,
|
||||||
|
nsString newValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,103 @@
|
||||||
|
/* 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 protocol PBackground;
|
||||||
|
|
||||||
|
using struct mozilla::null_t
|
||||||
|
from "ipc/IPCMessageUtils.h";
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
union NullableDatastoreId
|
||||||
|
{
|
||||||
|
null_t;
|
||||||
|
uint64_t;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSRequestPrepareDatastoreResponse
|
||||||
|
{
|
||||||
|
NullableDatastoreId datastoreId;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSRequestPrepareObserverResponse
|
||||||
|
{
|
||||||
|
uint64_t observerId;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discriminated union which can contain an error code (`nsresult`) or
|
||||||
|
* particular request response.
|
||||||
|
*/
|
||||||
|
union LSRequestResponse
|
||||||
|
{
|
||||||
|
nsresult;
|
||||||
|
LSRequestPrepareDatastoreResponse;
|
||||||
|
LSRequestPrepareObserverResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An asynchronous protocol for issuing requests that are used in a synchronous
|
||||||
|
* fashion by LocalStorage via LSObject's RequestHelper mechanism. This differs
|
||||||
|
* from LSSimpleRequest which is implemented and used asynchronously.
|
||||||
|
*
|
||||||
|
* See `PBackgroundLSSharedTypes.ipdlh` for more on the request types, the
|
||||||
|
* response types above for their corresponding responses, and `RequestHelper`
|
||||||
|
* for more on the usage and lifecycle of this mechanism.
|
||||||
|
*/
|
||||||
|
protocol PBackgroundLSRequest
|
||||||
|
{
|
||||||
|
manager PBackground;
|
||||||
|
|
||||||
|
parent:
|
||||||
|
// The Cancel message is used to avoid a possible dead lock caused by a CPOW
|
||||||
|
// sending a synchronous message from the main thread in the chrome process
|
||||||
|
// to the main thread in the content process at the time we are blocking
|
||||||
|
// the main thread in the content process to handle a request.
|
||||||
|
// We use the PBackground thread on the parent side to handle requests, but
|
||||||
|
// sometimes we need to get information from principals and that's currently
|
||||||
|
// only possible on the main thread. So if the main thread in the chrome
|
||||||
|
// process is blocked by a CPOW operation, our request must wait for the CPOW
|
||||||
|
// operation to complete. However the CPOW operation can't complete either
|
||||||
|
// because we are blocking the main thread in the content process.
|
||||||
|
// The dead lock is prevented by canceling our nested event loop in the
|
||||||
|
// content process when we receive a synchronous IPC message from the parent.
|
||||||
|
//
|
||||||
|
// Note that cancellation isn't instantaneous. It's just an asynchronous flow
|
||||||
|
// that definitely doesn't involve the main thread in the parent process, so
|
||||||
|
// we're guaranteed to unblock the main-thread in the content process and
|
||||||
|
// allow the sync IPC to make progress. When Cancel() is received by the
|
||||||
|
// parent, it will Send__delete__. The child will either send Cancel or
|
||||||
|
// Finish, but not both.
|
||||||
|
async Cancel();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent by the child in response to Ready, requesting that __delete__ be sent
|
||||||
|
* with the result. The child will either send Finish or Cancel, but not
|
||||||
|
* both. No further message will be sent from the child after invoking one.
|
||||||
|
*/
|
||||||
|
async Finish();
|
||||||
|
|
||||||
|
child:
|
||||||
|
/**
|
||||||
|
* The deletion is sent with the result of the request directly in response to
|
||||||
|
* either Cancel or Finish.
|
||||||
|
*/
|
||||||
|
async __delete__(LSRequestResponse response);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sent by the parent when it has completed whatever async stuff it needs to
|
||||||
|
* do and is ready to send the results. It then awaits the Finish() call to
|
||||||
|
* send the results. This may seem redundant, but it's not. If the
|
||||||
|
* __delete__ was sent directly, it's possible there could be a race where
|
||||||
|
* Cancel() would be received by the parent after it had already sent
|
||||||
|
* __delete__. (Which may no longer be fatal thanks to improvements to the
|
||||||
|
* IPC layer, but it would still lead to warnings, etc. And we don't
|
||||||
|
* expect PBackground to be highly contended nor the DOM File thread.)
|
||||||
|
*/
|
||||||
|
async Ready();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,38 @@
|
||||||
|
/* 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 PBackgroundSharedTypes;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
struct LSRequestPrepareDatastoreParams
|
||||||
|
{
|
||||||
|
PrincipalInfo principalInfo;
|
||||||
|
bool createIfNotExists;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSRequestPrepareObserverParams
|
||||||
|
{
|
||||||
|
PrincipalInfo principalInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
union LSRequestParams
|
||||||
|
{
|
||||||
|
LSRequestPrepareDatastoreParams;
|
||||||
|
LSRequestPrepareObserverParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSSimpleRequestPreloadedParams
|
||||||
|
{
|
||||||
|
PrincipalInfo principalInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
union LSSimpleRequestParams
|
||||||
|
{
|
||||||
|
LSSimpleRequestPreloadedParams;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,50 @@
|
||||||
|
/* 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 protocol PBackground;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Response to a `LSSimpleRequestPreloadedParams` request indicating whether the
|
||||||
|
* origin was preloaded.
|
||||||
|
*/
|
||||||
|
struct LSSimpleRequestPreloadedResponse
|
||||||
|
{
|
||||||
|
bool preloaded;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Discriminated union which can contain an error code (`nsresult`) or
|
||||||
|
* particular simple request response.
|
||||||
|
*/
|
||||||
|
union LSSimpleRequestResponse
|
||||||
|
{
|
||||||
|
nsresult;
|
||||||
|
LSSimpleRequestPreloadedResponse;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Simple requests are async-only from both a protocol perspective and the
|
||||||
|
* manner in which they're used. In comparison, PBackgroundLSRequests are
|
||||||
|
* async only from a protocol perspective; they are used synchronously from the
|
||||||
|
* main thread via LSObject's RequestHelper mechanism. (With the caveat that
|
||||||
|
* nsILocalStorageManager does expose LSRequests asynchronously.)
|
||||||
|
*
|
||||||
|
* These requests use the common idiom where the arguments to the request are
|
||||||
|
* sent in the constructor and the result is sent in the __delete__ response.
|
||||||
|
* Request types are indicated by the Params variant used and those live in
|
||||||
|
* `PBackgroundLSSharedTypes.ipdlh`.
|
||||||
|
*/
|
||||||
|
protocol PBackgroundLSSimpleRequest
|
||||||
|
{
|
||||||
|
manager PBackground;
|
||||||
|
|
||||||
|
child:
|
||||||
|
async __delete__(LSSimpleRequestResponse response);
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,114 @@
|
||||||
|
/* 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 protocol PBackground;
|
||||||
|
include protocol PBackgroundLSDatabase;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
|
||||||
|
struct LSSetItemInfo
|
||||||
|
{
|
||||||
|
nsString key;
|
||||||
|
nsString oldValue;
|
||||||
|
nsString value;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSRemoveItemInfo
|
||||||
|
{
|
||||||
|
nsString key;
|
||||||
|
nsString oldValue;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct LSClearInfo
|
||||||
|
{
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Union of LocalStorage mutation types.
|
||||||
|
*/
|
||||||
|
union LSWriteInfo
|
||||||
|
{
|
||||||
|
LSSetItemInfo;
|
||||||
|
LSRemoveItemInfo;
|
||||||
|
LSClearInfo;
|
||||||
|
};
|
||||||
|
|
||||||
|
sync protocol PBackgroundLSSnapshot
|
||||||
|
{
|
||||||
|
manager PBackgroundLSDatabase;
|
||||||
|
|
||||||
|
parent:
|
||||||
|
async DeleteMe();
|
||||||
|
|
||||||
|
async Checkpoint(LSWriteInfo[] writeInfos);
|
||||||
|
|
||||||
|
async Finish();
|
||||||
|
|
||||||
|
async Loaded();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on demand to load an item that didn't fit into the initial
|
||||||
|
* snapshot prefill.
|
||||||
|
*
|
||||||
|
* This needs to be synchronous because LocalStorage's semantics are
|
||||||
|
* synchronous. Note that the Snapshot in the PBackground parent already
|
||||||
|
* has the answers to this request immediately available without needing to
|
||||||
|
* consult any other threads or perform any I/O.
|
||||||
|
*/
|
||||||
|
sync LoadItem(nsString key)
|
||||||
|
returns (nsString value);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked on demand to load all keys in in their canonical order if they
|
||||||
|
* didn't fit into the initial snapshot prefill.
|
||||||
|
*
|
||||||
|
* This needs to be synchronous because LocalStorage's semantics are
|
||||||
|
* synchronous. Note that the Snapshot in the PBackground parent already
|
||||||
|
* has the answers to this request immediately available without needing to
|
||||||
|
* consult any other threads or perform any I/O.
|
||||||
|
*/
|
||||||
|
sync LoadKeys()
|
||||||
|
returns (nsString[] keys);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This needs to be synchronous because LocalStorage's semantics are
|
||||||
|
* synchronous. Note that the Snapshot in the PBackground parent typically
|
||||||
|
* doesn't need to consult any other threads or perform any I/O to handle
|
||||||
|
* this request. However, it has to call a quota manager method that can
|
||||||
|
* potentially do I/O directly on the PBackground thread. It can only happen
|
||||||
|
* rarely in a storage pressure (low storage space) situation. Specifically,
|
||||||
|
* after we get a list of origin directories for eviction, we will delete
|
||||||
|
* them directly on the PBackground thread. This doesn't cause any
|
||||||
|
* performance problems, but avoiding I/O completely might need to be done as
|
||||||
|
* a futher optimization.
|
||||||
|
*/
|
||||||
|
sync IncreasePeakUsage(int64_t requestedSize, int64_t minSize)
|
||||||
|
returns (int64_t size);
|
||||||
|
|
||||||
|
// A synchronous ping to the parent actor to confirm that the parent actor
|
||||||
|
// has received previous async message. This should only be used by the
|
||||||
|
// snapshotting code to end an explicit snapshot.
|
||||||
|
sync Ping();
|
||||||
|
|
||||||
|
child:
|
||||||
|
/**
|
||||||
|
* Compels the child LSSnapshot to Checkpoint() and Finish(), effectively
|
||||||
|
* compelling the snapshot to flush any issued mutations and close itself.
|
||||||
|
* The child LSSnapshot does that either immediately if it's just waiting
|
||||||
|
* to be reused or when it gets into a stable state.
|
||||||
|
*
|
||||||
|
* This message is expected to be sent in the following two cases only:
|
||||||
|
* 1. The state of the underlying Datastore starts to differ from the state
|
||||||
|
* captured at the time of snapshot creation.
|
||||||
|
* 2. The last private browsing context exits. And in that case we expect
|
||||||
|
* all private browsing globals to already have been destroyed.
|
||||||
|
*/
|
||||||
|
async MarkDirty();
|
||||||
|
|
||||||
|
async __delete__();
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,36 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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 "ReportInternalError.h"
|
||||||
|
|
||||||
|
#include "mozilla/IntegerPrintfMacros.h"
|
||||||
|
|
||||||
|
#include "nsContentUtils.h"
|
||||||
|
#include "nsPrintfCString.h"
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
namespace localstorage {
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr)
|
||||||
|
{
|
||||||
|
// Get leaf of file path
|
||||||
|
for (const char* p = aFile; *p; ++p) {
|
||||||
|
if (*p == '/' && *(p + 1)) {
|
||||||
|
aFile = p + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nsContentUtils::LogSimpleConsoleError(
|
||||||
|
NS_ConvertUTF8toUTF16(nsPrintfCString(
|
||||||
|
"LocalStorage %s: %s:%" PRIu32, aStr, aFile, aLine)),
|
||||||
|
"localstorage", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace localstorage
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
|
@ -0,0 +1,32 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_ReportInternalError_h
|
||||||
|
#define mozilla_dom_localstorage_ReportInternalError_h
|
||||||
|
|
||||||
|
#include "nsDebug.h"
|
||||||
|
|
||||||
|
#define LS_WARNING(...) \
|
||||||
|
do { \
|
||||||
|
nsPrintfCString s(__VA_ARGS__); \
|
||||||
|
mozilla::dom::localstorage::ReportInternalError(__FILE__, \
|
||||||
|
__LINE__, \
|
||||||
|
s.get()); \
|
||||||
|
NS_WARNING(s.get()); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace dom {
|
||||||
|
namespace localstorage {
|
||||||
|
|
||||||
|
void
|
||||||
|
ReportInternalError(const char* aFile, uint32_t aLine, const char* aStr);
|
||||||
|
|
||||||
|
} // namespace localstorage
|
||||||
|
} // namespace dom
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_ReportInternalError_h
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set ts=8 sts=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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_dom_localstorage_SerializationHelpers_h
|
||||||
|
#define mozilla_dom_localstorage_SerializationHelpers_h
|
||||||
|
|
||||||
|
#include "ipc/IPCMessageUtils.h"
|
||||||
|
|
||||||
|
#include "mozilla/dom/LSSnapshot.h"
|
||||||
|
|
||||||
|
namespace IPC {
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct ParamTraits<mozilla::dom::LSSnapshot::LoadState> :
|
||||||
|
public ContiguousEnumSerializer<mozilla::dom::LSSnapshot::LoadState,
|
||||||
|
mozilla::dom::LSSnapshot::LoadState::Initial,
|
||||||
|
mozilla::dom::LSSnapshot::LoadState::EndGuard>
|
||||||
|
{ };
|
||||||
|
|
||||||
|
} // namespace IPC
|
||||||
|
|
||||||
|
#endif // mozilla_dom_localstorage_SerializationHelpers_h
|
|
@ -0,0 +1,64 @@
|
||||||
|
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||||
|
# vim: set filetype=python:
|
||||||
|
# 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/.
|
||||||
|
|
||||||
|
XPCSHELL_TESTS_MANIFESTS += [
|
||||||
|
'test/unit/xpcshell.ini'
|
||||||
|
]
|
||||||
|
|
||||||
|
TEST_HARNESS_FILES.xpcshell.dom.localstorage.test.unit += [
|
||||||
|
'test/unit/databaseShadowing-shared.js',
|
||||||
|
]
|
||||||
|
|
||||||
|
XPIDL_SOURCES += [
|
||||||
|
'nsILocalStorageManager.idl',
|
||||||
|
]
|
||||||
|
|
||||||
|
XPIDL_MODULE = 'dom_localstorage'
|
||||||
|
|
||||||
|
EXPORTS.mozilla.dom.localstorage += [
|
||||||
|
'ActorsParent.h',
|
||||||
|
'SerializationHelpers.h',
|
||||||
|
]
|
||||||
|
|
||||||
|
EXPORTS.mozilla.dom += [
|
||||||
|
'LocalStorageCommon.h',
|
||||||
|
'LocalStorageManager2.h',
|
||||||
|
'LSObject.h',
|
||||||
|
'LSObserver.h',
|
||||||
|
'LSSnapshot.h',
|
||||||
|
]
|
||||||
|
|
||||||
|
UNIFIED_SOURCES += [
|
||||||
|
'ActorsChild.cpp',
|
||||||
|
'ActorsParent.cpp',
|
||||||
|
'LocalStorageCommon.cpp',
|
||||||
|
'LocalStorageManager2.cpp',
|
||||||
|
'LSDatabase.cpp',
|
||||||
|
'LSObject.cpp',
|
||||||
|
'LSObserver.cpp',
|
||||||
|
'LSSnapshot.cpp',
|
||||||
|
'ReportInternalError.cpp',
|
||||||
|
]
|
||||||
|
|
||||||
|
IPDL_SOURCES += [
|
||||||
|
'PBackgroundLSDatabase.ipdl',
|
||||||
|
'PBackgroundLSObserver.ipdl',
|
||||||
|
'PBackgroundLSRequest.ipdl',
|
||||||
|
'PBackgroundLSSharedTypes.ipdlh',
|
||||||
|
'PBackgroundLSSimpleRequest.ipdl',
|
||||||
|
'PBackgroundLSSnapshot.ipdl',
|
||||||
|
]
|
||||||
|
|
||||||
|
include('/ipc/chromium/chromium-config.mozbuild')
|
||||||
|
|
||||||
|
FINAL_LIBRARY = 'xul'
|
||||||
|
|
||||||
|
if CONFIG['GNU_CXX']:
|
||||||
|
CXXFLAGS += ['-Wno-error=shadow']
|
||||||
|
|
||||||
|
LOCAL_INCLUDES += [
|
||||||
|
'/dom/file/ipc',
|
||||||
|
]
|
|
@ -0,0 +1,35 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||||
|
/* vim: set 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 "nsISupports.idl"
|
||||||
|
|
||||||
|
interface nsIPrincipal;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Methods specific to LocalStorage, see nsIDOMStorageManager for methods shared
|
||||||
|
* with SessionStorage. Methods may migrate there as SessionStorage is
|
||||||
|
* overhauled.
|
||||||
|
*/
|
||||||
|
[scriptable, builtinclass, uuid(d4f534da-2744-4db3-8774-8b187c64ade9)]
|
||||||
|
interface nsILocalStorageManager : nsISupports
|
||||||
|
{
|
||||||
|
readonly attribute boolean nextGenLocalStorageEnabled;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trigger preload of LocalStorage for the given principal. For use by
|
||||||
|
* ContentParent::AboutToLoadHttpFtpWyciwygDocumentForChild to maximize the
|
||||||
|
* amount of time we have to load the data off disk before the page might
|
||||||
|
* attempt to touch LocalStorage.
|
||||||
|
*
|
||||||
|
* This method will not create a QuotaManager-managed directory on disk if
|
||||||
|
* one does not already exist for the principal.
|
||||||
|
*/
|
||||||
|
[implicit_jscontext] nsISupports
|
||||||
|
preload(in nsIPrincipal aPrincipal);
|
||||||
|
|
||||||
|
[implicit_jscontext] nsISupports
|
||||||
|
isPreloaded(in nsIPrincipal aPrincipal);
|
||||||
|
};
|
Двоичный файл не отображается.
|
@ -0,0 +1,114 @@
|
||||||
|
const principalInfos = [
|
||||||
|
{ url: "http://example.com", attrs: {} },
|
||||||
|
|
||||||
|
{ url: "http://origin.test", attrs: {} },
|
||||||
|
|
||||||
|
{ url: "http://prefix.test", attrs: {} },
|
||||||
|
{ url: "http://prefix.test", attrs: { userContextId: 10 } },
|
||||||
|
|
||||||
|
{ url: "http://pattern.test", attrs: { userContextId: 15 } },
|
||||||
|
{ url: "http://pattern.test:8080", attrs: { userContextId: 15 } },
|
||||||
|
{ url: "https://pattern.test", attrs: { userContextId: 15 } },
|
||||||
|
];
|
||||||
|
|
||||||
|
function enableNextGenLocalStorage()
|
||||||
|
{
|
||||||
|
info("Setting pref");
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("dom.storage.next_gen", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function disableNextGenLocalStorage()
|
||||||
|
{
|
||||||
|
info("Setting pref");
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("dom.storage.next_gen", false);
|
||||||
|
}
|
||||||
|
|
||||||
|
function storeData()
|
||||||
|
{
|
||||||
|
for (let i = 0; i < principalInfos.length; i++) {
|
||||||
|
let principalInfo = principalInfos[i];
|
||||||
|
let principal = getPrincipal(principalInfo.url, principalInfo.attrs);
|
||||||
|
|
||||||
|
info("Getting storage");
|
||||||
|
|
||||||
|
let storage = getLocalStorage(principal);
|
||||||
|
|
||||||
|
info("Adding data");
|
||||||
|
|
||||||
|
storage.setItem("key0", "value0");
|
||||||
|
storage.clear();
|
||||||
|
storage.setItem("key1", "value1");
|
||||||
|
storage.removeItem("key1");
|
||||||
|
storage.setItem("key2", "value2");
|
||||||
|
|
||||||
|
info("Closing storage");
|
||||||
|
|
||||||
|
storage.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function exportShadowDatabase(name)
|
||||||
|
{
|
||||||
|
info("Verifying shadow database");
|
||||||
|
|
||||||
|
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||||
|
let shadowDatabase = profileDir.clone();
|
||||||
|
shadowDatabase.append("webappsstore.sqlite");
|
||||||
|
|
||||||
|
let exists = shadowDatabase.exists();
|
||||||
|
ok(exists, "Shadow database does exist");
|
||||||
|
|
||||||
|
info("Copying shadow database");
|
||||||
|
|
||||||
|
let currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
|
||||||
|
shadowDatabase.copyTo(currentDir, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
function importShadowDatabase(name)
|
||||||
|
{
|
||||||
|
info("Verifying shadow database");
|
||||||
|
|
||||||
|
let currentDir = Services.dirsvc.get("CurWorkD", Ci.nsIFile);
|
||||||
|
let shadowDatabase = currentDir.clone();
|
||||||
|
shadowDatabase.append(name);
|
||||||
|
|
||||||
|
let exists = shadowDatabase.exists();
|
||||||
|
if (!exists) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Copying shadow database");
|
||||||
|
|
||||||
|
let profileDir = Services.dirsvc.get("ProfD", Ci.nsIFile);
|
||||||
|
shadowDatabase.copyTo(profileDir, "webappsstore.sqlite");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function verifyData(clearedOrigins)
|
||||||
|
{
|
||||||
|
for (let i = 0; i < principalInfos.length; i++) {
|
||||||
|
let principalInfo = principalInfos[i];
|
||||||
|
let principal = getPrincipal(principalInfo.url, principalInfo.attrs);
|
||||||
|
|
||||||
|
info("Getting storage");
|
||||||
|
|
||||||
|
let storage = getLocalStorage(principal);
|
||||||
|
|
||||||
|
info("Verifying data");
|
||||||
|
|
||||||
|
if (clearedOrigins.includes(i)) {
|
||||||
|
ok(storage.getItem("key2") == null, "Correct value");
|
||||||
|
} else {
|
||||||
|
ok(storage.getItem("key0") == null, "Correct value");
|
||||||
|
ok(storage.getItem("key1") == null, "Correct value");
|
||||||
|
ok(storage.getItem("key2") == "value2", "Correct value");
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Closing storage");
|
||||||
|
|
||||||
|
storage.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,271 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
const NS_ERROR_DOM_QUOTA_EXCEEDED_ERR = 22;
|
||||||
|
|
||||||
|
ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
function is(a, b, msg)
|
||||||
|
{
|
||||||
|
Assert.equal(a, b, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ok(cond, msg)
|
||||||
|
{
|
||||||
|
Assert.ok(!!cond, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
function run_test()
|
||||||
|
{
|
||||||
|
runTest();
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!this.runTest) {
|
||||||
|
this.runTest = function()
|
||||||
|
{
|
||||||
|
do_get_profile();
|
||||||
|
|
||||||
|
enableTesting();
|
||||||
|
|
||||||
|
Assert.ok(typeof testSteps === "function",
|
||||||
|
"There should be a testSteps function");
|
||||||
|
Assert.ok(testSteps.constructor.name === "AsyncFunction",
|
||||||
|
"testSteps should be an async function");
|
||||||
|
|
||||||
|
registerCleanupFunction(resetTesting);
|
||||||
|
|
||||||
|
add_task(testSteps);
|
||||||
|
|
||||||
|
// Since we defined run_test, we must invoke run_next_test() to start the
|
||||||
|
// async test.
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function returnToEventLoop()
|
||||||
|
{
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
executeSoon(resolve);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function enableTesting()
|
||||||
|
{
|
||||||
|
Services.prefs.setBoolPref("dom.storage.testing", true);
|
||||||
|
Services.prefs.setBoolPref("dom.quotaManager.testing", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetTesting()
|
||||||
|
{
|
||||||
|
Services.prefs.clearUserPref("dom.quotaManager.testing");
|
||||||
|
Services.prefs.clearUserPref("dom.storage.testing");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setGlobalLimit(globalLimit)
|
||||||
|
{
|
||||||
|
Services.prefs.setIntPref("dom.quotaManager.temporaryStorage.fixedLimit",
|
||||||
|
globalLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetGlobalLimit()
|
||||||
|
{
|
||||||
|
Services.prefs.clearUserPref("dom.quotaManager.temporaryStorage.fixedLimit");
|
||||||
|
}
|
||||||
|
|
||||||
|
function setOriginLimit(originLimit)
|
||||||
|
{
|
||||||
|
Services.prefs.setIntPref("dom.storage.default_quota", originLimit);
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetOriginLimit()
|
||||||
|
{
|
||||||
|
Services.prefs.clearUserPref("dom.storage.default_quota");
|
||||||
|
}
|
||||||
|
|
||||||
|
function getOriginUsage(principal)
|
||||||
|
{
|
||||||
|
let request = Services.qms.getUsageForPrincipal(principal, function() { });
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clear()
|
||||||
|
{
|
||||||
|
let request = Services.qms.clear();
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearOriginsByPattern(pattern)
|
||||||
|
{
|
||||||
|
let request = Services.qms.clearStoragesForOriginAttributesPattern(pattern);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearOriginsByPrefix(principal, persistence)
|
||||||
|
{
|
||||||
|
let request =
|
||||||
|
Services.qms.clearStoragesForPrincipal(principal, persistence, null, true);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearOrigin(principal, persistence)
|
||||||
|
{
|
||||||
|
let request = Services.qms.clearStoragesForPrincipal(principal, persistence);
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function reset(callback)
|
||||||
|
{
|
||||||
|
let request = Services.qms.reset();
|
||||||
|
request.callback = callback;
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function resetOrigin(principal)
|
||||||
|
{
|
||||||
|
let request =
|
||||||
|
Services.qms.resetStoragesForPrincipal(principal, "default", "ls");
|
||||||
|
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
function installPackage(packageName)
|
||||||
|
{
|
||||||
|
let directoryService = Cc["@mozilla.org/file/directory_service;1"]
|
||||||
|
.getService(Ci.nsIProperties);
|
||||||
|
|
||||||
|
let currentDir = directoryService.get("CurWorkD", Ci.nsIFile);
|
||||||
|
|
||||||
|
let packageFile = currentDir.clone();
|
||||||
|
packageFile.append(packageName + ".zip");
|
||||||
|
|
||||||
|
let zipReader = Cc["@mozilla.org/libjar/zip-reader;1"]
|
||||||
|
.createInstance(Ci.nsIZipReader);
|
||||||
|
zipReader.open(packageFile);
|
||||||
|
|
||||||
|
let entryNames = [];
|
||||||
|
let entries = zipReader.findEntries(null);
|
||||||
|
while (entries.hasMore()) {
|
||||||
|
let entry = entries.getNext();
|
||||||
|
entryNames.push(entry);
|
||||||
|
}
|
||||||
|
entryNames.sort();
|
||||||
|
|
||||||
|
for (let entryName of entryNames) {
|
||||||
|
let zipentry = zipReader.getEntry(entryName);
|
||||||
|
|
||||||
|
let file = getRelativeFile(entryName);
|
||||||
|
|
||||||
|
if (zipentry.isDirectory) {
|
||||||
|
file.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
|
||||||
|
} else {
|
||||||
|
let istream = zipReader.getInputStream(entryName);
|
||||||
|
|
||||||
|
var ostream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||||
|
.createInstance(Ci.nsIFileOutputStream);
|
||||||
|
ostream.init(file, -1, parseInt("0644", 8), 0);
|
||||||
|
|
||||||
|
let bostream = Cc['@mozilla.org/network/buffered-output-stream;1']
|
||||||
|
.createInstance(Ci.nsIBufferedOutputStream);
|
||||||
|
bostream.init(ostream, 32768);
|
||||||
|
|
||||||
|
bostream.writeFrom(istream, istream.available());
|
||||||
|
|
||||||
|
istream.close();
|
||||||
|
bostream.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zipReader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProfileDir()
|
||||||
|
{
|
||||||
|
let directoryService =
|
||||||
|
Cc["@mozilla.org/file/directory_service;1"].getService(Ci.nsIProperties);
|
||||||
|
|
||||||
|
return directoryService.get("ProfD", Ci.nsIFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a "/"-delimited path relative to the profile directory,
|
||||||
|
// return an nsIFile representing the path. This does not test
|
||||||
|
// for the existence of the file or parent directories.
|
||||||
|
// It is safe even on Windows where the directory separator is not "/",
|
||||||
|
// but make sure you're not passing in a "\"-delimited path.
|
||||||
|
function getRelativeFile(relativePath)
|
||||||
|
{
|
||||||
|
let profileDir = getProfileDir();
|
||||||
|
|
||||||
|
let file = profileDir.clone();
|
||||||
|
relativePath.split('/').forEach(function(component) {
|
||||||
|
file.append(component);
|
||||||
|
});
|
||||||
|
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
|
||||||
|
function repeatChar(count, ch) {
|
||||||
|
if (count == 0) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = ch;
|
||||||
|
let count2 = count / 2;
|
||||||
|
|
||||||
|
// Double the input until it is long enough.
|
||||||
|
while (result.length <= count2) {
|
||||||
|
result += result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use substring to hit the precise length target without using extra memory.
|
||||||
|
return result + result.substring(0, count - result.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPrincipal(url, attrs)
|
||||||
|
{
|
||||||
|
let uri = Services.io.newURI(url);
|
||||||
|
if (!attrs) {
|
||||||
|
attrs = {};
|
||||||
|
}
|
||||||
|
return Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getCurrentPrincipal()
|
||||||
|
{
|
||||||
|
return Cc["@mozilla.org/systemprincipal;1"].createInstance(Ci.nsIPrincipal);
|
||||||
|
}
|
||||||
|
|
||||||
|
function getLocalStorage(principal)
|
||||||
|
{
|
||||||
|
if (!principal) {
|
||||||
|
principal = getCurrentPrincipal();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Services.domStorageManager.createStorage(null, principal, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function requestFinished(request) {
|
||||||
|
return new Promise(function(resolve, reject) {
|
||||||
|
request.callback = function(request) {
|
||||||
|
if (request.resultCode == Cr.NS_OK) {
|
||||||
|
resolve(request.result);
|
||||||
|
} else {
|
||||||
|
reject(request.resultCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadSubscript(path)
|
||||||
|
{
|
||||||
|
let file = do_get_file(path, false);
|
||||||
|
let uri = Services.io.newFileURI(file);
|
||||||
|
Services.scriptloader.loadSubScript(uri.spec);
|
||||||
|
}
|
Двоичный файл не отображается.
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
const lsArchiveFile = "storage/ls-archive.sqlite";
|
||||||
|
|
||||||
|
const principalInfo = {
|
||||||
|
url: "http://example.com",
|
||||||
|
attrs: {}
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkStorage()
|
||||||
|
{
|
||||||
|
let principal = getPrincipal(principalInfo.url, principalInfo.attrs);
|
||||||
|
let storage = getLocalStorage(principal);
|
||||||
|
try {
|
||||||
|
storage.open();
|
||||||
|
ok(true, "Did not throw");
|
||||||
|
} catch(ex) {
|
||||||
|
ok(false, "Should not have thrown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
info("Setting pref");
|
||||||
|
|
||||||
|
Services.prefs.setBoolPref("dom.storage.next_gen", true);
|
||||||
|
|
||||||
|
// Profile 1 - Archive file is a directory.
|
||||||
|
info("Clearing");
|
||||||
|
|
||||||
|
let request = clear();
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
let archiveFile = getRelativeFile(lsArchiveFile);
|
||||||
|
|
||||||
|
archiveFile.create(Ci.nsIFile.DIRECTORY_TYPE, parseInt("0755", 8));
|
||||||
|
|
||||||
|
checkStorage();
|
||||||
|
|
||||||
|
// Profile 2 - Corrupted archive file.
|
||||||
|
info("Clearing");
|
||||||
|
|
||||||
|
request = clear();
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
let ostream = Cc["@mozilla.org/network/file-output-stream;1"]
|
||||||
|
.createInstance(Ci.nsIFileOutputStream);
|
||||||
|
ostream.init(archiveFile, -1, parseInt("0644", 8), 0);
|
||||||
|
ostream.write("foobar", 6);
|
||||||
|
ostream.close();
|
||||||
|
|
||||||
|
checkStorage();
|
||||||
|
|
||||||
|
// Profile 3 - Nonupdateable archive file.
|
||||||
|
info("Clearing");
|
||||||
|
|
||||||
|
request = clear();
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
info("Installing package");
|
||||||
|
|
||||||
|
// The profile contains storage.sqlite and storage/ls-archive.sqlite
|
||||||
|
// storage/ls-archive.sqlite was taken from FF 54 to force an upgrade.
|
||||||
|
// There's just one record in the webappsstore2 table. The record was
|
||||||
|
// modified by renaming the origin attribute userContextId to userContextKey.
|
||||||
|
// This triggers an error during the upgrade.
|
||||||
|
installPackage("archive_profile");
|
||||||
|
|
||||||
|
let fileSize = archiveFile.fileSize;
|
||||||
|
ok(fileSize > 0, "archive file size is greater than zero");
|
||||||
|
|
||||||
|
checkStorage();
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadSubscript("databaseShadowing-shared.js");
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
enableNextGenLocalStorage();
|
||||||
|
|
||||||
|
storeData();
|
||||||
|
|
||||||
|
verifyData([]);
|
||||||
|
|
||||||
|
// Wait for all database connections to close.
|
||||||
|
let request = reset();
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
exportShadowDatabase("shadowdb.sqlite");
|
||||||
|
|
||||||
|
// The shadow database is now prepared for test_databaseShadowing2.js
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadSubscript("databaseShadowing-shared.js");
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
// The shadow database was prepared in test_databaseShadowing1.js
|
||||||
|
|
||||||
|
disableNextGenLocalStorage();
|
||||||
|
|
||||||
|
if (!importShadowDatabase("shadowdb.sqlite")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyData([]);
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadSubscript("databaseShadowing-shared.js");
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
enableNextGenLocalStorage();
|
||||||
|
|
||||||
|
storeData();
|
||||||
|
|
||||||
|
verifyData([]);
|
||||||
|
|
||||||
|
let principal = getPrincipal("http://origin.test", {});
|
||||||
|
let request = clearOrigin(principal, "default");
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
verifyData([1]);
|
||||||
|
|
||||||
|
// Wait for all database connections to close.
|
||||||
|
request = reset();
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
exportShadowDatabase("shadowdb_clearedOrigin.sqlite");
|
||||||
|
|
||||||
|
// The shadow database is now prepared for
|
||||||
|
// test_databaseShadowing_clearOrigin2.js
|
||||||
|
}
|
|
@ -0,0 +1,19 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadSubscript("databaseShadowing-shared.js");
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
// The shadow database was prepared in test_databaseShadowing_clearOrigin1.js
|
||||||
|
|
||||||
|
disableNextGenLocalStorage();
|
||||||
|
|
||||||
|
if (!importShadowDatabase("shadowdb-clearedOrigin.sqlite")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyData([1]);
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadSubscript("databaseShadowing-shared.js");
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
enableNextGenLocalStorage();
|
||||||
|
|
||||||
|
storeData();
|
||||||
|
|
||||||
|
verifyData([]);
|
||||||
|
|
||||||
|
let request = clearOriginsByPattern(JSON.stringify({ userContextId: 15 }));
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
verifyData([4,5,6]);
|
||||||
|
|
||||||
|
// Wait for all database connections to close.
|
||||||
|
request = reset();
|
||||||
|
await requestFinished(request);
|
||||||
|
|
||||||
|
exportShadowDatabase("shadowdb-clearedOriginsByPattern.sqlite");
|
||||||
|
|
||||||
|
// The shadow database is now prepared for
|
||||||
|
// test_databaseShadowing_clearOriginsByPattern2.js
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/publicdomain/zero/1.0/
|
||||||
|
*/
|
||||||
|
|
||||||
|
loadSubscript("databaseShadowing-shared.js");
|
||||||
|
|
||||||
|
async function testSteps()
|
||||||
|
{
|
||||||
|
// The shadow database was prepared in
|
||||||
|
// test_databaseShadowing_clearOriginsByPattern1.js
|
||||||
|
|
||||||
|
disableNextGenLocalStorage();
|
||||||
|
|
||||||
|
if (!importShadowDatabase("shadowdb-clearedOriginsByPattern.sqlite")) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
verifyData([4,5,6]);
|
||||||
|
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче