Merge inbound to m-c. a=merge
Merge inbound to m-c. a=merge
--- a/browser/components/contextualidentity/test/browser/browser.ini
+++ b/browser/components/contextualidentity/test/browser/browser.ini
@@ -8,16 +8,19 @@ support-files =
serviceworker.html
worker.js
[browser_aboutURLs.js]
skip-if = (debug && (os == "win" || os == "linux")) # intermittent negative leak bug 1271182
[browser_eme.js]
[browser_favicon.js]
[browser_forgetaboutsite.js]
+[browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js]
+[browser_forgetAPI_EME_forgetThisSite.js]
+[browser_forgetAPI_quota_clearStoragesForPrincipal.js]
[browser_usercontext.js]
[browser_usercontextid_tabdrop.js]
skip-if = os == "mac" || os == "win" # Intermittent failure - bug 1268276
[browser_windowName.js]
tags = openwindow
[browser_windowOpen.js]
tags = openwindow
[browser_serviceworkers.js]
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_EME_forgetThisSite.js
@@ -0,0 +1,218 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the media key.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+
+const TEST_EME_KEY = {
+ initDataType: 'keyids',
+ initData: '{"kids":["LwVHf8JLtPrv2GUXFW2v_A"], "type":"persistent-license"}',
+ kid: "LwVHf8JLtPrv2GUXFW2v_A",
+ key: "97b9ddc459c8d5ff23c1f2754c95abe8",
+ sessionType: 'persistent-license',
+};
+
+//
+// Support functions.
+//
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+ return {tab, browser};
+}
+
+function HexToBase64(hex) {
+ var bin = "";
+ for (var i = 0; i < hex.length; i += 2) {
+ bin += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
+ }
+ return window.btoa(bin).replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
+}
+
+function Base64ToHex(str) {
+ var bin = window.atob(str.replace(/-/g, "+").replace(/_/g, "/"));
+ var res = "";
+ for (var i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+ return res;
+}
+
+function ByteArrayToHex(array) {
+ let bin = String.fromCharCode.apply(null, new Uint8Array(array));
+ let res = "";
+
+ for (let i = 0; i < bin.length; i++) {
+ res += ("0" + bin.charCodeAt(i).toString(16)).substr(-2);
+ }
+
+ return res;
+}
+
+function generateKeyObject(aKid, aKey) {
+ let keyObj = {
+ kty: 'oct',
+ kid: aKid,
+ k: HexToBase64(aKey),
+ };
+
+ return new TextEncoder().encode(JSON.stringify({
+ keys: [keyObj]
+ }));
+}
+
+function generateKeyInfo(aData) {
+ let keyInfo = {
+ initDataType: aData.initDataType,
+ initData: new TextEncoder().encode(aData.initData),
+ sessionType: aData.sessionType,
+ keyObj: generateKeyObject(aData.kid, aData.key),
+ };
+
+ return keyInfo;
+}
+
+// Setup a EME key for the given browser, and return the sessionId.
+function* setupEMEKey(browser) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+
+ // Setup the EME key.
+ let result = yield ContentTask.spawn(browser, keyInfo, function* (aKeyInfo) {
+ let access = yield content.navigator.requestMediaKeySystemAccess('org.w3.clearkey',
+ [{
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{contentType: 'video/webm'}],
+ sessionTypes: ['persistent-license'],
+ persistentState: 'required',
+ }]);
+ let mediaKeys = yield access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+ let res = {};
+
+ // Insert the EME key.
+ let result = yield new Promise(resolve => {
+ session.addEventListener("message", function(event) {
+ session.update(aKeyInfo.keyObj).then(
+ () => { resolve(); }
+ ).catch(
+ () => {
+ ok(false, "Update the EME key fail.");
+ resolve();
+ }
+ );
+ });
+
+ session.generateRequest(aKeyInfo.initDataType, aKeyInfo.initData);
+ });
+
+ let map = session.keyStatuses;
+
+ is(map.size, 1, "One EME key has been added.");
+
+ if (map.size === 1) {
+ res.keyId = map.keys().next().value;
+ res.sessionId = session.sessionId;
+ }
+
+ // Close the session.
+ session.close();
+ yield session.closed;
+
+ return res;
+ });
+
+ // Check the EME key ID.
+ is(ByteArrayToHex(result.keyId), Base64ToHex(TEST_EME_KEY.kid), "The key Id is correct.");
+ return result.sessionId;
+}
+
+// Check whether the EME key has been cleared.
+function* checkEMEKey(browser, emeSessionId) {
+ // Generate the key info.
+ let keyInfo = generateKeyInfo(TEST_EME_KEY);
+ keyInfo.sessionId = emeSessionId;
+
+ yield ContentTask.spawn(browser, keyInfo, function* (aKeyInfo) {
+ let access = yield content.navigator.requestMediaKeySystemAccess('org.w3.clearkey',
+ [{
+ initDataTypes: [aKeyInfo.initDataType],
+ videoCapabilities: [{contentType: 'video/webm'}],
+ sessionTypes: ['persistent-license'],
+ persistentState: 'required',
+ }]);
+ let mediaKeys = yield access.createMediaKeys();
+ let session = mediaKeys.createSession(aKeyInfo.sessionType);
+
+ // First, load the session with the sessionId.
+ yield session.load(aKeyInfo.sessionId);
+
+ let map = session.keyStatuses;
+
+ // Check that there is no media key here.
+ is(map.size, 0, "No media key should be here after forgetThisSite() was called.");
+ });
+}
+
+//
+// Test functions.
+//
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ [ "media.mediasource.enabled", true ],
+ [ "media.eme.apiVisible", true ],
+ [ "media.mediasource.webm.enabled", true ],
+ ]});
+});
+
+add_task(function* test_EME_forgetThisSite() {
+ let tabs = [];
+ let emeSessionIds = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Setup EME Key.
+ emeSessionIds[userContextId] = yield setupEMEKey(tabs[userContextId].browser);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Clear all EME data for a given domain with originAttributes pattern.
+ let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].
+ getService(Ci.mozIGeckoMediaPluginChromeService);
+ mps.forgetThisSite(TEST_HOST, JSON.stringify({}));
+
+ // Open tabs again to check EME keys have been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Check whether EME Key has been cleared.
+ yield checkEMEKey(tabs[userContextId].browser, emeSessionIds[userContextId]);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_cookie_getCookiesWithOriginAttributes.js
@@ -0,0 +1,86 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for cookies.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+
+//
+// Support functions.
+//
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+ return {tab, browser};
+}
+
+function getCookiesForOA(host, userContextId) {
+ return Services.cookies.getCookiesFromHost(host, {userContextId});
+}
+
+//
+// Test functions.
+//
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ ]});
+});
+
+add_task(function* test_cookie_getCookiesWithOriginAttributes() {
+ let tabs = [];
+ let cookieName = "userContextId";
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Load the page in 2 different contexts and set a cookie
+ // which should only be visible in that context.
+ let value = USER_CONTEXTS[userContextId];
+
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "file_reflect_cookie_into_title.html?" + value, userContextId);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Check that cookies have been set properly.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+ ok(enumerator.hasMoreElements(), "Cookies available");
+
+ let foundCookie = enumerator.getNext().QueryInterface(Ci.nsICookie2);
+ is(foundCookie["name"], cookieName, "Check cookie name");
+ is(foundCookie["value"], USER_CONTEXTS[userContextId], "Check cookie value");
+ }
+
+ // Using getCookiesWithOriginAttributes() to get all cookies for a certain
+ // domain by using the originAttributes pattern, and clear all these cookies.
+ let enumerator = Services.cookies.getCookiesWithOriginAttributes(JSON.stringify({}), TEST_HOST);
+ while (enumerator.hasMoreElements()) {
+ let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+ Services.cookies.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
+ }
+
+ // Check that whether cookies has been cleared.
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ let enumerator = getCookiesForOA(TEST_HOST, userContextId);
+ ok(!enumerator.hasMoreElements(), "No Cookie should be here");
+ }
+});
new file mode 100644
--- /dev/null
+++ b/browser/components/contextualidentity/test/browser/browser_forgetAPI_quota_clearStoragesForPrincipal.js
@@ -0,0 +1,148 @@
+/*
+ * Bug 1278037 - A Test case for checking whether forgetting APIs are working for the quota manager.
+ */
+
+const { classes: Cc, Constructor: CC, interfaces: Ci, utils: Cu } = Components;
+
+const TEST_HOST = "example.com";
+const TEST_URL = "http://" + TEST_HOST + "/browser/browser/components/contextualidentity/test/browser/";
+
+const USER_CONTEXTS = [
+ "default",
+ "personal",
+];
+
+//
+// Support functions.
+//
+
+function* openTabInUserContext(uri, userContextId) {
+ // Open the tab in the correct userContextId.
+ let tab = gBrowser.addTab(uri, {userContextId});
+
+ // Select tab and make sure its browser is focused.
+ gBrowser.selectedTab = tab;
+ tab.ownerGlobal.focus();
+
+ let browser = gBrowser.getBrowserForTab(tab);
+ yield BrowserTestUtils.browserLoaded(browser);
+ return {tab, browser};
+}
+
+// Setup an entry for the indexedDB.
+function* setupIndexedDB(browser) {
+ yield ContentTask.spawn(browser, { input: "TestForgetAPIs" }, function* (arg) {
+ let request = content.indexedDB.open("idb", 1);
+
+ request.onerror = function() {
+ throw new Error("error opening db connection");
+ };
+
+ request.onupgradeneeded = event => {
+ let db = event.target.result;
+ let store = db.createObjectStore("obj", { keyPath: "id" });
+ store.createIndex("userContext", "userContext", { unique: false });
+ };
+
+ let db = yield new Promise(resolve => {
+ request.onsuccess = event => {
+ resolve(event.target.result);
+ };
+ });
+
+ // Add an entry into the indexedDB.
+ let transaction = db.transaction(["obj"], "readwrite");
+ let store = transaction.objectStore("obj");
+ store.add({id: 1, userContext: arg.input});
+
+ yield new Promise(resolve => {
+ transaction.oncomplete = () => {
+ resolve();
+ };
+ });
+
+ // Check the indexedDB has been set properly.
+ transaction = db.transaction(["obj"], "readonly");
+ store = transaction.objectStore("obj");
+ let getRequest = store.get(1);
+ yield new Promise(resolve => {
+ getRequest.onsuccess = () => {
+ let res = getRequest.result;
+ is(res.userContext, arg.input, "Check the indexedDB value");
+ resolve();
+ };
+ });
+ });
+}
+
+// Check whether the indexedDB has been cleared.
+function* checkIndexedDB(browser) {
+ yield ContentTask.spawn(browser, null, function* () {
+ let request = content.indexedDB.open("idb", 1);
+
+ let db = yield new Promise(done => {
+ request.onsuccess = event => {
+ done(event.target.result);
+ };
+ });
+
+ try {
+ let transaction = db.transaction(["obj"], "readonly");
+ ok(false, "The indexedDB should not exist");
+ } catch (e) {
+ is(e.name, "NotFoundError", "The indexedDB does not exist as expected");
+ }
+ });
+}
+
+//
+// Test functions.
+//
+
+add_task(function* setup() {
+ // Make sure userContext is enabled.
+ yield SpecialPowers.pushPrefEnv({"set": [
+ [ "privacy.userContext.enabled", true ],
+ ]});
+});
+
+add_task(function* test_quota_clearStoragesForPrincipal() {
+ let tabs = [];
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Setup an entry for the indexedDB.
+ yield setupIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+
+ // Using quota manager to clear all indexed DB for a given domain.
+ let qms = Cc["@mozilla.org/dom/quota-manager-service;1"].
+ getService(Ci.nsIQuotaManagerService);
+
+ let caUtils = {};
+ let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
+ getService(Ci.mozIJSSubScriptLoader);
+ scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
+ caUtils);
+ let httpURI = caUtils.makeURI("http://" + TEST_HOST);
+ let httpPrincipal = Services.scriptSecurityManager
+ .createCodebasePrincipal(httpURI, {});
+ qms.clearStoragesForPrincipal(httpPrincipal, null, true);
+
+ for (let userContextId of Object.keys(USER_CONTEXTS)) {
+ // Open our tab in the given user context.
+ tabs[userContextId] = yield* openTabInUserContext(TEST_URL+ "empty_file.html", userContextId);
+
+ // Check whether indexed DB has been cleared.
+ yield checkIndexedDB(tabs[userContextId].browser);
+
+ // Close this tab.
+ yield BrowserTestUtils.removeTab(tabs[userContextId].tab);
+ }
+});
+
--- a/dom/media/gmp/GMPServiceParent.cpp
+++ b/dom/media/gmp/GMPServiceParent.cpp
@@ -1548,29 +1548,48 @@ ExtractHostName(const nsACString& aOrigi
}
nsDependentCSubstring host(str, begin + 3);
aOutData.Assign(host);
return true;
}
bool
-MatchOrigin(nsIFile* aPath, const nsACString& aSite)
+MatchOrigin(nsIFile* aPath,
+ const nsACString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern)
{
// http://en.wikipedia.org/wiki/Domain_Name_System#Domain_name_syntax
static const uint32_t MaxDomainLength = 253;
nsresult rv;
nsCString str;
+ nsCString originNoSuffix;
+ mozilla::PrincipalOriginAttributes originAttributes;
+
rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("origin"), str, MaxDomainLength);
- if (NS_SUCCEEDED(rv) && ExtractHostName(str, str) && str.Equals(aSite)) {
+ if (!originAttributes.PopulateFromOrigin(str, originNoSuffix)) {
+ // Fails on parsing the originAttributes, treat this as a non-match.
+ return false;
+ }
+
+ if (NS_SUCCEEDED(rv) && ExtractHostName(originNoSuffix, str) && str.Equals(aSite) &&
+ aPattern.Matches(originAttributes)) {
return true;
}
+
+ mozilla::PrincipalOriginAttributes topLevelOriginAttributes;
rv = ReadFromFile(aPath, NS_LITERAL_CSTRING("topLevelOrigin"), str, MaxDomainLength);
- if (NS_SUCCEEDED(rv) && ExtractHostName(str, str) && str.Equals(aSite)) {
+ if (!topLevelOriginAttributes.PopulateFromOrigin(str, originNoSuffix)) {
+ // Fails on paring the originAttributes, treat this as a non-match.
+ return false;
+ }
+
+ if (NS_SUCCEEDED(rv) && ExtractHostName(originNoSuffix, str) && str.Equals(aSite) &&
+ aPattern.Matches(topLevelOriginAttributes)) {
return true;
}
return false;
}
template<typename T> static void
KillPlugins(const nsTArray<RefPtr<GMPParent>>& aPlugins,
Mutex& aMutex, T&& aFilter)
@@ -1696,29 +1715,35 @@ GeckoMediaPluginServiceParent::ClearNode
if (NS_FAILED(DeleteDir(dirEntry))) {
NS_WARNING("Failed to delete GMP storage directory for the node");
}
}
}
void
-GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread(const nsACString& aSite)
+GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread(const nsACString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
LOGD(("%s::%s: origin=%s", __CLASS__, __FUNCTION__, aSite.Data()));
struct OriginFilter : public DirectoryFilter {
- explicit OriginFilter(const nsACString& aSite) : mSite(aSite) {}
+ explicit OriginFilter(const nsACString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern)
+ : mSite(aSite)
+ , mPattern(aPattern)
+ { }
bool operator()(nsIFile* aPath) override {
- return MatchOrigin(aPath, mSite);
+ return MatchOrigin(aPath, mSite, mPattern);
}
private:
const nsACString& mSite;
- } filter(aSite);
+ const mozilla::OriginAttributesPattern& mPattern;
+ } filter(aSite, aPattern);
ClearNodeIdAndPlugin(filter);
}
void
GeckoMediaPluginServiceParent::ClearRecentHistoryOnGMPThread(PRTime aSince)
{
MOZ_ASSERT(NS_GetCurrentThread() == mGMPThread);
@@ -1778,22 +1803,39 @@ GeckoMediaPluginServiceParent::ClearRece
} filter(aSince);
ClearNodeIdAndPlugin(filter);
NS_DispatchToMainThread(new NotifyObserversTask("gmp-clear-storage-complete"), NS_DISPATCH_NORMAL);
}
NS_IMETHODIMP
-GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite)
+GeckoMediaPluginServiceParent::ForgetThisSite(const nsAString& aSite,
+ const nsAString& aPattern)
{
MOZ_ASSERT(NS_IsMainThread());
- return GMPDispatch(NewRunnableMethod<nsCString>(
+
+ mozilla::OriginAttributesPattern pattern;
+
+ if (!pattern.Init(aPattern)) {
+ return NS_ERROR_INVALID_ARG;
+ }
+
+ return ForgetThisSiteNative(aSite, pattern);
+}
+
+nsresult
+GeckoMediaPluginServiceParent::ForgetThisSiteNative(const nsAString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern)
+{
+ MOZ_ASSERT(NS_IsMainThread());
+
+ return GMPDispatch(NewRunnableMethod<nsCString, mozilla::OriginAttributesPattern>(
this, &GeckoMediaPluginServiceParent::ForgetThisSiteOnGMPThread,
- NS_ConvertUTF16toUTF8(aSite)));
+ NS_ConvertUTF16toUTF8(aSite), aPattern));
}
static bool IsNodeIdValid(GMPParent* aParent) {
return !aParent->GetNodeId().IsEmpty();
}
void
GeckoMediaPluginServiceParent::ClearStorage()
--- a/dom/media/gmp/GMPServiceParent.h
+++ b/dom/media/gmp/GMPServiceParent.h
@@ -57,16 +57,18 @@ public:
#endif // MOZ_CRASHREPORTER
RefPtr<GenericPromise> EnsureInitialized();
RefPtr<GenericPromise> AsyncAddPluginDirectory(const nsAString& aDirectory);
// GMP thread access only
bool IsShuttingDown();
already_AddRefed<GMPStorage> GetMemoryStorageFor(const nsACString& aNodeId);
+ nsresult ForgetThisSiteNative(const nsAString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern);
private:
friend class GMPServiceParent;
virtual ~GeckoMediaPluginServiceParent();
void ClearStorage();
@@ -98,17 +100,18 @@ private:
struct DirectoryFilter {
virtual bool operator()(nsIFile* aPath) = 0;
~DirectoryFilter() {}
};
void ClearNodeIdAndPlugin(DirectoryFilter& aFilter);
void ClearNodeIdAndPlugin(nsIFile* aPluginStorageDir,
DirectoryFilter& aFilter);
- void ForgetThisSiteOnGMPThread(const nsACString& aOrigin);
+ void ForgetThisSiteOnGMPThread(const nsACString& aOrigin,
+ const mozilla::OriginAttributesPattern& aPattern);
void ClearRecentHistoryOnGMPThread(PRTime aSince);
already_AddRefed<GMPParent> GetById(uint32_t aPluginId);
protected:
friend class GMPParent;
void ReAddOnGMPThread(const RefPtr<GMPParent>& aOld);
void PluginTerminated(const RefPtr<GMPParent>& aOld);
@@ -212,17 +215,19 @@ private:
MozPromiseHolder<GenericPromise> mInitPromise;
bool mLoadPluginsFromDiskComplete;
// Hashes nodeId to the hashtable of storage for that nodeId.
nsRefPtrHashtable<nsCStringHashKey, GMPStorage> mTempGMPStorage;
};
nsresult ReadSalt(nsIFile* aPath, nsACString& aOutData);
-bool MatchOrigin(nsIFile* aPath, const nsACString& aSite);
+bool MatchOrigin(nsIFile* aPath,
+ const nsACString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern);
class GMPServiceParent final : public PGMPServiceParent
{
public:
explicit GMPServiceParent(GeckoMediaPluginServiceParent* aService)
: mService(aService)
{
}
--- a/dom/media/gmp/mozIGeckoMediaPluginChromeService.idl
+++ b/dom/media/gmp/mozIGeckoMediaPluginChromeService.idl
@@ -25,19 +25,21 @@ interface mozIGeckoMediaPluginChromeServ
* Remove a directory for gecko media plugins and delete it from disk.
* If |defer| is true, wait until the plugin is unused before removing.
* @note Main-thread API.
*/
void removeAndDeletePluginDirectory(in AString directory,
[optional] in bool defer);
/**
- * Clears storage data associated with the site.
+ * Clears storage data associated with the site and the originAttributes
+ * pattern in JSON format.
*/
- void forgetThisSite(in AString site);
+ void forgetThisSite(in AString site,
+ in DOMString aPattern);
/**
* Returns true if the given node id is allowed to store things
* persistently on disk. Private Browsing and local content are not
* allowed to store persistent data.
*/
bool isPersistentStorageAllowed(in ACString nodeId);
--- a/dom/media/gtest/TestGMPCrossOrigin.cpp
+++ b/dom/media/gtest/TestGMPCrossOrigin.cpp
@@ -735,50 +735,59 @@ class GMPStorageTest : public GMPDecrypt
CreateDecryptor(NS_LITERAL_STRING("http://example3.com"),
NS_LITERAL_STRING("http://example4.com"),
false,
NS_LITERAL_CSTRING("test-storage"));
}
struct NodeInfo {
- explicit NodeInfo(const nsACString& aSite) : siteToForget(aSite) {}
+ explicit NodeInfo(const nsACString& aSite,
+ const mozilla::OriginAttributesPattern& aPattern)
+ : siteToForget(aSite)
+ , mPattern(aPattern)
+ { }
nsCString siteToForget;
+ mozilla::OriginAttributesPattern mPattern;
nsTArray<nsCString> expectedRemainingNodeIds;
};
class NodeIdCollector {
public:
explicit NodeIdCollector(NodeInfo* aInfo) : mNodeInfo(aInfo) {}
void operator()(nsIFile* aFile) {
nsCString salt;
nsresult rv = ReadSalt(aFile, salt);
ASSERT_TRUE(NS_SUCCEEDED(rv));
- if (!MatchOrigin(aFile, mNodeInfo->siteToForget)) {
+ if (!MatchOrigin(aFile, mNodeInfo->siteToForget, mNodeInfo->mPattern)) {
mNodeInfo->expectedRemainingNodeIds.AppendElement(salt);
}
}
private:
NodeInfo* mNodeInfo;
};
void TestForgetThisSite_CollectSiteInfo() {
+ mozilla::OriginAttributesPattern pattern;
+
nsAutoPtr<NodeInfo> siteInfo(
- new NodeInfo(NS_LITERAL_CSTRING("http://example1.com")));
+ new NodeInfo(NS_LITERAL_CSTRING("http://example1.com"),
+ pattern));
// Collect nodeIds that are expected to remain for later comparison.
EnumerateGMPStorageDir(NS_LITERAL_CSTRING("id"), NodeIdCollector(siteInfo));
// Invoke "Forget this site" on the main thread.
NS_DispatchToMainThread(NewRunnableMethod<nsAutoPtr<NodeInfo>>(
this, &GMPStorageTest::TestForgetThisSite_Forget, siteInfo));
}
void TestForgetThisSite_Forget(nsAutoPtr<NodeInfo> aSiteInfo) {
RefPtr<GeckoMediaPluginServiceParent> service =
GeckoMediaPluginServiceParent::GetSingleton();
- service->ForgetThisSite(NS_ConvertUTF8toUTF16(aSiteInfo->siteToForget));
+ service->ForgetThisSiteNative(NS_ConvertUTF8toUTF16(aSiteInfo->siteToForget),
+ aSiteInfo->mPattern);
nsCOMPtr<nsIThread> thread;
service->GetThread(getter_AddRefs(thread));
nsCOMPtr<nsIRunnable> r = NewRunnableMethod<nsAutoPtr<NodeInfo>>(
this, &GMPStorageTest::TestForgetThisSite_Verify, aSiteInfo);
thread->Dispatch(r, NS_DISPATCH_NORMAL);
@@ -792,17 +801,17 @@ class GMPStorageTest : public GMPDecrypt
explicit NodeIdVerifier(const NodeInfo* aInfo)
: mNodeInfo(aInfo)
, mExpectedRemainingNodeIds(aInfo->expectedRemainingNodeIds) {}
void operator()(nsIFile* aFile) {
nsCString salt;
nsresult rv = ReadSalt(aFile, salt);
ASSERT_TRUE(NS_SUCCEEDED(rv));
// Shouldn't match the origin if we clear correctly.
- EXPECT_FALSE(MatchOrigin(aFile, mNodeInfo->siteToForget));
+ EXPECT_FALSE(MatchOrigin(aFile, mNodeInfo->siteToForget, mNodeInfo->mPattern));
// Check if remaining nodeIDs are as expected.
EXPECT_TRUE(mExpectedRemainingNodeIds.RemoveElement(salt));
}
~NodeIdVerifier() {
EXPECT_TRUE(mExpectedRemainingNodeIds.IsEmpty());
}
private:
const NodeInfo* mNodeInfo;
--- a/dom/quota/ActorsParent.cpp
+++ b/dom/quota/ActorsParent.cpp
@@ -6246,18 +6246,21 @@ OriginClearOp::DoInitOnMainThread()
// Figure out which origin we're dealing with.
nsCString origin;
rv = QuotaManager::GetInfoFromPrincipal(principal, nullptr, nullptr, &origin,
nullptr);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
-
- mOriginScope.SetFromOrigin(origin);
+ if (params.clearAll()) {
+ mOriginScope.SetFromPrefix(origin);
+ } else {
+ mOriginScope.SetFromOrigin(origin);
+ }
}
return NS_OK;
}
void
OriginClearOp::DeleteFiles(QuotaManager* aQuotaManager,
PersistenceType aPersistenceType)
@@ -6284,16 +6287,20 @@ OriginClearOp::DeleteFiles(QuotaManager*
return;
}
OriginScope originScope = mOriginScope.Clone();
if (originScope.IsOrigin()) {
nsCString originSanitized(originScope.GetOrigin());
SanitizeOriginString(originSanitized);
originScope.SetOrigin(originSanitized);
+ } else if (originScope.IsPrefix()) {
+ nsCString prefixSanitized(originScope.GetPrefix());
+ SanitizeOriginString(prefixSanitized);
+ originScope.SetPrefix(prefixSanitized);
}
bool hasMore;
while (NS_SUCCEEDED((rv = entries->HasMoreElements(&hasMore))) && hasMore) {
nsCOMPtr<nsISupports> entry;
rv = entries->GetNext(getter_AddRefs(entry));
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
--- a/dom/quota/OriginScope.h
+++ b/dom/quota/OriginScope.h
@@ -15,16 +15,17 @@ BEGIN_QUOTA_NAMESPACE
class OriginScope
{
public:
enum Type
{
eOrigin,
ePattern,
+ ePrefix,
eNull
};
private:
struct OriginAndAttributes
{
nsCString mOrigin;
PrincipalOriginAttributes mAttributes;
@@ -53,54 +54,65 @@ private:
union {
// eOrigin
OriginAndAttributes* mOriginAndAttributes;
// ePattern
mozilla::OriginAttributesPattern* mPattern;
+ // ePrefix
+ nsCString* mPrefix;
+
// eNull
void* mDummy;
};
Type mType;
public:
static OriginScope
FromOrigin(const nsACString& aOrigin)
{
- return OriginScope(aOrigin);
+ return OriginScope(aOrigin, true);
}
static OriginScope
FromPattern(const mozilla::OriginAttributesPattern& aPattern)
{
return OriginScope(aPattern);
}
static OriginScope
FromJSONPattern(const nsAString& aJSONPattern)
{
return OriginScope(aJSONPattern);
}
static OriginScope
+ FromPrefix(const nsACString& aPrefix)
+ {
+ return OriginScope(aPrefix, false);
+ }
+
+ static OriginScope
FromNull()
{
return OriginScope();
}
OriginScope(const OriginScope& aOther)
{
if (aOther.IsOrigin()) {
mOriginAndAttributes =
new OriginAndAttributes(*aOther.mOriginAndAttributes);
} else if (aOther.IsPattern()) {
mPattern = new mozilla::OriginAttributesPattern(*aOther.mPattern);
+ } else if (aOther.IsPrefix()) {
+ mPrefix = new nsCString(*aOther.mPrefix);
} else {
mDummy = aOther.mDummy;
}
mType = aOther.mType;
}
~OriginScope()
@@ -116,16 +128,22 @@ public:
bool
IsPattern() const
{
return mType == ePattern;
}
bool
+ IsPrefix() const
+ {
+ return mType == ePrefix;
+ }
+
+ bool
IsNull() const
{
return mType == eNull;
}
Type
GetType() const
{
@@ -159,16 +177,26 @@ public:
mPattern = new mozilla::OriginAttributesPattern();
MOZ_ALWAYS_TRUE(mPattern->Init(aJSONPattern));
mType = ePattern;
}
void
+ SetFromPrefix(const nsACString& aPrefix)
+ {
+ Destroy();
+
+ mPrefix = new nsCString(aPrefix);
+
+ mType = ePrefix;
+ }
+
+ void
SetFromNull()
{
Destroy();
mDummy = nullptr;
mType = eNull;
}
@@ -201,30 +229,51 @@ public:
const mozilla::OriginAttributesPattern&
GetPattern() const
{
MOZ_ASSERT(IsPattern());
MOZ_ASSERT(mPattern);
return *mPattern;
}
+ const nsACString&
+ GetPrefix() const
+ {
+ MOZ_ASSERT(IsPrefix());
+ MOZ_ASSERT(mPrefix);
+
+ return *mPrefix;
+ }
+
+ void
+ SetPrefix(const nsACString& aPrefix)
+ {
+ MOZ_ASSERT(IsPrefix());
+ MOZ_ASSERT(mPrefix);
+
+ *mPrefix = aPrefix;
+ }
+
bool MatchesOrigin(const OriginScope& aOther) const
{
MOZ_ASSERT(aOther.IsOrigin());
MOZ_ASSERT(aOther.mOriginAndAttributes);
bool match;
if (IsOrigin()) {
MOZ_ASSERT(mOriginAndAttributes);
match = mOriginAndAttributes->mOrigin.Equals(
aOther.mOriginAndAttributes->mOrigin);
} else if (IsPattern()) {
MOZ_ASSERT(mPattern);
match = mPattern->Matches(aOther.mOriginAndAttributes->mAttributes);
+ } else if (IsPrefix()) {
+ MOZ_ASSERT(mPrefix);
+ match = StringBeginsWith(aOther.mOriginAndAttributes->mOrigin, *mPrefix);
} else {
match = true;
}
return match;
}
bool MatchesPattern(const OriginScope& aOther) const
@@ -235,31 +284,65 @@ public:
bool match;
if (IsOrigin()) {
MOZ_ASSERT(mOriginAndAttributes);
match = aOther.mPattern->Matches(mOriginAndAttributes->mAttributes);
} else if (IsPattern()) {
MOZ_ASSERT(mPattern);
match = mPattern->Overlaps(*aOther.mPattern);
+ } else if (IsPrefix()) {
+ MOZ_ASSERT(mPrefix);
+ // The match will be always true here because any origin attributes
+ // pattern overlaps any origin prefix (an origin prefix targets all
+ // origin attributes).
+ match = true;
+ } else {
+ match = true;
+ }
+
+ return match;
+ }
+
+ bool MatchesPrefix(const OriginScope& aOther) const
+ {
+ MOZ_ASSERT(aOther.IsPrefix());
+ MOZ_ASSERT(aOther.mPrefix);
+
+ bool match;
+
+ if (IsOrigin()) {
+ MOZ_ASSERT(mOriginAndAttributes);
+ match = StringBeginsWith(mOriginAndAttributes->mOrigin, *aOther.mPrefix);
+ } else if (IsPattern()) {
+ MOZ_ASSERT(mPattern);
+ // The match will be always true here because any origin attributes
+ // pattern overlaps any origin prefix (an origin prefix targets all
+ // origin attributes).
+ match = true;
+ } else if (IsPrefix()) {
+ MOZ_ASSERT(mPrefix);
+ match = mPrefix->Equals(*aOther.mPrefix);
} else {
match = true;
}
return match;
}
bool Matches(const OriginScope& aOther) const
{
bool match;
if (aOther.IsOrigin()) {
match = MatchesOrigin(aOther);
} else if (aOther.IsPattern()) {
match = MatchesPattern(aOther);
+ } else if (aOther.IsPrefix()) {
+ match = MatchesPrefix(aOther);
} else {
match = true;
}
return match;
}
OriginScope
@@ -270,30 +353,41 @@ public:
return OriginScope(*mOriginAndAttributes);
}
if (IsPattern()) {
MOZ_ASSERT(mPattern);
return OriginScope(*mPattern);
}
+ if (IsPrefix()) {
+ MOZ_ASSERT(mPrefix);
+ return OriginScope(*mPrefix, false);
+ }
+
MOZ_ASSERT(IsNull());
return OriginScope();
}
private:
explicit OriginScope(const OriginAndAttributes& aOriginAndAttributes)
: mOriginAndAttributes(new OriginAndAttributes(aOriginAndAttributes))
, mType(eOrigin)
{ }
- explicit OriginScope(const nsACString& aOrigin)
- : mOriginAndAttributes(new OriginAndAttributes(aOrigin))
- , mType(eOrigin)
- { }
+ explicit OriginScope(const nsACString& aOriginOrPrefix, bool aOrigin)
+ {
+ if (aOrigin) {
+ mOriginAndAttributes = new OriginAndAttributes(aOriginOrPrefix);
+ mType = eOrigin;
+ } else {
+ mPrefix = new nsCString(aOriginOrPrefix);
+ mType = ePrefix;
+ }
+ }
explicit OriginScope(const mozilla::OriginAttributesPattern& aPattern)
: mPattern(new mozilla::OriginAttributesPattern(aPattern))
, mType(ePattern)
{ }
explicit OriginScope(const nsAString& aJSONPattern)
: mPattern(new mozilla::OriginAttributesPattern())
@@ -313,16 +407,20 @@ private:
if (IsOrigin()) {
MOZ_ASSERT(mOriginAndAttributes);
delete mOriginAndAttributes;
mOriginAndAttributes = nullptr;
} else if (IsPattern()) {
MOZ_ASSERT(mPattern);
delete mPattern;
mPattern = nullptr;
+ } else if (IsPrefix()) {
+ MOZ_ASSERT(mPrefix);
+ delete mPrefix;
+ mPrefix = nullptr;
}
}
bool
operator==(const OriginScope& aOther) = delete;
};
END_QUOTA_NAMESPACE
--- a/dom/quota/PQuota.ipdl
+++ b/dom/quota/PQuota.ipdl
@@ -28,16 +28,17 @@ union UsageRequestParams
UsageParams;
};
struct ClearOriginParams
{
PrincipalInfo principalInfo;
PersistenceType persistenceType;
bool persistenceTypeIsExplicit;
+ bool clearAll;
};
struct ClearOriginsParams
{
nsString pattern;
};
struct ClearAllParams
--- a/dom/quota/QuotaManagerService.cpp
+++ b/dom/quota/QuotaManagerService.cpp
@@ -556,22 +556,32 @@ QuotaManagerService::Clear(nsIQuotaReque
request.forget(_retval);
return NS_OK;
}
NS_IMETHODIMP
QuotaManagerService::ClearStoragesForPrincipal(nsIPrincipal* aPrincipal,
const nsACString& aPersistenceType,
+ bool aClearAll,
nsIQuotaRequest** _retval)
{
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aPrincipal);
MOZ_ASSERT(nsContentUtils::IsCallerChrome());
+ nsCString suffix;
+ BasePrincipal::Cast(aPrincipal)->OriginAttributesRef().CreateSuffix(suffix);
+
+ if (NS_WARN_IF(aClearAll && !suffix.IsEmpty())) {
+ // The originAttributes should be default originAttributes when the
+ // aClearAll flag is set.
+ return NS_ERROR_INVALID_ARG;
+ }
+
RefPtr<Request> request = new Request(aPrincipal);
ClearOriginParams params;
PrincipalInfo& principalInfo = params.principalInfo();
nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
if (NS_WARN_IF(NS_FAILED(rv))) {
@@ -591,16 +601,18 @@ QuotaManagerService::ClearStoragesForPri
if (persistenceType.IsNull()) {
params.persistenceTypeIsExplicit() = false;
} else {
params.persistenceType() = persistenceType.Value();
params.persistenceTypeIsExplicit() = true;
}
+ params.clearAll() = aClearAll;
+
nsAutoPtr<PendingRequestInfo> info(new RequestInfo(request, params));
rv = InitiateRequest(info);
if (NS_WARN_IF(NS_FAILED(rv))) {
return rv;
}
request.forget(_retval);
--- a/dom/quota/nsIQuotaManagerService.idl
+++ b/dom/quota/nsIQuotaManagerService.idl
@@ -45,20 +45,27 @@ interface nsIQuotaManagerService : nsISu
clear();
/**
* Removes all storages stored for the given URI. The files may not be
* deleted immediately depending on prohibitive concurrent operations.
*
* @param aPrincipal
* A principal for the origin whose storages are to be cleared.
+ * @param aPersistenceType
+ * An optional string that tells what persistence type of storages
+ * will be cleared.
+ * @param aClearAll
+ * An optional boolean to indicate clearing all storages under the
+ * given origin.
*/
[must_use] nsIQuotaRequest
clearStoragesForPrincipal(in nsIPrincipal aPrincipal,
- [optional] in ACString aPersistenceType);
+ [optional] in ACString aPersistenceType,
+ [optional] in boolean aClearAll);
/**
* Resets quota and storage management. This can be used to force
* reinitialization of the temp storage, for example when the pref for
* overriding the temp storage limit has changed.
* Be carefull, this invalidates all live storages!
*
* If the dom.quotaManager.testing preference is not true the call will be
--- a/js/src/asmjs/WasmSignalHandlers.cpp
+++ b/js/src/asmjs/WasmSignalHandlers.cpp
@@ -315,29 +315,29 @@ enum { REG_EIP = 14 };
# define CONTEXT ucontext_t
#endif
// Define a context type for use in the emulator code. This is usually just
// the same as CONTEXT, but on Mac we use a different structure since we call
// into the emulator code from a Mach exception handler rather than a
// sigaction-style signal handler.
#if defined(XP_DARWIN)
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
struct macos_x64_context {
x86_thread_state64_t thread;
x86_float_state64_t float_;
};
# define EMULATOR_CONTEXT macos_x64_context
-# elif defined(JS_CODEGEN_X86)
+# elif defined(JS_CPU_X86)
struct macos_x86_context {
x86_thread_state_t thread;
x86_float_state_t float_;
};
# define EMULATOR_CONTEXT macos_x86_context
-# elif defined(JS_CODEGEN_ARM)
+# elif defined(JS_CPU_ARM)
struct macos_arm_context {
arm_thread_state_t thread;
arm_neon_state_t float_;
};
# define EMULATOR_CONTEXT macos_arm_context
# else
# error Unsupported architecture
# endif
@@ -879,27 +879,27 @@ HandleMachException(JSRuntime* rt, const
return false;
AutoSetHandlingSegFault handling(rt);
// Get the port of the JSRuntime's thread from the message.
mach_port_t rtThread = request.body.thread.name;
// Read out the JSRuntime thread's register state.
EMULATOR_CONTEXT context;
-# if defined(JS_CODEGEN_X64)
+# if defined(JS_CPU_X64)
unsigned int thread_state_count = x86_THREAD_STATE64_COUNT;
unsigned int float_state_count = x86_FLOAT_STATE64_COUNT;
int thread_state = x86_THREAD_STATE64;
int float_state = x86_FLOAT_STATE64;
-# elif defined(JS_CODEGEN_X86)
+# elif defined(JS_CPU_X86)
unsigned int thread_state_count = x86_THREAD_STATE_COUNT;
unsigned int float_state_count = x86_FLOAT_STATE_COUNT;
int thread_state = x86_THREAD_STATE;
int float_state = x86_FLOAT_STATE;
-# elif defined(JS_CODEGEN_ARM)
+# elif defined(JS_CPU_ARM)
unsigned int thread_state_count = ARM_THREAD_STATE_COUNT;
unsigned int float_state_count = ARM_NEON_STATE_COUNT;
int thread_state = ARM_THREAD_STATE;
int float_state = ARM_NEON_STATE;
# else
# error Unsupported architecture
# endif
kern_return_t kret;
@@ -1225,17 +1225,17 @@ RedirectIonBackedgesToInterruptCheck(JSR
// a failure.
static bool
RedirectJitCodeToInterruptCheck(JSRuntime* rt, CONTEXT* context)
{
RedirectIonBackedgesToInterruptCheck(rt);
if (WasmActivation* activation = rt->wasmActivationStack()) {
#ifdef JS_SIMULATOR
- (void)ContextToPC; // silence static 'unused' errors
+ (void)ContextToPC(context); // silence static 'unused' errors
void* pc = rt->simulator()->get_pc_as<void*>();
const Instance* instance = activation->compartment()->wasm.lookupInstanceDeprecated(pc);
if (instance && instance->codeSegment().containsFunctionPC(pc))
rt->simulator()->set_resume_pc(instance->codeSegment().interruptCode());
#else
uint8_t** ppc = ContextToPC(context);
--- a/netwerk/cache2/CacheIndex.cpp
+++ b/netwerk/cache2/CacheIndex.cpp
@@ -436,35 +436,35 @@ CacheIndex::Shutdown()
switch (oldState) {
case WRITING:
index->FinishWrite(false);
MOZ_FALLTHROUGH;
case READY:
if (index->mIndexOnDiskIsValid && !index->mDontMarkIndexClean) {
if (!sanitize && NS_FAILED(index->WriteLogToDisk())) {
- index->RemoveIndexFromDisk();
+ index->RemoveJournalAndTempFile();
}
} else {
- index->RemoveIndexFromDisk();
+ index->RemoveJournalAndTempFile();
}
break;
case READING:
index->FinishRead(false);
break;
case BUILDING:
case UPDATING:
index->FinishUpdate(false);
break;
default:
MOZ_ASSERT(false, "Unexpected state!");
}
if (sanitize) {
- index->RemoveIndexFromDisk();
+ index->RemoveAllIndexFiles();
}
return NS_OK;
}
// static
nsresult
CacheIndex::AddEntry(const SHA1Sum::Hash *aHash)
@@ -1834,31 +1834,36 @@ CacheIndex::RemoveFile(const nsACString
return rv;
}
}
return NS_OK;
}
void
-CacheIndex::RemoveIndexFromDisk()
+CacheIndex::RemoveAllIndexFiles()
{
- LOG(("CacheIndex::RemoveIndexFromDisk()"));
-
+ LOG(("CacheIndex::RemoveAllIndexFiles()"));
RemoveFile(NS_LITERAL_CSTRING(INDEX_NAME));
+ RemoveJournalAndTempFile();
+}
+
+void
+CacheIndex::RemoveJournalAndTempFile()
+{
+ LOG(("CacheIndex::RemoveJournalAndTempFile()"));
RemoveFile(NS_LITERAL_CSTRING(TEMP_INDEX_NAME));
RemoveFile(NS_LITERAL_CSTRING(JOURNAL_NAME));
}
class WriteLogHelper
{
public:
explicit WriteLogHelper(PRFileDesc *aFD)
- : mStatus(NS_OK)
- , mFD(aFD)
+ : mFD(aFD)
, mBufSize(kMaxBufSize)
, mBufPos(0)
{
mHash = new CacheHash();
mBuf = static_cast<char *>(moz_xmalloc(mBufSize));
}
~WriteLogHelper() {
@@ -1867,83 +1872,70 @@ public:
nsresult AddEntry(CacheIndexEntry *aEntry);
nsresult Finish();
private:
nsresult FlushBuffer();
- nsresult mStatus;
- PRFileDesc *mFD;
- char *mBuf;
- uint32_t mBufSize;
- int32_t mBufPos;
+ PRFileDesc *mFD;
+ char *mBuf;
+ uint32_t mBufSize;
+ int32_t mBufPos;
RefPtr<CacheHash> mHash;
};
nsresult
WriteLogHelper::AddEntry(CacheIndexEntry *aEntry)
{
nsresult rv;
- if (NS_FAILED(mStatus)) {
- return mStatus;
- }
-
if (mBufPos + sizeof(CacheIndexRecord) > mBufSize) {
mHash->Update(mBuf, mBufPos);
rv = FlushBuffer();
- if (NS_FAILED(rv)) {
- mStatus = rv;
- return rv;
- }
+ NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mBufPos + sizeof(CacheIndexRecord) <= mBufSize);
}
aEntry->WriteToBuf(mBuf + mBufPos);
mBufPos += sizeof(CacheIndexRecord);
return NS_OK;
}
nsresult
WriteLogHelper::Finish()
{
nsresult rv;
- if (NS_FAILED(mStatus)) {
- return mStatus;
- }
-
mHash->Update(mBuf, mBufPos);
if (mBufPos + sizeof(CacheHash::Hash32_t) > mBufSize) {
rv = FlushBuffer();
- if (NS_FAILED(rv)) {
- mStatus = rv;
- return rv;
- }
+ NS_ENSURE_SUCCESS(rv, rv);
MOZ_ASSERT(mBufPos + sizeof(CacheHash::Hash32_t) <= mBufSize);
}
NetworkEndian::writeUint32(mBuf + mBufPos, mHash->GetHash());
mBufPos += sizeof(CacheHash::Hash32_t);
rv = FlushBuffer();
NS_ENSURE_SUCCESS(rv, rv);
- mStatus = NS_ERROR_UNEXPECTED; // Don't allow any other operation
return NS_OK;
}
nsresult
WriteLogHelper::FlushBuffer()
{
- MOZ_ASSERT(NS_SUCCEEDED(mStatus));
+ if (CacheObserver::IsPastShutdownIOLag()) {
+ LOG(("WriteLogHelper::FlushBuffer() - Interrupting writing journal."));
+ return NS_ERROR_FAILURE;
+ }
int32_t bytesWritten = PR_Write(mFD, mBuf, mBufPos);
if (bytesWritten != mBufPos) {
return NS_ERROR_FAILURE;
}
mBufPos = 0;
@@ -1955,16 +1947,21 @@ CacheIndex::WriteLogToDisk()
{
LOG(("CacheIndex::WriteLogToDisk()"));
nsresult rv;
MOZ_ASSERT(mPendingUpdates.Count() == 0);
MOZ_ASSERT(mState == SHUTDOWN);
+ if (CacheObserver::IsPastShutdownIOLag()) {
+ LOG(("CacheIndex::WriteLogToDisk() - Skipping writing journal."));
+ return NS_ERROR_FAILURE;
+ }
+
RemoveFile(NS_LITERAL_CSTRING(TEMP_INDEX_NAME));
nsCOMPtr<nsIFile> indexFile;
rv = GetFile(NS_LITERAL_CSTRING(INDEX_NAME), getter_AddRefs(indexFile));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIFile> logFile;
rv = GetFile(NS_LITERAL_CSTRING(JOURNAL_NAME), getter_AddRefs(logFile));
@@ -1976,19 +1973,21 @@ CacheIndex::WriteLogToDisk()
rv = logFile->OpenNSPRFileDesc(PR_RDWR | PR_CREATE_FILE | PR_TRUNCATE,
0600, &fd);
NS_ENSURE_SUCCESS(rv, rv);
WriteLogHelper wlh(fd);
for (auto iter = mIndex.Iter(); !iter.Done(); iter.Next()) {
CacheIndexEntry* entry = iter.Get();
if (entry->IsRemoved() || entry->IsDirty()) {
- wlh.AddEntry(entry);
+ rv = wlh.AddEntry(entry);
+ if (NS_WARN_IF(NS_FAILED(rv))) {
+ return rv;
+ }
}
- iter.Remove();
}
rv = wlh.Finish();
PR_Close(fd);
NS_ENSURE_SUCCESS(rv, rv);
rv = indexFile->OpenNSPRFileDesc(PR_RDWR, 0600, &fd);
NS_ENSURE_SUCCESS(rv, rv);
--- a/netwerk/cache2/CacheIndex.h
+++ b/netwerk/cache2/CacheIndex.h
@@ -767,17 +767,18 @@ private:
// methods must be called only during shutdown since they write/delete files
// directly on the main thread instead of using CacheFileIOManager that does
// it asynchronously on IO thread. Journal contains only entries that are
// dirty, i.e. changes that are not present in the index file on the disk.
// When the log is written successfully, the dirty flag in index file is
// cleared.
nsresult GetFile(const nsACString &aName, nsIFile **_retval);
nsresult RemoveFile(const nsACString &aName);
- void RemoveIndexFromDisk();
+ void RemoveAllIndexFiles();
+ void RemoveJournalAndTempFile();
// Writes journal to the disk and clears dirty flag in index header.
nsresult WriteLogToDisk();
// Following methods perform reading of the index from the disk.
//
// Index is read at startup just after initializing the CacheIndex. There are
// 3 files used when manipulating with index: index file, journal file and
// a temporary file. All files contain the hash of the data, so we can check
--- a/netwerk/cookie/nsCookie.cpp
+++ b/netwerk/cookie/nsCookie.cpp
@@ -4,16 +4,18 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/ToJSValue.h"
#include "nsAutoPtr.h"
#include "nsCookie.h"
#include "nsUTF8ConverterService.h"
#include <stdlib.h>
+static const int64_t kCookieStaleThreshold = 60 * PR_USEC_PER_SEC; // 1 minute in microseconds
+
/******************************************************************************
* nsCookie:
* string helper impl
******************************************************************************/
// copy aSource strings into contiguous storage provided in aDest1,
// providing terminating nulls for each destination string.
static inline void
@@ -123,17 +125,17 @@ nsCookie::SizeOfIncludingThis(mozilla::M
return aMallocSizeOf(this);
}
bool
nsCookie::IsStale() const
{
int64_t currentTimeInUsec = PR_Now();
- return currentTimeInUsec - LastAccessed() > mCookieStaleThreshold * PR_USEC_PER_SEC;
+ return currentTimeInUsec - LastAccessed() > kCookieStaleThreshold;
}
/******************************************************************************
* nsCookie:
* xpcom impl
******************************************************************************/
// xpcom getters
--- a/netwerk/cookie/nsCookie.h
+++ b/netwerk/cookie/nsCookie.h
@@ -7,17 +7,16 @@
#define nsCookie_h__
#include "nsICookie.h"
#include "nsICookie2.h"
#include "nsString.h"
#include "mozilla/MemoryReporting.h"
#include "mozilla/BasePrincipal.h"
-#include "mozilla/Preferences.h"
using mozilla::OriginAttributes;
/**
* The nsCookie class is the main cookie storage medium for use within cookie
* code. It implements nsICookie2, which extends nsICookie, a frozen interface
* for xpcom access of cookie objects.
*/
@@ -52,18 +51,16 @@ class nsCookie : public nsICookie2
: mName(aName)
, mValue(aValue)
, mHost(aHost)
, mPath(aPath)
, mEnd(aEnd)
, mExpiry(aExpiry)
, mLastAccessed(aLastAccessed)
, mCreationTime(aCreationTime)
- // Defaults to 60s
- , mCookieStaleThreshold(mozilla::Preferences::GetInt("network.cookie.staleThreshold", 60))
, mIsSession(aIsSession)
, mIsSecure(aIsSecure)
, mIsHttpOnly(aIsHttpOnly)
, mOriginAttributes(aOriginAttributes)
{
}
public:
@@ -125,16 +122,15 @@ class nsCookie : public nsICookie2
const char *mName;
const char *mValue;
const char *mHost;
const char *mPath;
const char *mEnd;
int64_t mExpiry;
int64_t mLastAccessed;
int64_t mCreationTime;
- int64_t mCookieStaleThreshold;
bool mIsSession;
bool mIsSecure;
bool mIsHttpOnly;
mozilla::OriginAttributes mOriginAttributes;
};
#endif // nsCookie_h__
--- a/netwerk/cookie/nsCookieService.cpp
+++ b/netwerk/cookie/nsCookieService.cpp
@@ -578,17 +578,17 @@ public:
MOZ_ASSERT(!nsCRT::strcmp(aTopic, TOPIC_CLEAR_ORIGIN_DATA));
MOZ_ASSERT(XRE_IsParentProcess());
nsCOMPtr<nsICookieManager2> cookieManager
= do_GetService(NS_COOKIEMANAGER_CONTRACTID);
MOZ_ASSERT(cookieManager);
- return cookieManager->RemoveCookiesWithOriginAttributes(nsDependentString(aData));
+ return cookieManager->RemoveCookiesWithOriginAttributes(nsDependentString(aData), EmptyCString());
}
};
NS_IMPL_ISUPPORTS(AppClearDataObserver, nsIObserver)
} // namespace
size_t
@@ -3488,17 +3488,17 @@ nsCookieService::AddInternal(const nsCoo
"cookie has already expired");
return;
}
// check if we have to delete an old cookie.
nsCookieEntry *entry = mDBState->hostTable.GetEntry(aKey);
if (entry && entry->GetCookies().Length() >= mMaxCookiesPerHost) {
nsListIter iter;
- FindStaleCookie(entry, currentTime, aHostURI, iter);
+ FindStaleCookie(entry, currentTime, iter);
oldCookie = iter.Cookie();
// remove the oldest cookie from the domain
RemoveCookieFromList(iter);
COOKIE_LOGEVICTED(oldCookie, "Too many cookies for this domain");
purgedList = CreatePurgeList(oldCookie);
} else if (mDBState->cookieCount >= ADD_TEN_PERCENT(mMaxNumberOfCookies)) {
@@ -4356,118 +4356,38 @@ nsCookieService::CookieExists(nsICookie2
return NS_OK;
}
// For a given base domain, find either an expired cookie or the oldest cookie
// by lastAccessed time.
void
nsCookieService::FindStaleCookie(nsCookieEntry *aEntry,
int64_t aCurrentTime,
- nsIURI* aSource,
nsListIter &aIter)
{
- bool requireHostMatch = true;
- nsAutoCString baseDomain, sourceHost, sourcePath;
- if (aSource) {
- GetBaseDomain(aSource, baseDomain, requireHostMatch);
- aSource->GetAsciiHost(sourceHost);
- aSource->GetPath(sourcePath);
- }
-
+ aIter.entry = nullptr;
+
+ int64_t oldestTime = 0;
const nsCookieEntry::ArrayType &cookies = aEntry->GetCookies();
-
- int64_t oldestNonMatchingSessionCookieTime = 0;
- nsListIter oldestNonMatchingSessionCookie;
- oldestNonMatchingSessionCookie.entry = nullptr;
-
- int64_t oldestSessionCookieTime = 0;
- nsListIter oldestSessionCookie;
- oldestSessionCookie.entry = nullptr;
-
- int64_t oldestNonMatchingNonSessionCookieTime = 0;
- nsListIter oldestNonMatchingNonSessionCookie;
- oldestNonMatchingNonSessionCookie.entry = nullptr;
-
- int64_t oldestCookieTime = 0;
- nsListIter oldestCookie;
- oldestCookie.entry = nullptr;
-
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
nsCookie *cookie = cookies[i];
// If we found an expired cookie, we're done.
if (cookie->Expiry() <= aCurrentTime) {
aIter.entry = aEntry;
aIter.index = i;
return;
}
- // Update our various records of oldest cookies fitting several restrictions:
- // * session cookies
- // * non-session cookies
- // * cookies with paths and domains that don't match the cookie triggering this purge
-
- uint32_t cookiePathLen = cookie->Path().Length();
- if (cookiePathLen > 0 && cookie->Path().Last() == '/')
- --cookiePathLen;
-
- // This cookie is a candidate for eviction if we have no information about
- // the source request, or if it is not a path or domain match against the
- // source request.
- bool isPrimaryEvictionCandidate = true;
- if (aSource) {
- bool pathMatches = StringBeginsWith(sourcePath, Substring(cookie->Path(), 0, cookiePathLen));
- bool domainMatches = cookie->RawHost() == sourceHost ||
- (cookie->IsDomain() && StringEndsWith(sourceHost, cookie->Host()));
- isPrimaryEvictionCandidate = !pathMatches || !domainMatches;
+ // Check if we've found the oldest cookie so far.
+ if (!aIter.entry || oldestTime > cookie->LastAccessed()) {
+ oldestTime = cookie->LastAccessed();
+ aIter.entry = aEntry;
+ aIter.index = i;
}
-
- int64_t lastAccessed = cookie->LastAccessed();
- if (cookie->IsSession()) {
- if (!oldestSessionCookie.entry || oldestSessionCookieTime > lastAccessed) {
- oldestSessionCookieTime = lastAccessed;
- oldestSessionCookie.entry = aEntry;
- oldestSessionCookie.index = i;
- }
-
- if (isPrimaryEvictionCandidate &&
- (!oldestNonMatchingSessionCookie.entry ||
- oldestNonMatchingSessionCookieTime > lastAccessed)) {
- oldestNonMatchingSessionCookieTime = lastAccessed;
- oldestNonMatchingSessionCookie.entry = aEntry;
- oldestNonMatchingSessionCookie.index = i;
- }
- } else if (isPrimaryEvictionCandidate &&
- (!oldestNonMatchingNonSessionCookie.entry ||
- oldestNonMatchingNonSessionCookieTime > lastAccessed)) {
- oldestNonMatchingNonSessionCookieTime = lastAccessed;
- oldestNonMatchingNonSessionCookie.entry = aEntry;
- oldestNonMatchingNonSessionCookie.index = i;
- }
-
- // Check if we've found the oldest cookie so far.
- if (!oldestCookie.entry || oldestCookieTime > lastAccessed) {
- oldestCookieTime = lastAccessed;
- oldestCookie.entry = aEntry;
- oldestCookie.index = i;
- }
- }
-
- // Prefer to evict the oldest session cookies with a non-matching path/domain,
- // followed by the oldest session cookie with a matching path/domain,
- // followed by the oldest non-session cookie with a non-matching path/domain,
- // resorting to the oldest non-session cookie with a matching path/domain.
- if (oldestNonMatchingSessionCookie.entry) {
- aIter = oldestNonMatchingSessionCookie;
- } else if (oldestSessionCookie.entry) {
- aIter = oldestSessionCookie;
- } else if (oldestNonMatchingNonSessionCookie.entry) {
- aIter = oldestNonMatchingNonSessionCookie;
- } else {
- aIter = oldestCookie;
}
}
// count the number of cookies stored by a particular host. this is provided by the
// nsICookieManager2 interface.
NS_IMETHODIMP
nsCookieService::CountCookiesFromHost(const nsACString &aHost,
uint32_t *aCountFromHost)
@@ -4541,85 +4461,113 @@ nsCookieService::GetCookiesFromHost(cons
for (nsCookieEntry::IndexType i = 0; i < cookies.Length(); ++i) {
cookieList.AppendObject(cookies[i]);
}
return NS_NewArrayEnumerator(aEnumerator, cookieList);
}
NS_IMETHODIMP
-nsCookieService::GetCookiesWithOriginAttributes(const nsAString& aPattern,
+nsCookieService::GetCookiesWithOriginAttributes(const nsAString& aPattern,
+ const nsACString& aHost,
nsISimpleEnumerator **aEnumerator)
{
mozilla::OriginAttributesPattern pattern;
if (!pattern.Init(aPattern)) {
return NS_ERROR_INVALID_ARG;
}
- return GetCookiesWithOriginAttributes(pattern, aEnumerator);
+ nsAutoCString host(aHost);
+ nsresult rv = NormalizeHost(host);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString baseDomain;
+ rv = GetBaseDomainFromHost(host, baseDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return GetCookiesWithOriginAttributes(pattern, baseDomain, aEnumerator);
}
nsresult
nsCookieService::GetCookiesWithOriginAttributes(
const mozilla::OriginAttributesPattern& aPattern,
+ const nsCString& aBaseDomain,
nsISimpleEnumerator **aEnumerator)
{
if (!mDBState) {
NS_WARNING("No DBState! Profile already closed?");
return NS_ERROR_NOT_AVAILABLE;
}
if (aPattern.mAppId.WasPassed() && aPattern.mAppId.Value() == NECKO_UNKNOWN_APP_ID) {
return NS_ERROR_INVALID_ARG;
}
nsCOMArray<nsICookie> cookies;
for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
nsCookieEntry* entry = iter.Get();
+ if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
+ continue;
+ }
+
if (!aPattern.Matches(entry->mOriginAttributes)) {
continue;
}
const nsCookieEntry::ArrayType& entryCookies = entry->GetCookies();
for (nsCookieEntry::IndexType i = 0; i < entryCookies.Length(); ++i) {
cookies.AppendObject(entryCookies[i]);
}
}
return NS_NewArrayEnumerator(aEnumerator, cookies);
}
NS_IMETHODIMP
-nsCookieService::RemoveCookiesWithOriginAttributes(const nsAString& aPattern)
+nsCookieService::RemoveCookiesWithOriginAttributes(const nsAString& aPattern,
+ const nsACString& aHost)
{
MOZ_ASSERT(XRE_IsParentProcess());
mozilla::OriginAttributesPattern pattern;
if (!pattern.Init(aPattern)) {
return NS_ERROR_INVALID_ARG;
}
- return RemoveCookiesWithOriginAttributes(pattern);
+ nsAutoCString host(aHost);
+ nsresult rv = NormalizeHost(host);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString baseDomain;
+ rv = GetBaseDomainFromHost(host, baseDomain);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return RemoveCookiesWithOriginAttributes(pattern, baseDomain);
}
nsresult
nsCookieService::RemoveCookiesWithOriginAttributes(
- const mozilla::OriginAttributesPattern& aPattern)
+ const mozilla::OriginAttributesPattern& aPattern,
+ const nsCString& aBaseDomain)
{
if (!mDBState) {
NS_WARNING("No DBState! Profile already close?");
return NS_ERROR_NOT_AVAILABLE;
}
// Iterate the hash table of nsCookieEntry.
for (auto iter = mDBState->hostTable.Iter(); !iter.Done(); iter.Next()) {
nsCookieEntry* entry = iter.Get();
+ if (!aBaseDomain.IsEmpty() && !aBaseDomain.Equals(entry->mBaseDomain)) {
+ continue;
+ }
+
if (!aPattern.Matches(entry->mOriginAttributes)) {
continue;
}
// Pattern matches. Delete all cookies within this nsCookieEntry.
const nsCookieEntry::ArrayType& cookies = entry->GetCookies();
while (!cookies.IsEmpty()) {
--- a/netwerk/cookie/nsCookieService.h
+++ b/netwerk/cookie/nsCookieService.h
@@ -307,26 +307,26 @@ class nsCookieService final : public nsI
CookieStatus CheckPrefs(nsIURI *aHostURI, bool aIsForeign, const char *aCookieHeader);
bool CheckDomain(nsCookieAttributes &aCookie, nsIURI *aHostURI, const nsCString &aBaseDomain, bool aRequireHostMatch);
static bool CheckPath(nsCookieAttributes &aCookie, nsIURI *aHostURI);
static bool CheckPrefixes(nsCookieAttributes &aCookie, bool aSecureRequest);
static bool GetExpiry(nsCookieAttributes &aCookie, int64_t aServerTime, int64_t aCurrentTime);
void RemoveAllFromMemory();
already_AddRefed<nsIArray> PurgeCookies(int64_t aCurrentTimeInUsec);
bool FindCookie(const nsCookieKey& aKey, const nsAFlatCString &aHost, const nsAFlatCString &aName, const nsAFlatCString &aPath, nsListIter &aIter);
- void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsIURI* aSource, nsListIter &aIter);
+ static void FindStaleCookie(nsCookieEntry *aEntry, int64_t aCurrentTime, nsListIter &aIter);
void NotifyRejected(nsIURI *aHostURI);
void NotifyThirdParty(nsIURI *aHostURI, bool aAccepted, nsIChannel *aChannel);
void NotifyChanged(nsISupports *aSubject, const char16_t *aData);
void NotifyPurged(nsICookie2* aCookie);
already_AddRefed<nsIArray> CreatePurgeList(nsICookie2* aCookie);
void UpdateCookieOldestTime(DBState* aDBState, nsCookie* aCookie);
- nsresult GetCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern, nsISimpleEnumerator **aEnumerator);
- nsresult RemoveCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern);
+ nsresult GetCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern, const nsCString& aBaseDomain, nsISimpleEnumerator **aEnumerator);
+ nsresult RemoveCookiesWithOriginAttributes(const mozilla::OriginAttributesPattern& aPattern, const nsCString& aBaseDomain);
/**
* This method is a helper that allows calling nsICookieManager::Remove()
* with NeckoOriginAttributes parameter.
* NOTE: this could be added to a public interface if we happen to need it.
*/
nsresult Remove(const nsACString& aHost, const NeckoOriginAttributes& aAttrs,
const nsACString& aName, const nsACString& aPath,
--- a/netwerk/cookie/nsICookieManager2.idl
+++ b/netwerk/cookie/nsICookieManager2.idl
@@ -127,18 +127,26 @@ interface nsICookieManager2 : nsICookieM
* @param aCookieFile the file to import, usually cookies.txt
*/
void importCookies(in nsIFile aCookieFile);
/**
* Returns an enumerator of all cookies whose origin attributes matches aPattern
*
* @param aPattern origin attribute pattern in JSON format
+ *
+ * @param aHost
+ * the host string to search for, e.g. "google.com". this should consist
+ * of only the host portion of a URI. see @add for a description of
+ * acceptable host strings. This attribute is optional. It will search
+ * all hosts if this attribute is not given.
*/
- nsISimpleEnumerator getCookiesWithOriginAttributes(in DOMString aPattern);
+ nsISimpleEnumerator getCookiesWithOriginAttributes(in DOMString aPattern,
+ [optional] in AUTF8String aHost);
/**
* Remove all the cookies whose origin attributes matches aPattern
*
* @param aPattern origin attribute pattern in JSON format
*/
- void removeCookiesWithOriginAttributes(in DOMString aPattern);
+ void removeCookiesWithOriginAttributes(in DOMString aPattern,
+ [optional] in AUTF8String aHost);
};
deleted file mode 100644
--- a/netwerk/cookie/test/unit/test_eviction.js
+++ /dev/null
@@ -1,252 +0,0 @@
-var {utils: Cu, interfaces: Ci, classes: Cc} = Components;
-
-Cu.import("resource://gre/modules/Services.jsm");
-
-const BASE_HOSTNAMES = ["example.org", "example.co.uk"];
-const SUBDOMAINS = ["", "pub.", "www.", "other."];
-
-const cs = Cc["@mozilla.org/cookieService;1"].getService(Ci.nsICookieService);
-const cm = cs.QueryInterface(Ci.nsICookieManager2);
-
-function run_test() {
- Services.prefs.setIntPref("network.cookie.staleThreshold", 0);
- for (var host of BASE_HOSTNAMES) {
- do_print('testing with host ' + host);
- var base = SUBDOMAINS[0] + host;
- var sub = SUBDOMAINS[1] + host;
- var other = SUBDOMAINS[2] + host;
- var another = SUBDOMAINS[3] + host;
- test_basic_eviction(base, sub, other, another);
- cm.removeAll();
- test_domain_or_path_matches_not_both(base, sub, other, another);
- cm.removeAll();
- }
- test_localdomain();
-}
-
-// Verify that subdomains of localhost are treated as separate hosts and aren't considered
-// candidates for eviction.
-function test_localdomain() {
- Services.prefs.setIntPref("network.cookie.maxPerHost", 2);
-
- const BASE_URI = Services.io.newURI("http://localhost", null, null);
- const BASE_BAR = Services.io.newURI("http://localhost/bar", null, null);
- const OTHER_URI = Services.io.newURI("http://other.localhost", null, null);
- const OTHER_BAR = Services.io.newURI("http://other.localhost/bar", null, null);
-
- setCookie("session_no_path", null, null, null, BASE_URI);
- setCookie("session_bar_path", null, "/bar", null, BASE_BAR);
-
- setCookie("session_no_path", null, null, null, OTHER_URI);
- setCookie("session_bar_path", null, "/bar", null, OTHER_BAR);
-
- verifyCookies(['session_no_path',
- 'session_bar_path'], BASE_URI);
- verifyCookies(['session_no_path',
- 'session_bar_path'], OTHER_URI);
-
- setCookie("session_another_no_path", null, null, null, BASE_URI);
- verifyCookies(['session_no_path',
- 'session_another_no_path'], BASE_URI);
-
- setCookie("session_another_no_path", null, null, null, OTHER_URI);
- verifyCookies(['session_no_path',
- 'session_another_no_path'], OTHER_URI);
-}
-
-// Ensure that cookies are still considered candidates for eviction if either the domain
-// or path matches, but not both.
-function test_domain_or_path_matches_not_both(base_host,
- subdomain_host,
- other_subdomain_host,
- another_subdomain_host) {
- Services.prefs.setIntPref("network.cookie.maxPerHost", 2);
-
- const BASE_URI = Services.io.newURI("http://" + base_host, null, null);
- const PUB_FOO_PATH = Services.io.newURI("http://" + subdomain_host + "/foo", null, null);
- const WWW_BAR_PATH = Services.io.newURI("http://" + other_subdomain_host + "/bar", null, null);
- const OTHER_BAR_PATH = Services.io.newURI("http://" + another_subdomain_host + "/bar", null, null);
- const PUB_BAR_PATH = Services.io.newURI("http://" + subdomain_host + "/bar", null, null);
- const WWW_FOO_PATH = Services.io.newURI("http://" + other_subdomain_host + "/foo", null, null);
-
- setCookie("session_pub_with_foo_path", subdomain_host, "/foo", null, PUB_FOO_PATH);
- setCookie("session_www_with_bar_path", other_subdomain_host, "/bar", null, WWW_BAR_PATH);
- verifyCookies(['session_pub_with_foo_path',
- 'session_www_with_bar_path'], BASE_URI);
-
- setCookie("session_pub_with_bar_path", subdomain_host, "/bar", null, PUB_BAR_PATH);
- verifyCookies(['session_www_with_bar_path',
- 'session_pub_with_bar_path'], BASE_URI);
-
- setCookie("session_other_with_bar_path", another_subdomain_host, "/bar", null, OTHER_BAR_PATH);
- verifyCookies(['session_pub_with_bar_path',
- 'session_other_with_bar_path'], BASE_URI);
-}
-
-function test_basic_eviction(base_host, subdomain_host, other_subdomain_host) {
- Services.prefs.setIntPref("network.cookie.maxPerHost", 5);
-
- const BASE_URI = Services.io.newURI("http://" + base_host, null, null);
- const SUBDOMAIN_URI = Services.io.newURI("http://" + subdomain_host, null, null);
- const OTHER_SUBDOMAIN_URI = Services.io.newURI("http://" + other_subdomain_host, null, null);
- const FOO_PATH = Services.io.newURI("http://" + base_host + "/foo", null, null);
- const BAR_PATH = Services.io.newURI("http://" + base_host + "/bar", null, null);
- const ALL_SUBDOMAINS = '.' + base_host;
- const OTHER_SUBDOMAIN = other_subdomain_host;
-
- // Initialize the set of cookies with a mix of non-session cookies with no path,
- // and session cookies with explicit paths. Any subsequent cookies added will cause
- // existing cookies to be evicted.
- setCookie("non_session_non_path_non_domain", null, null, 10000, BASE_URI);
- setCookie("non_session_non_path_subdomain", ALL_SUBDOMAINS, null, 10000, SUBDOMAIN_URI);
- setCookie("session_non_path_pub_domain", OTHER_SUBDOMAIN, null, null, OTHER_SUBDOMAIN_URI);
- setCookie("session_foo_path", null, "/foo", null, FOO_PATH);
- setCookie("session_bar_path", null, "/bar", null, BAR_PATH);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'session_non_path_pub_domain',
- 'session_foo_path',
- 'session_bar_path'], BASE_URI);
-
- // Ensure that cookies set for the / path appear more recent.
- cs.getCookieString(OTHER_SUBDOMAIN_URI, null)
- verifyCookies(['non_session_non_path_non_domain',
- 'session_foo_path',
- 'session_bar_path',
- 'non_session_non_path_subdomain',
- 'session_non_path_pub_domain'], BASE_URI);
-
- // Evict oldest session cookie that does not match example.org/foo (session_bar_path)
- setCookie("session_foo_path_2", null, "/foo", null, FOO_PATH);
- verifyCookies(['non_session_non_path_non_domain',
- 'session_foo_path',
- 'non_session_non_path_subdomain',
- 'session_non_path_pub_domain',
- 'session_foo_path_2'], BASE_URI);
-
- // Evict oldest session cookie that does not match example.org/bar (session_foo_path)
- setCookie("session_bar_path_2", null, "/bar", null, BAR_PATH);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'session_non_path_pub_domain',
- 'session_foo_path_2',
- 'session_bar_path_2'], BASE_URI);
-
- // Evict oldest session cookie that does not match example.org/ (session_non_path_pub_domain)
- setCookie("non_session_non_path_non_domain_2", null, null, 10000, BASE_URI);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'session_foo_path_2',
- 'session_bar_path_2',
- 'non_session_non_path_non_domain_2'], BASE_URI);
-
- // Evict oldest session cookie that does not match example.org/ (session_foo_path_2)
- setCookie("session_non_path_non_domain_3", null, null, null, BASE_URI);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'session_bar_path_2',
- 'non_session_non_path_non_domain_2',
- 'session_non_path_non_domain_3'], BASE_URI);
-
- // Evict oldest session cookie; all such cookies match example.org/bar (session_bar_path_2)
- setCookie("non_session_non_path_non_domain_3", null, null, 10000, BAR_PATH);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'non_session_non_path_non_domain_2',
- 'session_non_path_non_domain_3',
- 'non_session_non_path_non_domain_3'], BASE_URI);
-
- // Evict oldest session cookie, even though it matches pub.example.org (session_non_path_non_domain_3)
- setCookie("non_session_non_path_pub_domain", null, null, 10000, OTHER_SUBDOMAIN_URI);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'non_session_non_path_non_domain_2',
- 'non_session_non_path_non_domain_3',
- 'non_session_non_path_pub_domain'], BASE_URI);
-
- // All session cookies have been evicted.
- // Evict oldest non-session non-domain-matching cookie (non_session_non_path_pub_domain)
- setCookie("non_session_bar_path_non_domain", null, '/bar', 10000, BAR_PATH);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'non_session_non_path_non_domain_2',
- 'non_session_non_path_non_domain_3',
- 'non_session_bar_path_non_domain'], BASE_URI);
-
- // Evict oldest non-session non-path-matching cookie (non_session_bar_path_non_domain)
- setCookie("non_session_non_path_non_domain_4", null, null, 10000, BASE_URI);
- verifyCookies(['non_session_non_path_non_domain',
- 'non_session_non_path_subdomain',
- 'non_session_non_path_non_domain_2',
- 'non_session_non_path_non_domain_3',
- 'non_session_non_path_non_domain_4'], BASE_URI);
-
- // At this point all remaining cookies are non-session cookies, have a path of /,
- // and either don't have a domain or have one that matches subdomains.
- // They will therefore be evicted from oldest to newest if all new cookies added share
- // similar characteristics.
-}
-
-// Verify that the given cookie names exist, and are ordered from least to most recently accessed
-function verifyCookies(names, uri) {
- do_check_eq(cm.countCookiesFromHost(uri.host), names.length);
- let cookies = cm.getCookiesFromHost(uri.host, {});
- let actual_cookies = [];
- while (cookies.hasMoreElements()) {
- let cookie = cookies.getNext().QueryInterface(Ci.nsICookie2);
- actual_cookies.push(cookie);
- }
- if (names.length != actual_cookies.length) {
- let left = names.filter(function(n) {
- return actual_cookies.findIndex(function(c) {
- return c.name == n;
- }) == -1;
- });
- let right = actual_cookies.filter(function(c) {
- return names.findIndex(function(n) {
- return c.name == n;
- }) == -1;
- }).map(function(c) { return c.name });
- if (left.length) {
- do_print("unexpected cookies: " + left);
- }
- if (right.length) {
- do_print("expected cookies: " + right);
- }
- }
- do_check_eq(names.length, actual_cookies.length);
- actual_cookies.sort(function(a, b) {
- if (a.lastAccessed < b.lastAccessed)
- return -1;
- if (a.lastAccessed > b.lastAccessed)
- return 1;
- return 0;
- });
- for (var i = 0; i < names.length; i++) {
- do_check_eq(names[i], actual_cookies[i].name);
- do_check_eq(names[i].startsWith('session'), actual_cookies[i].isSession);
- }
-}
-
-var lastValue = 0
-function setCookie(name, domain, path, maxAge, url) {
- let value = name + "=" + ++lastValue;
- var s = 'setting cookie ' + value;
- if (domain) {
- value += "; Domain=" + domain;
- s += ' (d=' + domain + ')';
- }
- if (path) {
- value += "; Path=" + path;
- s += ' (p=' + path + ')';
- }
- if (maxAge) {
- value += "; Max-Age=" + maxAge;
- s += ' (non-session)';
- } else {
- s += ' (session)';
- }
- s += ' for ' + url.spec;
- do_print(s);
- cs.setCookieStringFromHttp(url, null, null, value, null, null);
-}
--- a/netwerk/cookie/test/unit/xpcshell.ini
+++ b/netwerk/cookie/test/unit/xpcshell.ini
@@ -3,9 +3,8 @@ head =
tail =
skip-if = toolkit == 'gonk'
[test_bug643051.js]
[test_bug1155169.js]
[test_bug1267910.js]
[test_parser_0001.js]
[test_parser_0019.js]
-[test_eviction.js]
\ No newline at end of file
deleted file mode 100644
--- a/testing/web-platform/meta/fetch/api/basic/scheme-data.html.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[scheme-data.html]
- type: testharness
- [Fetching [POST\] data:,response%27s%20body is KO]
- expected: FAIL
-
- [Fetching [HEAD\] data:,response%27s%20body is KO]
- expected: FAIL
-
--- a/testing/web-platform/tests/fetch/api/basic/scheme-data.js
+++ b/testing/web-platform/tests/fetch/api/basic/scheme-data.js
@@ -1,39 +1,39 @@
if (this.document === undefined) {
importScripts("/resources/testharness.js");
importScripts("../resources/utils.js");
}
-function checkFetchResponse(url, data, mime) {
+function checkFetchResponse(url, data, mime, method) {
var cut = (url.length >= 40) ? "[...]" : "";
- desc = "Fetching " + url.substring(0, 40) + cut + " is OK"
+ desc = "Fetching " + (method ? "[" + method + "] " : "") + url.substring(0, 40) + cut + " is OK";
promise_test(function(test) {
- return fetch(url).then(function(resp) {
+ return fetch(url, {"method": method || "GET"}).then(function(resp) {
assert_equals(resp.status, 200, "HTTP status is 200");
assert_equals(resp.type, "basic", "response type is basic");
assert_equals(resp.headers.get("Content-Type"), mime, "Content-Type is " + resp.headers.get("Content-Type"));
return resp.text();
}).then(function(body) {
assert_equals(body, data, "Response's body is correct");
});
}, desc);
}
checkFetchResponse("data:,response%27s%20body", "response's body", "text/plain;charset=US-ASCII");
checkFetchResponse("data:text/plain;base64,cmVzcG9uc2UncyBib2R5", "response's body", "text/plain");
checkFetchResponse("data:image/png;base64,cmVzcG9uc2UncyBib2R5",
"response's body",
"image/png");
+checkFetchResponse("data:,response%27s%20body", "response's body", "text/plain;charset=US-ASCII", "POST");
+checkFetchResponse("data:,response%27s%20body", "response's body", "text/plain;charset=US-ASCII", "HEAD");
function checkKoUrl(url, method, desc) {
var cut = (url.length >= 40) ? "[...]" : "";
desc = "Fetching [" + method + "] " + url.substring(0, 45) + cut + " is KO"
promise_test(function(test) {
return promise_rejects(test, new TypeError(), fetch(url, {"method": method}));
}, desc);
}
checkKoUrl("data:notAdataUrl.com", "GET");
-checkKoUrl("data:,response%27s%20body", "POST");
-checkKoUrl("data:,response%27s%20body", "HEAD");
done();
--- a/toolkit/forgetaboutsite/ForgetAboutSite.jsm
+++ b/toolkit/forgetaboutsite/ForgetAboutSite.jsm
@@ -7,18 +7,16 @@
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
Components.utils.import("resource://gre/modules/NetUtil.jsm");
Components.utils.import("resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Downloads",
"resource://gre/modules/Downloads.jsm");
-XPCOMUtils.defineLazyModuleGetter(this, "ContextualIdentityService",
- "resource://gre/modules/ContextualIdentityService.jsm");
this.EXPORTED_SYMBOLS = ["ForgetAboutSite"];
/**
* Returns true if the string passed in is part of the root domain of the
* current string. For example, if this is "www.mozilla.org", and we pass in
* "mozilla.org", this will return true. It would return false the other way
* around.
@@ -44,24 +42,16 @@ function hasRootDomain(str, aDomain)
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
this.ForgetAboutSite = {
removeDataFromDomain: function CRH_removeDataFromDomain(aDomain)
{
- // Get all userContextId from the ContextualIdentityService and create
- // all originAttributes.
- let oaList = [ {} ]; // init the list with the default originAttributes.
-
- for (let identity of ContextualIdentityService.getIdentities()) {
- oaList.push({ userContextId: identity.userContextId});
- }
-
PlacesUtils.history.removePagesFromHost(aDomain, true);
// Cache
let cs = Cc["@mozilla.org/netwerk/cache-storage-service;1"].
getService(Ci.nsICacheStorageService);
// NOTE: there is no way to clear just that domain, so we clear out
// everything)
try {
@@ -79,29 +69,26 @@ this.ForgetAboutSite = {
} catch (ex) {
Cu.reportError("Exception thrown while clearing the image cache: " +
ex.toString());
}
// Cookies
let cm = Cc["@mozilla.org/cookiemanager;1"].
getService(Ci.nsICookieManager2);
- let enumerator;
- for (let originAttributes of oaList) {
- enumerator = cm.getCookiesFromHost(aDomain, originAttributes);
- while (enumerator.hasMoreElements()) {
- let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
- cm.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
- }
+ let enumerator = cm.getCookiesWithOriginAttributes(JSON.stringify({}), aDomain);
+ while (enumerator.hasMoreElements()) {
+ let cookie = enumerator.getNext().QueryInterface(Ci.nsICookie);
+ cm.remove(cookie.host, cookie.name, cookie.path, false, cookie.originAttributes);
}
// EME
let mps = Cc["@mozilla.org/gecko-media-plugin-service;1"].
getService(Ci.mozIGeckoMediaPluginChromeService);
- mps.forgetThisSite(aDomain);
+ mps.forgetThisSite(aDomain, JSON.stringify({}));
// Plugin data
const phInterface = Ci.nsIPluginHost;
const FLAG_CLEAR_ALL = phInterface.FLAG_CLEAR_ALL;
let ph = Cc["@mozilla.org/plugin/host;1"].getService(phInterface);
let tags = ph.getPluginTags();
let promises = [];
for (let i = 0; i < tags.length; i++) {
@@ -166,24 +153,26 @@ this.ForgetAboutSite = {
// delete data from both HTTP and HTTPS sites
let caUtils = {};
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
scriptLoader.loadSubScript("chrome://global/content/contentAreaUtils.js",
caUtils);
let httpURI = caUtils.makeURI("http://" + aDomain);
let httpsURI = caUtils.makeURI("https://" + aDomain);
- for (let originAttributes of oaList) {
- let httpPrincipal = Services.scriptSecurityManager
- .createCodebasePrincipal(httpURI, originAttributes);
- let httpsPrincipal = Services.scriptSecurityManager
- .createCodebasePrincipal(httpsURI, originAttributes);
- qms.clearStoragesForPrincipal(httpPrincipal);
- qms.clearStoragesForPrincipal(httpsPrincipal);
- }
+ // Following code section has been reverted to the state before Bug 1238183,
+ // but added a new argument to clearStoragesForPrincipal() for indicating
+ // clear all storages under a given origin.
+ let httpPrincipal = Services.scriptSecurityManager
+ .createCodebasePrincipal(httpURI, {});
+ let httpsPrincipal = Services.scriptSecurityManager
+ .createCodebasePrincipal(httpsURI, {});
+ qms.clearStoragesForPrincipal(httpPrincipal, null, true);
+ qms.clearStoragesForPrincipal(httpsPrincipal, null, true);
+
function onContentPrefsRemovalFinished() {
// Everybody else (including extensions)
Services.obs.notifyObservers(null, "browser:purge-domain-data", aDomain);
}
// Content Preferences
let cps2 = Cc["@mozilla.org/content-pref/service;1"].