Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Sun, 04 Sep 2016 13:36:59 -0400
changeset 409737 dbe4b47941c7b3d6298a0ead5e40dd828096c808
parent 409736 00c15597bb8d2e2c6615fe0b8ae616a42b558694 (current diff)
parent 409735 9855046ffeb7c945f572de0c51e82cf3c9b30dfc (diff)
child 409738 396ad56d67f8fe48c7f9807d239024b49d4ad9bd
child 409741 96d66fb7d22346d35efb7485fac704a610db954f
child 409744 110a20db97a9dc1da2a93ad3a9a6f79682f60b55
child 409745 05dd33ee7ac764364ba3fcac1fb76888303329b6
child 409746 d5590cbc772eb18f2dc1b5ce595d4a1fb1b88486
child 409749 07154bf24b5fb118af4fe78e539f97be2b2610df
child 409752 deee02ee61738e612cb6f74edb5c27b90c87057b
child 409753 d072231154cdb7a1de4f4b2fe4b36f6b0cb2c6d5
child 409758 cb14f2e7f42879d105d1bb33a406ca6181b2bd7d
child 409760 29ab1a59092406449c3cc69e981d9405922accb5
child 409778 795c2a98b1ef5c5a7347aac751c19fad36c5cbbb
child 409793 cc259e208b662625dd46edb6ee324b152c2ea50f
child 409798 edfc53aaddf087748d33961893afec5c8d0d99fe
child 409799 e20fc280635ae228ae630b23e4b28acad74a4594
child 409810 ade73d3186d22f197df66e3e463e73202b017fc4
child 409812 8c29b67a4018f85ddb7c9c3c39685c76129486c4
child 409819 d4106c222be3bec02664d30a2a7c0c5c95bb19d7
child 409821 27b87d9c985561a5a06d9aa2138309e3ed6635b4
child 409824 99ab58df50b3635a6a79b27ee90d75f9df62b377
child 409826 73529c092ff29418acf94ff37d8b244b0fb08278
child 409827 4de520286f0dfd080eb18812b96660384f33132b
child 409829 fe16dafa60280fd69e5b3f99b37dd686ae88b91f
child 409830 93b76b279b3fa2ad21edd2addbeb0e73cf7d06eb
child 409836 b61696bfd2c4f22df215bca002f6cfcc5ba869ce
child 409837 5121df8f002dd9dcc84427f08655155835beb001
child 409853 8bc66ab5871680d1719d2de3709ac4d9bc9ebbad
child 409856 28fa1ca65cd6bd0d8d92260e71022bbceca8fefd
child 409860 067469d9ca06b998ea76c4c237cf246c823faa59
child 409861 f52e52c09961a3fddf60bd9793bb5d786840d9a4
child 409863 345ee260b6ddcdf3ed1f33c37a081311d133cf80
child 409864 9431390a4d875eb815e4a72b523b1ea5cce382a9
child 409865 8225311fda9c2e70eef18be7f7a61c95f25a4c81
child 409867 6bb76fe9a7e32ddd87b7df4797561b4103c7e9ec
child 409868 06697525e786bb2cfdf71885357d7329cf8e7dea
child 409873 df8e93ca91ed3070ca7578f534dbacc8e6d31c37
child 409874 3e5d4c7c736d07efeb783ee64a0e8c9204f7da22
child 409875 14a1862ba96455b8bdd7dc4a1a607b330c3e2503
child 409876 adb62a9e270435c47789c124cd0f0a9e9eee607e
child 409879 bd6770b4a82639282d0467b60b7b5bb3346898ad
child 409880 211963a7a74a9e97bc40b4233410d736952ec95e
child 409881 d7add62fb6a640e80eb3c9f4ce0f8876fb8fd238
child 409882 c439bdb48b230d09cf82e571e5e0f1deef58b8ba
child 409883 e8681991184808839a9b9573778f3d32a7fa3f97
child 409884 5b8bfe705a2dd381d320bec2da6b564584e49073
child 409891 a3afbfc02b2c951609de3e3f322953d166c3d43f
child 409895 3ab72db16fe0c5a36a8a4399387b64f4e9b3f495
child 409897 ffbd9fe08f24fd972a2c9cc4b6611ef08945a519
child 409898 0885572c15cec641fdd4f8a0a773e1e26274981d
child 409905 96c240c32674eca060db7650dc64e53f0b763172
child 409912 14abf032a582d530e7b77cba955b2f7d1b135bb5
child 409920 a5b86174eb3d486408c4abce8f34816455e3bff9
child 409922 f38efca9b50a6f4b88e0a59feb0435d1f13e4d5e
child 409943 c3ca7426e9c827644e181b115f3152b6bec01a07
child 409964 edeae4a294a74581aed5056070e991f0b1cb2e10
child 410093 fb1122aea50aa5c25a17e70b3d97f53ab8ee98ba
child 410106 cd3626436b6bc9a6360ae4477968d3c46d84828a
child 410147 0e396a9ee640b5d7044a3e384337618f309882fa
child 410149 219cb1ba67b3eb31acc97383f1a3faa1aa27f786
child 410202 bf90416dd239049fe438ce234526dd4ef0f241dd
child 410225 117e27423855ec479b6bd32c243bbbbe7ff885b9
child 410227 1926044e218649ee559104d6f6205a3b68d70706
child 410243 6762ef73fb15c4915c611f6664e0a3221f32b4a6
child 410251 e6d11c02edff2a87da3e4e2c2b710c4b687ab9a9
child 410254 5fe2fcf1213cc1fbba72a5f48f86ff4e8a4de95f
child 410256 99651056373390cc2e6715a6cb13e67f3af8950c
child 410285 d991c62e088886e067d992ac5543d95292c498e8
child 410297 a861bc050d8590e3e6e3ecf8f7215c40b4138821
child 410337 b539dd4b280227d71fb28af1ae503171725c2c55
child 410810 4f48c01680bac4b19ca3fde75cd296c39cb0f6a6
child 410811 1f5848eac2ad199a362e14f1b4e85ea6a76ea353
child 410820 363d33f0f716f44cc01c87aa7be1e88187776939
child 410821 a4143dcac79d167cd3953b5e5ddc7cacedf051db
child 410837 b90b711456d68ab1352319d138a933e842568e7d
child 410883 9ec648bc865e117f63f6768c8b8b6715afbdd06f
child 410892 6e29812642bc3f8ca0f5b39b36064a6d50e09ea7
child 410949 8795388cb8d9c1fc72f55ce28ec881865bbff6cf
child 410958 624e1bb088de142868e78b50c81df12612095025
child 411023 6a4a3bd26229553eb56413435f0f09408ec26ed8
child 411024 4254a0dd5fd5ea087fb52c9fc138b12ab4da0d11
child 411057 c7fddda5c4dc57e75c4e095751f6ddfd9ba78fec
child 411060 bbe3a0e4ba88ca69a99c40a3a12517fe813e09b8
child 411156 73003ff76a64ab59eb4ee4aeb6d0f163e58f5d53
child 411416 0fba904b6f2f22b6c428ba47ffe065bc2fa92ec7
child 411747 9b4e7700274779749f5fd7714573ccd97aef6e65
child 415886 536cf508a8595a1baf4ea82e9c57e7b07747f8aa
push id28527
push usergsquelart@mozilla.com
push dateMon, 05 Sep 2016 01:07:03 +0000
reviewersmerge
milestone51.0a1
Merge inbound to m-c. a=merge
netwerk/cookie/test/unit/test_eviction.js
testing/web-platform/meta/fetch/api/basic/scheme-data.html.ini
--- 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"].