mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-26 12:20:56 +00:00
Merge inbound to mozilla-central a=merge
This commit is contained in:
commit
85ad02d41a
1
Cargo.lock
generated
1
Cargo.lock
generated
@ -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",
|
||||
]
|
||||
|
||||
|
@ -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],
|
||||
|
@ -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) {
|
||||
|
@ -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"
|
||||
|
@ -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");
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
});
|
||||
}
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
},
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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()));
|
||||
}
|
||||
|
||||
|
@ -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]
|
||||
|
@ -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");
|
||||
});
|
@ -0,0 +1,9 @@
|
||||
<body>
|
||||
<script>
|
||||
var script = `function foo() {
|
||||
console.log('called foo');
|
||||
}
|
||||
//# sourceURL=my-foo.js`;
|
||||
eval(script);
|
||||
</script>
|
||||
</body>
|
@ -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");
|
||||
}
|
||||
|
@ -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]
|
||||
|
@ -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"
|
||||
);
|
||||
});
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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;",
|
||||
|
@ -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"
|
||||
);
|
||||
});
|
||||
|
@ -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"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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>
|
||||
|
@ -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,
|
||||
|
@ -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";
|
||||
|
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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:
|
||||
|
13
dom/cache/CacheStorage.cpp
vendored
13
dom/cache/CacheStorage.cpp
vendored
@ -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:
|
||||
//
|
||||
|
7
dom/cache/CacheStorageParent.cpp
vendored
7
dom/cache/CacheStorageParent.cpp
vendored
@ -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);
|
||||
}
|
||||
|
||||
|
11
dom/cache/DBSchema.cpp
vendored
11
dom/cache/DBSchema.cpp
vendored
@ -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;
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
};
|
||||
|
||||
|
@ -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) {}
|
||||
|
@ -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);
|
||||
|
@ -11,6 +11,7 @@ EXPORTS.mozilla.dom += [
|
||||
'ClientIPCUtils.h',
|
||||
'ClientManager.h',
|
||||
'ClientManagerActors.h',
|
||||
'ClientManagerService.h',
|
||||
'ClientOpenWindowOpActors.h',
|
||||
'ClientOpPromise.h',
|
||||
'ClientSource.h',
|
||||
|
@ -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());
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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!");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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());
|
||||
|
@ -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(
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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!");
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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',
|
||||
]
|
||||
|
113
dom/localstorage/test/gtest/TestLocalStorage.cpp
Normal file
113
dom/localstorage/test/gtest/TestLocalStorage.cpp
Normal 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);
|
||||
}
|
||||
}
|
17
dom/localstorage/test/gtest/moz.build
Normal file
17
dom/localstorage/test/gtest/moz.build
Normal 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
@ -13,10 +13,14 @@ namespace quota {
|
||||
|
||||
class PQuotaParent;
|
||||
|
||||
void InitializeQuotaManager();
|
||||
|
||||
PQuotaParent* AllocPQuotaParent();
|
||||
|
||||
bool DeallocPQuotaParent(PQuotaParent* aActor);
|
||||
|
||||
bool RecvShutdownQuotaManager();
|
||||
|
||||
} // namespace quota
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
@ -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() {}
|
||||
};
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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
14
dom/quota/components.conf
Normal 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'],
|
||||
},
|
||||
]
|
@ -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',
|
||||
|
63
dom/quota/nsIndexedDBProtocolHandler.cpp
Normal file
63
dom/quota/nsIndexedDBProtocolHandler.cpp
Normal 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;
|
||||
}
|
25
dom/quota/nsIndexedDBProtocolHandler.h
Normal file
25
dom/quota/nsIndexedDBProtocolHandler.h
Normal 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
|
@ -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;
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -7,6 +7,8 @@
|
||||
#ifndef mozilla_dom_StorageUtils_h
|
||||
#define mozilla_dom_StorageUtils_h
|
||||
|
||||
#include "nsStringFwd.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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?
|
||||
|
||||
|
@ -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.,
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -191,6 +191,8 @@ parent:
|
||||
|
||||
async PQuota();
|
||||
|
||||
async ShutdownQuotaManager();
|
||||
|
||||
async PFileSystemRequest(FileSystemParams params);
|
||||
|
||||
async PGamepadEventChannel();
|
||||
|
@ -33,6 +33,9 @@ struct ContentPrincipalInfo
|
||||
nsCString? domain;
|
||||
|
||||
ContentSecurityPolicy[] securityPolicies;
|
||||
|
||||
// Like originNoSuffix, baseDomain is used out of the main-thread.
|
||||
nsCString baseDomain;
|
||||
};
|
||||
|
||||
struct SystemPrincipalInfo
|
||||
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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
Loading…
x
Reference in New Issue
Block a user