Merge autoland to central, a=merge
authorWes Kocher <wkocher@mozilla.com>
Mon, 24 Jul 2017 17:57:54 -0700
changeset 421679 f6528783c52507e29d5be409cc8fbf9a394a5ac8
parent 421630 899590a34d56e099d4e064a9e4d41a54993790e9 (current diff)
parent 421678 40b0b14356e190be8fac53b56369426d7b7f55f6 (diff)
child 421720 dcfb58fcb6dd8f6474eed6520ba6272dedded393
push id1517
push userjlorenzo@mozilla.com
push dateThu, 14 Sep 2017 16:50:54 +0000
treeherdermozilla-release@3b41fd564418 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge autoland to central, a=merge MozReview-Commit-ID: AWf410QGui6
devtools/client/inspector/rules/test/browser_rules_css-docs-tooltip_closes-on-escape.js
devtools/client/jsonview/main.js
--- a/browser/components/downloads/test/browser/browser_indicatorDrop.js
+++ b/browser/components/downloads/test/browser/browser_indicatorDrop.js
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
   "resource://testing-common/httpd.js");
 
 registerCleanupFunction(async function() {
   await task_resetState();
-  await task_clearHistory();
+  await PlacesUtils.history.clear();
 });
 
 add_task(async function test_indicatorDrop() {
   let downloadButton = document.getElementById("downloads-button");
   ok(downloadButton, "download button present");
 
   let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
       getService(Ci.mozIJSSubScriptLoader);
--- a/browser/components/downloads/test/browser/browser_libraryDrop.js
+++ b/browser/components/downloads/test/browser/browser_libraryDrop.js
@@ -3,17 +3,17 @@
 /* Any copyright is dedicated to the Public Domain.
    http://creativecommons.org/publicdomain/zero/1.0/ */
 
 XPCOMUtils.defineLazyModuleGetter(this, "HttpServer",
   "resource://testing-common/httpd.js");
 
 registerCleanupFunction(async function() {
   await task_resetState();
-  await task_clearHistory();
+  await PlacesUtils.history.clear();
 });
 
 add_task(async function test_indicatorDrop() {
   let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
       getService(Ci.mozIJSSubScriptLoader);
   let EventUtils = {};
   scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/EventUtils.js", EventUtils);
 
--- a/browser/components/downloads/test/browser/head.js
+++ b/browser/components/downloads/test/browser/head.js
@@ -158,26 +158,16 @@ function startServer() {
   });
 }
 
 function httpUrl(aFileName) {
   return "http://localhost:" + gHttpServer.identity.primaryPort + "/" +
     aFileName;
 }
 
-function task_clearHistory() {
-  return new Promise(function(resolve) {
-    Services.obs.addObserver(function observeCH(aSubject, aTopic, aData) {
-      Services.obs.removeObserver(observeCH, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-      resolve();
-    }, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-    PlacesUtils.history.clear();
-  });
-}
-
 function openLibrary(aLeftPaneRoot) {
   let library = window.openDialog("chrome://browser/content/places/places.xul",
                                   "", "chrome,toolbar=yes,dialog=no,resizable",
                                   aLeftPaneRoot);
 
   return new Promise(resolve => {
     waitForFocus(resolve, library);
   });
--- a/browser/components/places/tests/unit/test_clearHistory_shutdown.js
+++ b/browser/components/places/tests/unit/test_clearHistory_shutdown.js
@@ -14,17 +14,16 @@ const URIS = [
   "http://b.example2.com/",
   "http://c.example3.com/"
 ];
 
 const TOPIC_CONNECTION_CLOSED = "places-connection-closed";
 
 var EXPECTED_NOTIFICATIONS = [
   "places-shutdown",
-  "places-will-close-connection",
   "places-expiration-finished",
   "places-connection-closed"
 ];
 
 const UNEXPECTED_NOTIFICATIONS = [
   "xpcom-shutdown"
 ];
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.js
@@ -1,72 +1,45 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 // Test to make sure that the visited page titles do not get updated inside the
 // private browsing mode.
 "use strict";
 
-Cu.import("resource://gre/modules/Services.jsm");
-Cu.import("resource://gre/modules/PlacesUtils.jsm");
-
 add_task(async function test() {
   const TEST_URL = "http://mochi.test:8888/browser/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placesTitleNoUpdate.html"
-  const TEST_URI = Services.io.newURI(TEST_URL);
   const TITLE_1 = "Title 1";
   const TITLE_2 = "Title 2";
 
-  function waitForTitleChanged() {
-    return new Promise(resolve => {
-      let historyObserver = {
-        onTitleChanged(uri, pageTitle) {
-          PlacesUtils.history.removeObserver(historyObserver, false);
-          resolve({uri, pageTitle});
-        },
-        onBeginUpdateBatch() {},
-        onEndUpdateBatch() {},
-        onVisit() {},
-        onDeleteURI() {},
-        onClearHistory() {},
-        onPageChanged() {},
-        onDeleteVisits() {},
-        QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
-      };
-
-      PlacesUtils.history.addObserver(historyObserver);
-    });
-  }
+  await PlacesUtils.history.clear();
 
-  await PlacesTestUtils.clearHistory();
-
-  let tabToClose = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, TEST_URL);
-  await waitForTitleChanged();
-  is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_1, "The title matches the orignal title after first visit");
+  let promiseTitleChanged = PlacesTestUtils.waitForNotification(
+    "onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
+  let tab = await BrowserTestUtils.openNewForegroundTab(gBrowser, TEST_URL);
+  registerCleanupFunction(async () => {
+    await BrowserTestUtils.removeTab(tab);
+  });
+  info("Wait for a title change notification.");
+  await promiseTitleChanged;
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, TITLE_1,
+     "The title matches the orignal title after first visit");
 
-  let place = {
-    uri: TEST_URI,
-    title: TITLE_2,
-    visits: [{
-      visitDate: Date.now() * 1000,
-      transitionType: Ci.nsINavHistoryService.TRANSITION_LINK
-    }]
-  };
-  PlacesUtils.asyncHistory.updatePlaces(place, {
-    handleError: () => ok(false, "Unexpected error in adding visit."),
-    handleResult() { },
-    handleCompletion() {}
-  });
-
-  await waitForTitleChanged();
-  is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_2, "The title matches the updated title after updating visit");
+  promiseTitleChanged = PlacesTestUtils.waitForNotification(
+    "onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
+  await PlacesTestUtils.addVisits({ uri: TEST_URL, title: TITLE_2 });
+  info("Wait for a title change notification.");
+  await promiseTitleChanged;
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, TITLE_2,
+     "The title matches the orignal title after updating visit");
 
   let privateWin = await BrowserTestUtils.openNewBrowserWindow({private: true});
-  await BrowserTestUtils.browserLoaded(privateWin.gBrowser.addTab(TEST_URL).linkedBrowser);
-
-  is(PlacesUtils.history.getPageTitle(TEST_URI), TITLE_2, "The title remains the same after visiting in private window");
-  await PlacesTestUtils.clearHistory();
-
-  // Cleanup
-  BrowserTestUtils.closeWindow(privateWin);
-  gBrowser.removeTab(tabToClose);
+  registerCleanupFunction(async () => {
+    await BrowserTestUtils.closeWindow(privateWin);
+  });
+  await BrowserTestUtils.openNewForegroundTab(privateWin.gBrowser, TEST_URL);
+  // Wait long enough to be sure history didn't set a title.
+  await new Promise(resolve => setTimeout(resolve, 1000));
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, TITLE_2,
+     "The title remains the same after visiting in private window");
 });
 
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_placestitle.js
@@ -13,83 +13,53 @@ add_task(async function test() {
   function cleanup() {
     // delete all cookies
     cm.removeAll();
     // delete all history items
     return PlacesTestUtils.clearHistory();
   }
 
   await cleanup();
-
-  let deferredFirst = PromiseUtils.defer();
-  let deferredSecond = PromiseUtils.defer();
-  let deferredThird = PromiseUtils.defer();
-
-  let testNumber = 0;
-  let historyObserver = {
-    onTitleChanged(aURI, aPageTitle) {
-      if (aURI.spec != TEST_URL)
-        return;
-      switch (++testNumber) {
-        case 1:
-          // The first time that the page is loaded
-          deferredFirst.resolve(aPageTitle);
-          break;
-        case 2:
-          // The second time that the page is loaded
-          deferredSecond.resolve(aPageTitle);
-          break;
-        case 3:
-          // After clean up
-          deferredThird.resolve(aPageTitle);
-          break;
-        default:
-          // Checks that opening the page in a private window should not fire a
-          // title change.
-          ok(false, "Title changed. Unexpected pass: " + testNumber);
-      }
-    },
-
-    onBeginUpdateBatch() {},
-    onEndUpdateBatch() {},
-    onVisit() {},
-    onDeleteURI() {},
-    onClearHistory() {},
-    onPageChanged() {},
-    onDeleteVisits() {},
-    QueryInterface: XPCOMUtils.generateQI([Ci.nsINavHistoryObserver])
-  };
-  PlacesUtils.history.addObserver(historyObserver);
-
+  registerCleanupFunction(cleanup);
 
   let win = await BrowserTestUtils.openNewBrowserWindow();
-  win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
-  let aPageTitle = await deferredFirst.promise;
-  // The first time that the page is loaded
-  is(aPageTitle, "No Cookie",
+  registerCleanupFunction(async () => {
+    await BrowserTestUtils.closeWindow(win);
+  });
+
+  let promiseTitleChanged = PlacesTestUtils.waitForNotification(
+    "onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
+  await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+  await promiseTitleChanged;
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, "No Cookie",
      "The page should be loaded without any cookie for the first time");
 
-  win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
-  aPageTitle = await deferredSecond.promise;
-  // The second time that the page is loaded
-  is(aPageTitle, "Cookie",
+  promiseTitleChanged = PlacesTestUtils.waitForNotification(
+    "onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
+  await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+  await promiseTitleChanged;
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, "Cookie",
      "The page should be loaded with a cookie for the second time");
 
   await cleanup();
 
-  win.gBrowser.selectedTab = win.gBrowser.addTab(TEST_URL);
-  aPageTitle = await deferredThird.promise;
-  // After clean up
-  is(aPageTitle, "No Cookie",
+  promiseTitleChanged = PlacesTestUtils.waitForNotification(
+    "onTitleChanged", (uri, title) => uri.spec == TEST_URL, "history");
+  await BrowserTestUtils.openNewForegroundTab(win.gBrowser, TEST_URL);
+  await promiseTitleChanged;
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, "No Cookie",
      "The page should be loaded without any cookie again");
 
+  // Reopen the page in a private browser window, it should not notify a title
+  // change.
   let win2 = await BrowserTestUtils.openNewBrowserWindow({private: true});
-
-  let private_tab = win2.gBrowser.addTab(TEST_URL);
-  win2.gBrowser.selectedTab = private_tab;
-  await BrowserTestUtils.browserLoaded(private_tab.linkedBrowser);
+  registerCleanupFunction(async () => {
+    let promisePBExit = TestUtils.topicObserved("last-pb-context-exited");
+    await BrowserTestUtils.closeWindow(win2);
+    await promisePBExit;
+  });
 
-  // Cleanup
-  await cleanup();
-  PlacesUtils.history.removeObserver(historyObserver);
-  await BrowserTestUtils.closeWindow(win);
-  await BrowserTestUtils.closeWindow(win2);
+  await BrowserTestUtils.openNewForegroundTab(win2.gBrowser, TEST_URL);
+  // Wait long enough to be sure history didn't set a title.
+  await new Promise(resolve => setTimeout(resolve, 1000));
+  is((await PlacesUtils.history.fetch(TEST_URL)).title, "No Cookie",
+     "The title remains the same after visiting in private window");
 });
--- a/browser/components/privatebrowsing/test/browser/head.js
+++ b/browser/components/privatebrowsing/test/browser/head.js
@@ -1,14 +1,19 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 var {PromiseUtils} = Cu.import("resource://gre/modules/PromiseUtils.jsm", {});
+Cu.import("resource://gre/modules/Services.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
+  "resource://gre/modules/PlacesUtils.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesTestUtils",
   "resource://testing-common/PlacesTestUtils.jsm");
+XPCOMUtils.defineLazyModuleGetter(this, "TestUtils",
+  "resource://testing-common/TestUtils.jsm");
 
 function whenNewWindowLoaded(aOptions, aCallback) {
   let win = OpenBrowserWindow(aOptions);
   let focused = SimpleTest.promiseFocus(win);
   let startupFinished = TestUtils.topicObserved("browser-delayed-startup-finished",
                                                 subject => subject == win).then(() => win);
   Promise.all([focused, startupFinished])
     .then(results => executeSoon(() => aCallback(results[1])));
--- a/browser/extensions/formautofill/FormAutofillHandler.jsm
+++ b/browser/extensions/formautofill/FormAutofillHandler.jsm
@@ -64,17 +64,17 @@ FormAutofillHandler.prototype = {
 
   /**
    * Similiar to `fieldDetails`, and `creditCardFieldDetails` contains the
    * Credit Card records only.
    */
   creditCardFieldDetails: null,
 
   get isValidAddressForm() {
-    return this.addressFieldDetails.length > FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD;
+    return this.addressFieldDetails.length >= FormAutofillUtils.AUTOFILL_FIELDS_THRESHOLD;
   },
 
   get isValidCreditCardForm() {
     return this.creditCardFieldDetails.some(i => i.fieldName == "cc-number");
   },
 
   /**
    * String of the filled profile's guid.
--- a/browser/extensions/formautofill/test/unit/test_collectFormFields.js
+++ b/browser/extensions/formautofill/test/unit/test_collectFormFields.js
@@ -112,47 +112,43 @@ const TESTCASES = [
     },
   },
   {
     description: "It's a valid address and credit card form.",
     document: `<form>
                <input id="given-name" autocomplete="shipping given-name">
                <input id="family-name" autocomplete="shipping family-name">
                <input id="street-addr" autocomplete="shipping street-address">
-               <input id="city" autocomplete="shipping address-level2">
                <input id="cc-number" autocomplete="shipping cc-number">
                </form>`,
     addressFieldDetails: [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "family-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "street-address"},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
     ],
     creditCardFieldDetails: [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "cc-number"},
     ],
     isValidForm: {
       address: true,
       creditCard: true,
     },
   },
   {
     description: "It's an invalid address and credit form.",
     document: `<form>
                <input id="given-name" autocomplete="shipping given-name">
                <input autocomplete="shipping address-level2">
-               <select autocomplete="shipping country"></select>
                <input id="cc-name" autocomplete="cc-name">
                <input id="cc-exp-month" autocomplete="cc-exp-month">
                <input id="cc-exp-year" autocomplete="cc-exp-year">
                </form>`,
     addressFieldDetails: [
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "given-name"},
       {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "address-level2"},
-      {"section": "", "addressType": "shipping", "contactType": "", "fieldName": "country"},
     ],
     creditCardFieldDetails: [
       {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-name"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-month"},
       {"section": "", "addressType": "", "contactType": "", "fieldName": "cc-exp-year"},
     ],
     isValidForm: {
       address: false,
deleted file mode 100644
--- a/devtools/client/inspector/rules/test/browser_rules_css-docs-tooltip_closes-on-escape.js
+++ /dev/null
@@ -1,52 +0,0 @@
-/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
-/* Any copyright is dedicated to the Public Domain.
- http://creativecommons.org/publicdomain/zero/1.0/ */
-
-/**
- * Test that the CssDocs tooltip of the ruleview can be closed when pressing the Escape
- * key.
- */
-
-"use strict";
-
-const {setBaseCssDocsUrl} =
-  require("devtools/client/shared/widgets/MdnDocsWidget");
-
-const PROPERTYNAME = "color";
-
-const TEST_URI = `
-  <html>
-    <body>
-      <div style="color: red">
-        Test "Show MDN Docs" closes on escape
-      </div>
-    </body>
-  </html>
-`;
-
-/**
- * Test that the tooltip is hidden when we press Escape
- */
-add_task(function* () {
-  yield addTab("data:text/html;charset=utf8," + encodeURIComponent(TEST_URI));
-  let {inspector, view} = yield openRuleView();
-  yield selectNode("div", inspector);
-
-  setBaseCssDocsUrl(URL_ROOT);
-
-  info("Retrieve a valid anchor for the CssDocs tooltip");
-  let {nameSpan} = getRuleViewProperty(view, "element", PROPERTYNAME);
-
-  info("Showing the MDN docs tooltip");
-  let cssDocs = view.tooltips.getTooltip("cssDocs");
-  let onShown = cssDocs.tooltip.once("shown");
-  cssDocs.show(nameSpan, PROPERTYNAME);
-  yield onShown;
-  ok(true, "The MDN docs tooltip was shown");
-
-  info("Simulate pressing the 'Escape' key");
-  let onHidden = cssDocs.tooltip.once("hidden");
-  EventUtils.sendKey("escape");
-  yield onHidden;
-  ok(true, "The MDN docs tooltip was hidden on pressing 'escape'");
-});
--- a/devtools/client/inspector/test/browser.ini
+++ b/devtools/client/inspector/test/browser.ini
@@ -175,15 +175,16 @@ skip-if = os == "mac" # Full keyboard na
 [browser_inspector_search-filter_context-menu.js]
 subsuite = clipboard
 skip-if = (os == 'linux' && bits == 32 && debug) # bug 1328915, disable linux32 debug devtools for timeouts
 [browser_inspector_search_keyboard_trap.js]
 [browser_inspector_search-label.js]
 [browser_inspector_search-reserved.js]
 [browser_inspector_search-selection.js]
 [browser_inspector_search-sidebar.js]
+[browser_inspector_search-suggests-ids-and-classes.js]
 [browser_inspector_select-docshell.js]
 [browser_inspector_select-last-selected.js]
 [browser_inspector_search-navigation.js]
 [browser_inspector_sidebarstate.js]
 [browser_inspector_startup.js]
 [browser_inspector_switch-to-inspector-on-pick.js]
 [browser_inspector_textbox-menu.js]
--- a/devtools/client/inspector/test/browser_inspector_search-suggests-ids-and-classes.js
+++ b/devtools/client/inspector/test/browser_inspector_search-suggests-ids-and-classes.js
@@ -12,22 +12,22 @@
 //  suggestions array with count [
 //    [suggestion1, count1], [suggestion2] ...
 //  ] count can be left to represent 1
 // ]
 const KEY_STATES = [
   ["s", [["span", 1], [".span", 1], ["#span", 1]]],
   ["p", [["span", 1], [".span", 1], ["#span", 1]]],
   ["a", [["span", 1], [".span", 1], ["#span", 1]]],
-  ["n", []],
+  ["n", [["span", 1], [".span", 1], ["#span", 1]]],
   [" ", [["span div", 1]]],
   // mixed tag/class/id suggestions only work for the first word
   ["d", [["span div", 1]]],
   ["VK_BACK_SPACE", [["span div", 1]]],
-  ["VK_BACK_SPACE", []],
+  ["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
   ["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
   ["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
   ["VK_BACK_SPACE", [["span", 1], [".span", 1], ["#span", 1]]],
   ["VK_BACK_SPACE", []],
   // Test that mixed tags, classes and ids are grouped by types, sorted by
   // count and alphabetical order
   ["b", [
     ["button", 3],
@@ -72,13 +72,11 @@ add_task(function* () {
     let actualSuggestions = popup.getItems();
     is(popup.isOpen ? actualSuggestions.length : 0, expectedSuggestions.length,
        "There are expected number of suggestions");
     actualSuggestions.reverse();
 
     for (let i = 0; i < expectedSuggestions.length; i++) {
       is(expectedSuggestions[i][0], actualSuggestions[i].label,
          "The suggestion at " + i + "th index is correct.");
-      is(expectedSuggestions[i][1] || 1, actualSuggestions[i].count,
-         "The count for suggestion at " + i + "th index is correct.");
     }
   }
 });
--- a/docshell/base/nsDocShell.cpp
+++ b/docshell/base/nsDocShell.cpp
@@ -1142,18 +1142,18 @@ nsDocShell::ConvertDocShellLoadInfoToLoa
       break;
     case nsIDocShellLoadInfo::loadReloadCharsetChange:
       loadType = LOAD_RELOAD_CHARSET_CHANGE;
       break;
     case nsIDocShellLoadInfo::loadReloadCharsetChangeBypassCache:
       loadType = LOAD_RELOAD_CHARSET_CHANGE_BYPASS_CACHE;
       break;
     case nsIDocShellLoadInfo::loadReloadCharsetChangeBypassProxyAndCache:
-      break;
       loadType = LOAD_RELOAD_CHARSET_CHANGE_BYPASS_PROXY_AND_CACHE;
+      break;
     case nsIDocShellLoadInfo::loadReloadBypassCache:
       loadType = LOAD_RELOAD_BYPASS_CACHE;
       break;
     case nsIDocShellLoadInfo::loadReloadBypassProxy:
       loadType = LOAD_RELOAD_BYPASS_PROXY;
       break;
     case nsIDocShellLoadInfo::loadReloadBypassProxyAndCache:
       loadType = LOAD_RELOAD_BYPASS_PROXY_AND_CACHE;
--- a/dom/animation/KeyframeEffectReadOnly.cpp
+++ b/dom/animation/KeyframeEffectReadOnly.cpp
@@ -1171,18 +1171,17 @@ KeyframeEffectReadOnly::GetProperties(
       propertyDetails.mValues.AppendElement(fromValue, mozilla::fallible);
 
       // Normally we can ignore the to-value for this segment since it is
       // identical to the from-value from the next segment. However, we need
       // to add it if either:
       // a) this is the last segment, or
       // b) the next segment's from-value differs.
       if (segmentIdx == segmentLen - 1 ||
-          property.mSegments[segmentIdx + 1].mFromValue.mGecko !=
-            segment.mToValue.mGecko) {
+          property.mSegments[segmentIdx + 1].mFromValue != segment.mToValue) {
         binding_detail::FastAnimationPropertyValueDetails toValue;
         CreatePropertyValue(property.mProperty, segment.mToKey,
                             Nothing(), segment.mToValue,
                             segment.mToComposite, toValue);
         // It doesn't really make sense to have a timing function on the
         // last property value or before a sudden jump so we just drop the
         // easing property altogether.
         toValue.mEasing.Reset();
--- a/dom/media/gmp/ChromiumCDMChild.cpp
+++ b/dom/media/gmp/ChromiumCDMChild.cpp
@@ -134,16 +134,20 @@ ToString(const nsTArray<ipc::Shmem>& aBu
 cdm::Buffer*
 ChromiumCDMChild::Allocate(uint32_t aCapacity)
 {
   GMP_LOG("ChromiumCDMChild::Allocate(capacity=%" PRIu32 ") bufferSizes={%s}",
           aCapacity,
           ToString(mBuffers).get());
   MOZ_ASSERT(IsOnMessageLoopThread());
 
+  if (mBuffers.IsEmpty()) {
+    Unused << SendIncreaseShmemPoolSize();
+  }
+
   // Find the shmem with the least amount of wasted space if we were to
   // select it for this sized allocation. We need to do this because shmems
   // for decrypted audio as well as video frames are both stored in this
   // list, and we don't want to use the video frame shmems for audio samples.
   const size_t invalid = std::numeric_limits<size_t>::max();
   size_t best = invalid;
   auto wastedSpace = [this, aCapacity](size_t index) {
     return mBuffers[index].Size<uint8_t>() - aCapacity;
--- a/dom/media/gmp/ChromiumCDMParent.cpp
+++ b/dom/media/gmp/ChromiumCDMParent.cpp
@@ -203,16 +203,17 @@ ChromiumCDMParent::InitCDMInputBuffer(gm
                                 crypto.mEncryptedSizes,
                                 crypto.mValid);
   return true;
 }
 
 bool
 ChromiumCDMParent::SendBufferToCDM(uint32_t aSizeInBytes)
 {
+  GMP_LOG("ChromiumCDMParent::SendBufferToCDM() size=%" PRIu32, aSizeInBytes);
   Shmem shmem;
   if (!AllocShmem(aSizeInBytes, Shmem::SharedMemory::TYPE_BASIC, &shmem)) {
     return false;
   }
   if (!SendGiveBuffer(shmem)) {
     DeallocShmem(shmem);
     return false;
   }
@@ -612,16 +613,46 @@ ChromiumCDMParent::RecvDecrypted(const u
         MakeSpan<const uint8_t>(aShmem.get<uint8_t>(), aShmem.Size<uint8_t>()));
       mDecrypts.RemoveElementAt(i);
       break;
     }
   }
   return IPC_OK();
 }
 
+ipc::IPCResult
+ChromiumCDMParent::RecvIncreaseShmemPoolSize()
+{
+  GMP_LOG("%s(this=%p) limit=%" PRIu32 " active=%" PRIu32,
+          __func__,
+          this,
+          mVideoShmemLimit,
+          mVideoShmemsActive);
+
+  // Put an upper limit on the number of shmems we tolerate the CDM asking
+  // for, to prevent a memory blow-out. In practice, we expect the CDM to
+  // need less than 5, but some encodings require more.
+  // We'd expect CDMs to not have video frames larger than 720p-1080p
+  // (due to DRM robustness requirements), which is about 1.5MB-3MB per
+  // frame.
+  if (mVideoShmemLimit > 50) {
+    mDecodePromise.RejectIfExists(
+      MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
+                  RESULT_DETAIL("Failled to ensure CDM has enough shmems.")),
+      __func__);
+    Shutdown();
+    return IPC_OK();
+  }
+  mVideoShmemLimit++;
+
+  EnsureSufficientShmems(mVideoFrameBufferSize);
+
+  return IPC_OK();
+}
+
 bool
 ChromiumCDMParent::PurgeShmems()
 {
   GMP_LOG("ChromiumCDMParent::PurgeShmems(this=%p) frame_size=%" PRIuSIZE
           " limit=%" PRIu32 " active=%" PRIu32,
           this,
           mVideoFrameBufferSize,
           mVideoShmemLimit,
@@ -670,44 +701,33 @@ ChromiumCDMParent::EnsureSufficientShmem
   // we've seen cases where it allocates multiple buffers, returns one and
   // holds onto the rest. So we need to ensure we have several extra
   // shmems pre-allocated for the CDM. This threshold is set by the pref
   // media.eme.chromium-api.video-shmems.
   //
   // We also have a failure recovery mechanism; if the CDM asks for more
   // buffers than we have shmem's available, ChromiumCDMChild gives the
   // CDM a non-shared memory buffer, and returns the frame to the parent
-  // in an nsTArray<uint8_t> instead of a shmem. Every time this happens,
-  // the parent sends an extra shmem to the CDM process for it to add to the
-  // set of shmems with which to return output. Via this mechanism we should
-  // recover from incorrectly predicting how many shmems to pre-allocate.
+  // in an nsTArray<uint8_t> instead of a shmem. The child then sends a
+  // message to the parent asking it to increase the number of shmems in
+  // the pool. Via this mechanism we should recover from incorrectly
+  // predicting how many shmems to pre-allocate.
   //
   // At decoder start up, we guess how big the shmems need to be based on
   // the video frame dimensions. If we guess wrong, the CDM will follow
   // the non-shmem path, and we'll re-create the shmems of the correct size.
   // This meanns we can recover from guessing the shmem size wrong.
   // We must re-take this path after every decoder de-init/re-init, as the
   // frame sizes should change every time we switch video stream.
 
   if (mVideoFrameBufferSize < aVideoFrameSize) {
     if (!PurgeShmems()) {
       return false;
     }
     mVideoFrameBufferSize = aVideoFrameSize;
-  } else {
-    // Put an upper limit on the number of shmems we tolerate the CDM asking
-    // for, to prevent a memory blow-out. In practice, we expect the CDM to
-    // need less than 5, but some encodings require more.
-    // We'd expect CDMs to not have video frames larger than 720p-1080p
-    // (due to DRM robustness requirements), which is about 1.5MB-3MB per
-    // frame.
-    if (mVideoShmemLimit > 50) {
-      return false;
-    }
-    mVideoShmemLimit++;
   }
 
   while (mVideoShmemsActive < mVideoShmemLimit) {
     if (!SendBufferToCDM(mVideoFrameBufferSize)) {
       return false;
     }
     mVideoShmemsActive++;
   }
@@ -717,17 +737,19 @@ ChromiumCDMParent::EnsureSufficientShmem
 
   return true;
 }
 
 ipc::IPCResult
 ChromiumCDMParent::RecvDecodedData(const CDMVideoFrame& aFrame,
                                    nsTArray<uint8_t>&& aData)
 {
-  GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p)", this);
+  GMP_LOG("ChromiumCDMParent::RecvDecodedData(this=%p) time=%" PRId64,
+          this,
+          aFrame.mTimestamp());
 
   if (mIsShutdown || mDecodePromise.IsEmpty()) {
     return IPC_OK();
   }
 
   if (!EnsureSufficientShmems(aData.Length())) {
     mDecodePromise.RejectIfExists(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
@@ -920,18 +942,25 @@ ChromiumCDMParent::InitializeVideoDecode
 {
   if (mIsShutdown) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                   RESULT_DETAIL("ChromiumCDMParent is shutdown")),
       __func__);
   }
 
+  // The Widevine CDM version 1.4.8.970 and above contain a video decoder that
+  // does not optimally allocate video frames; it requests buffers much larger
+  // than required. The exact formula the CDM uses to calculate their frame
+  // sizes isn't obvious, but they normally request around or slightly more
+  // than 1.5X the optimal amount. So pad the size of buffers we allocate so
+  // that we're likely to have buffers big enough to accomodate the CDM's weird
+  // frame size calculation.
   const size_t bufferSize =
-    I420FrameBufferSizePadded(aInfo.mImage.width, aInfo.mImage.height);
+    1.7 * I420FrameBufferSizePadded(aInfo.mImage.width, aInfo.mImage.height);
   if (bufferSize <= 0) {
     return MediaDataDecoder::InitPromise::CreateAndReject(
       MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR,
                   RESULT_DETAIL("Video frame buffer size is invalid.")),
       __func__);
   }
 
   if (!EnsureSufficientShmems(bufferSize)) {
--- a/dom/media/gmp/ChromiumCDMParent.h
+++ b/dom/media/gmp/ChromiumCDMParent.h
@@ -121,16 +121,17 @@ protected:
   ipc::IPCResult RecvDecodedShmem(const CDMVideoFrame& aFrame,
                                   ipc::Shmem&& aShmem) override;
   ipc::IPCResult RecvDecodedData(const CDMVideoFrame& aFrame,
                                  nsTArray<uint8_t>&& aData) override;
   ipc::IPCResult RecvDecodeFailed(const uint32_t& aStatus) override;
   ipc::IPCResult RecvShutdown() override;
   ipc::IPCResult RecvResetVideoDecoderComplete() override;
   ipc::IPCResult RecvDrainComplete() override;
+  ipc::IPCResult RecvIncreaseShmemPoolSize() override;
   void ActorDestroy(ActorDestroyReason aWhy) override;
   bool SendBufferToCDM(uint32_t aSizeInBytes);
 
   void ReorderAndReturnOutput(RefPtr<VideoData>&& aFrame);
 
   void RejectPromise(uint32_t aPromiseId,
                      nsresult aError,
                      const nsCString& aErrorMessage);
--- a/dom/media/gmp/PChromiumCDM.ipdl
+++ b/dom/media/gmp/PChromiumCDM.ipdl
@@ -101,12 +101,14 @@ parent:
   async DecodedData(CDMVideoFrame aFrame, uint8_t[] aData);
   async DecodeFailed(uint32_t aStatus);
 
   async ResetVideoDecoderComplete();
 
   async DrainComplete();
 
   async Shutdown();
+
+  async IncreaseShmemPoolSize();
 };
 
 } // namespace gmp
 } // namespace mozilla
--- a/js/src/jsopcode.h
+++ b/js/src/jsopcode.h
@@ -499,17 +499,17 @@ class SrcNoteLineScanner
         lineHeader = (offset == 0);
 
         if (SN_IS_TERMINATOR(sn))
             return;
 
         ptrdiff_t nextOffset;
         while ((nextOffset = offset + SN_DELTA(sn)) <= relpc && !SN_IS_TERMINATOR(sn)) {
             offset = nextOffset;
-            SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+            SrcNoteType type = SN_TYPE(sn);
             if (type == SRC_SETLINE || type == SRC_NEWLINE) {
                 if (type == SRC_SETLINE)
                     lineno = GetSrcNoteOffset(sn, 0);
                 else
                     lineno++;
 
                 if (offset == relpc)
                     lineHeader = true;
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -3281,17 +3281,17 @@ js::PCToLineNumber(unsigned startLine, j
      */
     ptrdiff_t offset = 0;
     ptrdiff_t target = pc - code;
     for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
         offset += SN_DELTA(sn);
         if (offset > target)
             break;
 
-        SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+        SrcNoteType type = SN_TYPE(sn);
         if (type == SRC_SETLINE) {
             lineno = unsigned(GetSrcNoteOffset(sn, 0));
             column = 0;
         } else if (type == SRC_NEWLINE) {
             lineno++;
             column = 0;
         } else if (type == SRC_COLSPAN) {
             ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
@@ -3333,17 +3333,17 @@ js::LineNumberToPC(JSScript* script, uns
         if (lineno >= target) {
             unsigned diff = lineno - target;
             if (diff < bestdiff) {
                 bestdiff = diff;
                 best = offset;
             }
         }
         offset += SN_DELTA(sn);
-        SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+        SrcNoteType type = SN_TYPE(sn);
         if (type == SRC_SETLINE) {
             lineno = unsigned(GetSrcNoteOffset(sn, 0));
         } else if (type == SRC_NEWLINE) {
             lineno++;
         }
     }
     if (best >= 0)
         offset = best;
@@ -3352,17 +3352,17 @@ out:
 }
 
 JS_FRIEND_API(unsigned)
 js::GetScriptLineExtent(JSScript* script)
 {
     unsigned lineno = script->lineno();
     unsigned maxLineNo = lineno;
     for (jssrcnote* sn = script->notes(); !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
-        SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+        SrcNoteType type = SN_TYPE(sn);
         if (type == SRC_SETLINE)
             lineno = unsigned(GetSrcNoteOffset(sn, 0));
         else if (type == SRC_NEWLINE)
             lineno++;
 
         if (maxLineNo < lineno)
             maxLineNo = lineno;
     }
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -2374,17 +2374,17 @@ SrcNotes(JSContext* cx, HandleScript scr
     unsigned offset = 0;
     unsigned colspan = 0;
     unsigned lineno = script->lineno();
     jssrcnote* notes = script->notes();
     unsigned switchTableEnd = 0, switchTableStart = 0;
     for (jssrcnote* sn = notes; !SN_IS_TERMINATOR(sn); sn = SN_NEXT(sn)) {
         unsigned delta = SN_DELTA(sn);
         offset += delta;
-        SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+        SrcNoteType type = SN_TYPE(sn);
         const char* name = js_SrcNoteSpec[type].name;
         if (!sp->jsprintf("%3u: %4u %5u [%4u] %-8s",
                           unsigned(sn - notes), lineno, offset, delta, name))
         {
             return false;
         }
 
         switch (type) {
--- a/js/src/vm/CodeCoverage.cpp
+++ b/js/src/vm/CodeCoverage.cpp
@@ -184,17 +184,17 @@ LCovSource::writeScript(JSScript* script
                 hits = counts->numExec();
         }
 
         // If we have additional source notes, walk all the source notes of the
         // current pc.
         if (snpc <= pc) {
             size_t oldLine = lineno;
             while (!SN_IS_TERMINATOR(sn) && snpc <= pc) {
-                SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+                SrcNoteType type = SN_TYPE(sn);
                 if (type == SRC_SETLINE)
                     lineno = size_t(GetSrcNoteOffset(sn, 0));
                 else if (type == SRC_NEWLINE)
                     lineno++;
                 else if (type == SRC_TABLESWITCH)
                     tableswitchExitOffset = GetSrcNoteOffset(sn, 0);
 
                 sn = SN_NEXT(sn);
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -5930,17 +5930,17 @@ class BytecodeRangeWithPosition : privat
   private:
     void updatePosition() {
         /*
          * Determine the current line number by reading all source notes up to
          * and including the current offset.
          */
         jsbytecode *lastLinePC = nullptr;
         while (!SN_IS_TERMINATOR(sn) && snpc <= frontPC()) {
-            SrcNoteType type = (SrcNoteType) SN_TYPE(sn);
+            SrcNoteType type = SN_TYPE(sn);
             if (type == SRC_COLSPAN) {
                 ptrdiff_t colspan = SN_OFFSET_TO_COLSPAN(GetSrcNoteOffset(sn, 0));
                 MOZ_ASSERT(ptrdiff_t(column) + colspan >= 0);
                 column += colspan;
                 lastLinePC = snpc;
             } else if (type == SRC_SETLINE) {
                 lineno = size_t(GetSrcNoteOffset(sn, 0));
                 column = 0;
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -3905,19 +3905,19 @@ nsLayoutUtils::BinarySearchForPosition(D
                                 aBaseInx, inx, aEndInx, aCursorPos, aIndex,
                                 aTextWidth)) {
       return true;
     }
   }
   return false;
 }
 
-static void
-AddBoxesForFrame(nsIFrame* aFrame,
-                 nsLayoutUtils::BoxCallback* aCallback)
+void
+nsLayoutUtils::AddBoxesForFrame(nsIFrame* aFrame,
+                                nsLayoutUtils::BoxCallback* aCallback)
 {
   nsIAtom* pseudoType = aFrame->StyleContext()->GetPseudo();
 
   if (pseudoType == nsCSSAnonBoxes::tableWrapper) {
     AddBoxesForFrame(aFrame->PrincipalChildList().FirstChild(), aCallback);
     if (aCallback->mIncludeCaptionBoxForTable) {
       nsIFrame* kid = aFrame->GetChildList(nsIFrame::kCaptionList).FirstChild();
       if (kid) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -1150,16 +1150,21 @@ public:
    * continuations, "drilling down" through table wrapper frames and
    * some anonymous blocks since they're not real CSS boxes.
    * If aFrame is null, no boxes are returned.
    * SVG frames return a single box, themselves.
    */
   static void GetAllInFlowBoxes(nsIFrame* aFrame, BoxCallback* aCallback);
 
   /**
+   * Like GetAllInFlowBoxes, but doesn't include continuations.
+   */
+  static void AddBoxesForFrame(nsIFrame* aFrame, BoxCallback* aCallback);
+
+  /**
    * Find the first frame descendant of aFrame (including aFrame) which is
    * not an anonymous frame that getBoxQuads/getClientRects should ignore.
    */
   static nsIFrame* GetFirstNonAnonymousFrame(nsIFrame* aFrame);
 
   class RectCallback {
   public:
     virtual void AddRect(const nsRect& aRect) = 0;
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -8576,16 +8576,22 @@ nsDisplayMask::~nsDisplayMask()
 }
 #endif
 
 bool nsDisplayMask::TryMerge(nsDisplayItem* aItem)
 {
   if (aItem->GetType() != TYPE_MASK)
     return false;
 
+  // Do not merge items for box-decoration-break:clone elements,
+  // since each box should have its own mask in that case.
+  if (mFrame->StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone) {
+    return false;
+  }
+
   // items for the same content element should be merged into a single
   // compositing group
   // aItem->GetUnderlyingFrame() returns non-null because it's nsDisplaySVGEffects
   if (aItem->Frame()->GetContent() != mFrame->GetContent()) {
     return false;
   }
   if (aItem->GetClipChain() != GetClipChain()) {
     return false;
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/box-decoration-break-clone-ref.html
@@ -0,0 +1,27 @@
+<html>
+<head>
+  <meta charset="utf-8">
+  <style>
+    span {
+      padding: 0em 1em;
+      margin-left: 10px;
+      font: 24px sans-serif;
+      line-height: 2;
+      clip-path: url(#path);
+    }
+  </style>
+</head>
+<body>
+  <span>The</span><br>
+  <span>quick</span><br>
+  <span>orange fox</span>
+
+  <svg height="0">
+    <defs>
+      <clipPath id="path" clipPathUnits="objectBoundingBox">
+        <rect x="0" y="0" width="1" height="0.5"/>
+      </clipPath>
+    </defs>
+  </svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/box-decoration-break-clone.html
@@ -0,0 +1,34 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test of box-decoration-break:clone with clip-path</title>
+  <style>
+    .clone {
+      box-decoration-break: clone;
+    }
+    span {
+      padding: 0em 1em;
+      margin-left: 10px;
+      font: 24px sans-serif;
+      line-height: 2;
+      clip-path: url(#path);
+    }
+  </style>
+</head>
+<body>
+  <span class="clone">The<br>quick<br>orange fox</span>
+
+  <svg height="0">
+    <defs>
+      <clipPath id="path" clipPathUnits="objectBoundingBox">
+        <rect x="0" y="0" width="1" height="0.5"/>
+      </clipPath>
+    </defs>
+  </svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/box-decoration-break-slice-ref.html
@@ -0,0 +1,14 @@
+<head>
+  <meta charset="utf-8">
+  <style>
+    span {
+      padding: 0em 1em;
+      margin-left: 10px;
+      font: 24px sans-serif;
+      line-height: 2;
+    }
+  </style>
+</head>
+<body>
+  <span>The</span>
+</body>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/box-decoration-break-slice.html
@@ -0,0 +1,31 @@
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test of box-decoration-break:slice with clip-path</title>
+  <style>
+    span {
+      padding: 0em 1em;
+      margin-left: 10px;
+      font: 24px sans-serif;
+      line-height: 2;
+      clip-path: url(#path);
+    }
+  </style>
+</head>
+<body>
+  <span>The<br>quick<br>orange fox</span>
+
+  <svg height="0">
+    <defs>
+      <clipPath id="path" clipPathUnits="objectBoundingBox">
+        <rect x="0" y="0" width="1" height="0.3"/>
+      </clipPath>
+    </defs>
+  </svg>
+</body>
+</html>
--- a/layout/reftests/svg/reftest.list
+++ b/layout/reftests/svg/reftest.list
@@ -51,16 +51,19 @@ pref(layout.css.mix-blend-mode.enabled,t
 #skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-overlay.svg blend-overlay-ref.svg
 #skip-if(Android)  pref(layout.css.mix-blend-mode.enabled,true) == blend-saturation.svg blend-saturation-ref.svg
 #skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-screen.svg blend-screen-ref.svg
 #skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-soft-light.svg blend-soft-light-ref.svg
 skip-if(Android) pref(layout.css.mix-blend-mode.enabled,true) == blend-difference-stacking.html blend-difference-stacking-ref.html
 
 == border-radius-01.html pass.svg
 
+== box-decoration-break-clone.html box-decoration-break-clone-ref.html
+== box-decoration-break-slice.html box-decoration-break-slice-ref.html
+
 == clip-01.svg pass.svg
 == clip-02a.svg clip-02-ref.svg
 == clip-02b.svg clip-02-ref.svg
 == clip-surface-clone-01.svg clip-surface-clone-01-ref.svg
 == clip-use-element-01.svg pass.svg
 == clip-use-element-02.svg pass.svg
 
 == clipPath-advanced-01.svg pass.svg
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1383001-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<html>
+<head>
+<meta charset="UTF-8">
+<script>
+window.onload = () => {
+  a = document.createElement("p")
+  document.documentElement.appendChild(a)
+  a.animate([])
+  document.designMode = 'on'
+  document.documentElement.appendChild(document.createElement("div"))
+}
+</script>
+</head>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1383001.html
@@ -0,0 +1,23 @@
+<!doctype html>
+<html class="reftest-wait">
+<div id="test" contenteditable>
+  <div id="first"></div>
+</div>
+<script>
+document.body.offsetTop;
+let anim = document.createElement('div');
+anim.animate({ color: ['red', 'green' ] }, 1000);
+first.appendChild(anim);
+
+let child = document.createElement('span');
+child.innerText = 'text';
+
+requestAnimationFrame(() => {
+  requestAnimationFrame(() => {
+    anim.appendChild(child);
+    test.appendChild(anim);
+    document.documentElement.className = "";
+  });
+});
+</script>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/style/crashtests/1383319.html
@@ -0,0 +1,18 @@
+<!doctype html>
+<style>
+  @keyframes anim {
+    to { background-color: green; }
+  }
+  .foo {
+    width: 100px;
+    height: 100px;
+    background: red;
+    animation: anim 10s ease;
+  }
+</style>
+<div id="test" contenteditable>
+</div>
+<script>
+document.documentElement.offsetTop;
+test.innerHTML = '<div class="foo">Some random element</div>';
+</script>
--- a/layout/style/crashtests/crashtests.list
+++ b/layout/style/crashtests/crashtests.list
@@ -180,8 +180,11 @@ load 1377053-1.html
 load 1377256-1.html
 load 1378064-1.html
 load 1378814.html
 load 1380800.html
 load link-transition-before.html
 load 1381682.html
 load 1382672.html
 load 1382710.html
+load 1383001.html
+load 1383001-2.html
+load 1383319.html
--- a/layout/svg/nsSVGIntegrationUtils.cpp
+++ b/layout/svg/nsSVGIntegrationUtils.cpp
@@ -132,16 +132,31 @@ GetPreEffectsVisualOverflowUnion(nsIFram
                                               aCurrentFramePreEffectsOverflow,
                                               aInReflow);
   // Compute union of all overflow areas relative to aFirstContinuation:
   nsLayoutUtils::GetAllInFlowBoxes(aFirstContinuation, &collector);
   // Return the result in user space:
   return collector.GetResult() + aFirstContinuationToUserSpace;
 }
 
+static nsRect
+GetPreEffectsVisualOverflow(nsIFrame* aFirstContinuation,
+                            nsIFrame* aCurrentFrame,
+                            const nsPoint& aFirstContinuationToUserSpace)
+{
+  PreEffectsVisualOverflowCollector collector(aFirstContinuation,
+                                              nullptr,
+                                              nsRect(),
+                                              false);
+  // Compute overflow areas of current frame relative to aFirstContinuation:
+  nsLayoutUtils::AddBoxesForFrame(aCurrentFrame, &collector);
+  // Return the result in user space:
+  return collector.GetResult() + aFirstContinuationToUserSpace;
+}
+
 
 bool
 nsSVGIntegrationUtils::UsingEffectsForFrame(const nsIFrame* aFrame)
 {
   // Even when SVG display lists are disabled, returning true for SVG frames
   // does not adversely affect any of our callers. Therefore we don't bother
   // checking the SDL prefs here, since we don't know if we're being called for
   // painting or hit-testing anyway.
@@ -206,19 +221,26 @@ nsSVGIntegrationUtils::GetSVGBBoxForNonS
   NS_ASSERTION(!(aNonSVGFrame->GetStateBits() & NS_FRAME_SVG_LAYOUT),
                "Frames with SVG layout should not get here");
   MOZ_ASSERT(!aNonSVGFrame->IsFrameOfType(nsIFrame::eSVG) ||
              aNonSVGFrame->IsSVGOuterSVGFrame());
 
   nsIFrame* firstFrame =
     nsLayoutUtils::FirstContinuationOrIBSplitSibling(aNonSVGFrame);
   // 'r' is in "user space":
-  nsRect r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
-                                              GetOffsetToBoundingBox(firstFrame),
-                                              false);
+  nsRect r;
+  if (aNonSVGFrame->StyleBorder()->mBoxDecorationBreak == StyleBoxDecorationBreak::Clone) {
+    r = GetPreEffectsVisualOverflow(firstFrame, aNonSVGFrame,
+                                    GetOffsetToBoundingBox(firstFrame));
+  } else {
+    r = GetPreEffectsVisualOverflowUnion(firstFrame, nullptr, nsRect(),
+                                         GetOffsetToBoundingBox(firstFrame),
+                                         false);
+  }
+
   return nsLayoutUtils::RectToGfxRect(r,
            aNonSVGFrame->PresContext()->AppUnitsPerCSSPixel());
 }
 
 // XXX Since we're called during reflow, this method is broken for frames with
 // continuations. When we're called for a frame with continuations, we're
 // called for each continuation in turn as it's reflowed. However, it isn't
 // until the last continuation is reflowed that this method's
--- a/layout/tools/reftest/reftest-content.js
+++ b/layout/tools/reftest/reftest-content.js
@@ -670,19 +670,27 @@ function WaitForTestEnd(contentRootEleme
 
 function OnDocumentLoad(event)
 {
     var currentDoc = content.document;
     if (event.target != currentDoc)
         // Ignore load events for subframes.
         return;
 
-    if (gClearingForAssertionCheck &&
-        currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
-        DoAssertionCheck();
+    if (gClearingForAssertionCheck) {
+        if (currentDoc.location.href == BLANK_URL_FOR_CLEARING) {
+            DoAssertionCheck();
+            return;
+        }
+
+        // It's likely the previous test document reloads itself and causes the
+        // attempt of loading blank page fails. In this case we should retry
+        // loading the blank page.
+        LogInfo("Retry loading a blank page");
+        LoadURI(BLANK_URL_FOR_CLEARING);
         return;
     }
 
     if (currentDoc.location.href != gCurrentURL) {
         LogInfo("OnDocumentLoad fired for previous document");
         // Ignore load events for previous documents.
         return;
     }
--- a/mobile/android/app/src/main/res/layout/bookmark_edit_with_full_page.xml
+++ b/mobile/android/app/src/main/res/layout/bookmark_edit_with_full_page.xml
@@ -15,17 +15,16 @@
         android:id="@+id/toolbar"
         android:layout_width="match_parent"
         android:layout_height="56dp"
         android:background="@color/text_and_tabs_tray_grey"
         android:minHeight="?actionBarSize"
         app:navigationIcon="@drawable/abc_ic_clear_mtrl_alpha"
         app:subtitleTextColor="@android:color/white"
         app:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
-        app:title="@string/bookmark_edit_title"
         app:titleTextColor="@android:color/white" />
 
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="0dp"
         android:layout_weight="1"
         android:orientation="vertical"
         android:paddingEnd="16dp"
--- a/mobile/android/base/java/org/mozilla/gecko/bookmarks/BookmarkEditFragment.java
+++ b/mobile/android/base/java/org/mozilla/gecko/bookmarks/BookmarkEditFragment.java
@@ -227,18 +227,20 @@ public class BookmarkEditFragment extend
     }
 
     private void invalidateView(Bookmark bookmark) {
         this.bookmark = bookmark;
 
         nameText.setText(bookmark.title);
 
         if (bookmark.type == Bookmarks.TYPE_FOLDER) {
+            toolbar.setTitle(R.string.bookmark_edit_folder_title);
             locationLayout.setVisibility(View.GONE);
         } else {
+            toolbar.setTitle(R.string.bookmark_edit_title);
             locationLayout.setVisibility(View.VISIBLE);
         }
         locationText.setText(bookmark.url);
 
         if (Bookmarks.MOBILE_FOLDER_GUID.equals(bookmark.guid)) {
             folderText.setText(R.string.bookmarks_folder_mobile);
         } else {
             folderText.setText(bookmark.folder);
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -543,16 +543,17 @@
 <!ENTITY pref_compact_tabs_summary2 "Arrange tabs in two columns in portrait mode">
 
 <!-- Localization note (page_removed): This string appears in a toast message when
      any page is removed frome about:home. This includes pages that are in history,
      bookmarks, or reading list. -->
 <!ENTITY page_removed "Page removed">
 
 <!ENTITY bookmark_edit_title "Edit Bookmark">
+<!ENTITY bookmark_edit_folder_title "Edit Folder">
 <!ENTITY bookmark_edit_name "Name">
 <!ENTITY bookmark_edit_location "Location">
 <!ENTITY bookmark_edit_keyword "Keyword">
 <!ENTITY bookmark_select_folder "Select folder">
 
 <!-- Localization note (site_settings_*) : These strings are used in the "Site Settings"
      dialog that appears after selecting the "Edit Site Settings" context menu item. -->
 <!ENTITY site_settings_title3       "Site Settings">
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -431,16 +431,17 @@
   <string name="pref_scroll_title_bar_summary">&pref_scroll_title_bar_summary2;</string>
 
   <string name="pref_compact_tabs">&pref_compact_tabs;</string>
   <string name="pref_compact_tabs_summary">&pref_compact_tabs_summary2;</string>
 
   <string name="page_removed">&page_removed;</string>
 
   <string name="bookmark_edit_title">&bookmark_edit_title;</string>
+  <string name="bookmark_edit_folder_title">&bookmark_edit_folder_title;</string>
   <string name="bookmark_edit_name">&bookmark_edit_name;</string>
   <string name="bookmark_edit_location">&bookmark_edit_location;</string>
   <string name="bookmark_edit_keyword">&bookmark_edit_keyword;</string>
   <string name="bookmark_select_folder">&bookmark_select_folder;</string>
 
   <string name="pref_use_master_password">&pref_use_master_password;</string>
   <string name="masterpassword_create_title">&masterpassword_create_title;</string>
   <string name="masterpassword_remove_title">&masterpassword_remove_title;</string>
--- a/mobile/android/chrome/content/about.js
+++ b/mobile/android/chrome/content/about.js
@@ -49,17 +49,19 @@ function init() {
       {id: "faqURL",          pref: "app.faqURL"},
       {id: "privacyURL",      pref: "app.privacyURL"},
       {id: "creditsURL",      pref: "app.creditsURL"},
     ];
 
     links.forEach(function(link) {
       let url = formatter.formatURLPref(link.pref);
       let element = document.getElementById(link.id);
-      element.setAttribute("href", url);
+      if (element) {
+        element.setAttribute("href", url);
+      }
     });
   } catch (ex) {}
 
 #ifdef MOZ_UPDATER
   function expectUpdateResult() {
     EventDispatcher.instance.registerListener(function listener(event, data, callback) {
       EventDispatcher.instance.unregisterListener(listener, event);
       showUpdateMessage(data.result);
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -5343,19 +5343,19 @@ pref("urlclassifier.trackingTable", "tes
 pref("urlclassifier.trackingWhitelistTable", "test-trackwhite-simple,mozstd-trackwhite-digest256");
 
 // The number of random entries to send with a gethash request.
 pref("urlclassifier.gethashnoise", 4);
 
 // Gethash timeout for Safebrowsing.
 pref("urlclassifier.gethash.timeout_ms", 5000);
 // Update server response timeout for Safebrowsing.
-pref("urlclassifier.update.response_timeout_ms", 15000);
+pref("urlclassifier.update.response_timeout_ms", 30000);
 // Download update timeout for Safebrowsing.
-pref("urlclassifier.update.timeout_ms", 60000);
+pref("urlclassifier.update.timeout_ms", 90000);
 
 // Name of the about: page contributed by safebrowsing to handle display of error
 // pages on phishing/malware hits.  (bug 399233)
 pref("urlclassifier.alternate_error_page", "blocked");
 
 // Enable phishing protection
 pref("browser.safebrowsing.phishing.enabled", true);
 
--- a/servo/components/net_traits/lib.rs
+++ b/servo/components/net_traits/lib.rs
@@ -223,20 +223,18 @@ impl FetchTaskTarget for IpcSender<Fetch
         let _ = self.send(FetchResponseMsg::ProcessResponse(response.metadata()));
     }
 
     fn process_response_chunk(&mut self, chunk: Vec<u8>) {
         let _ = self.send(FetchResponseMsg::ProcessResponseChunk(chunk));
     }
 
     fn process_response_eof(&mut self, response: &Response) {
-        if response.is_network_error() {
-            // todo: finer grained errors
-            let _ =
-                self.send(FetchResponseMsg::ProcessResponseEOF(Err(NetworkError::Internal("Network error".into()))));
+        if let Some(e) = response.get_network_error() {
+            let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Err(e.clone())));
         } else {
             let _ = self.send(FetchResponseMsg::ProcessResponseEOF(Ok(())));
         }
     }
 }
 
 
 pub trait Action<Listener> {
--- a/servo/components/style/data.rs
+++ b/servo/components/style/data.rs
@@ -293,83 +293,80 @@ impl ElementData {
         }
     }
 
     /// Returns true if this element has styles.
     pub fn has_styles(&self) -> bool {
         self.styles.primary.is_some()
     }
 
-    /// Returns whether we have any outstanding style invalidation.
-    pub fn has_invalidations(&self) -> bool {
-        self.restyle.hint.has_self_invalidations()
-    }
-
     /// Returns the kind of restyling that we're going to need to do on this
     /// element, based of the stored restyle hint.
-    pub fn restyle_kind(&self,
-                        shared_context: &SharedStyleContext)
-                        -> RestyleKind {
+    pub fn restyle_kind(
+        &self,
+        shared_context: &SharedStyleContext
+    ) -> RestyleKind {
         if shared_context.traversal_flags.for_animation_only() {
             return self.restyle_kind_for_animation(shared_context);
         }
 
-        debug_assert!(!self.has_styles() || self.has_invalidations(),
-                      "Should've stopped earlier");
         if !self.has_styles() {
             return RestyleKind::MatchAndCascade;
         }
 
         let hint = self.restyle.hint;
         if hint.match_self() {
             return RestyleKind::MatchAndCascade;
         }
 
         if hint.has_replacements() {
             debug_assert!(!hint.has_animation_hint(),
                           "Animation only restyle hint should have already processed");
             return RestyleKind::CascadeWithReplacements(hint & RestyleHint::replacements());
         }
 
         debug_assert!(hint.has_recascade_self(),
-                      "We definitely need to do something!");
+                      "We definitely need to do something: {:?}!", hint);
         return RestyleKind::CascadeOnly;
     }
 
     /// Returns the kind of restyling for animation-only restyle.
-    pub fn restyle_kind_for_animation(&self,
-                                      shared_context: &SharedStyleContext)
-                                      -> RestyleKind {
+    fn restyle_kind_for_animation(
+        &self,
+        shared_context: &SharedStyleContext,
+    ) -> RestyleKind {
         debug_assert!(shared_context.traversal_flags.for_animation_only());
         debug_assert!(self.has_styles(),
                       "Unstyled element shouldn't be traversed during \
                        animation-only traversal");
 
         // return either CascadeWithReplacements or CascadeOnly in case of
         // animation-only restyle. I.e. animation-only restyle never does
         // selector matching.
         let hint = self.restyle.hint;
         if hint.has_animation_hint() {
             return RestyleKind::CascadeWithReplacements(hint & RestyleHint::for_animations());
         }
+
         return RestyleKind::CascadeOnly;
-
     }
 
     /// Return true if important rules are different.
     /// We use this to make sure the cascade of off-main thread animations is correct.
     /// Note: Ignore custom properties for now because we only support opacity and transform
     ///       properties for animations running on compositor. Actually, we only care about opacity
     ///       and transform for now, but it's fine to compare all properties and let the user
     ///       the check which properties do they want.
     ///       If it costs too much, get_properties_overriding_animations() should return a set
     ///       containing only opacity and transform properties.
-    pub fn important_rules_are_different(&self,
-                                         rules: &StrongRuleNode,
-                                         guards: &StylesheetGuards) -> bool {
+    pub fn important_rules_are_different(
+        &self,
+        rules: &StrongRuleNode,
+        guards: &StylesheetGuards
+    ) -> bool {
         debug_assert!(self.has_styles());
         let (important_rules, _custom) =
             self.styles.primary().rules().get_properties_overriding_animations(&guards);
         let (other_important_rules, _custom) = rules.get_properties_overriding_animations(&guards);
         important_rules != other_important_rules
     }
 
     /// Drops any restyle state from the element.
--- a/servo/components/style/dom.rs
+++ b/servo/components/style/dom.rs
@@ -465,46 +465,39 @@ pub trait TElement : Eq + PartialEq + De
     fn has_snapshot(&self) -> bool;
 
     /// Returns whether the current snapshot if present has been handled.
     fn handled_snapshot(&self) -> bool;
 
     /// Flags this element as having handled already its snapshot.
     unsafe fn set_handled_snapshot(&self);
 
-    /// Returns whether the element's styles are up-to-date.
-    fn has_current_styles(&self, data: &ElementData) -> bool {
-        if self.has_snapshot() && !self.handled_snapshot() {
-            return false;
-        }
-
-        data.has_styles() && !data.has_invalidations()
-    }
-
     /// Returns whether the element's styles are up-to-date for |traversal_flags|.
-    fn has_current_styles_for_traversal(&self,
-                                        data: &ElementData,
-                                        traversal_flags: TraversalFlags) -> bool {
+    fn has_current_styles_for_traversal(
+        &self,
+        data: &ElementData,
+        traversal_flags: TraversalFlags,
+    ) -> bool {
         if traversal_flags.for_animation_only() {
             // In animation-only restyle we never touch snapshots and don't
             // care about them. But we can't assert '!self.handled_snapshot()'
             // here since there are some cases that a second animation-only
             // restyle which is a result of normal restyle (e.g. setting
             // animation-name in normal restyle and creating a new CSS
             // animation in a SequentialTask) is processed after the normal
             // traversal in that we had elements that handled snapshot.
             return data.has_styles() &&
                    !data.restyle.hint.has_animation_hint_or_recascade();
         }
 
         if self.has_snapshot() && !self.handled_snapshot() {
             return false;
         }
 
-        data.has_styles() && !data.has_invalidations()
+        data.has_styles() && !data.restyle.hint.has_non_animation_invalidations()
     }
 
     /// Flags an element and its ancestors with a given `DescendantsBit`.
     ///
     /// TODO(emilio): We call this conservatively from restyle_element_internal
     /// because we never flag unstyled stuff. A different setup for this may be
     /// a bit cleaner, but it's probably not worth to invest on it right now
     /// unless necessary.
--- a/servo/components/style/invalidation/element/restyle_hints.rs
+++ b/servo/components/style/invalidation/element/restyle_hints.rs
@@ -60,18 +60,22 @@ impl RestyleHint {
 
     /// Returns whether this hint invalidates the element and all its
     /// descendants.
     pub fn contains_subtree(&self) -> bool {
         self.contains(RESTYLE_SELF | RESTYLE_DESCENDANTS)
     }
 
     /// Returns whether we need to restyle this element.
-    pub fn has_self_invalidations(&self) -> bool {
-        self.intersects(RESTYLE_SELF | RECASCADE_SELF | Self::replacements())
+    pub fn has_non_animation_invalidations(&self) -> bool {
+        self.intersects(
+            RESTYLE_SELF |
+            RECASCADE_SELF |
+            (Self::replacements() & !Self::for_animations())
+        )
     }
 
     /// Propagates this restyle hint to a child element.
     pub fn propagate(&mut self, traversal_flags: &TraversalFlags) -> Self {
         use std::mem;
 
         // In the middle of an animation only restyle, we don't need to
         // propagate any restyle hints, and we need to remove ourselves.
--- a/servo/components/style/properties/data.py
+++ b/servo/components/style/properties/data.py
@@ -41,16 +41,21 @@ def to_camel_case(ident):
     return re.sub("(^|_|-)([a-z])", lambda m: m.group(2).upper(), ident.strip("_").strip("-"))
 
 
 def to_camel_case_lower(ident):
     camel = to_camel_case(ident)
     return camel[0].lower() + camel[1:]
 
 
+# https://drafts.csswg.org/cssom/#css-property-to-idl-attribute
+def to_idl_name(ident):
+    return re.sub("-([a-z])", lambda m: m.group(1).upper(), ident)
+
+
 def parse_aliases(value):
     aliases = {}
     for pair in value.split():
         [a, v] = pair.split("=")
         aliases[a] = v
     return aliases
 
 
--- a/servo/components/style/properties/helpers/animated_properties.mako.rs
+++ b/servo/components/style/properties/helpers/animated_properties.mako.rs
@@ -1,15 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 <%namespace name="helpers" file="/helpers.mako.rs" />
 
-<% from data import SYSTEM_FONT_LONGHANDS %>
+<% from data import to_idl_name, SYSTEM_FONT_LONGHANDS %>
 
 use app_units::Au;
 use cssparser::{Parser, RGBA};
 use euclid::{Point2D, Size2D};
 #[cfg(feature = "gecko")] use gecko_bindings::bindings::RawServoAnimationValueMap;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::RawGeckoGfxMatrix4x4;
 #[cfg(feature = "gecko")] use gecko_bindings::structs::nsCSSPropertyID;
 #[cfg(feature = "gecko")] use gecko_bindings::sugar::ownership::{HasFFI, HasSimpleFFI};
@@ -18,17 +18,18 @@ use properties::{CSSWideKeyword, Propert
 use properties::longhands;
 use properties::longhands::font_weight::computed_value::T as FontWeight;
 use properties::longhands::font_stretch::computed_value::T as FontStretch;
 use properties::longhands::transform::computed_value::ComputedMatrix;
 use properties::longhands::transform::computed_value::ComputedOperation as TransformOperation;
 use properties::longhands::transform::computed_value::T as TransformList;
 use properties::longhands::vertical_align::computed_value::T as VerticalAlign;
 use properties::longhands::visibility::computed_value::T as Visibility;
-#[cfg(feature = "gecko")] use properties::{PropertyDeclarationId, LonghandId};
+#[cfg(feature = "gecko")] use properties::{PropertyId, PropertyDeclarationId, LonghandId};
+#[cfg(feature = "gecko")] use properties::{ShorthandId};
 use selectors::parser::SelectorParseError;
 use smallvec::SmallVec;
 use std::cmp;
 #[cfg(feature = "gecko")] use fnv::FnvHashMap;
 use style_traits::ParseError;
 use super::ComputedValues;
 #[cfg(any(feature = "gecko", feature = "testing"))]
 use values::Auto;
@@ -3199,8 +3200,61 @@ impl Animatable for AnimatedFilterList {
                 from = from_iter.next();
                 to = to_iter.next();
             }
             square_distance += current_square_distance;
         }
         Ok(square_distance.sqrt())
     }
 }
+
+/// A comparator to sort PropertyIds such that longhands are sorted before shorthands,
+/// shorthands with fewer components are sorted before shorthands with more components,
+/// and otherwise shorthands are sorted by IDL name as defined by [Web Animations][property-order].
+///
+/// Using this allows us to prioritize values specified by longhands (or smaller
+/// shorthand subsets) when longhands and shorthands are both specified on the one keyframe.
+///
+/// Example orderings that result from this:
+///
+///   margin-left, margin
+///
+/// and:
+///
+///   border-top-color, border-color, border-top, border
+///
+/// [property-order] https://w3c.github.io/web-animations/#calculating-computed-keyframes
+#[cfg(feature = "gecko")]
+pub fn compare_property_priority(a: &PropertyId, b: &PropertyId) -> cmp::Ordering {
+    match (a.as_shorthand(), b.as_shorthand()) {
+        // Within shorthands, sort by the number of subproperties, then by IDL name.
+        (Ok(a), Ok(b)) => {
+            let subprop_count_a = a.longhands().len();
+            let subprop_count_b = b.longhands().len();
+            subprop_count_a.cmp(&subprop_count_b).then_with(
+                || get_idl_name_sort_order(&a).cmp(&get_idl_name_sort_order(&b)))
+        },
+
+        // Longhands go before shorthands.
+        (Ok(_), Err(_)) => cmp::Ordering::Greater,
+        (Err(_), Ok(_)) => cmp::Ordering::Less,
+
+        // Both are longhands or custom properties in which case they don't overlap and should
+        // sort equally.
+        _ => cmp::Ordering::Equal,
+    }
+}
+
+#[cfg(feature = "gecko")]
+fn get_idl_name_sort_order(shorthand: &ShorthandId) -> u32 {
+<%
+# Sort by IDL name.
+sorted_shorthands = sorted(data.shorthands, key=lambda p: to_idl_name(p.ident))
+
+# Annotate with sorted position
+sorted_shorthands = [(p, position) for position, p in enumerate(sorted_shorthands)]
+%>
+    match *shorthand {
+        % for property, position in sorted_shorthands:
+            ShorthandId::${property.camel_case} => ${position},
+        % endfor
+    }
+}
--- a/servo/components/style/style_resolver.rs
+++ b/servo/components/style/style_resolver.rs
@@ -47,27 +47,17 @@ pub struct PrimaryStyle {
 
 fn with_default_parent_styles<E, F, R>(element: E, f: F) -> R
 where
     E: TElement,
     F: FnOnce(Option<&ComputedValues>, Option<&ComputedValues>) -> R,
 {
     let parent_el = element.inheritance_parent();
     let parent_data = parent_el.as_ref().and_then(|e| e.borrow_data());
-    let parent_style = parent_data.as_ref().map(|d| {
-        // Sometimes Gecko eagerly styles things without processing
-        // pending restyles first. In general we'd like to avoid this,
-        // but there can be good reasons (for example, needing to
-        // construct a frame for some small piece of newly-added
-        // content in order to do something specific with that frame,
-        // but not wanting to flush all of layout).
-        debug_assert!(cfg!(feature = "gecko") ||
-                      parent_el.unwrap().has_current_styles(d));
-        d.styles.primary()
-    });
+    let parent_style = parent_data.as_ref().map(|d| d.styles.primary());
 
     let mut layout_parent_el = parent_el.clone();
     let layout_parent_data;
     let mut layout_parent_style = parent_style;
     if parent_style.map_or(false, |s| s.is_display_contents()) {
         layout_parent_el = Some(layout_parent_el.unwrap().layout_parent());
         layout_parent_data = layout_parent_el.as_ref().unwrap().borrow_data().unwrap();
         layout_parent_style = Some(layout_parent_data.styles.primary());
--- a/servo/components/style/traversal.rs
+++ b/servo/components/style/traversal.rs
@@ -369,19 +369,18 @@ pub trait DomTraversal<E: TElement> : Sy
 
     /// Returns true if we want to cull this subtree from the travesal.
     fn should_cull_subtree(
         &self,
         context: &mut StyleContext<E>,
         parent: E,
         parent_data: &ElementData,
     ) -> bool {
-        // See the comment on `cascade_node` for why we allow this on Gecko.
         debug_assert!(cfg!(feature = "gecko") ||
-                      parent.has_current_styles(parent_data));
+                      parent.has_current_styles_for_traversal(parent_data, context.shared.traversal_flags));
 
         // If the parent computed display:none, we don't style the subtree.
         if parent_data.styles.is_display_none() {
             debug!("Parent {:?} is display:none, culling traversal", parent);
             return true;
         }
 
         // Gecko-only XBL handling.
--- a/servo/ports/geckolib/glue.rs
+++ b/servo/ports/geckolib/glue.rs
@@ -78,30 +78,32 @@ use style::gecko_bindings::structs::RawG
 use style::gecko_bindings::structs::RawGeckoPresContextOwned;
 use style::gecko_bindings::structs::ServoElementSnapshotTable;
 use style::gecko_bindings::structs::StyleRuleInclusion;
 use style::gecko_bindings::structs::URLExtraData;
 use style::gecko_bindings::structs::nsCSSValueSharedList;
 use style::gecko_bindings::structs::nsCompatibility;
 use style::gecko_bindings::structs::nsIDocument;
 use style::gecko_bindings::structs::nsStyleTransformMatrix::MatrixTransformOperator;
+use style::gecko_bindings::structs::nsTArray;
 use style::gecko_bindings::structs::nsresult;
 use style::gecko_bindings::sugar::ownership::{FFIArcHelpers, HasFFI, HasArcFFI, HasBoxFFI};
 use style::gecko_bindings::sugar::ownership::{HasSimpleFFI, Strong};
 use style::gecko_bindings::sugar::refptr::RefPtr;
 use style::gecko_properties::{self, style_structs};
 use style::invalidation::element::restyle_hints::{self, RestyleHint};
 use style::media_queries::{MediaList, parse_media_query_list};
 use style::parallel;
 use style::parser::ParserContext;
 use style::properties::{ComputedValues, Importance};
 use style::properties::{IS_FIELDSET_CONTENT, LonghandIdSet};
-use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId};
+use style::properties::{PropertyDeclaration, PropertyDeclarationBlock, PropertyId, ShorthandId};
 use style::properties::{SKIP_ROOT_AND_ITEM_BASED_DISPLAY_FIXUP, SourcePropertyDeclaration, StyleBuilder};
 use style::properties::animated_properties::{Animatable, AnimatableLonghand, AnimationValue};
+use style::properties::animated_properties::compare_property_priority;
 use style::properties::parse_one_declaration_into;
 use style::rule_tree::StyleSource;
 use style::selector_parser::PseudoElementCascadeType;
 use style::sequential;
 use style::shared_lock::{SharedRwLockReadGuard, StylesheetGuards, ToCssWithGuard, Locked};
 use style::string_cache::Atom;
 use style::style_adjuster::StyleAdjuster;
 use style::stylesheets::{CssRule, CssRules, CssRuleType, CssRulesHelpers, DocumentRule};
@@ -268,31 +270,36 @@ pub extern "C" fn Servo_TraverseSubtree(
     debug!("Servo_TraverseSubtree: {:?} {:?}", element, restyle_behavior);
 
     let traversal_flags = match (root_behavior, restyle_behavior) {
         (Root::Normal, Restyle::Normal) |
         (Root::Normal, Restyle::ForNewlyBoundElement) |
         (Root::Normal, Restyle::ForThrottledAnimationFlush)
             => TraversalFlags::empty(),
         (Root::UnstyledChildrenOnly, Restyle::Normal) |
-        (Root::UnstyledChildrenOnly, Restyle::ForNewlyBoundElement) |
-        (Root::UnstyledChildrenOnly, Restyle::ForThrottledAnimationFlush)
+        (Root::UnstyledChildrenOnly, Restyle::ForNewlyBoundElement)
             => UNSTYLED_CHILDREN_ONLY,
         (Root::Normal, Restyle::ForCSSRuleChanges) => FOR_CSS_RULE_CHANGES,
         (Root::Normal, Restyle::ForReconstruct) => FOR_RECONSTRUCT,
         _ => panic!("invalid combination of TraversalRootBehavior and TraversalRestyleBehavior"),
     };
 
-    let needs_animation_only_restyle = element.has_animation_only_dirty_descendants() ||
-                                       element.has_animation_restyle_hints();
-    if needs_animation_only_restyle {
-        traverse_subtree(element,
-                         raw_data,
-                         traversal_flags | ANIMATION_ONLY,
-                         unsafe { &*snapshots });
+    // It makes no sense to do an animation restyle when we're restyling
+    // newly-inserted content.
+    if !traversal_flags.contains(UNSTYLED_CHILDREN_ONLY) {
+        let needs_animation_only_restyle =
+            element.has_animation_only_dirty_descendants() ||
+            element.has_animation_restyle_hints();
+
+        if needs_animation_only_restyle {
+            traverse_subtree(element,
+                             raw_data,
+                             traversal_flags | ANIMATION_ONLY,
+                             unsafe { &*snapshots });
+        }
     }
 
     if restyle_behavior == Restyle::ForThrottledAnimationFlush {
         return element.has_animation_only_dirty_descendants() ||
                element.borrow_data().unwrap().restyle.is_restyle();
     }
 
     traverse_subtree(element,
@@ -2807,17 +2814,17 @@ pub extern "C" fn Servo_ResolveStyle(ele
     // In the case where we process for throttled animation, there remaings
     // restyle hints other than animation hints.
     let flags = if restyle_behavior == Restyle::ForThrottledAnimationFlush {
         ANIMATION_ONLY
     } else {
         TraversalFlags::empty()
     };
     debug_assert!(element.has_current_styles_for_traversal(&*data, flags),
-                  "Resolving style on element without current styles");
+                  "Resolving style on {:?} without current styles: {:?}", element, data);
     data.styles.primary().clone().into()
 }
 
 #[no_mangle]
 pub extern "C" fn Servo_ResolveStyleLazily(element: RawGeckoElementBorrowed,
                                            pseudo_type: CSSPseudoElementType,
                                            rule_inclusion: StyleRuleInclusion,
                                            snapshots: *const ServoElementSnapshotTable,
@@ -2911,16 +2918,63 @@ fn create_context<'a>(
         ),
         font_metrics_provider: font_metrics_provider,
         cached_system_font: None,
         in_media_query: false,
         quirks_mode: per_doc_data.stylist.quirks_mode(),
     }
 }
 
+struct PropertyAndIndex {
+    property: PropertyId,
+    index: usize,
+}
+
+struct PrioritizedPropertyIter<'a> {
+    properties: &'a nsTArray<PropertyValuePair>,
+    sorted_property_indices: Vec<PropertyAndIndex>,
+    curr: usize,
+}
+
+impl<'a> PrioritizedPropertyIter<'a> {
+    pub fn new(properties: &'a nsTArray<PropertyValuePair>) -> PrioritizedPropertyIter {
+        // If we fail to convert a nsCSSPropertyID into a PropertyId we shouldn't fail outright
+        // but instead by treating that property as the 'all' property we make it sort last.
+        let all = PropertyId::Shorthand(ShorthandId::All);
+
+        let mut sorted_property_indices: Vec<PropertyAndIndex> =
+            properties.iter().enumerate().map(|(index, pair)| {
+                PropertyAndIndex {
+                    property: PropertyId::from_nscsspropertyid(pair.mProperty)
+                              .unwrap_or(all.clone()),
+                    index,
+                }
+            }).collect();
+        sorted_property_indices.sort_by(|a, b| compare_property_priority(&a.property, &b.property));
+
+        PrioritizedPropertyIter {
+            properties,
+            sorted_property_indices,
+            curr: 0,
+        }
+    }
+}
+
+impl<'a> Iterator for PrioritizedPropertyIter<'a> {
+    type Item = &'a PropertyValuePair;
+
+    fn next(&mut self) -> Option<&'a PropertyValuePair> {
+        if self.curr >= self.sorted_property_indices.len() {
+            return None
+        }
+        self.curr += 1;
+        Some(&self.properties[self.sorted_property_indices[self.curr - 1].index])
+    }
+}
+
 #[no_mangle]
 pub extern "C" fn Servo_GetComputedKeyframeValues(keyframes: RawGeckoKeyframeListBorrowed,
                                                   element: RawGeckoElementBorrowed,
                                                   style: ServoStyleContextBorrowed,
                                                   raw_data: RawServoStyleSetBorrowed,
                                                   computed_keyframes: RawGeckoComputedKeyframeValuesListBorrowedMut)
 {
     use std::mem;
@@ -2941,19 +2995,18 @@ pub extern "C" fn Servo_GetComputedKeyfr
     let guard = global_style_data.shared_lock.read();
     let default_values = data.default_computed_values();
 
     for (index, keyframe) in keyframes.iter().enumerate() {
         let ref mut animation_values = computed_keyframes[index];
 
         let mut seen = LonghandIdSet::new();
 
-        let iter = keyframe.mPropertyValues.iter();
         let mut property_index = 0;
-        for property in iter {
+        for property in PrioritizedPropertyIter::new(&keyframe.mPropertyValues) {
             if simulate_compute_values_failure(property) {
                 continue;
             }
 
             let mut maybe_append_animation_value = |property: AnimatableLonghand,
                                                     value: Option<AnimationValue>| {
                 if seen.has_animatable_longhand_bit(&property) {
                     return;
--- a/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
+++ b/testing/firefox-ui/tests/functional/security/test_safe_browsing_initial_download.py
@@ -93,17 +93,17 @@ class TestSafeBrowsingInitialDownload(Pu
             super(TestSafeBrowsingInitialDownload, self).tearDown()
 
     def test_safe_browsing_initial_download(self):
         def check_downloaded(_):
             return reduce(lambda state, pref: state and int(self.marionette.get_pref(pref)) != 1,
                           self.prefs_provider_update_time.keys(), True)
 
         try:
-            Wait(self.marionette, timeout=90).until(
+            Wait(self.marionette, timeout=170).until(
                 check_downloaded, message='Not all safebrowsing files have been downloaded')
         finally:
             files_on_disk_toplevel = os.listdir(self.safebrowsing_path)
             for f in self.safebrowsing_v2_files:
                 self.assertIn(f, files_on_disk_toplevel)
 
             if len(self.safebrowsing_v4_files) > 0:
                 files_on_disk_google4 = os.listdir(os.path.join(self.safebrowsing_path, 'google4'))
--- a/testing/web-platform/meta/cssom-view/elementFromPoint.html.ini
+++ b/testing/web-platform/meta/cssom-view/elementFromPoint.html.ini
@@ -1,11 +1,10 @@
 [elementFromPoint.html]
   type: testharness
   [Image Maps]
     expected: FAIL
 
   [Fieldsets]
+    disabled:
+      if (os == "win") and (version == "10.0.15063"): https://bugzilla.mozilla.org/show_bug.cgi?id=1383056
     expected:
-      if not debug and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
-      if not debug and not e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
-      if debug and not stylo and e10s and (os == "win") and (version == "10.0.15063") and (processor == "x86_64") and (bits == 64): FAIL
-
+      if (os == "win") and (version == "10.0.15063"): FAIL
--- a/toolkit/components/asyncshutdown/AsyncShutdown.jsm
+++ b/toolkit/components/asyncshutdown/AsyncShutdown.jsm
@@ -998,17 +998,16 @@ Barrier.prototype = Object.freeze({
 // Ideally, phases should be registered from the component that decides
 // when they start/stop. For compatibility with existing startup/shutdown
 // mechanisms, we register a few phases here.
 
 // Parent process
 if (!isContent) {
   this.AsyncShutdown.profileChangeTeardown = getPhase("profile-change-teardown");
   this.AsyncShutdown.profileBeforeChange = getPhase("profile-before-change");
-  this.AsyncShutdown.placesClosingInternalConnection = getPhase("places-will-close-connection");
   this.AsyncShutdown.sendTelemetry = getPhase("profile-before-change-telemetry");
 }
 
 // Notifications that fire in the parent and content process, but should
 // only have phases in the parent process.
 if (!isContent) {
   this.AsyncShutdown.quitApplicationGranted = getPhase("quit-application-granted");
 }
--- a/toolkit/components/places/Database.cpp
+++ b/toolkit/components/places/Database.cpp
@@ -451,18 +451,27 @@ Database::GetStatement(const nsACString&
   // already.
   MOZ_ASSERT(mMainConn);
   return mAsyncThreadStatements.GetCachedStatement(aQuery);
 }
 
 already_AddRefed<nsIAsyncShutdownClient>
 Database::GetClientsShutdown()
 {
-  MOZ_ASSERT(mClientsShutdown);
-  return mClientsShutdown->GetClient();
+  if (mClientsShutdown)
+    return mClientsShutdown->GetClient();
+  return nullptr;
+}
+
+already_AddRefed<nsIAsyncShutdownClient>
+Database::GetConnectionShutdown()
+{
+  if (mConnectionShutdown)
+    return mConnectionShutdown->GetClient();
+  return nullptr;
 }
 
 // static
 already_AddRefed<Database>
 Database::GetDatabase()
 {
   if (PlacesShutdownBlocker::IsStarted()) {
     return nullptr;
--- a/toolkit/components/places/Database.h
+++ b/toolkit/components/places/Database.h
@@ -27,20 +27,16 @@
 // initial shutdown work and notifies TOPIC_PLACES_SHUTDOWN to all listeners.
 // Any shutdown work that requires the Places APIs should happen here.
 #define TOPIC_PROFILE_CHANGE_TEARDOWN "profile-change-teardown"
 // Fired when Places is shutting down.  Any code should stop accessing Places
 // APIs after this notification.  If you need to listen for Places shutdown
 // you should only use this notification, next ones are intended only for
 // internal Places use.
 #define TOPIC_PLACES_SHUTDOWN "places-shutdown"
-// For Internal use only.  Fired when connection is about to be closed, only
-// cleanup tasks should run at this stage, nothing should be added to the
-// database, nor APIs should be called.
-#define TOPIC_PLACES_WILL_CLOSE_CONNECTION "places-will-close-connection"
 // Fired when the connection has gone, nothing will work from now on.
 #define TOPIC_PLACES_CONNECTION_CLOSED "places-connection-closed"
 
 // Simulate profile-before-change. This topic may only be used by
 // calling `observe` directly on the database. Used for testing only.
 #define TOPIC_SIMULATE_PLACES_SHUTDOWN "test-simulate-places-shutdown"
 
 class nsIRunnable;
@@ -82,16 +78,21 @@ public:
   nsresult Init();
 
   /**
    * The AsyncShutdown client used by clients of this API to be informed of shutdown.
    */
   already_AddRefed<nsIAsyncShutdownClient> GetClientsShutdown();
 
   /**
+   * The AsyncShutdown client used by clients of this API to be informed of connection shutdown.
+   */
+  already_AddRefed<nsIAsyncShutdownClient> GetConnectionShutdown();
+
+  /**
    * Getter to use when instantiating the class.
    *
    * @return Singleton instance of this class.
    */
   static already_AddRefed<Database> GetDatabase();
 
   /**
    * Actually initialized the connection on first need.
--- a/toolkit/components/places/History.jsm
+++ b/toolkit/components/places/History.jsm
@@ -745,38 +745,66 @@ var invalidateFrecencies = async functio
      SET hidden = 0
      WHERE id in (${ ids })
      AND frecency <> 0`
   );
 };
 
 // Inner implementation of History.clear().
 var clear = async function(db) {
-  // Remove all history.
-  await db.execute("DELETE FROM moz_historyvisits");
+  await db.executeTransaction(async function() {
+    // Remove all non-bookmarked places entries first, this will speed up the
+    // triggers work.
+    await db.execute(`DELETE FROM moz_places WHERE foreign_count = 0`);
+    await db.execute(`DELETE FROM moz_updatehosts_temp`);
+
+    // Expire orphan icons.
+    await db.executeCached(`DELETE FROM moz_pages_w_icons
+                            WHERE page_url_hash NOT IN (SELECT url_hash FROM moz_places)`);
+    await db.executeCached(`DELETE FROM moz_icons
+                            WHERE root = 0 AND id NOT IN (SELECT icon_id FROM moz_icons_to_pages)`);
+
+    // Expire annotations.
+    await db.execute(`DELETE FROM moz_items_annos WHERE expiration = :expire_session`,
+                     { expire_session: Ci.nsIAnnotationService.EXPIRE_SESSION });
+    await db.execute(`DELETE FROM moz_annos WHERE id in (
+                        SELECT a.id FROM moz_annos a
+                        LEFT JOIN moz_places h ON a.place_id = h.id
+                        WHERE h.id IS NULL
+                           OR expiration = :expire_session
+                           OR (expiration = :expire_with_history
+                               AND h.last_visit_date ISNULL)
+                      )`, { expire_session: Ci.nsIAnnotationService.EXPIRE_SESSION,
+                            expire_with_history: Ci.nsIAnnotationService.EXPIRE_WITH_HISTORY });
+
+    // Expire inputhistory.
+    await db.execute(`DELETE FROM moz_inputhistory WHERE place_id IN (
+                        SELECT i.place_id FROM moz_inputhistory i
+                        LEFT JOIN moz_places h ON h.id = i.place_id
+                        WHERE h.id IS NULL)`);
+
+    // Remove all history.
+    await db.execute("DELETE FROM moz_historyvisits");
+
+    // Invalidate frecencies for the remaining places.
+    await db.execute(`UPDATE moz_places SET frecency =
+                        (CASE
+                          WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
+                                                hash("place", "prefix_hi")
+                          THEN 0
+                          ELSE -1
+                          END)
+                        WHERE frecency > 0`);
+  });
 
   // Clear the registered embed visits.
   PlacesUtils.history.clearEmbedVisits();
 
-  // Expiration will take care of orphans.
   let observers = PlacesUtils.history.getObservers();
   notify(observers, "onClearHistory");
-
-  // Invalidate frecencies for the remaining places. This must happen
-  // after the notification to ensure it runs enqueued to expiration.
-  await db.execute(
-    `UPDATE moz_places SET frecency =
-     (CASE
-      WHEN url_hash BETWEEN hash("place", "prefix_lo") AND
-                            hash("place", "prefix_hi")
-      THEN 0
-      ELSE -1
-      END)
-     WHERE frecency > 0`);
-
   // Notify frecency change observers.
   notify(observers, "onManyFrecenciesChanged");
 };
 
 /**
  * Clean up pages whose history has been modified, by either
  * removing them entirely (if they are marked for removal,
  * typically because all visits have been removed and there
--- a/toolkit/components/places/PlacesUtils.jsm
+++ b/toolkit/components/places/PlacesUtils.jsm
@@ -2137,17 +2137,18 @@ XPCOMUtils.defineLazyGetter(this, "bundl
 function setupDbForShutdown(conn, name) {
   try {
     let state = "0. Not started.";
     let promiseClosed = new Promise((resolve, reject) => {
       // The service initiates shutdown.
       // Before it can safely close its connection, we need to make sure
       // that we have closed the high-level connection.
       try {
-        AsyncShutdown.placesClosingInternalConnection.addBlocker(`${name} closing as part of Places shutdown`,
+        PlacesUtils.history.connectionShutdownClient.jsclient.addBlocker(
+          `${name} closing as part of Places shutdown`,
           async function() {
             state = "1. Service has initiated shutdown";
 
             // At this stage, all external clients have finished using the
             // database. We just need to close the high-level connection.
             await conn.close();
             state = "2. Closed Sqlite.jsm connection.";
 
--- a/toolkit/components/places/Shutdown.cpp
+++ b/toolkit/components/places/Shutdown.cpp
@@ -17,16 +17,29 @@ PlacesShutdownBlocker::PlacesShutdownBlo
   , mCounter(sCounter++)
 {
   MOZ_ASSERT(NS_IsMainThread());
   // During tests, we can end up with the Database singleton being resurrected.
   // Make sure that each instance of DatabaseShutdown has a unique name.
   if (mCounter > 1) {
     mName.AppendInt(mCounter);
   }
+  // Create a barrier that will be exposed to clients through GetClient(), so
+  // they can block Places shutdown.
+  nsCOMPtr<nsIAsyncShutdownService> asyncShutdown = services::GetAsyncShutdown();
+  MOZ_ASSERT(asyncShutdown);
+  if (asyncShutdown) {
+    nsCOMPtr<nsIAsyncShutdownBarrier> barrier;
+    nsresult rv = asyncShutdown->MakeBarrier(mName, getter_AddRefs(barrier));
+    MOZ_ALWAYS_SUCCEEDS(rv);
+    if (NS_SUCCEEDED(rv) && barrier) {
+      mBarrier = new nsMainThreadPtrHolder<nsIAsyncShutdownBarrier>(
+        "PlacesShutdownBlocker::mBarrier", barrier);
+    }
+  }
 }
 
 // nsIAsyncShutdownBlocker
 NS_IMETHODIMP
 PlacesShutdownBlocker::GetName(nsAString& aName)
 {
   aName = mName;
   return NS_OK;
@@ -66,60 +79,29 @@ PlacesShutdownBlocker::GetState(nsIPrope
   if (NS_WARN_IF(NS_FAILED(rv))) return rv;
   rv = static_cast<nsIWritablePropertyBag2*>(*_state)->SetPropertyAsInterface(
     NS_LITERAL_STRING("Barrier"), barrier);
   if (NS_WARN_IF(NS_FAILED(rv))) return rv;
 
   return NS_OK;
 }
 
-// nsIAsyncShutdownBlocker
-NS_IMETHODIMP
-PlacesShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
-{
-  MOZ_ASSERT(false, "should always be overridden");
-  return NS_ERROR_NOT_IMPLEMENTED;
-}
-
-NS_IMPL_ISUPPORTS(
-  PlacesShutdownBlocker,
-  nsIAsyncShutdownBlocker
-)
-
-////////////////////////////////////////////////////////////////////////////////
-
-ClientsShutdownBlocker::ClientsShutdownBlocker()
-  : PlacesShutdownBlocker(NS_LITERAL_STRING("Places Clients shutdown"))
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  // Create a barrier that will be exposed to clients through GetClient(), so
-  // they can block Places shutdown.
-  nsCOMPtr<nsIAsyncShutdownService> asyncShutdown = services::GetAsyncShutdown();
-  MOZ_ASSERT(asyncShutdown);
-  if (asyncShutdown) {
-    nsCOMPtr<nsIAsyncShutdownBarrier> barrier;
-    MOZ_ALWAYS_SUCCEEDS(asyncShutdown->MakeBarrier(mName, getter_AddRefs(barrier)));
-    mBarrier = new nsMainThreadPtrHolder<nsIAsyncShutdownBarrier>(
-      "ClientsShutdownBlocker::mBarrier", barrier);
-  }
-}
-
 already_AddRefed<nsIAsyncShutdownClient>
-ClientsShutdownBlocker::GetClient()
+PlacesShutdownBlocker::GetClient()
 {
   nsCOMPtr<nsIAsyncShutdownClient> client;
   if (mBarrier) {
     MOZ_ALWAYS_SUCCEEDS(mBarrier->GetClient(getter_AddRefs(client)));
   }
   return client.forget();
 }
 
 // nsIAsyncShutdownBlocker
 NS_IMETHODIMP
-ClientsShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
+PlacesShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
     "ClientsShutdownBlocker::mParentClient", aParentClient);
   mState = RECEIVED_BLOCK_SHUTDOWN;
 
   if (NS_WARN_IF(!mBarrier)) {
     return NS_ERROR_NOT_AVAILABLE;
@@ -129,75 +111,89 @@ ClientsShutdownBlocker::BlockShutdown(ns
   MOZ_ALWAYS_SUCCEEDS(mBarrier->Wait(this));
 
   mState = CALLED_WAIT_CLIENTS;
   return NS_OK;
 }
 
 // nsIAsyncShutdownCompletionCallback
 NS_IMETHODIMP
+PlacesShutdownBlocker::Done()
+{
+  MOZ_ASSERT(false, "Should always be overridden");
+  return NS_OK;
+}
+
+NS_IMPL_ISUPPORTS(
+  PlacesShutdownBlocker,
+  nsIAsyncShutdownBlocker,
+  nsIAsyncShutdownCompletionCallback
+)
+
+////////////////////////////////////////////////////////////////////////////////
+
+ClientsShutdownBlocker::ClientsShutdownBlocker()
+  : PlacesShutdownBlocker(NS_LITERAL_STRING("Places Clients shutdown"))
+{
+  // Do nothing.
+}
+
+// nsIAsyncShutdownCompletionCallback
+NS_IMETHODIMP
 ClientsShutdownBlocker::Done()
 {
   // At this point all the clients are done, we can stop blocking the shutdown
   // phase.
   mState = RECEIVED_DONE;
 
   // mParentClient is nullptr in tests.
   if (mParentClient) {
     nsresult rv = mParentClient->RemoveBlocker(this);
     if (NS_WARN_IF(NS_FAILED(rv))) return rv;
     mParentClient = nullptr;
   }
   mBarrier = nullptr;
   return NS_OK;
 }
 
-NS_IMPL_ISUPPORTS_INHERITED(
+NS_IMPL_ISUPPORTS_INHERITED0(
   ClientsShutdownBlocker,
-  PlacesShutdownBlocker,
-  nsIAsyncShutdownCompletionCallback
+  PlacesShutdownBlocker
 )
 
 ////////////////////////////////////////////////////////////////////////////////
 
 ConnectionShutdownBlocker::ConnectionShutdownBlocker(Database* aDatabase)
   : PlacesShutdownBlocker(NS_LITERAL_STRING("Places Connection shutdown"))
   , mDatabase(aDatabase)
 {
   // Do nothing.
 }
 
-// nsIAsyncShutdownBlocker
+// nsIAsyncShutdownCompletionCallback
 NS_IMETHODIMP
-ConnectionShutdownBlocker::BlockShutdown(nsIAsyncShutdownClient* aParentClient)
+ConnectionShutdownBlocker::Done()
 {
-  MOZ_ASSERT(NS_IsMainThread());
-  mParentClient = new nsMainThreadPtrHolder<nsIAsyncShutdownClient>(
-    "ConnectionShutdownBlocker::mParentClient", aParentClient);
-  mState = RECEIVED_BLOCK_SHUTDOWN;
+  // At this point all the clients are done, we can stop blocking the shutdown
+  // phase.
+  mState = RECEIVED_DONE;
+
   // Annotate that Database shutdown started.
   sIsStarted = true;
 
-  // Fire internal database closing notification.
-  nsCOMPtr<nsIObserverService> os = services::GetObserverService();
-  MOZ_ASSERT(os);
-  if (os) {
-    Unused << os->NotifyObservers(nullptr, TOPIC_PLACES_WILL_CLOSE_CONNECTION, nullptr);
-  }
-  mState = NOTIFIED_OBSERVERS_PLACES_WILL_CLOSE_CONNECTION;
-
   // At this stage, any use of this database is forbidden. Get rid of
   // `gDatabase`. Note, however, that the database could be
   // resurrected.  This can happen in particular during tests.
   MOZ_ASSERT(Database::gDatabase == nullptr || Database::gDatabase == mDatabase);
   Database::gDatabase = nullptr;
 
   // Database::Shutdown will invoke Complete once the connection is closed.
   mDatabase->Shutdown();
   mState = CALLED_STORAGESHUTDOWN;
+  mBarrier = nullptr;
   return NS_OK;
 }
 
 // mozIStorageCompletionCallback
 NS_IMETHODIMP
 ConnectionShutdownBlocker::Complete(nsresult, nsISupports*)
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/toolkit/components/places/Shutdown.h
+++ b/toolkit/components/places/Shutdown.h
@@ -38,38 +38,38 @@ class Database;
  * its `Done` method is invoked, and it stops blocking the shutdown phase, so
  * that it can continue.
  *
  * PHASE 3 (Connection shutdown)
  * ConnectionBlocker is registered as a profile-before-change blocker in
  * Database::Init (see Database::mConnectionShutdown).
  * When profile-before-change is observer by async shutdown, it calls
  * ConnectionShutdownBlocker::BlockShutdown.
- * This is the last chance for any Places internal work, like privacy cleanups,
- * before the connection is closed. This a places-will-close-connection
- * notification is sent to legacy clients that must complete any operation in
- * the same tick, since we won't wait for them.
  * Then the control is passed to Database::Shutdown, that executes some sanity
  * checks, clears cached statements and proceeds with asyncClose.
  * Once the connection is definitely closed, Database will call back
  * ConnectionBlocker::Complete. At this point a final
  * places-connection-closed notification is sent, for testing purposes.
  */
 
 /**
  * A base AsyncShutdown blocker in charge of shutting down Places.
  */
 class PlacesShutdownBlocker : public nsIAsyncShutdownBlocker
+                            , public nsIAsyncShutdownCompletionCallback
 {
 public:
   NS_DECL_THREADSAFE_ISUPPORTS
   NS_DECL_NSIASYNCSHUTDOWNBLOCKER
+  NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
 
   explicit PlacesShutdownBlocker(const nsString& aName);
 
+  already_AddRefed<nsIAsyncShutdownClient> GetClient();
+
   /**
    * `true` if we have not started shutdown, i.e.  if
    * `BlockShutdown()` hasn't been called yet, false otherwise.
    */
   static bool IsStarted() {
     return sIsStarted;
   }
 
@@ -121,45 +121,40 @@ protected:
 
   virtual ~PlacesShutdownBlocker() {}
 };
 
 /**
  * Blocker also used to wait for clients, through an owned barrier.
  */
 class ClientsShutdownBlocker final : public PlacesShutdownBlocker
-                                   , public nsIAsyncShutdownCompletionCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
-  NS_DECL_NSIASYNCSHUTDOWNCOMPLETIONCALLBACK
 
   explicit ClientsShutdownBlocker();
 
-  NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aParentClient) override;
-
-  already_AddRefed<nsIAsyncShutdownClient> GetClient();
-
+  NS_IMETHOD Done() override;
 private:
   ~ClientsShutdownBlocker() {}
 };
 
 /**
  * Blocker used to wait when closing the database connection.
  */
 class ConnectionShutdownBlocker final : public PlacesShutdownBlocker
                                       , public mozIStorageCompletionCallback
 {
 public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_MOZISTORAGECOMPLETIONCALLBACK
 
-  NS_IMETHOD BlockShutdown(nsIAsyncShutdownClient* aParentClient) override;
+  explicit ConnectionShutdownBlocker(mozilla::places::Database* aDatabase);
 
-  explicit ConnectionShutdownBlocker(mozilla::places::Database* aDatabase);
+  NS_IMETHOD Done() override;
 
 private:
   ~ConnectionShutdownBlocker() {}
 
   // The owning database.
   // The cycle is broken in method Complete(), once the connection
   // has been closed by mozStorage.
   RefPtr<mozilla::places::Database> mDatabase;
--- a/toolkit/components/places/UnifiedComplete.js
+++ b/toolkit/components/places/UnifiedComplete.js
@@ -41,16 +41,18 @@ const PREF_SUGGEST_SEARCHES =       [ "s
 
 const PREF_MAX_CHARS_FOR_SUGGEST =  [ "maxCharsForSearchSuggestions", 20];
 const PREF_MAX_HISTORICAL_SUGGESTIONS =  [ "maxHistoricalSearchSuggestions", 0];
 
 const PREF_PRELOADED_SITES_ENABLED =  [ "usepreloadedtopurls.enabled",   true ];
 const PREF_PRELOADED_SITES_EXPIRE_DAYS = [ "usepreloadedtopurls.expire_days",  14 ];
 
 const PREF_MATCH_BUCKETS = [ "matchBuckets", "general:5,suggestion:Infinity" ];
+// Will default to matchBuckets if not defined.
+const PREF_MATCH_BUCKETS_SEARCH = [ "matchBucketsSearch", "" ];
 
 // AutoComplete query type constants.
 // Describes the various types of queries that we can process rows for.
 const QUERYTYPE_FILTERED            = 0;
 const QUERYTYPE_AUTOFILL_HOST       = 1;
 const QUERYTYPE_AUTOFILL_URL        = 2;
 
 // This separator is used as an RTL-friendly way to split the title and tags.
@@ -119,16 +121,18 @@ const MATCHTYPE = {
 };
 
 // Buckets for match insertion.
 // Every time a new match is returned, we go through each bucket in array order,
 // and look for the first one having available space for the given match type.
 // Each bucket is an array containing the following indices:
 //   0: The match type of the acceptable entries.
 //   1: available number of slots in this bucket.
+// There are different matchBuckets definition for different contexts, currently
+// a general one (_matchBuckets) and a search one (_matchBucketsSearch).
 //
 // First buckets. Anything with an Infinity frecency ends up here.
 const DEFAULT_BUCKETS_BEFORE = [
   [MATCHTYPE.HEURISTIC, 1],
   [MATCHTYPE.EXTENSION, MAXIMUM_ALLOWED_EXTENSION_MATCHES - 1],
 ];
 // => USER DEFINED BUCKETS WILL BE INSERTED HERE <=
 //
@@ -503,26 +507,40 @@ XPCOMUtils.defineLazyGetter(this, "Prefs
     store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
     store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
     store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
     store.maxCharsForSearchSuggestions = prefs.get(...PREF_MAX_CHARS_FOR_SUGGEST);
     store.maxHistoricalSearchSuggestions = prefs.get(...PREF_MAX_HISTORICAL_SUGGESTIONS);
     store.preloadedSitesEnabled = prefs.get(...PREF_PRELOADED_SITES_ENABLED);
     store.preloadedSitesExpireDays = prefs.get(...PREF_PRELOADED_SITES_EXPIRE_DAYS);
     store.matchBuckets = prefs.get(...PREF_MATCH_BUCKETS);
-    // Convert from the string format to an array.
+    // Convert from pref char format to an array and add the default buckets.
     try {
-      store.matchBuckets = convertBucketsCharPrefToArray(store.matchBuckets)
+      store.matchBuckets = convertBucketsCharPrefToArray(store.matchBuckets);
     } catch (ex) {
       store.matchBuckets = convertBucketsCharPrefToArray(PREF_MATCH_BUCKETS[1]);
     }
-    // Add the default buckets.
     store.matchBuckets = [ ...DEFAULT_BUCKETS_BEFORE,
                            ...store.matchBuckets,
                            ...DEFAULT_BUCKETS_AFTER ];
+    store.matchBucketsSearch = prefs.get(...PREF_MATCH_BUCKETS_SEARCH);
+    // Default to matchBuckets if not defined.
+    if (!store.matchBucketsSearch) {
+      store.matchBucketsSearch = store.matchBuckets;
+    } else {
+      // Convert from pref char format to an array and add the default buckets.
+      try {
+        store.matchBucketsSearch = convertBucketsCharPrefToArray(store.matchBucketsSearch);
+        store.matchBucketsSearch = [ ...DEFAULT_BUCKETS_BEFORE,
+                                     ...store.matchBucketsSearch,
+                                     ...DEFAULT_BUCKETS_AFTER ];
+      } catch (ex) {
+        store.matchBucketsSearch = store.matchBuckets;
+      }
+    }
     store.keywordEnabled = Services.prefs.getBoolPref("keyword.enabled", true);
 
     // If history is not set, onlyTyped value should be ignored.
     if (!store.suggestHistory) {
       store.suggestTyped = false;
     }
     store.defaultBehavior = [...types, "Typed"].reduce((memo, type) => {
       let prefValue = store["suggest" + type];
@@ -810,24 +828,16 @@ function Search(searchString, searchPara
 
   // These are used to avoid adding duplicate entries to the results.
   this._usedURLs = new Set();
   this._usedPlaceIds = new Set();
 
   // Resolved when all the matches providers have been fetched.
   this._allMatchesPromises = [];
 
-  // Convert the buckets to readable objects with a count property.
-  this._buckets = Prefs.matchBuckets
-                       .map(([type, available]) => ({
-                         type,
-                         available,
-                         count: 0
-                       }));
-
   // Counters for the number of matches per MATCHTYPE.
   this._counts = Object.values(MATCHTYPE)
                        .reduce((o, p) => { o[p] = 0; return o; }, {});
 
   this._searchStringHasWWW = this._strippedPrefix.endsWith("www.");
   this._searchStringWWW = this._searchStringHasWWW ? "www." : "";
   this._searchStringFromWWW = this._searchStringWWW + this._searchString;
 
@@ -1819,16 +1829,27 @@ Search.prototype = {
     if (this._result.matchCount == 6)
       TelemetryStopwatch.finish(TELEMETRY_6_FIRST_RESULTS, this);
 
     this.notifyResults(true);
   },
 
   _getInsertIndexForMatch(match) {
     let index = 0;
+    // The buckets change depending on the context, that is currently decided by
+    // the first added match (the heuristic one).
+    if (!this._buckets) {
+      // Convert the buckets to readable objects with a count property.
+      let buckets = match.style.includes("searchengine") ? Prefs.matchBucketsSearch
+                                                         : Prefs.matchBuckets;
+      this._buckets = buckets.map(([type, available]) => ({ type,
+                                                            available,
+                                                            count: 0,
+                                                          }));
+    }
     for (let bucket of this._buckets) {
       // Move to the next bucket if the match type is incompatible, or if there
       // is no available space or if the frecency is below the threshold.
       if (match.type != bucket.type || !bucket.available) {
         index += bucket.count;
         continue;
       }
 
--- a/toolkit/components/places/nsNavHistory.cpp
+++ b/toolkit/components/places/nsNavHistory.cpp
@@ -2900,20 +2900,33 @@ nsNavHistory::GetDBConnection(mozIStorag
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistory::GetShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
 {
   NS_ENSURE_ARG_POINTER(_shutdownClient);
-  RefPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
-  MOZ_ASSERT(client);
+  nsCOMPtr<nsIAsyncShutdownClient> client = mDB->GetClientsShutdown();
+  if (!client) {
+    return NS_ERROR_UNEXPECTED;
+  }
   client.forget(_shutdownClient);
-
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsNavHistory::GetConnectionShutdownClient(nsIAsyncShutdownClient **_shutdownClient)
+{
+  NS_ENSURE_ARG_POINTER(_shutdownClient);
+  nsCOMPtr<nsIAsyncShutdownClient> client = mDB->GetConnectionShutdown();
+  if (!client) {
+    return NS_ERROR_UNEXPECTED;
+  }
+  client.forget(_shutdownClient);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsNavHistory::AsyncExecuteLegacyQueries(nsINavHistoryQuery** aQueries,
                                         uint32_t aQueryCount,
                                         nsINavHistoryQueryOptions* aOptions,
                                         mozIStorageStatementCallback* aCallback,
--- a/toolkit/components/places/nsPIPlacesDatabase.idl
+++ b/toolkit/components/places/nsPIPlacesDatabase.idl
@@ -42,11 +42,19 @@ interface nsPIPlacesDatabase : nsISuppor
     [array, size_is(aQueryCount)] in nsINavHistoryQuery aQueries,
     in unsigned long aQueryCount,
     in nsINavHistoryQueryOptions aOptions,
     in mozIStorageStatementCallback aCallback);
 
   /**
    * Hook for clients who need to perform actions during/by the end of
    * the shutdown of the database.
+   * May be null if it's too late to get one.
    */
   readonly attribute nsIAsyncShutdownClient shutdownClient;
+
+  /**
+   * Hook for internal clients who need to perform actions just before the
+   * connection gets closed.
+   * May be null if it's too late to get one.
+   */
+  readonly attribute nsIAsyncShutdownClient connectionShutdownClient;
 };
--- a/toolkit/components/places/nsPlacesExpiration.js
+++ b/toolkit/components/places/nsPlacesExpiration.js
@@ -7,17 +7,16 @@
 /**
  * This component handles history and orphans expiration through asynchronous
  * Storage statements.
  * Expiration runs:
  * - At idle, but just once, we stop any other kind of expiration during idle
  *   to preserve batteries in portable devices.
  * - At shutdown, only if the database is dirty, we should still avoid to
  *   expire too heavily on shutdown.
- * - On ClearHistory we run a full expiration for privacy reasons.
  * - On a repeating timer we expire in small chunks.
  *
  * Expiration algorithm will adapt itself based on:
  * - Memory size of the device.
  * - Status of the database (clean or dirty).
  */
 
 const Cc = Components.classes;
@@ -28,17 +27,16 @@ Cu.import("resource://gre/modules/XPCOMU
 Cu.import("resource://gre/modules/Services.jsm");
 
 XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
   "resource://gre/modules/PlacesUtils.jsm");
 
 // Constants
 
 // Last expiration step should run before the final sync.
-const TOPIC_SHUTDOWN = "places-will-close-connection";
 const TOPIC_PREF_CHANGED = "nsPref:changed";
 const TOPIC_DEBUG_START_EXPIRATION = "places-debug-start-expiration";
 const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
 const TOPIC_IDLE_BEGIN = "idle";
 const TOPIC_IDLE_END = "active";
 const TOPIC_IDLE_DAILY = "idle-daily";
 const TOPIC_TESTING_MODE = "testing-mode";
 const TOPIC_TEST_INTERVAL_CHANGED = "test-interval-changed";
@@ -104,21 +102,16 @@ const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3
 // Used as a fall back value when it's not possible to calculate the real value.
 const URIENTRY_AVG_SIZE = 600;
 
 // Seconds of idle time before starting a larger expiration step.
 // Notice during idle we stop the expiration timer since we don't want to hurt
 // stand-by or mobile devices batteries.
 const IDLE_TIMEOUT_SECONDS = 5 * 60;
 
-// If a clear history ran just before we shutdown, we will skip most of the
-// expiration at shutdown.  This is maximum number of seconds from last
-// clearHistory to decide to skip expiration at shutdown.
-const SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS = 10;
-
 // If the number of pages over history limit is greater than this threshold,
 // expiration will be more aggressive, to bring back history to a saner size.
 const OVERLIMIT_PAGES_THRESHOLD = 1000;
 
 const MSECS_PER_DAY = 86400000;
 const ANNOS_EXPIRE_POLICIES = [
   { bind: "expire_days",
     type: Ci.nsIAnnotationService.EXPIRE_DAYS,
@@ -129,17 +122,17 @@ const ANNOS_EXPIRE_POLICIES = [
   { bind: "expire_months",
     type: Ci.nsIAnnotationService.EXPIRE_MONTHS,
     time: 180 * 1000 * MSECS_PER_DAY },
 ];
 
 // When we expire we can use these limits:
 // - SMALL for usual partial expirations, will expire a small chunk.
 // - LARGE for idle or shutdown expirations, will expire a large chunk.
-// - UNLIMITED for clearHistory, will expire everything.
+// - UNLIMITED will expire all the orphans.
 // - DEBUG will use a known limit, passed along with the debug notification.
 const LIMIT = {
   SMALL: 0,
   LARGE: 1,
   UNLIMITED: 2,
   DEBUG: 3,
 };
 
@@ -149,21 +142,20 @@ const STATUS = {
   DIRTY: 1,
   UNKNOWN: 2,
 };
 
 // Represents actions on which a query will run.
 const ACTION = {
   TIMED:           1 << 0, // happens every this._interval
   TIMED_OVERLIMIT: 1 << 1, // like TIMED but only when history is over limits
-  CLEAR_HISTORY:   1 << 2, // happens when history is cleared
-  SHUTDOWN_DIRTY:  1 << 3, // happens at shutdown for DIRTY state
-  IDLE_DIRTY:      1 << 4, // happens on idle for DIRTY state
-  IDLE_DAILY:      1 << 5, // happens once a day on idle
-  DEBUG:           1 << 6, // happens on TOPIC_DEBUG_START_EXPIRATION
+  SHUTDOWN_DIRTY:  1 << 2, // happens at shutdown for DIRTY state
+  IDLE_DIRTY:      1 << 3, // happens on idle for DIRTY state
+  IDLE_DAILY:      1 << 4, // happens once a day on idle
+  DEBUG:           1 << 5, // happens on TOPIC_DEBUG_START_EXPIRATION
 };
 
 // The queries we use to expire.
 const EXPIRATION_QUERIES = {
 
   // Some visits can be expired more often than others, cause they are less
   // useful to the user and can pollute awesomebar results:
   // 1. urls over 255 chars
@@ -235,161 +227,127 @@ const EXPIRATION_QUERIES = {
   QUERY_EXPIRE_URIS: {
     sql: `DELETE FROM moz_places WHERE id IN (
             SELECT p_id FROM expiration_notify WHERE p_id NOTNULL
           ) AND foreign_count = 0 AND last_visit_date ISNULL`,
     actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
              ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
-  // Expire orphan URIs from the database.
-  QUERY_SILENT_EXPIRE_ORPHAN_URIS: {
-    sql: `DELETE FROM moz_places WHERE id IN (
-            SELECT h.id
-            FROM moz_places h
-            LEFT JOIN moz_historyvisits v ON h.id = v.place_id
-            WHERE h.last_visit_date IS NULL
-              AND h.foreign_count = 0
-              AND v.id IS NULL
-            LIMIT :limit_uris
-          )`,
-    actions: ACTION.CLEAR_HISTORY
-  },
-
   // Hosts accumulated during the places delete are updated through a trigger
   // (see nsPlacesTriggers.h).
   QUERY_UPDATE_HOSTS: {
     sql: `DELETE FROM moz_updatehosts_temp`,
-    actions: ACTION.CLEAR_HISTORY | ACTION.TIMED | ACTION.TIMED_OVERLIMIT |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire orphan pages from the icons database.
   QUERY_EXPIRE_FAVICONS_PAGES: {
     sql: `DELETE FROM moz_pages_w_icons
           WHERE page_url_hash NOT IN (
             SELECT url_hash FROM moz_places
           )`,
-    actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire orphan icons from the database.
   QUERY_EXPIRE_FAVICONS: {
     sql: `DELETE FROM moz_icons
           WHERE root = 0 AND id NOT IN (
             SELECT icon_id FROM moz_icons_to_pages
           )`,
-    actions: ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire orphan page annotations from the database.
   QUERY_EXPIRE_ANNOS: {
     sql: `DELETE FROM moz_annos WHERE id in (
             SELECT a.id FROM moz_annos a
             LEFT JOIN moz_places h ON a.place_id = h.id
             WHERE h.id IS NULL
             LIMIT :limit_annos
           )`,
-    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire page annotations based on expiration policy.
   QUERY_EXPIRE_ANNOS_WITH_POLICY: {
     sql: `DELETE FROM moz_annos
           WHERE (expiration = :expire_days
             AND :expire_days_time > MAX(lastModified, dateAdded))
              OR (expiration = :expire_weeks
             AND :expire_weeks_time > MAX(lastModified, dateAdded))
              OR (expiration = :expire_months
             AND :expire_months_time > MAX(lastModified, dateAdded))`,
-    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire items annotations based on expiration policy.
   QUERY_EXPIRE_ITEMS_ANNOS_WITH_POLICY: {
     sql: `DELETE FROM moz_items_annos
           WHERE (expiration = :expire_days
             AND :expire_days_time > MAX(lastModified, dateAdded))
              OR (expiration = :expire_weeks
             AND :expire_weeks_time > MAX(lastModified, dateAdded))
              OR (expiration = :expire_months
             AND :expire_months_time > MAX(lastModified, dateAdded))`,
-    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire page annotations based on expiration policy.
   QUERY_EXPIRE_ANNOS_WITH_HISTORY: {
     sql: `DELETE FROM moz_annos
           WHERE expiration = :expire_with_history
             AND NOT EXISTS (SELECT id FROM moz_historyvisits
                             WHERE place_id = moz_annos.place_id LIMIT 1)`,
-    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
+    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire item annos without a corresponding item id.
   QUERY_EXPIRE_ITEMS_ANNOS: {
     sql: `DELETE FROM moz_items_annos WHERE id IN (
             SELECT a.id FROM moz_items_annos a
             LEFT JOIN moz_bookmarks b ON a.item_id = b.id
             WHERE b.id IS NULL
             LIMIT :limit_annos
           )`,
-    actions: ACTION.CLEAR_HISTORY | ACTION.IDLE_DAILY | ACTION.DEBUG
+    actions: ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire all annotation names without a corresponding annotation.
   QUERY_EXPIRE_ANNO_ATTRIBUTES: {
     sql: `DELETE FROM moz_anno_attributes WHERE id IN (
             SELECT n.id FROM moz_anno_attributes n
             LEFT JOIN moz_annos a ON n.id = a.anno_attribute_id
             LEFT JOIN moz_items_annos t ON n.id = t.anno_attribute_id
             WHERE a.anno_attribute_id IS NULL
               AND t.anno_attribute_id IS NULL
             LIMIT :limit_annos
           )`,
-    actions: ACTION.CLEAR_HISTORY | ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY |
+    actions: ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY |
              ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Expire orphan inputhistory.
   QUERY_EXPIRE_INPUTHISTORY: {
     sql: `DELETE FROM moz_inputhistory WHERE place_id IN (
             SELECT i.place_id FROM moz_inputhistory i
             LEFT JOIN moz_places h ON h.id = i.place_id
             WHERE h.id IS NULL
             LIMIT :limit_inputhistory
           )`,
-    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.CLEAR_HISTORY |
-             ACTION.SHUTDOWN_DIRTY | ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY |
-             ACTION.DEBUG
-  },
-
-  // Expire all session annotations.  Should only be called at shutdown.
-  QUERY_EXPIRE_ANNOS_SESSION: {
-    sql: "DELETE FROM moz_annos WHERE expiration = :expire_session",
-    actions: ACTION.CLEAR_HISTORY | ACTION.DEBUG
-  },
-
-  // Expire all session item annotations.  Should only be called at shutdown.
-  QUERY_EXPIRE_ITEMS_ANNOS_SESSION: {
-    sql: "DELETE FROM moz_items_annos WHERE expiration = :expire_session",
-    actions: ACTION.CLEAR_HISTORY | ACTION.DEBUG
+    actions: ACTION.TIMED | ACTION.TIMED_OVERLIMIT | ACTION.SHUTDOWN_DIRTY |
+             ACTION.IDLE_DIRTY | ACTION.IDLE_DAILY | ACTION.DEBUG
   },
 
   // Select entries for notifications.
   // If p_id is set whole_entry = 1, then we have expired the full page.
   // Either p_id or v_id are always set.
   QUERY_SELECT_NOTIFICATIONS: {
     sql: `SELECT url, guid, MAX(visit_date) AS visit_date,
                  MAX(IFNULL(MIN(p_id, 1), MIN(v_id, 0))) AS whole_entry,
@@ -469,50 +427,47 @@ function nsPlacesExpiration() {
     // Observe our preferences branch for changes.
     this._prefBranch.addObserver("", this, true);
 
     // Create our expiration timer.
     this._newTimer();
   }, Cu.reportError);
 
   // Register topic observers.
-  Services.obs.addObserver(this, TOPIC_SHUTDOWN, true);
   Services.obs.addObserver(this, TOPIC_DEBUG_START_EXPIRATION, true);
   Services.obs.addObserver(this, TOPIC_IDLE_DAILY, true);
+
+  // Block shutdown.
+  let shutdownClient = PlacesUtils.history.connectionShutdownClient.jsclient;
+  shutdownClient.addBlocker("Places Expiration: shutdown", () => {
+    if (this._shuttingDown) {
+      return;
+    }
+    this._shuttingDown = true;
+    this.expireOnIdle = false;
+    if (this._timer) {
+      this._timer.cancel();
+      this._timer = null;
+    }
+    // If the database is dirty, we want to expire some entries, to speed up
+    // the expiration process.
+    if (this.status == STATUS.DIRTY) {
+      this._expireWithActionAndLimit(ACTION.SHUTDOWN_DIRTY, LIMIT.LARGE);
+    }
+    this._finalizeInternalStatements();
+  });
 }
 
 nsPlacesExpiration.prototype = {
-
-  // nsIObserver
-
   observe: function PEX_observe(aSubject, aTopic, aData) {
     if (this._shuttingDown) {
       return;
     }
 
-    if (aTopic == TOPIC_SHUTDOWN) {
-      this._shuttingDown = true;
-      this.expireOnIdle = false;
-
-      if (this._timer) {
-        this._timer.cancel();
-        this._timer = null;
-      }
-
-      // If we didn't ran a clearHistory recently and database is dirty, we
-      // want to expire some entries, to speed up the expiration process.
-      let hasRecentClearHistory =
-        Date.now() - this._lastClearHistoryTime <
-          SHUTDOWN_WITH_RECENT_CLEARHISTORY_TIMEOUT_SECONDS * 1000;
-      if (!hasRecentClearHistory && this.status == STATUS.DIRTY) {
-        this._expireWithActionAndLimit(ACTION.SHUTDOWN_DIRTY, LIMIT.LARGE);
-      }
-
-      this._finalizeInternalStatements();
-    } else if (aTopic == TOPIC_PREF_CHANGED) {
+    if (aTopic == TOPIC_PREF_CHANGED) {
       this._loadPrefsPromise = this._loadPrefs().then(() => {
         if (aData == PREF_INTERVAL_SECONDS) {
           // Renew the timer with the new interval value.
           this._newTimer();
         }
       }, Cu.reportError);
     } else if (aTopic == TOPIC_DEBUG_START_EXPIRATION) {
       // The passed-in limit is the maximum number of visits to expire when
@@ -572,22 +527,19 @@ nsPlacesExpiration.prototype = {
   onEndUpdateBatch: function PEX_onEndUpdateBatch() {
     this._inBatchMode = false;
 
     // Restore timer.
     if (!this._timer)
       this._newTimer();
   },
 
-  _lastClearHistoryTime: 0,
   onClearHistory: function PEX_onClearHistory() {
-    this._lastClearHistoryTime = Date.now();
-    // Expire orphans.  History status is clean after a clear history.
+    // History status is clean after a clear history.
     this.status = STATUS.CLEAN;
-    this._expireWithActionAndLimit(ACTION.CLEAR_HISTORY, LIMIT.UNLIMITED);
   },
 
   onVisit() {},
   onTitleChanged() {},
   onDeleteURI() {},
   onPageChanged() {},
   onDeleteVisits() {},
 
@@ -977,19 +929,16 @@ nsPlacesExpiration.prototype = {
         // Avoid expiring all visits in case of an unlimited debug expiration,
         // just remove orphans instead.
         params.limit_visits =
           aLimit == LIMIT.DEBUG && baseLimit == -1 ? 0 : baseLimit;
         break;
       case "QUERY_FIND_URIS_TO_EXPIRE":
         params.limit_uris = baseLimit;
         break;
-      case "QUERY_SILENT_EXPIRE_ORPHAN_URIS":
-        params.limit_uris = baseLimit;
-        break;
       case "QUERY_EXPIRE_ANNOS":
         // Each page may have multiple annos.
         params.limit_annos = baseLimit * EXPIRE_AGGRESSIVITY_MULTIPLIER;
         break;
       case "QUERY_EXPIRE_ANNOS_WITH_POLICY":
       case "QUERY_EXPIRE_ITEMS_ANNOS_WITH_POLICY":
         let microNow = Date.now() * 1000;
         ANNOS_EXPIRE_POLICIES.forEach(function(policy) {
@@ -1004,20 +953,16 @@ nsPlacesExpiration.prototype = {
         params.limit_annos = baseLimit;
         break;
       case "QUERY_EXPIRE_ANNO_ATTRIBUTES":
         params.limit_annos = baseLimit;
         break;
       case "QUERY_EXPIRE_INPUTHISTORY":
         params.limit_inputhistory = baseLimit;
         break;
-      case "QUERY_EXPIRE_ANNOS_SESSION":
-      case "QUERY_EXPIRE_ITEMS_ANNOS_SESSION":
-        params.expire_session = Ci.nsIAnnotationService.EXPIRE_SESSION;
-        break;
     }
 
     return stmt;
   },
 
   /**
    * Creates a new timer based on this._interval.
    *
--- a/toolkit/components/places/tests/PlacesTestUtils.jsm
+++ b/toolkit/components/places/tests/PlacesTestUtils.jsm
@@ -123,26 +123,21 @@ this.PlacesTestUtils = Object.freeze({
   },
 
   /**
    * Clear all history.
    *
    * @return {Promise}
    * @resolves When history was cleared successfully.
    * @rejects JavaScript exception.
+   *
+   * @deprecated New consumers should directly use PlacesUtils.history.clear().
    */
   clearHistory() {
-    let expirationFinished = new Promise(resolve => {
-      Services.obs.addObserver(function observe(subj, topic, data) {
-        Services.obs.removeObserver(observe, topic);
-        resolve();
-      }, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
-    });
-
-    return Promise.all([expirationFinished, PlacesUtils.history.clear()]);
+    return PlacesUtils.history.clear();
   },
 
   /**
    * Waits for all pending async statements on the default connection.
    *
    * @return {Promise}
    * @resolves When all pending async statements finished.
    * @rejects Never.
--- a/toolkit/components/places/tests/bookmarks/test_async_observers.js
+++ b/toolkit/components/places/tests/bookmarks/test_async_observers.js
@@ -86,33 +86,25 @@ add_task(async function test_remove_page
 
 add_task(async function shutdown() {
   // Check that async observers don't try to create async statements after
   // shutdown.  That would cause assertions, since the async thread is gone
   // already.  Note that in such a case the notifications are not fired, so we
   // cannot test for them.
   // Put an history notification that triggers AsyncGetBookmarksForURI between
   // asyncClose() and the actual connection closing.  Enqueuing a main-thread
-  // event just after places-will-close-connection should ensure it runs before
+  // event as a shutdown blocker should ensure it runs before
   // places-connection-closed.
   // Notice this code is not using helpers cause it depends on a very specific
   // order, a change in the helpers code could make this test useless.
-  await new Promise(resolve => {
-    Services.obs.addObserver(function onNotification() {
-      Services.obs.removeObserver(onNotification, "places-will-close-connection");
-      do_check_true(true, "Observed fake places shutdown");
 
-      Services.tm.dispatchToMainThread(() => {
+  let shutdownClient = PlacesUtils.history.shutdownClient.jsclient;
+  shutdownClient.addBlocker("Places Expiration: shutdown",
+    function() {
+      Services.tm.mainThread.dispatch(() => {
         // WARNING: this is very bad, never use out of testing code.
         PlacesUtils.bookmarks.QueryInterface(Ci.nsINavHistoryObserver)
                              .onPageChanged(NetUtil.newURI("http://book.ma.rk/"),
                                             Ci.nsINavHistoryObserver.ATTRIBUTE_FAVICON,
                                             "test", "test");
-        resolve(promiseTopicObserved("places-connection-closed"));
-      });
-    }, "places-will-close-connection");
-    shutdownPlaces();
-  });
+      }, Ci.nsIThread.DISPATCH_NORMAL);
+    });
 });
-
-
-
-
--- a/toolkit/components/places/tests/expiration/head_expiration.js
+++ b/toolkit/components/places/tests/expiration/head_expiration.js
@@ -16,24 +16,16 @@ Cu.import("resource://gre/modules/Servic
   /* import-globals-from ../head_common.js */
   let commonFile = do_get_file("../head_common.js", false);
   let uri = Services.io.newFileURI(commonFile);
   Services.scriptloader.loadSubScript(uri.spec, this);
 }
 
 // Put any other stuff relative to this test folder below.
 
-
-// Simulates an expiration at shutdown.
-function shutdownExpiration() {
-  let expire = Cc["@mozilla.org/places/expiration;1"].getService(Ci.nsIObserver);
-  expire.observe(null, "places-will-close-connection", null);
-}
-
-
 /**
  * Causes expiration component to start, otherwise it would wait for the first
  * history notification.
  */
 function force_expiration_start() {
   Cc["@mozilla.org/places/expiration;1"]
     .getService(Ci.nsIObserver)
     .observe(null, "testing-mode", null);
--- a/toolkit/components/places/tests/expiration/test_clearHistory.js
+++ b/toolkit/components/places/tests/expiration/test_clearHistory.js
@@ -6,79 +6,18 @@
 
 /**
  * What this is aimed to test:
  *
  * History.clear() should expire everything but bookmarked pages and valid
  * annos.
  */
 
-var as = PlacesUtils.annotations;
-
-/**
- * Creates an aged annotation.
- *
- * @param aIdentifier Either a page url or an item id.
- * @param aIdentifier Name of the annotation.
- * @param aValue Value for the annotation.
- * @param aExpirePolicy Expiration policy of the annotation.
- * @param aAgeInDays Age in days of the annotation.
- * @param [optional] aLastModifiedAgeInDays Age in days of the annotation, for lastModified.
- */
-var now = Date.now();
-function add_old_anno(aIdentifier, aName, aValue, aExpirePolicy,
-                      aAgeInDays, aLastModifiedAgeInDays) {
-  let expireDate = (now - (aAgeInDays * 86400 * 1000)) * 1000;
-  let lastModifiedDate = 0;
-  if (aLastModifiedAgeInDays)
-    lastModifiedDate = (now - (aLastModifiedAgeInDays * 86400 * 1000)) * 1000;
-
-  let sql;
-  if (typeof(aIdentifier) == "number") {
-    // Item annotation.
-    as.setItemAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
-    // Update dateAdded for the last added annotation.
-    sql = "UPDATE moz_items_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
-          "WHERE id = ( " +
-            "SELECT a.id FROM moz_items_annos a " +
-            "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
-            "WHERE a.item_id = :id " +
-              "AND n.name = :anno_name " +
-            "ORDER BY a.dateAdded DESC LIMIT 1 " +
-          ")";
-  } else if (aIdentifier instanceof Ci.nsIURI) {
-    // Page annotation.
-    as.setPageAnnotation(aIdentifier, aName, aValue, 0, aExpirePolicy);
-    // Update dateAdded for the last added annotation.
-    sql = "UPDATE moz_annos SET dateAdded = :expire_date, lastModified = :last_modified " +
-          "WHERE id = ( " +
-            "SELECT a.id FROM moz_annos a " +
-            "JOIN moz_anno_attributes n ON n.id = a.anno_attribute_id " +
-            "JOIN moz_places h on h.id = a.place_id " +
-            "WHERE h.url_hash = hash(:id) AND h.url = :id " +
-            "AND n.name = :anno_name " +
-            "ORDER BY a.dateAdded DESC LIMIT 1 " +
-          ")";
-  } else
-    do_throw("Wrong identifier type");
-
-  let stmt = DBConn().createStatement(sql);
-  stmt.params.id = (typeof(aIdentifier) == "number") ? aIdentifier
-                                                     : aIdentifier.spec;
-  stmt.params.expire_date = expireDate;
-  stmt.params.last_modified = lastModifiedDate;
-  stmt.params.anno_name = aName;
-  try {
-    stmt.executeStep();
-  } finally {
-    stmt.finalize();
-  }
-}
-
 add_task(async function test_historyClear() {
+  let as = PlacesUtils.annotations;
   // Set interval to a large value so we don't expire on it.
   setInterval(3600); // 1h
 
   // Expire all expirable pages.
   setMaxPages(0);
 
   // Add some bookmarked page with visit and annotations.
   for (let i = 0; i < 5; i++) {
@@ -94,48 +33,38 @@ add_task(async function test_historyClea
     // Will persist because it's an EXPIRE_NEVER item anno.
     as.setItemAnnotation(id, "persist", "test", 0, as.EXPIRE_NEVER);
     // Will persist because the page is bookmarked.
     as.setPageAnnotation(pageURI, "persist", "test", 0, as.EXPIRE_NEVER);
     // All EXPIRE_SESSION annotations are expected to expire on clear history.
     as.setItemAnnotation(id, "expire_session", "test", 0, as.EXPIRE_SESSION);
     as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION);
     // Annotations with timed policy will expire regardless bookmarked status.
-    add_old_anno(id, "expire_days", "test", as.EXPIRE_DAYS, 8);
-    add_old_anno(id, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
-    add_old_anno(id, "expire_months", "test", as.EXPIRE_MONTHS, 181);
-    add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8);
-    add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
-    add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181);
   }
 
   // Add some visited page and annotations for each.
   for (let i = 0; i < 5; i++) {
     // All page annotations related to these expired pages are expected to
     // expire as well.
     let pageURI = uri("http://page_anno." + i + ".mozilla.org/");
     await PlacesTestUtils.addVisits({ uri: pageURI });
     as.setPageAnnotation(pageURI, "expire", "test", 0, as.EXPIRE_NEVER);
     as.setPageAnnotation(pageURI, "expire_session", "test", 0, as.EXPIRE_SESSION);
-    add_old_anno(pageURI, "expire_days", "test", as.EXPIRE_DAYS, 8);
-    add_old_anno(pageURI, "expire_weeks", "test", as.EXPIRE_WEEKS, 31);
-    add_old_anno(pageURI, "expire_months", "test", as.EXPIRE_MONTHS, 181);
   }
 
   // Expire all visits for the bookmarks
   await PlacesTestUtils.clearHistory();
 
-  ["expire_days", "expire_weeks", "expire_months", "expire_session",
+  ["expire_session",
    "expire"].forEach(function(aAnno) {
     let pages = as.getPagesWithAnnotation(aAnno);
     do_check_eq(pages.length, 0);
   });
 
-  ["expire_days", "expire_weeks", "expire_months", "expire_session",
-   "expire"].forEach(function(aAnno) {
+  ["expire_session", "expire"].forEach(function(aAnno) {
     let items = as.getItemsWithAnnotation(aAnno);
     do_check_eq(items.length, 0);
   });
 
   let pages = as.getPagesWithAnnotation("persist");
   do_check_eq(pages.length, 5);
 
   let items = as.getItemsWithAnnotation("persist");
--- a/toolkit/components/places/tests/expiration/test_notifications.js
+++ b/toolkit/components/places/tests/expiration/test_notifications.js
@@ -20,17 +20,17 @@ var gObserver = {
   }
 };
 os.addObserver(gObserver, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
 
 function run_test() {
   // Set interval to a large value so we don't expire on it.
   setInterval(3600); // 1h
 
-  PlacesTestUtils.clearHistory();
+  promiseForceExpirationStep(1);
 
   do_timeout(2000, check_result);
   do_test_pending();
 }
 
 function check_result() {
   os.removeObserver(gObserver, PlacesUtils.TOPIC_EXPIRATION_FINISHED);
   do_check_eq(gObserver.notifications, 1);
--- a/toolkit/components/places/tests/expiration/test_pref_interval.js
+++ b/toolkit/components/places/tests/expiration/test_pref_interval.js
@@ -12,50 +12,47 @@
 // PREF_INTERVAL_SECONDS_NOTSET in nsPlacesExpiration.
 const DEFAULT_TIMER_DELAY_SECONDS = 3 * 60;
 
 // Sync this with the const value in the component.
 const EXPIRE_AGGRESSIVITY_MULTIPLIER = 3;
 
 var tests = [
 
-  // This test should be the first, so the interval won't be influenced by
-  // status of history.
   { desc: "Set interval to 1s.",
     interval: 1,
-    expectedTimerDelay: 1
+    expectedTimerDelay: 1 * EXPIRE_AGGRESSIVITY_MULTIPLIER
   },
 
   { desc: "Set interval to a negative value.",
     interval: -1,
-    expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS
+    expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS * EXPIRE_AGGRESSIVITY_MULTIPLIER
   },
 
   { desc: "Set interval to 0.",
     interval: 0,
-    expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS
+    expectedTimerDelay: DEFAULT_TIMER_DELAY_SECONDS * EXPIRE_AGGRESSIVITY_MULTIPLIER
   },
 
   { desc: "Set interval to a large value.",
     interval: 100,
-    expectedTimerDelay: 100
+    expectedTimerDelay: 100 * EXPIRE_AGGRESSIVITY_MULTIPLIER
   },
 
 ];
 
 add_task(async function test() {
   // The pref should not exist by default.
   Assert.throws(() => getInterval());
 
   // Force the component, so it will start observing preferences.
   force_expiration_start();
 
   for (let currentTest of tests) {
+    currentTest = tests.shift();
     print(currentTest.desc);
     let promise = promiseTopicObserved("test-interval-changed");
     setInterval(currentTest.interval);
     let [, data] = await promise;
-    Assert.equal(data, currentTest.expectedTimerDelay * EXPIRE_AGGRESSIVITY_MULTIPLIER);
+    Assert.equal(data, currentTest.expectedTimerDelay);
   }
-
   clearInterval();
 });
-
--- a/toolkit/components/places/tests/unifiedcomplete/test_search_suggestions.js
+++ b/toolkit/components/places/tests/unifiedcomplete/test_search_suggestions.js
@@ -438,17 +438,17 @@ add_task(async function mixup_frecency()
         title: "low frecency 2" },
       { uri: NetUtil.newURI("http://example.com/lo1"),
         title: "low frecency 1" },
       { uri: NetUtil.newURI("http://example.com/lo0"),
         title: "low frecency 0" },
     ],
   });
 
-  // Change the results mixup.
+  // Change the "general" context mixup.
   Services.prefs.setCharPref("browser.urlbar.matchBuckets",
                              "suggestion:1,general:5,suggestion:1");
 
   // Do an unrestricted search to make sure everything appears in it, including
   // the visit and bookmark.
   await check_autocomplete({
     checkSorting: true,
     search: "frecency",
@@ -497,17 +497,75 @@ add_task(async function mixup_frecency()
         title: "low frecency 2" },
       { uri: NetUtil.newURI("http://example.com/lo1"),
         title: "low frecency 1" },
       { uri: NetUtil.newURI("http://example.com/lo0"),
         title: "low frecency 0" },
     ],
   });
 
+  // Change the "search" context mixup.
+  Services.prefs.setCharPref("browser.urlbar.matchBucketsSearch",
+                             "suggestion:2,general:4");
+
+  await check_autocomplete({
+    checkSorting: true,
+    search: "frecency",
+    searchParam: "enable-actions",
+    matches: [
+      makeSearchMatch("frecency", { engineName: ENGINE_NAME, heuristic: true }),
+      {
+        uri: makeActionURI(("searchengine"), {
+          engineName: ENGINE_NAME,
+          input: "frecency foo",
+          searchQuery: "frecency",
+          searchSuggestion: "frecency foo",
+        }),
+        title: ENGINE_NAME,
+        style: ["action", "searchengine"],
+        icon: "",
+      },
+      {
+        uri: makeActionURI(("searchengine"), {
+          engineName: ENGINE_NAME,
+          input: "frecency bar",
+          searchQuery: "frecency",
+          searchSuggestion: "frecency bar",
+        }),
+        title: ENGINE_NAME,
+        style: ["action", "searchengine"],
+        icon: "",
+      },
+      { uri: NetUtil.newURI("http://example.com/hi3"),
+        title: "high frecency 3",
+        style: [ "bookmark" ] },
+      { uri: NetUtil.newURI("http://example.com/hi2"),
+        title: "high frecency 2",
+        style: [ "bookmark" ] },
+      { uri: NetUtil.newURI("http://example.com/hi1"),
+        title: "high frecency 1",
+        style: [ "bookmark" ] },
+      { uri: NetUtil.newURI("http://example.com/hi0"),
+        title: "high frecency 0",
+        style: [ "bookmark" ] },
+      { uri: NetUtil.newURI("http://example.com/lo4"),
+        title: "low frecency 4" },
+      { uri: NetUtil.newURI("http://example.com/lo3"),
+        title: "low frecency 3" },
+      { uri: NetUtil.newURI("http://example.com/lo2"),
+        title: "low frecency 2" },
+      { uri: NetUtil.newURI("http://example.com/lo1"),
+        title: "low frecency 1" },
+      { uri: NetUtil.newURI("http://example.com/lo0"),
+        title: "low frecency 0" },
+    ],
+  });
+
   Services.prefs.clearUserPref("browser.urlbar.matchBuckets");
+  Services.prefs.clearUserPref("browser.urlbar.matchBucketsSearch");
   await cleanUpSuggestions();
 });
 
 add_task(async function prohibit_suggestions() {
   Services.prefs.setBoolPref(SUGGEST_PREF, true);
 
   await check_autocomplete({
     search: "localhost",
--- a/toolkit/components/places/tests/unit/test_history_clear.js
+++ b/toolkit/components/places/tests/unit/test_history_clear.js
@@ -1,41 +1,16 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 var mDBConn = DBConn();
 
-function promiseOnClearHistoryObserved() {
-  return new Promise(resolve => {
-
-    let historyObserver = {
-      onBeginUpdateBatch() {},
-      onEndUpdateBatch() {},
-      onVisit() {},
-      onTitleChanged() {},
-      onDeleteURI(aURI) {},
-      onPageChanged() {},
-      onDeleteVisits() {},
-
-      onClearHistory() {
-        PlacesUtils.history.removeObserver(this, false);
-        resolve();
-      },
-
-      QueryInterface: XPCOMUtils.generateQI([
-        Ci.nsINavHistoryObserver,
-      ])
-    }
-    PlacesUtils.history.addObserver(historyObserver);
-  });
-}
-
 add_task(async function test_history_clear() {
   await PlacesTestUtils.addVisits([
     { uri: uri("http://typed.mozilla.org/"),
       transition: TRANSITION_TYPED },
     { uri: uri("http://link.mozilla.org/"),
       transition: TRANSITION_LINK },
     { uri: uri("http://download.mozilla.org/"),
       transition: TRANSITION_DOWNLOAD },
@@ -73,24 +48,24 @@ add_task(async function test_history_cle
     { uri: uri("http://typed.mozilla.org/"),
       transition: TRANSITION_BOOKMARK },
     { uri: uri("http://frecency.mozilla.org/"),
       transition: TRANSITION_LINK },
   ]);
   await PlacesTestUtils.promiseAsyncUpdates();
 
   // Clear history and wait for the onClearHistory notification.
-  let promiseWaitClearHistory = promiseOnClearHistoryObserved();
+  let promiseClearHistory =
+    PlacesTestUtils.waitForNotification("onClearHistory", () => true, "history");
   PlacesUtils.history.clear();
-  await promiseWaitClearHistory;
+  await promiseClearHistory;
 
   // check browserHistory returns no entries
   do_check_eq(0, PlacesUtils.history.hasHistoryEntries);
 
-  await promiseTopicObserved(PlacesUtils.TOPIC_EXPIRATION_FINISHED);
   await PlacesTestUtils.promiseAsyncUpdates();
 
   // Check that frecency for not cleared items (bookmarks) has been converted
   // to -1.
   let stmt = mDBConn.createStatement(
     "SELECT h.id FROM moz_places h WHERE h.frecency > 0 ");
   do_check_false(stmt.executeStep());
   stmt.finalize();
--- a/toolkit/components/resistfingerprinting/nsRFPService.cpp
+++ b/toolkit/components/resistfingerprinting/nsRFPService.cpp
@@ -143,17 +143,29 @@ nsRFPService::UpdatePref()
     PR_SetEnv("TZ=UTC");
     JS::SetTimeResolutionUsec(kResolutionUSec);
   } else if (sInitialized) {
     JS::SetTimeResolutionUsec(0);
     // We will not touch the TZ value if 'privacy.resistFingerprinting' is false during
     // the time of initialization.
     if (!mInitialTZValue.IsEmpty()) {
       nsAutoCString tzValue = NS_LITERAL_CSTRING("TZ=") + mInitialTZValue;
-      PR_SetEnv(tzValue.get());
+      static char* tz = nullptr;
+
+      // If the tz has been set before, we free it first since it will be allocated
+      // a new value later.
+      if (tz) {
+        free(tz);
+      }
+      // PR_SetEnv() needs the input string been leaked intentionally, so
+      // we copy it here.
+      tz = ToNewCString(tzValue);
+      if (tz) {
+        PR_SetEnv(tz);
+      }
     } else {
 #if defined(XP_LINUX) || defined (XP_MACOSX)
       // For POSIX like system, we reset the TZ to the /etc/localtime, which is the
       // system timezone.
       PR_SetEnv("TZ=:/etc/localtime");
 #else
       // For Windows, we reset the TZ to an empty string. This will make Windows to use
       // its system timezone.