Merge inbound to mozilla-central a=merge

This commit is contained in:
arthur.iakab 2019-03-07 23:58:42 +02:00
commit 85ad02d41a
177 changed files with 15808 additions and 2167 deletions

1
Cargo.lock generated
View File

@ -1722,6 +1722,7 @@ dependencies = [
"nserror 0.1.0",
"nsstring 0.1.0",
"url 1.7.2 (registry+https://github.com/rust-lang/crates.io-index)",
"uuid 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)",
"xpcom 0.1.0",
]

View File

@ -22,11 +22,6 @@
display: none;
}
.tab-icon-pending[preopened],
.tab-icon-image[preopened] {
visibility: hidden;
}
.tab-sharing-icon-overlay[sharing]:not([selected]),
.tab-icon-overlay[soundplaying][pinned],
.tab-icon-overlay[muted][pinned],

View File

@ -32,7 +32,6 @@ window._gBrowser = {
window.addEventListener("occlusionstatechange", this);
this._setupInitialBrowserAndTab();
this._preopenPinnedTabs();
if (Services.prefs.getBoolPref("browser.display.use_system_colors")) {
this.tabpanels.style.backgroundColor = "-moz-default-background-color";
@ -384,31 +383,6 @@ window._gBrowser = {
browser.webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
},
_preopenPinnedTabs() {
let numPinnedTabs = 0;
if (!PrivateBrowsingUtils.isWindowPrivate(window)) {
let windows = browserWindows();
windows.getNext();
let isOnlyWindow = !windows.hasMoreElements();
if (isOnlyWindow) {
numPinnedTabs = Services.prefs.getIntPref("browser.tabs.firstWindowRestore.numPinnedTabs", 0);
}
}
for (let i = 0; i < numPinnedTabs; i++) {
let tab = this.addTrustedTab("about:blank", {
skipAnimation: true,
noInitialLabel: true,
skipBackgroundNotify: true,
createLazyBrowser: true,
pinned: true,
isForFirstWindowRestore: true,
});
tab.setAttribute("preopened", "true");
}
},
/**
* BEGIN FORWARDED BROWSER PROPERTIES. IF YOU ADD A PROPERTY TO THE BROWSER ELEMENT
* MAKE SURE TO ADD IT HERE AS WELL.
@ -619,33 +593,14 @@ window._gBrowser = {
this.tabContainer._updateCloseButtons();
},
_sendPinnedTabContentMessage(aTab) {
_notifyPinnedStatus(aTab) {
this.getBrowserForTab(aTab).messageManager.sendAsyncMessage("Browser:AppTab", { isAppTab: aTab.pinned });
},
_notifyPinnedStatus(aTab, aDeferContentMessage = false) {
if (!aDeferContentMessage) {
this._sendPinnedTabContentMessage(aTab);
}
let event = document.createEvent("Events");
event.initEvent(aTab.pinned ? "TabPinned" : "TabUnpinned", true, false);
aTab.dispatchEvent(event);
},
_maybeUpdateNumPinnedTabsPref() {
if (!PrivateBrowsingUtils.isWindowPrivate(window) &&
BrowserWindowTracker.getTopWindow() == window) {
Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
this._numPinnedTabs);
}
},
activatePreopenedPinnedTab(aTab) {
this._insertBrowser(aTab);
this._sendPinnedTabContentMessage(aTab);
},
pinTab(aTab) {
if (aTab.pinned)
return;
@ -657,7 +612,6 @@ window._gBrowser = {
aTab.setAttribute("pinned", "true");
this._updateTabBarForPinnedTabs();
this._notifyPinnedStatus(aTab);
this._maybeUpdateNumPinnedTabsPref();
},
unpinTab(aTab) {
@ -670,7 +624,6 @@ window._gBrowser = {
aTab._pinnedUnscrollable = false;
this._updateTabBarForPinnedTabs();
this._notifyPinnedStatus(aTab);
this._maybeUpdateNumPinnedTabsPref();
},
previewTab(aTab, aCallback) {
@ -2354,7 +2307,6 @@ window._gBrowser = {
forceNotRemote,
fromExternal,
index,
isForFirstWindowRestore,
lazyTabTitle,
name,
nextTabParentId,
@ -2538,9 +2490,6 @@ window._gBrowser = {
if (pinned) {
this._updateTabBarForPinnedTabs();
if (!isForFirstWindowRestore) {
this._maybeUpdateNumPinnedTabsPref();
}
}
this.tabContainer._setPositionalAttributes();
@ -2620,15 +2569,13 @@ window._gBrowser = {
userContextId);
b.registeredOpenURI = lazyBrowserURI;
}
if (!isForFirstWindowRestore) {
SessionStore.setTabState(t, {
entries: [{
url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
title: lazyTabTitle,
triggeringPrincipal_base64: E10SUtils.serializePrincipal(triggeringPrincipal),
}],
});
}
SessionStore.setTabState(t, {
entries: [{
url: lazyBrowserURI ? lazyBrowserURI.spec : "about:blank",
title: lazyTabTitle,
triggeringPrincipal_base64: E10SUtils.serializePrincipal(triggeringPrincipal),
}],
});
} else {
this._insertBrowser(t, true);
}
@ -2666,9 +2613,7 @@ window._gBrowser = {
// If we didn't swap docShells with a preloaded browser
// then let's just continue loading the page normally.
if (!usingPreloadedContent &&
!createLazyBrowser &&
(!uriIsAboutBlank || !allowInheritPrincipal)) {
if (!usingPreloadedContent && (!uriIsAboutBlank || !allowInheritPrincipal)) {
// pretend the user typed this so it'll be available till
// the document successfully loads
if (aURI && !gInitialPages.includes(aURI)) {
@ -2721,7 +2666,7 @@ window._gBrowser = {
// Additionally send pinned tab events
if (pinned) {
this._notifyPinnedStatus(t, isForFirstWindowRestore);
this._notifyPinnedStatus(t);
}
return t;
@ -3219,10 +3164,8 @@ window._gBrowser = {
this.tabs[i]._tPos = i;
if (!this._windowIsClosing) {
if (wasPinned) {
if (wasPinned)
this.tabContainer._positionPinnedTabs();
this._maybeUpdateNumPinnedTabsPref();
}
// update tab close buttons state
this.tabContainer._updateCloseButtons();
@ -3895,6 +3838,7 @@ window._gBrowser = {
skipAnimation: true,
index: aIndex,
createLazyBrowser,
allowInheritPrincipal: createLazyBrowser,
};
let numPinned = this._numPinnedTabs;
@ -5071,7 +5015,6 @@ class TabProgressListener {
this.mTab.removeAttribute("crashed");
}
this.mTab.removeAttribute("preopened");
if (this._shouldShowProgress(aRequest)) {
if (!(aStateFlags & Ci.nsIWebProgressListener.STATE_RESTORING) &&
aWebProgress && aWebProgress.isTopLevel) {

View File

@ -1937,10 +1937,10 @@
anonid="tab-throbber"
class="tab-throbber"
layer="true"/>
<xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon,preopened"
<xul:hbox xbl:inherits="fadein,pinned,busy,progress,selected=visuallyselected,pendingicon"
anonid="tab-icon-pending"
class="tab-icon-pending"/>
<xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing,preopened"
<xul:image xbl:inherits="src=image,triggeringprincipal=iconloadingprincipal,requestcontextid,fadein,pinned,selected=visuallyselected,busy,crashed,sharing"
anonid="tab-icon-image"
class="tab-icon-image"
validate="never"

View File

@ -249,8 +249,8 @@ var SessionStore = {
return SessionStoreInternal.getWindowState(aWindow);
},
setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite, aFirstWindow) {
SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite, aFirstWindow);
setWindowState: function ss_setWindowState(aWindow, aState, aOverwrite) {
SessionStoreInternal.setWindowState(aWindow, aState, aOverwrite);
},
getTabState: function ss_getTabState(aTab) {
@ -731,7 +731,6 @@ var SessionStoreInternal = {
this._prefBranch.addObserver("sessionstore.max_windows_undo", this, true);
this._restore_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_on_demand");
this._restore_pinned_tabs_on_demand = this._prefBranch.getBoolPref("sessionstore.restore_pinned_tabs_on_demand");
this._prefBranch.addObserver("sessionstore.restore_on_demand", this, true);
gResistFingerprintingEnabled = Services.prefs.getBoolPref("privacy.resistFingerprinting");
@ -2500,15 +2499,12 @@ var SessionStoreInternal = {
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
},
setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite, aFirstWindow) {
setWindowState: function ssi_setWindowState(aWindow, aState, aOverwrite) {
if (!aWindow.__SSi) {
throw Components.Exception("Window is not tracked", Cr.NS_ERROR_INVALID_ARG);
}
this.restoreWindows(aWindow, aState, {
overwriteTabs: aOverwrite,
firstWindow: aFirstWindow,
});
this.restoreWindows(aWindow, aState, {overwriteTabs: aOverwrite});
// Notify of changes to closed objects.
this._notifyOfClosedObjectsChange();
@ -3207,7 +3203,6 @@ var SessionStoreInternal = {
if (!uriObj || (uriObj && !window.gBrowser.isLocalAboutURI(uriObj))) {
tab.setAttribute("busy", "true");
}
tab.removeAttribute("preopened");
// Hack to ensure that the about:home, about:newtab, and about:welcome
// favicon is loaded instantaneously, to avoid flickering and improve
@ -3600,36 +3595,6 @@ var SessionStoreInternal = {
return Promise.all([...WINDOW_SHOWING_PROMISES.values()].map(deferred => deferred.promise));
},
/**
* Handles the pinning / unpinning of a selected tab restored with
* restoreWindow.
*
* @param aWindow
* Window reference to the window used for restoration
* @param aWinData
* The window data we're restoring
* @param aRestoreIndex
* The index of the tab data we're currently restoring
* @returns the selected tab
*/
_updateRestoredSelectedTabPinnedState(aWindow, aWinData, aRestoreIndex) {
let tabbrowser = aWindow.gBrowser;
let tabData = aWinData.tabs[aRestoreIndex];
let tab = tabbrowser.selectedTab;
let needsUnpin = tab.pinned && !tabData.pinned;
let needsPin = !tab.pinned && tabData.pinned;
if (needsUnpin) {
tabbrowser.unpinTab(tab);
} else if (needsPin && tab == tabbrowser.tabs[aRestoreIndex]) {
tabbrowser.pinTab(tab);
} else if (needsPin) {
tabbrowser.removeTab(tabbrowser.tabs[aRestoreIndex]);
tabbrowser.pinTab(tab);
tabbrowser.moveTabTo(tab, aRestoreIndex);
}
return tab;
},
/**
* restore features to a single window
* @param aWindow
@ -3685,7 +3650,7 @@ var SessionStoreInternal = {
// We need to keep track of the initially open tabs so that they
// can be moved to the end of the restored tabs.
let initialTabs = [];
let initialTabs;
if (!overwriteTabs && firstWindow) {
initialTabs = Array.slice(tabbrowser.tabs);
}
@ -3693,11 +3658,8 @@ var SessionStoreInternal = {
// Get rid of tabs that aren't needed anymore.
if (overwriteTabs) {
for (let i = tabbrowser.browsers.length - 1; i >= 0; i--) {
if (!tabbrowser.tabs[i].selected &&
!tabbrowser.tabs[i].hasAttribute("preopened")) {
if (!tabbrowser.tabs[i].selected) {
tabbrowser.removeTab(tabbrowser.tabs[i]);
} else if (tabbrowser.tabs[i].hasAttribute("preopened")) {
initialTabs.push(tabbrowser.tabs[i]);
}
}
}
@ -3715,19 +3677,14 @@ var SessionStoreInternal = {
// selecting a new tab.
if (select &&
tabbrowser.selectedTab.userContextId == userContextId) {
tab = this._updateRestoredSelectedTabPinnedState(aWindow, winData, t);
tab = tabbrowser.selectedTab;
if (!tabData.pinned) {
tabbrowser.unpinTab(tab);
}
tabbrowser.moveTabToEnd();
if (aWindow.gMultiProcessBrowser && !tab.linkedBrowser.isRemoteBrowser) {
tabbrowser.updateBrowserRemoteness(tab.linkedBrowser, true);
}
} else if (tabData.pinned &&
tabbrowser.tabs[t] &&
tabbrowser.tabs[t].pinned &&
!tabbrowser.tabs[t].linkedPanel &&
tabbrowser.tabs[t].userContextId == userContextId) {
tab = tabbrowser.tabs[t];
tabbrowser.activatePreopenedPinnedTab(tab);
}
// Add a new tab if needed.
@ -3776,14 +3733,11 @@ var SessionStoreInternal = {
}
// Move the originally open tabs to the end.
let endPosition = tabbrowser.tabs.length - 1;
for (let tab of initialTabs) {
if (tab.hasAttribute("preopened") &&
!tab.linkedPanel) {
tabbrowser.removeTab(tab);
} else if (!tab.hasAttribute("preopened")) {
tabbrowser.unpinTab(tab);
tabbrowser.moveTabTo(tab, endPosition);
if (initialTabs) {
let endPosition = tabbrowser.tabs.length - 1;
for (let i = 0; i < initialTabs.length; i++) {
tabbrowser.unpinTab(initialTabs[i]);
tabbrowser.moveTabTo(initialTabs[i], endPosition);
}
}
@ -4044,9 +3998,6 @@ var SessionStoreInternal = {
for (let t = 0; t < aTabs.length; t++) {
if (t != selectedIndex) {
this.restoreTab(aTabs[t], aTabData[t]);
if (this._restore_pinned_tabs_on_demand) {
aTabs[t].removeAttribute("preopened");
}
}
}
},

View File

@ -113,7 +113,6 @@ skip-if = (verify && (os == 'win' || os == 'mac'))
[browser_old_favicon.js]
[browser_page_title.js]
[browser_pending_tabs.js]
[browser_preopened_apptabs.js]
[browser_privatetabs.js]
[browser_purge_shistory.js]
skip-if = e10s # Bug 1271024

View File

@ -1,65 +0,0 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
const PREF_RESTORE_ON_DEMAND = "browser.sessionstore.restore_on_demand";
const PREF_NUM_PINNED_TABS = "browser.tabs.firstWindowRestore.numPinnedTabs";
function muffleMainWindowType() {
let oldWinType = document.documentElement.getAttribute("windowtype");
// Check if we've already done this to allow calling multiple times:
if (oldWinType != "navigator:testrunner") {
// Make the main test window not count as a browser window any longer
document.documentElement.setAttribute("windowtype", "navigator:testrunner");
registerCleanupFunction(() => {
document.documentElement.setAttribute("windowtype", oldWinType);
});
}
}
registerCleanupFunction(function() {
Services.prefs.clearUserPref(PREF_NUM_PINNED_TABS);
});
let testCases = [
{numPinnedPref: 5, selected: 3, overrideTabs: false},
{numPinnedPref: 5, selected: 3, overrideTabs: true},
{numPinnedPref: 5, selected: 1, overrideTabs: false},
{numPinnedPref: 5, selected: 1, overrideTabs: true},
{numPinnedPref: 3, selected: 3, overrideTabs: false},
{numPinnedPref: 3, selected: 3, overrideTabs: true},
{numPinnedPref: 3, selected: 1, overrideTabs: false},
{numPinnedPref: 3, selected: 1, overrideTabs: true},
{numPinnedPref: 1, selected: 3, overrideTabs: false},
{numPinnedPref: 1, selected: 3, overrideTabs: true},
{numPinnedPref: 1, selected: 1, overrideTabs: false},
{numPinnedPref: 1, selected: 1, overrideTabs: true},
{numPinnedPref: 0, selected: 3, overrideTabs: false},
{numPinnedPref: 0, selected: 3, overrideTabs: true},
{numPinnedPref: 0, selected: 1, overrideTabs: false},
{numPinnedPref: 0, selected: 1, overrideTabs: true},
];
for (let {numPinnedPref, selected, overrideTabs} of testCases) {
add_task(async function testPrefSynced() {
Services.prefs.setIntPref(PREF_NUM_PINNED_TABS, numPinnedPref);
let state = { windows: [{ tabs: [
{ entries: [{ url: "http://example.org/#1", triggeringPrincipal_base64 }], pinned: true, userContextId: 0 },
{ entries: [{ url: "http://example.org/#2", triggeringPrincipal_base64 }], pinned: true, userContextId: 0 },
{ entries: [{ url: "http://example.org/#3", triggeringPrincipal_base64 }], pinned: true, userContextId: 0 },
], selected }] };
muffleMainWindowType();
let win = await BrowserTestUtils.openNewBrowserWindow();
Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, numPinnedPref);
Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length, 1);
await setWindowState(win, state, overrideTabs, true);
Assert.equal([...win.gBrowser.tabs].filter(t => t.pinned).length, 3);
Assert.equal([...win.gBrowser.tabs].filter(t => !!t.linkedPanel).length,
overrideTabs ? 3 : 4);
await BrowserTestUtils.closeWindow(win);
});
}

View File

@ -190,9 +190,8 @@ async function setBrowserState(state, win = window) {
await promiseWindowRestored(win);
}
async function setWindowState(win, state, overwrite = false, firstWindow = false) {
ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state,
overwrite, firstWindow);
async function setWindowState(win, state, overwrite = false) {
ss.setWindowState(win, typeof state != "string" ? JSON.stringify(state) : state, overwrite);
await promiseWindowRestored(win);
}

View File

@ -96,11 +96,6 @@ function _untrackWindowOrder(window) {
_trackedWindows.splice(idx, 1);
}
function _trackPinnedTabs(window) {
Services.prefs.setIntPref("browser.tabs.firstWindowRestore.numPinnedTabs",
window.gBrowser._numPinnedTabs);
}
// Methods that impact a window. Put into single object for organization.
var WindowHelper = {
addWindow(window) {
@ -143,7 +138,6 @@ var WindowHelper = {
_untrackWindowOrder(window);
_trackWindowOrder(window);
_trackPinnedTabs(window);
_updateCurrentContentOuterWindowID(window.gBrowser.selectedBrowser);
},

View File

@ -152,8 +152,8 @@ nsresult ContentPrincipal::GenerateOriginNoSuffixFromURI(
// about:blank is special since it can be generated from different
// sources. We check for moz-safe-about:blank since origin is an
// innermost URI.
!origin->GetSpecOrDefault().EqualsLiteral("moz-safe-about:blank")) ||
(NS_SUCCEEDED(origin->SchemeIs("indexeddb", &isBehaved)) && isBehaved)) {
!StringBeginsWith(origin->GetSpecOrDefault(),
NS_LITERAL_CSTRING("moz-safe-about:blank")))) {
rv = origin->GetAsciiSpec(aOriginNoSuffix);
NS_ENSURE_SUCCESS(rv, rv);
@ -410,6 +410,13 @@ static nsresult GetSpecialBaseDomain(const nsCOMPtr<nsIURI>& aCodebase,
return aCodebase->GetSpec(aBaseDomain);
}
bool isBehaved;
if (NS_SUCCEEDED(aCodebase->SchemeIs("indexeddb", &isBehaved)) &&
isBehaved) {
*aHandled = true;
return aCodebase->GetSpec(aBaseDomain);
}
return NS_OK;
}

View File

@ -124,7 +124,7 @@ bool nsJSPrincipals::ReadPrincipals(JSContext* aCx,
static bool ReadPrincipalInfo(
JSStructuredCloneReader* aReader, OriginAttributes& aAttrs,
nsACString& aSpec, nsACString& aOriginNoSuffix,
nsACString& aSpec, nsACString& aOriginNoSuffix, nsACString& aBaseDomain,
nsTArray<ContentSecurityPolicy>* aPolicies = nullptr) {
uint32_t suffixLength, specLength;
if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
@ -193,6 +193,28 @@ static bool ReadPrincipalInfo(
}
}
uint32_t baseDomainIsVoid, baseDomainLength;
if (!JS_ReadUint32Pair(aReader, &baseDomainIsVoid, &baseDomainLength)) {
return false;
}
MOZ_ASSERT(baseDomainIsVoid == 0 || baseDomainIsVoid == 1);
if (baseDomainIsVoid) {
MOZ_ASSERT(baseDomainLength == 0);
aBaseDomain.SetIsVoid(true);
return true;
}
if (!aBaseDomain.SetLength(baseDomainLength, fallible)) {
return false;
}
if (!JS_ReadBytes(aReader, aBaseDomain.BeginWriting(), baseDomainLength)) {
return false;
}
return true;
}
@ -204,7 +226,8 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
OriginAttributes attrs;
nsAutoCString spec;
nsAutoCString originNoSuffix;
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix)) {
nsAutoCString baseDomain;
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain)) {
return false;
}
aInfo = NullPrincipalInfo(attrs, spec);
@ -234,8 +257,10 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
OriginAttributes attrs;
nsAutoCString spec;
nsAutoCString originNoSuffix;
nsAutoCString baseDomain;
nsTArray<ContentSecurityPolicy> policies;
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, &policies)) {
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, baseDomain,
&policies)) {
return false;
}
@ -249,7 +274,7 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
// XXX: Do we care about mDomain for structured clone?
aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec, Nothing(),
std::move(policies));
std::move(policies), baseDomain);
} else {
#ifdef FUZZING
return false;
@ -295,6 +320,7 @@ bool nsJSPrincipals::ReadKnownPrincipalType(JSContext* aCx,
static bool WritePrincipalInfo(
JSStructuredCloneWriter* aWriter, const OriginAttributes& aAttrs,
const nsCString& aSpec, const nsCString& aOriginNoSuffix,
const nsCString& aBaseDomain,
const nsTArray<ContentSecurityPolicy>* aPolicies = nullptr) {
nsAutoCString suffix;
aAttrs.CreateSuffix(suffix);
@ -322,7 +348,12 @@ static bool WritePrincipalInfo(
}
}
return true;
if (aBaseDomain.IsVoid()) {
return JS_WriteUint32Pair(aWriter, 1, 0);
}
return JS_WriteUint32Pair(aWriter, 0, aBaseDomain.Length()) &&
JS_WriteBytes(aWriter, aBaseDomain.get(), aBaseDomain.Length());
}
static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
@ -331,7 +362,7 @@ static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
const NullPrincipalInfo& nullInfo = aInfo;
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_NULL_PRINCIPAL, 0) &&
WritePrincipalInfo(aWriter, nullInfo.attrs(), nullInfo.spec(),
EmptyCString());
EmptyCString(), EmptyCString());
}
if (aInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_SYSTEM_PRINCIPAL, 0);
@ -355,7 +386,7 @@ static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
const ContentPrincipalInfo& cInfo = aInfo;
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
cInfo.originNoSuffix(),
cInfo.originNoSuffix(), cInfo.baseDomain(),
&(cInfo.securityPolicies()));
}

View File

@ -657,6 +657,7 @@ support-files =
examples/simple-worker.js
examples/doc-event-handler.html
examples/doc-eval-throw.html
examples/doc-sourceURL-breakpoint.html
[browser_dbg-asm.js]
[browser_dbg-async-stepping.js]
@ -774,3 +775,4 @@ skip-if = true
[browser_dbg-windowless-workers-early-breakpoint.js]
[browser_dbg-event-handler.js]
[browser_dbg-eval-throw.js]
[browser_dbg-sourceURL-breakpoint.js]

View File

@ -0,0 +1,14 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
// Test that breakpoints are hit in eval'ed sources with a sourceURL property.
add_task(async function() {
const dbg = await initDebugger("doc-sourceURL-breakpoint.html", "my-foo.js");
await selectSource(dbg, "my-foo.js");
await addBreakpoint(dbg, "my-foo.js", 2);
invokeInTab("foo");
await waitForPaused(dbg);
ok(true, "paused at breakpoint");
});

View File

@ -0,0 +1,9 @@
<body>
<script>
var script = `function foo() {
console.log('called foo');
}
//# sourceURL=my-foo.js`;
eval(script);
</script>
</body>

View File

@ -756,17 +756,18 @@ function getFirstBreakpointColumn(dbg, {line, sourceId}) {
* @return {Promise}
* @static
*/
async function addBreakpoint(dbg, source, line, column) {
async function addBreakpoint(dbg, source, line, column, options) {
source = findSource(dbg, source);
const sourceId = source.id;
column = column || getFirstBreakpointColumn(dbg, {line, sourceId: source.id});
const bpCount = dbg.selectors.getBreakpointCount(dbg.getState());
dbg.actions.addBreakpoint({ sourceId, line, column });
dbg.actions.addBreakpoint({ sourceId, line, column }, options);
await waitForDispatch(dbg, "ADD_BREAKPOINT");
is(dbg.selectors.getBreakpointCount(dbg.getState()), bpCount + 1, "a new breakpoint was created");
}
function disableBreakpoint(dbg, source, line, column) {
column = column || getFirstBreakpointColumn(dbg, {line, sourceId: source.id});
const location = { sourceId: source.id, sourceUrl: source.url, line, column };
const bp = dbg.selectors.getBreakpointForLocation(dbg.getState(), location);
dbg.actions.disableBreakpoint(bp);
@ -776,6 +777,7 @@ function disableBreakpoint(dbg, source, line, column) {
function setBreakpointOptions(dbg, source, line, column, options) {
source = findSource(dbg, source);
const sourceId = source.id;
column = column || getFirstBreakpointColumn(dbg, {line, sourceId});
dbg.actions.setBreakpointOptions({ sourceId, line, column }, options);
return waitForDispatch(dbg, "SET_BREAKPOINT_OPTIONS");
}

View File

@ -236,6 +236,7 @@ subsuite = clipboard
[browser_markup_toggle_04.js]
[browser_markup_toggle_closing_tag_line.js]
[browser_markup_update-on-navigtion.js]
[browser_markup_view-source.js]
[browser_markup_void_elements_html.js]
[browser_markup_void_elements_xhtml.js]
[browser_markup_whitespace.js]

View File

@ -0,0 +1,63 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* 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";
const DOCUMENT_SRC = `
<body>
<button id="foo">Button</button>
<script>
var script = \`
function foo() {
console.log('handler');
}
\`;
eval(script);
var button = document.getElementById("foo");
button.addEventListener("click", foo, false);
</script>
</body>`;
const TEST_URI = "data:text/html;charset=utf-8," + DOCUMENT_SRC;
add_task(async function() {
// Test that event handler links go to the right debugger source when it
// came from an eval().
const { inspector, toolbox } = await openInspectorForURL(TEST_URI);
const target = await TargetFactory.forTab(gBrowser.selectedTab);
const nodeFront = await getNodeFront("#foo", inspector);
const container = getContainerForNodeFront(nodeFront, inspector);
const evHolder = container.elt.querySelector(
".inspector-badge.interactive[data-event]");
evHolder.scrollIntoView();
EventUtils.synthesizeMouseAtCenter(evHolder, {},
inspector.markup.doc.defaultView);
const tooltip = inspector.markup.eventDetailsTooltip;
await tooltip.once("shown");
const debuggerIcon = tooltip.panel.querySelector(".event-tooltip-debugger-icon");
EventUtils.synthesizeMouse(debuggerIcon, 2, 2, {}, debuggerIcon.ownerGlobal);
await gDevTools.showToolbox(target, "jsdebugger");
const dbg = toolbox.getPanel("jsdebugger");
let source;
await BrowserTestUtils.waitForCondition(() => {
source = dbg._selectors.getSelectedSource(dbg._getState());
return !!source;
}, "loaded source", 100, 20);
is(
source.url,
null,
"expected no url for eval source"
);
});

View File

@ -140,8 +140,9 @@ class Frame extends Component {
// to Scratchpad URIs.
// Source mapped sources might not necessary linkable, but they
// are still valid in the debugger.
// If we have a source ID then we can show the source in the debugger.
const isLinkable = !!(isScratchpadScheme(source) || parseURL(source))
|| isSourceMapped;
|| isSourceMapped || sourceId;
const elements = [];
const sourceElements = [];
let sourceEl;

View File

@ -52,7 +52,8 @@ exports.viewSourceInStyleEditor = async function(toolbox, sourceURL,
exports.viewSourceInDebugger = async function(toolbox, sourceURL, sourceLine, sourceId,
reason = "unknown") {
const dbg = await toolbox.loadTool("jsdebugger");
const source = dbg.getSourceByURL(sourceURL) || dbg.getSourceByActorId(sourceId);
const source =
sourceId ? dbg.getSourceByActorId(sourceId) : dbg.getSourceByURL(sourceURL);
if (source) {
await toolbox.selectTool("jsdebugger", reason);
dbg.selectSource(source.id, sourceLine);

View File

@ -184,6 +184,7 @@ EventTooltip.prototype = {
editor: editor,
handler: listener.handler,
uri: listener.origin,
sourceActor: listener.sourceActor,
dom0: listener.DOM0,
native: listener.native,
appended: false,
@ -263,7 +264,7 @@ EventTooltip.prototype = {
const header = event.currentTarget;
const content = header.nextElementSibling;
const {uri} = this._eventEditors.get(content);
const {sourceActor, uri} = this._eventEditors.get(content);
const location = this._parseLocation(uri);
if (location) {
@ -272,7 +273,7 @@ EventTooltip.prototype = {
this._tooltip.hide();
toolbox.viewSourceInDebugger(location.url, location.line);
toolbox.viewSourceInDebugger(location.url, location.line, sourceActor);
}
},

View File

@ -112,6 +112,7 @@ stubPreparedMessages.set(`SyntaxError: redeclaration of let a`, new ConsoleMessa
},
"groupId": null,
"errorMessageName": "JSMSG_REDECLARED_VAR",
"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Redeclared_parameter?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
"userProvidedStyles": null,
"notes": [
{
@ -292,6 +293,7 @@ stubPackets.set(`SyntaxError: redeclaration of let a`, {
"pageError": {
"errorMessage": "SyntaxError: redeclaration of let a",
"errorMessageName": "JSMSG_REDECLARED_VAR",
"exceptionDocURL": "https://developer.mozilla.org/docs/Web/JavaScript/Reference/Errors/Redeclared_parameter?utm_source=mozilla&utm_medium=firefox-console-errors&utm_campaign=default",
"sourceName": "http://example.com/browser/devtools/client/webconsole/test/fixtures/stub-generators/test-console-api.html",
"sourceId": null,
"lineText": " let a, a;",

View File

@ -8,11 +8,12 @@
const TEST_URI = "http://example.com/browser/devtools/client/webconsole/" +
"test/mochitest/test-eval-sources.html";
async function clickFirstStackElement(hud, message) {
const button = message.querySelector(".collapse-button");
ok(button, "has button");
button.click();
async function clickFirstStackElement(hud, message, needsExpansion) {
if (needsExpansion) {
const button = message.querySelector(".collapse-button");
ok(button, "has button");
button.click();
}
let frame;
await waitUntil(() => {
@ -33,8 +34,8 @@ add_task(async function() {
const target = await TargetFactory.forTab(gBrowser.selectedTab);
const toolbox = gDevTools.getToolbox(target);
const messageNode = await waitFor(() => findMessage(hud, "BAR"));
await clickFirstStackElement(hud, messageNode);
let messageNode = await waitFor(() => findMessage(hud, "BAR"));
await clickFirstStackElement(hud, messageNode, true);
const dbg = toolbox.getPanel("jsdebugger");
@ -46,4 +47,18 @@ add_task(async function() {
await testOpenInDebugger(hud, toolbox, "FOO", false);
await testOpenInDebugger(hud, toolbox, "BAR", false);
// Test that links in the API work when the eval source has a sourceURL property
// which is not considered to be a valid URL.
await testOpenInDebugger(hud, toolbox, "BAZ", false);
// Test that stacks in console.trace() calls work.
messageNode = await waitFor(() => findMessage(hud, "TRACE"));
await clickFirstStackElement(hud, messageNode, false);
is(
/my-foo.js/.test(dbg._selectors.getSelectedSource(dbg._getState()).url),
true,
"expected source url"
);
});

View File

@ -377,11 +377,12 @@ async function checkClickOnNode(hud, toolbox, frameLinkNode, expectUrl) {
return !!dbg._selectors.getSelectedSource(dbg._getState());
});
is(
dbg._selectors.getSelectedSource(dbg._getState()).url,
expectUrl ? url : null,
"expected source url"
);
if (expectUrl) {
is(
dbg._selectors.getSelectedSource(dbg._getState()).url, url,
"expected source url"
);
}
}
/**

View File

@ -4,6 +4,14 @@
/* eslint-disable */
eval("window.foo = function() { console.log('FOO'); }");
eval("window.bar = function() { throw new Error('BAR') };");
eval(`window.baz = function() {
console.log('BAZ');
console.trace('TRACE');
}
//# sourceURL=my-foo.js`);
foo();
baz();
bar();
</script>

View File

@ -18,6 +18,7 @@ add_task(async function() {
const console = await getDebuggerSplitConsole(dbg);
const hud = console.hud;
await selectSource(dbg, "doc_rr_basic.html");
await addBreakpoint(dbg, "doc_rr_basic.html", 21, undefined,
{ logValue: `"Logpoint Number " + number` });
await addBreakpoint(dbg, "doc_rr_basic.html", 6, undefined,

View File

@ -89,6 +89,7 @@ const ErrorDocs = {
JSMSG_NOT_ITERABLE: "is_not_iterable",
JSMSG_PROPERTY_FAIL: "cant_access_property",
JSMSG_PROPERTY_FAIL_EXPR: "cant_access_property",
JSMSG_REDECLARED_VAR: "Redeclared_parameter",
};
const MIXED_CONTENT_LEARN_MORE = "https://developer.mozilla.org/docs/Web/Security/Mixed_content";

View File

@ -708,7 +708,9 @@ class ReactEventCollector extends MainEventCollector {
* The exposed class responsible for gathering events.
*/
class EventCollector {
constructor() {
constructor(targetActor) {
this.targetActor = targetActor;
// The event collector array. Please preserve the order otherwise there will
// be multiple failing tests.
this.eventCollectors = [
@ -864,6 +866,7 @@ class EventCollector {
let line = 0;
let native = false;
let url = "";
let sourceActor = "";
// If the listener is an object with a 'handleEvent' method, use that.
if (listenerDO.class === "Object" || /^XUL\w*Element$/.test(listenerDO.class)) {
@ -900,6 +903,8 @@ class EventCollector {
line = script.startLine;
url = script.url;
const actor = this.targetActor.sources.getOrCreateSourceActor(script.source);
sourceActor = actor ? actor.actorID : null;
// Checking for the string "[native code]" is the only way at this point
// to check for native code. Even if this provides a false positive then
@ -957,6 +962,7 @@ class EventCollector {
override.capturing : capturing,
hide: typeof override.hide !== "undefined" ? override.hide : hide,
native,
sourceActor,
};
// Hide the debugger icon for DOM0 and native listeners. DOM0 listeners are

View File

@ -48,7 +48,7 @@ const NodeActor = protocol.ActorClassWithSpec(nodeSpec, {
protocol.Actor.prototype.initialize.call(this, null);
this.walker = walker;
this.rawNode = node;
this._eventCollector = new EventCollector();
this._eventCollector = new EventCollector(this.walker.targetActor);
// Store the original display type and scrollable state and whether or not the node is
// displayed to track changes when reflows occur.

View File

@ -1141,10 +1141,8 @@ const ThreadActor = ActorClassWithSpec(threadSpec, {
},
onSources: function(request) {
// FIXME bug 1530699 we should make sure that existing breakpoints are
// applied to any sources we find here.
for (const source of this.dbg.findSources()) {
this.sources.createSourceActor(source);
this._addSource(source);
}
// No need to flush the new source packets here, as we are sending the

View File

@ -147,6 +147,13 @@ TabSources.prototype = {
return sourceActor;
},
getOrCreateSourceActor(source) {
if (this.hasSourceActor(source)) {
return this.getSourceActor(source);
}
return this.createSourceActor(source);
},
getSourceActorByInternalSourceId: function(id) {
if (!this._sourcesByInternalSourceId) {
this._sourcesByInternalSourceId = new Map();
@ -158,10 +165,7 @@ TabSources.prototype = {
}
const source = this._sourcesByInternalSourceId.get(id);
if (source) {
if (this.hasSourceActor(source)) {
return this.getSourceActor(source);
}
return this.createSourceActor(source);
return this.getOrCreateSourceActor(source);
}
return null;
},
@ -169,8 +173,8 @@ TabSources.prototype = {
getSourceActorsByURL: function(url) {
const rv = [];
if (url) {
for (const [source, actor] of this._sourceActors) {
if (source.url === url) {
for (const [, actor] of this._sourceActors) {
if (actor.url === url) {
rv.push(actor);
}
}

View File

@ -1701,6 +1701,12 @@ WebConsoleActor.prototype =
delete result.innerID;
delete result.consoleID;
if (result.stacktrace) {
result.stacktrace = Array.map(result.stacktrace, (frame) => {
return { ...frame, sourceId: this.getActorIdForInternalSourceId(frame.sourceId) };
});
}
result.arguments = Array.map(message.arguments || [], (obj) => {
const dbgObj = this.makeDebuggeeValue(obj, useObjectGlobal);
return this.createValueGrip(dbgObj);

View File

@ -1148,6 +1148,11 @@ PAsmJSCacheEntryParent* AllocEntryParent(OpenMode aOpenMode,
return nullptr;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
MOZ_ASSERT(false);
return nullptr;
}
RefPtr<ParentRunnable> runnable =
new ParentRunnable(aPrincipalInfo, aOpenMode, aWriteParams);
@ -1408,6 +1413,11 @@ ChildRunnable::Run() {
return NS_OK;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
Fail(JS::AsmJSCache_InternalError);
return NS_OK;
}
mPrincipalInfo = std::move(principalInfo);
PBackgroundChild* actor = BackgroundChild::GetOrCreateForCurrentThread();

View File

@ -15,6 +15,7 @@
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
#include "nsReadableUtils.h"
#include "nsThreadUtils.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Logging.h"
@ -33,6 +34,14 @@ static mozilla::LazyLogModule gThirdPartyLog("thirdPartyUtil");
static mozilla::StaticRefPtr<ThirdPartyUtil> gService;
// static
void ThirdPartyUtil::Startup() {
nsCOMPtr<mozIThirdPartyUtil> tpu;
if (NS_WARN_IF(!(tpu = do_GetService(THIRDPARTYUTIL_CONTRACTID)))) {
NS_WARNING("Failed to get third party util!");
}
}
nsresult ThirdPartyUtil::Init() {
NS_ENSURE_TRUE(NS_IsMainThread(), NS_ERROR_NOT_AVAILABLE);
@ -348,3 +357,37 @@ ThirdPartyUtil::GetBaseDomain(nsIURI* aHostURI, nsACString& aBaseDomain) {
return NS_OK;
}
NS_IMETHODIMP
ThirdPartyUtil::GetBaseDomainFromSchemeHost(const nsACString& aScheme,
const nsACString& aAsciiHost,
nsACString& aBaseDomain) {
MOZ_DIAGNOSTIC_ASSERT(IsASCII(aAsciiHost));
// Get the base domain. this will fail if the host contains a leading dot,
// more than one trailing dot, or is otherwise malformed.
nsresult rv = mTLDService->GetBaseDomainFromHost(aAsciiHost, 0, aBaseDomain);
if (rv == NS_ERROR_HOST_IS_IP_ADDRESS ||
rv == NS_ERROR_INSUFFICIENT_DOMAIN_LEVELS) {
// aMozURL is either an IP address, an alias such as 'localhost', an eTLD
// such as 'co.uk', or the empty string. Uses the normalized host in such
// cases.
aBaseDomain = aAsciiHost;
rv = NS_OK;
}
NS_ENSURE_SUCCESS(rv, rv);
// aMozURL (and thus aBaseDomain) may be the string '.'. If so, fail.
if (aBaseDomain.Length() == 1 && aBaseDomain.Last() == '.')
return NS_ERROR_INVALID_ARG;
// Reject any URLs without a host that aren't file:// URLs. This makes it the
// only way we can get a base domain consisting of the empty string, which
// means we can safely perform foreign tests on such URLs where "not foreign"
// means "the involved URLs are all file://".
if (aBaseDomain.IsEmpty() && !aScheme.EqualsLiteral("file")) {
return NS_ERROR_INVALID_ARG;
}
return NS_OK;
}

View File

@ -17,11 +17,12 @@ class nsIURI;
class ThirdPartyUtil final : public mozIThirdPartyUtil {
public:
NS_DECL_ISUPPORTS
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_MOZITHIRDPARTYUTIL
nsresult Init();
static void Startup();
static ThirdPartyUtil* GetInstance();
private:

View File

@ -20,6 +20,7 @@
#include "mozilla/dom/cache/PCacheChild.h"
#include "mozilla/dom/cache/ReadStream.h"
#include "mozilla/dom/cache/TypeUtils.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundUtils.h"
@ -38,6 +39,7 @@ namespace cache {
using mozilla::ErrorResult;
using mozilla::Unused;
using mozilla::dom::quota::QuotaManager;
using mozilla::ipc::BackgroundChild;
using mozilla::ipc::IProtocol;
using mozilla::ipc::PBackgroundChild;
@ -151,6 +153,12 @@ already_AddRefed<CacheStorage> CacheStorage::CreateOnMainThread(
return nullptr;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo))) {
NS_WARNING("CacheStorage not supported on invalid origins.");
RefPtr<CacheStorage> ref = new CacheStorage(NS_ERROR_DOM_SECURITY_ERR);
return ref.forget();
}
bool testingEnabled =
aForceTrustedOrigin ||
Preferences::GetBool("dom.caches.testing.enabled", false) ||
@ -191,6 +199,11 @@ already_AddRefed<CacheStorage> CacheStorage::CreateOnWorker(
const PrincipalInfo& principalInfo = aWorkerPrivate->GetPrincipalInfo();
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo))) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
// We have a number of cases where we want to skip the https scheme
// validation:
//

View File

@ -10,12 +10,14 @@
#include "mozilla/dom/cache/ActorUtils.h"
#include "mozilla/dom/cache/CacheOpParent.h"
#include "mozilla/dom/cache/ManagerId.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/ipc/PBackgroundParent.h"
namespace mozilla {
namespace dom {
namespace cache {
using mozilla::dom::quota::QuotaManager;
using mozilla::ipc::PBackgroundParent;
using mozilla::ipc::PrincipalInfo;
@ -23,6 +25,11 @@ using mozilla::ipc::PrincipalInfo;
PCacheStorageParent* AllocPCacheStorageParent(
PBackgroundParent* aManagingActor, Namespace aNamespace,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo) {
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
MOZ_ASSERT(false);
return nullptr;
}
return new CacheStorageParent(aManagingActor, aNamespace, aPrincipalInfo);
}

View File

@ -20,6 +20,7 @@
#include "mozilla/net/MozURL.h"
#include "mozIStorageConnection.h"
#include "mozIStorageStatement.h"
#include "mozIThirdPartyUtil.h"
#include "mozStorageHelper.h"
#include "nsCOMPtr.h"
#include "nsCRT.h"
@ -2493,9 +2494,17 @@ nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
// CSP is recovered from the headers, no need to initialise it here.
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
nsCString baseDomain;
rv = url->BaseDomain(baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
aSavedResponseOut->mValue.principalInfo() =
Some(mozilla::ipc::ContentPrincipalInfo(
attrs, origin, specNoSuffix, Nothing(), std::move(policies)));
attrs, origin, specNoSuffix, Nothing(), std::move(policies),
baseDomain));
}
bool nullPadding = false;

View File

@ -76,7 +76,14 @@ bool ClientManagerParent::DeallocPClientNavigateOpParent(
PClientSourceParent* ClientManagerParent::AllocPClientSourceParent(
const ClientSourceConstructorArgs& aArgs) {
return new ClientSourceParent(aArgs);
Maybe<ContentParentId> contentParentId;
uint64_t childID = BackgroundParent::GetChildID(Manager());
if (childID) {
contentParentId = Some(ContentParentId(childID));
}
return new ClientSourceParent(aArgs, contentParentId);
}
bool ClientManagerParent::DeallocPClientSourceParent(

View File

@ -611,5 +611,30 @@ RefPtr<ClientOpPromise> ClientManagerService::OpenWindow(
return promise.forget();
}
bool ClientManagerService::HasWindow(
const Maybe<ContentParentId>& aContentParentId,
const PrincipalInfo& aPrincipalInfo, const nsID& aClientId) {
AssertIsOnBackgroundThread();
ClientSourceParent* source = FindSource(aClientId, aPrincipalInfo);
if (!source) {
return false;
}
if (!source->ExecutionReady()) {
return false;
}
if (source->Info().Type() != ClientType::Window) {
return false;
}
if (aContentParentId && !source->IsOwnedByProcess(aContentParentId.value())) {
return false;
}
return true;
}
} // namespace dom
} // namespace mozilla

View File

@ -11,6 +11,12 @@
namespace mozilla {
namespace ipc {
class PrincipalInfo;
} // namespace ipc
namespace dom {
class ClientManagerParent;
@ -64,6 +70,10 @@ class ClientManagerService final {
const ClientOpenWindowArgs& aArgs,
already_AddRefed<ContentParent> aSourceProcess);
bool HasWindow(const Maybe<ContentParentId>& aContentParentId,
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
const nsID& aClientId);
NS_INLINE_DECL_REFCOUNTING(mozilla::dom::ClientManagerService)
};

View File

@ -201,9 +201,12 @@ bool ClientSourceParent::DeallocPClientSourceOpParent(
return true;
}
ClientSourceParent::ClientSourceParent(const ClientSourceConstructorArgs& aArgs)
ClientSourceParent::ClientSourceParent(
const ClientSourceConstructorArgs& aArgs,
const Maybe<ContentParentId>& aContentParentId)
: mClientInfo(aArgs.id(), aArgs.type(), aArgs.principalInfo(),
aArgs.creationTime()),
mContentParentId(aContentParentId),
mService(ClientManagerService::GetOrCreateInstance()),
mExecutionReady(false),
mFrozen(false) {}

View File

@ -10,6 +10,7 @@
#include "ClientOpPromise.h"
#include "mozilla/dom/PClientSourceParent.h"
#include "mozilla/dom/ServiceWorkerDescriptor.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/MozPromise.h"
namespace mozilla {
@ -21,6 +22,7 @@ class ClientManagerService;
class ClientSourceParent final : public PClientSourceParent {
ClientInfo mClientInfo;
Maybe<ServiceWorkerDescriptor> mController;
const Maybe<ContentParentId> mContentParentId;
RefPtr<ClientManagerService> mService;
nsTArray<ClientHandleParent*> mHandleList;
MozPromiseHolder<GenericPromise> mExecutionReadyPromise;
@ -54,7 +56,8 @@ class ClientSourceParent final : public PClientSourceParent {
bool DeallocPClientSourceOpParent(PClientSourceOpParent* aActor) override;
public:
explicit ClientSourceParent(const ClientSourceConstructorArgs& aArgs);
explicit ClientSourceParent(const ClientSourceConstructorArgs& aArgs,
const Maybe<ContentParentId>& aContentParentId);
~ClientSourceParent();
void Init();
@ -71,6 +74,10 @@ class ClientSourceParent final : public PClientSourceParent {
void ClearController();
bool IsOwnedByProcess(ContentParentId aContentParentId) const {
return mContentParentId && mContentParentId.value() == aContentParentId;
}
void AttachHandle(ClientHandleParent* aClientSource);
void DetachHandle(ClientHandleParent* aClientSource);

View File

@ -11,6 +11,7 @@ EXPORTS.mozilla.dom += [
'ClientIPCUtils.h',
'ClientManager.h',
'ClientManagerActors.h',
'ClientManagerService.h',
'ClientOpenWindowOpActors.h',
'ClientOpPromise.h',
'ClientSource.h',

View File

@ -272,6 +272,8 @@ const char kPrefFileHandleEnabled[] = "dom.fileHandle.enabled";
// origin.
#define IDB_DELETION_MARKER_FILE_PREFIX "idb-deleting-"
const uint32_t kDeleteTimeoutMs = 1000;
#ifdef DEBUG
const int32_t kDEBUGThreadPriority = nsISupportsPriority::PRIORITY_NORMAL;
@ -7665,17 +7667,6 @@ class GetFileReferencesHelper final : public Runnable {
NS_DECL_NSIRUNNABLE
};
class FlushPendingFileDeletionsRunnable final : public Runnable {
public:
FlushPendingFileDeletionsRunnable()
: Runnable("FlushPendingFileDeletionsRunnable") {}
private:
~FlushPendingFileDeletionsRunnable() override = default;
NS_DECL_NSIRUNNABLE
};
class PermissionRequestHelper final : public PermissionRequestBase,
public PIndexedDBPermissionRequestParent {
bool mActorDestroyed;
@ -7776,9 +7767,12 @@ class QuotaClient final : public mozilla::dom::quota::Client {
static QuotaClient* sInstance;
nsCOMPtr<nsIEventTarget> mBackgroundThread;
nsCOMPtr<nsITimer> mDeleteTimer;
nsTArray<RefPtr<Maintenance>> mMaintenanceQueue;
RefPtr<Maintenance> mCurrentMaintenance;
RefPtr<nsThreadPool> mMaintenanceThreadPool;
nsClassHashtable<nsRefPtrHashKey<FileManager>, nsTArray<int64_t>>
mPendingDeleteInfos;
bool mShutdownRequested;
public:
@ -7817,6 +7811,10 @@ class QuotaClient final : public mozilla::dom::quota::Client {
return mShutdownRequested;
}
nsresult AsyncDeleteFile(FileManager* aFileManager, int64_t aFileId);
nsresult FlushPendingFileDeletions();
already_AddRefed<Maintenance> GetCurrentMaintenance() const {
RefPtr<Maintenance> result = mCurrentMaintenance;
return result.forget();
@ -7866,13 +7864,11 @@ class QuotaClient final : public mozilla::dom::quota::Client {
void ShutdownWorkThreads() override;
void DidInitialize(QuotaManager* aQuotaManager) override;
void WillShutdown() override;
private:
~QuotaClient() override;
static void DeleteTimerCallback(nsITimer* aTimer, void* aClosure);
nsresult GetDirectory(PersistenceType aPersistenceType,
const nsACString& aOrigin, nsIFile** aDirectory);
@ -7895,6 +7891,66 @@ class QuotaClient final : public mozilla::dom::quota::Client {
void ProcessMaintenanceQueue();
};
class DeleteFilesRunnable final : public Runnable,
public OpenDirectoryListener {
typedef mozilla::dom::quota::DirectoryLock DirectoryLock;
enum State {
// Just created on the PBackground thread. Next step is
// State_DirectoryOpenPending.
State_Initial,
// Waiting for directory open allowed on the main thread. The next step is
// State_DatabaseWorkOpen.
State_DirectoryOpenPending,
// Waiting to do/doing work on the QuotaManager IO thread. The next step is
// State_UnblockingOpen.
State_DatabaseWorkOpen,
// Notifying the QuotaManager that it can proceed to the next operation on
// the main thread. Next step is State_Completed.
State_UnblockingOpen,
// All done.
State_Completed
};
nsCOMPtr<nsIEventTarget> mOwningEventTarget;
RefPtr<FileManager> mFileManager;
RefPtr<DirectoryLock> mDirectoryLock;
nsCOMPtr<nsIFile> mDirectory;
nsCOMPtr<nsIFile> mJournalDirectory;
nsTArray<int64_t> mFileIds;
State mState;
public:
DeleteFilesRunnable(FileManager* aFileManager, nsTArray<int64_t>&& aFileIds);
void RunImmediately();
private:
~DeleteFilesRunnable() = default;
nsresult Open();
nsresult DeleteFile(int64_t aFileId);
nsresult DoDatabaseWork();
void Finish();
void UnblockOpen();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIRUNNABLE
// OpenDirectoryListener overrides.
virtual void DirectoryLockAcquired(DirectoryLock* aLock) override;
virtual void DirectoryLockFailed() override;
};
class Maintenance final : public Runnable, public OpenDirectoryListener {
struct DirectoryInfo;
@ -9145,10 +9201,12 @@ bool DeallocPBackgroundIndexedDBUtilsParent(
bool RecvFlushPendingFileDeletions() {
AssertIsOnBackgroundThread();
RefPtr<FlushPendingFileDeletionsRunnable> runnable =
new FlushPendingFileDeletionsRunnable();
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
QuotaClient* quotaClient = QuotaClient::GetInstance();
if (quotaClient) {
if (NS_FAILED(quotaClient->FlushPendingFileDeletions())) {
NS_WARNING("Failed to flush pending file deletions!");
}
}
return true;
}
@ -9216,6 +9274,20 @@ FileHandleThreadPool* GetFileHandleThreadPool() {
return gFileHandleThreadPool;
}
nsresult AsyncDeleteFile(FileManager* aFileManager, int64_t aFileId) {
AssertIsOnBackgroundThread();
QuotaClient* quotaClient = QuotaClient::GetInstance();
if (quotaClient) {
nsresult rv = quotaClient->AsyncDeleteFile(aFileManager, aFileId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
return NS_OK;
}
/*******************************************************************************
* DatabaseConnection implementation
******************************************************************************/
@ -12177,6 +12249,11 @@ Factory::AllocPBackgroundIDBFactoryRequestParent(
return nullptr;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo))) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
RefPtr<ContentParent> contentParent =
BackgroundParent::GetContentParent(Manager());
@ -15653,7 +15730,8 @@ nsresult FileManager::GetUsage(nsIFile* aDirectory, uint64_t* aUsage) {
QuotaClient* QuotaClient::sInstance = nullptr;
QuotaClient::QuotaClient() : mShutdownRequested(false) {
QuotaClient::QuotaClient()
: mDeleteTimer(NS_NewTimer()), mShutdownRequested(false) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(!sInstance, "We expect this to be a singleton!");
MOZ_ASSERT(!gTelemetryIdMutex);
@ -15679,6 +15757,44 @@ QuotaClient::~QuotaClient() {
sInstance = nullptr;
}
nsresult QuotaClient::AsyncDeleteFile(FileManager* aFileManager,
int64_t aFileId) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mDeleteTimer);
MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
nsresult rv = mDeleteTimer->InitWithNamedFuncCallback(
DeleteTimerCallback, this, kDeleteTimeoutMs, nsITimer::TYPE_ONE_SHOT,
"dom::indexeddb::QuotaClient::AsyncDeleteFile");
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsTArray<int64_t>* array;
if (!mPendingDeleteInfos.Get(aFileManager, &array)) {
array = new nsTArray<int64_t>();
mPendingDeleteInfos.Put(aFileManager, array);
}
array->AppendElement(aFileId);
return NS_OK;
}
nsresult QuotaClient::FlushPendingFileDeletions() {
AssertIsOnBackgroundThread();
nsresult rv = mDeleteTimer->Cancel();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
DeleteTimerCallback(mDeleteTimer, this);
return NS_OK;
}
nsThreadPool* QuotaClient::GetOrCreateThreadPool() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(!mShutdownRequested);
@ -16129,22 +16245,37 @@ void QuotaClient::ShutdownWorkThreads() {
mMaintenanceThreadPool->Shutdown();
mMaintenanceThreadPool = nullptr;
}
}
void QuotaClient::DidInitialize(QuotaManager* aQuotaManager) {
MOZ_ASSERT(NS_IsMainThread());
if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
mgr->NoteLiveQuotaManager(aQuotaManager);
if (mDeleteTimer) {
MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
mDeleteTimer = nullptr;
}
}
void QuotaClient::WillShutdown() {
MOZ_ASSERT(NS_IsMainThread());
void QuotaClient::DeleteTimerCallback(nsITimer* aTimer, void* aClosure) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aTimer);
if (IndexedDatabaseManager* mgr = IndexedDatabaseManager::Get()) {
mgr->NoteShuttingDownQuotaManager();
auto* self = static_cast<QuotaClient*>(aClosure);
MOZ_ASSERT(self);
MOZ_ASSERT(self->mDeleteTimer);
MOZ_ASSERT(SameCOMIdentity(self->mDeleteTimer, aTimer));
for (auto iter = self->mPendingDeleteInfos.ConstIter(); !iter.Done();
iter.Next()) {
auto key = iter.Key();
auto value = iter.Data();
MOZ_ASSERT(!value->IsEmpty());
RefPtr<DeleteFilesRunnable> runnable =
new DeleteFilesRunnable(key, std::move(*value));
MOZ_ASSERT(value->IsEmpty());
runnable->RunImmediately();
}
self->mPendingDeleteInfos.Clear();
}
nsresult QuotaClient::GetDirectory(PersistenceType aPersistenceType,
@ -16393,6 +16524,182 @@ void QuotaClient::ProcessMaintenanceQueue() {
mCurrentMaintenance->RunImmediately();
}
/*******************************************************************************
* DeleteFilesRunnable
******************************************************************************/
DeleteFilesRunnable::DeleteFilesRunnable(FileManager* aFileManager,
nsTArray<int64_t>&& aFileIds)
: Runnable("dom::indexeddb::DeleteFilesRunnable"),
mOwningEventTarget(GetCurrentThreadEventTarget()),
mFileManager(aFileManager),
mFileIds(std::move(aFileIds)),
mState(State_Initial) {}
void DeleteFilesRunnable::RunImmediately() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_Initial);
Unused << this->Run();
}
nsresult DeleteFilesRunnable::Open() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_Initial);
QuotaManager* quotaManager = QuotaManager::Get();
if (NS_WARN_IF(!quotaManager)) {
return NS_ERROR_FAILURE;
}
mState = State_DirectoryOpenPending;
quotaManager->OpenDirectory(mFileManager->Type(), mFileManager->Group(),
mFileManager->Origin(), quota::Client::IDB,
/* aExclusive */ false, this);
return NS_OK;
}
nsresult DeleteFilesRunnable::DeleteFile(int64_t aFileId) {
MOZ_ASSERT(mDirectory);
MOZ_ASSERT(mJournalDirectory);
nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(mDirectory, aFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
nsresult rv;
int64_t fileSize;
if (mFileManager->EnforcingQuota()) {
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
}
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
if (mFileManager->EnforcingQuota()) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(), fileSize);
}
file = mFileManager->GetFileForId(mJournalDirectory, aFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult DeleteFilesRunnable::DoDatabaseWork() {
AssertIsOnIOThread();
MOZ_ASSERT(mState == State_DatabaseWorkOpen);
if (!mFileManager->Invalidated()) {
mDirectory = mFileManager->GetDirectory();
if (NS_WARN_IF(!mDirectory)) {
return NS_ERROR_FAILURE;
}
mJournalDirectory = mFileManager->GetJournalDirectory();
if (NS_WARN_IF(!mJournalDirectory)) {
return NS_ERROR_FAILURE;
}
for (int64_t fileId : mFileIds) {
if (NS_FAILED(DeleteFile(fileId))) {
NS_WARNING("Failed to delete file!");
}
}
}
Finish();
return NS_OK;
}
void DeleteFilesRunnable::Finish() {
// Must set mState before dispatching otherwise we will race with the main
// thread.
mState = State_UnblockingOpen;
MOZ_ALWAYS_SUCCEEDS(mOwningEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
}
void DeleteFilesRunnable::UnblockOpen() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_UnblockingOpen);
mDirectoryLock = nullptr;
mState = State_Completed;
}
NS_IMPL_ISUPPORTS_INHERITED0(DeleteFilesRunnable, Runnable)
NS_IMETHODIMP
DeleteFilesRunnable::Run() {
nsresult rv;
switch (mState) {
case State_Initial:
rv = Open();
break;
case State_DatabaseWorkOpen:
rv = DoDatabaseWork();
break;
case State_UnblockingOpen:
UnblockOpen();
return NS_OK;
case State_DirectoryOpenPending:
default:
MOZ_CRASH("Should never get here!");
}
if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) {
Finish();
}
return NS_OK;
}
void DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock* aLock) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_DirectoryOpenPending);
MOZ_ASSERT(!mDirectoryLock);
mDirectoryLock = aLock;
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
// Must set this before dispatching otherwise we will race with the IO thread
mState = State_DatabaseWorkOpen;
nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
Finish();
return;
}
}
void DeleteFilesRunnable::DirectoryLockFailed() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_DirectoryOpenPending);
MOZ_ASSERT(!mDirectoryLock);
Finish();
}
void Maintenance::RegisterDatabaseMaintenance(
DatabaseMaintenance* aDatabaseMaintenance) {
AssertIsOnBackgroundThread();
@ -26298,23 +26605,6 @@ GetFileReferencesHelper::Run() {
return NS_OK;
}
NS_IMETHODIMP
FlushPendingFileDeletionsRunnable::Run() {
MOZ_ASSERT(NS_IsMainThread());
RefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
if (NS_WARN_IF(!mgr)) {
return NS_ERROR_FAILURE;
}
nsresult rv = mgr->FlushPendingFileDeletions();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
void PermissionRequestHelper::OnPromptComplete(
PermissionValue aPermissionValue) {
MOZ_ASSERT(NS_IsMainThread());

View File

@ -7,6 +7,10 @@
#ifndef mozilla_dom_indexeddb_actorsparent_h__
#define mozilla_dom_indexeddb_actorsparent_h__
#include "nscore.h"
#include <stdint.h>
template <class>
struct already_AddRefed;
class nsIPrincipal;
@ -25,6 +29,7 @@ class Client;
namespace indexedDB {
class FileManager;
class LoggingInfo;
class PBackgroundIDBFactoryParent;
class PBackgroundIndexedDBUtilsParent;
@ -58,6 +63,8 @@ already_AddRefed<mozilla::dom::quota::Client> CreateQuotaClient();
FileHandleThreadPool* GetFileHandleThreadPool();
nsresult AsyncDeleteFile(FileManager* aFileManager, int64_t aFileId);
} // namespace indexedDB
} // namespace dom
} // namespace mozilla

View File

@ -6,12 +6,14 @@
#include "FileInfo.h"
#include "ActorsParent.h"
#include "FileManager.h"
#include "IndexedDatabaseManager.h"
#include "mozilla/Assertions.h"
#include "mozilla/Attributes.h"
#include "mozilla/Mutex.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "nsError.h"
#include "nsThreadUtils.h"
@ -20,6 +22,7 @@ namespace dom {
namespace indexedDB {
using namespace mozilla::dom::quota;
using namespace mozilla::ipc;
namespace {
@ -40,29 +43,6 @@ class FileInfoImpl final : public FileInfo {
virtual int64_t Id() const override { return int64_t(mFileId); }
};
class CleanupFileRunnable final : public Runnable {
RefPtr<FileManager> mFileManager;
int64_t mFileId;
public:
static void DoCleanup(FileManager* aFileManager, int64_t aFileId);
CleanupFileRunnable(FileManager* aFileManager, int64_t aFileId)
: Runnable("dom::indexedDB::CleanupFileRunnable"),
mFileManager(aFileManager),
mFileId(aFileId) {
MOZ_ASSERT(aFileManager);
MOZ_ASSERT(aFileId > 0);
}
NS_INLINE_DECL_REFCOUNTING_INHERITED(CleanupFileRunnable, Runnable);
private:
~CleanupFileRunnable() {}
NS_DECL_NSIRUNNABLE
};
} // namespace
FileInfo::FileInfo(FileManager* aFileManager) : mFileManager(aFileManager) {
@ -180,48 +160,15 @@ bool FileInfo::LockedClearDBRefs() {
}
void FileInfo::Cleanup() {
AssertIsOnBackgroundThread();
int64_t id = Id();
// IndexedDatabaseManager is main-thread only.
if (!NS_IsMainThread()) {
RefPtr<CleanupFileRunnable> cleaner =
new CleanupFileRunnable(mFileManager, id);
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(cleaner));
return;
}
CleanupFileRunnable::DoCleanup(mFileManager, id);
}
// static
void CleanupFileRunnable::DoCleanup(FileManager* aFileManager,
int64_t aFileId) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aFileManager);
MOZ_ASSERT(aFileId > 0);
if (NS_WARN_IF(QuotaManager::IsShuttingDown())) {
return;
}
RefPtr<IndexedDatabaseManager> mgr = IndexedDatabaseManager::Get();
MOZ_ASSERT(mgr);
if (NS_FAILED(mgr->AsyncDeleteFile(aFileManager, aFileId))) {
if (NS_FAILED(AsyncDeleteFile(mFileManager, id))) {
NS_WARNING("Failed to delete file asynchronously!");
}
}
NS_IMETHODIMP
CleanupFileRunnable::Run() {
MOZ_ASSERT(NS_IsMainThread());
DoCleanup(mFileManager, mFileId);
return NS_OK;
}
/* static */
already_AddRefed<nsIFile> FileInfo::GetFileForFileInfo(FileInfo* aFileInfo) {
FileManager* fileManager = aFileInfo->Manager();

View File

@ -133,6 +133,11 @@ nsresult IDBFactory::CreateForWindow(nsPIDOMWindowInner* aWindow,
MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
IDB_REPORT_INTERNAL_ERR();
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
nsCOMPtr<nsIWebNavigation> webNav = do_GetInterface(aWindow);
nsCOMPtr<nsILoadContext> loadContext = do_QueryInterface(webNav);
@ -169,6 +174,10 @@ nsresult IDBFactory::CreateForMainThreadJS(JSContext* aCx,
return rv;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
return NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR;
}
rv = CreateForMainThreadJSInternal(aCx, aOwningObject, principalInfo,
aFactory);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -584,6 +593,12 @@ already_AddRefed<IDBOpenDBRequest> IDBFactory::OpenInternal(
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(principalInfo))) {
IDB_REPORT_INTERNAL_ERR();
aRv.Throw(NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return nullptr;
}
} else {
principalInfo = *mPrincipalInfo;
}

View File

@ -9,32 +9,25 @@
#include "chrome/common/ipc_channel.h" // for IPC::Channel::kMaximumMessageSize
#include "nsIConsoleService.h"
#include "nsIDOMWindow.h"
#include "nsIEventTarget.h"
#include "nsIFile.h"
#include "nsIScriptError.h"
#include "nsIScriptGlobalObject.h"
#include "jsapi.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/CondVar.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/ErrorEvent.h"
#include "mozilla/dom/ErrorEventBinding.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/dom/WorkerScope.h"
#include "mozilla/dom/WorkerPrivate.h"
#include "mozilla/ipc/BackgroundChild.h"
#include "mozilla/ipc/BackgroundParent.h"
#include "mozilla/ipc/PBackgroundChild.h"
#include "nsContentUtils.h"
#include "nsGlobalWindow.h"
#include "nsThreadUtils.h"
#include "mozilla/Logging.h"
#include "FileInfo.h"
#include "FileManager.h"
#include "IDBEvents.h"
#include "IDBFactory.h"
@ -155,69 +148,6 @@ Atomic<bool> gPrefErrorEventToSelfError(false);
Atomic<int32_t> gDataThresholdBytes(0);
Atomic<int32_t> gMaxSerializedMsgSize(0);
class DeleteFilesRunnable final : public nsIRunnable,
public OpenDirectoryListener {
typedef mozilla::dom::quota::DirectoryLock DirectoryLock;
enum State {
// Just created on the main thread. Next step is State_DirectoryOpenPending.
State_Initial,
// Waiting for directory open allowed on the main thread. The next step is
// State_DatabaseWorkOpen.
State_DirectoryOpenPending,
// Waiting to do/doing work on the QuotaManager IO thread. The next step is
// State_UnblockingOpen.
State_DatabaseWorkOpen,
// Notifying the QuotaManager that it can proceed to the next operation on
// the main thread. Next step is State_Completed.
State_UnblockingOpen,
// All done.
State_Completed
};
nsCOMPtr<nsIEventTarget> mBackgroundThread;
RefPtr<FileManager> mFileManager;
nsTArray<int64_t> mFileIds;
RefPtr<DirectoryLock> mDirectoryLock;
nsCOMPtr<nsIFile> mDirectory;
nsCOMPtr<nsIFile> mJournalDirectory;
State mState;
public:
DeleteFilesRunnable(nsIEventTarget* aBackgroundThread,
FileManager* aFileManager, nsTArray<int64_t>& aFileIds);
void Dispatch();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIRUNNABLE
virtual void DirectoryLockAcquired(DirectoryLock* aLock) override;
virtual void DirectoryLockFailed() override;
private:
~DeleteFilesRunnable() {}
nsresult Open();
nsresult DeleteFile(int64_t aFileId);
nsresult DoDatabaseWork();
void Finish();
void UnblockOpen();
};
void AtomicBoolPrefChangedCallback(const char* aPrefName,
Atomic<bool>* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
@ -317,17 +247,6 @@ IndexedDatabaseManager* IndexedDatabaseManager::Get() {
nsresult IndexedDatabaseManager::Init() {
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
// During Init() we can't yet call IsMainProcess(), just check sIsMainProcess
// directly.
if (sIsMainProcess) {
mDeleteTimer = NS_NewTimer();
NS_ENSURE_STATE(mDeleteTimer);
if (QuotaManager* quotaManager = QuotaManager::Get()) {
NoteLiveQuotaManager(quotaManager);
}
}
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
kTestingPref, &gTestingMode);
Preferences::RegisterCallbackAndCall(AtomicBoolPrefChangedCallback,
@ -391,14 +310,6 @@ void IndexedDatabaseManager::Destroy() {
NS_ERROR("Shutdown more than once?!");
}
if (sIsMainProcess && mDeleteTimer) {
if (NS_FAILED(mDeleteTimer->Cancel())) {
NS_WARNING("Failed to cancel timer!");
}
mDeleteTimer = nullptr;
}
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback, kTestingPref,
&gTestingMode);
Preferences::UnregisterCallback(AtomicBoolPrefChangedCallback,
@ -707,24 +618,6 @@ void IndexedDatabaseManager::ClearBackgroundActor() {
mBackgroundActor = nullptr;
}
void IndexedDatabaseManager::NoteLiveQuotaManager(QuotaManager* aQuotaManager) {
// This can be called during Init, so we can't use IsMainProcess() yet.
MOZ_ASSERT(sIsMainProcess);
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aQuotaManager);
mBackgroundThread = aQuotaManager->OwningThread();
}
void IndexedDatabaseManager::NoteShuttingDownQuotaManager() {
MOZ_ASSERT(IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ALWAYS_SUCCEEDS(mDeleteTimer->Cancel());
mBackgroundThread = nullptr;
}
already_AddRefed<FileManager> IndexedDatabaseManager::GetFileManager(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName) {
@ -801,40 +694,6 @@ void IndexedDatabaseManager::InvalidateFileManager(
}
}
nsresult IndexedDatabaseManager::AsyncDeleteFile(FileManager* aFileManager,
int64_t aFileId) {
MOZ_ASSERT(IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aFileManager);
MOZ_ASSERT(aFileId > 0);
MOZ_ASSERT(mDeleteTimer);
if (!mBackgroundThread) {
return NS_OK;
}
nsresult rv = mDeleteTimer->Cancel();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = mDeleteTimer->InitWithCallback(this, kDeleteTimeoutMs,
nsITimer::TYPE_ONE_SHOT);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsTArray<int64_t>* array;
if (!mPendingDeleteInfos.Get(aFileManager, &array)) {
array = new nsTArray<int64_t>();
mPendingDeleteInfos.Put(aFileManager, array);
}
array->AppendElement(aFileId);
return NS_OK;
}
nsresult IndexedDatabaseManager::BlockAndGetFileReferences(
PersistenceType aPersistenceType, const nsACString& aOrigin,
const nsAString& aDatabaseName, int64_t aFileId, int32_t* aRefCnt,
@ -883,25 +742,13 @@ nsresult IndexedDatabaseManager::FlushPendingFileDeletions() {
return NS_ERROR_UNEXPECTED;
}
if (IsMainProcess()) {
nsresult rv = mDeleteTimer->Cancel();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
if (NS_WARN_IF(!bgActor)) {
return NS_ERROR_FAILURE;
}
rv = Notify(mDeleteTimer);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
} else {
PBackgroundChild* bgActor = BackgroundChild::GetForCurrentThread();
if (NS_WARN_IF(!bgActor)) {
return NS_ERROR_FAILURE;
}
if (!bgActor->SendFlushPendingFileDeletions()) {
return NS_ERROR_FAILURE;
}
if (!bgActor->SendFlushPendingFileDeletions()) {
return NS_ERROR_FAILURE;
}
return NS_OK;
@ -950,40 +797,6 @@ const nsCString& IndexedDatabaseManager::GetLocale() {
return idbManager->mLocale;
}
NS_IMPL_ADDREF(IndexedDatabaseManager)
NS_IMPL_RELEASE_WITH_DESTROY(IndexedDatabaseManager, Destroy())
NS_IMPL_QUERY_INTERFACE(IndexedDatabaseManager, nsITimerCallback, nsINamed)
NS_IMETHODIMP
IndexedDatabaseManager::Notify(nsITimer* aTimer) {
MOZ_ASSERT(IsMainProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mBackgroundThread);
for (auto iter = mPendingDeleteInfos.ConstIter(); !iter.Done(); iter.Next()) {
auto key = iter.Key();
auto value = iter.Data();
MOZ_ASSERT(!value->IsEmpty());
RefPtr<DeleteFilesRunnable> runnable =
new DeleteFilesRunnable(mBackgroundThread, key, *value);
MOZ_ASSERT(value->IsEmpty());
runnable->Dispatch();
}
mPendingDeleteInfos.Clear();
return NS_OK;
}
NS_IMETHODIMP
IndexedDatabaseManager::GetName(nsACString& aName) {
aName.AssignLiteral("IndexedDatabaseManager");
return NS_OK;
}
already_AddRefed<FileManager> FileManagerInfo::GetFileManager(
PersistenceType aPersistenceType, const nsAString& aName) const {
AssertIsOnIOThread();
@ -1076,178 +889,5 @@ nsTArray<RefPtr<FileManager> >& FileManagerInfo::GetArray(
}
}
DeleteFilesRunnable::DeleteFilesRunnable(nsIEventTarget* aBackgroundThread,
FileManager* aFileManager,
nsTArray<int64_t>& aFileIds)
: mBackgroundThread(aBackgroundThread),
mFileManager(aFileManager),
mState(State_Initial) {
mFileIds.SwapElements(aFileIds);
}
void DeleteFilesRunnable::Dispatch() {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(mState == State_Initial);
MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
}
NS_IMPL_ISUPPORTS(DeleteFilesRunnable, nsIRunnable)
NS_IMETHODIMP
DeleteFilesRunnable::Run() {
nsresult rv;
switch (mState) {
case State_Initial:
rv = Open();
break;
case State_DatabaseWorkOpen:
rv = DoDatabaseWork();
break;
case State_UnblockingOpen:
UnblockOpen();
return NS_OK;
case State_DirectoryOpenPending:
default:
MOZ_CRASH("Should never get here!");
}
if (NS_WARN_IF(NS_FAILED(rv)) && mState != State_UnblockingOpen) {
Finish();
}
return NS_OK;
}
void DeleteFilesRunnable::DirectoryLockAcquired(DirectoryLock* aLock) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_DirectoryOpenPending);
MOZ_ASSERT(!mDirectoryLock);
mDirectoryLock = aLock;
QuotaManager* quotaManager = QuotaManager::Get();
MOZ_ASSERT(quotaManager);
// Must set this before dispatching otherwise we will race with the IO thread
mState = State_DatabaseWorkOpen;
nsresult rv = quotaManager->IOThread()->Dispatch(this, NS_DISPATCH_NORMAL);
if (NS_WARN_IF(NS_FAILED(rv))) {
Finish();
return;
}
}
void DeleteFilesRunnable::DirectoryLockFailed() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_DirectoryOpenPending);
MOZ_ASSERT(!mDirectoryLock);
Finish();
}
nsresult DeleteFilesRunnable::Open() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_Initial);
QuotaManager* quotaManager = QuotaManager::Get();
if (NS_WARN_IF(!quotaManager)) {
return NS_ERROR_FAILURE;
}
mState = State_DirectoryOpenPending;
quotaManager->OpenDirectory(mFileManager->Type(), mFileManager->Group(),
mFileManager->Origin(), quota::Client::IDB,
/* aExclusive */ false, this);
return NS_OK;
}
nsresult DeleteFilesRunnable::DeleteFile(int64_t aFileId) {
MOZ_ASSERT(mDirectory);
MOZ_ASSERT(mJournalDirectory);
nsCOMPtr<nsIFile> file = mFileManager->GetFileForId(mDirectory, aFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
nsresult rv;
int64_t fileSize;
if (mFileManager->EnforcingQuota()) {
rv = file->GetFileSize(&fileSize);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
}
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
if (mFileManager->EnforcingQuota()) {
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "Shouldn't be null!");
quotaManager->DecreaseUsageForOrigin(mFileManager->Type(),
mFileManager->Group(),
mFileManager->Origin(), fileSize);
}
file = mFileManager->GetFileForId(mJournalDirectory, aFileId);
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
rv = file->Remove(false);
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
nsresult DeleteFilesRunnable::DoDatabaseWork() {
AssertIsOnIOThread();
MOZ_ASSERT(mState == State_DatabaseWorkOpen);
if (!mFileManager->Invalidated()) {
mDirectory = mFileManager->GetDirectory();
if (NS_WARN_IF(!mDirectory)) {
return NS_ERROR_FAILURE;
}
mJournalDirectory = mFileManager->GetJournalDirectory();
if (NS_WARN_IF(!mJournalDirectory)) {
return NS_ERROR_FAILURE;
}
for (int64_t fileId : mFileIds) {
if (NS_FAILED(DeleteFile(fileId))) {
NS_WARNING("Failed to delete file!");
}
}
}
Finish();
return NS_OK;
}
void DeleteFilesRunnable::Finish() {
// Must set mState before dispatching otherwise we will race with the main
// thread.
mState = State_UnblockingOpen;
MOZ_ALWAYS_SUCCEEDS(mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL));
}
void DeleteFilesRunnable::UnblockOpen() {
AssertIsOnBackgroundThread();
MOZ_ASSERT(mState == State_UnblockingOpen);
mDirectoryLock = nullptr;
mState = State_Completed;
}
} // namespace dom
} // namespace mozilla

View File

@ -12,12 +12,7 @@
#include "mozilla/dom/quota/PersistenceType.h"
#include "mozilla/Mutex.h"
#include "nsClassHashtable.h"
#include "nsCOMPtr.h"
#include "nsHashKeys.h"
#include "nsINamed.h"
#include "nsITimer.h"
class nsIEventTarget;
namespace mozilla {
@ -27,12 +22,6 @@ namespace dom {
class IDBFactory;
namespace quota {
class QuotaManager;
} // namespace quota
namespace indexedDB {
class BackgroundUtilsChild;
@ -41,9 +30,8 @@ class FileManagerInfo;
} // namespace indexedDB
class IndexedDatabaseManager final : public nsITimerCallback, public nsINamed {
class IndexedDatabaseManager final {
typedef mozilla::dom::quota::PersistenceType PersistenceType;
typedef mozilla::dom::quota::QuotaManager QuotaManager;
typedef mozilla::dom::indexedDB::FileManager FileManager;
typedef mozilla::dom::indexedDB::FileManagerInfo FileManagerInfo;
@ -56,9 +44,7 @@ class IndexedDatabaseManager final : public nsITimerCallback, public nsINamed {
Logging_DetailedProfilerMarks
};
NS_DECL_ISUPPORTS
NS_DECL_NSITIMERCALLBACK
NS_DECL_NSINAMED
NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(IndexedDatabaseManager, Destroy())
// Returns a non-owning reference.
static IndexedDatabaseManager* GetOrCreate();
@ -111,10 +97,6 @@ class IndexedDatabaseManager final : public nsITimerCallback, public nsINamed {
void ClearBackgroundActor();
void NoteLiveQuotaManager(QuotaManager* aQuotaManager);
void NoteShuttingDownQuotaManager();
already_AddRefed<FileManager> GetFileManager(PersistenceType aPersistenceType,
const nsACString& aOrigin,
const nsAString& aDatabaseName);
@ -130,8 +112,6 @@ class IndexedDatabaseManager final : public nsITimerCallback, public nsINamed {
const nsACString& aOrigin,
const nsAString& aDatabaseName);
nsresult AsyncDeleteFile(FileManager* aFileManager, int64_t aFileId);
// Don't call this method in real code, it blocks the main thread!
// It is intended to be used by mochitests to test correctness of the special
// reference counting of stored blobs/files.
@ -171,10 +151,6 @@ class IndexedDatabaseManager final : public nsITimerCallback, public nsINamed {
static void LoggingModePrefChangedCallback(const char* aPrefName,
void* aClosure);
nsCOMPtr<nsIEventTarget> mBackgroundThread;
nsCOMPtr<nsITimer> mDeleteTimer;
// Maintains a list of all file managers per origin. This list isn't
// protected by any mutex but it is only ever touched on the IO thread.
nsClassHashtable<nsCStringHashKey, FileManagerInfo> mFileManagerInfos;

View File

@ -5320,8 +5320,8 @@ nsresult ContentParent::AboutToLoadHttpFtpDocumentForChild(
nsCOMPtr<nsISupports> dummy;
rv = lsm->Preload(principal, nullptr, getter_AddRefs(dummy));
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
if (NS_FAILED(rv)) {
NS_WARNING("Failed to preload local storage!");
}
}

View File

@ -17,6 +17,7 @@
#include "mozilla/Telemetry.h"
#include "mozilla/Unused.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/ClientManagerService.h"
#include "mozilla/dom/PBackgroundLSDatabaseParent.h"
#include "mozilla/dom/PBackgroundLSObserverParent.h"
#include "mozilla/dom/PBackgroundLSRequestParent.h"
@ -181,9 +182,15 @@ const uint32_t kFlushTimeoutMs = 5000;
const char kPrivateBrowsingObserverTopic[] = "last-pb-context-exited";
const uint32_t kDefaultNextGen = false;
const uint32_t kDefaultOriginLimitKB = 5 * 1024;
const uint32_t kDefaultShadowWrites = true;
const uint32_t kDefaultSnapshotPrefill = 4096;
const uint32_t kDefaultClientValidation = true;
/**
*
*/
const char kNextGenPref[] = "dom.storage.next_gen";
/**
* LocalStorage data limit as determined by summing up the lengths of all string
* keys and values. This is consistent with the legacy implementation and other
@ -209,6 +216,8 @@ const char kShadowWritesPref[] = "dom.storage.shadow_writes";
*/
const char kSnapshotPrefillPref[] = "dom.storage.snapshot_prefill";
const char kClientValidationPref[] = "dom.storage.client_validation";
/**
* The amount of time a PreparedDatastore instance should stick around after a
* preload is triggered in order to give time for the page to use LocalStorage
@ -2228,7 +2237,7 @@ class PrepareDatastoreOp : public LSRequestBase, public OpenDirectoryListener {
LoadDataOp* mLoadDataOp;
nsDataHashtable<nsStringHashKey, nsString> mValues;
nsTArray<LSItemInfo> mOrderedItems;
const LSRequestPrepareDatastoreParams mParams;
const LSRequestCommonParams mParams;
Maybe<ContentParentId> mContentParentId;
nsCString mSuffix;
nsCString mGroup;
@ -2241,6 +2250,7 @@ class PrepareDatastoreOp : public LSRequestBase, public OpenDirectoryListener {
int64_t mSizeOfKeys;
int64_t mSizeOfItems;
NestedState mNestedState;
const bool mCreateIfNotExists;
bool mDatabaseNotAvailable;
bool mRequestedDirectoryLock;
bool mInvalidated;
@ -2490,9 +2500,10 @@ class ArchivedOriginScope {
DataType mData;
public:
static ArchivedOriginScope* CreateFromOrigin(nsIPrincipal* aPrincipal);
static ArchivedOriginScope* CreateFromOrigin(
const nsACString& aOriginAttrSuffix, const nsACString& aOriginKey);
static ArchivedOriginScope* CreateFromPrefix(nsIPrincipal* aPrincipal);
static ArchivedOriginScope* CreateFromPrefix(const nsACString& aOriginKey);
static ArchivedOriginScope* CreateFromPattern(
const OriginAttributesPattern& aPattern);
@ -2547,37 +2558,6 @@ class ArchivedOriginScope {
explicit ArchivedOriginScope(const Null&& aNull) : mData(aNull) {}
};
class ArchivedOriginScopeHelper : public Runnable {
Monitor mMonitor;
const OriginAttributes mAttrs;
const nsCString mSpec;
nsAutoPtr<ArchivedOriginScope> mArchivedOriginScope;
nsresult mMainThreadResultCode;
bool mWaiting;
bool mPrefix;
public:
ArchivedOriginScopeHelper(const nsACString& aSpec,
const OriginAttributes& aAttrs, bool aPrefix)
: Runnable("dom::localstorage::ArchivedOriginScopeHelper"),
mMonitor("ArchivedOriginScopeHelper::mMonitor"),
mAttrs(aAttrs),
mSpec(aSpec),
mMainThreadResultCode(NS_OK),
mWaiting(true),
mPrefix(aPrefix) {
AssertIsOnIOThread();
}
nsresult BlockAndReturnArchivedOriginScope(
nsAutoPtr<ArchivedOriginScope>& aArchivedOriginScope);
private:
nsresult RunOnMainThread();
NS_DECL_NSIRUNNABLE
};
class QuotaClient final : public mozilla::dom::quota::Client {
class Observer;
class MatchFunction;
@ -2743,9 +2723,11 @@ typedef nsClassHashtable<nsCStringHashKey, nsTArray<Observer*>>
StaticAutoPtr<ObserverHashtable> gObservers;
Atomic<bool> gNextGen(kDefaultNextGen);
Atomic<uint32_t, Relaxed> gOriginLimitKB(kDefaultOriginLimitKB);
Atomic<bool> gShadowWrites(kDefaultShadowWrites);
Atomic<int32_t, Relaxed> gSnapshotPrefill(kDefaultSnapshotPrefill);
Atomic<bool> gClientValidation(kDefaultClientValidation);
typedef nsDataHashtable<nsCStringHashKey, int64_t> UsageHashtable;
@ -2932,6 +2914,15 @@ void SnapshotPrefillPrefChangedCallback(const char* aPrefName, void* aClosure) {
gSnapshotPrefill = snapshotPrefill;
}
void ClientValidationPrefChangedCallback(const char* aPrefName,
void* aClosure) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aPrefName, kClientValidationPref));
MOZ_ASSERT(!aClosure);
gClientValidation = Preferences::GetBool(aPrefName, kDefaultClientValidation);
}
} // namespace
/*******************************************************************************
@ -2955,6 +2946,11 @@ void InitializeLocalStorage() {
NS_WARNING("Failed to initialize quota client!");
}
if (NS_FAILED(Preferences::AddAtomicBoolVarCache(&gNextGen, kNextGenPref,
kDefaultNextGen))) {
NS_WARNING("Unable to respond to next gen pref changes!");
}
if (NS_FAILED(Preferences::AddAtomicUintVarCache(
&gOriginLimitKB, kDefaultQuotaPref, kDefaultOriginLimitKB))) {
NS_WARNING("Unable to respond to default quota pref changes!");
@ -2966,11 +2962,16 @@ void InitializeLocalStorage() {
Preferences::RegisterCallbackAndCall(SnapshotPrefillPrefChangedCallback,
kSnapshotPrefillPref);
Preferences::RegisterCallbackAndCall(ClientValidationPrefChangedCallback,
kClientValidationPref);
#ifdef DEBUG
gLocalStorageInitialized = true;
#endif
}
bool GetCurrentNextGenPrefValue() { return gNextGen; }
PBackgroundLSDatabaseParent* AllocPBackgroundLSDatabaseParent(
const PrincipalInfo& aPrincipalInfo, const uint32_t& aPrivateBrowsingId,
const uint64_t& aDatastoreId) {
@ -3111,14 +3112,144 @@ bool DeallocPBackgroundLSObserverParent(PBackgroundLSObserverParent* aActor) {
return true;
}
bool VerifyPrincipalInfo(const Maybe<ContentParentId>& aContentParentId,
const PrincipalInfo& aPrincipalInfo,
const Maybe<nsID>& aClientId) {
AssertIsOnBackgroundThread();
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (aClientId.isSome() && gClientValidation) {
RefPtr<ClientManagerService> svc = ClientManagerService::GetInstance();
if (svc &&
!svc->HasWindow(aContentParentId, aPrincipalInfo, aClientId.ref())) {
ASSERT_UNLESS_FUZZING();
return false;
}
}
return true;
}
bool VerifyOriginKey(const nsACString& aOriginKey,
const PrincipalInfo& aPrincipalInfo) {
AssertIsOnBackgroundThread();
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey2(aPrincipalInfo, originAttrSuffix, originKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(originKey != aOriginKey)) {
LS_WARNING("originKey (%s) doesn't match passed one (%s)!", originKey.get(),
nsCString(aOriginKey).get());
ASSERT_UNLESS_FUZZING();
return false;
}
return true;
}
bool VerifyRequestParams(const Maybe<ContentParentId>& aContentParentId,
const LSRequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
switch (aParams.type()) {
case LSRequestParams::TLSRequestPreloadDatastoreParams: {
const LSRequestCommonParams& params =
aParams.get_LSRequestPreloadDatastoreParams().commonParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
params.principalInfo(), Nothing()))) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(
!VerifyOriginKey(params.originKey(), params.principalInfo()))) {
ASSERT_UNLESS_FUZZING();
return false;
}
break;
}
case LSRequestParams::TLSRequestPrepareDatastoreParams: {
const LSRequestPrepareDatastoreParams& params =
aParams.get_LSRequestPrepareDatastoreParams();
const LSRequestCommonParams& commonParams = params.commonParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
commonParams.principalInfo(),
params.clientId()))) {
ASSERT_UNLESS_FUZZING();
return false;
}
if (NS_WARN_IF(!VerifyOriginKey(commonParams.originKey(),
commonParams.principalInfo()))) {
ASSERT_UNLESS_FUZZING();
return false;
}
break;
}
case LSRequestParams::TLSRequestPrepareObserverParams: {
const LSRequestPrepareObserverParams& params =
aParams.get_LSRequestPrepareObserverParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
params.principalInfo(),
params.clientId()))) {
ASSERT_UNLESS_FUZZING();
return false;
}
break;
}
default:
MOZ_CRASH("Should never get here!");
}
return true;
}
PBackgroundLSRequestParent* AllocPBackgroundLSRequestParent(
PBackgroundParent* aBackgroundActor, const LSRequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != LSRequestParams::T__None);
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
return nullptr;
}
#ifdef DEBUG
// Always verify parameters in DEBUG builds!
bool trustParams = false;
#else
bool trustParams = !BackgroundParent::IsOtherProcessActor(aBackgroundActor);
#endif
Maybe<ContentParentId> contentParentId;
uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
if (childID) {
contentParentId = Some(ContentParentId(childID));
}
if (!trustParams &&
NS_WARN_IF(!VerifyRequestParams(contentParentId, aParams))) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
// If we're in the same process as the actor, we need to get the target event
// queue from the current RequestHelper.
nsCOMPtr<nsIEventTarget> mainEventTarget;
@ -3129,14 +3260,8 @@ PBackgroundLSRequestParent* AllocPBackgroundLSRequestParent(
RefPtr<LSRequestBase> actor;
switch (aParams.type()) {
case LSRequestParams::TLSRequestPreloadDatastoreParams:
case LSRequestParams::TLSRequestPrepareDatastoreParams: {
Maybe<ContentParentId> contentParentId;
uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
if (childID) {
contentParentId = Some(ContentParentId(childID));
}
RefPtr<PrepareDatastoreOp> prepareDatastoreOp =
new PrepareDatastoreOp(mainEventTarget, aParams, contentParentId);
@ -3193,14 +3318,62 @@ bool DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor) {
return true;
}
PBackgroundLSSimpleRequestParent* AllocPBackgroundLSSimpleRequestParent(
const LSSimpleRequestParams& aParams) {
bool VerifyRequestParams(const Maybe<ContentParentId>& aContentParentId,
const LSSimpleRequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
switch (aParams.type()) {
case LSSimpleRequestParams::TLSSimpleRequestPreloadedParams: {
const LSSimpleRequestPreloadedParams& params =
aParams.get_LSSimpleRequestPreloadedParams();
if (NS_WARN_IF(!VerifyPrincipalInfo(aContentParentId,
params.principalInfo(),
Nothing()))) {
ASSERT_UNLESS_FUZZING();
return false;
}
break;
}
default:
MOZ_CRASH("Should never get here!");
}
return true;
}
PBackgroundLSSimpleRequestParent* AllocPBackgroundLSSimpleRequestParent(
PBackgroundParent* aBackgroundActor, const LSSimpleRequestParams& aParams) {
AssertIsOnBackgroundThread();
MOZ_ASSERT(aParams.type() != LSSimpleRequestParams::T__None);
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread())) {
return nullptr;
}
#ifdef DEBUG
// Always verify parameters in DEBUG builds!
bool trustParams = false;
#else
bool trustParams = !BackgroundParent::IsOtherProcessActor(aBackgroundActor);
#endif
if (!trustParams) {
Maybe<ContentParentId> contentParentId;
uint64_t childID = BackgroundParent::GetChildID(aBackgroundActor);
if (childID) {
contentParentId = Some(ContentParentId(childID));
}
if (NS_WARN_IF(!VerifyRequestParams(contentParentId, aParams))) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
}
RefPtr<LSSimpleRequestBase> actor;
switch (aParams.type()) {
@ -5447,11 +5620,7 @@ void LSRequestBase::Dispatch() {
mState = State::Opening;
if (mMainEventTarget) {
MOZ_ALWAYS_SUCCEEDS(mMainEventTarget->Dispatch(this, NS_DISPATCH_NORMAL));
} else {
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
}
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
}
nsresult LSRequestBase::NestedRun() { return NS_OK; }
@ -5587,13 +5756,18 @@ PrepareDatastoreOp::PrepareDatastoreOp(
: LSRequestBase(aMainEventTarget),
mMainEventTarget(aMainEventTarget),
mLoadDataOp(nullptr),
mParams(aParams.get_LSRequestPrepareDatastoreParams()),
mParams(
aParams.type() == LSRequestParams::TLSRequestPreloadDatastoreParams
? aParams.get_LSRequestPreloadDatastoreParams().commonParams()
: aParams.get_LSRequestPrepareDatastoreParams().commonParams()),
mContentParentId(aContentParentId),
mPrivateBrowsingId(0),
mUsage(0),
mSizeOfKeys(0),
mSizeOfItems(0),
mNestedState(NestedState::BeforeNesting),
mCreateIfNotExists(aParams.type() ==
LSRequestParams::TLSRequestPrepareDatastoreParams),
mDatabaseNotAvailable(false),
mRequestedDirectoryLock(false),
mInvalidated(false)
@ -5602,8 +5776,9 @@ PrepareDatastoreOp::PrepareDatastoreOp(
mDEBUGUsage(0)
#endif
{
MOZ_ASSERT(aParams.type() ==
LSRequestParams::TLSRequestPrepareDatastoreParams);
MOZ_ASSERT(
aParams.type() == LSRequestParams::TLSRequestPreloadDatastoreParams ||
aParams.type() == LSRequestParams::TLSRequestPrepareDatastoreParams);
}
PrepareDatastoreOp::~PrepareDatastoreOp() {
@ -5614,11 +5789,11 @@ PrepareDatastoreOp::~PrepareDatastoreOp() {
}
nsresult PrepareDatastoreOp::Open() {
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
MOZ_ASSERT(mState == State::Opening);
MOZ_ASSERT(mNestedState == NestedState::BeforeNesting);
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
!MayProceedOnNonOwningThread()) {
return NS_ERROR_FAILURE;
}
@ -5630,28 +5805,8 @@ nsresult PrepareDatastoreOp::Open() {
} else {
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
nsresult rv;
nsCOMPtr<nsIPrincipal> principal =
PrincipalInfoToPrincipal(principalInfo, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = QuotaManager::GetInfoFromPrincipal(principal, &mSuffix, &mGroup,
&mMainThreadOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = principal->GetPrivateBrowsingId(&mPrivateBrowsingId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
mArchivedOriginScope = ArchivedOriginScope::CreateFromOrigin(principal);
if (NS_WARN_IF(!mArchivedOriginScope)) {
return NS_ERROR_FAILURE;
}
QuotaManager::GetInfoFromValidatedPrincipalInfo(
principalInfo, &mSuffix, &mGroup, &mMainThreadOrigin);
}
mState = State::Nesting;
@ -5673,6 +5828,27 @@ nsresult PrepareDatastoreOp::CheckExistingOperations() {
return NS_ERROR_FAILURE;
}
const PrincipalInfo& principalInfo = mParams.principalInfo();
nsCString originAttrSuffix;
uint32_t privateBrowsingId;
if (principalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
privateBrowsingId = 0;
} else {
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
const ContentPrincipalInfo& info = principalInfo.get_ContentPrincipalInfo();
const OriginAttributes& attrs = info.attrs();
attrs.CreateSuffix(originAttrSuffix);
privateBrowsingId = attrs.mPrivateBrowsingId;
}
mArchivedOriginScope = ArchivedOriginScope::CreateFromOrigin(
originAttrSuffix, mParams.originKey());
MOZ_ASSERT(mArchivedOriginScope);
// Normally it's safe to access member variables without a mutex because even
// though we hop between threads, the variables are never accessed by multiple
// threads at the same time.
@ -5683,6 +5859,8 @@ nsresult PrepareDatastoreOp::CheckExistingOperations() {
MOZ_ASSERT(!mOrigin.IsEmpty());
mPrivateBrowsingId = privateBrowsingId;
mNestedState = NestedState::CheckClosingDatastore;
// See if this PrepareDatastoreOp needs to wait.
@ -5906,7 +6084,7 @@ nsresult PrepareDatastoreOp::DatabaseWork() {
bool hasDataForMigration = mArchivedOriginScope->HasMatches(gArchivedOrigins);
bool createIfNotExists = mParams.createIfNotExists() || hasDataForMigration;
bool createIfNotExists = mCreateIfNotExists || hasDataForMigration;
nsCOMPtr<nsIFile> directoryEntry;
rv = quotaManager->EnsureOriginIsInitialized(
@ -6352,12 +6530,11 @@ void PrepareDatastoreOp::GetResponse(LSRequestResponse& aResponse) {
MOZ_ASSERT(NS_SUCCEEDED(ResultCode()));
if (mDatabaseNotAvailable) {
MOZ_ASSERT(!mParams.createIfNotExists());
MOZ_ASSERT(!mCreateIfNotExists);
LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
prepareDatastoreResponse.datastoreId() = null_t();
LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
aResponse = prepareDatastoreResponse;
aResponse = preloadDatastoreResponse;
return;
}
@ -6391,7 +6568,7 @@ void PrepareDatastoreOp::GetResponse(LSRequestResponse& aResponse) {
nsAutoPtr<PreparedDatastore> preparedDatastore(
new PreparedDatastore(mDatastore, mContentParentId, mOrigin, datastoreId,
/* aForPreload */ !mParams.createIfNotExists()));
/* aForPreload */ !mCreateIfNotExists));
if (!gPreparedDatastores) {
gPreparedDatastores = new PreparedDatastoreHashtable();
@ -6404,10 +6581,16 @@ void PrepareDatastoreOp::GetResponse(LSRequestResponse& aResponse) {
preparedDatastore.forget();
LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
prepareDatastoreResponse.datastoreId() = datastoreId;
if (mCreateIfNotExists) {
LSRequestPrepareDatastoreResponse prepareDatastoreResponse;
prepareDatastoreResponse.datastoreId() = datastoreId;
aResponse = prepareDatastoreResponse;
aResponse = prepareDatastoreResponse;
} else {
LSRequestPreloadDatastoreResponse preloadDatastoreResponse;
aResponse = preloadDatastoreResponse;
}
}
void PrepareDatastoreOp::Cleanup() {
@ -6631,10 +6814,10 @@ PrepareObserverOp::PrepareObserverOp(nsIEventTarget* aMainEventTarget,
}
nsresult PrepareObserverOp::Open() {
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
MOZ_ASSERT(mState == State::Opening);
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
!MayProceedOnNonOwningThread()) {
return NS_ERROR_FAILURE;
}
@ -6646,18 +6829,8 @@ nsresult PrepareObserverOp::Open() {
} else {
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
nsresult rv;
nsCOMPtr<nsIPrincipal> principal =
PrincipalInfoToPrincipal(principalInfo, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr,
&mOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
QuotaManager::GetInfoFromValidatedPrincipalInfo(principalInfo, nullptr,
nullptr, &mOrigin);
}
mState = State::SendingReadyMessage;
@ -6703,7 +6876,7 @@ void LSSimpleRequestBase::Dispatch() {
mState = State::Opening;
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToCurrentThread(this));
}
void LSSimpleRequestBase::SendResults() {
@ -6782,10 +6955,10 @@ PreloadedOp::PreloadedOp(const LSSimpleRequestParams& aParams)
}
nsresult PreloadedOp::Open() {
MOZ_ASSERT(NS_IsMainThread());
AssertIsOnOwningThread();
MOZ_ASSERT(mState == State::Opening);
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnNonBackgroundThread()) ||
if (NS_WARN_IF(QuotaClient::IsShuttingDownOnBackgroundThread()) ||
!MayProceedOnNonOwningThread()) {
return NS_ERROR_FAILURE;
}
@ -6797,18 +6970,8 @@ nsresult PreloadedOp::Open() {
} else {
MOZ_ASSERT(principalInfo.type() == PrincipalInfo::TContentPrincipalInfo);
nsresult rv;
nsCOMPtr<nsIPrincipal> principal =
PrincipalInfoToPrincipal(principalInfo, &rv);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr,
&mOrigin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
QuotaManager::GetInfoFromValidatedPrincipalInfo(principalInfo, nullptr,
nullptr, &mOrigin);
}
mState = State::SendingResults;
@ -6843,35 +7006,15 @@ void PreloadedOp::GetResponse(LSSimpleRequestResponse& aResponse) {
// static
ArchivedOriginScope* ArchivedOriginScope::CreateFromOrigin(
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPrincipal);
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
const nsACString& aOriginAttrSuffix, const nsACString& aOriginKey) {
return new ArchivedOriginScope(
std::move(Origin(originAttrSuffix, originKey)));
std::move(Origin(aOriginAttrSuffix, aOriginKey)));
}
// static
ArchivedOriginScope* ArchivedOriginScope::CreateFromPrefix(
nsIPrincipal* aPrincipal) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPrincipal);
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
return new ArchivedOriginScope(std::move(Prefix(originKey)));
const nsACString& aOriginKey) {
return new ArchivedOriginScope(std::move(Prefix(aOriginKey)));
}
// static
@ -7066,74 +7209,6 @@ void ArchivedOriginScope::RemoveMatches(
mData.match(Matcher(aHashtable));
}
/*******************************************************************************
* ArchivedOriginScopeHelper
******************************************************************************/
nsresult ArchivedOriginScopeHelper::BlockAndReturnArchivedOriginScope(
nsAutoPtr<ArchivedOriginScope>& aArchivedOriginScope) {
AssertIsOnIOThread();
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(this));
mozilla::MonitorAutoLock lock(mMonitor);
while (mWaiting) {
lock.Wait();
}
if (NS_WARN_IF(NS_FAILED(mMainThreadResultCode))) {
return mMainThreadResultCode;
}
aArchivedOriginScope = std::move(mArchivedOriginScope);
return NS_OK;
}
nsresult ArchivedOriginScopeHelper::RunOnMainThread() {
MOZ_ASSERT(NS_IsMainThread());
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), mSpec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateCodebasePrincipal(uri, mAttrs);
if (NS_WARN_IF(!principal)) {
return NS_ERROR_FAILURE;
}
if (mPrefix) {
mArchivedOriginScope = ArchivedOriginScope::CreateFromPrefix(principal);
} else {
mArchivedOriginScope = ArchivedOriginScope::CreateFromOrigin(principal);
}
if (NS_WARN_IF(!mArchivedOriginScope)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
ArchivedOriginScopeHelper::Run() {
MOZ_ASSERT(NS_IsMainThread());
nsresult rv = RunOnMainThread();
if (NS_WARN_IF(NS_FAILED(rv))) {
mMainThreadResultCode = rv;
}
mozilla::MonitorAutoLock lock(mMonitor);
MOZ_ASSERT(mWaiting);
mWaiting = false;
lock.Notify();
return NS_OK;
}
/*******************************************************************************
* QuotaClient
******************************************************************************/
@ -7772,13 +7847,21 @@ nsresult QuotaClient::CreateArchivedOriginScope(
return NS_ERROR_FAILURE;
}
RefPtr<ArchivedOriginScopeHelper> helper =
new ArchivedOriginScopeHelper(spec, attrs, /* aPrefix */ false);
ContentPrincipalInfo contentPrincipalInfo;
contentPrincipalInfo.attrs() = attrs;
contentPrincipalInfo.spec() = spec;
rv = helper->BlockAndReturnArchivedOriginScope(archivedOriginScope);
PrincipalInfo principalInfo(contentPrincipalInfo);
nsCString originAttrSuffix;
nsCString originKey;
rv = GenerateOriginKey2(principalInfo, originAttrSuffix, originKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
archivedOriginScope =
ArchivedOriginScope::CreateFromOrigin(originAttrSuffix, originKey);
} else if (aOriginScope.IsPrefix()) {
nsCString spec;
OriginAttributes attrs;
@ -7787,13 +7870,20 @@ nsresult QuotaClient::CreateArchivedOriginScope(
return NS_ERROR_FAILURE;
}
RefPtr<ArchivedOriginScopeHelper> helper =
new ArchivedOriginScopeHelper(spec, attrs, /* aPrefix */ true);
ContentPrincipalInfo contentPrincipalInfo;
contentPrincipalInfo.attrs() = attrs;
contentPrincipalInfo.spec() = spec;
rv = helper->BlockAndReturnArchivedOriginScope(archivedOriginScope);
PrincipalInfo principalInfo(contentPrincipalInfo);
nsCString originAttrSuffix;
nsCString originKey;
rv = GenerateOriginKey2(principalInfo, originAttrSuffix, originKey);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
archivedOriginScope = ArchivedOriginScope::CreateFromPrefix(originKey);
} else if (aOriginScope.IsPattern()) {
archivedOriginScope =
ArchivedOriginScope::CreateFromPattern(aOriginScope.GetPattern());

View File

@ -33,6 +33,8 @@ class Client;
void InitializeLocalStorage();
bool GetCurrentNextGenPrefValue();
PBackgroundLSDatabaseParent* AllocPBackgroundLSDatabaseParent(
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
const uint32_t& aPrivateBrowsingId, const uint64_t& aDatastoreId);
@ -62,6 +64,7 @@ bool RecvPBackgroundLSRequestConstructor(PBackgroundLSRequestParent* aActor,
bool DeallocPBackgroundLSRequestParent(PBackgroundLSRequestParent* aActor);
PBackgroundLSSimpleRequestParent* AllocPBackgroundLSSimpleRequestParent(
mozilla::ipc::PBackgroundParent* aBackgroundActor,
const LSSimpleRequestParams& aParams);
bool RecvPBackgroundLSSimpleRequestConstructor(

View File

@ -9,14 +9,31 @@
namespace mozilla {
namespace dom {
using namespace mozilla::services;
namespace {
#define XPCOM_SHUTDOWN_OBSERVER_TOPIC "xpcom-shutdown"
typedef nsDataHashtable<nsCStringHashKey, LSDatabase*> LSDatabaseHashtable;
StaticAutoPtr<LSDatabaseHashtable> gLSDatabases;
} // namespace
StaticRefPtr<LSDatabase::Observer> LSDatabase::sObserver;
class LSDatabase::Observer final : public nsIObserver {
public:
Observer() { MOZ_ASSERT(NS_IsMainThread()); }
private:
~Observer() { MOZ_ASSERT(NS_IsMainThread()); }
NS_DECL_ISUPPORTS
NS_DECL_NSIOBSERVER
};
LSDatabase::LSDatabase(const nsACString& aOrigin)
: mActor(nullptr),
mSnapshot(nullptr),
@ -27,6 +44,16 @@ LSDatabase::LSDatabase(const nsACString& aOrigin)
if (!gLSDatabases) {
gLSDatabases = new LSDatabaseHashtable();
MOZ_ASSERT(!sObserver);
sObserver = new Observer();
nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
MOZ_ASSERT(obsSvc);
MOZ_ALWAYS_SUCCEEDS(
obsSvc->AddObserver(sObserver, XPCOM_SHUTDOWN_OBSERVER_TOPIC, false));
}
MOZ_ASSERT(!gLSDatabases->Get(mOrigin));
@ -62,7 +89,10 @@ void LSDatabase::SetActor(LSDatabaseChild* aActor) {
void LSDatabase::RequestAllowToClose() {
AssertIsOnOwningThread();
MOZ_ASSERT(!mRequestedAllowToClose);
if (mRequestedAllowToClose) {
return;
}
mRequestedAllowToClose = true;
@ -315,8 +345,43 @@ void LSDatabase::AllowToClose() {
if (!gLSDatabases->Count()) {
gLSDatabases = nullptr;
MOZ_ASSERT(sObserver);
nsCOMPtr<nsIObserverService> obsSvc = GetObserverService();
MOZ_ASSERT(obsSvc);
MOZ_ALWAYS_SUCCEEDS(
obsSvc->RemoveObserver(sObserver, XPCOM_SHUTDOWN_OBSERVER_TOPIC));
sObserver = nullptr;
}
}
NS_IMPL_ISUPPORTS(LSDatabase::Observer, nsIObserver)
NS_IMETHODIMP
LSDatabase::Observer::Observe(nsISupports* aSubject, const char* aTopic,
const char16_t* aData) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(!strcmp(aTopic, XPCOM_SHUTDOWN_OBSERVER_TOPIC));
MOZ_ASSERT(gLSDatabases);
nsTArray<RefPtr<LSDatabase>> databases;
for (auto iter = gLSDatabases->ConstIter(); !iter.Done(); iter.Next()) {
LSDatabase* database = iter.Data();
MOZ_ASSERT(database);
databases.AppendElement(database);
}
for (RefPtr<LSDatabase>& database : databases) {
database->RequestAllowToClose();
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -14,6 +14,8 @@ class LSDatabaseChild;
class LSSnapshot;
class LSDatabase final {
class Observer;
LSDatabaseChild* mActor;
LSSnapshot* mSnapshot;
@ -23,6 +25,8 @@ class LSDatabase final {
bool mAllowedToClose;
bool mRequestedAllowToClose;
static StaticRefPtr<Observer> sObserver;
public:
explicit LSDatabase(const nsACString& aOrigin);

View File

@ -220,10 +220,9 @@ nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
// localStorage is not available on some pages on purpose, for example
// about:home. Match the old implementation by using GenerateOriginKey
// for the check.
nsCString dummyOriginAttrSuffix;
nsCString dummyOriginKey;
nsresult rv =
GenerateOriginKey(principal, dummyOriginAttrSuffix, dummyOriginKey);
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey(principal, originAttrSuffix, originKey);
if (NS_FAILED(rv)) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -236,18 +235,32 @@ nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo);
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
return NS_ERROR_FAILURE;
}
nsCString suffix;
nsCString origin;
rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr, &origin);
rv = QuotaManager::GetInfoFromPrincipal(principal, &suffix, nullptr, &origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
MOZ_ASSERT(originAttrSuffix == suffix);
uint32_t privateBrowsingId;
rv = principal->GetPrivateBrowsingId(&privateBrowsingId);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
if (clientInfo.isNothing()) {
return NS_ERROR_FAILURE;
}
Maybe<nsID> clientId = Some(clientInfo.ref().Id());
nsString documentURI;
if (nsCOMPtr<Document> doc = aWindow->GetExtantDoc()) {
rv = doc->GetDocumentURI(documentURI);
@ -259,7 +272,9 @@ nsresult LSObject::CreateForWindow(nsPIDOMWindowInner* aWindow,
RefPtr<LSObject> object = new LSObject(aWindow, principal);
object->mPrincipalInfo = std::move(principalInfo);
object->mPrivateBrowsingId = privateBrowsingId;
object->mClientId = clientId;
object->mOrigin = origin;
object->mOriginKey = originKey;
object->mDocumentURI = documentURI;
object.forget(aStorage);
@ -275,10 +290,9 @@ nsresult LSObject::CreateForPrincipal(nsPIDOMWindowInner* aWindow,
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(aObject);
nsCString dummyOriginAttrSuffix;
nsCString dummyOriginKey;
nsresult rv =
GenerateOriginKey(aPrincipal, dummyOriginAttrSuffix, dummyOriginKey);
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
if (NS_FAILED(rv)) {
return NS_ERROR_NOT_AVAILABLE;
}
@ -292,22 +306,41 @@ nsresult LSObject::CreateForPrincipal(nsPIDOMWindowInner* aWindow,
MOZ_ASSERT(principalInfo->type() == PrincipalInfo::TContentPrincipalInfo ||
principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo);
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
return NS_ERROR_FAILURE;
}
nsCString suffix;
nsCString origin;
if (principalInfo->type() == PrincipalInfo::TSystemPrincipalInfo) {
QuotaManager::GetInfoForChrome(nullptr, nullptr, &origin);
QuotaManager::GetInfoForChrome(&suffix, nullptr, &origin);
} else {
rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, nullptr, nullptr,
rv = QuotaManager::GetInfoFromPrincipal(aPrincipal, &suffix, nullptr,
&origin);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
}
MOZ_ASSERT(originAttrSuffix == suffix);
Maybe<nsID> clientId;
if (aWindow) {
Maybe<ClientInfo> clientInfo = aWindow->GetClientInfo();
if (clientInfo.isNothing()) {
return NS_ERROR_FAILURE;
}
clientId = Some(clientInfo.ref().Id());
}
RefPtr<LSObject> object = new LSObject(aWindow, aPrincipal);
object->mPrincipalInfo = std::move(principalInfo);
object->mPrivateBrowsingId = aPrivate ? 1 : 0;
object->mClientId = clientId;
object->mOrigin = origin;
object->mOriginKey = originKey;
object->mDocumentURI = aDocumentURI;
object.forget(aObject);
@ -732,9 +765,13 @@ nsresult LSObject::EnsureDatabase() {
return NS_ERROR_FAILURE;
}
LSRequestCommonParams commonParams;
commonParams.principalInfo() = *mPrincipalInfo;
commonParams.originKey() = mOriginKey;
LSRequestPrepareDatastoreParams params;
params.principalInfo() = *mPrincipalInfo;
params.createIfNotExists() = true;
params.commonParams() = commonParams;
params.clientId() = mClientId;
LSRequestResponse response;
@ -749,10 +786,7 @@ nsresult LSObject::EnsureDatabase() {
const LSRequestPrepareDatastoreResponse& prepareDatastoreResponse =
response.get_LSRequestPrepareDatastoreResponse();
const NullableDatastoreId& datastoreId =
prepareDatastoreResponse.datastoreId();
MOZ_ASSERT(datastoreId.type() == NullableDatastoreId::Tuint64_t);
uint64_t datastoreId = prepareDatastoreResponse.datastoreId();
// The datastore is now ready on the parent side (prepared by the asynchronous
// request on the DOM File thread).
@ -801,6 +835,7 @@ nsresult LSObject::EnsureObserver() {
LSRequestPrepareObserverParams params;
params.principalInfo() = *mPrincipalInfo;
params.clientId() = mClientId;
LSRequestResponse response;
@ -1012,20 +1047,22 @@ nsresult RequestHelper::StartAndReturnResponse(LSRequestResponse& aResponse) {
return rv;
}
MOZ_ALWAYS_TRUE(SpinEventLoopUntil([&]() {
if (!mWaiting) {
return true;
}
MOZ_ALWAYS_TRUE(SpinEventLoopUntil(
[&]() {
if (!mWaiting) {
return true;
}
{
StaticMutexAutoLock lock(gRequestHelperMutex);
if (NS_WARN_IF(gPendingSyncMessage)) {
return true;
}
}
{
StaticMutexAutoLock lock(gRequestHelperMutex);
if (NS_WARN_IF(gPendingSyncMessage)) {
return true;
}
}
return false;
}, thread));
return false;
},
thread));
}
// If mWaiting is still set to true, it means that the event loop spinning

View File

@ -66,7 +66,9 @@ class LSObject final : public Storage {
RefPtr<LSObserver> mObserver;
uint32_t mPrivateBrowsingId;
Maybe<nsID> mClientId;
nsCString mOrigin;
nsCString mOriginKey;
nsString mDocumentURI;
bool mInExplicitSnapshot;

View File

@ -6,11 +6,17 @@
#include "LocalStorageCommon.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/net/MozURL.h"
namespace mozilla {
namespace dom {
using namespace mozilla::net;
namespace {
StaticMutex gNextGenLocalStorageMutex;
Atomic<int32_t> gNextGenLocalStorageEnabled(-1);
} // namespace
@ -18,6 +24,17 @@ Atomic<int32_t> gNextGenLocalStorageEnabled(-1);
const char16_t* kLocalStorageType = u"localStorage";
bool NextGenLocalStorageEnabled() {
if (XRE_IsParentProcess()) {
StaticMutexAutoLock lock(gNextGenLocalStorageMutex);
if (gNextGenLocalStorageEnabled == -1) {
bool enabled = GetCurrentNextGenPrefValue();
gNextGenLocalStorageEnabled = enabled ? 1 : 0;
}
return !!gNextGenLocalStorageEnabled;
}
MOZ_ASSERT(NS_IsMainThread());
if (gNextGenLocalStorageEnabled == -1) {
@ -34,5 +51,87 @@ bool CachedNextGenLocalStorageEnabled() {
return !!gNextGenLocalStorageEnabled;
}
nsresult GenerateOriginKey2(const PrincipalInfo& aPrincipalInfo,
nsACString& aOriginAttrSuffix,
nsACString& aOriginKey) {
OriginAttributes attrs;
nsCString spec;
switch (aPrincipalInfo.type()) {
case PrincipalInfo::TNullPrincipalInfo: {
const NullPrincipalInfo& info = aPrincipalInfo.get_NullPrincipalInfo();
attrs = info.attrs();
spec = info.spec();
break;
}
case PrincipalInfo::TContentPrincipalInfo: {
const ContentPrincipalInfo& info =
aPrincipalInfo.get_ContentPrincipalInfo();
attrs = info.attrs();
spec = info.spec();
break;
}
default: {
spec.SetIsVoid(true);
break;
}
}
if (spec.IsVoid()) {
return NS_ERROR_UNEXPECTED;
}
attrs.CreateSuffix(aOriginAttrSuffix);
RefPtr<MozURL> specURL;
nsresult rv = MozURL::Init(getter_AddRefs(specURL), spec);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
nsCString host(specURL->Host());
uint32_t length = host.Length();
if (length > 0 && host.CharAt(0) == '[' && host.CharAt(length - 1) == ']') {
host = Substring(host, 1, length - 2);
}
nsCString domainOrigin(host);
if (domainOrigin.IsEmpty()) {
// For the file:/// protocol use the exact directory as domain.
if (specURL->Scheme().EqualsLiteral("file")) {
domainOrigin.Assign(specURL->Directory());
}
}
// Append reversed domain
nsAutoCString reverseDomain;
rv = CreateReversedDomain(domainOrigin, reverseDomain);
if (NS_FAILED(rv)) {
return rv;
}
aOriginKey.Append(reverseDomain);
// Append scheme
aOriginKey.Append(':');
aOriginKey.Append(specURL->Scheme());
// Append port if any
int32_t port = specURL->RealPort();
if (port != -1) {
aOriginKey.Append(nsPrintfCString(":%d", port));
}
return NS_OK;
}
} // namespace dom
} // namespace mozilla

View File

@ -181,7 +181,17 @@
* interface.
*/
#include "mozilla/Attributes.h"
#include "nsString.h"
namespace mozilla {
namespace ipc {
class PrincipalInfo;
} // namespace ipc
namespace dom {
extern const char16_t* kLocalStorageType;
@ -210,8 +220,12 @@ class MOZ_STACK_CLASS LSNotifyInfo {
};
/**
* Main-thread-only check of LSNG being enabled, the value is latched once
* initialized so changing the preference during runtime has no effect.
* A check of LSNG being enabled, the value is latched once initialized so
* changing the preference during runtime has no effect.
* May be called on any thread in the parent process, but you should call
* CachedNextGenLocalStorageEnabled if you know that NextGenLocalStorageEnabled
* was already called because it is faster.
* May be called on the main thread only in a content process.
*/
bool NextGenLocalStorageEnabled();
@ -220,6 +234,10 @@ bool NextGenLocalStorageEnabled();
*/
bool CachedNextGenLocalStorageEnabled();
nsresult GenerateOriginKey2(const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
nsACString& aOriginAttrSuffix,
nsACString& aOriginKey);
} // namespace dom
} // namespace mozilla

View File

@ -27,7 +27,7 @@ class RequestResolver final : public LSRequestChildCallback {
void HandleResponse(nsresult aResponse);
void HandleResponse(const NullableDatastoreId& aDatastoreId);
void HandleResponse();
// LSRequestChildCallback
void OnResponse(const LSRequestResponse& aResponse) override;
@ -82,6 +82,10 @@ nsresult CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
return rv;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
return NS_ERROR_FAILURE;
}
if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
return NS_ERROR_UNEXPECTED;
@ -188,7 +192,18 @@ LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(_retval);
nsresult rv;
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
if (NS_FAILED(rv)) {
return NS_ERROR_NOT_AVAILABLE;
}
nsAutoPtr<PrincipalInfo> principalInfo(new PrincipalInfo());
rv = CheckedPrincipalToPrincipalInfo(aPrincipal, *principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
RefPtr<Promise> promise;
@ -199,13 +214,11 @@ LocalStorageManager2::Preload(nsIPrincipal* aPrincipal, JSContext* aContext,
}
}
LSRequestPrepareDatastoreParams params;
params.createIfNotExists() = false;
LSRequestCommonParams commonParams;
commonParams.principalInfo() = *principalInfo;
commonParams.originKey() = originKey;
rv = CheckedPrincipalToPrincipalInfo(aPrincipal, params.principalInfo());
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
LSRequestPreloadDatastoreParams params(commonParams);
rv = StartRequest(promise, params);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -299,25 +312,14 @@ void RequestResolver::HandleResponse(nsresult aResponse) {
mPromise->MaybeReject(aResponse);
}
void RequestResolver::HandleResponse(const NullableDatastoreId& aDatastoreId) {
void RequestResolver::HandleResponse() {
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!");
}
mPromise->MaybeResolveWithUndefined();
}
void RequestResolver::OnResponse(const LSRequestResponse& aResponse) {
@ -328,9 +330,8 @@ void RequestResolver::OnResponse(const LSRequestResponse& aResponse) {
HandleResponse(aResponse.get_nsresult());
break;
case LSRequestResponse::TLSRequestPrepareDatastoreResponse:
HandleResponse(
aResponse.get_LSRequestPrepareDatastoreResponse().datastoreId());
case LSRequestResponse::TLSRequestPreloadDatastoreResponse:
HandleResponse();
break;
default:
MOZ_CRASH("Unknown response type!");

View File

@ -10,15 +10,13 @@ using struct mozilla::null_t
namespace mozilla {
namespace dom {
union NullableDatastoreId
struct LSRequestPreloadDatastoreResponse
{
null_t;
uint64_t;
};
struct LSRequestPrepareDatastoreResponse
{
NullableDatastoreId datastoreId;
uint64_t datastoreId;
};
struct LSRequestPrepareObserverResponse
@ -33,6 +31,7 @@ struct LSRequestPrepareObserverResponse
union LSRequestResponse
{
nsresult;
LSRequestPreloadDatastoreResponse;
LSRequestPrepareDatastoreResponse;
LSRequestPrepareObserverResponse;
};

View File

@ -3,23 +3,37 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
include PBackgroundSharedTypes;
include ProtocolTypes;
namespace mozilla {
namespace dom {
struct LSRequestPrepareDatastoreParams
struct LSRequestCommonParams
{
PrincipalInfo principalInfo;
bool createIfNotExists;
nsCString originKey;
};
struct LSRequestPreloadDatastoreParams
{
LSRequestCommonParams commonParams;
};
struct LSRequestPrepareDatastoreParams
{
LSRequestCommonParams commonParams;
nsID? clientId;
};
struct LSRequestPrepareObserverParams
{
PrincipalInfo principalInfo;
nsID? clientId;
};
union LSRequestParams
{
LSRequestPreloadDatastoreParams;
LSRequestPrepareDatastoreParams;
LSRequestPrepareObserverParams;
};

View File

@ -12,6 +12,8 @@ TEST_HARNESS_FILES.xpcshell.dom.localstorage.test.unit += [
'test/unit/databaseShadowing-shared.js',
]
TEST_DIRS += ['test/gtest']
XPIDL_SOURCES += [
'nsILocalStorageManager.idl',
]

View File

@ -0,0 +1,113 @@
/* -*- 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 "gtest/gtest.h"
#include "mozilla/NullPrincipal.h"
#include "mozilla/dom/LocalStorageCommon.h"
#include "mozilla/dom/StorageUtils.h"
#include "mozilla/ipc/BackgroundUtils.h"
#include "mozilla/ipc/PBackgroundSharedTypes.h"
#include "nsCOMPtr.h"
#include "nsContentUtils.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
using namespace mozilla;
using namespace mozilla::dom;
using namespace mozilla::dom::StorageUtils;
using namespace mozilla::ipc;
namespace {
struct OriginKeyTest {
const char* mSpec;
const char* mOriginKey;
};
already_AddRefed<nsIPrincipal> GetCodebasePrincipal(const char* aSpec) {
nsCOMPtr<nsIURI> uri;
nsresult rv = NS_NewURI(getter_AddRefs(uri), nsDependentCString(aSpec));
if (NS_WARN_IF(NS_FAILED(rv))) {
return nullptr;
}
OriginAttributes attrs;
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateCodebasePrincipal(uri, attrs);
return principal.forget();
}
void CheckGeneratedOriginKey(nsIPrincipal* aPrincipal, const char* aOriginKey) {
nsCString originAttrSuffix;
nsCString originKey;
nsresult rv = GenerateOriginKey(aPrincipal, originAttrSuffix, originKey);
if (aOriginKey) {
ASSERT_EQ(rv, NS_OK) << "GenerateOriginKey should not fail";
EXPECT_TRUE(originKey == nsDependentCString(aOriginKey));
} else {
ASSERT_NE(rv, NS_OK) << "GenerateOriginKey should fail";
}
PrincipalInfo principalInfo;
rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
ASSERT_EQ(rv, NS_OK) << "PrincipalToPrincipalInfo should not fail";
originKey.Truncate();
rv = GenerateOriginKey2(principalInfo, originAttrSuffix, originKey);
if (aOriginKey) {
ASSERT_EQ(rv, NS_OK) << "GenerateOriginKey2 should not fail";
EXPECT_TRUE(originKey == nsDependentCString(aOriginKey));
} else {
ASSERT_NE(rv, NS_OK) << "GenerateOriginKey2 should fail";
}
}
} // namespace
TEST(LocalStorage, OriginKey) {
// Check the system principal.
nsCOMPtr<nsIScriptSecurityManager> secMan =
nsContentUtils::GetSecurityManager();
ASSERT_TRUE(secMan) << "GetSecurityManager() should not fail";
nsCOMPtr<nsIPrincipal> principal;
secMan->GetSystemPrincipal(getter_AddRefs(principal));
ASSERT_TRUE(principal) << "GetSystemPrincipal() should not fail";
CheckGeneratedOriginKey(principal, nullptr);
// Check the null principal.
principal = NullPrincipal::CreateWithoutOriginAttributes();
ASSERT_TRUE(principal) << "CreateWithoutOriginAttributes() should not fail";
CheckGeneratedOriginKey(principal, nullptr);
// Check content principals.
static const OriginKeyTest tests[] = {
{"http://www.mozilla.org", "gro.allizom.www.:http:80"},
{"https://www.mozilla.org", "gro.allizom.www.:https:443"},
{"http://www.mozilla.org:32400", "gro.allizom.www.:http:32400"},
{"file:///Users/Joe/Sites/", "/setiS/eoJ/sresU/.:file"},
{"file:///Users/Joe/Sites/#foo", "/setiS/eoJ/sresU/.:file"},
{"file:///Users/Joe/Sites/?foo", "/setiS/eoJ/sresU/.:file"},
{"file:///Users/Joe/Sites", "/eoJ/sresU/.:file"},
{"file:///Users/Joe/Sites#foo", "/eoJ/sresU/.:file"},
{"file:///Users/Joe/Sites?foo", "/eoJ/sresU/.:file"},
{"moz-extension://53711a8f-65ed-e742-9671-1f02e267c0bc/"
"_generated_background_page.html",
"cb0c762e20f1-1769-247e-de56-f8a11735.:moz-extension"},
{"http://[::1]:8/test.html", "1::.:http:8"},
};
for (const auto& test : tests) {
principal = GetCodebasePrincipal(test.mSpec);
ASSERT_TRUE(principal) << "GetCodebasePrincipal() should not fail";
CheckGeneratedOriginKey(principal, test.mOriginKey);
}
}

View File

@ -0,0 +1,17 @@
# -*- 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/.
UNIFIED_SOURCES = [
'TestLocalStorage.cpp',
]
include('/ipc/chromium/chromium-config.mozbuild')
FINAL_LIBRARY = 'xul-gtest'
LOCAL_INCLUDES += [
'/dom/localstorage',
]

File diff suppressed because it is too large Load Diff

View File

@ -13,10 +13,14 @@ namespace quota {
class PQuotaParent;
void InitializeQuotaManager();
PQuotaParent* AllocPQuotaParent();
bool DeallocPQuotaParent(PQuotaParent* aActor);
bool RecvShutdownQuotaManager();
} // namespace quota
} // namespace dom
} // namespace mozilla

View File

@ -172,11 +172,6 @@ class Client {
virtual void ShutdownWorkThreads() = 0;
// Methods which are called on the main thread.
virtual void DidInitialize(QuotaManager* aQuotaManager) {}
virtual void WillShutdown() {}
protected:
virtual ~Client() {}
};

View File

@ -10,12 +10,18 @@ include PBackgroundSharedTypes;
include "mozilla/dom/quota/SerializationHelpers.h";
using mozilla::OriginAttributesPattern
from "mozilla/OriginAttributes.h";
using mozilla::dom::quota::PersistenceType
from "mozilla/dom/quota/PersistenceType.h";
using mozilla::dom::quota::Client::Type
from "mozilla/dom/quota/Client.h";
using mozilla::dom::ContentParentId
from "mozilla/dom/ipc/IdType.h";
namespace mozilla {
namespace dom {
namespace quota {
@ -73,7 +79,7 @@ struct ResetOriginParams
struct ClearDataParams
{
nsString pattern;
OriginAttributesPattern pattern;
};
struct ClearAllParams
@ -125,6 +131,8 @@ parent:
async StartIdleMaintenance();
async StopIdleMaintenance();
async AbortOperationsForProcess(ContentParentId contentParentId);
};
} // namespace quota

View File

@ -36,6 +36,12 @@ namespace mozilla {
class OriginAttributes;
namespace ipc {
class PrincipalInfo;
} // namespace ipc
} // namespace mozilla
BEGIN_QUOTA_NAMESPACE
@ -85,19 +91,17 @@ class QuotaManager final : public BackgroundThreadObject {
friend class OriginInfo;
friend class QuotaObject;
typedef mozilla::ipc::PrincipalInfo PrincipalInfo;
typedef nsClassHashtable<nsCStringHashKey, nsTArray<DirectoryLockImpl*>>
DirectoryLockTable;
public:
class CreateRunnable;
private:
class ShutdownRunnable;
class ShutdownObserver;
class Observer;
public:
NS_INLINE_DECL_REFCOUNTING(QuotaManager)
static nsresult Initialize();
static bool IsRunningXPCShellTests() {
static bool kRunningXPCShellTests =
!!PR_GetEnv("XPCSHELL_TEST_PROFILE_DIR");
@ -120,6 +124,8 @@ class QuotaManager final : public BackgroundThreadObject {
// Returns true if we've begun the shutdown process.
static bool IsShuttingDown();
static void ShutdownInstance();
static bool IsOSMetadata(const nsAString& aFileName);
static bool IsDotFile(const nsAString& aFileName);
@ -327,6 +333,12 @@ class QuotaManager final : public BackgroundThreadObject {
const nsACString& aOrigin, Client::Type aClientType,
nsACString& aDatabaseId);
static bool IsPrincipalInfoValid(const PrincipalInfo& aPrincipalInfo);
static void GetInfoFromValidatedPrincipalInfo(
const PrincipalInfo& aPrincipalInfo, nsACString* aSuffix,
nsACString* aGroup, nsACString* aOrigin);
static nsresult GetInfoFromPrincipal(nsIPrincipal* aPrincipal,
nsACString* aSuffix, nsACString* aGroup,
nsACString* aOrigin);

View File

@ -65,6 +65,10 @@ nsresult CheckedPrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
return rv;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
return NS_ERROR_FAILURE;
}
if (aPrincipalInfo.type() != PrincipalInfo::TContentPrincipalInfo &&
aPrincipalInfo.type() != PrincipalInfo::TSystemPrincipalInfo) {
return NS_ERROR_UNEXPECTED;
@ -117,18 +121,6 @@ nsresult GetClearResetOriginParams(nsIPrincipal* aPrincipal,
return NS_OK;
}
class AbortOperationsRunnable final : public Runnable {
ContentParentId mContentParentId;
public:
explicit AbortOperationsRunnable(ContentParentId aContentParentId)
: Runnable("dom::quota::AbortOperationsRunnable"),
mContentParentId(aContentParentId) {}
private:
NS_DECL_NSIRUNNABLE
};
} // namespace
class QuotaManagerService::PendingRequestInfo {
@ -240,35 +232,20 @@ void QuotaManagerService::ClearBackgroundActor() {
mBackgroundActor = nullptr;
}
void QuotaManagerService::NoteLiveManager(QuotaManager* aManager) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aManager);
mBackgroundThread = aManager->OwningThread();
}
void QuotaManagerService::NoteShuttingDownManager() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
mBackgroundThread = nullptr;
}
void QuotaManagerService::AbortOperationsForProcess(
ContentParentId aContentParentId) {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
if (!mBackgroundThread) {
nsresult rv = EnsureBackgroundActor();
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
RefPtr<AbortOperationsRunnable> runnable =
new AbortOperationsRunnable(aContentParentId);
MOZ_ALWAYS_SUCCEEDS(
mBackgroundThread->Dispatch(runnable, NS_DISPATCH_NORMAL));
if (NS_WARN_IF(
!mBackgroundActor->SendAbortOperationsForProcess(aContentParentId))) {
return;
}
}
nsresult QuotaManagerService::Init() {
@ -306,8 +283,9 @@ void QuotaManagerService::Destroy() {
delete this;
}
nsresult QuotaManagerService::InitiateRequest(
nsAutoPtr<PendingRequestInfo>& aInfo) {
nsresult QuotaManagerService::EnsureBackgroundActor() {
MOZ_ASSERT(NS_IsMainThread());
// Nothing can be done here if we have previously failed to create a
// background actor.
if (mBackgroundActorFailed) {
@ -335,8 +313,17 @@ nsresult QuotaManagerService::InitiateRequest(
return NS_ERROR_FAILURE;
}
// If we already have a background actor then we can start this request now.
nsresult rv = aInfo->InitiateRequest(mBackgroundActor);
return NS_OK;
}
nsresult QuotaManagerService::InitiateRequest(
nsAutoPtr<PendingRequestInfo>& aInfo) {
nsresult rv = EnsureBackgroundActor();
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
rv = aInfo->InitiateRequest(mBackgroundActor);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -586,11 +573,14 @@ QuotaManagerService::ClearStoragesForOriginAttributesPattern(
const nsAString& aPattern, nsIQuotaRequest** _retval) {
MOZ_ASSERT(NS_IsMainThread());
OriginAttributesPattern pattern;
MOZ_ALWAYS_TRUE(pattern.Init(aPattern));
RefPtr<Request> request = new Request();
ClearDataParams params;
params.pattern() = aPattern;
params.pattern() = pattern;
nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
@ -828,24 +818,6 @@ void QuotaManagerService::Notify(const hal::BatteryInformation& aBatteryInfo) {
// deal with this notification.
}
NS_IMETHODIMP
AbortOperationsRunnable::Run() {
AssertIsOnBackgroundThread();
if (QuotaManager::IsShuttingDown()) {
return NS_OK;
}
QuotaManager* quotaManager = QuotaManager::Get();
if (!quotaManager) {
return NS_OK;
}
quotaManager->AbortOperationsForProcess(mContentParentId);
return NS_OK;
}
nsresult QuotaManagerService::UsageRequestInfo::InitiateRequest(
QuotaChild* aActor) {
MOZ_ASSERT(aActor);

View File

@ -44,8 +44,6 @@ class QuotaManagerService final : public nsIQuotaManagerService,
class RequestInfo;
class IdleMaintenanceInfo;
nsCOMPtr<nsIEventTarget> mBackgroundThread;
QuotaChild* mBackgroundActor;
bool mBackgroundActorFailed;
@ -63,10 +61,6 @@ class QuotaManagerService final : public nsIQuotaManagerService,
void ClearBackgroundActor();
void NoteLiveManager(QuotaManager* aManager);
void NoteShuttingDownManager();
// Called when a process is being shot down. Aborts any running operations
// for the given process.
void AbortOperationsForProcess(ContentParentId aContentParentId);
@ -79,12 +73,10 @@ class QuotaManagerService final : public nsIQuotaManagerService,
void Destroy();
nsresult EnsureBackgroundActor();
nsresult InitiateRequest(nsAutoPtr<PendingRequestInfo>& aInfo);
nsresult BackgroundActorCreated(PBackgroundChild* aBackgroundActor);
void BackgroundActorFailed();
void PerformIdleMaintenance();
void RemoveIdleObserver();

14
dom/quota/components.conf Normal file
View File

@ -0,0 +1,14 @@
# -*- 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/.
Classes = [
{
'cid': '{b6f2f870-b0bc-4a1a-9c40-02cc171adb5b}',
'contract_ids': ['@mozilla.org/network/protocol;1?name=indexeddb'],
'type': 'nsIndexedDBProtocolHandler',
'headers': ['/dom/quota/nsIndexedDBProtocolHandler.h'],
},
]

View File

@ -49,11 +49,16 @@ EXPORTS.mozilla.dom.quota += [
'UsageInfo.h',
]
XPCOM_MANIFESTS += [
'components.conf',
]
UNIFIED_SOURCES += [
'ActorsChild.cpp',
'ActorsParent.cpp',
'FileStreams.cpp',
'MemoryOutputStream.cpp',
'nsIndexedDBProtocolHandler.cpp',
'QuotaCommon.cpp',
'QuotaManagerService.cpp',
'QuotaRequests.cpp',

View File

@ -0,0 +1,63 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim:set ts=2 sts=2 sw=2 et cin:
*
* 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 "nsIndexedDBProtocolHandler.h"
#include "nsIURIMutator.h"
#include "nsStandardURL.h"
using namespace mozilla::net;
nsIndexedDBProtocolHandler::nsIndexedDBProtocolHandler() {}
nsIndexedDBProtocolHandler::~nsIndexedDBProtocolHandler() {}
NS_IMPL_ISUPPORTS(nsIndexedDBProtocolHandler, nsIProtocolHandler,
nsISupportsWeakReference)
NS_IMETHODIMP nsIndexedDBProtocolHandler::GetScheme(nsACString& aScheme) {
aScheme.AssignLiteral("indexeddb");
return NS_OK;
}
NS_IMETHODIMP nsIndexedDBProtocolHandler::GetDefaultPort(
int32_t* aDefaultPort) {
*aDefaultPort = -1;
return NS_OK;
}
NS_IMETHODIMP nsIndexedDBProtocolHandler::GetProtocolFlags(
uint32_t* aProtocolFlags) {
*aProtocolFlags = URI_STD | URI_DANGEROUS_TO_LOAD | URI_DOES_NOT_RETURN_DATA |
URI_NON_PERSISTABLE;
return NS_OK;
}
NS_IMETHODIMP nsIndexedDBProtocolHandler::NewURI(
const nsACString& aSpec, const char* aOriginCharset, nsIURI* aBaseURI,
nsIURI** _retval) {
nsCOMPtr<nsIURI> baseURI(aBaseURI);
return NS_MutateURI(new nsStandardURL::Mutator())
.Apply(NS_MutatorMethod(&nsIStandardURLMutator::Init,
nsIStandardURL::URLTYPE_AUTHORITY, 0,
nsCString(aSpec), aOriginCharset, baseURI,
nullptr))
.Finalize(_retval);
}
NS_IMETHODIMP
nsIndexedDBProtocolHandler::NewChannel(nsIURI* aURI, nsILoadInfo* aLoadInfo,
nsIChannel** _retval) {
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsIndexedDBProtocolHandler::AllowPort(int32_t aPort, const char* aScheme,
bool* _retval) {
*_retval = false;
return NS_OK;
}

View File

@ -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 nsIndexedDBProtocolHandler_h
#define nsIndexedDBProtocolHandler_h
#include "nsIProtocolHandler.h"
#include "nsWeakReference.h"
class nsIndexedDBProtocolHandler final : public nsIProtocolHandler,
public nsSupportsWeakReference {
public:
nsIndexedDBProtocolHandler();
private:
~nsIndexedDBProtocolHandler();
NS_DECL_THREADSAFE_ISUPPORTS
NS_DECL_NSIPROTOCOLHANDLER
};
#endif // nsIndexedDBProtocolHandler_h

View File

@ -9,6 +9,7 @@
#include "mozilla/dom/DOMException.h"
#include "mozilla/net/MozURL.h"
#include "mozIThirdPartyUtil.h"
#include "nsIEventTarget.h"
#include "nsIInputStream.h"
#include "nsILineInputStream.h"
@ -53,7 +54,8 @@ static const uint32_t kInvalidGeneration = static_cast<uint32_t>(-1);
StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
nsresult GetOrigin(const nsACString& aURL, nsACString& aOrigin) {
nsresult GetOriginAndBaseDomain(const nsACString& aURL, nsACString& aOrigin,
nsACString& aBaseDomain) {
RefPtr<net::MozURL> url;
nsresult rv = net::MozURL::Init(getter_AddRefs(url), aURL);
if (NS_WARN_IF(NS_FAILED(rv))) {
@ -61,6 +63,12 @@ nsresult GetOrigin(const nsACString& aURL, nsACString& aOrigin) {
}
url->Origin(aOrigin);
rv = url->BaseDomain(aBaseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
return NS_OK;
}
@ -106,7 +114,8 @@ nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
}
nsCString origin;
rv = GetOrigin(aEntry->scope(), origin);
nsCString baseDomain;
rv = GetOriginAndBaseDomain(aEntry->scope(), origin, baseDomain);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
@ -114,7 +123,8 @@ nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
// CSP will be applied during the script load.
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
aEntry->principal() = mozilla::ipc::ContentPrincipalInfo(
attrs, origin, aEntry->scope(), Nothing(), std::move(policies));
attrs, origin, aEntry->scope(), Nothing(), std::move(policies),
baseDomain);
return NS_OK;
}

View File

@ -275,7 +275,7 @@ TEST(ServiceWorkerRegistrar, TestWriteData) {
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
reg.principal() = mozilla::ipc::ContentPrincipalInfo(
mozilla::OriginAttributes(i, i % 2), spec, spec, mozilla::Nothing(),
std::move(policies));
std::move(policies), spec);
swr->TestRegisterServiceWorker(reg);
}
@ -865,7 +865,7 @@ TEST(ServiceWorkerRegistrar, TestDedupeWrite) {
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
reg.principal() = mozilla::ipc::ContentPrincipalInfo(
mozilla::OriginAttributes(0, false), spec, spec, mozilla::Nothing(),
std::move(policies));
std::move(policies), spec);
swr->TestRegisterServiceWorker(reg);
}

View File

@ -497,6 +497,11 @@ PBackgroundSDBConnectionParent* AllocPBackgroundSDBConnectionParent(
return nullptr;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(aPrincipalInfo))) {
ASSERT_UNLESS_FUZZING();
return nullptr;
}
RefPtr<Connection> actor = new Connection(aPrincipalInfo);
return actor.forget().take();

View File

@ -221,6 +221,10 @@ SDBConnection::Init(nsIPrincipal* aPrincipal) {
return NS_ERROR_INVALID_ARG;
}
if (NS_WARN_IF(!QuotaManager::IsPrincipalInfoValid(*principalInfo))) {
return NS_ERROR_INVALID_ARG;
}
mPrincipalInfo = std::move(principalInfo);
return NS_OK;

View File

@ -7,6 +7,8 @@
#ifndef mozilla_dom_StorageUtils_h
#define mozilla_dom_StorageUtils_h
#include "nsStringFwd.h"
class nsIPrincipal;
namespace mozilla {

View File

@ -7,8 +7,10 @@
<script type="text/javascript">
function startTest()
async function startTest()
{
await SpecialPowers.pushPrefEnv({"set": [["dom.storage.client_validation", false]]});
var url = "http://example.com/tests/dom/tests/mochitest/localstorage/frameChromeSlave.html";
var ios = Components.classes["@mozilla.org/network/io-service;1"]
.getService(Components.interfaces.nsIIOService);

View File

@ -126,7 +126,7 @@ nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow,
nsresult rv;
if (!mInteractive) {
rv = DisableJSAndPlugins(aWindow);
rv = DisableJSAndPlugins(*docShell);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -178,25 +178,20 @@ nsEditingSession::MakeWindowEditable(mozIDOMWindowProxy* aWindow,
return rv;
}
NS_IMETHODIMP
nsEditingSession::DisableJSAndPlugins(mozIDOMWindowProxy* aWindow) {
NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
nsIDocShell* docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
nsresult nsEditingSession::DisableJSAndPlugins(nsIDocShell& aDocShell) {
bool tmp;
nsresult rv = docShell->GetAllowJavascript(&tmp);
nsresult rv = aDocShell.GetAllowJavascript(&tmp);
NS_ENSURE_SUCCESS(rv, rv);
mScriptsEnabled = tmp;
rv = docShell->SetAllowJavascript(false);
rv = aDocShell.SetAllowJavascript(false);
NS_ENSURE_SUCCESS(rv, rv);
// Disable plugins in this document:
mPluginsEnabled = docShell->PluginsAllowedInCurrentDoc();
mPluginsEnabled = aDocShell.PluginsAllowedInCurrentDoc();
rv = docShell->SetAllowPlugins(false);
rv = aDocShell.SetAllowPlugins(false);
NS_ENSURE_SUCCESS(rv, rv);
mDisabledJSAndPlugins = true;
@ -204,16 +199,18 @@ nsEditingSession::DisableJSAndPlugins(mozIDOMWindowProxy* aWindow) {
return NS_OK;
}
NS_IMETHODIMP
nsEditingSession::RestoreJSAndPlugins(mozIDOMWindowProxy* aWindow) {
nsresult nsEditingSession::RestoreJSAndPlugins(nsPIDOMWindowOuter* aWindow) {
if (!mDisabledJSAndPlugins) {
return NS_OK;
}
mDisabledJSAndPlugins = false;
NS_ENSURE_TRUE(aWindow, NS_ERROR_FAILURE);
nsIDocShell* docShell = nsPIDOMWindowOuter::From(aWindow)->GetDocShell();
if (NS_WARN_IF(!aWindow)) {
// DetachFromWindow may call this method with nullptr.
return NS_ERROR_FAILURE;
}
nsIDocShell* docShell = aWindow->GetDocShell();
NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled);
@ -223,13 +220,6 @@ nsEditingSession::RestoreJSAndPlugins(mozIDOMWindowProxy* aWindow) {
return docShell->SetAllowPlugins(mPluginsEnabled);
}
NS_IMETHODIMP
nsEditingSession::GetJsAndPluginsDisabled(bool* aResult) {
NS_ENSURE_ARG_POINTER(aResult);
*aResult = mDisabledJSAndPlugins;
return NS_OK;
}
/*---------------------------------------------------------------------------
WindowIsEditable
@ -541,7 +531,7 @@ nsEditingSession::TearDownEditorOnWindow(mozIDOMWindowProxy* aWindow) {
if (stopEditing) {
// Make things the way they were before we started editing.
RestoreJSAndPlugins(aWindow);
RestoreJSAndPlugins(window);
RestoreAnimationMode(window);
if (mMakeWholeDocumentEditable) {
@ -1239,7 +1229,7 @@ nsresult nsEditingSession::DetachFromWindow(mozIDOMWindowProxy* aWindow) {
// make things the way they were before we started editing.
RemoveEditorControllers(window);
RemoveWebProgressListener(window);
RestoreJSAndPlugins(aWindow);
RestoreJSAndPlugins(window);
RestoreAnimationMode(window);
// Kill our weak reference to our original window, in case
@ -1267,7 +1257,7 @@ nsresult nsEditingSession::ReattachToWindow(mozIDOMWindowProxy* aWindow) {
// Disable plugins.
if (!mInteractive) {
rv = DisableJSAndPlugins(aWindow);
rv = DisableJSAndPlugins(*docShell);
NS_ENSURE_SUCCESS(rv, rv);
}

View File

@ -86,6 +86,17 @@ class nsEditingSession final : public nsIEditingSession,
void RemoveListenersAndControllers(nsPIDOMWindowOuter* aWindow,
mozilla::HTMLEditor* aHTMLEditor);
/**
* Disable scripts and plugins in aDocShell.
*/
nsresult DisableJSAndPlugins(nsIDocShell& aDocShell);
/**
* Restore JS and plugins (enable/disable them) according to the state they
* were before the last call to disableJSAndPlugins.
*/
nsresult RestoreJSAndPlugins(nsPIDOMWindowOuter* aWindow);
protected:
bool mDoneSetup; // have we prepared for editing yet?

View File

@ -80,17 +80,6 @@ interface nsIEditingSession : nsISupports
void setEditorOnControllers(in mozIDOMWindowProxy aWindow,
in nsIEditor aEditor);
/**
* Disable scripts and plugins in aWindow.
*/
void disableJSAndPlugins(in mozIDOMWindowProxy aWindow);
/**
* Restore JS and plugins (enable/disable them) according to the state they
* were before the last call to disableJSAndPlugins.
*/
void restoreJSAndPlugins(in mozIDOMWindowProxy aWindow);
/**
* Removes all the editor's controllers/listeners etc and makes the window
* uneditable.
@ -103,11 +92,6 @@ interface nsIEditingSession : nsISupports
*/
void reattachToWindow(in mozIDOMWindowProxy aWindow);
/**
* Whether this session has disabled JS and plugins.
*/
readonly attribute boolean jsAndPluginsDisabled;
%{C++
/**
* This method is implemented with nsIDocShell::GetHTMLEditor(). I.e.,

View File

@ -367,7 +367,7 @@ BackgroundParentImpl::AllocPBackgroundLSSimpleRequestParent(
AssertIsInMainOrSocketProcess();
AssertIsOnBackgroundThread();
return mozilla::dom::AllocPBackgroundLSSimpleRequestParent(aParams);
return mozilla::dom::AllocPBackgroundLSSimpleRequestParent(this, aParams);
}
mozilla::ipc::IPCResult
@ -984,6 +984,20 @@ bool BackgroundParentImpl::DeallocPQuotaParent(PQuotaParent* aActor) {
return mozilla::dom::quota::DeallocPQuotaParent(aActor);
}
mozilla::ipc::IPCResult BackgroundParentImpl::RecvShutdownQuotaManager() {
AssertIsInMainOrSocketProcess();
AssertIsOnBackgroundThread();
if (BackgroundParent::IsOtherProcessActor(this)) {
return IPC_FAIL_NO_REASON(this);
}
if (!mozilla::dom::quota::RecvShutdownQuotaManager()) {
return IPC_FAIL_NO_REASON(this);
}
return IPC_OK();
}
dom::PFileSystemRequestParent*
BackgroundParentImpl::AllocPFileSystemRequestParent(
const FileSystemParams& aParams) {

View File

@ -275,6 +275,8 @@ class BackgroundParentImpl : public PBackgroundParent {
virtual bool DeallocPQuotaParent(PQuotaParent* aActor) override;
virtual mozilla::ipc::IPCResult RecvShutdownQuotaManager() override;
virtual PFileSystemRequestParent* AllocPFileSystemRequestParent(
const FileSystemParams&) override;

View File

@ -130,6 +130,15 @@ already_AddRefed<nsIPrincipal> PrincipalInfoToPrincipal(
principal->SetCsp(csp);
}
if (!info.baseDomain().IsVoid()) {
nsAutoCString baseDomain;
rv = principal->GetBaseDomain(baseDomain);
if (NS_WARN_IF(NS_FAILED(rv)) ||
!info.baseDomain().Equals(baseDomain)) {
MOZ_CRASH("Base domain must be available when deserialized");
}
}
return principal.forget();
}
@ -312,9 +321,16 @@ nsresult PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
PopulateContentSecurityPolicies(csp, policies);
}
// This attribute is not crucial.
nsCString baseDomain;
if (NS_FAILED(aPrincipal->GetBaseDomain(baseDomain))) {
NS_WARNING("Failed to get base domain!");
baseDomain.SetIsVoid(true);
}
*aPrincipalInfo =
ContentPrincipalInfo(aPrincipal->OriginAttributesRef(), originNoSuffix,
spec, domain, std::move(policies));
spec, domain, std::move(policies), baseDomain);
return NS_OK;
}

View File

@ -191,6 +191,8 @@ parent:
async PQuota();
async ShutdownQuotaManager();
async PFileSystemRequest(FileSystemParams params);
async PGamepadEventChannel();

View File

@ -33,6 +33,9 @@ struct ContentPrincipalInfo
nsCString? domain;
ContentSecurityPolicy[] securityPolicies;
// Like originNoSuffix, baseDomain is used out of the main-thread.
nsCString baseDomain;
};
struct SystemPrincipalInfo

View File

@ -28,7 +28,7 @@ class JS_PUBLIC_API ContextOptions {
#ifdef ENABLE_WASM_CRANELIFT
wasmCranelift_(false),
#endif
#ifdef ENABLE_WASM_REFTYPES
#ifdef ENABLE_WASM_GC
wasmGc_(false),
#endif
testWasmAwaitTier2_(false),
@ -119,7 +119,7 @@ class JS_PUBLIC_API ContextOptions {
return *this;
}
#ifdef ENABLE_WASM_REFTYPES
#ifdef ENABLE_WASM_GC
bool wasmGc() const { return wasmGc_; }
ContextOptions& setWasmGc(bool flag) {
wasmGc_ = flag;
@ -210,7 +210,7 @@ class JS_PUBLIC_API ContextOptions {
setWasm(false);
setWasmBaseline(false);
setWasmIon(false);
#ifdef ENABLE_WASM_REFTYPES
#ifdef ENABLE_WASM_GC
setWasmGc(false);
#endif
setNativeRegExp(false);
@ -227,7 +227,7 @@ class JS_PUBLIC_API ContextOptions {
#ifdef ENABLE_WASM_CRANELIFT
bool wasmCranelift_ : 1;
#endif
#ifdef ENABLE_WASM_REFTYPES
#ifdef ENABLE_WASM_GC
bool wasmGc_ : 1;
#endif
bool testWasmAwaitTier2_ : 1;

View File

@ -4064,7 +4064,7 @@ static MOZ_ALWAYS_INLINE ArrayObject* NewArray(
AutoSetNewObjectMetadata metadata(cx);
RootedArrayObject arr(
cx, ArrayObject::createArray(
cx, allocKind, GetInitialHeap(newKind, &ArrayObject::class_),
cx, allocKind, GetInitialHeap(newKind, group),
shape, group, length, metadata));
if (!arr) {
return nullptr;
@ -4153,7 +4153,7 @@ ArrayObject* js::NewDenseFullyAllocatedArrayWithTemplate(
RootedObjectGroup group(cx, templateObject->group());
RootedShape shape(cx, templateObject->as<ArrayObject>().lastProperty());
gc::InitialHeap heap = GetInitialHeap(GenericObject, &ArrayObject::class_);
gc::InitialHeap heap = GetInitialHeap(GenericObject, group);
Rooted<ArrayObject*> arr(
cx, ArrayObject::createArray(cx, allocKind, heap, shape, group, length,
metadata));
@ -4171,10 +4171,11 @@ ArrayObject* js::NewDenseFullyAllocatedArrayWithTemplate(
}
ArrayObject* js::NewDenseCopyOnWriteArray(JSContext* cx,
HandleArrayObject templateObject,
gc::InitialHeap heap) {
HandleArrayObject templateObject) {
MOZ_ASSERT(!gc::IsInsideNursery(templateObject));
gc::InitialHeap heap = GetInitialHeap(GenericObject, templateObject->group());
ArrayObject* arr =
ArrayObject::createCopyOnWriteArray(cx, heap, templateObject);
if (!arr) {

View File

@ -79,8 +79,7 @@ extern ArrayObject* NewDenseFullyAllocatedArrayWithTemplate(
// Create a dense array with the same copy-on-write elements as another object.
extern ArrayObject* NewDenseCopyOnWriteArray(JSContext* cx,
HandleArrayObject templateObject,
gc::InitialHeap heap);
HandleArrayObject templateObject);
extern ArrayObject* NewFullyAllocatedArrayTryUseGroup(
JSContext* cx, HandleObjectGroup group, size_t length,

View File

@ -1553,7 +1553,7 @@ static MOZ_MUST_USE JSObject* ReadableStreamCreateReadResult(
NativeObject* obj;
JS_TRY_VAR_OR_RETURN_NULL(
cx, obj,
NativeObject::createWithTemplate(cx, gc::DefaultHeap, templateObject));
NativeObject::createWithTemplate(cx, templateObject));
// Step 5: Perform CreateDataProperty(obj, "value", value).
obj->setSlot(Realm::IterResultObjectValueSlot, value);

View File

@ -701,11 +701,7 @@ static bool WasmReftypesEnabled(JSContext* cx, unsigned argc, Value* vp) {
static bool WasmGcEnabled(JSContext* cx, unsigned argc, Value* vp) {
CallArgs args = CallArgsFromVp(argc, vp);
#ifdef ENABLE_WASM_GC
args.rval().setBoolean(wasm::HasReftypesSupport(cx));
#else
args.rval().setBoolean(false);
#endif
args.rval().setBoolean(wasm::HasGcSupport(cx));
return true;
}
@ -6031,15 +6027,15 @@ gc::ZealModeHelpText),
"This will return true early if compilation isn't two-tiered. "),
JS_FN_HELP("wasmReftypesEnabled", WasmReftypesEnabled, 1, 0,
"wasmReftypesEnabled(bool)",
"wasmReftypesEnabled()",
" Returns a boolean indicating whether the WebAssembly reftypes proposal is enabled."),
JS_FN_HELP("wasmGcEnabled", WasmGcEnabled, 1, 0,
"wasmGcEnabled(bool)",
"wasmGcEnabled()",
" Returns a boolean indicating whether the WebAssembly GC types proposal is enabled."),
JS_FN_HELP("wasmDebugSupport", WasmDebugSupport, 1, 0,
"wasmDebugSupport(bool)",
"wasmDebugSupport()",
" Returns a boolean indicating whether the WebAssembly compilers support debugging."),
JS_FN_HELP("isLazyFunction", IsLazyFunction, 1, 0,

View File

@ -2317,7 +2317,7 @@ bool TypedObject::construct(JSContext* cx, unsigned int argc, Value* vp) {
MOZ_ASSERT(::IsTypedObjectClass(clasp));
JSObject* obj =
js::Allocate<JSObject>(cx, kind, /* nDynamicSlots = */ 0, heap, clasp);
js::AllocateObject(cx, kind, /* nDynamicSlots = */ 0, heap, clasp);
if (!obj) {
return cx->alreadyReportedOOM();
}

View File

@ -25,11 +25,9 @@
using namespace js;
using namespace gc;
template <typename T, AllowGC allowGC /* = CanGC */>
JSObject* js::Allocate(JSContext* cx, AllocKind kind, size_t nDynamicSlots,
InitialHeap heap, const Class* clasp) {
static_assert(mozilla::IsConvertible<T*, JSObject*>::value,
"must be JSObject derived");
template <AllowGC allowGC /* = CanGC */>
JSObject* js::AllocateObject(JSContext* cx, AllocKind kind, size_t nDynamicSlots,
InitialHeap heap, const Class* clasp) {
MOZ_ASSERT(IsObjectAllocKind(kind));
size_t thingSize = Arena::thingSize(kind);
@ -77,16 +75,16 @@ JSObject* js::Allocate(JSContext* cx, AllocKind kind, size_t nDynamicSlots,
return GCRuntime::tryNewTenuredObject<allowGC>(cx, kind, thingSize,
nDynamicSlots);
}
template JSObject* js::Allocate<JSObject, NoGC>(JSContext* cx,
gc::AllocKind kind,
size_t nDynamicSlots,
gc::InitialHeap heap,
const Class* clasp);
template JSObject* js::Allocate<JSObject, CanGC>(JSContext* cx,
gc::AllocKind kind,
size_t nDynamicSlots,
gc::InitialHeap heap,
const Class* clasp);
template JSObject* js::AllocateObject<NoGC>(JSContext* cx,
gc::AllocKind kind,
size_t nDynamicSlots,
gc::InitialHeap heap,
const Class* clasp);
template JSObject* js::AllocateObject<CanGC>(JSContext* cx,
gc::AllocKind kind,
size_t nDynamicSlots,
gc::InitialHeap heap,
const Class* clasp);
// Attempt to allocate a new JSObject out of the nursery. If there is not
// enough room in the nursery or there is an OOM, this method will return
@ -177,7 +175,7 @@ JSString* GCRuntime::tryNewNurseryString(JSContext* cx, size_t thingSize,
}
template <typename StringAllocT, AllowGC allowGC /* = CanGC */>
StringAllocT* js::AllocateString(JSContext* cx, InitialHeap heap) {
StringAllocT* js::AllocateStringImpl(JSContext* cx, InitialHeap heap) {
static_assert(mozilla::IsConvertible<StringAllocT*, JSString*>::value,
"must be JSString derived");
@ -224,10 +222,10 @@ StringAllocT* js::AllocateString(JSContext* cx, InitialHeap heap) {
#define DECL_ALLOCATOR_INSTANCES(allocKind, traceKind, type, sizedType, \
bgfinal, nursery, compact) \
template type* js::AllocateString<type, NoGC>(JSContext * cx, \
InitialHeap heap); \
template type* js::AllocateString<type, CanGC>(JSContext * cx, \
InitialHeap heap);
template type* js::AllocateStringImpl<type, NoGC>(JSContext * cx, \
InitialHeap heap); \
template type* js::AllocateStringImpl<type, CanGC>(JSContext * cx, \
InitialHeap heap);
FOR_EACH_NURSERY_STRING_ALLOCKIND(DECL_ALLOCATOR_INSTANCES)
#undef DECL_ALLOCATOR_INSTANCES

View File

@ -17,47 +17,52 @@ namespace js {
struct Class;
// Allocate a new GC thing. After a successful allocation the caller must
// fully initialize the thing before calling any function that can potentially
// trigger GC. This will ensure that GC tracing never sees junk values stored
// in the partially initialized thing.
// Allocate a new GC thing that's not a JSObject or a string.
//
// After a successful allocation the caller must fully initialize the thing
// before calling any function that can potentially trigger GC. This will ensure
// that GC tracing never sees junk values stored in the partially initialized
// thing.
template <typename T, AllowGC allowGC = CanGC>
T* Allocate(JSContext* cx);
// Use for JSObject. A longer signature that includes additional information in
// support of various optimizations. If dynamic slots are requested they will be
// allocated and the pointer stored directly in |NativeObject::slots_|.
template <typename, AllowGC allowGC = CanGC>
JSObject* Allocate(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots,
gc::InitialHeap heap, const Class* clasp);
// Allocate a JSObject.
//
// A longer signature that includes additional information in support of various
// optimizations. If dynamic slots are requested they will be allocated and the
// pointer stored directly in |NativeObject::slots_|.
template <AllowGC allowGC = CanGC>
JSObject* AllocateObject(JSContext* cx, gc::AllocKind kind, size_t nDynamicSlots,
gc::InitialHeap heap, const Class* clasp);
// Internal function used for nursery-allocatable strings.
template <typename StringAllocT, AllowGC allowGC = CanGC>
StringAllocT* AllocateString(JSContext* cx, gc::InitialHeap heap);
StringAllocT* AllocateStringImpl(JSContext* cx, gc::InitialHeap heap);
// Allocate a string.
//
// Use for nursery-allocatable strings. Returns a value cast to the correct
// type.
template <typename StringT, AllowGC allowGC = CanGC>
StringT* Allocate(JSContext* cx, gc::InitialHeap heap) {
return static_cast<StringT*>(js::AllocateString<JSString, allowGC>(cx, heap));
StringT* AllocateString(JSContext* cx, gc::InitialHeap heap) {
return static_cast<StringT*>(AllocateStringImpl<JSString, allowGC>(cx, heap));
}
// Specialization for JSFatInlineString that must use a different allocation
// type. Note that we have to explicitly specialize for both values of AllowGC
// because partial function specialization is not allowed.
template <>
inline JSFatInlineString* Allocate<JSFatInlineString, CanGC>(
inline JSFatInlineString* AllocateString<JSFatInlineString, CanGC>(
JSContext* cx, gc::InitialHeap heap) {
return static_cast<JSFatInlineString*>(
js::AllocateString<JSFatInlineString, CanGC>(cx, heap));
js::AllocateStringImpl<JSFatInlineString, CanGC>(cx, heap));
}
template <>
inline JSFatInlineString* Allocate<JSFatInlineString, NoGC>(
inline JSFatInlineString* AllocateString<JSFatInlineString, NoGC>(
JSContext* cx, gc::InitialHeap heap) {
return static_cast<JSFatInlineString*>(
js::AllocateString<JSFatInlineString, NoGC>(cx, heap));
js::AllocateStringImpl<JSFatInlineString, NoGC>(cx, heap));
}
} // namespace js

View File

@ -109,6 +109,54 @@ const SimdPrefix = 0xfd;
const ThreadPrefix = 0xfe;
const MozPrefix = 0xff;
// See WasmConstants.h for documentation.
// Limit this to a group of 8 per line.
const definedOpcodes =
[0x00, 0x01, 0x02, 0x03, 0x04, 0x05,
0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
0x10, 0x11,
0x1a, 0x1b,
0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26,
0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,
0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,
0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,
0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
0xc0, 0xc1, 0xc2, 0xc3, 0xc4,
0xd0, 0xd1, 0xd2,
0xf0,
0xfc, 0xfe, 0xff ];
const undefinedOpcodes = (function () {
let a = [];
let j = 0;
let i = 0;
while (i < 256) {
while (definedOpcodes[j] > i)
a.push(i++);
assertEq(definedOpcodes[j], i);
i++;
j++;
}
assertEq(definedOpcodes.length + a.length, 256);
return a;
})();
// Secondary opcode bytes for misc prefix
const MemoryInitCode = 0x08; // Pending
const DataDropCode = 0x09; // Pending

View File

@ -235,8 +235,8 @@ for (var bad of [0xff, 0, 1, 0x3f])
assertErrorMessage(() => wasmEval(moduleWithSections([sigSection([v2vSig]), declSection([0]), bodySection([funcBody({locals:[], body:[BlockCode, bad, EndCode]})])])), CompileError, /invalid inline block type/);
// Ensure all invalid opcodes rejected
for (let i = FirstInvalidOpcode; i <= LastInvalidOpcode; i++) {
let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[i]})])]);
for (let op of undefinedOpcodes) {
let binary = moduleWithSections([v2vSigSection, declSection([0]), bodySection([funcBody({locals:[], body:[op]})])]);
assertErrorMessage(() => wasmEval(binary), CompileError, /unrecognized opcode/);
assertEq(WebAssembly.validate(binary), false);
}

View File

@ -58,7 +58,6 @@ for (let v of VALUES)
let g = new WebAssembly.Global({value: "anyref"}, v);
let ins = wasmEvalText(
`(module
(gc_feature_opt_in 3)
(import $glob "m" "g" (global anyref))
(func (export "f") (result anyref)
(get_global $glob)))`,
@ -73,7 +72,6 @@ for (let v of VALUES)
let g = new WebAssembly.Global({value: "anyref", mutable: true});
let ins = wasmEvalText(
`(module
(gc_feature_opt_in 3)
(import $glob "m" "g" (global (mut anyref)))
(func (export "f") (param $v anyref)
(set_global $glob (get_local $v))))`,
@ -112,7 +110,6 @@ for (let v of VALUES)
let t = new WebAssembly.Table({element: "anyref", initial: 10});
let ins = wasmEvalText(
`(module
(gc_feature_opt_in 3)
(import $t "m" "t" (table 10 anyref))
(func (export "f") (param $v anyref)
(table.set $t (i32.const 3) (get_local $v))))`,
@ -128,7 +125,6 @@ for (let v of VALUES)
let t = new WebAssembly.Table({element: "anyref", initial: 10});
let ins = wasmEvalText(
`(module
(gc_feature_opt_in 3)
(import $t "m" "t" (table 10 anyref))
(func (export "f") (result anyref)
(table.get $t (i32.const 3))))`,
@ -146,7 +142,6 @@ for (let v of VALUES)
let receiver = function (w) { assertEq(w, v); };
let ins = wasmEvalText(
`(module
(gc_feature_opt_in 3)
(import $returner "m" "returner" (func (result anyref)))
(import $receiver "m" "receiver" (func (param anyref)))
(func (export "test_returner") (result anyref)

Some files were not shown because too many files have changed in this diff Show More