Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Tue, 20 Dec 2016 20:15:20 -0800
changeset 326707 c36fbe84042debef0a5d58b7fc88185b401762ce
parent 326706 20774bffb62a3c1fecf98ed8ad9ee1a861bcd9b7 (current diff)
parent 326640 70e65619c68e14a51f98e42133322affd6dd2a9f (diff)
child 326708 009bb9bc85e40dad6d22e0b0257f722bf7fd0927
child 326754 b5a511ec22c1a5b881babebba4e009991f23f964
child 326787 084a46effa8547c355940266cdab6b3b28e8416d
push id85010
push userphilringnalda@gmail.com
push dateWed, 21 Dec 2016 04:21:25 +0000
treeherdermozilla-inbound@009bb9bc85e4 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone53.0a1
first release with
nightly linux32
c36fbe84042d / 53.0a1 / 20161221030226 / files
nightly linux64
c36fbe84042d / 53.0a1 / 20161221030226 / files
nightly mac
c36fbe84042d / 53.0a1 / 20161221030226 / files
nightly win32
c36fbe84042d / 53.0a1 / 20161221030226 / files
nightly win64
c36fbe84042d / 53.0a1 / 20161221030226 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge MozReview-Commit-ID: EI1fU13SR79
dom/canvas/WebGL2ContextDraw.cpp
dom/webidl/moz.build
layout/reftests/css-grid/grid-percent-intrinsic-sizing-002-ref.html
layout/reftests/css-grid/grid-percent-intrinsic-sizing-002.html
python/mozbuild/mozbuild/vendor_rust.py
testing/web-platform/meta/MANIFEST.json
--- a/browser/base/content/contentSearchUI.js
+++ b/browser/base/content/contentSearchUI.js
@@ -60,20 +60,16 @@ function ContentSearchUIController(input
   this._hideSuggestions();
 
   this._getSearchEngines();
   this._getStrings();
 }
 
 ContentSearchUIController.prototype = {
 
-  // The timeout (ms) of the remote suggestions.  Corresponds to
-  // SearchSuggestionController.remoteTimeout.  Uses
-  // SearchSuggestionController's default timeout if falsey.
-  remoteTimeout: undefined,
   _oneOffButtons: [],
   // Setting up the one off buttons causes an uninterruptible reflow. If we
   // receive the list of engines while the newtab page is loading, this reflow
   // may regress performance - so we set this flag and only set up the buttons
   // if it's set when the suggestions table is actually opened.
   _pendingOneOffRefresh: undefined,
 
   get defaultEngine() {
@@ -711,17 +707,16 @@ ContentSearchUIController.prototype = {
   },
 
   _getSuggestions: function() {
     this._stickyInputValue = this.input.value;
     if (this.defaultEngine) {
       this._sendMsg("GetSuggestions", {
         engineName: this.defaultEngine.name,
         searchString: this.input.value,
-        remoteTimeout: this.remoteTimeout,
       });
     }
   },
 
   _clearSuggestionRows: function() {
     while (this._suggestionsList.firstElementChild) {
       this._suggestionsList.firstElementChild.remove();
     }
--- a/browser/base/content/test/general/browser_aboutHome.js
+++ b/browser/base/content/test/general/browser_aboutHome.js
@@ -311,19 +311,16 @@ add_task(function* () {
     // Add a test engine that provides suggestions and switch to it.
     let currEngine = Services.search.currentEngine;
     let engine = yield promiseNewEngine("searchSuggestionEngine.xml");
     let p = promiseContentSearchChange(browser, engine.name);
     Services.search.currentEngine = engine;
     yield p;
 
     yield ContentTask.spawn(browser, null, function* () {
-      // Avoid intermittent failures.
-      content.wrappedJSObject.gContentSearchController.remoteTimeout = 5000;
-
       // Type an X in the search input.
       let input = content.document.getElementById("searchText");
       input.focus();
     });
 
     yield BrowserTestUtils.synthesizeKey("x", {}, browser);
 
     yield ContentTask.spawn(browser, null, function* () {
--- a/browser/base/content/test/general/contentSearchUI.js
+++ b/browser/base/content/test/general/contentSearchUI.js
@@ -22,17 +22,16 @@ var messageHandlers = {
       new content.ContentSearchUIController(input, input.parentNode, "test", "test");
     content.addEventListener("ContentSearchService", function listener(aEvent) {
       if (aEvent.detail.type == "State" &&
           gController.defaultEngine.name == ENGINE_NAME) {
         content.removeEventListener("ContentSearchService", listener);
         ack("init");
       }
     });
-    gController.remoteTimeout = 5000;
   },
 
   key: function(arg) {
     let keyName = typeof(arg) == "string" ? arg : arg.key;
     content.synthesizeKey(keyName, arg.modifiers || {});
     let wait = arg.waitForSuggestions ? waitForSuggestions : cb => cb();
     wait(ack.bind(null, "key"));
   },
--- a/browser/base/content/test/newtab/browser_newtab_search.js
+++ b/browser/base/content/test/newtab/browser_newtab_search.js
@@ -120,21 +120,16 @@ add_task(function* () {
 
   // Add the engine that provides search suggestions and switch to it.
   let suggestionEngine = yield promiseNewSearchEngine(ENGINE_SUGGESTIONS);
   searchEventsPromise = promiseSearchEvents(["CurrentEngine"]);
   Services.search.currentEngine = suggestionEngine;
   yield searchEventsPromise;
   yield* checkCurrentEngine(ENGINE_SUGGESTIONS);
 
-  // Avoid intermittent failures.
-  yield ContentTask.spawn(gBrowser.selectedBrowser, {}, function* () {
-    content.gSearch._contentSearchController.remoteTimeout = 5000;
-  });
-
   // Type an X in the search input.  This is only a smoke test.  See
   // browser_searchSuggestionUI.js for comprehensive content search suggestion
   // UI tests.
   let suggestionsOpenPromise = new Promise(resolve => {
     mm.addMessageListener("test:newtab-suggestions-open", function onResponse(message) {
       mm.removeMessageListener("test:newtab-suggestions-open", onResponse);
       resolve();
     });
--- a/browser/modules/ContentSearch.jsm
+++ b/browser/modules/ContentSearch.jsm
@@ -36,17 +36,17 @@ const MAX_SUGGESTIONS = 6;
  *
  * Inbound messages have the following types:
  *
  *   AddFormHistoryEntry
  *     Adds an entry to the search form history.
  *     data: the entry, a string
  *   GetSuggestions
  *     Retrieves an array of search suggestions given a search string.
- *     data: { engineName, searchString, [remoteTimeout] }
+ *     data: { engineName, searchString }
  *   GetState
  *     Retrieves the current search engine state.
  *     data: null
  *   GetStrings
  *     Retrieves localized search UI strings.
  *     data: null
  *   ManageEngines
  *     Opens the search engine management window.
@@ -254,28 +254,27 @@ this.ContentSearch = {
       };
       win.openUILinkIn(submission.uri.spec, where, params);
     }
     win.BrowserSearch.recordSearchInTelemetry(engine, data.healthReportKey,
                                               { selection: data.selection });
     return;
   },
 
-  getSuggestions: Task.async(function* (engineName, searchString, browser, remoteTimeout = null) {
+  getSuggestions: Task.async(function* (engineName, searchString, browser) {
     let engine = Services.search.getEngineByName(engineName);
     if (!engine) {
       throw new Error("Unknown engine name: " + engineName);
     }
 
     let browserData = this._suggestionDataForBrowser(browser, true);
     let { controller } = browserData;
     let ok = SearchSuggestionController.engineOffersSuggestions(engine);
     controller.maxLocalResults = ok ? MAX_LOCAL_SUGGESTIONS : MAX_SUGGESTIONS;
     controller.maxRemoteResults = ok ? MAX_SUGGESTIONS : 0;
-    controller.remoteTimeout = remoteTimeout || undefined;
     let priv = PrivateBrowsingUtils.isBrowserPrivate(browser);
     // fetch() rejects its promise if there's a pending request, but since we
     // process our event queue serially, there's never a pending request.
     this._currentSuggestion = { controller: controller, target: browser };
     let suggestions = yield controller.fetch(searchString, priv, engine);
     this._currentSuggestion = null;
 
     // suggestions will be null if the request was cancelled
--- a/browser/modules/test/browser_ContentSearch.js
+++ b/browser/modules/test/browser_ContentSearch.js
@@ -1,20 +1,16 @@
 /* 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/. */
 
 const TEST_MSG = "ContentSearchTest";
 const CONTENT_SEARCH_MSG = "ContentSearch";
 const TEST_CONTENT_SCRIPT_BASENAME = "contentSearch.js";
 
-// This timeout is absurdly high to avoid random failures like bug 1087120.
-// That bug was reported when the timeout was 5 seconds, so let's try 10.
-const SUGGESTIONS_TIMEOUT = 10000;
-
 var gMsgMan;
 
 add_task(function* GetState() {
   yield addTab();
   gMsgMan.sendAsyncMessage(TEST_MSG, {
     type: "GetState",
   });
   let msg = yield waitForTestMsg("State");
@@ -187,17 +183,16 @@ add_task(function* GetSuggestions_AddFor
 
   // Send GetSuggestions using the test engine.  Its suggestions should appear
   // in the remote suggestions in the Suggestions response below.
   gMsgMan.sendAsyncMessage(TEST_MSG, {
     type: "GetSuggestions",
     data: {
       engineName: engine.name,
       searchString: searchStr,
-      remoteTimeout: SUGGESTIONS_TIMEOUT,
     },
   });
 
   // Check the Suggestions response.
   let msg = yield waitForTestMsg("Suggestions");
   checkMsg(msg, {
     type: "Suggestions",
     data: {
@@ -223,17 +218,16 @@ add_task(function* GetSuggestions_AddFor
   yield deferred.promise;
 
   // Send GetSuggestions again.
   gMsgMan.sendAsyncMessage(TEST_MSG, {
     type: "GetSuggestions",
     data: {
       engineName: engine.name,
       searchString: searchStr,
-      remoteTimeout: SUGGESTIONS_TIMEOUT,
     },
   });
 
   // The formHistory suggestions in the Suggestions response should be empty.
   msg = yield waitForTestMsg("Suggestions");
   checkMsg(msg, {
     type: "Suggestions",
     data: {
--- a/browser/modules/test/head.js
+++ b/browser/modules/test/head.js
@@ -62,20 +62,16 @@ function checkKeyedScalar(scalars, scala
  *        The browser that contains the content.
  * @param {String} text
  *        The string to write in the search field.
  * @param {String} fieldName
  *        The name of the field to write to.
  */
 let typeInSearchField = Task.async(function* (browser, text, fieldName) {
   yield ContentTask.spawn(browser, [fieldName, text], function* ([contentFieldName, contentText]) {
-    // Avoid intermittent failures.
-    if (contentFieldName === "searchText") {
-      content.wrappedJSObject.gContentSearchController.remoteTimeout = 5000;
-    }
     // Put the focus on the search box.
     let searchInput = content.document.getElementById(contentFieldName);
     searchInput.focus();
     searchInput.value = contentText;
   });
 });
 
 /**
new file mode 100644
--- /dev/null
+++ b/dom/base/OrderedTimeoutIterator.h
@@ -0,0 +1,187 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_OrderedTimeoutIterator_h__
+#define mozilla_dom_OrderedTimeoutIterator_h__
+
+#include "mozilla/RefPtr.h"
+#include "mozilla/dom/Timeout.h"
+#include "mozilla/dom/TimeoutManager.h"
+
+namespace mozilla {
+namespace dom {
+
+// This class implements and iterator which iterates the normal and tracking
+// timeouts lists simultaneously in the mWhen order.
+class MOZ_STACK_CLASS OrderedTimeoutIterator final {
+public:
+  typedef TimeoutManager::Timeouts Timeouts;
+  typedef Timeouts::TimeoutList    TimeoutList;
+
+  // Passing null for aNormalStopAt or aTrackingStopAt means that the
+  // corresponding timeout list should be iterated all the way to the end.
+  OrderedTimeoutIterator(Timeouts& aNormalTimeouts,
+                         Timeouts& aTrackingTimeouts,
+                         Timeout* aNormalStopAt,
+                         Timeout* aTrackingStopAt)
+    : mNormalTimeouts(aNormalTimeouts.mTimeoutList),
+      mTrackingTimeouts(aTrackingTimeouts.mTimeoutList),
+      mNormalIter(mNormalTimeouts.getFirst()),
+      mTrackingIter(mTrackingTimeouts.getFirst()),
+      mNormalStopAt(aNormalStopAt),
+      mTrackingStopAt(aTrackingStopAt),
+      mKind(Kind::None),
+      mUpdateIteratorCalled(true)
+  {
+  }
+
+  // Return the current timeout and move to the next one.
+  // Unless this is the first time calling Next(), you must call
+  // UpdateIterator() before calling this method.
+  Timeout* Next()
+  {
+    MOZ_ASSERT(mUpdateIteratorCalled);
+    MOZ_ASSERT_IF(mNormalIter && mNormalIter != mNormalStopAt,
+                  mNormalIter->isInList());
+    MOZ_ASSERT_IF(mTrackingIter && mTrackingIter != mTrackingStopAt,
+                  mTrackingIter->isInList());
+
+    mUpdateIteratorCalled = false;
+    mKind = Kind::None;
+    Timeout* timeout = nullptr;
+    if (mNormalIter == mNormalStopAt) {
+      if (mTrackingIter == mTrackingStopAt) {
+        // We have reached the end of both lists.  Bail out!
+        return nullptr;
+      } else {
+        // We have reached the end of the normal timeout list, select the next
+        // tracking timeout.
+        timeout = mTrackingIter;
+        mKind = Kind::Tracking;
+      }
+    } else if (mTrackingIter == mTrackingStopAt) {
+      // We have reached the end of the tracking timeout list, select the next
+      // normal timeout.
+      timeout = mNormalIter;
+      mKind = Kind::Normal;
+    } else {
+      // If we have a normal and a tracking timer, return the one with the
+      // smaller mWhen (and prefer the timeout with a lower ID in case they are
+      // equal.) Otherwise, return whichever iterator has an item left,
+      // preferring a non-tracking timeout again.  Note that in practice, even
+      // if a web page calls setTimeout() twice in a row, it should get
+      // different mWhen values, so in practice we shouldn't fall back to
+      // comparing timeout IDs.
+      if (mNormalIter && mTrackingIter &&
+          mNormalIter != mNormalStopAt &&
+          mTrackingIter != mTrackingStopAt &&
+          (mTrackingIter->mWhen < mNormalIter->mWhen ||
+           (mTrackingIter->mWhen == mNormalIter->mWhen &&
+            mTrackingIter->mTimeoutId < mNormalIter->mTimeoutId))) {
+        timeout = mTrackingIter;
+        mKind = Kind::Tracking;
+      } else if (mNormalIter && mNormalIter != mNormalStopAt) {
+        timeout = mNormalIter;
+        mKind = Kind::Normal;
+      } else if (mTrackingIter && mTrackingIter != mTrackingStopAt) {
+        timeout = mTrackingIter;
+        mKind = Kind::Tracking;
+      }
+    }
+    if (!timeout) {
+      // We didn't find any suitable iterator.  This can happen for example
+      // when getNext() in UpdateIterator() returns nullptr and then Next()
+      // gets called.  Bail out!
+      return nullptr;
+    }
+
+    MOZ_ASSERT(mKind != Kind::None);
+
+    // Record the current timeout we just found.
+    mCurrent = timeout;
+    MOZ_ASSERT(mCurrent);
+
+    return mCurrent;
+  }
+
+  // Prepare the iterator for the next call to Next().
+  // This method can be called as many times as needed.  Calling this more than
+  // once is helpful in cases where we expect the timeouts list has been
+  // modified before we got a chance to call Next().
+  void UpdateIterator()
+  {
+    MOZ_ASSERT(mKind != Kind::None);
+    // Update the winning iterator to point to the next element.  Also check to
+    // see if the other iterator is still valid, otherwise reset it to the
+    // beginning of the list.  This is needed in case a timeout handler removes
+    // the timeout pointed to from one of our iterators.
+    if (mKind == Kind::Normal) {
+      mNormalIter = mCurrent->getNext();
+      if (mTrackingIter && mTrackingIter != mTrackingStopAt &&
+          !mTrackingIter->isInList()) {
+        mTrackingIter = mTrackingTimeouts.getFirst();
+      }
+    } else {
+      mTrackingIter = mCurrent->getNext();
+      if (mNormalIter && mNormalIter != mNormalStopAt &&
+          !mNormalIter->isInList()) {
+        mNormalIter = mNormalTimeouts.getFirst();
+      }
+    }
+
+    mUpdateIteratorCalled = true;
+  }
+
+  // This function resets the iterator to a defunct state.  It should only be
+  // used when we want to forcefully sever all of the strong references this
+  // class holds.
+  void Clear()
+  {
+    // Release all strong references.
+    mNormalIter = nullptr;
+    mTrackingIter = nullptr;
+    mCurrent = nullptr;
+    mKind = Kind::None;
+    mUpdateIteratorCalled = true;
+  }
+
+  // Returns true if the previous call to Next() picked a normal timeout.
+  // Cannot be called before Next() has been called.  Note that the result of
+  // this method is only affected by Next() and not UpdateIterator(), so calling
+  // UpdateIterator() before calling this is allowed.
+  bool PickedNormalIter() const
+  {
+    MOZ_ASSERT(mKind != Kind::None);
+    return mKind == Kind::Normal;
+  }
+
+  // Returns true if the previous call to Next() picked a tracking timeout.
+  // Cannot be called before Next() has been called.  Note that the result of
+  // this method is only affected by Next() and not UpdateIterator(), so calling
+  // UpdateIterator() before calling this is allowed.
+  bool PickedTrackingIter() const
+  {
+    MOZ_ASSERT(mKind != Kind::None);
+    return mKind == Kind::Tracking;
+  }
+
+private:
+  TimeoutList& mNormalTimeouts;          // The list of normal timeouts.
+  TimeoutList& mTrackingTimeouts;        // The list of tracking timeouts.
+  RefPtr<Timeout> mNormalIter;           // The iterator over the normal timeout list.
+  RefPtr<Timeout> mTrackingIter;         // The iterator over the tracking timeout list.
+  void* mNormalStopAt;                   // Where to stop iterating the normal list at.
+  void* mTrackingStopAt;                 // Where to stop iterating the tracking list at.
+  RefPtr<Timeout> mCurrent;              // The current timeout that Next() just found.
+  enum class Kind { Normal, Tracking, None };
+  Kind mKind;                            // The kind of iterator picked the last time.
+  DebugOnly<bool> mUpdateIteratorCalled; // Whether we have called UpdateIterator() before calling Next().
+};
+
+}
+}
+
+#endif
--- a/dom/base/Timeout.cpp
+++ b/dom/base/Timeout.cpp
@@ -93,20 +93,20 @@ Timeout::InitTimer(nsIEventTarget* aTarg
     MOZ_ALWAYS_SUCCEEDS(mTimer->Cancel());
     MOZ_ALWAYS_SUCCEEDS(mTimer->SetTarget(aTarget));
   }
 
   return mTimer->InitWithNameableFuncCallback(
     TimerCallback, this, aDelay, nsITimer::TYPE_ONE_SHOT, TimerNameCallback);
 }
 
-// Return true if this timeout has a refcount of 1. This is used to check
+// Return true if this timeout has a refcount of aCount. This is used to check
 // that dummy_timeout doesn't leak from nsGlobalWindow::RunTimeout.
 #ifdef DEBUG
 bool
-Timeout::HasRefCntOne() const
+Timeout::HasRefCnt(uint32_t aCount) const
 {
-  return mRefCnt.get() == 1;
+  return mRefCnt.get() == aCount;
 }
 #endif // DEBUG
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/base/Timeout.h
+++ b/dom/base/Timeout.h
@@ -40,17 +40,17 @@ public:
   // The target may be specified to use a particular event queue for the
   // resulting timer runnable.  A nullptr target will result in the
   // default main thread being used.
   nsresult InitTimer(nsIEventTarget* aTarget, uint32_t aDelay);
 
   enum class Reason { eTimeoutOrInterval, eIdleCallbackTimeout };
 
 #ifdef DEBUG
-  bool HasRefCntOne() const;
+  bool HasRefCnt(uint32_t aCount) const;
 #endif // DEBUG
 
   // Window for which this timeout fires
   RefPtr<nsGlobalWindow> mWindow;
 
   // The actual timer object
   nsCOMPtr<nsITimer> mTimer;
 
--- a/dom/base/TimeoutManager.cpp
+++ b/dom/base/TimeoutManager.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "TimeoutManager.h"
 #include "nsGlobalWindow.h"
 #include "mozilla/ThrottledEventQueue.h"
 #include "mozilla/TimeStamp.h"
 #include "nsITimeoutHandler.h"
 #include "mozilla/dom/TabGroup.h"
+#include "OrderedTimeoutIterator.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 static int32_t              gRunningTimeoutDepth       = 0;
 
 // The default shortest interval/timeout we permit
 #define DEFAULT_MIN_TIMEOUT_VALUE 4 // 4ms
@@ -28,16 +29,23 @@ TimeoutManager::DOMMinTimeoutValue() con
   // Don't use the background timeout value when there are audio contexts
   // present, so that background audio can keep running smoothly. (bug 1181073)
   bool isBackground = !mWindow.AsInner()->HasAudioContexts() &&
     mWindow.IsBackgroundInternal();
   return
     std::max(isBackground ? gMinBackgroundTimeoutValue : gMinTimeoutValue, value);
 }
 
+#define TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY 0 // Consider all timeouts coming from tracking scripts as tracking
+// These strategies are useful for testing.
+#define ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY        1 // Consider all timeouts as normal
+#define ALTERNATE_TIMEOUT_BUCKETING_STRATEGY         2 // Put every other timeout in the list of tracking timeouts
+#define RANDOM_TIMEOUT_BUCKETING_STRATEGY            3 // Put timeouts into either the normal or tracking timeouts list randomly
+static int32_t gTimeoutBucketingStrategy = 0;
+
 // The number of nested timeouts before we start clamping. HTML5 says 1, WebKit
 // uses 5.
 #define DOM_CLAMP_TIMEOUT_NESTING_LEVEL 5
 
 // The longest interval (as PRIntervalTime) we permit, or that our
 // timer code can handle, really. See DELAY_INTERVAL_LIMIT in
 // nsTimerImpl.h for details.
 #define DOM_MAX_TIMEOUT_VALUE    DELAY_INTERVAL_LIMIT
@@ -107,16 +115,19 @@ void
 TimeoutManager::Initialize()
 {
   Preferences::AddIntVarCache(&gMinTimeoutValue,
                               "dom.min_timeout_value",
                               DEFAULT_MIN_TIMEOUT_VALUE);
   Preferences::AddIntVarCache(&gMinBackgroundTimeoutValue,
                               "dom.min_background_timeout_value",
                               DEFAULT_MIN_BACKGROUND_TIMEOUT_VALUE);
+  Preferences::AddIntVarCache(&gTimeoutBucketingStrategy,
+                              "dom.timeout_bucketing_strategy",
+                              TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY);
 }
 
 uint32_t
 TimeoutManager::GetTimeoutId(Timeout::Reason aReason)
 {
   switch (aReason) {
     case Timeout::Reason::eIdleCallbackTimeout:
       return ++mIdleCallbackTimeoutCounter;
@@ -128,17 +139,18 @@ TimeoutManager::GetTimeoutId(Timeout::Re
 
 nsresult
 TimeoutManager::SetTimeout(nsITimeoutHandler* aHandler,
                            int32_t interval, bool aIsInterval,
                            Timeout::Reason aReason, int32_t* aReturn)
 {
   // If we don't have a document (we could have been unloaded since
   // the call to setTimeout was made), do nothing.
-  if (!mWindow.GetExtantDoc()) {
+  nsCOMPtr<nsIDocument> doc = mWindow.GetExtantDoc();
+  if (!doc) {
     return NS_OK;
   }
 
   // Disallow negative intervals.  If aIsInterval also disallow 0,
   // because we use that as a "don't repeat" flag.
   interval = std::max(aIsInterval ? 1 : 0, interval);
 
   // Make sure we don't proceed with an interval larger than our timer
@@ -223,31 +235,57 @@ TimeoutManager::SetTimeout(nsITimeoutHan
     // This is checking |interval|, not realInterval, on purpose,
     // because our lower bound for |realInterval| could be pretty high
     // in some cases.
     if (interval <= delay) {
       timeout->mPopupState = mWindow.GetPopupControlState();
     }
   }
 
-  mTimeouts.Insert(timeout, mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
-                                               : Timeouts::SortBy::TimeWhen);
+  bool isTracking = false;
+  switch (gTimeoutBucketingStrategy) {
+  default:
+  case TRACKING_SEPARATE_TIMEOUT_BUCKETING_STRATEGY: {
+    const char* filename = nullptr;
+    uint32_t dummyLine = 0, dummyColumn = 0;
+    aHandler->GetLocation(&filename, &dummyLine, &dummyColumn);
+    isTracking = doc->IsScriptTracking(nsDependentCString(filename));
+    break;
+  }
+  case ALL_NORMAL_TIMEOUT_BUCKETING_STRATEGY:
+    // isTracking is already false!
+    break;
+  case ALTERNATE_TIMEOUT_BUCKETING_STRATEGY:
+    isTracking = (mTimeoutIdCounter % 2) == 0;
+    break;
+  case RANDOM_TIMEOUT_BUCKETING_STRATEGY:
+    isTracking = (rand() % 2) == 0;
+    break;
+  }
+
+  Timeouts::SortBy sort(mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
+                                           : Timeouts::SortBy::TimeWhen);
+  if (isTracking) {
+    mTrackingTimeouts.Insert(timeout, sort);
+  } else {
+    mNormalTimeouts.Insert(timeout, sort);
+  }
 
   timeout->mTimeoutId = GetTimeoutId(aReason);
   *aReturn = timeout->mTimeoutId;
 
   return NS_OK;
 }
 
 void
 TimeoutManager::ClearTimeout(int32_t aTimerId, Timeout::Reason aReason)
 {
   uint32_t timerId = (uint32_t)aTimerId;
 
-  ForEachTimeoutAbortable([&](Timeout* aTimeout) {
+  ForEachUnorderedTimeoutAbortable([&](Timeout* aTimeout) {
     if (aTimeout->mTimeoutId == timerId && aTimeout->mReason == aReason) {
       if (aTimeout->mRunning) {
         /* We're running from inside the aTimeout. Mark this
            aTimeout for deferred deletion by the code in
            RunTimeout() */
         aTimeout->mIsInterval = false;
       }
       else {
@@ -271,19 +309,21 @@ void
 TimeoutManager::RunTimeout(Timeout* aTimeout)
 {
   if (mWindow.IsSuspended()) {
     return;
   }
 
   NS_ASSERTION(!mWindow.IsFrozen(), "Timeout running on a window in the bfcache!");
 
-  Timeout* nextTimeout;
-  Timeout* last_expired_timeout;
-  Timeout* last_insertion_point;
+  Timeout* last_expired_normal_timeout = nullptr;
+  Timeout* last_expired_tracking_timeout = nullptr;
+  bool     last_expired_timeout_is_normal = false;
+  Timeout* last_normal_insertion_point = nullptr;
+  Timeout* last_tracking_insertion_point = nullptr;
   uint32_t firingDepth = mTimeoutFiringDepth + 1;
 
   // Make sure that the window and the script context don't go away as
   // a result of running timeouts
   nsCOMPtr<nsIScriptGlobalObject> windowKungFuDeathGrip(&mWindow);
   // Silence the static analysis error about windowKungFuDeathGrip.  Accessing
   // members of mWindow here is safe, because the lifetime of TimeoutManager is
   // the same as the lifetime of the containing nsGlobalWindow.
@@ -307,140 +347,233 @@ TimeoutManager::RunTimeout(Timeout* aTim
   }
 
   // The timeout list is kept in deadline order. Discover the latest timeout
   // whose deadline has expired. On some platforms, native timeout events fire
   // "early", but we handled that above by setting deadline to aTimeout->mWhen
   // if the timer fired early.  So we can stop walking if we get to timeouts
   // whose mWhen is greater than deadline, since once that happens we know
   // nothing past that point is expired.
-  last_expired_timeout = nullptr;
-  for (Timeout* timeout = mTimeouts.GetFirst();
-       timeout && timeout->mWhen <= deadline;
-       timeout = timeout->getNext()) {
-    if (timeout->mFiringDepth == 0) {
-      // Mark any timeouts that are on the list to be fired with the
-      // firing depth so that we can reentrantly run timeouts
-      timeout->mFiringDepth = firingDepth;
-      last_expired_timeout = timeout;
-
-      // Run available timers until we see our target timer.  After
-      // that, however, stop coalescing timers so we can yield the
-      // main thread.  Further timers that are ready will get picked
-      // up by their own nsITimer runnables when they execute.
-      //
-      // For chrome windows, however, we do coalesce all timers and
-      // do not yield the main thread.  This is partly because we
-      // trust chrome windows not to misbehave and partly because a
-      // number of browser chrome tests have races that depend on this
-      // coalescing.
-      if (timeout == aTimeout && !mWindow.IsChromeWindow()) {
+  {
+    // Use a nested scope in order to make sure the strong references held by
+    // the iterator are freed after the loop.
+    OrderedTimeoutIterator expiredIter(mNormalTimeouts,
+                                       mTrackingTimeouts,
+                                       nullptr,
+                                       nullptr);
+    while (true) {
+      Timeout* timeout = expiredIter.Next();
+      if (!timeout || timeout->mWhen > deadline) {
         break;
       }
+
+      if (timeout->mFiringDepth == 0) {
+        // Mark any timeouts that are on the list to be fired with the
+        // firing depth so that we can reentrantly run timeouts
+        timeout->mFiringDepth = firingDepth;
+        last_expired_timeout_is_normal = expiredIter.PickedNormalIter();
+        if (last_expired_timeout_is_normal) {
+          last_expired_normal_timeout = timeout;
+        } else {
+          last_expired_tracking_timeout = timeout;
+        }
+
+        // Run available timers until we see our target timer.  After
+        // that, however, stop coalescing timers so we can yield the
+        // main thread.  Further timers that are ready will get picked
+        // up by their own nsITimer runnables when they execute.
+        //
+        // For chrome windows, however, we do coalesce all timers and
+        // do not yield the main thread.  This is partly because we
+        // trust chrome windows not to misbehave and partly because a
+        // number of browser chrome tests have races that depend on this
+        // coalescing.
+        if (timeout == aTimeout && !mWindow.IsChromeWindow()) {
+          break;
+        }
+      }
+
+      expiredIter.UpdateIterator();
     }
   }
 
   // Maybe the timeout that the event was fired for has been deleted
   // and there are no others timeouts with deadlines that make them
   // eligible for execution yet. Go away.
-  if (!last_expired_timeout) {
+  if (!last_expired_normal_timeout && !last_expired_tracking_timeout) {
     return;
   }
 
   // Insert a dummy timeout into the list of timeouts between the
   // portion of the list that we are about to process now and those
   // timeouts that will be processed in a future call to
   // win_run_timeout(). This dummy timeout serves as the head of the
   // list for any timeouts inserted as a result of running a timeout.
-  RefPtr<Timeout> dummy_timeout = new Timeout();
-  dummy_timeout->mFiringDepth = firingDepth;
-  dummy_timeout->mWhen = now;
-  last_expired_timeout->setNext(dummy_timeout);
-  RefPtr<Timeout> timeoutExtraRef(dummy_timeout);
+  RefPtr<Timeout> dummy_normal_timeout = new Timeout();
+  dummy_normal_timeout->mFiringDepth = firingDepth;
+  dummy_normal_timeout->mWhen = now;
+  if (last_expired_timeout_is_normal) {
+    last_expired_normal_timeout->setNext(dummy_normal_timeout);
+  }
+
+  RefPtr<Timeout> dummy_tracking_timeout = new Timeout();
+  dummy_tracking_timeout->mFiringDepth = firingDepth;
+  dummy_tracking_timeout->mWhen = now;
+  if (!last_expired_timeout_is_normal) {
+    last_expired_tracking_timeout->setNext(dummy_tracking_timeout);
+  }
+
+  RefPtr<Timeout> timeoutExtraRef1(dummy_normal_timeout);
+  RefPtr<Timeout> timeoutExtraRef2(dummy_tracking_timeout);
 
-  last_insertion_point = mTimeouts.InsertionPoint();
-  // If we ever start setting insertion point to a non-dummy timeout, the logic
-  // in ResetTimersForThrottleReduction will need to change.
-  mTimeouts.SetInsertionPoint(dummy_timeout);
+  // Now we need to search the normal and tracking timer list at the same
+  // time to run the timers in the scheduled order.
 
-  for (Timeout* timeout = mTimeouts.GetFirst();
-       timeout != dummy_timeout && !mWindow.IsFrozen();
-       timeout = nextTimeout) {
-    nextTimeout = timeout->getNext();
+  last_normal_insertion_point = mNormalTimeouts.InsertionPoint();
+  if (last_expired_timeout_is_normal) {
+    // If we ever start setting insertion point to a non-dummy timeout, the logic
+    // in ResetTimersForThrottleReduction will need to change.
+    mNormalTimeouts.SetInsertionPoint(dummy_normal_timeout);
+  }
+
+  last_tracking_insertion_point = mTrackingTimeouts.InsertionPoint();
+  if (!last_expired_timeout_is_normal) {
+    // If we ever start setting mTrackingTimeoutInsertionPoint to a non-dummy timeout,
+    // the logic in ResetTimersForThrottleReduction will need to change.
+    mTrackingTimeouts.SetInsertionPoint(dummy_tracking_timeout);
+  }
 
-    if (timeout->mFiringDepth != firingDepth) {
-      // We skip the timeout since it's on the list to run at another
-      // depth.
-
-      continue;
-    }
+  // We stop iterating each list when we go past the last expired timeout from
+  // that list that we have observed above.  That timeout will either be the
+  // dummy timeout for the list that the last expired timeout came from, or it
+  // will be the next item after the last timeout we looked at (or nullptr if
+  // we have exhausted the entire list while looking for the last expired
+  // timeout).
+  {
+    // Use a nested scope in order to make sure the strong references held by
+    // the iterator are freed after the loop.
+    OrderedTimeoutIterator runIter(mNormalTimeouts,
+                                   mTrackingTimeouts,
+                                   last_expired_normal_timeout ?
+                                     last_expired_normal_timeout->getNext() :
+                                     nullptr,
+                                   last_expired_tracking_timeout ?
+                                     last_expired_tracking_timeout->getNext() :
+                                     nullptr);
+    while (!mWindow.IsFrozen()) {
+      Timeout* timeout = runIter.Next();
+      MOZ_ASSERT(timeout != dummy_normal_timeout &&
+                 timeout != dummy_tracking_timeout,
+                 "We should have stopped iterating before getting to the dummy timeout");
+      if (!timeout) {
+        // We have run out of timeouts!
+        break;
+      }
+      runIter.UpdateIterator();
 
-    if (mWindow.IsSuspended()) {
-      // Some timer did suspend us. Make sure the
-      // rest of the timers get executed later.
-      timeout->mFiringDepth = 0;
-      continue;
-    }
-
-    // The timeout is on the list to run at this depth, go ahead and
-    // process it.
-
-    // Get the script context (a strong ref to prevent it going away)
-    // for this timeout and ensure the script language is enabled.
-    nsCOMPtr<nsIScriptContext> scx = mWindow.GetContextInternal();
+      if (timeout->mFiringDepth != firingDepth) {
+        // We skip the timeout since it's on the list to run at another
+        // depth.
+        continue;
+      }
 
-    if (!scx) {
-      // No context means this window was closed or never properly
-      // initialized for this language.
-      continue;
-    }
+      if (mWindow.IsSuspended()) {
+        // Some timer did suspend us. Make sure the
+        // rest of the timers get executed later.
+        timeout->mFiringDepth = 0;
+        continue;
+      }
+
+      // The timeout is on the list to run at this depth, go ahead and
+      // process it.
 
-    // This timeout is good to run
-    bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
+      // Get the script context (a strong ref to prevent it going away)
+      // for this timeout and ensure the script language is enabled.
+      nsCOMPtr<nsIScriptContext> scx = mWindow.GetContextInternal();
+
+      if (!scx) {
+        // No context means this window was closed or never properly
+        // initialized for this language.
+        continue;
+      }
+
+      // This timeout is good to run
+      bool timeout_was_cleared = mWindow.RunTimeoutHandler(timeout, scx);
+
+      if (timeout_was_cleared) {
+        // Make sure the iterator isn't holding any Timeout objects alive.
+        runIter.Clear();
 
-    if (timeout_was_cleared) {
-      // The running timeout's window was cleared, this means that
-      // ClearAllTimeouts() was called from a *nested* call, possibly
-      // through a timeout that fired while a modal (to this window)
-      // dialog was open or through other non-obvious paths.
-      MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
-      Unused << timeoutExtraRef.forget().take();
+        // The running timeout's window was cleared, this means that
+        // ClearAllTimeouts() was called from a *nested* call, possibly
+        // through a timeout that fired while a modal (to this window)
+        // dialog was open or through other non-obvious paths.
+        // Note that if the last expired timeout corresponding to each list
+        // is null, then we should expect a refcount of two, since the
+        // dummy timeout for this queue was never injected into it, and the
+        // corresponding timeoutExtraRef variable hasn't been cleared yet.
+        if (last_expired_timeout_is_normal) {
+          MOZ_ASSERT(dummy_normal_timeout->HasRefCnt(1), "dummy_normal_timeout may leak");
+          MOZ_ASSERT(dummy_tracking_timeout->HasRefCnt(2), "dummy_tracking_timeout may leak");
+          Unused << timeoutExtraRef1.forget().take();
+        } else {
+          MOZ_ASSERT(dummy_normal_timeout->HasRefCnt(2), "dummy_normal_timeout may leak");
+          MOZ_ASSERT(dummy_tracking_timeout->HasRefCnt(1), "dummy_tracking_timeout may leak");
+          Unused << timeoutExtraRef2.forget().take();
+        }
+
+        mNormalTimeouts.SetInsertionPoint(last_normal_insertion_point);
+        mTrackingTimeouts.SetInsertionPoint(last_tracking_insertion_point);
+
+        return;
+      }
 
-      mTimeouts.SetInsertionPoint(last_insertion_point);
+      // If we have a regular interval timer, we re-schedule the
+      // timeout, accounting for clock drift.
+      bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
+
+      // Running a timeout can cause another timeout to be deleted, so
+      // we need to reset the pointer to the following timeout.
+      runIter.UpdateIterator();
+
+      timeout->remove();
 
-      return;
+      if (needsReinsertion) {
+        // Insert interval timeout onto the corresponding list sorted in
+        // deadline order. AddRefs timeout.
+        if (runIter.PickedTrackingIter()) {
+          mTrackingTimeouts.Insert(timeout,
+                                   mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
+                                                      : Timeouts::SortBy::TimeWhen);
+        } else {
+          mNormalTimeouts.Insert(timeout,
+                                 mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
+                                                    : Timeouts::SortBy::TimeWhen);
+        }
+      }
+
+      // Release the timeout struct since it's possibly out of the list
+      timeout->Release();
     }
-
-    // If we have a regular interval timer, we re-schedule the
-    // timeout, accounting for clock drift.
-    bool needsReinsertion = RescheduleTimeout(timeout, now, !aTimeout);
-
-    // Running a timeout can cause another timeout to be deleted, so
-    // we need to reset the pointer to the following timeout.
-    nextTimeout = timeout->getNext();
-
-    timeout->remove();
-
-    if (needsReinsertion) {
-      // Insert interval timeout onto list sorted in deadline order.
-      // AddRefs timeout.
-      mTimeouts.Insert(timeout, mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
-                                                   : Timeouts::SortBy::TimeWhen);
-    }
-
-    // Release the timeout struct since it's possibly out of the list
-    timeout->Release();
   }
 
   // Take the dummy timeout off the head of the list
-  dummy_timeout->remove();
-  timeoutExtraRef = nullptr;
-  MOZ_ASSERT(dummy_timeout->HasRefCntOne(), "dummy_timeout may leak");
+  if (dummy_normal_timeout->isInList()) {
+    dummy_normal_timeout->remove();
+  }
+  timeoutExtraRef1 = nullptr;
+  MOZ_ASSERT(dummy_normal_timeout->HasRefCnt(1), "dummy_normal_timeout may leak");
+  if (dummy_tracking_timeout->isInList()) {
+    dummy_tracking_timeout->remove();
+  }
+  timeoutExtraRef2 = nullptr;
+  MOZ_ASSERT(dummy_tracking_timeout->HasRefCnt(1), "dummy_tracking_timeout may leak");
 
-  mTimeouts.SetInsertionPoint(last_insertion_point);
+  mNormalTimeouts.SetInsertionPoint(last_normal_insertion_point);
+  mTrackingTimeouts.SetInsertionPoint(last_tracking_insertion_point);
 
   MaybeApplyBackPressure();
 }
 
 void
 TimeoutManager::MaybeApplyBackPressure()
 {
   MOZ_ASSERT(NS_IsMainThread());
@@ -627,23 +760,32 @@ nsresult
 TimeoutManager::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS)
 {
   MOZ_ASSERT(aPreviousThrottleDelayMS > 0);
 
   if (mWindow.IsFrozen() || mWindow.IsSuspended()) {
     return NS_OK;
   }
 
+  auto minTimeout = DOMMinTimeoutValue();
   Timeouts::SortBy sortBy = mWindow.IsFrozen() ? Timeouts::SortBy::TimeRemaining
                                                : Timeouts::SortBy::TimeWhen;
 
-  return mTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
-                                                   DOMMinTimeoutValue(),
-                                                   sortBy,
-                                                   mWindow.GetThrottledEventQueue());
+  nsresult rv = mNormalTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
+                                                                minTimeout,
+                                                                sortBy,
+                                                                mWindow.GetThrottledEventQueue());
+  NS_ENSURE_SUCCESS(rv, rv);
+  rv = mTrackingTimeouts.ResetTimersForThrottleReduction(aPreviousThrottleDelayMS,
+                                                         minTimeout,
+                                                         sortBy,
+                                                         mWindow.GetThrottledEventQueue());
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  return NS_OK;
 }
 
 nsresult
 TimeoutManager::Timeouts::ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS,
                                                           int32_t aMinTimeoutValueMS,
                                                           SortBy aSortBy,
                                                           ThrottledEventQueue* aQueue)
 {
@@ -731,17 +873,17 @@ TimeoutManager::Timeouts::ResetTimersFor
   return NS_OK;
 }
 
 void
 TimeoutManager::ClearAllTimeouts()
 {
   bool seenRunningTimeout = false;
 
-  ForEachTimeout([&](Timeout* aTimeout) {
+  ForEachUnorderedTimeout([&](Timeout* aTimeout) {
     /* If RunTimeout() is higher up on the stack for this
        window, e.g. as a result of document.write from a timeout,
        then we need to reset the list insertion point for
        newly-created timeouts in case the user adds a timeout,
        before we pop the stack back to RunTimeout. */
     if (mRunningTimeout == aTimeout) {
       seenRunningTimeout = true;
     }
@@ -759,21 +901,23 @@ TimeoutManager::ClearAllTimeouts()
     // cleared and taken out of the list of timeouts
     aTimeout->mCleared = true;
 
     // Drop the count since we're removing it from the list.
     aTimeout->Release();
   });
 
   if (seenRunningTimeout) {
-    mTimeouts.SetInsertionPoint(nullptr);
+    mNormalTimeouts.SetInsertionPoint(nullptr);
+    mTrackingTimeouts.SetInsertionPoint(nullptr);
   }
 
   // Clear out our list
-  mTimeouts.Clear();
+  mNormalTimeouts.Clear();
+  mTrackingTimeouts.Clear();
 }
 
 void
 TimeoutManager::Timeouts::Insert(Timeout* aTimeout, SortBy aSortBy)
 {
   // Start at mLastTimeout and go backwards.  Don't go further than insertion
   // point, though.  This optimizes for the common case of insertion at the end.
   Timeout* prevSibling;
@@ -821,27 +965,27 @@ TimeoutManager::EndRunningTimeout(Timeou
   --gRunningTimeoutDepth;
 
   mRunningTimeout = aTimeout;
 }
 
 void
 TimeoutManager::UnmarkGrayTimers()
 {
-  ForEachTimeout([](Timeout* aTimeout) {
+  ForEachUnorderedTimeout([](Timeout* aTimeout) {
     if (aTimeout->mScriptHandler) {
       aTimeout->mScriptHandler->MarkForCC();
     }
   });
 }
 
 void
 TimeoutManager::Suspend()
 {
-  ForEachTimeout([](Timeout* aTimeout) {
+  ForEachUnorderedTimeout([](Timeout* aTimeout) {
     // Leave the timers with the current time remaining.  This will
     // cause the timers to potentially fire when the window is
     // Resume()'d.  Time effectively passes while suspended.
 
     // Drop the XPCOM timer; we'll reschedule when restoring the state.
     if (aTimeout->mTimer) {
       aTimeout->mTimer->Cancel();
       aTimeout->mTimer = nullptr;
@@ -854,17 +998,17 @@ TimeoutManager::Suspend()
 }
 
 void
 TimeoutManager::Resume()
 {
   TimeStamp now = TimeStamp::Now();
   DebugOnly<bool> _seenDummyTimeout = false;
 
-  ForEachTimeout([&](Timeout* aTimeout) {
+  ForEachUnorderedTimeout([&](Timeout* aTimeout) {
     // There's a chance we're being called with RunTimeout on the stack in which
     // case we have a dummy timeout in the list that *must not* be resumed. It
     // can be identified by a null mWindow.
     if (!aTimeout->mWindow) {
       NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
       _seenDummyTimeout = true;
       return;
     }
@@ -900,17 +1044,17 @@ TimeoutManager::Resume()
     aTimeout->AddRef();
   });
 }
 
 void
 TimeoutManager::Freeze()
 {
   TimeStamp now = TimeStamp::Now();
-  ForEachTimeout([&](Timeout* aTimeout) {
+  ForEachUnorderedTimeout([&](Timeout* aTimeout) {
     // Save the current remaining time for this timeout.  We will
     // re-apply it when the window is Thaw()'d.  This effectively
     // shifts timers to the right as if time does not pass while
     // the window is frozen.
     if (aTimeout->mWhen > now) {
       aTimeout->mTimeRemaining = aTimeout->mWhen - now;
     } else {
       aTimeout->mTimeRemaining = TimeDuration(0);
@@ -923,24 +1067,32 @@ TimeoutManager::Freeze()
 }
 
 void
 TimeoutManager::Thaw()
 {
   TimeStamp now = TimeStamp::Now();
   DebugOnly<bool> _seenDummyTimeout = false;
 
-  ForEachTimeout([&](Timeout* aTimeout) {
+  ForEachUnorderedTimeout([&](Timeout* aTimeout) {
     // There's a chance we're being called with RunTimeout on the stack in which
     // case we have a dummy timeout in the list that *must not* be resumed. It
     // can be identified by a null mWindow.
     if (!aTimeout->mWindow) {
       NS_ASSERTION(!_seenDummyTimeout, "More than one dummy timeout?!");
       _seenDummyTimeout = true;
       return;
     }
 
     // Set mWhen back to the time when the timer is supposed to fire.
     aTimeout->mWhen = now + aTimeout->mTimeRemaining;
 
     MOZ_ASSERT(!aTimeout->mTimer);
   });
 }
+
+bool
+TimeoutManager::IsTimeoutTracking(uint32_t aTimeoutId)
+{
+  return mTrackingTimeouts.ForEachAbortable([&](Timeout* aTimeout) {
+      return aTimeout->mTimeoutId == aTimeoutId;
+    });
+}
--- a/dom/base/TimeoutManager.h
+++ b/dom/base/TimeoutManager.h
@@ -13,30 +13,36 @@ class nsITimeoutHandler;
 class nsGlobalWindow;
 
 namespace mozilla {
 
 class ThrottledEventQueue;
 
 namespace dom {
 
+class OrderedTimeoutIterator;
+
 // This class manages the timeouts in a Window's setTimeout/setInterval pool.
 class TimeoutManager final
 {
 public:
   explicit TimeoutManager(nsGlobalWindow& aWindow);
   TimeoutManager(const TimeoutManager& rhs) = delete;
   void operator=(const TimeoutManager& rhs) = delete;
 
   bool IsRunningTimeout() const { return mTimeoutFiringDepth > 0; }
 
   static uint32_t GetNestingLevel() { return sNestingLevel; }
   static void SetNestingLevel(uint32_t aLevel) { sNestingLevel = aLevel; }
 
-  bool HasTimeouts() const { return !mTimeouts.IsEmpty(); }
+  bool HasTimeouts() const
+  {
+    return !mNormalTimeouts.IsEmpty() ||
+           !mTrackingTimeouts.IsEmpty();
+  }
 
   nsresult SetTimeout(nsITimeoutHandler* aHandler,
                       int32_t interval, bool aIsInterval,
                       mozilla::dom::Timeout::Reason aReason,
                       int32_t* aReturn);
   void ClearTimeout(int32_t aTimerId,
                     mozilla::dom::Timeout::Reason aReason);
 
@@ -78,36 +84,43 @@ public:
   void Suspend();
   void Resume();
   void Freeze();
   void Thaw();
 
   // Initialize TimeoutManager before the first time it is accessed.
   static void Initialize();
 
-  // Run some code for each Timeout in our list.
+  // Exposed only for testing
+  bool IsTimeoutTracking(uint32_t aTimeoutId);
+
+  // Run some code for each Timeout in our list.  Note that this function
+  // doesn't guarantee that Timeouts are iterated in any particular order.
   template <class Callable>
-  void ForEachTimeout(Callable c)
+  void ForEachUnorderedTimeout(Callable c)
   {
-    mTimeouts.ForEach(c);
+    mNormalTimeouts.ForEach(c);
+    mTrackingTimeouts.ForEach(c);
   }
 
-  // Run some code for each Timeout in our list, but let the callback cancel
-  // the iteration by returning true.
+  // Run some code for each Timeout in our list, but let the callback cancel the
+  // iteration by returning true.  Note that this function doesn't guarantee
+  // that Timeouts are iterated in any particular order.
   template <class Callable>
-  void ForEachTimeoutAbortable(Callable c)
+  void ForEachUnorderedTimeoutAbortable(Callable c)
   {
-    mTimeouts.ForEachAbortable(c);
+    if (!mNormalTimeouts.ForEachAbortable(c)) {
+      mTrackingTimeouts.ForEachAbortable(c);
+    }
   }
 
 private:
   nsresult ResetTimersForThrottleReduction(int32_t aPreviousThrottleDelayMS);
 
 private:
-  typedef mozilla::LinkedList<mozilla::dom::Timeout> TimeoutList;
   struct Timeouts {
     Timeouts()
       : mTimeoutInsertionPoint(nullptr)
     {
     }
 
     // Insert aTimeout into the list, before all timeouts that would
     // fire after it, but no earlier than mTimeoutInsertionPoint, if any.
@@ -144,44 +157,55 @@ private:
     {
       for (Timeout* timeout = GetFirst();
            timeout;
            timeout = timeout->getNext()) {
         c(timeout);
       }
     }
 
+    // Returns true when a callback aborts iteration.
     template <class Callable>
-    void ForEachAbortable(Callable c)
+    bool ForEachAbortable(Callable c)
     {
       for (Timeout* timeout = GetFirst();
            timeout;
            timeout = timeout->getNext()) {
         if (c(timeout)) {
-          break;
+          return true;
         }
       }
+      return false;
     }
 
+    friend class OrderedTimeoutIterator;
+
   private:
+    typedef mozilla::LinkedList<mozilla::dom::Timeout> TimeoutList;
+
     // mTimeoutList is generally sorted by mWhen, unless mTimeoutInsertionPoint is
     // non-null.  In that case, the dummy timeout pointed to by
     // mTimeoutInsertionPoint may have a later mWhen than some of the timeouts
     // that come after it.
     TimeoutList               mTimeoutList;
     // If mTimeoutInsertionPoint is non-null, insertions should happen after it.
     // This is a dummy timeout at the moment; if that ever changes, the logic in
     // ResetTimersForThrottleReduction needs to change.
     mozilla::dom::Timeout*    mTimeoutInsertionPoint;
   };
 
+  friend class OrderedTimeoutIterator;
+
   // Each nsGlobalWindow object has a TimeoutManager member.  This reference
   // points to that holder object.
   nsGlobalWindow&             mWindow;
-  Timeouts                    mTimeouts;
+  // The list of timeouts coming from non-tracking scripts.
+  Timeouts                    mNormalTimeouts;
+  // The list of timeouts coming from scripts on the tracking protection list.
+  Timeouts                    mTrackingTimeouts;
   uint32_t                    mTimeoutIdCounter;
   uint32_t                    mTimeoutFiringDepth;
   mozilla::dom::Timeout*      mRunningTimeout;
 
    // The current idle request callback timeout handle
   uint32_t                    mIdleCallbackTimeoutCounter;
 
   int32_t                     mBackPressureDelayMS;
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -106,16 +106,17 @@
 #include "nsNetUtil.h"
 #include "nsDocument.h"
 #include "HTMLImageElement.h"
 #include "mozilla/css/ImageLoader.h"
 #include "mozilla/layers/APZCTreeManager.h" // for layers::ZoomToRectBehavior
 #include "mozilla/dom/Promise.h"
 #include "mozilla/StyleSheetInlines.h"
 #include "mozilla/gfx/GPUProcessManager.h"
+#include "mozilla/dom/TimeoutManager.h"
 
 #ifdef XP_WIN
 #undef GetClassName
 #endif
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
@@ -4087,16 +4088,31 @@ nsDOMWindowUtils::GetGpuProcessPid(int32
     *aPid = pm->GPUProcessPid();
   } else {
     *aPid = -1;
   }
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsDOMWindowUtils::IsTimeoutTracking(uint32_t aTimeoutId, bool* aResult)
+{
+  NS_ENSURE_ARG_POINTER(aResult);
+  *aResult = false;
+
+  nsCOMPtr<nsPIDOMWindowOuter> window = do_QueryReferent(mWindow);
+  NS_ENSURE_STATE(window);
+  nsCOMPtr<nsPIDOMWindowInner> innerWindow = window->GetCurrentInnerWindow();
+  NS_ENSURE_STATE(innerWindow);
+
+  *aResult = innerWindow->TimeoutManager().IsTimeoutTracking(aTimeoutId);
+  return NS_OK;
+}
+
 NS_INTERFACE_MAP_BEGIN(nsTranslationNodeList)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
   NS_INTERFACE_MAP_ENTRY(nsITranslationNodeList)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_ADDREF(nsTranslationNodeList)
 NS_IMPL_RELEASE(nsTranslationNodeList)
 
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -1931,17 +1931,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSpeechSynthesis)
 #endif
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOuterWindow)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerManager)
 
   if (tmp->mTimeoutManager) {
-    tmp->mTimeoutManager->ForEachTimeout([&cb](Timeout* timeout) {
+    tmp->mTimeoutManager->ForEachUnorderedTimeout([&cb](Timeout* timeout) {
       cb.NoteNativeChild(timeout, NS_CYCLE_COLLECTION_PARTICIPANT(Timeout));
     });
   }
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mHistory)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCustomElements)
 
--- a/dom/base/nsImageLoadingContent.cpp
+++ b/dom/base/nsImageLoadingContent.cpp
@@ -39,17 +39,16 @@
 #include "nsLayoutUtils.h"
 #include "nsIContentPolicy.h"
 #include "nsSVGEffects.h"
 
 #include "gfxPrefs.h"
 
 #include "mozAutoDocUpdate.h"
 #include "mozilla/AsyncEventDispatcher.h"
-#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/EventStates.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ImageTracker.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 
 #ifdef LoadImage
 // Undefine LoadImage to prevent naming conflict with Windows.
@@ -144,19 +143,16 @@ nsImageLoadingContent::Notify(imgIReques
     // We should definitely have a request here
     MOZ_ASSERT(aRequest, "no request?");
 
     NS_PRECONDITION(aRequest == mCurrentRequest || aRequest == mPendingRequest,
                     "Unknown request");
   }
 
   {
-    MOZ_RELEASE_ASSERT(js::AllowGCBarriers(CycleCollectedJSContext::Get()->Context()),
-                       "ImageObservers can be implement in JS, so they should not be called during painting. See bug 1311841");
-
     nsAutoScriptBlocker scriptBlocker;
 
     for (ImageObserver* observer = &mObserverList, *next; observer;
          observer = next) {
       next = observer->mNext;
       if (observer->mObserver) {
         observer->mObserver->Notify(aRequest, aType, aData);
       }
--- a/dom/base/test/file_timer_flood.html
+++ b/dom/base/test/file_timer_flood.html
@@ -1,19 +1,29 @@
 <!DOCTYPE HTML>
 <html>
 <body>
 <script>
 let count = 0;
-function cb() {
+let last_timer_set = 0;
+let last_timer_observed = 0;
+function cb(timer_observed) {
+  if (timer_observed > last_timer_observed) {
+    // In order to make the test more efficient, we don't use the SimpleTest
+    // ok() function to avoid generating one test assertion per one of these
+    // checks.  We only send a message to the parent which fails the test if
+    // we detect out of order firing of timeouts.
+    window.parent.postMessage('OUT_OF_ORDER', '*');
+  }
+  last_timer_observed = timer_observed;
   count += 1;
   // Notify our parent that we are ready once the timer flood has
   // warmed up.
   if (count === 10000) {
     window.parent.postMessage('STARTED', '*');
   }
-  setTimeout(cb, 0);
-  setTimeout(cb, 0);
+  last_timer_set = setTimeout(cb.bind(last_timer_set), 0);
+  last_timer_set = setTimeout(cb.bind(last_timer_set), 0);
 }
 addEventListener('load', cb);
 </script>
 </body>
 </html>
--- a/dom/base/test/test_timer_flood.html
+++ b/dom/base/test/test_timer_flood.html
@@ -8,32 +8,43 @@
 </head>
 <body>
 <p id="display"></p>
 <div id="content" style="display: none">
 </div>
 <pre id="test">
 <script type="application/javascript">
 SimpleTest.waitForExplicitFinish();
+// This test takes a long time to run and it times out on Android debug as a result.
+SimpleTest.requestLongerTimeout(5);
 
 function onLoad() {
   return new Promise(resolve => {
     addEventListener('load', resolve, { once: true });
   });
 }
 
+function setPrefs() {
+  // Put timeouts randomly in the tracking or normal buffer.  We do this in order to
+  // test to ensure that by default, this will not change the scheduling of timeouts.
+  return SpecialPowers.pushPrefEnv({"set": [["dom.timeout_bucketing_strategy", 3]]});
+}
+
 // Create a frame that executes a timer flood.  The frame signals
 // that is ready once the flood has had a chance to warm up.
 function withFloodFrame() {
-  return new Promise(resolve => {
+  return new Promise((resolve, reject) => {
     let frame = document.createElement('iframe');
     addEventListener('message', function onMsg(evt) {
       if (evt.data === 'STARTED') {
         removeEventListener('message', onMsg);
         resolve(frame);
+      } else if (evt.data == 'OUT_OF_ORDER') {
+        ok(false, "Out of order timeout observed");
+        reject();
       }
     });
     frame.src = 'file_timer_flood.html';
     document.body.appendChild(frame);
   });
 }
 
 // Test that we can load documents during a timer flood.
@@ -70,17 +81,19 @@ function testRequestAnimationFrame() {
       }
     };
     requestAnimationFrame(nextFrame);
   });
 }
 
 let floodFrame;
 
-onLoad().then(_ => {
+onLoad()
+.then(setPrefs)
+.then(_ => {
   // Start a timer flood in a frame.
   return withFloodFrame();
 }).then(frame => {
   floodFrame = frame;
 
   // Next we are going to start a bunch of asynchronous work that we
   // expect to complete in spite of the timer flood.  The type of work
   // is a bit arbitrary, but is chosen to reflect the kinds of things
@@ -104,13 +117,15 @@ onLoad().then(_ => {
 
   // Wait for all tests to finish.  If we do not handle the timer flood
   // well then this will likely time out.
   return Promise.all(tests);
 }).then(_ => {
   ok(true, 'completed tests without timing out');
   floodFrame.remove();
   SimpleTest.finish();
+}).catch(_ => {
+  SimpleTest.finish();
 });
 </script>
 </pre>
 </body>
 </html>
--- a/dom/canvas/WebGL2Context.h
+++ b/dom/canvas/WebGL2Context.h
@@ -265,18 +265,22 @@ public:
     // -------------------------------------------------------------------------
     // Writing to the drawing buffer
 
     /* Implemented in WebGLContext
     void VertexAttribDivisor(GLuint index, GLuint divisor);
     void DrawArraysInstanced(GLenum mode, GLint first, GLsizei count, GLsizei instanceCount);
     void DrawElementsInstanced(GLenum mode, GLsizei count, GLenum type, GLintptr offset, GLsizei instanceCount);
     */
-    void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset);
 
+    void DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count,
+                           GLenum type, WebGLintptr byteOffset)
+    {
+        DrawElements(mode, count, type, byteOffset);
+    }
 
     // ------------------------------------------------------------------------
     // Multiple Render Targets - WebGL2ContextMRTs.cpp
     /* Implemented in WebGLContext
     void DrawBuffers(const dom::Sequence<GLenum>& buffers);
     */
 
 private:
@@ -320,16 +324,18 @@ public:
     void SamplerParameterf(WebGLSampler& sampler, GLenum pname, GLfloat param);
     void GetSamplerParameter(JSContext*, const WebGLSampler& sampler, GLenum pname,
                              JS::MutableHandleValue retval);
 
 
     // -------------------------------------------------------------------------
     // Sync objects - WebGL2ContextSync.cpp
 
+    const GLuint64 kMaxClientWaitSyncTimeoutNS = 1000 * 1000 * 1000; // 1000ms in ns.
+
     already_AddRefed<WebGLSync> FenceSync(GLenum condition, GLbitfield flags);
     bool IsSync(const WebGLSync* sync);
     void DeleteSync(WebGLSync* sync);
     GLenum ClientWaitSync(const WebGLSync& sync, GLbitfield flags, GLuint64 timeout);
     void WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout);
     void GetSyncParameter(JSContext*, const WebGLSync& sync, GLenum pname,
                           JS::MutableHandleValue retval);
 
--- a/dom/canvas/WebGL2ContextBuffers.cpp
+++ b/dom/canvas/WebGL2ContextBuffers.cpp
@@ -126,18 +126,18 @@ WebGL2Context::GetBufferSubData(GLenum t
     }
     const GLsizeiptr glByteLen(byteLen);
 
     ////
 
     gl->MakeCurrent();
     const ScopedLazyBind readBind(gl, target, buffer);
 
-    const auto mappedBytes = gl->fMapBufferRange(target, srcByteOffset, glByteLen,
-                                                 LOCAL_GL_MAP_READ_BIT);
-    // Warning: Possibly shared memory.  See bug 1225033.
     if (byteLen) {
+        const auto mappedBytes = gl->fMapBufferRange(target, srcByteOffset, glByteLen,
+                                                     LOCAL_GL_MAP_READ_BIT);
+        // Warning: Possibly shared memory.  See bug 1225033.
         memcpy(bytes, mappedBytes, byteLen);
+        gl->fUnmapBuffer(target);
     }
-    gl->fUnmapBuffer(target);
 }
 
 } // namespace mozilla
deleted file mode 100644
--- a/dom/canvas/WebGL2ContextDraw.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "WebGL2Context.h"
-
-namespace mozilla {
-
-// -------------------------------------------------------------------------
-// Writing to the drawing buffer
-
-void
-WebGL2Context::DrawRangeElements(GLenum mode, GLuint start, GLuint end, GLsizei count, GLenum type, GLintptr offset)
-{
-    GenerateWarning("drawRangeElements: Not Implemented.");
-}
-
-} // namespace mozilla
--- a/dom/canvas/WebGL2ContextState.cpp
+++ b/dom/canvas/WebGL2ContextState.cpp
@@ -107,23 +107,23 @@ WebGL2Context::GetParameter(JSContext* c
       // value is 4 * GL_MAX_VARYING_VECTORS
       GLint val;
       gl->fGetIntegerv(LOCAL_GL_MAX_VARYING_VECTORS, &val);
       return JS::Int32Value(4*val);
     }
 
     /* GLint64 */
     case LOCAL_GL_MAX_CLIENT_WAIT_TIMEOUT_WEBGL:
-      return JS::NumberValue(0); // TODO
+      return JS::NumberValue(kMaxClientWaitSyncTimeoutNS);
 
     case LOCAL_GL_MAX_ELEMENT_INDEX:
       // GL_MAX_ELEMENT_INDEX becomes available in GL 4.3 or via ES3
       // compatibility
       if (!gl->IsSupported(gl::GLFeature::ES3_compatibility))
-        return JS::NumberValue(0);
+        return JS::NumberValue(UINT32_MAX);
 
       /*** fall through to fGetInteger64v ***/
       MOZ_FALLTHROUGH;
 
     case LOCAL_GL_MAX_COMBINED_FRAGMENT_UNIFORM_COMPONENTS:
     case LOCAL_GL_MAX_COMBINED_VERTEX_UNIFORM_COMPONENTS:
     case LOCAL_GL_MAX_UNIFORM_BLOCK_SIZE: {
       GLint64 val;
--- a/dom/canvas/WebGL2ContextSync.cpp
+++ b/dom/canvas/WebGL2ContextSync.cpp
@@ -62,16 +62,22 @@ WebGL2Context::ClientWaitSync(const WebG
     if (!ValidateObject(funcName, sync))
         return LOCAL_GL_WAIT_FAILED;
 
     if (flags != 0 && flags != LOCAL_GL_SYNC_FLUSH_COMMANDS_BIT) {
         ErrorInvalidValue("%s: `flags` must be SYNC_FLUSH_COMMANDS_BIT or 0.", funcName);
         return LOCAL_GL_WAIT_FAILED;
     }
 
+    if (timeout > kMaxClientWaitSyncTimeoutNS) {
+        ErrorInvalidOperation("%s: `timeout` must not exceed %s nanoseconds.", funcName,
+                              "MAX_CLIENT_WAIT_TIMEOUT_WEBGL");
+        return LOCAL_GL_WAIT_FAILED;
+    }
+
     MakeContextCurrent();
     return gl->fClientWaitSync(sync.mGLName, flags, timeout);
 }
 
 void
 WebGL2Context::WaitSync(const WebGLSync& sync, GLbitfield flags, GLint64 timeout)
 {
     const char funcName[] = "waitSync";
--- a/dom/canvas/WebGL2ContextUniforms.cpp
+++ b/dom/canvas/WebGL2ContextUniforms.cpp
@@ -177,16 +177,24 @@ WebGL2Context::GetActiveUniforms(JSConte
         return;
 
     if (!ValidateUniformEnum(this, pname, funcName))
         return;
 
     if (!ValidateObject("getActiveUniforms: program", program))
         return;
 
+    const auto& numActiveUniforms = program.LinkInfo()->uniforms.size();
+    for (const auto& curIndex : uniformIndices) {
+        if (curIndex >= numActiveUniforms) {
+            ErrorInvalidValue("%s: Too-large active uniform index queried.", funcName);
+            return;
+        }
+    }
+
     const auto& count = uniformIndices.Length();
 
     JS::Rooted<JSObject*> array(cx, JS_NewArrayObject(cx, count));
     UniquePtr<GLint[]> samples(new GLint[count]);
     if (!array || !samples) {
         ErrorOutOfMemory("%s: Failed to allocate buffers.", funcName);
         return;
     }
--- a/dom/canvas/WebGLContext.h
+++ b/dom/canvas/WebGLContext.h
@@ -969,16 +969,17 @@ public:
 
 private:
     // State tracking slots
     realGLboolean mDitherEnabled;
     realGLboolean mRasterizerDiscardEnabled;
     realGLboolean mScissorTestEnabled;
     realGLboolean mDepthTestEnabled;
     realGLboolean mStencilTestEnabled;
+    GLenum mGenerateMipmapHint;
 
     bool ValidateCapabilityEnum(GLenum cap, const char* info);
     realGLboolean* GetStateTrackingSlot(GLenum cap);
 
 // -----------------------------------------------------------------------------
 // Texture funcions (WebGLContextTextures.cpp)
 public:
     void ActiveTexture(GLenum texUnit);
--- a/dom/canvas/WebGLContextGL.cpp
+++ b/dom/canvas/WebGLContextGL.cpp
@@ -687,19 +687,16 @@ WebGLContext::GetFramebufferAttachmentPa
         default:
             ErrorInvalidEnum("%s: With the default framebuffer, can only query COLOR, DEPTH,"
                              " or STENCIL for GL_FRAMEBUFFER_ATTACHMENT_OBJECT_TYPE",
                              funcName);
             return JS::NullValue();
         }
         return JS::Int32Value(LOCAL_GL_FRAMEBUFFER_DEFAULT);
 
-    case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_OBJECT_NAME:
-        return JS::NullValue();
-
     ////////////////
 
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_RED_SIZE:
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_GREEN_SIZE:
     case LOCAL_GL_FRAMEBUFFER_ATTACHMENT_BLUE_SIZE:
         if (attachment == LOCAL_GL_BACK)
             return JS::NumberValue(8);
         return JS::NumberValue(0);
@@ -942,16 +939,18 @@ WebGLContext::Hint(GLenum target, GLenum
 {
     if (IsContextLost())
         return;
 
     bool isValid = false;
 
     switch (target) {
     case LOCAL_GL_GENERATE_MIPMAP_HINT:
+        mGenerateMipmapHint = mode;
+
         // Deprecated and removed in desktop GL Core profiles.
         if (gl->IsCoreProfile())
             return;
 
         isValid = true;
         break;
 
     case LOCAL_GL_FRAGMENT_SHADER_DERIVATIVE_HINT:
@@ -1460,17 +1459,17 @@ ValidateReadPixelsFormatAndType(const we
     }
 
     if (pi.type == LOCAL_GL_UNSIGNED_INT_24_8) {
         webgl->ErrorInvalidEnum("readPixels: Invalid type: 0x%04x", pi.type);
         return false;
     }
 
     MOZ_ASSERT(gl->IsCurrent());
-    if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
+    if (gl->IsGLES()) {
         const auto auxFormat = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT);
         const auto auxType = gl->GetIntAs<GLenum>(LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE);
 
         if (auxFormat && auxType &&
             pi.format == auxFormat && pi.type == auxType)
         {
             return true;
         }
--- a/dom/canvas/WebGLContextState.cpp
+++ b/dom/canvas/WebGLContextState.cpp
@@ -381,45 +381,53 @@ WebGLContext::GetParameter(JSContext* cx
         case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_FAIL:
         case LOCAL_GL_STENCIL_BACK_PASS_DEPTH_PASS:
         case LOCAL_GL_DEPTH_FUNC:
         case LOCAL_GL_BLEND_SRC_RGB:
         case LOCAL_GL_BLEND_SRC_ALPHA:
         case LOCAL_GL_BLEND_DST_RGB:
         case LOCAL_GL_BLEND_DST_ALPHA:
         case LOCAL_GL_BLEND_EQUATION_RGB:
-        case LOCAL_GL_BLEND_EQUATION_ALPHA:
-        case LOCAL_GL_GENERATE_MIPMAP_HINT: {
+        case LOCAL_GL_BLEND_EQUATION_ALPHA: {
             GLint i = 0;
             gl->fGetIntegerv(pname, &i);
             return JS::NumberValue(uint32_t(i));
         }
+
+        case LOCAL_GL_GENERATE_MIPMAP_HINT:
+            return JS::NumberValue(mGenerateMipmapHint);
+
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_TYPE: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
             if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
                 return JS::NullValue();
 
             GLint i = 0;
-            if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
+            if (gl->IsGLES()) {
+                // ES2_compatibility always returns UNSIGNED_BYTE here, so
+                // branch on actual IsGLES().
+                // Also OSX+NV generates an error here.
                 gl->fGetIntegerv(pname, &i);
             } else {
                 i = LOCAL_GL_UNSIGNED_BYTE;
             }
-
             return JS::NumberValue(uint32_t(i));
         }
         case LOCAL_GL_IMPLEMENTATION_COLOR_READ_FORMAT: {
             const webgl::FormatUsageInfo* usage;
             uint32_t width, height;
             if (!ValidateCurFBForRead(funcName, &usage, &width, &height))
                 return JS::NullValue();
 
             GLint i = 0;
-            if (gl->IsSupported(gl::GLFeature::ES2_compatibility)) {
+            if (gl->IsGLES()) {
+                // ES2_compatibility always returns UNSIGNED_BYTE here, so
+                // branch on actual IsGLES().
+                // Also OSX+NV generates an error here.
                 gl->fGetIntegerv(pname, &i);
             } else {
                 i = LOCAL_GL_RGBA;
             }
 
             // OpenGL ES 3.0.4 p112 Table 3.2 shows that read format SRGB_ALPHA is
             // not supported. And if internal format of fbo is SRGB8_ALPHA8, then
             // IMPLEMENTATION_COLOR_READ_FORMAT is SRGB_ALPHA which is not supported
--- a/dom/canvas/WebGLContextValidate.cpp
+++ b/dom/canvas/WebGLContextValidate.cpp
@@ -496,16 +496,17 @@ WebGLContext::InitAndValidateGL(FailureR
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_VALUE_MASK,      mStencilValueMaskFront);
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_VALUE_MASK, mStencilValueMaskBack);
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_WRITEMASK,       mStencilWriteMaskFront);
     AssertUintParamCorrect(gl, LOCAL_GL_STENCIL_BACK_WRITEMASK,  mStencilWriteMaskBack);
 
     mDitherEnabled = true;
     mRasterizerDiscardEnabled = false;
     mScissorTestEnabled = false;
+    mGenerateMipmapHint = LOCAL_GL_DONT_CARE;
 
     // Bindings, etc.
     mActiveTexture = 0;
     mDefaultFB_DrawBuffer0 = LOCAL_GL_BACK;
 
     mEmitContextLostErrorOnce = true;
     mWebGLError = LOCAL_GL_NO_ERROR;
     mUnderlyingGLError = LOCAL_GL_NO_ERROR;
--- a/dom/canvas/WebGLTexture.cpp
+++ b/dom/canvas/WebGLTexture.cpp
@@ -233,26 +233,36 @@ WebGLTexture::IsMipmapComplete(uint32_t 
                 cur.mFormat != baseImageInfo.mFormat)
             {
                 return false;
             }
         }
 
         // GLES 3.0.4, p158:
         // "[...] until the last array is reached with dimension 1 x 1 x 1."
-        if (refWidth == 1 &&
-            refHeight == 1 &&
-            refDepth == 1)
-        {
-            break;
+        if (mTarget == LOCAL_GL_TEXTURE_3D) {
+            if (refWidth == 1 &&
+                refHeight == 1 &&
+                refDepth == 1)
+            {
+                break;
+            }
+
+            refDepth = std::max(uint32_t(1), refDepth / 2);
+        } else {
+            // TEXTURE_2D_ARRAY may have depth != 1, but that's normal.
+            if (refWidth == 1 &&
+                refHeight == 1)
+            {
+                break;
+            }
         }
 
         refWidth  = std::max(uint32_t(1), refWidth  / 2);
         refHeight = std::max(uint32_t(1), refHeight / 2);
-        refDepth  = std::max(uint32_t(1), refDepth  / 2);
     }
 
     return true;
 }
 
 bool
 WebGLTexture::IsCubeComplete() const
 {
@@ -1009,17 +1019,17 @@ WebGLTexture::IsTexture() const
 // See this discussion:
 //   https://www.khronos.org/webgl/public-mailing-list/archives/1008/msg00014.html
 void
 WebGLTexture::TexParameter(TexTarget texTarget, GLenum pname, GLint* maybeIntParam,
                            GLfloat* maybeFloatParam)
 {
     MOZ_ASSERT(maybeIntParam || maybeFloatParam);
 
-    GLint   intParam   = maybeIntParam   ? *maybeIntParam   : GLint(*maybeFloatParam);
+    GLint   intParam   = maybeIntParam   ? *maybeIntParam   : GLint(roundf(*maybeFloatParam));
     GLfloat floatParam = maybeFloatParam ? *maybeFloatParam : GLfloat(*maybeIntParam);
 
     bool isPNameValid = false;
     switch (pname) {
     // GLES 2.0.25 p76:
     case LOCAL_GL_TEXTURE_WRAP_S:
     case LOCAL_GL_TEXTURE_WRAP_T:
     case LOCAL_GL_TEXTURE_MIN_FILTER:
--- a/dom/canvas/WebGLTextureUpload.cpp
+++ b/dom/canvas/WebGLTextureUpload.cpp
@@ -439,16 +439,21 @@ WebGLTexture::TexSubImage(const char* fu
 {
     const GLint border = 0;
     dom::RootedTypedArray<dom::Uint8ClampedArray> scopedArr(dom::RootingCx());
     const auto blob = ValidateTexOrSubImage(mContext, funcName, target, width, height,
                                             depth, border, pi, src, &scopedArr);
     if (!blob)
         return;
 
+    if (!blob->HasData()) {
+        mContext->ErrorInvalidValue("%s: Source must not be null.", funcName);
+        return;
+    }
+
     TexSubImage(funcName, target, level, xOffset, yOffset, zOffset, pi, blob.get());
 }
 
 //////////////////////////////////////////////////////////////////////////////////////////
 //////////////////////////////////////////////////////////////////////////////////////////
 
 static bool
 ValidateTexImage(WebGLContext* webgl, WebGLTexture* texture, const char* funcName,
@@ -2014,16 +2019,23 @@ DoCopyTexOrSubImage(WebGLContext* webgl,
         return true;
     } while (false);
 
     if (error == LOCAL_GL_OUT_OF_MEMORY) {
         webgl->ErrorOutOfMemory("%s: Ran out of memory during texture copy.", funcName);
         return false;
     }
 
+    if (gl->IsANGLE() && error == LOCAL_GL_INVALID_OPERATION) {
+        webgl->ErrorImplementationBug("%s: ANGLE is particular about CopyTexSubImage"
+                                      " formats matching exactly.",
+                                      funcName);
+        return false;
+    }
+
     MOZ_RELEASE_ASSERT(false, "GFX: We should have caught all other errors.");
     webgl->GenerateWarning("%s: Unexpected error during texture copy. Context lost.",
                            funcName);
     webgl->ForceLoseContext();
     return false;
 }
 
 // There is no CopyTexImage3D.
--- a/dom/canvas/moz.build
+++ b/dom/canvas/moz.build
@@ -68,17 +68,16 @@ SOURCES += [
 
 # WebGL Sources
 UNIFIED_SOURCES += [
     'TexUnpackBlob.cpp',
     'WebGL1Context.cpp',
     'WebGL1ContextUniforms.cpp',
     'WebGL2Context.cpp',
     'WebGL2ContextBuffers.cpp',
-    'WebGL2ContextDraw.cpp',
     'WebGL2ContextFramebuffers.cpp',
     'WebGL2ContextMRTs.cpp',
     'WebGL2ContextPrograms.cpp',
     'WebGL2ContextQueries.cpp',
     'WebGL2ContextRenderbuffers.cpp',
     'WebGL2ContextSamplers.cpp',
     'WebGL2ContextState.cpp',
     'WebGL2ContextSync.cpp',
--- a/dom/canvas/test/webgl-conf/generated-mochitest.ini
+++ b/dom/canvas/test/webgl-conf/generated-mochitest.ini
@@ -4581,17 +4581,16 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__samplers__sampler-drawing-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__samplers__samplers.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__state__gl-enum-tests.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__state__gl-get-calls.html]
-fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__state__gl-getstring.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__state__gl-object-get-calls.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__transform_feedback__transform_feedback.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance2__vertex_arrays__vertex-array-object.html]
@@ -5764,17 +5763,17 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__gl-scissor-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__gl-viewport-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__line-loop-tri-fan.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__many-draw-calls.html]
-skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
+skip-if = debug || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__more-than-65536-indices.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__multisample-corruption.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__negative-one-index.html]
 fail-if = (os == 'mac')
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__point-no-attributes.html]
@@ -5789,18 +5788,17 @@ skip-if = (os == 'android' || os == 'lin
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__simple.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__rendering__triangle.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__state__gl-enable-enum-test.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__state__gl-get-calls.html]
-skip-if = (os == 'mac' && debug) || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
-fail-if = (os == 'mac')
+skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__state__gl-geterror.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__state__gl-initial-state.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__state__state-uneffected-after-compositing.html]
 skip-if = (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
 [generated/test_2_conformance__textures__misc__copy-tex-image-2d-formats.html]
 skip-if = (os == 'mac') || (os == 'android' || os == 'linux' || (os == 'win' && os_version == '5.1'))
--- a/dom/canvas/test/webgl-conf/mochitest-errata.ini
+++ b/dom/canvas/test/webgl-conf/mochitest-errata.ini
@@ -296,16 +296,19 @@ skip-if = (os == 'android')
 skip-if = (os == 'android')
 [generated/test_conformance__glsl__variables__gl-fragcoord.html]
 # Crashes
 skip-if = (os == 'android')
 [generated/test_conformance__rendering__many-draw-calls.html]
 # Crashes on Android
 # Times-out on DEBUG builds
 skip-if = (os == 'android') || debug
+[generated/test_2_conformance__rendering__many-draw-calls.html]
+# Appears to just take too long on debug, most of the time.
+skip-if = debug
 [generated/test_conformance__uniforms__out-of-bounds-uniform-array-access.html]
 # Crashes
 skip-if = (os == 'android') || (os == 'mac' && os_version == '10.6')
 [generated/test_conformance__glsl__samplers__glsl-function-texture2dproj.html]
 # Crashes
 skip-if = (os == 'android')
 [generated/test_conformance__rendering__framebuffer-switch.html]
 # Crashes
@@ -528,35 +531,27 @@ fail-if = (os == 'mac' && os_version == 
 fail-if = (os == 'mac' && os_version == '10.8')
 [generated/test_conformance__glsl__variables__gl-pointcoord.html]
 fail-if = (os == 'mac' && os_version == '10.8')
 [generated/test_conformance__limits__gl-max-texture-dimensions.html]
 fail-if = (os == 'mac' && os_version == '10.8')
 
 ####################
 # failure on OSX
-[generated/test_2_conformance2__state__gl-get-calls.html]
-fail-if = (os == 'mac')
 [generated/test_conformance__extensions__angle-instanced-arrays.html]
 fail-if = (os == 'mac')
 [generated/test_conformance__glsl__misc__shaders-with-invariance.html]
 fail-if = (os == 'mac')
 
 [generated/test_2_conformance2__extensions__ext-color-buffer-float.html]
 skip-if = (os == 'mac' && debug)
 [generated/test_2_conformance__limits__gl-line-width.html]
 skip-if = (os == 'mac')
 [generated/test_2_conformance__misc__type-conversion-test.html]
 skip-if = (os == 'mac' && debug)
-[generated/test_2_conformance__state__gl-get-calls.html]
-# Hit MOZ_GL_DEBUG_ABORT_ON_ERROR on debug build
-fail-if = (os == 'mac')
-skip-if = (os == 'mac' && debug)
-
-
 
 ########################################################################
 ########################################################################
 # Win
 [generated/test_2_conformance__ogles__GL__built_in_varying_array_out_of_bounds__built_in_varying_array_out_of_bounds_001_to_001.html]
 # time out crash
 skip-if = (os == 'win')
 [generated/test_conformance__ogles__GL__built_in_varying_array_out_of_bounds__built_in_varying_array_out_of_bounds_001_to_001.html]
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -3322,16 +3322,30 @@ EventStateManager::PostHandleEvent(nsPre
     }
     break;
 
   case eDragEnter:
   case eDragOver:
     {
       NS_ASSERTION(aEvent->mClass == eDragEventClass, "Expected a drag event");
 
+      // Check if the drag is occurring inside a scrollable area. If so, scroll
+      // the area when the mouse is near the edges.
+      if (mCurrentTarget && aEvent->mMessage == eDragOver) {
+        nsIFrame* checkFrame = mCurrentTarget;
+        while (checkFrame) {
+          nsIScrollableFrame* scrollFrame = do_QueryFrame(checkFrame);
+          // Break out so only the innermost scrollframe is scrolled.
+          if (scrollFrame && scrollFrame->DragScroll(aEvent)) {
+            break;
+          }
+          checkFrame = checkFrame->GetParent();
+        }
+      }
+
       nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
       if (!dragSession)
         break;
 
       // Reset the flag.
       dragSession->SetOnlyChromeDrop(false);
       if (mPresContext) {
         EnsureDocument(mPresContext);
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -1968,16 +1968,22 @@ interface nsIDOMWindowUtils : nsISupport
    */
   void terminateGPUProcess();
 
   /**
     * Returns the GPU process pid, or -1 if there is no GPU process.
     */
   readonly attribute int32_t gpuProcessPid;
 
+  /**
+   * Returns true if the given timeout ID is in the list of tracking
+   * timeouts.
+   */
+  boolean isTimeoutTracking(in unsigned long timeoutId);
+
   // Match WidgetMouseEventBase::buttonType.
   const long MOUSE_BUTTON_LEFT_BUTTON   = 0;
   const long MOUSE_BUTTON_MIDDLE_BUTTON = 1;
   const long MOUSE_BUTTON_RIGHT_BUTTON  = 2;
 
   // Match WidgetMouseEventBase::buttonsFlag.
   const long MOUSE_BUTTONS_NO_BUTTON = 0x00;
   const long MOUSE_BUTTONS_LEFT_BUTTON = 0x01;
--- a/dom/media/gtest/TestMP3Demuxer.cpp
+++ b/dom/media/gtest/TestMP3Demuxer.cpp
@@ -265,16 +265,54 @@ protected:
       res.mDemuxer = new MP3TrackDemuxer(res.mResource);
       mTargets.push_back(res);
 
       streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
       streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
       mTargets.push_back(streamRes);
     }
 
+    {
+      MP3Resource res;
+      res.mFilePath = "small-shot-partial-xing.mp3";
+      res.mIsVBR = true;
+      res.mFileSize = 6825;
+      res.mMPEGLayer = 3;
+      res.mMPEGVersion = 1;
+      res.mID3MajorVersion = 4;
+      res.mID3MinorVersion = 0;
+      res.mID3Flags = 0;
+      res.mID3Size = 24;
+      res.mDuration = 336686;
+      res.mDurationError = 0.01f;
+      res.mSeekError = 0.2f;
+      res.mSampleRate = 44100;
+      res.mSamplesPerFrame = 1152;
+      res.mNumSamples = 12;
+      res.mNumTrailingFrames = 0;
+      res.mBitrate = 256000;
+      res.mSlotSize = 1;
+      res.mPrivate = 0;
+      const int syncs[] = { 34, 556, 1078, 1601, 2123, 2646, 3168, 3691, 4213,
+                            4736, 5258, 5781, 6303 };
+      res.mSyncOffsets.insert(res.mSyncOffsets.begin(), syncs, syncs + 13);
+
+      // No content length can be estimated for CBR stream resources.
+      MP3Resource streamRes = res;
+      streamRes.mFileSize = -1;
+
+      res.mResource = new MockMP3MediaResource(res.mFilePath);
+      res.mDemuxer = new MP3TrackDemuxer(res.mResource);
+      mTargets.push_back(res);
+
+      streamRes.mResource = new MockMP3StreamMediaResource(streamRes.mFilePath);
+      streamRes.mDemuxer = new MP3TrackDemuxer(streamRes.mResource);
+      mTargets.push_back(streamRes);
+    }
+
     for (auto& target: mTargets) {
       ASSERT_EQ(NS_OK, target.mResource->Open(nullptr));
       ASSERT_TRUE(target.mDemuxer->Init());
     }
   }
 
   std::vector<MP3Resource> mTargets;
 };
--- a/dom/media/gtest/moz.build
+++ b/dom/media/gtest/moz.build
@@ -46,16 +46,17 @@ TEST_HARNESS_FILES.gtest += [
     'id3v2header.mp3',
     'mediasource_test.mp4',
     'negative_duration.mp4',
     'noise.mp3',
     'noise_vbr.mp3',
     'short-zero-in-moov.mp4',
     'short-zero-inband.mov',
     'small-shot-false-positive.mp3',
+    'small-shot-partial-xing.mp3',
     'small-shot.mp3',
     'test.webm',
     'test_case_1224361.vp8.ivf',
     'test_case_1224363.vp8.ivf',
     'test_case_1224369.vp8.ivf',
 ]
 
 include('/ipc/chromium/chromium-config.mozbuild')
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..99d68e3cbe0cc7d8d1bc84082efbf82d9117af26
GIT binary patch
literal 6825
zc%1E+c~BGSx`(@y5CU{U5)x=2VgfOOAa)2U2##Tq&4{2d;)V%02#UxK&T)o>uoD<I
znSiK4kO9OMXH;~?Y%D@hR1|e|L`6`?1qNkgkzAOAbMD-#Q>X5&`^UXi=c!6{|B<eI
zo%iYA`}XJS$-w}#z(E@}te!a%007MsBu6ZDw0E#)BM6|KlmY*Wkf@lQGY4?y0|9_p
zHn4CGmpT0B2>V3x9JzBG`ox(z)N|bZ#H%^9bNtIi{hR+~W=z-t02uEjyrrf>6ae7q
zG619xv~n&!0*C<MD4zKa5s>I}g)=Ag<7Y8A`hcA85VO9lhL8|}HE{m^*QX&1awB&4
zoeKTwqqARNX4%ENSr|El7LCw8hPCD!+img-{r$(cqnsK1mV|y(sZ_H+j8^@3PavY4
z%Y`Wr76l0Dv&-Tu9Hvyrb4_i|)e)b^39&*0I<<gi0Ue}u^OL&ge_61?NJCx%Eyt+&
zZjyHJysRH*;UNa(YCRs=jj4R+s|gm?s~_a^mb*jhs^dH&w@JyRNl<PYb)*>tXQg&r
z$n9o&%R7dJ684SWYqg(i7Ax#j8b%+2GuHg*A?xBfYvOzo6_-sUPt}0B-%&H9Y`Pgr
z<yo~O^*X(SARptTf9wj|1ak}BzYIB`+l(H5tb^T@5wZf-2OC7OWGM-Bas(wH7j;mp
zy+5SYUb%ZaYQ6KGBifv|TJ5@w7Nz)@VEgo?RqySU&8=zgOpKFLsQ~cB>3fY1Vc=0l
zE!!ZQY*%fK=&$&k=N^lvUBQNcN!fszO}~>6Zy@I%4RHiT5|u0hjKyPwPCnQolqe&U
z8<t)K??<quh$vT|i@%K~=nA!yM|ao~#!ha#ab%YaGiyRW0Gh8(S+VEc3Hgi(GXh{n
zFAcA-Y=TNYpc#2WO@9Ewufi4yAxpFYD9ptXLGTN@sb>q8sV^+zQcOYc0!;JA>X>7(
z02hZh0u*#v%TZ>@%!59@|3FIe-ieCa6L(w98<ID*__*A-f4%RXk>B)=S8a=&>+%n;
zPKiiBXW)t|Enl!>?>;Wud**b&qkf$90Tp_s_H^3e?j>b~4b+{p-t`>og*b=Onaj~(
z{gr}ydB;qP-QvV_M!IZM=0I*<hTb<AQSs9=5}9e<6GetfQ4mhwx)PIL=d15Up=D*A
z8{DPCTBkGg>+7tRC0<Ogx=_<L@i#1I3;y$tt8r5uf#;NG5<5nU)Ej7WC0)^Tp>r5k
z7DYlh_)DUcfJSGQ;#)U>7&KXhqFZWL{SjpSLZWZB^Og5MwAdo1d5jhHrA=Mh7fHsu
z)^YO-1kWN!i6)i|>S#mew!kiO?As}K09-zXS>GP95a;IdVCoxik#`}X-d)mupt(o}
zJErfKV|+v!CP1e^I23_>qpXrlSQlS^!Mf_^YF>f-wrj!52QfQ3dC*q4lPfOP(BUi9
zXq_UQ;UchA5}>C~@x1(UyaJ=!!rOGDBkfb%PAXMg>DQK;)p32=m5W+~1IoN>^j!D3
z&4z+k;rkQYdd<x}5@%JI=>w*jJ6-oA{;7gIec=x~Ppo>9^hOrh{n&{9xh|~2*IM={
z-bG*T>{z;Do~@gE&dJ8h#aHAxSvrCu0I$z4uV5}vI0oX=N^`1|hg^>uHmV4BUwJqX
zUC}09PUsp1DchYMXC~mI_`nVvK~()%2smd8+205rXDg#JS)!(ak-~u&$5smtO^F`r
z=Qi0@l!G?e+2chSqCA~_BDHT>f5QRc<GdQ*V);l8-&qnCC}A+f@{6LHKoMwN+@go(
z5z(4JJxpeTo@nWnzg_v`e;B54o2<8Mf89MqXts35WRovl(LQin$y<8QCUI#|t$j-s
zRZSzfOm7B&W2dQZb!WaPQq$<g!d9M|R|j+~KKlkEO(^6KhZQl9)V6dhEYKAnP6zmW
zB|kl^rygiy49A(0pk!yz0eb0#0Gc~1t>W7XTN9U>dp?ohY`CCSY`|yo>{lUNdPi1*
zP6m(2LPD%Nk3H2DY6wK6et=CDs?4!G2+t${B7p07<WmW?!*W@&|DGlH<Mw}beWUtp
zj*hvB_a@@EA8)@-Fw#0hmtVF&<V1&aGbzuKDB^X98@A3Xz0Nvz$ldhBn$3Dd+IjLT
zu8edv0t7OzEe&_A=irS)9#(mc6rcfOy<j#JQVYjow%ssy{nG@K>J-oD;nzKLCR8cK
zB^i;j3sA`7;d<uP9n`yvj3^9KBtBAffu6H-$h7L<&b;5u6=QqCqB5^xEt!?4mHhtV
z0u=7Kz>SgLZWr0<j&?Nm@#ONK!^$ur(OL+*>H<70n`MK=#mw2NmJ=45R3&K}Z6~a{
z1vgTEyL_Xis_xClq|v|!6%QV_A&1|D!~3x8?UkC?a^KCJ+jwbpGY{2x^0#Mf*PO@2
z29NB^l!TVpE=zW#24{UZ8!mU3f6;49#GpZ;2(Y&FY}PgQmh~S+H=B~!XshLabHb{o
z=R2rXG%|;MLm8Y-Ho}oeU>3pTd1_EQ2r+}`h>QZG5K4!8^@5*6Og#Rq$<#CVKy1MN
zYge>GJCfR0esAfqj^^KM>w0xZ((s#mQ@w3*BUkbk-2Zj+>u|$L4{AgC$uXnW@bR`I
zfo4$y_gXhTx<0}&syZ@t^S(jy=8^-h$5bLeN{PL}N;@nDO0cld^?5dRVy5gwJh0^G
zZkzBDEJy>qs*7WRaiO@P66}^s_Z8!cFd<%MbUcNkDbq_?DC{98$j%xr);+;9AN*<i
z9YWW>xzz?T^`UP)cu|`8#$%jKORblqYDwg*T-`-C67C&5<<WM1knJAfJoSDy6zU4!
zKaZRVg)N^{pzKQtijMwM1!U0iX<x9l;TM{QCf5a-^KDWBvSy0#p?rbQlc_zG9$SAW
zb9K}MyD~ALY>0}EGl(<>3eOhvb<E841kX+_5=nCN#CAmh7sOO=y{qw;QrhsE3;R=h
zJJA)W!`|uL7?1=s@ngdu7=va85Yc}@Wfi{~H&1+H?q=H2sQxFq6|Y~kM89V1`dWk?
zEih<En|wWV++t5m&9&t#+r4!Q4xDL>2o5T;Cq2l!=B`UzzG|%_mC>kwqj5o0bH{Tt
zPY%@#Qx&neJ}a`C^}V;3-f^$p*>x4x5jEXeJDnCrmDf>Y!?M5**bR6E^Mt$LeSC`4
zo#6;)@Uviy+)_#fs$i<nMT*Fl!LXYitgixKNB%{a28yMd-5OwTSrWX%&CoRVX~sbZ
zRJ@^UYIs<tEUYPy!l#JL1pc~r_}1cjeq+WkzZ~q~ztph{(@8HB1f(Yl?0_Ex+r_nl
zZvmwsF#T&muDFA*2h#YNKovhCeUP7)zKpK`oA~R|>f;ts`011jZ?!*b*D^q5rw3Mi
z(=(BR=WXx%eLdABkX2Z7@fP}d^P;X3X%iBo$x2R4WvSb;<P$z;OMDJ9*7zkxuZ8Kd
z*9N;r8Jf*Jx3#Mq8v%gT1Vqn<!VAKCWZP}iNtaJ1SdH(qw4cU*Y7efoXGY7-0vmG6
zE8SnT9;u84&)uV;OfQjgjv2m>>?-3q>+nulKo^M6DI1BYtt6DmqZ>)!mA7KA3niYO
zu<>d$ixi<W0q%S1LwdS4v$<re5pfE=wywFyFx+f)>1XT0t+5GC<Xc1^qt6OCMq*XC
za@0s@M9B9bb#Bo2;OOws7Gfi6PVAXGf)BqS3#ErKQac=+LvPZ&|GO&`u^wsu{O#|j
zr(Hb{j^gkKThfLSli!73%-XX7{x$U4Q$Z?MsdzM1>=UhLTCQpjiG(}+1*N0|{(!OQ
zvM=j<yLtUT#p54e+BK;889vI2unx=xDAISLxV%_q#ozL)bFJS<4}T-bkmtfSgxvI~
zWY|xy=Z2rDpT6lKC%p9Ys<;EbTsT`-Abh#EvTS2+?a<*vXO4qs-Rh)Mg=Y$?*TccN
zxus6D3R$jAjt$F{mVGs^V~zBe%yDt`enXl-9I~{6pEdpsH$l2SOOVbTgS{{#IrIv6
z58kG*m>@+&zZuBOQZ8v`XOb-}E5t&#bD2S_kz;Ag`^9EAx5&<ID`vCZN__7`h`01j
zPG39u=3Gf{<5$6pI(IEdf06Y1v+X<YKl)_?dwaI}9D8Rcdw?Kb=A_Bk0-8gfLCEt5
zv!O6nI99N!``}kDv2!ZW*(oglFaK0Q<F119dF9?t^s-+4=G8S27Q4I&^YE_G!aT_q
zg;1VhCUJJNmIpwPoqJ>(6U8%WrK>(*C2l~6a>|-#nJH7egCwqKTze6jw~E`-UQ5x~
zse9WhY&F&8=N?|RUb_U#RR{1O0_vSSWHZ1G5~NaCwqfyMOdBuS;}zjf&VvU1cLZ2Q
zodtN4Pc~#3f{U0?7Q~1V{DjLQG8_eUMqVZ&UKkAg*6H$aXjYC12FfJvrE~Vc{ErKc
zcK&ETsPlC1=0{rXuiNjfNuIP>w{gtovom$cv@BmD%kXdnjH+8~(P7AW;LWiKUIq}B
zHTiR>r;*}-<?3OMwFX%=fcWaFunI%ACD{rS-^YH35@Y2AhqLC*SmZN=uUBJ;5RgEs
zhk5^7_JoO;$PaQb_6cWm-H`$tqt8gcJH7mU)R)@D4#3usnH&zDR_dw{B0sG{6D_4k
zpH+gn+e7vMgV>H|r{Yxv;w!eVRVgfJL@G@autX)>lTu?=%|HSP9)#HaNHhtpJG|aM
zckMFguP0}2c1Res6p*gRtKE#Gr>3X6BbR*qR;zt^<or60_nOP97<!%U)18vlKA%rI
zHvXxCW8+_#4TVX<?;AFC86DpJ$pni{Nle1{+D|6@<pKaOuDw+b%?sL96q$+{u-xQG
zsaYaqufIjzy*z-P>?R7!c|H{`Zw%`=N!TuN)PXk4lVmQEgxYI}RH(^Pvx&+JG|{Z2
zOBPv3$mDK!uEva1xX-!ZGGxe=e^ZcNKyQ`IGv#$H;hAstjJR0F9mzloGUfZyEd>X-
zQCWj`O$V~D6=?8MK0k`?1dk+Qmsx@ZrF=oL5BxC}E1HKsNRMDR)f`TqnLwt>78ju+
zPHDAc%h#H=>PH4mf6%$*V^-aV^fApGe6BXju8;J#k$5V4bayem^;uIZ22#YoQoc)6
zuRqsHuDbTPLoN>L4~A3+dfHQMc}eZja=@LpSebJ*(L}<)T3_AjC&|(2m%rK$QB=Kc
zO`k&~Z?3$ffw#gdwnJ0fy-Oc+J2D1}$yYzxdmIYMnrdIIt6A$qyc)6*8ud_LrNv9E
zP(7-uawxbXL~S>p=aW&?CA8(u$5Prqu&!obR(P6dXgV?_pCO@Qm`X|{6s(w1_wzIm
z`s#t*^M~GU*J`J8YQ;eYIB`xxL#o1X@~ij$3pd+dO4Ck1+7NSpV#|-z#&l0#{nBtW
z*;jk$AEDqF{8wf};Wc3_^s{dBuNwP5sc<aGk}&aSDE#FDKwo@&|I262{gN`VBhvfx
za=d#4%xd$9`%acZlX{CUShIA&M1?0+LwDmTa7>Af@U%RD2;r2Fd_WI!O!vtNhX_hL
z)d!B7yFdS|IoA9MfzLD<T$)}V7Q1F}8_#dYW@6g{x63<X=n)3}=LFNFHms$+AQ?3X
zay0@hF$>Lo$+rU{^wJ%%qb{PN1N<n&8grR@x9zmU(L@MZfUOQ-M*)y!78*<9YE*G}
zX2G_Zfp0o7z3jTnsVf`bR`)(PcYTsJIgr1MH)3pi+3t&*cfxHBN3a{iq|;&biQyei
z;kiGAvyX=5{ea!#PXZ+&&h2ZOGv=ul*m1xoeu%q{5mD~R-Ug476f)!`&C29V1Y)Xp
z3nrPdYY0|Y<U96ntPK?Q9=2$4Gi7}X=be?ung}O{m3Z-MsuySLJe4<@=3v3JvW~Mt
zj4XN--y?kn;g<1nNGo0@Fo%H*(_H#)=_(*sB&ERvqVY`mU8xxum{+tBj?Q*2LU`~t
zw2fG3E%g$iR&D%oui@L-SvCWeEeu#pz+cm9--j`Sztd_zPMr7q`oyZ3r#GUuzO!6;
zWMYx!KU4tl=umS>YA{kyQ}UnAhQh1DvF}_pMxl)Zb0*9aCDH#=D7chBUpqJ|60Z-I
z;@FU7n&Xal>T5!#u)99o?<a|Q1`oioNY!P6U#te{0=d4aO1J+FQf8*o>-R=VjIDHf
z!jOlQW@hYqcC9hmh%aSxNw)%7EXVyEbjpSg>~Y)%7AF+>fi#L2jk3!uHlX<ZNT2a7
zfNwJo3`h05vGbq=U3ma9LAJv8EJ2FVHE85#BmyiY%YI?I6FhWJtMyS0qEtWFP*|h2
zJD(jp+aErt#8_dB_I+31wd%f<Eq{MWIk~y;4)(<w%Ssc<k3AC)e409P9%ke(%OCA(
zh<1r0h?@>^Jky)RWJ)6W5rsx`QcKyW61cB)<@cx{s*WkJW>}40MA)GN`eKgv76v&`
zfaB-gc%R?=F63pT7!uve_<dMKNc$B+RHG`$d3@;6!!Kv}+n2Rj&)!WcOG;Xw3+~37
zskWvpD6_w>4kJ91?&*quJXL*u5BK(p_T3|?PhN3XtUfytar$&VNnI_-D_(EsMSl~T
zdwT4`z#4ni-bw9}dhNxSNv-*JondhNqw&ci)rV>SjcK&(k*V^K2Pc=k@hE3KK0iJG
zeBjUVk3M|8{eg`YaVqAck6<d>&hKHi<v(i=jkBRJQ8+fX*|+qtZTy@H^Q&w8k_!IV
zXiOLWm;Ug79{?Z`F#ymrLSmgrJ2DmZew$Wwa{siGHZMJ536w0|J1#jsa+GxSet8Ri
zA!nw|0>*Xrl))f8OTVluN?Gi9N;^u-<e<F6ZTvD3q!vl6ojNgNk_N4>nP&ijXdYch
zA*{&YYB)BBy2fYHhE9f91a}1^tN*cx5tz-O_ot8#tP6hBIoqOCH=JVEutOL~&PKM-
z1%V=w(@TMzHl`>z`q`-yTQ1BRIxb?6GGqbt#Rv_DL5C@FMWCKo2xC|vKqrQ$ZM68}
zRG0l9E>Ez<Tmc=n!<?a?#ufo`D$ZSIl}81Pwwi&=J1$cxn(rt2;vTJ<d*!tTl_H@&
z9}O&pvBLS<eBnPG6#g*bpb+<R*BXm(88B;te0Gl)tfc_}eRj+F|DOr}=6}S00EM1b
A9RL6T
--- a/gfx/2d/SourceSurfaceSkia.cpp
+++ b/gfx/2d/SourceSurfaceSkia.cpp
@@ -111,20 +111,21 @@ SourceSurfaceSkia::GetData()
     if (size.isValid()) {
       if (sk_sp<SkData> data = SkData::MakeUninitialized(size.value())) {
         SkImageInfo info = MakeSkiaImageInfo(mSize, mFormat);
         if (mImage->readPixels(info, data->writable_data(), mStride, 0, 0, SkImage::kDisallow_CachingHint)) {
           raster = SkImage::MakeRasterData(info, data, mStride);
         }
       }
     }
-    if (!raster) {
+    if (raster) {
+      mImage = raster;
+    } else {
       gfxCriticalError() << "Failed making Skia raster image for GPU surface";
     }
-    mImage = raster;
   }
 #endif
   SkPixmap pixmap;
   if (!mImage->peekPixels(&pixmap)) {
     gfxCriticalError() << "Failed accessing pixels for Skia raster image";
   }
   return reinterpret_cast<uint8_t*>(pixmap.writable_addr());
 }
--- a/image/ScriptedNotificationObserver.cpp
+++ b/image/ScriptedNotificationObserver.cpp
@@ -26,16 +26,19 @@ ScriptedNotificationObserver::ScriptedNo
 : mInner(aInner)
 { }
 
 NS_IMETHODIMP
 ScriptedNotificationObserver::Notify(imgIRequest* aRequest,
                                      int32_t aType,
                                      const nsIntRect* /*aUnused*/)
 {
+  MOZ_RELEASE_ASSERT(js::AllowGCBarriers(CycleCollectedJSContext::Get()->Context()),
+                     "sending image notification to JS observer during painting. See bug 1311841");
+
   if (aType == imgINotificationObserver::SIZE_AVAILABLE) {
     return mInner->SizeAvailable(aRequest);
   }
   if (aType == imgINotificationObserver::FRAME_UPDATE) {
     return mInner->FrameUpdate(aRequest);
   }
   if (aType == imgINotificationObserver::FRAME_COMPLETE) {
     return mInner->FrameComplete(aRequest);
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1517,16 +1517,27 @@ OptimizeMIR(MIRGenerator* mir)
         gs.spewPass("Prune Unused Branches");
         AssertBasicGraphCoherency(graph);
 
         if (mir->shouldCancel("Prune Unused Branches"))
             return false;
     }
 
     {
+        AutoTraceLog log(logger, TraceLogger_FoldEmptyBlocks);
+        if (!FoldEmptyBlocks(graph))
+            return false;
+        gs.spewPass("Fold Empty Blocks");
+        AssertBasicGraphCoherency(graph);
+
+        if (mir->shouldCancel("Fold Empty Blocks"))
+            return false;
+    }
+
+    {
         AutoTraceLog log(logger, TraceLogger_FoldTests);
         if (!FoldTests(graph))
             return false;
         gs.spewPass("Fold Tests");
         AssertBasicGraphCoherency(graph);
 
         if (mir->shouldCancel("Fold Tests"))
             return false;
--- a/js/src/jit/IonAnalysis.cpp
+++ b/js/src/jit/IonAnalysis.cpp
@@ -918,16 +918,52 @@ jit::FoldTests(MIRGraph& graph)
 {
     for (MBasicBlockIterator block(graph.begin()); block != graph.end(); block++) {
         if (!MaybeFoldConditionBlock(graph, *block))
             return false;
     }
     return true;
 }
 
+bool
+jit::FoldEmptyBlocks(MIRGraph& graph)
+{
+    for (MBasicBlockIterator iter(graph.begin()); iter != graph.end(); ) {
+        MBasicBlock* block = *iter;
+        iter++;
+
+        if (block->numPredecessors() != 1 || block->numSuccessors() != 1)
+            continue;
+
+        if (!block->phisEmpty())
+            continue;
+
+        if (block->outerResumePoint())
+            continue;
+
+        if (*block->begin() != *block->rbegin())
+            continue;
+
+        MBasicBlock* succ = block->getSuccessor(0);
+        MBasicBlock* pred = block->getPredecessor(0);
+
+        if (succ->numPredecessors() != 1)
+            continue;
+
+        size_t pos = pred->getSuccessorIndex(block);
+        pred->lastIns()->replaceSuccessor(pos, succ);
+
+        graph.removeBlock(block);
+
+        succ->addPredecessorSameInputsAs(pred, block);
+        succ->removePredecessor(block);
+    }
+    return true;
+}
+
 static void
 EliminateTriviallyDeadResumePointOperands(MIRGraph& graph, MResumePoint* rp)
 {
     // If we will pop the top of the stack immediately after resuming,
     // then don't preserve the top value in the resume point.
     if (rp->mode() != MResumePoint::ResumeAt || *rp->pc() != JSOP_POP)
         return;
 
--- a/js/src/jit/IonAnalysis.h
+++ b/js/src/jit/IonAnalysis.h
@@ -20,16 +20,19 @@ class MIRGraph;
 
 MOZ_MUST_USE bool
 PruneUnusedBranches(MIRGenerator* mir, MIRGraph& graph);
 
 MOZ_MUST_USE bool
 FoldTests(MIRGraph& graph);
 
 MOZ_MUST_USE bool
+FoldEmptyBlocks(MIRGraph& graph);
+
+MOZ_MUST_USE bool
 SplitCriticalEdges(MIRGraph& graph);
 
 bool
 IsUint32Type(const MDefinition* def);
 
 enum Observability {
     ConservativeObservability,
     AggressiveObservability
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -37,16 +37,17 @@
     _(VM)                                             \
     _(CompressSource)                                 \
     _(WasmCompilation)                                \
     _(Call)                                           \
                                                       \
     /* Specific passes during ion compilation */      \
     _(PruneUnusedBranches)                            \
     _(FoldTests)                                      \
+    _(FoldEmptyBlocks)                                \
     _(SplitCriticalEdges)                             \
     _(RenumberBlocks)                                 \
     _(ScalarReplacement)                              \
     _(DominatorTree)                                  \
     _(PhiAnalysis)                                    \
     _(MakeLoopsContiguous)                            \
     _(ApplyTypes)                                     \
     _(EagerSimdUnbox)                                 \
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -79,16 +79,17 @@
 #include "nsCSSProps.h"
 #include "nsListControlFrame.h"
 #include "mozilla/dom/Element.h"
 #include "nsCanvasFrame.h"
 #include "gfxDrawable.h"
 #include "gfxEnv.h"
 #include "gfxUtils.h"
 #include "nsDataHashtable.h"
+#include "nsTableWrapperFrame.h"
 #include "nsTextFrame.h"
 #include "nsFontFaceList.h"
 #include "nsFontInflationData.h"
 #include "nsSVGUtils.h"
 #include "SVGImageContext.h"
 #include "SVGTextFrame.h"
 #include "nsStyleStructInlines.h"
 #include "nsStyleTransformMatrix.h"
@@ -5863,16 +5864,29 @@ nsLayoutUtils::GetFirstLinePosition(Writ
   const nsBlockFrame* block = nsLayoutUtils::GetAsBlock(const_cast<nsIFrame*>(aFrame));
   if (!block) {
     // For the first-line baseline we also have to check for a table, and if
     // so, use the baseline of its first row.
     nsIAtom* fType = aFrame->GetType();
     if (fType == nsGkAtoms::tableWrapperFrame  ||
         fType == nsGkAtoms::flexContainerFrame ||
         fType == nsGkAtoms::gridContainerFrame) {
+      if ((fType == nsGkAtoms::gridContainerFrame &&
+           aFrame->HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) ||
+          (fType == nsGkAtoms::flexContainerFrame &&
+           aFrame->HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) ||
+          (fType == nsGkAtoms::tableWrapperFrame &&
+           static_cast<const nsTableWrapperFrame*>(aFrame)->GetRowCount() == 0)) {
+        // empty grid/flex/table container
+        aResult->mBStart = 0;
+        aResult->mBaseline = aFrame->SynthesizeBaselineBOffsetFromBorderBox(aWM,
+                                       BaselineSharingGroup::eFirst);
+        aResult->mBEnd = aFrame->BSize(aWM);
+        return true;
+      }
       aResult->mBStart = 0;
       aResult->mBaseline = aFrame->GetLogicalBaseline(aWM);
       // This is what we want for the list bullet caller; not sure if
       // other future callers will want the same.
       aResult->mBEnd = aFrame->BSize(aWM);
       return true;
     }
 
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -485,22 +485,54 @@ nsBlockFrame::InvalidateFrameWithRect(co
                  "unexpected block frame in SVG text");
     GetParent()->InvalidateFrame();
     return;
   }
   nsContainerFrame::InvalidateFrameWithRect(aRect, aDisplayItemKey);
 }
 
 nscoord
-nsBlockFrame::GetLogicalBaseline(WritingMode aWritingMode) const
-{
-  nscoord result;
-  if (nsLayoutUtils::GetLastLineBaseline(aWritingMode, this, &result))
-    return result;
-  return nsFrame::GetLogicalBaseline(aWritingMode);
+nsBlockFrame::GetLogicalBaseline(WritingMode aWM) const
+{
+  auto lastBaseline =
+    BaselineBOffset(aWM, BaselineSharingGroup::eLast, AlignmentContext::eInline);
+  return BSize(aWM) - lastBaseline;
+}
+
+bool
+nsBlockFrame::GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                        BaselineSharingGroup aBaselineGroup,
+                                        nscoord*             aBaseline) const
+{
+  if (aBaselineGroup == BaselineSharingGroup::eFirst) {
+    return nsLayoutUtils::GetFirstLineBaseline(aWM, this, aBaseline);
+  }
+
+  for (ConstReverseLineIterator line = LinesRBegin(), line_end = LinesREnd();
+       line != line_end; ++line) {
+    if (line->IsBlock()) {
+      nscoord offset;
+      nsIFrame* kid = line->mFirstChild;
+      if (kid->GetVerticalAlignBaseline(aWM, &offset)) {
+        // Ignore relative positioning for baseline calculations.
+        const nsSize& sz = line->mContainerSize;
+        offset += kid->GetLogicalNormalPosition(aWM, sz).B(aWM);
+        *aBaseline = BSize(aWM) - offset;
+        return true;
+      }
+    } else {
+      // XXX Is this the right test?  We have some bogus empty lines
+      // floating around, but IsEmpty is perhaps too weak.
+      if (line->BSize() != 0 || !line->IsEmpty()) {
+        *aBaseline = BSize(aWM) - (line->BStart() + line->GetLogicalAscent());
+        return true;
+      }
+    }
+  }
+  return false;
 }
 
 nscoord
 nsBlockFrame::GetCaretBaseline() const
 {
   nsRect contentRect = GetContentRect();
   nsMargin bp = GetUsedBorderAndPadding();
 
--- a/layout/generic/nsBlockFrame.h
+++ b/layout/generic/nsBlockFrame.h
@@ -116,16 +116,29 @@ public:
   virtual void InsertFrames(ChildListID aListID,
                             nsIFrame* aPrevFrame,
                             nsFrameList& aFrameList) override;
   virtual void RemoveFrame(ChildListID aListID,
                            nsIFrame* aOldFrame) override;
   virtual const nsFrameList& GetChildList(ChildListID aListID) const override;
   virtual void GetChildLists(nsTArray<ChildList>* aLists) const override;
   virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override
+  {
+    nscoord lastBaseline;
+    if (GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eLast, &lastBaseline)) {
+      *aBaseline = BSize() - lastBaseline;
+      return true;
+    }
+    return false;
+  }
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override;
   virtual nscoord GetCaretBaseline() const override;
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
   virtual nsSplittableType GetSplittableType() const override;
   virtual bool IsFloatContainingBlock() const override;
   virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
                                 const nsRect& aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
   virtual nsIAtom* GetType() const override;
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -1016,16 +1016,17 @@ nsContainerFrame::ReflowChild(nsIFrame* 
 
   // Position the child frame and its view if requested.
   if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
     aKidFrame->SetPosition(aWM, aPos, aContainerSize);
   }
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aKidFrame);
+    PositionChildViews(aKidFrame);
   }
 
   // Reflow the child frame
   aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
 
   // If the child frame is complete, delete any next-in-flows,
   // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
   if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) &&
@@ -1059,16 +1060,17 @@ nsContainerFrame::ReflowChild(nsIFrame* 
 
   // Position the child frame and its view if requested.
   if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) {
     aKidFrame->SetPosition(nsPoint(aX, aY));
   }
 
   if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) {
     PositionFrameView(aKidFrame);
+    PositionChildViews(aKidFrame);
   }
 
   // Reflow the child frame
   aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowInput, aStatus);
 
   // If the child frame is complete, delete any next-in-flows,
   // but only if the NO_DELETE_NEXT_IN_FLOW flag isn't set.
   if (NS_FRAME_IS_FULLY_COMPLETE(aStatus) &&
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -450,17 +450,18 @@ public:
       // or a dependency.
       // Use GetFirstLineBaseline() or GetLastLineBaseline() as appropriate,
       // or just GetLogicalBaseline() if that fails.
       bool found = aUseFirstBaseline ?
         nsLayoutUtils::GetFirstLineBaseline(mWM, mFrame, &mAscent) :
         nsLayoutUtils::GetLastLineBaseline(mWM, mFrame, &mAscent);
 
       if (!found) {
-        mAscent = mFrame->GetLogicalBaseline(mWM);
+        mAscent = mFrame->SynthesizeBaselineBOffsetFromBorderBox(mWM,
+                            BaselineSharingGroup::eFirst);
       }
     }
     return mAscent;
   }
 
   // Convenience methods to compute the main & cross size of our *margin-box*.
   // The caller is responsible for telling us the right axis, so that we can
   // pull out the appropriate components of our margin/border/padding structs.
@@ -2319,16 +2320,20 @@ nsFlexContainerFrame::GetFrameName(nsASt
 #endif
 
 nscoord
 nsFlexContainerFrame::GetLogicalBaseline(mozilla::WritingMode aWM) const
 {
   NS_ASSERTION(mBaselineFromLastReflow != NS_INTRINSIC_WIDTH_UNKNOWN,
                "baseline has not been set");
 
+  if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+    // Return a baseline synthesized from our margin-box.
+    return nsContainerFrame::GetLogicalBaseline(aWM);
+  }
   return mBaselineFromLastReflow;
 }
 
 // Helper for BuildDisplayList, to implement this special-case for flex items
 // from the spec:
 //    Flex items paint exactly the same as block-level elements in the
 //    normal flow, except that 'z-index' values other than 'auto' create
 //    a stacking context even if 'position' is 'static'.
@@ -4254,16 +4259,24 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
   AutoFlexLineListClearer cleanupLines(lines);
 
   GenerateFlexLines(aPresContext, aReflowInput,
                     aContentBoxMainSize,
                     aAvailableBSizeForContent,
                     aStruts, aAxisTracker,
                     placeholderKids, lines);
 
+  if (lines.getFirst()->IsEmpty() &&
+      !lines.getFirst()->getNext()) {
+    // We have no flex items, our parent should synthesize a baseline if needed.
+    AddStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
+  } else {
+    RemoveStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE);
+  }
+
   aContentBoxMainSize =
     ResolveFlexContainerMainSize(aReflowInput, aAxisTracker,
                                  aContentBoxMainSize, aAvailableBSizeForContent,
                                  lines.getFirst(), aStatus);
 
   for (FlexLine* line = lines.getFirst(); line; line = line->getNext()) {
     line->ResolveFlexibleLengths(aContentBoxMainSize);
   }
@@ -4514,22 +4527,25 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
     // Per spec, synthesize baseline from the flex container's content box
     // (i.e. use block-end side of content-box)
     // XXXdholbert This only makes sense if parent's writing mode is
     // horizontal (& even then, really we should be using the BSize in terms
     // of the parent's writing mode, not ours). Clean up in bug 1155322.
     flexContainerAscent = desiredSizeInFlexWM.BSize(flexWM);
   }
 
-  // XXXdholbert flexContainerAscent needs to be in terms of
-  // our parent's writing-mode here. See bug 1155322.
-  aDesiredSize.SetBlockStartAscent(flexContainerAscent);
-
-  // Cache this baseline for use outside of this call.
-  mBaselineFromLastReflow = flexContainerAscent;
+  if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+    // This will force our parent to call GetLogicalBaseline, which will
+    // synthesize a margin-box baseline.
+    aDesiredSize.SetBlockStartAscent(ReflowOutput::ASK_FOR_BASELINE);
+  } else {
+    // XXXdholbert flexContainerAscent needs to be in terms of
+    // our parent's writing-mode here. See bug 1155322.
+    aDesiredSize.SetBlockStartAscent(flexContainerAscent);
+  }
 
   // Now: If we're complete, add bottom border/padding to desired height (which
   // we skipped via skipSides) -- unless that pushes us over available height,
   // in which case we become incomplete (unless we already weren't asking for
   // any height, in which case we stay complete to avoid looping forever).
   // NOTE: If we're auto-height, we allow our bottom border/padding to push us
   // over the available height without requesting a continuation, for
   // consistency with the behavior of "display:block" elements.
@@ -4544,16 +4560,26 @@ nsFlexContainerFrame::DoFlexLayout(nsPre
       // Update desired height to include block-end border/padding
       desiredSizeInFlexWM.BSize(flexWM) = desiredBSizeWithBEndBP;
     } else {
       // We couldn't fit bottom border/padding, so we'll need a continuation.
       NS_FRAME_SET_INCOMPLETE(aStatus);
     }
   }
 
+  // Calculate the container baselines so that our parent can baseline-align us.
+  mBaselineFromLastReflow = flexContainerAscent;
+  mLastBaselineFromLastReflow = lines.getLast()->GetLastBaselineOffset();
+  if (mLastBaselineFromLastReflow == nscoord_MIN) {
+    // XXX we fall back to a mirrored first baseline here for now, but this
+    // should probably use the last baseline of the last item or something.
+    mLastBaselineFromLastReflow =
+      desiredSizeInFlexWM.BSize(flexWM) - flexContainerAscent;
+  }
+
   // Convert flex container's final desired size to parent's WM, for outparam.
   aDesiredSize.SetSize(flexWM, desiredSizeInFlexWM);
 
   // Overflow area = union(my overflow area, kids' overflow areas)
   aDesiredSize.SetOverflowAreasToDesiredBounds();
   for (nsIFrame* childFrame : mFrames) {
     ConsiderChildOverflow(aDesiredSize.mOverflowAreas, childFrame);
   }
--- a/layout/generic/nsFlexContainerFrame.h
+++ b/layout/generic/nsFlexContainerFrame.h
@@ -78,16 +78,34 @@ public:
 
   virtual nsIAtom* GetType() const override;
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override;
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override
+  {
+    return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
+  }
+
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override
+  {
+    if (HasAnyStateBits(NS_STATE_FLEX_SYNTHESIZE_BASELINE)) {
+      return false;
+    }
+    *aBaseline = aBaselineGroup == BaselineSharingGroup::eFirst ?
+                   mBaselineFromLastReflow : mLastBaselineFromLastReflow;
+    return true;
+  }
+
   // nsContainerFrame overrides
   uint16_t CSSAlignmentForAbsPosChild(
             const ReflowInput& aChildRI,
             mozilla::LogicalAxis aLogicalAxis) const override;
 
   // Flexbox-specific public methods
   bool IsHorizontal();
 
@@ -111,16 +129,17 @@ public:
                                     uint32_t* aNumPackingSpacesRemaining,
                                     nscoord* aPackingSpaceRemaining);
 
 protected:
   // Protected constructor & destructor
   explicit nsFlexContainerFrame(nsStyleContext* aContext)
     : nsContainerFrame(aContext)
     , mBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
+    , mLastBaselineFromLastReflow(NS_INTRINSIC_WIDTH_UNKNOWN)
   {}
   virtual ~nsFlexContainerFrame();
 
   /*
    * This method does the bulk of the flex layout, implementing the algorithm
    * described at:
    *   http://dev.w3.org/csswg/css-flexbox/#layout-algorithm
    * (with a few initialization pieces happening in the caller, Reflow().
@@ -296,11 +315,13 @@ protected:
                           nsTArray<nsIFrame*>& aPlaceholders,
                           const mozilla::LogicalPoint& aContentBoxOrigin,
                           const nsSize& aContainerSize);
 
   bool mChildrenHaveBeenReordered; // Have we ever had to reorder our kids
                                    // to satisfy their 'order' values?
 
   nscoord mBaselineFromLastReflow;
+  // Note: the last baseline is a distance from our border-box end edge.
+  nscoord mLastBaselineFromLastReflow;
 };
 
 #endif /* nsFlexContainerFrame_h___ */
--- a/layout/generic/nsFrameStateBits.h
+++ b/layout/generic/nsFrameStateBits.h
@@ -306,16 +306,19 @@ FRAME_STATE_GROUP(FlexContainer, nsFlexC
 // Set for a flex container whose children have been reordered due to 'order'.
 // (Means that we have to be more thorough about checking them for sortedness.)
 FRAME_STATE_BIT(FlexContainer, 20, NS_STATE_FLEX_CHILDREN_REORDERED)
 
 // Set for a flex container that is emulating a legacy
 // 'display:-webkit-{inline-}box' container.
 FRAME_STATE_BIT(FlexContainer, 21, NS_STATE_FLEX_IS_LEGACY_WEBKIT_BOX)
 
+// True if the container has no flex items; may lie if there is a pending reflow
+FRAME_STATE_BIT(FlexContainer, 22, NS_STATE_FLEX_SYNTHESIZE_BASELINE)
+
 // == Frame state bits that apply to grid container frames ====================
 
 FRAME_STATE_GROUP(GridContainer, nsGridContainerFrame)
 
 // True iff the normal flow children are already in CSS 'order' in the
 // order they occur in the child frame list.
 FRAME_STATE_BIT(GridContainer, 20, NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER)
 
@@ -323,16 +326,19 @@ FRAME_STATE_BIT(GridContainer, 20, NS_ST
 // Note that those child frames may have been removed without this bit
 // being updated for performance reasons, so code shouldn't depend on
 // actually finding any pushed items when this bit is set.
 FRAME_STATE_BIT(GridContainer, 21, NS_STATE_GRID_DID_PUSH_ITEMS)
 
 // True iff computed grid values should be generated on the next reflow
 FRAME_STATE_BIT(GridContainer, 22, NS_STATE_GRID_GENERATE_COMPUTED_VALUES)
 
+// True if the container has no grid items; may lie if there is a pending reflow
+FRAME_STATE_BIT(GridContainer, 23, NS_STATE_GRID_SYNTHESIZE_BASELINE)
+
 // == Frame state bits that apply to SVG frames ===============================
 
 FRAME_STATE_GROUP(SVG, nsISVGChildFrame)
 FRAME_STATE_GROUP(SVG, nsSVGContainerFrame)
 
 FRAME_STATE_BIT(SVG, 20, NS_STATE_IS_OUTER_SVG)
 
 // If this bit is set, we are a <clipPath> element or descendant.
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -6154,8 +6154,65 @@ ScrollFrameHelper::GetSnapPointForDestin
 bool
 ScrollFrameHelper::UsesContainerScrolling() const
 {
   if (gfxPrefs::LayoutUseContainersForRootFrames()) {
     return mIsRoot;
   }
   return false;
 }
+
+bool
+ScrollFrameHelper::DragScroll(WidgetEvent* aEvent)
+{
+  // Dragging is allowed while within a 20 pixel border. Note that device pixels
+  // are used so that the same margin is used even when zoomed in or out.
+  nscoord margin = 20 * mOuter->PresContext()->AppUnitsPerDevPixel();
+
+  // Don't drag scroll for small scrollareas.
+  if (mScrollPort.width < margin * 2 || mScrollPort.height < margin * 2) {
+    return false;
+  }
+
+  // If willScroll is computed as false, then the frame is already scrolled as
+  // far as it can go in both directions. Return false so that an ancestor
+  // scrollframe can scroll instead.
+  bool willScroll = false;
+  nsPoint pnt = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, mOuter);
+  nsPoint scrollPoint = GetScrollPosition();
+  nsRect rangeRect = GetScrollRangeForClamping();
+
+  // Only drag scroll when a scrollbar is present.
+  nsPoint offset;
+  if (mHasHorizontalScrollbar) {
+    if (pnt.x >= mScrollPort.x && pnt.x <= mScrollPort.x + margin) {
+      offset.x = -margin;
+      if (scrollPoint.x > 0) {
+        willScroll = true;
+      }
+    } else if (pnt.x >= mScrollPort.XMost() - margin && pnt.x <= mScrollPort.XMost()) {
+      offset.x = margin;
+      if (scrollPoint.x < rangeRect.width) {
+        willScroll = true;
+      }
+    }
+  }
+
+  if (mHasVerticalScrollbar) {
+    if (pnt.y >= mScrollPort.y && pnt.y <= mScrollPort.y + margin) {
+      offset.y = -margin;
+      if (scrollPoint.y > 0) {
+        willScroll = true;
+      }
+    } else if (pnt.y >= mScrollPort.YMost() - margin && pnt.y <= mScrollPort.YMost()) {
+      offset.y = margin;
+      if (scrollPoint.y < rangeRect.height) {
+        willScroll = true;
+      }
+    }
+  }
+
+  if (offset.x || offset.y) {
+    ScrollTo(GetScrollPosition() + offset, nsIScrollableFrame::NORMAL);
+  }
+
+  return willScroll;
+}
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -455,16 +455,18 @@ public:
                     int32_t aDirection,
                     nsIScrollableFrame::ScrollUnit aUnit,
                     nsIScrollbarMediator::ScrollSnapMode aSnap
                       = nsIScrollbarMediator::DISABLE_SNAP);
   bool ShouldSuppressScrollbarRepaints() const {
     return mSuppressScrollbarRepaints;
   }
 
+  bool DragScroll(WidgetEvent* aEvent);
+
   // owning references to the nsIAnonymousContentCreator-built content
   nsCOMPtr<nsIContent> mHScrollbarContent;
   nsCOMPtr<nsIContent> mVScrollbarContent;
   nsCOMPtr<nsIContent> mScrollCornerContent;
   nsCOMPtr<nsIContent> mResizerContent;
 
   RefPtr<ScrollEvent> mScrollEvent;
   nsRevocableEventPtr<AsyncScrollPortEvent> mAsyncScrollPortEvent;
@@ -719,16 +721,22 @@ public:
                       ReflowOutput&     aDesiredSize,
                       const ReflowInput& aReflowInput,
                       nsReflowStatus&          aStatus) override;
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
     return mHelper.ComputeCustomOverflow(aOverflowAreas);
   }
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override {
+    *aBaseline = GetLogicalBaseline(aWM);
+    return true;
+  }
+
   // Recomputes the scrollable overflow area we store in the helper to take children
   // that are affected by perpsective set on the outer frame and scroll at different
   // rates.
   void AdjustForPerspective(nsRect& aScrollableOverflow);
 
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   virtual void SetInitialChildList(ChildListID     aListID,
@@ -1028,16 +1036,20 @@ public:
   void SetScrollsClipOnUnscrolledOutOfFlow() override {
     mHelper.SetScrollsClipOnUnscrolledOutOfFlow();
   }
   
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
+  virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
+    return mHelper.DragScroll(aEvent);
+  }
+
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
 #ifdef ACCESSIBILITY
   virtual mozilla::a11y::AccType AccessibleType() override;
 #endif
 
@@ -1105,16 +1117,22 @@ public:
 #if 0
   virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
 #endif
 
   virtual bool ComputeCustomOverflow(nsOverflowAreas& aOverflowAreas) override {
     return mHelper.ComputeCustomOverflow(aOverflowAreas);
   }
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override {
+    *aBaseline = GetLogicalBaseline(aWM);
+    return true;
+  }
+
   // Called to set the child frames. We typically have three: the scroll area,
   // the vertical scrollbar, and the horizontal scrollbar.
   virtual void SetInitialChildList(ChildListID     aListID,
                                    nsFrameList&    aChildList) override;
   virtual void AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList) override;
   virtual void InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
@@ -1451,16 +1469,20 @@ public:
   void TriggerDisplayPortExpiration() override {
     mHelper.TriggerDisplayPortExpiration();
   }
 
   ScrollSnapInfo GetScrollSnapInfo() const override {
     return mHelper.GetScrollSnapInfo();
   }
 
+  virtual bool DragScroll(mozilla::WidgetEvent* aEvent) override {
+    return mHelper.DragScroll(aEvent);
+  }
+
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
 protected:
   nsXULScrollFrame(nsStyleContext* aContext, bool aIsRoot,
                    bool aClipAllDescendants);
 
--- a/layout/generic/nsGridContainerFrame.cpp
+++ b/layout/generic/nsGridContainerFrame.cpp
@@ -193,17 +193,17 @@ enum class GridLineSide
 struct nsGridContainerFrame::TrackSize
 {
   enum StateBits : uint16_t {
     eAutoMinSizing =              0x1,
     eMinContentMinSizing =        0x2,
     eMaxContentMinSizing =        0x4,
     eMinOrMaxContentMinSizing = eMinContentMinSizing | eMaxContentMinSizing,
     eIntrinsicMinSizing = eMinOrMaxContentMinSizing | eAutoMinSizing,
-    eIndefinitePercentMinSizing = 0x8,
+    // 0x8 is unused, feel free to take it!
     eAutoMaxSizing =             0x10,
     eMinContentMaxSizing =       0x20,
     eMaxContentMaxSizing =       0x40,
     eAutoOrMaxContentMaxSizing = eAutoMaxSizing | eMaxContentMaxSizing,
     eIntrinsicMaxSizing = eAutoOrMaxContentMaxSizing | eMinContentMaxSizing,
     eFlexMaxSizing =             0x80,
     eFrozen =                   0x100,
     eSkipGrowUnlimited1 =       0x200,
@@ -264,17 +264,16 @@ nsGridContainerFrame::TrackSize::Initial
     minSizeUnit = eStyleUnit_Auto;
     maxSizeUnit = eStyleUnit_Enumerated; // triggers max-content sizing below
   }
   if (::IsPercentOfIndefiniteSize(aMinCoord, aPercentageBasis)) {
     // https://drafts.csswg.org/css-grid/#valdef-grid-template-columns-percentage
     // "If the inline or block size of the grid container is indefinite,
     //  <percentage> values relative to that size are treated as 'auto'."
     minSizeUnit = eStyleUnit_Auto;
-    mState |= eIndefinitePercentMinSizing;
   }
   if (::IsPercentOfIndefiniteSize(aMaxCoord, aPercentageBasis)) {
     maxSizeUnit = eStyleUnit_Auto;
   }
   // http://dev.w3.org/csswg/css-grid/#algo-init
   switch (minSizeUnit) {
     case eStyleUnit_Auto:
       mState |= eAutoMinSizing;
@@ -1157,69 +1156,64 @@ struct nsGridContainerFrame::TrackSizing
     const uint32_t numTracks = mMinSizingFunctions.Length();
     MOZ_ASSERT(numTracks >= 1, "expected at least the repeat() track");
     nscoord maxFill = aSize != NS_UNCONSTRAINEDSIZE ? aSize : aMaxSize;
     if (maxFill == NS_UNCONSTRAINEDSIZE && aMinSize == 0) {
       // "Otherwise, the specified track list repeats only once."
       return 1;
     }
     nscoord repeatTrackSize = 0;
-    float repeatTrackPercent = 0.0f;
     // Note that the repeat() track size is included in |sum| in this loop.
     nscoord sum = 0;
-    float percentSum = 0.0f;
     const nscoord percentBasis = aSize;
     for (uint32_t i = 0; i < numTracks; ++i) {
       // "treating each track as its max track sizing function if that is
       // definite or as its minimum track sizing function otherwise"
       // https://drafts.csswg.org/css-grid/#valdef-repeat-auto-fill
       const auto& maxCoord = mMaxSizingFunctions[i];
       const auto* coord = &maxCoord;
       if (!coord->IsCoordPercentCalcUnit()) {
         coord = &mMinSizingFunctions[i];
         if (!coord->IsCoordPercentCalcUnit()) {
           return 1;
         }
       }
-      float trackPercent;
-      nscoord trackSize;
-      ResolvePercentSizeParts(*coord, percentBasis, &trackSize, &trackPercent);
+      nscoord trackSize = ::ResolveToDefiniteSize(*coord, percentBasis);
       if (i == mRepeatAutoStart) {
         if (percentBasis != NS_UNCONSTRAINEDSIZE) {
           // Use a minimum 1px for the repeat() track-size.
           if (trackSize < AppUnitsPerCSSPixel()) {
             trackSize = AppUnitsPerCSSPixel();
           }
         }
         repeatTrackSize = trackSize;
-        repeatTrackPercent = trackPercent;
       }
       sum += trackSize;
-      percentSum += trackPercent;
     }
     nscoord gridGap;
+    float percentSum = 0.0f;
     float gridGapPercent;
     ResolvePercentSizeParts(aGridGap, percentBasis, &gridGap, &gridGapPercent);
     if (numTracks > 1) {
       // Add grid-gaps for all the tracks including the repeat() track.
       sum += gridGap * (numTracks - 1);
-      percentSum += gridGapPercent * (numTracks - 1);
+      percentSum = gridGapPercent * (numTracks - 1);
     }
     // Calculate the max number of tracks that fits without overflow.
     nscoord available = maxFill != NS_UNCONSTRAINEDSIZE ? maxFill : aMinSize;
     nscoord size = nsLayoutUtils::AddPercents(sum, percentSum);
     if (available - size < 0) {
       // "if any number of repetitions would overflow, then 1 repetition"
       return 1;
     }
     uint32_t numRepeatTracks = 1;
     bool exactFit = false;
     while (true) {
       sum += gridGap + repeatTrackSize;
-      percentSum += gridGapPercent + repeatTrackPercent;
+      percentSum += gridGapPercent;
       nscoord newSize = nsLayoutUtils::AddPercents(sum, percentSum);
       if (newSize <= size) {
         // Adding more repeat-tracks won't make forward progress.
         return numRepeatTracks;
       }
       size = newSize;
       nscoord remaining = available - size;
       exactFit = remaining == 0;
@@ -2582,24 +2576,18 @@ nsGridContainerFrame::GridReflowInput::C
   mCols.mCanResolveLineRangeSize = true;
 
   mRows.CalculateSizes(*this, mGridItems, mRowFunctions,
                        aContentBox.BSize(mWM), &GridArea::mRows,
                        aConstraint);
   if (aContentBox.BSize(mWM) == NS_AUTOHEIGHT) {
     aContentBox.BSize(mWM) =
       mRows.BackComputedIntrinsicSize(mRowFunctions, mGridStyle->mGridRowGap);
-    if ((mRows.mStateUnion & TrackSize::eIndefinitePercentMinSizing) ||
-        mGridStyle->mGridRowGap.HasPercent()) {
-      mRows.Initialize(mRowFunctions, mGridStyle->mGridRowGap,
-                       aGrid.mGridRowEnd, aContentBox.BSize(mWM));
-      mRows.CalculateSizes(*this, mGridItems, mRowFunctions,
-                           aContentBox.BSize(mWM), &GridArea::mRows,
-                           aConstraint);
-    }
+    mRows.mGridGap =
+      ::ResolveToDefiniteSize(mGridStyle->mGridRowGap, aContentBox.BSize(mWM));
   }
 }
 
 /**
  * (XXX share this utility function with nsFlexContainerFrame at some point)
  *
  * Helper for BuildDisplayList, to implement this special-case for grid
  * items from the spec:
@@ -4953,41 +4941,31 @@ nsGridContainerFrame::Tracks::AlignJusti
 }
 
 nscoord
 nsGridContainerFrame::Tracks::BackComputedIntrinsicSize(
   const TrackSizingFunctions& aFunctions,
   const nsStyleCoord& aGridGap) const
 {
   // Sum up the current sizes (where percentage tracks were treated as 'auto')
-  // in 'size' and a sum of percentages in 'percent'.
+  // in 'size'.
   nscoord size = 0;
-  float percent = 0.0f;
-  bool hasPercent = mStateUnion & TrackSize::eIndefinitePercentMinSizing;
   for (size_t i = 0, len = mSizes.Length(); i < len; ++i) {
-    const nscoord trackSize = mSizes[i].mBase;
-    nscoord length;
-    float p;
-    if (hasPercent &&
-        ::GetPercentSizeParts(aFunctions.MinSizingFor(i), &length, &p)) {
-      size += std::max(length, trackSize);
-      percent += p;
-    } else {
-      size += trackSize;
-    }
-  }
-
-  // Add grid-gap contributions to 'size' and 'percent'.
+    size += mSizes[i].mBase;
+  }
+
+  // Add grid-gap contributions to 'size' and calculate a 'percent' sum.
+  float percent = 0.0f;
   size_t numTracks = mSizes.Length();
   if (numTracks > 1) {
     const size_t gridGapCount = numTracks - 1;
     nscoord gridGapLength;
     float gridGapPercent;
     if (::GetPercentSizeParts(aGridGap, &gridGapLength, &gridGapPercent)) {
-      percent += gridGapCount * gridGapPercent;
+      percent = gridGapCount * gridGapPercent;
     } else {
       gridGapLength = aGridGap.ToLength();
     }
     size += gridGapCount * gridGapLength;
   }
 
   return std::max(0, nsLayoutUtils::AddPercents(size, percent));
 }
@@ -6113,16 +6091,22 @@ nsGridContainerFrame::Reflow(nsPresConte
     InitImplicitNamedAreas(stylePos);
   }
   GridReflowInput gridReflowInput(this, aReflowInput);
   if (gridReflowInput.mIter.ItemsAreAlreadyInOrder()) {
     AddStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   } else {
     RemoveStateBits(NS_STATE_GRID_NORMAL_FLOW_CHILDREN_IN_CSS_ORDER);
   }
+  if (gridReflowInput.mIter.AtEnd()) {
+    // We have no grid items, our parent should synthesize a baseline if needed.
+    AddStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
+  } else {
+    RemoveStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE);
+  }
   const nscoord computedBSize = aReflowInput.ComputedBSize();
   const nscoord computedISize = aReflowInput.ComputedISize();
   const WritingMode& wm = gridReflowInput.mWM;
   LogicalSize computedSize(wm, computedISize, computedBSize);
 
   nscoord consumedBSize = 0;
   nscoord bSize;
   if (!prevInFlow) {
@@ -6733,21 +6717,18 @@ nsGridContainerFrame::SynthesizeBaseline
   nscoord start;
   nscoord size;
   if (aAxis == eLogicalAxisBlock) {
     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).B(aCBWM);
     size = child->BSize(aCBWM);
     if (grid && aGridOrderItem.mIsInEdgeTrack) {
       isOrthogonal ? grid->GetIBaseline(aGroup, &baseline) :
                      grid->GetBBaseline(aGroup, &baseline);
-    } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack &&
-               GetBBaseline(aGroup, childWM, child, &baseline)) {
-      if (aGroup == BaselineSharingGroup::eLast) {
-        baseline = size - baseline; // convert to distance from border-box end
-      }
+    } else if (!isOrthogonal && aGridOrderItem.mIsInEdgeTrack) {
+      baseline = child->BaselineBOffset(childWM, aGroup, AlignmentContext::eGrid);
     } else {
       baseline = ::SynthesizeBaselineFromBorderBox(aGroup, childWM, size);
     }
   } else {
     start = child->GetLogicalNormalPosition(aCBWM, aCBPhysicalSize).I(aCBWM);
     size = child->ISize(aCBWM);
     if (grid && aGridOrderItem.mIsInEdgeTrack) {
       isOrthogonal ? grid->GetBBaseline(aGroup, &baseline) :
--- a/layout/generic/nsGridContainerFrame.h
+++ b/layout/generic/nsGridContainerFrame.h
@@ -10,24 +10,16 @@
 #define nsGridContainerFrame_h___
 
 #include "mozilla/Maybe.h"
 #include "mozilla/TypeTraits.h"
 #include "nsContainerFrame.h"
 #include "nsHashKeys.h"
 #include "nsTHashtable.h"
 
-// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
-enum BaselineSharingGroup
-{
-  // NOTE Used as an array index so must be 0 and 1.
-  eFirst = 0,
-  eLast = 1,
-};
-
 /**
  * Factory function.
  * @return a newly allocated nsGridContainerFrame (infallible)
  */
 nsContainerFrame* NS_NewGridContainerFrame(nsIPresShell* aPresShell,
                                            nsStyleContext* aContext);
 
 namespace mozilla {
@@ -106,21 +98,41 @@ public:
   }
 
   void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                         const nsRect&           aDirtyRect,
                         const nsDisplayListSet& aLists) override;
 
   nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const override
   {
+    if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
+      // Return a baseline synthesized from our margin-box.
+      return nsContainerFrame::GetLogicalBaseline(aWM);
+    }
     nscoord b;
     GetBBaseline(BaselineSharingGroup::eFirst, &b);
     return b;
   }
 
+  bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                nscoord* aBaseline) const override
+  {
+    return GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, aBaseline);
+  }
+
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override
+  {
+    if (HasAnyStateBits(NS_STATE_GRID_SYNTHESIZE_BASELINE)) {
+      return false;
+    }
+    return GetBBaseline(aBaselineGroup, aBaseline);
+  }
+
 #ifdef DEBUG_FRAME_DUMP
   nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   // nsContainerFrame overrides
   bool DrainSelfOverflowList() override;
   void AppendFrames(ChildListID aListID, nsFrameList& aFrameList) override;
   void InsertFrames(ChildListID aListID, nsIFrame* aPrevFrame,
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -412,16 +412,34 @@ enum nsBidiDirection {
   NSBIDI_LTR,
   /** All right-to-left text This is a 1 value. */
   NSBIDI_RTL,
   /** Mixed-directional text. */
   NSBIDI_MIXED
 };
 
 namespace mozilla {
+
+// https://drafts.csswg.org/css-align-3/#baseline-sharing-group
+enum BaselineSharingGroup
+{
+  // NOTE Used as an array index so must be 0 and 1.
+  eFirst = 0,
+  eLast = 1,
+};
+
+// Loosely: https://drafts.csswg.org/css-align-3/#shared-alignment-context
+enum class AlignmentContext
+{
+  eInline,
+  eTable,
+  eFlexbox,
+  eGrid,
+};
+
 /*
  * For replaced elements only. Gets the intrinsic dimensions of this element.
  * The dimensions may only be one of the following two types:
  *
  *   eStyleUnit_Coord   - a length in app units
  *   eStyleUnit_None    - the element has no intrinsic size in this dimension
  */
 struct IntrinsicSize {
@@ -494,16 +512,18 @@ static void ReleaseValue(T* aPropertyVal
  * link to many of the functions defined here. Too bad.
  *
  * If you're not in layout but you must call functions in here, at least
  * restrict yourself to calling virtual methods, which won't hurt you as badly.
  */
 class nsIFrame : public nsQueryFrame
 {
 public:
+  using AlignmentContext = mozilla::AlignmentContext;
+  using BaselineSharingGroup = mozilla::BaselineSharingGroup;
   template <typename T> using Maybe = mozilla::Maybe<T>;
   using Nothing = mozilla::Nothing;
   using OnNonvisible = mozilla::OnNonvisible;
   template<typename T=void>
   using PropertyDescriptor = const mozilla::FramePropertyDescriptor<T>*;
   using ReflowInput = mozilla::ReflowInput;
   using ReflowOutput = mozilla::ReflowOutput;
   using Visibility = mozilla::Visibility;
@@ -1194,21 +1214,103 @@ public:
                               nscoord aRadii[8]) const;
   bool GetBorderRadii(nscoord aRadii[8]) const;
   bool GetMarginBoxBorderRadii(nscoord aRadii[8]) const;
   bool GetPaddingBoxBorderRadii(nscoord aRadii[8]) const;
   bool GetContentBoxBorderRadii(nscoord aRadii[8]) const;
   bool GetShapeBoxBorderRadii(nscoord aRadii[8]) const;
 
   /**
+   * XXX: this method will likely be replaced by GetVerticalAlignBaseline
    * Get the position of the frame's baseline, relative to the top of
    * the frame (its top border edge).  Only valid when Reflow is not
    * needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context, with the ltr/rtl
+   * direction tweak done by nsIFrame::GetWritingMode(nsIFrame*) in inline
+   * contexts (see that method).
    */
-  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const = 0;
+  virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWM) const = 0;
+
+  /**
+   * Synthesize a first(last) inline-axis baseline from our margin-box.
+   * An alphabetical baseline is at the start(end) edge and a central baseline
+   * is at the center of our block-axis margin-box (aWM tells which to use).
+   * https://drafts.csswg.org/css-align-3/#synthesize-baselines
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @return an offset from our border-box block-axis start(end) edge for
+   * a first(last) baseline respectively
+   * (implemented in nsIFrameInlines.h)
+   */
+  inline nscoord SynthesizeBaselineBOffsetFromMarginBox(
+                   mozilla::WritingMode aWM,
+                   BaselineSharingGroup aGroup) const;
+
+  /**
+   * Synthesize a first(last) inline-axis baseline from our border-box.
+   * An alphabetical baseline is at the start(end) edge and a central baseline
+   * is at the center of our block-axis border-box (aWM tells which to use).
+   * https://drafts.csswg.org/css-align-3/#synthesize-baselines
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @return an offset from our border-box block-axis start(end) edge for
+   * a first(last) baseline respectively
+   * (implemented in nsIFrameInlines.h)
+   */
+  inline nscoord SynthesizeBaselineBOffsetFromBorderBox(
+                   mozilla::WritingMode aWM,
+                   BaselineSharingGroup aGroup) const;
+
+  /**
+   * Return the position of the frame's inline-axis baseline, or synthesize one
+   * for the given alignment context. The returned baseline is the distance from
+   * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @param aBaselineOffset out-param, only valid if the method returns true
+   * (implemented in nsIFrameInlines.h)
+   */
+  inline nscoord BaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 AlignmentContext     aAlignmentContext) const;
+
+  /**
+   * XXX: this method is taking over the role that GetLogicalBaseline has.
+   * Return true if the frame has a CSS2 'vertical-align' baseline.
+   * If it has, then the returned baseline is the distance from the block-
+   * axis border-box start edge.
+   * @note This method should only be used in AlignmentContext::eInline contexts.
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @param aBaseline the baseline offset, only valid if the method returns true
+   */
+  virtual bool GetVerticalAlignBaseline(mozilla::WritingMode aWM,
+                                        nscoord* aBaseline) const {
+    return false;
+  }
+
+  /**
+   * Return true if the frame has a first(last) inline-axis natural baseline per
+   * CSS Box Alignment.  If so, then the returned baseline is the distance from
+   * the block-axis border-box start(end) edge for aBaselineGroup eFirst(eLast).
+   * https://drafts.csswg.org/css-align-3/#natural-baseline
+   * @note The returned value is only valid when reflow is not needed.
+   * @note You should only call this on frames with a WM that's parallel to aWM.
+   * @param aWM the writing-mode of the alignment context
+   * @param aBaseline the baseline offset, only valid if the method returns true
+   */
+  virtual bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                         BaselineSharingGroup aBaselineGroup,
+                                         nscoord*             aBaseline) const {
+    return false;
+  }
 
   /**
    * Get the position of the baseline on which the caret needs to be placed,
    * relative to the top of the frame.  This is mostly needed for frames
    * which return a baseline from GetBaseline which is not useful for
    * caret positioning.
    */
   virtual nscoord GetCaretBaseline() const {
--- a/layout/generic/nsIFrameInlines.h
+++ b/layout/generic/nsIFrameInlines.h
@@ -91,9 +91,73 @@ nsIFrame::IsInlineOutside() const
 }
 
 mozilla::StyleDisplay
 nsIFrame::GetDisplay() const
 {
   return StyleDisplay()->GetDisplay(this);
 }
 
+nscoord
+nsIFrame::SynthesizeBaselineBOffsetFromMarginBox(
+            mozilla::WritingMode aWM,
+            BaselineSharingGroup aGroup) const
+{
+  MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+  auto margin = GetLogicalUsedMargin(aWM);
+  if (aGroup == BaselineSharingGroup::eFirst) {
+    if (aWM.IsAlphabeticalBaseline()) {
+      // First baseline for inverted-line content is the block-start margin edge,
+      // as the frame is in effect "flipped" for alignment purposes.
+      return MOZ_UNLIKELY(aWM.IsLineInverted()) ? -margin.BStart(aWM)
+                                                : BSize(aWM) + margin.BEnd(aWM);
+    }
+    nscoord marginBoxCenter = (BSize(aWM) + margin.BStartEnd(aWM)) / 2;
+    return marginBoxCenter - margin.BStart(aWM);
+  }
+  MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+  if (aWM.IsAlphabeticalBaseline()) {
+    // Last baseline for inverted-line content is the block-start margin edge,
+    // as the frame is in effect "flipped" for alignment purposes.
+    return MOZ_UNLIKELY(aWM.IsLineInverted()) ? BSize(aWM) + margin.BStart(aWM)
+                                              : -margin.BEnd(aWM);
+  }
+  // Round up for central baseline offset, to be consistent with eFirst.
+  nscoord marginBoxSize = BSize(aWM) + margin.BStartEnd(aWM);
+  nscoord marginBoxCenter = (marginBoxSize / 2) + (marginBoxSize % 2);
+  return marginBoxCenter - margin.BEnd(aWM);
+}
+
+nscoord
+nsIFrame::SynthesizeBaselineBOffsetFromBorderBox(
+            mozilla::WritingMode aWM,
+            BaselineSharingGroup aGroup) const
+{
+  MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+  nscoord borderBoxSize = BSize(aWM);
+  if (aGroup == BaselineSharingGroup::eFirst) {
+    return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? borderBoxSize
+                                                    : borderBoxSize / 2;
+  }
+  MOZ_ASSERT(aGroup == BaselineSharingGroup::eLast);
+  // Round up for central baseline offset, to be consistent with eFirst.
+  auto borderBoxCenter = (borderBoxSize / 2) + (borderBoxSize % 2);
+  return MOZ_LIKELY(aWM.IsAlphabeticalBaseline()) ? 0 : borderBoxCenter;
+}
+
+nscoord
+nsIFrame::BaselineBOffset(mozilla::WritingMode aWM,
+                          BaselineSharingGroup aBaselineGroup,
+                          AlignmentContext     aAlignmentContext) const
+{
+  MOZ_ASSERT(!aWM.IsOrthogonalTo(GetWritingMode()));
+  nscoord baseline;
+  if (GetNaturalBaselineBOffset(aWM, aBaselineGroup, &baseline)) {
+    return baseline;
+  }
+  if (aAlignmentContext == AlignmentContext::eInline) {
+    return SynthesizeBaselineBOffsetFromMarginBox(aWM, aBaselineGroup);
+  }
+  // XXX AlignmentContext::eTable should use content box?
+  return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup);
+}
+
 #endif
--- a/layout/generic/nsIScrollableFrame.h
+++ b/layout/generic/nsIScrollableFrame.h
@@ -470,11 +470,19 @@ public:
   virtual void TriggerDisplayPortExpiration() = 0;
 
   /**
    * Returns information required to determine where to snap to after a scroll.
    */
   virtual ScrollSnapInfo GetScrollSnapInfo() const = 0;
 
   virtual void SetScrollsClipOnUnscrolledOutOfFlow() = 0;
+
+  /**
+   * Given the drag event aEvent, determine whether the mouse is near the edge
+   * of the scrollable area, and scroll the view in the direction of that edge
+   * if so. If scrolling occurred, true is returned. When false is returned, the
+   * caller should look for an ancestor to scroll.
+   */
+  virtual bool DragScroll(mozilla::WidgetEvent* aEvent) = 0;
 };
 
 #endif
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-container-synthesized-baseline-001-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference: Synthesized grid container baseline.</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313068">
+  <style type="text/css">
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+.ib {
+  display: inline-block;
+}
+.ig {
+  display: inline-grid;
+}
+.ib, .ig {
+  border-style: solid;
+  border-width: 3px 1px 5px 1px;
+  padding: 7px 10px 3px 8px;
+  margin: 5px 3px 2px 1px;
+}
+</style>
+
+</head><body>
+
+<pre>Inline-level context:</pre>
+Grid:<div class="ib"></div>
+Block:<div class="ig"></div>
+
+<pre>Grid-level context:</pre>
+<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start">
+Grid:<div class="ib"></div>
+Block:<div class="ig"></div>
+</div>
+
+<pre>Flexbox-level context:</pre>
+<div style="display:inline-flex; align-items:baseline; justify-items:start">
+Grid:<div class="ib" style="margin-bottom:0"></div>
+Block:<div class="ig"></div>
+</div>
+
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/css-grid/grid-container-synthesized-baseline-001.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Grid Test: Synthesized grid container baseline.</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313068">
+  <link rel="help" href="https://drafts.csswg.org/css-grid/#grid-baselines">
+  <link rel="match" href="grid-container-synthesized-baseline-001-ref.html">
+  <style type="text/css">
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+.ib {
+  display: inline-block;
+}
+.ig {
+  display: inline-grid;
+}
+.ib, .ig {
+  border-style: solid;
+  border-width: 3px 1px 5px 1px;
+  padding: 7px 10px 3px 8px;
+  margin: 5px 3px 2px 1px;
+}
+</style>
+
+</head><body>
+
+<pre>Inline-level context:</pre>
+Grid:<div class="ig"></div>
+Block:<div class="ib"></div>
+
+<pre>Grid-level context:</pre>
+<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start">
+Grid:<div class="ig"></div>
+Block:<div class="ib" style="margin-bottom:0"></div>
+</div>
+
+<pre>Flexbox-level context:</pre>
+<div style="display:inline-flex; align-items:baseline; justify-items:start">
+Grid:<div class="ig"></div>
+Block:<div class="ib" style="margin-bottom:0"></div>
+</div>
+
+</body></html>
--- a/layout/reftests/css-grid/grid-fragmentation-015-ref.html
+++ b/layout/reftests/css-grid/grid-fragmentation-015-ref.html
@@ -79,17 +79,17 @@ x { display:block; height:20px; }
 <span style="grid-row:span 2"><x></x></span>
 <span><x></x></span>
 <span><x></x></span>
 </div></div></div></div>
 
 <!-- grid wrapped in FIELDSET inline -->
 <div class="columns" style="height: 40px; margin-left:200px">
 <div style="padding-top:2px; background:grey">
-<div style="display:inline-block; overflow:hidden; border:none; padding:0; margin:0">
+<div style="display:inline-block; border:none; padding:0; margin:0">
 <div class="grid">
 <span style="grid-row:span 2"><x></x></span>
 <span><x></x></span>
 <span><x></x></span>
 </div></div></div></div>
 
 <!-- grid wrapped in FIELDSET overflow:hidden block -->
 <div class="columns" style="height: 40px; margin-left:400px">
--- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001-ref.html
+++ b/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001-ref.html
@@ -11,133 +11,65 @@
   <style type="text/css">
 html,body {
     color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
 }
 
 div {
   display: grid;
   float: left;
-  grid-template-columns: auto auto;
-  grid-template-rows: 5px;
-  height: 5px;
   border: 1px solid;
   clear: left;
   align-content: start;
   justify-content: start;
+  margin: 3px;
 }
 
 span {
-  min-width: 10px;
   background: grey;
 }
+.c > span { width: 10px; }
+.r > span { height: 10px; }
+
 span:nth-child(2) { background:lime; }
 x { background: blue; }
 x:nth-child(2) { background:pink; }
 
-.g10  { grid-gap:10%; }
-.p1   { grid-template-columns: 10px; width: calc(10px / 0.9); }
-.p1a  { grid-template-columns: 10px; width: calc(10px / 0.9); }
-.p10  { grid-template-columns: 10px 10px; width: calc(10px / 0.8); }
-.p10a { grid-template-columns: calc((20px / 0.5) * 0.4) calc((20px / 0.5) * 0.1); width: calc(20px / 0.5); }
-.p10b { grid-template-columns: calc((110px / 0.6) * 0.4) 100px; width: calc(110px / 0.6); }
-.g10.p10b { grid-template-columns: calc((110px / 0.5) * 0.4) 100px; width: calc(110px / 0.5);  }
-.p10c { grid-template-columns: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); width: calc(110px / 0.6); }
-.g10.p10c { grid-template-columns: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); width: calc(110px / 0.4); }
-.c10  { grid-template-columns: 62.5px 62.5px; }
-.g10.c10  { grid-template-columns: 64.2833px 64.2833px; }
-.c10120  { grid-template-columns: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); width: calc(170px / 0.8); }
-.g10.c10120 { grid-template-columns: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); width: calc(170px / 0.7); }
-.c10a { grid-template-columns: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); width: calc(170px / 0.8); }
-.g10.c10a { grid-template-columns: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); width: calc(170px / 0.7); }
-.c10b { grid-template-columns: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); width: 170px; }
+.c { grid-auto-rows: 5px; }
+.c.p1   { grid-template-columns: 1px; width: 10px; }
+.c.p1a  { grid-template-columns: minmax(10%,auto); }
+.c.p2   { grid-template-columns: 10% 10%; grid-gap: 20%; }
+.c.p2a  { grid-template-columns: repeat(2,minmax(10%,auto)); grid-gap: 20%; }
+.c.c0  { grid-template-columns: 0; }
 
-.p1x   { grid-template-columns: 0; }
-.p1ax  { grid-template-columns: 0; }
-.p10x  { grid-template-columns: auto auto; }
-.p10ax { grid-template-columns: calc((10px / 0.5) * 0.4) calc((10px / 0.5) * 0.1); width: calc(10px / 0.5); }
-.g10.p10 { width: calc(10px / 0.7); }
-.g10.p10a  { grid-template-columns: calc((20px / 0.4) * 0.4) calc((20px / 0.4) * 0.1); width: calc(20px / 0.4); }
-.g10.p10ax { grid-template-columns: calc((10px / 0.4) * 0.4) calc((10px / 0.4) * 0.1); width: calc(10px / 0.4);  }
-.p10axx{ grid-template-columns: auto auto; }
-.p10bx { grid-template-columns: calc((110px / 0.6) * 0.4) 100px; width: calc(110px / 0.6); }
-.p10bx120 { grid-template-columns: calc((100px / 0.6) * 0.4) 100px; width: calc(100px / 0.6); }
-.g10.p10bx { grid-template-columns: calc((110px / 0.5) * 0.4) 100px; width: calc(110px / 0.5); }
-.g10.p10bx1 { grid-template-columns: calc((100px / 0.5) * 0.4) 100px; width: calc(100px / 0.5); }
-.p10bxx{ grid-template-columns: 66.66667px 100px; }
-.g10.p10bxx{ grid-template-columns: 80px 100px; }
-.p10cx { grid-template-columns: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); width: calc(110px / 0.6); }
-.g10.p10cx { grid-template-columns: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); width: calc(110px / 0.4); }
-.c10x  { grid-template-columns: 62.5px 62.5px; }
-.g10.c10xx  { grid-template-columns: 64.2833px 64.2833px; }
-.g10.c10x  { grid-template-columns: 64.2833px 64.2833px; }
-.c10xx { grid-template-columns: 62.5px 62.5px; }
-.c10ax { grid-template-columns: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px) ; width: calc(170px / 0.8); }
-.g10.c10ax { grid-template-columns: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px) ; width: calc(170px / 0.7); }
-.c10bx { grid-template-columns: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); width: 170px; }
+.r { grid-auto-columns: 5px; grid-auto-flow: column; }
+.r.p1   { grid-template-rows: 10%; }
+.r.p1a  { grid-template-rows: minmax(10%,auto); }
+.r.p2   { grid-template-rows: 10% 10%; grid-gap: 20%; }
+.r.p2a  { grid-template-rows: repeat(2,minmax(10%,auto)); grid-gap: 20%; }
+.r.r0   { grid-template-rows: 0; }
 
-.gneg { grid-gap: 0; grid-template-columns: 10px 10px; width:0; }
-.gneg.c10a { grid-template-columns: 50px 50px; }
-.gneg.p10b { grid-template-columns: 8px 100px; width: 20px; }
   </style>
 </head>
 <body>
 
-<!-- Note that some of the min-width cases below SHOULD overflow. -->
+<div class="c p1"><span></span><x></x></div>
+<div class="c c0"><x></x></div>
+<div class="c p1a"><span></span><x></x></div>
+<div class="c c0"><x></x></div>
 
-<div class="p1"><span></span></div>
-<div class="p1x"><x></x></div>
-<div class="p10"><span></span></div>
-<div class="p10x"><x></x></div>
-<div class="p1a"><span></span></div>
-<div class="p1ax"><x></x></div>
-<div class="p10a"><span></span><span></span></div>
-<div class="p10ax"><x></x><span></span></div>
-<div class="p10axx"><x></x><x></x></div>
-<div class="p10b"><span></span><span></span></div>
-<div class="p10bx"><span></span><x></x></div>
-<div class="p10bxx"><x></x><x></x></div>
-<div class="p10b"><span></span><span style="min-width:80px"></span></div>
-<div class="p10bx120"><x></x><span style="min-width:120px"></span></div>
-<div class="p10c"><span></span><span></span></div>
-<div class="p10cx"><span></span><x></x></div>
-<div class="c10"><span></span><span></span></div>
-<div class="c10xx"><x></x><x></x></div>
-<div class="c10120"><span></span><span style="min-width:120px"></span></div>
-<div class="c10x"><x></x><span></span></div>
-<div class="c10a"><span></span><span style="min-width:120px"></span></div>
-<div class="c10ax"><x></x><span style="min-width:120px"></span></div>
-<div class="c10b"><span></span><span style="min-width:120px"></span></div>
-<div class="c10bx"><x></x><span style="min-width:120px"></span></div>
-
+<div class="c p2"><span></span><span></span><x></x></div>
+<div class="c c0"><x></x></div>
+<div class="c p2a"><span></span><span></span><x></x></div>
+<div class="c c0"><x></x></div>
 
-<div class="g10"><span></span><span></span></div>
-<div class="g10 p1"><span></span></div>
-<div class="g10 p1x"><x></x></div>
-<div class="g10 p10"><span></span></div>
-<div class="g10 p10x"><x></x></div>
-<div class="g10 p1a"><span></span></div>
-<div class="g10 p1ax"><x></x></div>
-<div class="g10 p10a"><span></span><span></span></div>
-<div class="g10 p10ax"><x></x><span></span></div>
-<div class="g10 p10axx"><x></x><x></x></div>
-<div class="g10 p10bx"><span></span><span></span></div>
-<div class="g10 p10bx"><span></span><x></x></div>
-<div class="g10 p10bxx"><x></x><x></x></div>
-<div class="g10 p10b"><span></span><span style="min-width:80px"></span></div>
-<div class="g10 p10bx1"><x></x><span style="min-width:120px"></span></div>
-<div class="g10 p10c"><span></span><span></span></div>
-<div class="g10 p10cx"><span></span><x></x></div>
-<div class="g10 c10"><span></span><span></span></div>
-<div class="g10 c10xx"><x></x><x></x></div>
-<div class="g10 c10120"><span></span><span style="min-width:120px"></span></div>
-<div class="g10 c10x"><x></x><span></span></div>
-<div class="g10 c10a"><span></span><span style="min-width:120px"></span></div>
-<div class="g10 c10ax"><x></x><span style="min-width:120px"></span></div>
-<div class="g10 c10b"><span></span><span style="min-width:120px"></span></div>
-<div class="g10 c10bx"><x></x><span style="min-width:120px"></span></div>
+<div class="r p1"><span></span><x></x></div>
+<div class="r r0"><x></x></div>
+<div class="r p1a"><span></span><x></x></div>
+<div class="r r0"><x></x></div>
 
-<div class="gneg"><span></span><span></span></div>
-<div class="gneg c10a"><span></span><span></span></div>
-<div class="gneg p10b"><span></span><span></span></div>
+<div class="r p2"><span></span><span></span><x></x></div>
+<div class="r r0"><x></x></div>
+<div class="r p2a"><span></span><span></span><x></x></div>
+<div class="r r0"><x></x></div>
 
 </body>
 </html>
--- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001.html
+++ b/layout/reftests/css-grid/grid-percent-intrinsic-sizing-001.html
@@ -12,99 +12,62 @@
   <style type="text/css">
 html,body {
     color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
 }
 
 div {
   display: grid;
   float: left;
-  grid-template-columns: auto auto;
-  grid-template-rows: 5px;
-  height: 5px;
   border: 1px solid;
   clear: left;
   place-content: start start;
+  margin: 3px;
 }
 
 span {
-  min-width: 10px;
   background: grey;
 }
+.c > span { width: 10px; }
+.r > span { height: 10px; }
+
 span:nth-child(2) { background:lime; }
 x { background: blue; }
 x:nth-child(2) { background:pink; }
 
-.g10  { grid-gap:10%; }
-.gneg { grid-gap: calc(10% - 100px); }
-.p1   { grid-template-columns: 10%; }
-.p1a  { grid-template-columns: minmax(10%,auto); }
-.p10  { grid-template-columns: 10% 10%; }
-.p10a { grid-template-columns: minmax(40%,auto) minmax(10%,auto); }
-.p10b { grid-template-columns: minmax(40%,auto) 100px; }
-.p10c { grid-template-columns: minmax(20%,auto) 100px minmax(20%,auto) ; }
-.c10  { grid-template-columns: calc(50px + 10%) calc(50px + 10%); }
-.c10a { grid-template-columns: minmax(calc(50px + 10%), auto) minmax(calc(50px + 10%), auto); }
-.c10b { grid-template-columns: minmax(calc(50px - 10%), auto) minmax(calc(50px - 10%), auto); }
+.c { grid-auto-rows: 5px; }
+.c.p1   { grid-template-columns: 10%; }
+.c.p1a  { grid-template-columns: minmax(10%,auto); }
+.c.p2   { grid-template-columns: 10% 10%; grid-gap: 20%; }
+.c.p2a  { grid-template-columns: repeat(2,minmax(10%,auto)); grid-gap: 20%; }
+
+.r { grid-auto-columns: 5px; grid-auto-flow: column; }
+.r.p1   { grid-template-rows: 10%; }
+.r.p1a  { grid-template-rows: minmax(10%,auto); }
+.r.p2   { grid-template-rows: 10% 10%; grid-gap: 20%; }
+.r.p2a  { grid-template-rows: repeat(2,minmax(10%,auto)); grid-gap: 20%; }
 
   </style>
 </head>
 <body>
 
-<!-- Note that some of the min-width cases below SHOULD overflow. -->
+<div class="c p1"><span></span><x></x></div>
+<div class="c p1"><x></x></div>
+<div class="c p1a"><span></span><x></x></div>
+<div class="c p1a"><x></x></div>
 
-<div class="p1"><span></span></div>
-<div class="p1"><x></x></div>
-<div class="p10"><span></span></div>
-<div class="p10"><x></x></div>
-<div class="p1a"><span></span></div>
-<div class="p1a"><x></x></div>
-<div class="p10a"><span></span><span></span></div>
-<div class="p10a"><x></x><span></span></div>
-<div class="p10a"><x></x><x></x></div>
-<div class="p10b"><span></span><span></span></div>
-<div class="p10b"><span></span><x></x></div>
-<div class="p10b"><x></x><x></x></div>
-<div class="p10b"><span></span><span style="min-width:80px"></span></div>
-<div class="p10b"><x></x><span style="min-width:120px"></span></div>
-<div class="p10c"><span></span><span></span></div>
-<div class="p10c"><span></span><x></x></div>
-<div class="c10"><span></span><span></span></div>
-<div class="c10"><x></x><x></x></div>
-<div class="c10"><span></span><span style="min-width:120px"></span></div>
-<div class="c10"><x></x><span></span></div>
-<div class="c10a"><span></span><span style="min-width:120px"></span></div>
-<div class="c10a"><x></x><span style="min-width:120px"></span></div>
-<div class="c10b"><span></span><span style="min-width:120px"></span></div>
-<div class="c10b"><x></x><span style="min-width:120px"></span></div>
+<div class="c p2"><span></span><span></span><x></x></div>
+<div class="c p2"><x></x><x></x></div>
+<div class="c p2a"><span></span><span></span><x></x></div>
+<div class="c p2a"><x></x><x></x></div>
 
-<div class="g10"><span></span><span></span></div>
-<div class="g10 p1"><span></span></div>
-<div class="g10 p1"><x></x></div>
-<div class="g10 p10"><span></span></div>
-<div class="g10 p10"><x></x></div>
-<div class="g10 p1a"><span></span></div>
-<div class="g10 p1a"><x></x></div>
-<div class="g10 p10a"><span></span><span></span></div>
-<div class="g10 p10a"><x></x><span></span></div>
-<div class="g10 p10a"><x></x><x></x></div>
-<div class="g10 p10b"><span></span><span></span></div>
-<div class="g10 p10b"><span></span><x></x></div>
-<div class="g10 p10b"><x></x><x></x></div>
-<div class="g10 p10b"><span></span><span style="min-width:80px"></span></div>
-<div class="g10 p10b"><x></x><span style="min-width:120px"></span></div>
-<div class="g10 p10c"><span></span><span></span></div>
-<div class="g10 p10c"><span></span><x></x></div>
-<div class="g10 c10"><span></span><span></span></div>
-<div class="g10 c10"><x></x><x></x></div>
-<div class="g10 c10"><span></span><span style="min-width:120px"></span></div>
-<div class="g10 c10"><x></x><span></span></div>
-<div class="g10 c10a"><span></span><span style="min-width:120px"></span></div>
-<div class="g10 c10a"><x></x><span style="min-width:120px"></span></div>
-<div class="g10 c10b"><span></span><span style="min-width:120px"></span></div>
-<div class="g10 c10b"><x></x><span style="min-width:120px"></span></div>
+<div class="r p1"><span></span><x></x></div>
+<div class="r p1"><x></x></div>
+<div class="r p1a"><span></span><x></x></div>
+<div class="r p1a"><x></x></div>
 
-<div class="gneg"><span></span><span></span></div>
-<div class="gneg c10a"><span></span><span></span></div>
-<div class="gneg p10b"><span></span><span></span></div>
+<div class="r p2"><span></span><span></span><x></x></div>
+<div class="r p2"><x></x><x></x></div>
+<div class="r p2a"><span></span><span></span><x></x></div>
+<div class="r p2a"><x></x><x></x></div>
 
 </body>
 </html>
deleted file mode 100644
--- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-002-ref.html
+++ /dev/null
@@ -1,142 +0,0 @@
-<!DOCTYPE HTML>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html><head>
-  <meta charset="utf-8">
-  <style type="text/css">
-  <title>CSS Grid Test: Grid container intrinsic sizing involving percent track min sizing / grid-gap</title>
-  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1302541">
-  <style type="text/css">
-html,body {
-    color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
-}
-
-div {
-  display: grid;
-  float: left;
-  grid-template-rows: auto auto;
-  grid-template-columns: 5px;
-  width: 5px;
-  border: 1px solid;
-  align-content: start;
-  justify-content: start;
-}
-
-span {
-  min-height: 10px;
-  background: grey;
-}
-span:nth-child(2) { background:lime; }
-x { background: blue; }
-x:nth-child(2) { background:pink; }
-
-.g10  { grid-gap:10%; }
-.p1   { grid-template-rows: 10px; height: calc(10px / 0.9); }
-.p1a  { grid-template-rows: 10px; height: calc(10px / 0.9); }
-.p10  { grid-template-rows: 10px 10px; height: calc(10px / 0.8); }
-.p10a { grid-template-rows: calc((20px / 0.5) * 0.4) calc((20px / 0.5) * 0.1); height: calc(20px / 0.5); }
-.p10b { grid-template-rows: calc((110px / 0.6) * 0.4) 100px; height: calc(110px / 0.6); }
-.g10.p10b { grid-template-rows: calc((110px / 0.5) * 0.4) 100px; height: calc(110px / 0.5);  }
-.p10c { grid-template-rows: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); height: calc(110px / 0.6); }
-.g10.p10c { grid-template-rows: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); height: calc(110px / 0.4); }
-.c10  { grid-template-rows: 62.5px 62.5px; }
-.g10.c10  { grid-template-rows: 64.2833px 64.2833px; }
-.c10120  { grid-template-rows: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); height: calc(170px / 0.8); }
-.g10.c10120 { grid-template-rows: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); height: calc(170px / 0.7); }
-.c10a { grid-template-rows: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px); height: calc(170px / 0.8); }
-.g10.c10a { grid-template-rows: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px); height: calc(170px / 0.7); }
-.c10b { grid-template-rows: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); height: 170px; }
-
-.p1x   { grid-template-rows: 0; }
-.p1ax  { grid-template-rows: 0; }
-.p10x  { grid-template-rows: auto auto; }
-.p10ax { grid-template-rows: calc((10px / 0.5) * 0.4) calc((10px / 0.5) * 0.1); height: calc(10px / 0.5); }
-.g10.p10 { height: calc(10px / 0.7); }
-.g10.p10a  { grid-template-rows: calc((20px / 0.4) * 0.4) calc((20px / 0.4) * 0.1); height: calc(20px / 0.4); }
-.g10.p10ax { grid-template-rows: calc((10px / 0.4) * 0.4) calc((10px / 0.4) * 0.1); height: calc(10px / 0.4);  }
-.p10axx{ grid-template-rows: auto auto; }
-.p10bx { grid-template-rows: calc((110px / 0.6) * 0.4) 100px; height: calc(110px / 0.6); }
-.p10bx120 { grid-template-rows: calc((100px / 0.6) * 0.4) 100px; height: calc(100px / 0.6); }
-.g10.p10bx { grid-template-rows: calc((110px / 0.5) * 0.4) 100px; height: calc(110px / 0.5); }
-.g10.p10bx1 { grid-template-rows: calc((100px / 0.5) * 0.4) 100px; height: calc(100px / 0.5); }
-.p10bxx{ grid-template-rows: 66.66667px 100px; }
-.g10.p10bxx{ grid-template-rows: 80px 100px; }
-.p10cx { grid-template-rows: calc((110px / 0.6) * 0.2) 100px calc((110px / 0.6) * 0.2); height: calc(110px / 0.6); }
-.g10.p10cx { grid-template-rows: calc((110px / 0.4) * 0.2) 100px calc((110px / 0.4) * 0.2); height: calc(110px / 0.4); }
-.c10x  { grid-template-rows: 62.5px 62.5px; }
-.g10.c10xx  { grid-template-rows: 64.2833px 64.2833px; }
-.g10.c10x  { grid-template-rows: 64.2833px 64.2833px; }
-.c10xx { grid-template-rows: 62.5px 62.5px; }
-.c10ax { grid-template-rows: calc(((170px / 0.8) * 0.1) + 50px) calc(((170px / 0.8) * 0.1) + 50px) ; height: calc(170px / 0.8); }
-.g10.c10ax { grid-template-rows: calc(((170px / 0.7) * 0.1) + 50px) calc(((170px / 0.7) * 0.1) + 50px) ; height: calc(170px / 0.7); }
-.c10bx { grid-template-rows: calc(50px - (170px * 0.1)) calc(50px - (170px * 0.1)); height: 170px; }
-
-.gneg { grid-gap: 0; grid-template-rows: 10px 10px; height:0; }
-.gneg.c10a { grid-template-rows: 50px 50px; }
-.gneg.p10b { grid-template-rows: 8px 100px; height: 20px; }
-  </style>
-</head>
-<body>
-
-<!-- Note that some of the min-height cases below SHOULD overflow. -->
-
-<div class="p1"><span></span></div>
-<div class="p1x"><x></x></div>
-<div class="p10"><span></span></div>
-<div class="p10x"><x></x></div>
-<div class="p1a"><span></span></div>
-<div class="p1ax"><x></x></div>
-<div class="p10a"><span></span><span></span></div>
-<div class="p10ax"><x></x><span></span></div>
-<div class="p10axx"><x></x><x></x></div>
-<div class="p10b"><span></span><span></span></div>
-<div class="p10bx"><span></span><x></x></div>
-<div class="p10bxx"><x></x><x></x></div>
-<div class="p10b"><span></span><span style="min-height:80px"></span></div>
-<div class="p10bx120"><x></x><span style="min-height:120px"></span></div>
-<div class="p10c"><span></span><span></span></div>
-<div class="p10cx"><span></span><x></x></div>
-<div class="c10"><span></span><span></span></div>
-<div class="c10xx"><x></x><x></x></div>
-<div class="c10120"><span></span><span style="min-height:120px"></span></div>
-<div class="c10x"><x></x><span></span></div>
-<div class="c10a"><span></span><span style="min-height:120px"></span></div>
-<div class="c10ax"><x></x><span style="min-height:120px"></span></div>
-<div class="c10b"><span></span><span style="min-height:120px"></span></div>
-<div class="c10bx"><x></x><span style="min-height:120px"></span></div>
-
-
-<div class="g10"><span></span><span></span></div>
-<div class="g10 p1"><span></span></div>
-<div class="g10 p1x"><x></x></div>
-<div class="g10 p10"><span></span></div>
-<div class="g10 p10x"><x></x></div>
-<div class="g10 p1a"><span></span></div>
-<div class="g10 p1ax"><x></x></div>
-<div class="g10 p10a"><span></span><span></span></div>
-<div class="g10 p10ax"><x></x><span></span></div>
-<div class="g10 p10axx"><x></x><x></x></div>
-<div class="g10 p10bx"><span></span><span></span></div>
-<div class="g10 p10bx"><span></span><x></x></div>
-<div class="g10 p10bxx"><x></x><x></x></div>
-<div class="g10 p10b"><span></span><span style="min-height:80px"></span></div>
-<div class="g10 p10bx1"><x></x><span style="min-height:120px"></span></div>
-<div class="g10 p10c"><span></span><span></span></div>
-<div class="g10 p10cx"><span></span><x></x></div>
-<div class="g10 c10"><span></span><span></span></div>
-<div class="g10 c10xx"><x></x><x></x></div>
-<div class="g10 c10120"><span></span><span style="min-height:120px"></span></div>
-<div class="g10 c10x"><x></x><span></span></div>
-<div class="g10 c10a"><span></span><span style="min-height:120px"></span></div>
-<div class="g10 c10ax"><x></x><span style="min-height:120px"></span></div>
-<div class="g10 c10b"><span></span><span style="min-height:120px"></span></div>
-<div class="g10 c10bx"><x></x><span style="min-height:120px"></span></div>
-
-<div class="gneg"><span></span><span></span></div>
-<div class="gneg c10a"><span></span><span></span></div>
-<div class="gneg p10b"><span></span><span></span></div>
-
-</body>
-</html>
deleted file mode 100644
--- a/layout/reftests/css-grid/grid-percent-intrinsic-sizing-002.html
+++ /dev/null
@@ -1,110 +0,0 @@
-<!DOCTYPE HTML>
-<!--
-     Any copyright is dedicated to the Public Domain.
-     http://creativecommons.org/publicdomain/zero/1.0/
--->
-<html><head>
-  <meta charset="utf-8">
-  <style type="text/css">
-  <title>CSS Grid Test: Grid container intrinsic sizing involving percent track min sizing / grid-gap</title>
-  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1302541">
-  <link rel="match" href="grid-percent-intrinsic-sizing-002-ref.html">
-  <style type="text/css">
-html,body {
-    color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
-}
-
-div {
-  display: grid;
-  float: left;
-  grid-template-rows: auto auto;
-  grid-template-columns: 5px;
-  width: 5px;
-  border: 1px solid;
-  align-content: start;
-  justify-content: start;
-}
-
-span {
-  min-height: 10px;
-  background: grey;
-}
-span:nth-child(2) { background:lime; }
-x { background: blue; }
-x:nth-child(2) { background:pink; }
-
-.g10  { grid-gap:10%; }
-.gneg { grid-gap: calc(10% - 100px); }
-.p1   { grid-template-rows: 10%; }
-.p1a  { grid-template-rows: minmax(10%,auto); }
-.p10  { grid-template-rows: 10% 10%; }
-.p10a { grid-template-rows: minmax(40%,auto) minmax(10%,auto); }
-.p10b { grid-template-rows: minmax(40%,auto) 100px; }
-.p10c { grid-template-rows: minmax(20%,auto) 100px minmax(20%,auto) ; }
-.c10  { grid-template-rows: calc(50px + 10%) calc(50px + 10%); }
-.c10a { grid-template-rows: minmax(calc(50px + 10%), auto) minmax(calc(50px + 10%), auto); }
-.c10b { grid-template-rows: minmax(calc(50px - 10%), auto) minmax(calc(50px - 10%), auto); }
-
-  </style>
-</head>
-<body>
-
-<!-- Note that some of the min-height cases below SHOULD overflow. -->
-
-<div class="p1"><span></span></div>
-<div class="p1"><x></x></div>
-<div class="p10"><span></span></div>
-<div class="p10"><x></x></div>
-<div class="p1a"><span></span></div>
-<div class="p1a"><x></x></div>
-<div class="p10a"><span></span><span></span></div>
-<div class="p10a"><x></x><span></span></div>
-<div class="p10a"><x></x><x></x></div>
-<div class="p10b"><span></span><span></span></div>
-<div class="p10b"><span></span><x></x></div>
-<div class="p10b"><x></x><x></x></div>
-<div class="p10b"><span></span><span style="min-height:80px"></span></div>
-<div class="p10b"><x></x><span style="min-height:120px"></span></div>
-<div class="p10c"><span></span><span></span></div>
-<div class="p10c"><span></span><x></x></div>
-<div class="c10"><span></span><span></span></div>
-<div class="c10"><x></x><x></x></div>
-<div class="c10"><span></span><span style="min-height:120px"></span></div>
-<div class="c10"><x></x><span></span></div>
-<div class="c10a"><span></span><span style="min-height:120px"></span></div>
-<div class="c10a"><x></x><span style="min-height:120px"></span></div>
-<div class="c10b"><span></span><span style="min-height:120px"></span></div>
-<div class="c10b"><x></x><span style="min-height:120px"></span></div>
-
-<div class="g10"><span></span><span></span></div>
-<div class="g10 p1"><span></span></div>
-<div class="g10 p1"><x></x></div>
-<div class="g10 p10"><span></span></div>
-<div class="g10 p10"><x></x></div>
-<div class="g10 p1a"><span></span></div>
-<div class="g10 p1a"><x></x></div>
-<div class="g10 p10a"><span></span><span></span></div>
-<div class="g10 p10a"><x></x><span></span></div>
-<div class="g10 p10a"><x></x><x></x></div>
-<div class="g10 p10b"><span></span><span></span></div>
-<div class="g10 p10b"><span></span><x></x></div>
-<div class="g10 p10b"><x></x><x></x></div>
-<div class="g10 p10b"><span></span><span style="min-height:80px"></span></div>
-<div class="g10 p10b"><x></x><span style="min-height:120px"></span></div>
-<div class="g10 p10c"><span></span><span></span></div>
-<div class="g10 p10c"><span></span><x></x></div>
-<div class="g10 c10"><span></span><span></span></div>
-<div class="g10 c10"><x></x><x></x></div>
-<div class="g10 c10"><span></span><span style="min-height:120px"></span></div>
-<div class="g10 c10"><x></x><span></span></div>
-<div class="g10 c10a"><span></span><span style="min-height:120px"></span></div>
-<div class="g10 c10a"><x></x><span style="min-height:120px"></span></div>
-<div class="g10 c10b"><span></span><span style="min-height:120px"></span></div>
-<div class="g10 c10b"><x></x><span style="min-height:120px"></span></div>
-
-<div class="gneg"><span></span><span></span></div>
-<div class="gneg c10a"><span></span><span></span></div>
-<div class="gneg p10b"><span></span><span></span></div>
-
-</body>
-</html>
--- a/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html
+++ b/layout/reftests/css-grid/grid-repeat-auto-fill-fit-002-ref.html
@@ -78,17 +78,17 @@ x:last-child {
 
 fill,fit {
   float: left;
   height: 400px;
 }
 
 .zero-progress {
   grid-row-gap: calc(10px - 1%);
-  grid-template-rows: [a] 10px repeat(1, [b] 0 [c]) [d];
+  grid-template-rows: [a] 10px repeat(4, [b] minmax(0,auto) [c]) [d];
 }
 .w50.zero-progress {
   grid-row-gap: calc(10px - 1%);
   grid-template-rows: [a] 10px repeat(3, [b] 0 [c]) [d];
 }
 </style>
 </head>
 <body>
--- a/layout/reftests/css-grid/grid-track-percent-sizing-001-ref.html
+++ b/layout/reftests/css-grid/grid-track-percent-sizing-001-ref.html
@@ -9,19 +9,19 @@
   <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1264607">
   <style type="text/css">
 body,html { color:black; background:white; font-family:monospace; font:1px/1 monospace; padding:0; margin:0; }
 
 .grid {
   display: grid;
   float: left;
   border: 3px solid;
-  grid-template-columns: calc((30px / 0.4) * 0.6);
-  width: calc(30px / 0.4);
-  grid-template-rows: calc(((15px / 0.6) * 0.4)) 10px;
+  grid-template-columns: 18px;
+  width: 30px;
+  grid-template-rows: 5px 10px;
   margin-right: 20px;
   grid-auto-rows: 10px;
   align-content: start;
   justify-content: start;
 }
 
 span {
   background: grey;
@@ -43,32 +43,28 @@ x {
 }
 
 .tA {
   grid-template-columns: 30px;
   width: auto;
 }
 .tB {
   grid-template-columns: 30px;
-  grid-template-rows: calc(10px / 0.6 - 10px) 10px; 
+  grid-template-rows: 0 10px; 
 }
 
-.t0, .t2, .t6, .t7 { height: calc(15px / 0.6); }
 .t1 {
-  grid-template-rows: calc(20px / 0.6 - 10px) 10px;
-  grid-template-columns: calc(((30px / 0.4) * 0.6) + 10px);
+  grid-template-columns: 28px;
 }
 .t3, .t4, .t8 { grid: auto 10px / auto; width: auto; }
-.t5 { width: 30px; height: 20px; }
-.t9 { width: 30px; grid-template-rows: 5px 10px; }
 
 .t9 x { width: 18px }
 .t3 x, .t4 x, .t8 x, .tA x { width: 0 }
 .t5 x { width: 10px }
-.tB x { width: 45px }
+.tB x { width: 18px }
 
 .sz {
   grid-template-rows: 40px;
   width: 100px;
   height: 100px;
 }
 .sz.t1 { grid-template-rows: 50px; }
 .sz.t3, .sz.t4 { grid-template-rows: 0; }
--- a/layout/reftests/css-grid/reftest.list
+++ b/layout/reftests/css-grid/reftest.list
@@ -132,16 +132,17 @@ skip-if(!gtkWidget) == grid-item-mixed-b
 == grid-align-content-001.html grid-align-content-001-ref.html
 == grid-justify-content-001.html grid-justify-content-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-justify-content-002.html grid-justify-content-002-ref.html # Bug 1245884 - slow
 skip-if(Android&&isDebugBuild) == grid-justify-content-003.html grid-justify-content-003-ref.html # Bug 1245884 - slow
 skip-if(!gtkWidget) == grid-container-baselines-001.html grid-container-baselines-001-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-002.html grid-container-baselines-002-ref.html
 skip-if(!gtkWidget) == grid-container-baselines-003.html grid-container-baselines-003-ref.html
 == grid-container-baselines-004.html grid-container-baselines-004-ref.html
+== grid-container-synthesized-baseline-001-ref.html grid-container-synthesized-baseline-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-column-gap-001.html grid-column-gap-001-ref.html # Bug 1245884 - slow
 == grid-column-gap-002.html grid-column-gap-002-ref.html
 == grid-column-gap-003.html grid-column-gap-003-ref.html
 == grid-column-gap-004.html grid-column-gap-004-ref.html
 == grid-row-gap-001.html grid-row-gap-001-ref.html
 == grid-percent-grid-gap-001.html grid-percent-grid-gap-001-ref.html
 skip-if(Android&&isDebugBuild) == grid-row-gap-002.html grid-row-gap-002-ref.html # Bug 1245884 - slow
 skip-if(Android&&isDebugBuild) == grid-row-gap-003.html grid-row-gap-003-ref.html # Bug 1245884 - slow
@@ -265,9 +266,8 @@ asserts(1-10) == grid-fragmentation-dyn4
 == grid-fragmentation-dyn4-028.html grid-fragmentation-028-ref.html
 == grid-fragmentation-dyn5-028.html grid-fragmentation-028-ref.html
 == grid-fragmentation-dyn1-029.html grid-fragmentation-029-ref.html
 == grid-fragmentation-dyn2-029.html grid-fragmentation-029-ref.html
 == grid-fragmentation-dyn2-030.html grid-fragmentation-030-ref.html
 == grid-fragmentation-dyn2-031.html grid-fragmentation-031-ref.html
 == bug1306106.html bug1306106-ref.html
 == grid-percent-intrinsic-sizing-001.html grid-percent-intrinsic-sizing-001-ref.html
-== grid-percent-intrinsic-sizing-002.html grid-percent-intrinsic-sizing-002-ref.html
--- a/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml
+++ b/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3-ref.xhtml
@@ -18,41 +18,49 @@
         display: block;
         border: 1px dashed blue;
         font: 14px sans-serif;
       }
 
       div   { display: inline-block; }
       table { display: inline-table; }
 
-      .big {
-        height: 100px;
-        font: 24px sans-serif;
-        margin-top: 20px;
-      }
-
       .lime   { background: lime;   }
       .pink   { background: pink;   }
       .aqua   { background: aqua;   }
+
+      i { display:inline-block; width:20px; height:2px; background:black; }
+      .ref {
+            -moz-appearance:none;
+             -ms-appearance:none;
+         -webkit-appearance:none;
+                 appearance:none;
+         border:none;
+         margin:0;
+         padding:0;
+         border-bottom:2px solid black;
+         width:20px;
+      }
    </style>
   </head>
   <body>
     <div class="flexbox">
       <div class="lime">text</div
       ><button>btn</button
-      ><input type="radio"
-     /><input type="checkbox"
-     /><label class="pink">label</label
+      ><label class="pink">label</label
       ><table cellspacing="0" cellpadding="0"  class="aqua">
         <label>lab<br/>el</label>
       </table
       ><table cellspacing="0" cellpadding="0">
         <fieldset>field<br/>set</fieldset>
       </table
       ><table cellspacing="0" cellpadding="0">
         <fieldset><legend>leg</legend>field<br/>set</fieldset>
       </table
       ><table cellspacing="0" cellpadding="0">
         <fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset>
       </table>
     </div>
+    <div class="flexbox" style="font-size:0"><input type="radio"
+     /><input type="checkbox"
+     /><input type="checkbox" class="ref"/></div>
   </body>
 </html>
--- a/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml
+++ b/layout/reftests/flexbox/flexbox-align-self-baseline-horiz-3.xhtml
@@ -25,24 +25,29 @@
         height: 100px;
         font: 24px sans-serif;
         margin-top: 20px;
       }
 
       .lime   { background: lime;   }
       .pink   { background: pink;   }
       .aqua   { background: aqua;   }
+
+      i { display:inline-block; width:20px; height:2px; background:black; }
    </style>
   </head>
   <body>
     <div class="flexbox">
       <div class="lime">text</div>
       <button>btn</button>
-      <input type="radio"/>
-      <input type="checkbox"/>
       <label class="pink">label</label>
       <label class="aqua">lab<br/>el</label>
       <fieldset>field<br/>set</fieldset>
       <fieldset><legend>leg</legend>field<br/>set</fieldset>
       <fieldset><legend>leg<br/>end</legend>field<br/>set</fieldset>
     </div>
+    <div class="flexbox" style="font-size:0">
+      <input type="radio"/>
+      <input type="checkbox"/>
+      <i></i>
+    </div>
   </body>
 </html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/flexbox/flexbox-empty-container-synthesized-baseline-001-ref.html
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>Reference: Synthesized flex container baseline.</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313811">
+  <style type="text/css">
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+.ib {
+  display: inline-block;
+}
+.ig {
+  display: inline-grid;
+}
+.ib, .ig {
+  border-style: solid;
+  border-width: 3px 1px 5px 1px;
+  padding: 7px 10px 3px 8px;
+  margin: 5px 3px 2px 1px;
+}
+</style>
+
+</head><body>
+
+<pre>Inline-level context:</pre>
+Flexbox:<div class="ib"></div>
+Block:<div class="ig"></div>
+
+<pre>Grid-level context:</pre>
+<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start">
+Flexbox:<div class="ib"></div>
+Block:<div class="ig"></div>
+</div>
+
+<pre>Flexbox-level context:</pre>
+<div style="display:inline-flex; align-items:baseline; justify-items:start">
+Flexbox:<div class="ib" style="margin-bottom:0"></div>
+Block:<div class="ig"></div>
+</div>
+
+</body></html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/flexbox/flexbox-empty-container-synthesized-baseline-001.html
@@ -0,0 +1,48 @@
+<!DOCTYPE HTML>
+<!--
+     Any copyright is dedicated to the Public Domain.
+     http://creativecommons.org/publicdomain/zero/1.0/
+-->
+<html><head>
+  <meta charset="utf-8">
+  <title>CSS Flexbox Test: Synthesized flex container baseline.</title>
+  <link rel="author" title="Mats Palmgren" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1313811">
+  <link rel="help" href="https://drafts.csswg.org/css-flexbox/#flex-baselines">
+  <link rel="match" href="flexbox-empty-container-synthesized-baseline-001.html">
+  <style type="text/css">
+html,body {
+  color:black; background-color:white; font:16px/1 monospace; padding:0; margin:0;
+}
+.ib {
+  display: inline-block;
+}
+.if {
+  display: inline-flex;
+}
+.ib, .if {
+  border-style: solid;
+  border-width: 3px 1px 5px 1px;
+  padding: 7px 10px 3px 8px;
+  margin: 5px 3px 2px 1px;
+}
+</style>
+
+</head><body>
+
+<pre>Inline-level context:</pre>
+Flexbox:<div class="if"></div>
+Block:<div class="ib"></div>
+
+<pre>Grid-level context:</pre>
+<div style="display:inline-grid; grid-auto-flow:column; align-items:baseline; justify-items:start">
+Flexbox:<div class="if"></div>
+Block:<div class="ib" style="margin-bottom:0"></div>
+</div>
+
+<pre>Flexbox-level context:</pre>
+<div style="display:inline-flex; align-items:baseline; justify-items:start">
+Flexbox:<div class="if"></div>
+Block:<div class="ib" style="margin-bottom:0"></div>
+</div>
+
+</body></html>
--- a/layout/reftests/flexbox/reftest.list
+++ b/layout/reftests/flexbox/reftest.list
@@ -12,17 +12,17 @@
 # SUBDIRECTORY: Reftests for paginated flex containers
 include pagination/reftest.list
 
 # Tests for cross-axis alignment (align-self / align-items properties)
 fails == flexbox-align-self-baseline-horiz-2.xhtml  flexbox-align-self-baseline-horiz-2-ref.xhtml # bug 793456, and possibly others
 # This one fails on windows R (but not Ru, strangely) and GTK.
 # On Windows R and GTK, the single-line <label> flex item has a different
 # background size in test vs. ref
-fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) == flexbox-align-self-baseline-horiz-3.xhtml  flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate
+fuzzy-if(cocoaWidget,1,2) random-if(winWidget||gtkWidget) skip-if(Android) == flexbox-align-self-baseline-horiz-3.xhtml  flexbox-align-self-baseline-horiz-3-ref.xhtml # XXXdholbert investigate the random-if. The skip-if(Android) is because checkbox/radio appearance:none doesn't work as expected.
 == flexbox-align-self-baseline-horiz-4.xhtml flexbox-align-self-baseline-horiz-4-ref.xhtml
 
 # Tests for box-sizing on flex containers and flex items.
 == flexbox-box-sizing-on-container-horiz-1.html flexbox-box-sizing-on-container-horiz-1-ref.html
 == flexbox-box-sizing-on-container-vert-1.html flexbox-box-sizing-on-container-vert-1-ref.html
 == flexbox-box-sizing-on-items-horiz-1a.html flexbox-box-sizing-on-items-horiz-1-ref.html
 == flexbox-box-sizing-on-items-horiz-1b.html flexbox-box-sizing-on-items-horiz-1-ref.html
 == flexbox-box-sizing-on-items-vert-1a.html flexbox-box-sizing-on-items-vert-1-ref.html
@@ -54,16 +54,17 @@ fuzzy-if(skiaContent,3,10) == flexbox-dy
 == flexbox-dyn-insertAroundText-3.xhtml flexbox-dyn-insertAroundText-3-ref.xhtml
 
 # Variant of one of the above tests, to regression-test an invalidation issue
 == flexbox-dyn-insertEmptySpan-1.xhtml flexbox-dyn-insertEmptySpan-1-ref.xhtml
 
 # Tests for empty flexboxes (with no flex items)
 == flexbox-empty-1a.xhtml  flexbox-empty-1-ref.xhtml
 == flexbox-empty-1b.xhtml  flexbox-empty-1-ref.xhtml
+== flexbox-empty-container-synthesized-baseline-001.html flexbox-empty-container-synthesized-baseline-001-ref.html
 
 # Tests for handling of floated elements inside a flexbox
 == flexbox-float-1a.xhtml  flexbox-float-1-ref.xhtml
 == flexbox-float-1b.xhtml  flexbox-float-1-ref.xhtml
 == flexbox-float-1c.xhtml  flexbox-float-1-ref.xhtml
 == flexbox-float-1d.xhtml  flexbox-float-1-ref.xhtml
 == flexbox-float-2a.xhtml  flexbox-float-2-ref.xhtml
 == flexbox-float-2b.xhtml  flexbox-float-2-ref.xhtml
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001-ref.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001-ref.html
@@ -1,48 +1,37 @@
 <!DOCTYPE html>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!-- In this reference case, we have inline-blocks instead of inline
-     flex containers. We stick an Ahem whitespace character in each
-     inline-block, with a customized line-height to make the baseline
-     end up at the bottom of the inline-block's content-box. -->
+     flex containers.  Otherwise it's the same. -->
 <html>
 <head>
   <title>CSS Reftest Reference</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <meta charset="utf-8">
   <link rel="stylesheet" type="text/css" href="support/ahem.css" />
   <style>
     body {
       font: 20px Ahem;
     }
     .flexContainer {
       display: inline-block;
       height: 16px;
       width: 16px;
-      /* Each inline-block's baseline will be the baseline of the single Ahem
-         character that it contains. We want to set up that char such that its
-         baseline is at the bottom of the container's content box (since that's
-         the corresponding flex container's baseline). So, we use a line-height
-         of 20px, which gives us a baseline of 20px * 0.8 = 16px, which is the
-         bottom of the container's content-box -- awesome. */
-      line-height: 20px;
       background: purple;
       border: 0px dotted black;
       /* (Elements that want a border will set their border-width.) */
     }
   </style>
 </head>
 <body>
   A
-  <!-- We have to include a character in the inline-blocks in order for them
-       to baseline-align; otherwise, they align the bottom of their
-       border-boxes. -->
-  <div class="flexContainer">&nbsp;</div>
-  <div class="flexContainer" style="padding-bottom: 20px">&nbsp;</div>
-  <div class="flexContainer" style="padding: 10px">&nbsp;</div>
-  <div class="flexContainer" style="border-width: 3px">&nbsp;</div>
-  <div class="flexContainer" style="border-bottom-width: 4px">&nbsp;</div>
+  <div class="flexContainer"></div>
+  <div class="flexContainer" style="padding-bottom: 20px"></div>
+  <div class="flexContainer" style="padding: 10px"></div>
+  <div class="flexContainer" style="border-width: 3px"></div>
+  <div class="flexContainer" style="border-bottom-width: 4px"></div>
+  <div class="flexContainer" style="border-bottom-width: 4px; margin: 2px"></div>
 </body>
 </html>
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001a.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001a.html
@@ -1,21 +1,22 @@
 <!DOCTYPE html>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!-- Testcase for how we compute the baseline of a horizontal flex container
      with no flex items. This is the main-axis baseline. The spec says this
      about this case:
+     https://drafts.csswg.org/css-flexbox/#flex-baselines
+     "Otherwise, the flex container has no first/last main-axis baseline set,
+     and one is synthesized if needed according to the rules of its alignment context."
 
-       The flex container's main-axis baseline is synthesized
-       from ... the flex container's content box.
-
-     I'm taking that to mean the baseline is the bottom of the content box.
+     The alignment context in this case is inline-level so the margin-box
+     should be used to synthesize the baseline.
 -->
 <html>
 <head>
   <title>CSS Test: Testing the baseline of an empty horizontal flex container</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines">
   <link rel="match" href="flexbox-baseline-empty-001-ref.html">
   <meta charset="utf-8">
@@ -36,10 +37,11 @@
 </head>
 <body>
   A
   <div class="flexContainer"></div>
   <div class="flexContainer" style="padding-bottom: 20px"></div>
   <div class="flexContainer" style="padding: 10px"></div>
   <div class="flexContainer" style="border-width: 3px"></div>
   <div class="flexContainer" style="border-bottom-width: 4px"></div>
+  <div class="flexContainer" style="border-bottom-width: 4px; margin: 2px"></div>
 </body>
 </html>
--- a/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001b.html
+++ b/layout/reftests/w3c-css/submitted/flexbox/flexbox-baseline-empty-001b.html
@@ -1,21 +1,22 @@
 <!DOCTYPE html>
 <!--
      Any copyright is dedicated to the Public Domain.
      http://creativecommons.org/publicdomain/zero/1.0/
 -->
 <!-- Testcase for how we compute the baseline of a vertical flex container
      with no flex items. This is the cross-axis baseline. The spec says this
      about this case:
+     https://drafts.csswg.org/css-flexbox/#flex-baselines
+     "Otherwise, the flex container has no first/last main-axis baseline set,
+     and one is synthesized if needed according to the rules of its alignment context."
 
-       ...the flex container's cross-axis baseline is synthesized
-       from ... the flex container's content box.
-
-     I'm taking that to mean the baseline is the bottom of the content box.
+     The alignment context in this case is inline-level so the margin-box
+     should be used to synthesize the baseline.
 -->
 <html>
 <head>
   <title>CSS Test: Testing the baseline of an empty vertical flex container</title>
   <link rel="author" title="Daniel Holbert" href="mailto:dholbert@mozilla.com">
   <link rel="help" href="http://www.w3.org/TR/css-flexbox-1/#flex-baselines">
   <link rel="match" href="flexbox-baseline-empty-001-ref.html">
   <meta charset="utf-8">
@@ -37,10 +38,11 @@
 </head>
 <body>
   A
   <div class="flexContainer"></div>
   <div class="flexContainer" style="padding-bottom: 20px"></div>
   <div class="flexContainer" style="padding: 10px"></div>
   <div class="flexContainer" style="border-width: 3px"></div>
   <div class="flexContainer" style="border-bottom-width: 4px"></div>
+  <div class="flexContainer" style="border-bottom-width: 4px; margin: 2px"></div>
 </body>
 </html>
--- a/layout/style/ServoBindings.cpp
+++ b/layout/style/ServoBindings.cpp
@@ -648,18 +648,22 @@ NS_IMPL_HOLDER_FFI_REFCOUNTING(nsIURI, U
 
 void
 Gecko_SetMozBinding(nsStyleDisplay* aDisplay,
                     const uint8_t* aURLString, uint32_t aURLStringLength,
                     ThreadSafeURIHolder* aBaseURI,
                     ThreadSafeURIHolder* aReferrer,
                     ThreadSafePrincipalHolder* aPrincipal)
 {
+  if (!aURLString) {
+    aDisplay->mBinding = nullptr;
+    return;
+  }
+
   MOZ_ASSERT(aDisplay);
-  MOZ_ASSERT(aURLString);
   MOZ_ASSERT(aBaseURI);
   MOZ_ASSERT(aReferrer);
   MOZ_ASSERT(aPrincipal);
 
   nsString url;
   nsDependentCSubstring urlString(reinterpret_cast<const char*>(aURLString),
                                   aURLStringLength);
   AppendUTF8toUTF16(urlString, url);
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -3800,45 +3800,64 @@ nsTableFrame::GetRowSpacing(int32_t aSta
   NS_ASSERTION(aStartRowIndex <= aEndRowIndex,
                "End index must not be less than start index");
   // Only one possible value so just multiply it out. Tables where index
   // matters will override this function
   return GetRowSpacing() * (aEndRowIndex - aStartRowIndex);
 }
 
 /* virtual */ nscoord
-nsTableFrame::GetLogicalBaseline(WritingMode aWritingMode) const
-{
-  nscoord ascent = 0;
+nsTableFrame::GetLogicalBaseline(WritingMode aWM) const
+{
+  nscoord baseline;
+  if (!GetNaturalBaselineBOffset(aWM, BaselineSharingGroup::eFirst, &baseline)) {
+    baseline = BSize(aWM);
+  }
+  return baseline;
+}
+
+/* virtual */ bool
+nsTableFrame::GetNaturalBaselineBOffset(WritingMode aWM,
+                                        BaselineSharingGroup aBaselineGroup,
+                                        nscoord*             aBaseline) const
+{
   RowGroupArray orderedRowGroups;
   OrderRowGroups(orderedRowGroups);
-  nsTableRowFrame* firstRow = nullptr;
   // XXX not sure if this should be the size of the containing block instead.
   nsSize containerSize = mRect.Size();
-  for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
-    nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
-    if (rgFrame->GetRowCount()) {
-      firstRow = rgFrame->GetFirstRow();
-
-      nscoord rgNormalBStart =
-        LogicalRect(aWritingMode, rgFrame->GetNormalRect(), containerSize)
-        .Origin(aWritingMode).B(aWritingMode);
-      nscoord firstRowNormalBStart =
-        LogicalRect(aWritingMode, firstRow->GetNormalRect(), containerSize)
-        .Origin(aWritingMode).B(aWritingMode);
-
-      ascent = rgNormalBStart + firstRowNormalBStart +
-               firstRow->GetRowBaseline(aWritingMode);
-      break;
-    }
-  }
-  if (!firstRow)
-    ascent = BSize(aWritingMode);
-  return ascent;
-}
+  auto TableBaseline = [aWM, containerSize] (nsTableRowGroupFrame* aRowGroup,
+                                             nsTableRowFrame* aRow) {
+    nscoord rgBStart = LogicalRect(aWM, aRowGroup->GetNormalRect(),
+                                   containerSize).BStart(aWM);
+    nscoord rowBStart = LogicalRect(aWM, aRow->GetNormalRect(),
+                                    containerSize).BStart(aWM);
+    return rgBStart + rowBStart + aRow->GetRowBaseline(aWM);
+  };
+  if (aBaselineGroup == BaselineSharingGroup::eFirst) {
+    for (uint32_t rgIndex = 0; rgIndex < orderedRowGroups.Length(); rgIndex++) {
+      nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
+      nsTableRowFrame* row = rgFrame->GetFirstRow();
+      if (row) {
+        *aBaseline = TableBaseline(rgFrame, row);
+        return true;
+      }
+    }
+  } else {
+    for (uint32_t rgIndex = orderedRowGroups.Length(); rgIndex-- > 0;) {
+      nsTableRowGroupFrame* rgFrame = orderedRowGroups[rgIndex];
+      nsTableRowFrame* row = rgFrame->GetLastRow();
+      if (row) {
+        *aBaseline = BSize(aWM) - TableBaseline(rgFrame, row);
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 /* ----- global methods ----- */
 
 nsTableFrame*
 NS_NewTableFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsTableFrame(aContext);
 }
 
--- a/layout/tables/nsTableFrame.h
+++ b/layout/tables/nsTableFrame.h
@@ -460,16 +460,20 @@ private:
   /* For the base implementation of nsTableFrame, cell spacing does not depend
    * on row/column indexing.
    */
   nscoord GetColSpacing();
   nscoord GetRowSpacing();
 
 public:
   virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override;
+
   /** return the row span of a cell, taking into account row span magic at the bottom
     * of a table. The row span equals the number of rows spanned by aCell starting at
     * aStartRowIndex, and can be smaller if aStartRowIndex is greater than the row
     * index in which aCell originates.
     *
     * @param aStartRowIndex the cell
     * @param aCell          the cell
     *
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -502,17 +502,29 @@ nsTableRowGroupFrame::ReflowChildren(nsP
     }
   }
 }
 
 nsTableRowFrame*
 nsTableRowGroupFrame::GetFirstRow()
 {
   for (nsIFrame* childFrame : mFrames) {
-    nsTableRowFrame *rowFrame = do_QueryFrame(childFrame);
+    nsTableRowFrame* rowFrame = do_QueryFrame(childFrame);
+    if (rowFrame) {
+      return rowFrame;
+    }
+  }
+  return nullptr;
+}
+
+nsTableRowFrame*
+nsTableRowGroupFrame::GetLastRow()
+{
+  for (auto iter = mFrames.rbegin(), end = mFrames.rend(); iter != end; ++iter) {
+    nsTableRowFrame* rowFrame = do_QueryFrame(*iter);
     if (rowFrame) {
       return rowFrame;
     }
   }
   return nullptr;
 }
 
 
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -98,16 +98,17 @@ public:
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableRowGroupFrame
    */
   virtual nsIAtom* GetType() const override;
 
   nsTableRowFrame* GetFirstRow();
+  nsTableRowFrame* GetLastRow();
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   virtual mozilla::WritingMode GetWritingMode() const override
     { return GetTableFrame()->GetWritingMode(); }
 
--- a/layout/tables/nsTableWrapperFrame.h
+++ b/layout/tables/nsTableWrapperFrame.h
@@ -63,16 +63,35 @@ public:
                                 const nsDisplayListSet& aLists) override;
 
   void BuildDisplayListForInnerTable(nsDisplayListBuilder*   aBuilder,
                                      const nsRect&           aDirtyRect,
                                      const nsDisplayListSet& aLists);
 
   virtual nscoord GetLogicalBaseline(mozilla::WritingMode aWritingMode) const override;
 
+  bool GetNaturalBaselineBOffset(mozilla::WritingMode aWM,
+                                 BaselineSharingGroup aBaselineGroup,
+                                 nscoord*             aBaseline) const override
+  {
+    auto innerTable = InnerTableFrame();
+    nscoord offset;
+    if (innerTable->GetNaturalBaselineBOffset(aWM, aBaselineGroup, &offset)) {
+      auto bStart = innerTable->BStart(aWM, mRect.Size());
+      if (aBaselineGroup == BaselineSharingGroup::eFirst) {
+        *aBaseline = offset + bStart;
+      } else {
+        auto bEnd = bStart + innerTable->BSize(aWM);
+        *aBaseline = BSize(aWM) - (bEnd - offset);
+      }
+      return true;
+    }
+    return false;
+  }
+
   virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
   virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
 
   virtual mozilla::LogicalSize
   ComputeAutoSize(nsRenderingContext*         aRenderingContext,
                   mozilla::WritingMode        aWM,
                   const mozilla::LogicalSize& aCBSize,
                   nscoord                     aAvailableISize,
--- a/mobile/android/base/java/org/mozilla/gecko/preferences/SearchEnginePreference.java
+++ b/mobile/android/base/java/org/mozilla/gecko/preferences/SearchEnginePreference.java
@@ -140,17 +140,17 @@ public class SearchEnginePreference exte
     /**
      * Configure this Preference object from the Gecko search engine object.
      * @param geckoEngine The Gecko-formatted object representing the search engine.
      */
     public void setSearchEngineFromBundle(GeckoBundle geckoEngine) {
         mIdentifier = geckoEngine.getString("identifier");
 
         // A null JS value gets converted into a string.
-        if (mIdentifier.equals("null")) {
+        if (mIdentifier == null || mIdentifier.equals("null")) {
             mIdentifier = "other";
         }
 
         final String engineName = geckoEngine.getString("name");
         final SpannableString titleSpannable = new SpannableString(engineName);
 
         setTitle(titleSpannable);
 
--- a/python/mozbuild/mozbuild/vendor_rust.py
+++ b/python/mozbuild/mozbuild/vendor_rust.py
@@ -100,16 +100,21 @@ Please commit or stash these changes bef
                              append_env=env)
         else:
             self.log(logging.DEBUG, 'cargo_vendor', {}, 'cargo-vendor already intalled')
         vendor_dir = mozpath.join(self.topsrcdir, 'third_party/rust')
         self.log(logging.INFO, 'rm_vendor_dir', {}, 'rm -rf %s' % vendor_dir)
         mozfile.remove(vendor_dir)
         # Once we require a new enough cargo to switch to workspaces, we can
         # just do this once on the workspace root crate.
-        for crate_root in ('toolkit/library/rust/',
-                           'toolkit/library/gtest/rust'):
+        crates_and_roots = (
+            ('gkrust', 'toolkit/library/rust'),
+            ('gkrust-gtest', 'toolkit/library/gtest/rust'),
+            ('mozjs_sys', 'js/src'),
+        )
+        for (lib, crate_root) in crates_and_roots:
             path = mozpath.join(self.topsrcdir, crate_root)
-            self._run_command_in_srcdir(args=[cargo, 'generate-lockfile', '--manifest-path', mozpath.join(path, 'Cargo.toml')])
+            # We do an |update -p| here to regenerate the Cargo.lock file with minimal changes. See bug 1324462
+            self._run_command_in_srcdir(args=[cargo, 'update', '--manifest-path', mozpath.join(path, 'Cargo.toml'), '-p', lib])
             self._run_command_in_srcdir(args=[cargo, 'vendor', '--sync', mozpath.join(path, 'Cargo.lock'), vendor_dir])
         #TODO: print stats on size of files added/removed, warn or error
         # when adding very large files (bug 1306078)
         self.repository.add_remove_files(vendor_dir)
--- a/testing/profiles/prefs_general.js
+++ b/testing/profiles/prefs_general.js
@@ -1,15 +1,16 @@
 // Base preferences file used by most test harnesses
 
 user_pref("browser.console.showInPanel", true);
 user_pref("browser.dom.window.dump.enabled", true);
 user_pref("browser.firstrun.show.localepicker", false);
 user_pref("browser.firstrun.show.uidiscovery", false);
 user_pref("browser.startup.page", 0); // use about:blank, not browser.startup.homepage
+user_pref("browser.search.suggest.timeout", 10000); // use a 10s suggestion timeout in tests
 user_pref("browser.ui.layout.tablet", 0); // force tablet UI off
 user_pref("dom.allow_scripts_to_close_windows", true);
 user_pref("dom.disable_open_during_load", false);
 user_pref("dom.experimental_forms", true); // on for testing
 user_pref("dom.forms.number", true); // on for testing
 user_pref("dom.forms.color", true); // on for testing
 user_pref("dom.forms.datetime", true); // on for testing
 user_pref("dom.max_script_run_time", 0); // no slow script dialogs
--- a/toolkit/components/search/SearchSuggestionController.jsm
+++ b/toolkit/components/search/SearchSuggestionController.jsm
@@ -11,18 +11,19 @@ const { classes: Cc, interfaces: Ci, uti
 Cu.import("resource://gre/modules/XPCOMUtils.jsm");
 Cu.import("resource://gre/modules/Services.jsm");
 Cu.import("resource://gre/modules/Promise.jsm");
 XPCOMUtils.defineLazyModuleGetter(this, "NS_ASSERT", "resource://gre/modules/debug.js");
 
 const SEARCH_RESPONSE_SUGGESTION_JSON = "application/x-suggestions+json";
 const DEFAULT_FORM_HISTORY_PARAM      = "searchbar-history";
 const HTTP_OK            = 200;
-const REMOTE_TIMEOUT     = 500; // maximum time (ms) to wait before giving up on a remote suggestions
 const BROWSER_SUGGEST_PREF = "browser.search.suggest.enabled";
+const REMOTE_TIMEOUT_PREF = "browser.search.suggest.timeout";
+const REMOTE_TIMEOUT_DEFAULT = 500; // maximum time (ms) to wait before giving up on a remote suggestions
 
 /**
  * Remote search suggestions will be shown if gRemoteSuggestionsEnabled
  * is true. Global because only one pref observer is needed for all instances.
  */
 var gRemoteSuggestionsEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF);
 Services.prefs.addObserver(BROWSER_SUGGEST_PREF, function(aSubject, aTopic, aData) {
   gRemoteSuggestionsEnabled = Services.prefs.getBoolPref(BROWSER_SUGGEST_PREF);
@@ -55,21 +56,16 @@ this.SearchSuggestionController.prototyp
   /**
    * The maximum number of remote search engine results to return.
    * We'll actually only display at most
    * maxRemoteResults - <displayed local results count> remote results.
    */
   maxRemoteResults: 10,
 
   /**
-   * The maximum time (ms) to wait before giving up on a remote suggestions.
-   */
-  remoteTimeout: REMOTE_TIMEOUT,
-
-  /**
    * The additional parameter used when searching form history.
    */
   formHistoryParam: DEFAULT_FORM_HISTORY_PARAM,
 
   // Private properties
   /**
    * The last form history result used to improve the performance of subsequent searches.
    * This shouldn't be used for any other purpose as it is never cleared and therefore could be stale.
@@ -187,18 +183,17 @@ this.SearchSuggestionController.prototyp
       // Implements nsIAutoCompleteSearch
       onSearchResult: (search, result) => {
         this._formHistoryResult = result;
 
         if (this._request) {
           this._remoteResultTimer = Cc["@mozilla.org/timer;1"].
                                     createInstance(Ci.nsITimer);
           this._remoteResultTimer.initWithCallback(this._onRemoteTimeout.bind(this),
-                                                   this.remoteTimeout || REMOTE_TIMEOUT,
-                                                   Ci.nsITimer.TYPE_ONE_SHOT);
+                                                   this.remoteTimeout, Ci.nsITimer.TYPE_ONE_SHOT);
         }
 
         switch (result.searchResult) {
           case Ci.nsIAutoCompleteResult.RESULT_SUCCESS:
           case Ci.nsIAutoCompleteResult.RESULT_NOMATCH:
             if (result.searchString !== this._searchString) {
               deferredFormHistory.resolve("Unexpected response, this._searchString does not match form history response");
               return;
@@ -391,8 +386,14 @@ this.SearchSuggestionController.prototyp
  * Determines whether the given engine offers search suggestions.
  *
  * @param {nsISearchEngine} engine - The search engine
  * @return {boolean} True if the engine offers suggestions and false otherwise.
  */
 this.SearchSuggestionController.engineOffersSuggestions = function(engine) {
  return engine.supportsResponseType(SEARCH_RESPONSE_SUGGESTION_JSON);
 };
+
+/**
+ * The maximum time (ms) to wait before giving up on a remote suggestions.
+ */
+XPCOMUtils.defineLazyPreferenceGetter(this.SearchSuggestionController.prototype, "remoteTimeout",
+                                      REMOTE_TIMEOUT_PREF, REMOTE_TIMEOUT_DEFAULT);
--- a/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
+++ b/toolkit/components/url-classifier/tests/mochitest/classifierFrame.html
@@ -27,25 +27,38 @@ function checkLoads() {
   // cache entries.
   if (window.parent.firstLoad) {
     window.parent.info("Reloading from cache...");
     window.parent.firstLoad = false;
     window.parent.loadTestFrame();
     return;
   }
 
+  let dwu = window.parent.SpecialPowers.getDOMWindowUtils(window);
+  let timer1 = window.setTimeout(function(){}, 0);
+  window.parent.ok(!dwu.isTimeoutTracking(timer1),
+                   "Timeout set from main script should not be considered as tracking");
+  let timer2 = getTrackerTimeout();
+  window.parent.ok(dwu.isTimeoutTracking(timer2),
+                   "Timeout set from included script should be considered as tracking");
+  window.clearTimeout(timer1);
+  window.clearTimeout(timer2);
+
   // End (parent) test.
   window.parent.SimpleTest.finish();
 }
 
 </script>
 
 <!-- Try loading from a malware javascript URI -->
 <script type="text/javascript" src="http://malware.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.js"></script>
 
+<!-- Try loading from a tracker javascript URI -->
+<script type="text/javascript" src="http://tracking.example.com/tests/toolkit/components/url-classifier/tests/mochitest/tracker.js"></script>
+
 <!-- Try loading from an uwanted software css URI -->
 <link rel="stylesheet" type="text/css" href="http://unwanted.example.com/tests/toolkit/components/url-classifier/tests/mochitest/evil.css"></link>
 
 <!-- Try loading a marked-as-malware css through an @import from a clean URI -->
 <link rel="stylesheet" type="text/css" href="import.css"></link>
 </head>
 
 <body onload="checkLoads()">
--- a/toolkit/components/url-classifier/tests/mochitest/mochitest.ini
+++ b/toolkit/components/url-classifier/tests/mochitest/mochitest.ini
@@ -22,16 +22,17 @@ support-files =
   basic.vtt
   dnt.html
   dnt.sjs
   update.sjs
   bad.css
   bad.css^headers^
   gethash.sjs
   gethashFrame.html
+  tracker.js
 
 [test_classifier.html]
 skip-if = (os == 'linux' && debug) #Bug 1199778
 [test_classifier_worker.html]
 [test_classify_ping.html]
 [test_classify_track.html]
 [test_gethash.html]
 [test_bug1254766.html]
new file mode 100644
--- /dev/null
+++ b/toolkit/components/url-classifier/tests/mochitest/tracker.js
@@ -0,0 +1,3 @@
+function getTrackerTimeout() {
+  return window.setTimeout(function(){}, 0);
+}
--- a/toolkit/content/tests/browser/browser_bug1198465.js
+++ b/toolkit/content/tests/browser/browser_bug1198465.js
@@ -63,10 +63,13 @@ add_task(function* () {
   // doesn't really matter.
   findBar.onCurrentSelection("foo", true);
   ok(!findBar._startFindDeferred, "prefilled value fetched");
   is(findBar._findField.value, "ab", "ab kept instead of prefill value");
 
   EventUtils.sendChar("c", window);
   is(findBar._findField.value, "abc", "c is appended after ab");
 
+  // Clear the findField value to make the test  run successfully
+  // for multiple runs in the same browser session.
+  findBar._findField.value = "";
   yield BrowserTestUtils.removeTab(aTab);
 });
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -106,17 +106,19 @@ class WidgetRenderingContext;
                                           // NSTitlebarContainerView
 
 @end
 
 @interface ChildView : NSView<
 #ifdef ACCESSIBILITY
                               mozAccessible,
 #endif
-                              mozView, NSTextInputClient>
+                              mozView, NSTextInputClient,
+                              NSDraggingSource, NSDraggingDestination,
+                              NSPasteboardItemDataProvider>
 {
 @private
   // the nsChildView that created the view. It retains this NSView, so
   // the link back to it must be weak.
   nsChildView* mGeckoChild;
 
   // Text input handler for mGeckoChild and us.  Note that this is a weak
   // reference.  Ideally, this should be a strong reference but a ChildView
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -3182,40 +3182,47 @@ NSEvent* gLastDragMouseDownEvent = nil;
 + (void)initialize
 {
   static BOOL initialized = NO;
 
   if (!initialized) {
     // Inform the OS about the types of services (from the "Services" menu)
     // that we can handle.
 
-    NSArray *sendTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
-    NSArray *returnTypes = [[NSArray alloc] initWithObjects:NSStringPboardType,NSHTMLPboardType,nil];
+    NSArray* sendTypes =
+      [[NSArray alloc] initWithObjects:NSPasteboardTypeString,
+                                       NSPasteboardTypeHTML,
+                                       nil];
+    NSArray* returnTypes =
+      [[NSArray alloc] initWithObjects:NSPasteboardTypeString,
+                                       NSPasteboardTypeHTML,
+                                       nil];
 
     [NSApp registerServicesMenuSendTypes:sendTypes returnTypes:returnTypes];
 
     [sendTypes release];
     [returnTypes release];
 
     initialized = YES;
   }
 }
 
 + (void)registerViewForDraggedTypes:(NSView*)aView
 {
-  [aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
-                                                           NSStringPboardType,
-                                                           NSHTMLPboardType,
-                                                           NSURLPboardType,
-                                                           NSFilesPromisePboardType,
-                                                           kWildcardPboardType,
-                                                           kCorePboardType_url,
-                                                           kCorePboardType_urld,
-                                                           kCorePboardType_urln,
-                                                           nil]];
+  [aView registerForDraggedTypes:[NSArray arrayWithObjects:
+                                    NSFilenamesPboardType,
+                                    NSPasteboardTypeString,
+                                    NSPasteboardTypeHTML,
+                                    NSURLPboardType,
+                                    kPasteboardTypeFileURLPromise,
+                                    kWildcardPboardType,
+                                    kCorePboardType_url,
+                                    kCorePboardType_urld,
+                                    kCorePboardType_urln,
+                                    nil]];
 }
 
 // initWithFrame:geckoChild:
 - (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if ((self = [super initWithFrame:inFrame])) {
@@ -5717,16 +5724,17 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
     }
   }
 
   return NSDragOperationGeneric;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
 }
 
+// NSDraggingDestination
 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingEntered: entered\n"));
 
   // there should never be a globalDragPboard when "draggingEntered:" is
   // called, but just in case we'll take care of it here.
@@ -5737,91 +5745,90 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
   // the view or a drop happens within the view).
   globalDragPboard = [[sender draggingPasteboard] retain];
 
   return [self doDragAction:eDragEnter sender:sender];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSDragOperationNone);
 }
 
+// NSDraggingDestination
 - (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
 {
   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingUpdated: entered\n"));
-
   return [self doDragAction:eDragOver sender:sender];
 }
 
+// NSDraggingDestination
 - (void)draggingExited:(id <NSDraggingInfo>)sender
 {
   MOZ_LOG(sCocoaLog, LogLevel::Info, ("ChildView draggingExited: entered\n"));
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   [self doDragAction:eDragExit sender:sender];
   NS_IF_RELEASE(mDragService);
 }
 
+// NSDraggingDestination
 - (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
 {
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   BOOL handled = [self doDragAction:eDrop sender:sender] != NSDragOperationNone;
   NS_IF_RELEASE(mDragService);
   return handled;
 }
 
 // NSDraggingSource
-- (void)draggedImage:(NSImage *)anImage movedTo:(NSPoint)aPoint
-{
-  // Get the drag service if it isn't already cached. The drag service
-  // isn't cached when dragging over a different application.
-  nsCOMPtr<nsIDragService> dragService = mDragService;
-  if (!dragService) {
-    dragService = do_GetService(kDragServiceContractID);
-  }
-
-  if (dragService) {
-    NSPoint pnt = [NSEvent mouseLocation];
-    FlipCocoaScreenCoordinate(pnt);
-
-    LayoutDeviceIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt);
-    dragService->DragMoved(devPoint.x, devPoint.y);
-  }
+// This is just implemented so we comply with the NSDraggingSource protocol.
+- (NSDragOperation)draggingSession:(NSDraggingSession*)session
+  sourceOperationMaskForDraggingContext:(NSDraggingContext)context
+{
+  return UINT_MAX;
 }
 
 // NSDraggingSource
-- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
+- (BOOL)ignoreModifierKeysForDraggingSession:(NSDraggingSession*)session
+{
+  return YES;
+}
+
+// NSDraggingSource
+- (void)draggingSession:(NSDraggingSession*)aSession
+           endedAtPoint:(NSPoint)aPoint
+              operation:(NSDragOperation)aOperation
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   gDraggedTransferables = nullptr;
 
-  NSEvent *currentEvent = [NSApp currentEvent];
+  NSEvent* currentEvent = [NSApp currentEvent];
   gUserCancelledDrag = ([currentEvent type] == NSKeyDown &&
                         [currentEvent keyCode] == kVK_Escape);
 
   if (!mDragService) {
     CallGetService(kDragServiceContractID, &mDragService);
     NS_ASSERTION(mDragService, "Couldn't get a drag service - big problem!");
   }
 
   if (mDragService) {
     // set the dragend point from the current mouse location
     nsDragService* dragService = static_cast<nsDragService *>(mDragService);
     NSPoint pnt = [NSEvent mouseLocation];
     FlipCocoaScreenCoordinate(pnt);
     dragService->SetDragEndPoint(gfx::IntPoint::Round(pnt.x, pnt.y));
 
-    // XXX: dropEffect should be updated per |operation|.
-    // As things stand though, |operation| isn't well handled within "our"
+    // XXX: dropEffect should be updated per |aOperation|.
+    // As things stand though, |aOperation| isn't well handled within "our"
     // events, that is, when the drop happens within the window: it is set
     // either to NSDragOperationGeneric or to NSDragOperationNone.
     // For that reason, it's not yet possible to override dropEffect per the
     // given OS value, and it's also unclear what's the correct dropEffect
     // value for NSDragOperationGeneric that is passed by other applications.
     // All that said, NSDragOperationNone is still reliable.
-    if (operation == NSDragOperationNone) {
+    if (aOperation == NSDragOperationNone) {
       nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
       dragService->GetDataTransfer(getter_AddRefs(dataTransfer));
       if (dataTransfer)
         dataTransfer->SetDropEffectInt(nsIDragService::DRAGDROP_ACTION_NONE);
     }
 
     mDragService->EndDragSession(true);
     NS_RELEASE(mDragService);
@@ -5831,20 +5838,55 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
   globalDragPboard = nil;
   [gLastDragMouseDownEvent release];
   gLastDragMouseDownEvent = nil;
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 // NSDraggingSource
-// this is just implemented so we comply with the NSDraggingSource informal protocol
-- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
-{
-  return UINT_MAX;
+- (void)draggingSession:(NSDraggingSession*)aSession
+           movedToPoint:(NSPoint)aPoint
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  // Get the drag service if it isn't already cached. The drag service
+  // isn't cached when dragging over a different application.
+  nsCOMPtr<nsIDragService> dragService = mDragService;
+  if (!dragService) {
+    dragService = do_GetService(kDragServiceContractID);
+  }
+
+  if (dragService) {
+    NSPoint pnt = [NSEvent mouseLocation];
+    FlipCocoaScreenCoordinate(pnt);
+
+    LayoutDeviceIntPoint devPoint = mGeckoChild->CocoaPointsToDevPixels(pnt);
+    dragService->DragMoved(devPoint.x, devPoint.y);
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
+// NSDraggingSource
+- (void)draggingSession:(NSDraggingSession*)aSession
+       willBeginAtPoint:(NSPoint)aPoint
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  // there should never be a globalDragPboard when "willBeginAtPoint:" is
+  // called, but just in case we'll take care of it here.
+  [globalDragPboard release];
+
+  // Set the global drag pasteboard that will be used for this drag session.
+  // This will be set back to nil when the drag session ends (mouse exits
+  // the view or a drop happens within the view).
+  globalDragPboard = [[aSession draggingPasteboard] retain];
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 // This method is a callback typically invoked in response to a drag ending on the desktop
 // or a Findow folder window; the argument passed is a path to the drop location, to be used
 // in constructing a complete pathname for the file(s) we want to create as a result of
 // the drag.
 - (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
 {
@@ -5898,16 +5940,78 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
 
   [name release];
 
   return rslt;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
+// NSPasteboardItemDataProvider
+- (void)pasteboard:(NSPasteboard*)aPasteboard
+              item:(NSPasteboardItem*)aItem
+provideDataForType:(NSString*)aType
+{
+  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+  if (!gDraggedTransferables) {
+    return;
+  }
+
+  uint32_t count = 0;
+  gDraggedTransferables->GetLength(&count);
+
+  for (uint32_t j = 0; j < count; j++) {
+    nsCOMPtr<nsITransferable> currentTransferable =
+      do_QueryElementAt(gDraggedTransferables, j);
+    if (!currentTransferable) {
+      return;
+    }
+
+    // Transform the transferable to an NSDictionary.
+    NSDictionary* pasteboardOutputDict =
+      nsClipboard::PasteboardDictFromTransferable(currentTransferable);
+    if (!pasteboardOutputDict) {
+      return;
+    }
+
+    // Write everything out to the pasteboard.
+    unsigned int typeCount = [pasteboardOutputDict count];
+    NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1];
+    [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
+    [types addObject:kWildcardPboardType];
+    for (unsigned int k = 0; k < typeCount; k++) {
+      NSString* curType = [types objectAtIndex:k];
+      if ([curType isEqualToString:NSPasteboardTypeString] ||
+          [curType isEqualToString:kCorePboardType_url] ||
+          [curType isEqualToString:kCorePboardType_urld] ||
+          [curType isEqualToString:kCorePboardType_urln]) {
+        [aPasteboard setString:[pasteboardOutputDict valueForKey:curType]
+                       forType:curType];
+      } else if ([curType isEqualToString:NSPasteboardTypeHTML]) {
+        [aPasteboard setString:
+          (nsClipboard::WrapHtmlForSystemPasteboard(
+            [pasteboardOutputDict valueForKey:curType]))
+                      forType:curType];
+      } else if ([curType isEqualToString:NSPasteboardTypeTIFF] ||
+                 [curType isEqualToString:kCustomTypesPboardType]) {
+        [aPasteboard setData:[pasteboardOutputDict valueForKey:curType]
+                     forType:curType];
+      } else if (
+        [curType isEqualToString:(NSString*)kPasteboardTypeFileURLPromise] ||
+        [curType isEqualToString:NSFilenamesPboardType]) {
+        [aPasteboard setPropertyList:[pasteboardOutputDict valueForKey:curType]
+                             forType:curType];
+      }
+    }
+  }
+
+  NS_OBJC_END_TRY_ABORT_BLOCK;
+}
+
 #pragma mark -
 
 // Support for the "Services" menu. We currently only support sending strings
 // and HTML to system services.
 
 - (id)validRequestorForSendType:(NSString *)sendType
                      returnType:(NSString *)returnType
 {
@@ -5920,17 +6024,19 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
   // returnType contains the type of data the the service would like to
   // return to this application (e.g., to overwrite the selection).
   // returnType is nil if the service will not return any data.
   //
   // The following condition thus triggers when the service expects a string
   // or HTML from us or no data at all AND when the service will either not
   // send back any data to us or will send a string or HTML back to us.
 
-#define IsSupportedType(typeStr) ([typeStr isEqual:NSStringPboardType] || [typeStr isEqual:NSHTMLPboardType])
+#define IsSupportedType(typeStr) \
+  ([typeStr isEqualToString:NSPasteboardTypeString] || \
+   [typeStr isEqualToString:NSPasteboardTypeHTML])
 
   id result = nil;
 
   if ((!sendType || IsSupportedType(sendType)) &&
       (!returnType || IsSupportedType(returnType))) {
     if (mGeckoChild) {
       // Assume that this object will be able to handle this request.
       result = self;
@@ -5972,18 +6078,18 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
 - (BOOL)writeSelectionToPasteboard:(NSPasteboard *)pboard
                              types:(NSArray *)types
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   // Make sure that the service will accept strings or HTML.
-  if ([types containsObject:NSStringPboardType] == NO &&
-      [types containsObject:NSHTMLPboardType] == NO)
+  if ([types containsObject:NSPasteboardTypeString] == NO &&
+      [types containsObject:NSPasteboardTypeHTML] == NO)
     return NO;
 
   // Bail out if there is no Gecko object.
   if (!mGeckoChild)
     return NO;
 
   // Transform the transferable to an NSDictionary.
   NSDictionary* pasteboardOutputDict = nullptr;
@@ -6000,26 +6106,28 @@ GetIntegerDeltaForEvent(NSEvent* aEvent)
   [declaredTypes addObjectsFromArray:[pasteboardOutputDict allKeys]];
   [pboard declareTypes:declaredTypes owner:nil];
 
   // Write the data to the pasteboard.
   for (unsigned int i = 0; i < typeCount; i++) {
     NSString* currentKey = [declaredTypes objectAtIndex:i];
     id currentValue = [pasteboardOutputDict valueForKey:currentKey];
 
-    if (currentKey == NSStringPboardType ||
-        currentKey == kCorePboardType_url ||
-        currentKey == kCorePboardType_urld ||
-        currentKey == kCorePboardType_urln) {
+    if ([currentKey isEqualToString:NSPasteboardTypeString] ||
+        [currentKey isEqualToString:kCorePboardType_url] ||
+        [currentKey isEqualToString:kCorePboardType_urld] ||
+        [currentKey isEqualToString:kCorePboardType_urln]) {
       [pboard setString:currentValue forType:currentKey];
-    } else if (currentKey == NSHTMLPboardType) {
-      [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue)) forType:currentKey];
-    } else if (currentKey == NSTIFFPboardType) {
+    } else if ([currentKey isEqualToString:NSPasteboardTypeHTML]) {
+      [pboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
+                forType:currentKey];
+    } else if ([currentKey isEqualToString:NSPasteboardTypeTIFF]) {
       [pboard setData:currentValue forType:currentKey];
-    } else if (currentKey == NSFilesPromisePboardType) {
+    } else if ([currentKey isEqualToString:
+                 (NSString*)kPasteboardTypeFileURLPromise]) {
       [pboard setPropertyList:currentValue forType:currentKey];
     }
   }
 
   return YES;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NO);
 }
--- a/widget/cocoa/nsClipboard.mm
+++ b/widget/cocoa/nsClipboard.mm
@@ -25,17 +25,19 @@
 #include "imgIContainer.h"
 #include "nsCocoaUtils.h"
 
 using mozilla::gfx::DataSourceSurface;
 using mozilla::gfx::SourceSurface;
 using mozilla::LogLevel;
 
 // Screenshots use the (undocumented) png pasteboard type.
-#define IMAGE_PASTEBOARD_TYPES NSTIFFPboardType, @"Apple PNG pasteboard type", nil
+#define IMAGE_PASTEBOARD_TYPES NSPasteboardTypeTIFF, \
+                               @"Apple PNG pasteboard type", \
+                               nil
 
 extern PRLogModuleInfo* sCocoaLog;
 
 extern void EnsureLogInitialized();
 
 mozilla::StaticRefPtr<nsITransferable> nsClipboard::sSelectionCache;
 
 nsClipboard::nsClipboard()
@@ -98,36 +100,37 @@ nsClipboard::SetNativeClipboardData(int3
   if (!pasteboardOutputDict)
     return NS_ERROR_FAILURE;
 
   unsigned int outputCount = [pasteboardOutputDict count];
   NSArray* outputKeys = [pasteboardOutputDict allKeys];
   NSPasteboard* cocoaPasteboard;
   if (aWhichClipboard == kFindClipboard) {
     cocoaPasteboard = [NSPasteboard pasteboardWithName:NSFindPboard];
-    [cocoaPasteboard declareTypes:[NSArray arrayWithObject:NSStringPboardType] owner:nil];
+    [cocoaPasteboard declareTypes:
+      [NSArray arrayWithObject:NSPasteboardTypeString] owner:nil];
   } else {
     // Write everything else out to the general pasteboard.
     cocoaPasteboard = [NSPasteboard generalPasteboard];
     [cocoaPasteboard declareTypes:outputKeys owner:nil];
   }
 
   for (unsigned int i = 0; i < outputCount; i++) {
     NSString* currentKey = [outputKeys objectAtIndex:i];
     id currentValue = [pasteboardOutputDict valueForKey:currentKey];
     if (aWhichClipboard == kFindClipboard) {
-      if (currentKey == NSStringPboardType)
+      if ([currentKey isEqualToString:NSPasteboardTypeString])
         [cocoaPasteboard setString:currentValue forType:currentKey];
     } else {
-      if (currentKey == NSStringPboardType ||
-          currentKey == kCorePboardType_url ||
-          currentKey == kCorePboardType_urld ||
-          currentKey == kCorePboardType_urln) {
+      if ([currentKey isEqualToString:NSPasteboardTypeString] ||
+          [currentKey isEqualToString:kCorePboardType_url] ||
+          [currentKey isEqualToString:kCorePboardType_urld] ||
+          [currentKey isEqualToString:kCorePboardType_urln]) {
         [cocoaPasteboard setString:currentValue forType:currentKey];
-      } else if (currentKey == NSHTMLPboardType) {
+      } else if ([currentKey isEqualToString:NSPasteboardTypeHTML]) {
         [cocoaPasteboard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
                          forType:currentKey];
       } else {
         [cocoaPasteboard setData:currentValue forType:currentKey];
       }
     }
   }
 
@@ -167,17 +170,17 @@ nsClipboard::TransferableFromPasteboard(
 
     NSString *pboardType = nil;
     if (nsClipboard::IsStringType(flavorStr, &pboardType)) {
       NSString* pString = [cocoaPasteboard stringForType:pboardType];
       if (!pString)
         continue;
 
       NSData* stringData;
-      if ([pboardType isEqualToString:NSRTFPboardType]) {
+      if ([pboardType isEqualToString:NSPasteboardTypeRTF]) {
         stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
       } else {
         stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
       }
       unsigned int dataLength = [stringData length];
       void* clipboardDataPtr = malloc(dataLength);
       if (!clipboardDataPtr)
         return NS_ERROR_OUT_OF_MEMORY;
@@ -254,18 +257,20 @@ nsClipboard::TransferableFromPasteboard(
         outputType = CFSTR("com.compuserve.gif");
       else
         continue;
 
       // Use ImageIO to interpret the data on the clipboard and transcode.
       // Note that ImageIO, like all CF APIs, allows NULLs to propagate freely
       // and safely in most cases (like ObjC). A notable exception is CFRelease.
       NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
-                                (NSNumber*)kCFBooleanTrue, kCGImageSourceShouldAllowFloat,
-                                (type == NSTIFFPboardType ? @"public.tiff" : @"public.png"),
+                                (NSNumber*)kCFBooleanTrue,
+                                kCGImageSourceShouldAllowFloat,
+                                (type == NSPasteboardTypeTIFF ? @"public.tiff" :
+                                                                @"public.png"),
                                 kCGImageSourceTypeIdentifierHint, nil];
 
       CGImageSourceRef source = CGImageSourceCreateWithData((CFDataRef)pasteboardData, 
                                                             (CFDictionaryRef)options);
       NSMutableData *encodedData = [NSMutableData data];
       CGImageDestinationRef dest = CGImageDestinationCreateWithData((CFMutableDataRef)encodedData,
                                                                     outputType,
                                                                     1, NULL);
@@ -550,17 +555,18 @@ nsClipboard::PasteboardDictFromTransfera
         CFRelease(destRef);
 
       if (!successfullyConverted) {
         if (tiffData)
           CFRelease(tiffData);
         continue;
       }
 
-      [pasteboardOutputDict setObject:(NSMutableData*)tiffData forKey:NSTIFFPboardType];
+      [pasteboardOutputDict setObject:(NSMutableData*)tiffData
+                               forKey:NSPasteboardTypeTIFF];
       if (tiffData)
         CFRelease(tiffData);
     }
     else if (flavorStr.EqualsLiteral(kFileMime)) {
       uint32_t len = 0;
       nsCOMPtr<nsISupports> genericFile;
       rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericFile), &len);
       if (NS_FAILED(rv)) {
@@ -587,17 +593,18 @@ nsClipboard::PasteboardDictFromTransfera
         continue;
       }
 
       NSString* str = nsCocoaUtils::ToNSString(fileURI);
       NSArray* fileList = [NSArray arrayWithObjects:str, nil];
       [pasteboardOutputDict setObject:fileList forKey:NSFilenamesPboardType];
     }
     else if (flavorStr.EqualsLiteral(kFilePromiseMime)) {
-      [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""] forKey:NSFilesPromisePboardType];      
+      [pasteboardOutputDict setObject:[NSArray arrayWithObject:@""]
+                               forKey:(NSString*)kPasteboardTypeFileURLPromise];
     }
     else if (flavorStr.EqualsLiteral(kURLMime)) {
       uint32_t len = 0;
       nsCOMPtr<nsISupports> genericURL;
       rv = aTransferable->GetTransferData(flavorStr, getter_AddRefs(genericURL), &len);
       nsCOMPtr<nsISupportsString> urlObject(do_QueryInterface(genericURL));
 
       nsAutoString url;
@@ -640,23 +647,23 @@ nsClipboard::PasteboardDictFromTransfera
   return pasteboardOutputDict;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 bool nsClipboard::IsStringType(const nsCString& aMIMEType, NSString** aPasteboardType)
 {
   if (aMIMEType.EqualsLiteral(kUnicodeMime)) {
-    *aPasteboardType = NSStringPboardType;
+    *aPasteboardType = NSPasteboardTypeString;
     return true;
   } else if (aMIMEType.EqualsLiteral(kRTFMime)) {
-    *aPasteboardType = NSRTFPboardType;
+    *aPasteboardType = NSPasteboardTypeRTF;
     return true;
   } else if (aMIMEType.EqualsLiteral(kHTMLMime)) {
-    *aPasteboardType = NSHTMLPboardType;
+    *aPasteboardType = NSPasteboardTypeHTML;
     return true;
   } else {
     return false;
   }
 }
 
 NSString* nsClipboard::WrapHtmlForSystemPasteboard(NSString* aString)
 {
--- a/widget/cocoa/nsDragService.h
+++ b/widget/cocoa/nsDragService.h
@@ -2,16 +2,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef nsDragService_h_
 #define nsDragService_h_
 
 #include "nsBaseDragService.h"
+#include "nsChildView.h"
 
 #include <Cocoa/Cocoa.h>
 
 extern NSString* const kWildcardPboardType;
 extern NSString* const kCorePboardType_url;
 extern NSString* const kCorePboardType_urld;
 extern NSString* const kCorePboardType_urln;
 extern NSString* const kCustomTypesPboardType;
@@ -43,13 +44,13 @@ private:
                               nsIScriptableRegion* aRegion);
   bool IsValidType(NSString* availableType, bool allowFileURL);
   NSString* GetStringForType(NSPasteboardItem* item, const NSString* type,
                              bool allowFileURL = false);
   NSString* GetTitleForURL(NSPasteboardItem* item);
   NSString* GetFilePath(NSPasteboardItem* item);
 
   nsCOMPtr<nsIArray> mDataItems; // only valid for a drag started within gecko
-  NSView* mNativeDragView;
+  ChildView* mNativeDragView;
   NSEvent* mNativeDragEvent;
 };
 
 #endif // nsDragService_h_
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -33,102 +33,46 @@
 using namespace mozilla;
 using namespace mozilla::gfx;
 
 extern PRLogModuleInfo* sCocoaLog;
 
 extern void EnsureLogInitialized();
 
 extern NSPasteboard* globalDragPboard;
-extern NSView* gLastDragView;
+extern ChildView* gLastDragView;
 extern NSEvent* gLastDragMouseDownEvent;
 extern bool gUserCancelledDrag;
 
 // This global makes the transferable array available to Cocoa's promised
 // file destination callback.
 nsIArray *gDraggedTransferables = nullptr;
 
-NSString* const kWildcardPboardType = @"MozillaWildcard";
-NSString* const kCorePboardType_url  = @"CorePasteboardFlavorType 0x75726C20"; // 'url '  url
-NSString* const kCorePboardType_urld = @"CorePasteboardFlavorType 0x75726C64"; // 'urld'  desc
-NSString* const kCorePboardType_urln = @"CorePasteboardFlavorType 0x75726C6E"; // 'urln'  title
-NSString* const kUTTypeURLName = @"public.url-name";
+NSString* const kWildcardPboardType    = @"org.mozilla.MozillaWildcard";
+NSString* const kCorePboardType_url    =
+  @"org.mozilla.CorePasteboardFlavorType0x75726C20"; // 'url '  url
+NSString* const kCorePboardType_urld   =
+  @"org.mozilla.CorePasteboardFlavorType0x75726C64"; // 'urld'  desc
+NSString* const kCorePboardType_urln   =
+  @"org.mozilla.CorePasteboardFlavorType0x75726C6E"; // 'urln'  title
+NSString* const kUTTypeURLName         = @"public.url-name";
 NSString* const kCustomTypesPboardType = @"org.mozilla.custom-clipdata";
 
 nsDragService::nsDragService()
 {
   mNativeDragView = nil;
   mNativeDragEvent = nil;
 
   EnsureLogInitialized();
 }
 
 nsDragService::~nsDragService()
 {
 }
 
-static nsresult SetUpDragClipboard(nsIArray* aTransferableArray)
-{
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
-
-  if (!aTransferableArray)
-    return NS_ERROR_FAILURE;
-
-  uint32_t count = 0;
-  aTransferableArray->GetLength(&count);
-
-  NSPasteboard* dragPBoard = [NSPasteboard pasteboardWithName:NSDragPboard];
-
-  for (uint32_t j = 0; j < count; j++) {
-    nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(aTransferableArray, j);
-    if (!currentTransferable)
-      return NS_ERROR_FAILURE;
-
-    // Transform the transferable to an NSDictionary
-    NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(currentTransferable);
-    if (!pasteboardOutputDict)
-      return NS_ERROR_FAILURE;
-
-    // write everything out to the general pasteboard
-    unsigned int typeCount = [pasteboardOutputDict count];
-    NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1];
-    [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
-    // Gecko is initiating this drag so we always want its own views to consider
-    // it. Add our wildcard type to the pasteboard to accomplish this.
-    [types addObject:kWildcardPboardType]; // we don't increase the count for the loop below on purpose
-    [dragPBoard declareTypes:types owner:nil];
-    for (unsigned int k = 0; k < typeCount; k++) {
-      NSString* currentKey = [types objectAtIndex:k];
-      id currentValue = [pasteboardOutputDict valueForKey:currentKey];
-      if (currentKey == NSStringPboardType ||
-          currentKey == kCorePboardType_url ||
-          currentKey == kCorePboardType_urld ||
-          currentKey == kCorePboardType_urln) {
-        [dragPBoard setString:currentValue forType:currentKey];
-      }
-      else if (currentKey == NSHTMLPboardType) {
-        [dragPBoard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
-                      forType:currentKey];
-      }
-      else if (currentKey == NSTIFFPboardType ||
-               currentKey == kCustomTypesPboardType) {
-        [dragPBoard setData:currentValue forType:currentKey];
-      }
-      else if (currentKey == NSFilesPromisePboardType ||
-               currentKey == NSFilenamesPboardType) {
-        [dragPBoard setPropertyList:currentValue forType:currentKey];        
-      }
-    }
-  }
-
-  return NS_OK;
-
-  NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
-}
-
 NSImage*
 nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode,
                                   LayoutDeviceIntRect* aDragRect,
                                   nsIScriptableRegion* aRegion)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
@@ -298,19 +242,59 @@ nsresult
 nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray,
                                      nsIScriptableRegion* aDragRgn,
                                      uint32_t aActionType)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
 
   mDataItems = aTransferableArray;
 
-  // put data on the clipboard
-  if (NS_FAILED(SetUpDragClipboard(aTransferableArray)))
-    return NS_ERROR_FAILURE;
+  // Save the transferables away in case a promised file callback is invoked.
+  gDraggedTransferables = aTransferableArray;
+
+  nsBaseDragService::StartDragSession();
+  nsBaseDragService::OpenDragPopup();
+
+  // We need to retain the view and the event during the drag in case either
+  // gets destroyed.
+  mNativeDragView = [gLastDragView retain];
+  mNativeDragEvent = [gLastDragMouseDownEvent retain];
+
+  gUserCancelledDrag = false;
+
+  NSPasteboardItem* pbItem = [NSPasteboardItem new];
+  NSMutableArray* types = [NSMutableArray arrayWithCapacity:5];
+
+  if (gDraggedTransferables) {
+    uint32_t count = 0;
+    gDraggedTransferables->GetLength(&count);
+
+    for (uint32_t j = 0; j < count; j++) {
+      nsCOMPtr<nsITransferable> currentTransferable =
+        do_QueryElementAt(aTransferableArray, j);
+      if (!currentTransferable) {
+        return NS_ERROR_FAILURE;
+      }
+
+      // Transform the transferable to an NSDictionary
+      NSDictionary* pasteboardOutputDict =
+        nsClipboard::PasteboardDictFromTransferable(currentTransferable);
+      if (!pasteboardOutputDict) {
+        return NS_ERROR_FAILURE;
+      }
+
+      // write everything out to the general pasteboard
+      [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
+      // Gecko is initiating this drag so we always want its own views to
+      // consider it. Add our wildcard type to the pasteboard to accomplish
+      // this.
+      [types addObject:kWildcardPboardType];
+    }
+  }
+  [pbItem setDataProvider:mNativeDragView forTypes:types];
 
   CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
 
   LayoutDeviceIntRect dragRect(0, 0, 20, 20);
   NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn);
   if (!image) {
     // if no image was returned, just draw a rectangle
     NSSize size;
@@ -325,46 +309,38 @@ nsDragService::InvokeDragSessionImpl(nsI
     [path lineToPoint:NSMakePoint(0, size.height)];
     [path lineToPoint:NSMakePoint(size.width, size.height)];
     [path lineToPoint:NSMakePoint(size.width, 0)];
     [path lineToPoint:NSMakePoint(0, 0)];
     [path stroke];
     [image unlockFocus];
   }
 
+  // Make drag image appear in the right place under the cursor.
   LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost());
   NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor);
   point.y = nsCocoaUtils::FlippedScreenY(point.y);
-
   point = nsCocoaUtils::ConvertPointFromScreen([gLastDragView window], point);
   NSPoint localPoint = [gLastDragView convertPoint:point fromView:nil];
- 
-  // Save the transferables away in case a promised file callback is invoked.
-  gDraggedTransferables = aTransferableArray;
-
-  nsBaseDragService::StartDragSession();
-  nsBaseDragService::OpenDragPopup();
-
-  // We need to retain the view and the event during the drag in case either gets destroyed.
-  mNativeDragView = [gLastDragView retain];
-  mNativeDragEvent = [gLastDragMouseDownEvent retain];
+  NSRect localDragRect = image.alignmentRect;
+  localDragRect.origin.x = localPoint.x;
+  localDragRect.origin.y = localPoint.y - localDragRect.size.height;
 
-  gUserCancelledDrag = false;
-  [mNativeDragView dragImage:image
-                          at:localPoint
-                      offset:NSZeroSize
-                       event:mNativeDragEvent
-                  pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
-                      source:mNativeDragView
-                   slideBack:YES];
-  gUserCancelledDrag = false;
+  NSDraggingItem* dragItem =
+    [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem];
+  [pbItem release];
+  [dragItem setDraggingFrame:localDragRect contents:image];
 
-  if (mDoingDrag)
-    nsBaseDragService::EndDragSession(false);
-  
+  NSDraggingSession* draggingSession =
+    [mNativeDragView beginDraggingSessionWithItems:
+        [NSArray arrayWithObject:[dragItem autorelease]]
+                                             event:mNativeDragEvent
+                                            source:mNativeDragView];
+  draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
+
   return NS_OK;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
 
 NS_IMETHODIMP
 nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex)
 {