Merge mozilla-central to autoland
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 01 Mar 2017 16:43:23 +0100
changeset 394374 49292318198c59a0e53f0af3f9959c8cf4c9dee0
parent 394373 36098d6bd3149192acfff82499f7ad4bbb928ae7 (current diff)
parent 394254 34c6c2f302e7b48e3ad2cec575cbd34d423a9d32 (diff)
child 394375 3395a077e8046c384b1f1b72ef0e2892dbd88c4f
push id1468
push userasasaki@mozilla.com
push dateMon, 05 Jun 2017 19:31:07 +0000
treeherdermozilla-release@0641fc6ee9d1 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
milestone54.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge mozilla-central to autoland
layout/generic/nsIFrame.h
security/nss/fuzz/hash.options
security/nss/fuzz/hash_target.cc
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -37,16 +37,17 @@ support-files =
   file_bug970276_popup2.html
   file_bug970276_favicon1.ico
   file_bug970276_favicon2.ico
   file_documentnavigation_frameset.html
   file_double_close_tab.html
   file_favicon_change.html
   file_favicon_change_not_in_document.html
   file_fullscreen-window-open.html
+  file_with_link_to_http.html
   head.js
   healthreport_pingData.js
   healthreport_testRemoteCommands.html
   moz.png
   navigating_window_with_download.html
   offlineQuotaNotification.cacheManifest
   offlineQuotaNotification.html
   page_style_sample.html
@@ -266,16 +267,17 @@ skip-if = e10s # Bug 863514 - no gesture
 skip-if = toolkit == "windows" # Disabled on Windows due to frequent failures (bug 969405)
 [browser_menuButtonFitts.js]
 skip-if = os != "win" # The Fitts Law menu button is only supported on Windows (bug 969376)
 [browser_middleMouse_noJSPaste.js]
 subsuite = clipboard
 [browser_minimize.js]
 [browser_misused_characters_in_strings.js]
 [browser_modifiedclick_inherit_principal.js]
+[browser_new_http_window_opened_from_file_tab.js]
 [browser_offlineQuotaNotification.js]
 skip-if = os == "linux" && !debug # bug 1304273
 [browser_feed_discovery.js]
 support-files = feed_discovery.html
 [browser_gZipOfflineChild.js]
 support-files = test_offline_gzip.html gZipOfflineChild.cacheManifest gZipOfflineChild.cacheManifest^headers^ gZipOfflineChild.html gZipOfflineChild.html^headers^
 [browser_overflowScroll.js]
 [browser_pageInfo.js]
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/browser_new_http_window_opened_from_file_tab.js
@@ -0,0 +1,54 @@
+/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
+/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
+
+const TEST_FILE = "file_with_link_to_http.html";
+const TEST_HTTP = "http://example.org/";
+
+// Test for bug 1338375.
+add_task(function* () {
+  // Open file:// page.
+  let dir = getChromeDir(getResolvedURI(gTestPath));
+  dir.append(TEST_FILE);
+  const uriString = Services.io.newFileURI(dir).spec;
+  let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, uriString);
+  registerCleanupFunction(function* () {
+    yield BrowserTestUtils.removeTab(tab);
+  });
+  let browser = tab.linkedBrowser;
+
+  // Set pref to open in new window.
+  Services.prefs.setIntPref("browser.link.open_newwindow", 2);
+  registerCleanupFunction(function() {
+    Services.prefs.clearUserPref("browser.link.open_newwindow");
+  });
+
+  // Open new http window from JavaScript in file:// page and check that we get
+  // a new window with the correct page and features.
+  let promiseNewWindow = BrowserTestUtils.waitForNewWindow(true, TEST_HTTP);
+  yield ContentTask.spawn(browser, TEST_HTTP, uri => {
+    content.open(uri, "_blank");
+  });
+  let win = yield promiseNewWindow;
+  registerCleanupFunction(function* () {
+    yield BrowserTestUtils.closeWindow(win);
+  });
+  ok(win, "Check that an http window loaded when using window.open.");
+  ok(win.menubar.visible,
+     "Check that the menu bar on the new window is visible.")
+  ok(win.toolbar.visible,
+     "Check that the tool bar on the new window is visible.")
+
+  // Open new http window from a link in file:// page and check that we get a
+  // new window with the correct page and features.
+  promiseNewWindow = BrowserTestUtils.waitForNewWindow(true, TEST_HTTP);
+  yield BrowserTestUtils.synthesizeMouseAtCenter("#linkToExample", {}, browser);
+  let win2 = yield promiseNewWindow;
+  registerCleanupFunction(function* () {
+    yield BrowserTestUtils.closeWindow(win2);
+  });
+  ok(win2, "Check that an http window loaded when using link.");
+  ok(win2.menubar.visible,
+     "Check that the menu bar on the new window is visible.")
+  ok(win2.toolbar.visible,
+     "Check that the tool bar on the new window is visible.")
+});
new file mode 100644
--- /dev/null
+++ b/browser/base/content/test/general/file_with_link_to_http.html
@@ -0,0 +1,9 @@
+<html>
+<head>
+  <meta charset="utf-8">
+  <title>Test page for Bug 1338375</title>
+</head>
+<body>
+  <a id="linkToExample" href="http://example.org" target="_blank">example.org</a>
+</body>
+</html>
--- a/devtools/client/inspector/markup/test/browser_markup_events_04.js
+++ b/devtools/client/inspector/markup/test/browser_markup_events_04.js
@@ -51,33 +51,33 @@ const TEST_DATA = [ // eslint-disable-li
       },
     ]
   },
   {
     selector: "#constructed-function",
     expected: [
       {
         type: "click",
-        filename: TEST_URL,
+        filename: TEST_URL + ":1",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function anonymous() {\n" +
                  "\n" +
                  "}"
       }
     ]
   },
   {
     selector: "#constructed-function-with-body-string",
     expected: [
       {
         type: "click",
-        filename: TEST_URL,
+        filename: TEST_URL + ":1",
         attributes: [
           "Bubbling",
           "DOM2"
         ],
         handler: "function anonymous(a, b, c) {\n" +
                  "  alert(\"constructedFuncWithBodyString\");\n" +
         "}"
       }
--- a/devtools/client/netmonitor/shared/components/headers-panel.js
+++ b/devtools/client/netmonitor/shared/components/headers-panel.js
@@ -7,17 +7,20 @@
 const {
   createClass,
   createFactory,
   DOM,
   PropTypes,
 } = require("devtools/client/shared/vendor/react");
 const { L10N } = require("../../utils/l10n");
 const { writeHeaderText } = require("../../utils/request-utils");
-const { getHeadersURL } = require("../../utils/mdn-utils");
+const {
+  getHeadersURL,
+  getHTTPStatusCodeURL,
+} = require("../../utils/mdn-utils");
 const { getFormattedSize } = require("../../utils/format-utils");
 const { REPS, MODE } = require("devtools/client/shared/components/reps/reps");
 const Rep = createFactory(REPS.Rep);
 
 // Components
 const MDNLink = createFactory(require("./mdn-link"));
 const PropertiesView = createFactory(require("./properties-view"));
 
@@ -167,30 +170,38 @@ const HeadersPanel = createClass({
       if (fromCache) {
         code = "cached";
       } else if (fromServiceWorker) {
         code = "service worker";
       } else {
         code = status;
       }
 
+      let statusCodeDocURL = getHTTPStatusCodeURL(code);
+      let inputWidth = status.length + statusText.length + 1;
+
       summaryStatus = (
         div({ className: "tabpanel-summary-container headers-summary" },
           div({
             className: "tabpanel-summary-label headers-summary-label",
           }, SUMMARY_STATUS),
           div({
             className: "requests-list-status-icon",
             "data-code": code,
           }),
           input({
-            className: "tabpanel-summary-value textbox-input devtools-monospace",
+            className: "tabpanel-summary-value textbox-input devtools-monospace"
+              + " status-text",
             readOnly: true,
             value: `${status} ${statusText}`,
+            size: `${inputWidth}`,
           }),
+          statusCodeDocURL ? MDNLink({
+            url: statusCodeDocURL,
+          }) : null,
           window.NetMonitorController.supportsCustomRequest && button({
             className: "devtools-button",
             onClick: cloneSelectedRequest,
           }, EDIT_AND_RESEND),
           button({
             className: "devtools-button",
             onClick: this.toggleRawHeaders,
           }, RAW_HEADERS),
--- a/devtools/client/netmonitor/utils/mdn-utils.js
+++ b/devtools/client/netmonitor/utils/mdn-utils.js
@@ -71,25 +71,66 @@ const SUPPORTED_HEADERS = [
   "Warning",
   "X-Content-Type-Options",
   "X-DNS-Prefetch-Control",
   "X-Frame-Options",
   "X-XSS-Protection"
 ];
 
 /**
+ * A mapping of HTTP status codes to external documentation. Any code included
+ * here will show a MDN link alongside it.
+ */
+const SUPPORTED_HTTP_CODES = [
+    "100",
+    "200",
+    "201",
+    "204",
+    "206",
+    "301",
+    "302",
+    "303",
+    "304",
+    "307",
+    "308",
+    "404",
+    "406",
+    "410",
+    "412",
+    "451",
+    "500",
+    "501",
+    "502",
+    "503",
+    "504"
+];
+
+/**
  * Get the MDN URL for the specified header.
  *
  * @param {string} header Name of the header for the baseURL to use.
  *
  * @return {string} The MDN URL for the header, or null if not available.
  */
 function getHeadersURL(header) {
   const lowerCaseHeader = header.toLowerCase();
   let idx = SUPPORTED_HEADERS.findIndex(item =>
     item.toLowerCase() === lowerCaseHeader);
   return idx > -1 ?
     `https://developer.mozilla.org/docs/Web/HTTP/Headers/${SUPPORTED_HEADERS[idx]}?utm_source=mozilla&utm_medium=devtools-netmonitor&utm_campaign=default` : null;
 }
 
+/**
+ * Get the MDN URL for the specified HTTP status code.
+ *
+ * @param {string} HTTP status code for the baseURL to use.
+ *
+ * @return {string} The MDN URL for the HTTP status code, or null if not available.
+ */
+function getHTTPStatusCodeURL(statusCode) {
+  let idx = SUPPORTED_HTTP_CODES.indexOf(statusCode);
+  return idx > -1 ? `https://developer.mozilla.org/docs/Web/HTTP/Status/${SUPPORTED_HTTP_CODES[idx]}` : null;
+}
+
 module.exports = {
   getHeadersURL,
+  getHTTPStatusCodeURL,
 };
--- a/devtools/client/themes/markup.css
+++ b/devtools/client/themes/markup.css
@@ -272,26 +272,34 @@ ul.children + .tag-line::before {
 
 /* Whitespace only text nodes are sometimes shown in the markup-view, and when they do
    they get a greyed-out whitespace symbol so users know what they are */
 .editor.text .whitespace {
   padding: 0 .5em;
 }
 
 .editor.text .whitespace::before {
-  content: "";
+  /* black circle (full) character */
+  content: "\25cf";
   display: inline-block;
-  height: 4px;
-  width: 4px;
-  border: 1px solid var(--theme-body-color-inactive);
-  border-radius: 50%;
+  width: 12px;
+  height: 8px;
+  margin: 0 2px;
+  line-height: 7px;
+  font-size: 0.6em;
+  color: var(--theme-body-color-inactive);
+  border-radius: 3px;
+  border-style: solid;
+  border-width: 1px;
+  text-align: center;
+  vertical-align: middle;
 }
 
 .tag-line[selected] .editor.text .whitespace::before {
-  border-color: white;
+  color: white;
 }
 
 .more-nodes {
   padding-left: 16px;
 }
 
 .styleinspector-propertyeditor {
   border: 1px solid #CCC;
--- a/devtools/client/themes/netmonitor.css
+++ b/devtools/client/themes/netmonitor.css
@@ -665,16 +665,32 @@
 }
 
 /* Headers tabpanel */
 
 .headers-overview {
   background: var(--theme-toolbar-background);
 }
 
+.headers-summary .status-text {
+    width: auto!important;
+}
+
+.headers-summary .learn-more-link {
+  color: var(--theme-highlight-blue);
+  cursor: pointer;
+  margin: 0 5px;
+  white-space: nowrap;
+  flex-grow: 1;
+}
+
+.headers-summary .learn-more-link:hover {
+  text-decoration: underline;
+}
+
 /* Response tabpanel */
 
 .response-error-header {
   margin: 0;
   padding: 3px 8px;
   background-color: var(--theme-highlight-red);
   color: var(--theme-selection-color);
 }
--- a/dom/base/ScriptSettings.cpp
+++ b/dom/base/ScriptSettings.cpp
@@ -650,16 +650,19 @@ AutoJSAPI::IsStackTop() const
 }
 #endif // DEBUG
 
 AutoEntryScript::AutoEntryScript(nsIGlobalObject* aGlobalObject,
                                  const char *aReason,
                                  bool aIsMainThread)
   : AutoJSAPI(aGlobalObject, aIsMainThread, eEntryScript)
   , mWebIDLCallerPrincipal(nullptr)
+  // This relies on us having a cx() because the AutoJSAPI constructor already
+  // ran.
+  , mCallerOverride(cx())
 {
   MOZ_ASSERT(aGlobalObject);
 
   if (aIsMainThread && gRunToCompletionListeners > 0) {
     mDocShellEntryMonitor.emplace(cx(), aReason);
   }
 }
 
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -374,16 +374,17 @@ private:
   // the aIsJSImplementedWebIDL case.  And in that case, the subject principal
   // is the principal of the callee function that is part of the CallArgs just a
   // bit up the stack, and which will outlive us.  So we know the principal
   // can't go away until then either.
   nsIPrincipal* MOZ_NON_OWNING_REF mWebIDLCallerPrincipal;
   friend nsIPrincipal* GetWebIDLCallerPrincipal();
 
   Maybe<DocshellEntryMonitor> mDocShellEntryMonitor;
+  JS::AutoHideScriptedCaller mCallerOverride;
 };
 
 /*
  * A class that can be used to force a particular incumbent script on the stack.
  */
 class AutoIncumbentScript : protected ScriptSettingsStackEntry {
 public:
   explicit AutoIncumbentScript(nsIGlobalObject* aGlobalObject);
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -7064,33 +7064,40 @@ nsContentUtils::GetSelectionInTextContro
   MOZ_ASSERT(aSelection && aRoot);
 
   if (!aSelection->RangeCount()) {
     // Nothing selected
     aOutStartOffset = aOutEndOffset = 0;
     return;
   }
 
-  nsCOMPtr<nsINode> anchorNode = aSelection->GetAnchorNode();
+  // All the node pointers here are raw pointers for performance.  We shouldn't
+  // be doing anything in this function that invalidates the node tree.
+  nsINode* anchorNode = aSelection->GetAnchorNode();
   uint32_t anchorOffset = aSelection->AnchorOffset();
-  nsCOMPtr<nsINode> focusNode = aSelection->GetFocusNode();
+  nsINode* focusNode = aSelection->GetFocusNode();
   uint32_t focusOffset = aSelection->FocusOffset();
 
   // We have at most two children, consisting of an optional text node followed
   // by an optional <br>.
   NS_ASSERTION(aRoot->GetChildCount() <= 2, "Unexpected children");
-  nsCOMPtr<nsIContent> firstChild = aRoot->GetFirstChild();
+  nsIContent* firstChild = aRoot->GetFirstChild();
 #ifdef DEBUG
   nsCOMPtr<nsIContent> lastChild = aRoot->GetLastChild();
   NS_ASSERTION(anchorNode == aRoot || anchorNode == firstChild ||
                anchorNode == lastChild, "Unexpected anchorNode");
   NS_ASSERTION(focusNode == aRoot || focusNode == firstChild ||
                focusNode == lastChild, "Unexpected focusNode");
+  // firstChild is either text or a <br> (hence an element).
+  MOZ_ASSERT_IF(firstChild,
+                firstChild->IsNodeOfType(nsINode::eTEXT) || firstChild->IsElement());
 #endif
-  if (!firstChild || !firstChild->IsNodeOfType(nsINode::eTEXT)) {
+  // Testing IsElement() is faster than testing IsNodeOfType(), since it's
+  // non-virtual.
+  if (!firstChild || firstChild->IsElement()) {
     // No text node, so everything is 0
     anchorOffset = focusOffset = 0;
   } else {
     // First child is text.  If the anchor/focus is already in the text node,
     // or the start of the root node, no change needed.  If it's in the root
     // node but not the start, or in the trailing <br>, we need to set the
     // offset to the end.
     if ((anchorNode == aRoot && anchorOffset != 0) ||
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -3249,16 +3249,31 @@ nsIDocument::HasFocus(ErrorResult& rv) c
       // Yes, we are an ancestor
       return true;
     }
   }
 
   return false;
 }
 
+TimeStamp
+nsIDocument::LastFocusTime() const
+{
+  return mLastFocusTime;
+}
+
+void
+nsIDocument::SetLastFocusTime(const TimeStamp& aFocusTime)
+{
+  MOZ_DIAGNOSTIC_ASSERT(!aFocusTime.IsNull());
+  MOZ_DIAGNOSTIC_ASSERT(mLastFocusTime.IsNull() ||
+                        aFocusTime >= mLastFocusTime);
+  mLastFocusTime = aFocusTime;
+}
+
 NS_IMETHODIMP
 nsDocument::GetReferrer(nsAString& aReferrer)
 {
   nsIDocument::GetReferrer(aReferrer);
   return NS_OK;
 }
 
 void
@@ -4748,16 +4763,24 @@ nsDocument::SetScriptGlobalObject(nsIScr
 #endif
         bool allowDNSPrefetch;
         docShell->GetAllowDNSPrefetch(&allowDNSPrefetch);
         mAllowDNSPrefetch = allowDNSPrefetch;
       }
     }
 
     MaybeRescheduleAnimationFrameNotifications();
+
+    // If we are set in a window that is already focused we should remember this
+    // as the time the document gained focus.
+    bool focused = false;
+    Unused << HasFocus(&focused);
+    if (focused) {
+      SetLastFocusTime(TimeStamp::Now());
+    }
   }
 
   // Remember the pointer to our window (or lack there of), to avoid
   // having to QI every time it's asked for.
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(mScriptGlobalObject);
   mWindow = window;
 
   // Now that we know what our window is, we can flush the CSP errors to the
--- a/dom/base/nsFocusManager.cpp
+++ b/dom/base/nsFocusManager.cpp
@@ -3606,16 +3606,27 @@ void
 nsFocusManager::SetFocusedWindowInternal(nsPIDOMWindowOuter* aWindow)
 {
   if (!PointerUnlocker::sActiveUnlocker &&
       nsContentUtils::IsInPointerLockContext(mFocusedWindow) &&
       !nsContentUtils::IsInPointerLockContext(aWindow)) {
     nsCOMPtr<nsIRunnable> runnable = new PointerUnlocker();
     NS_DispatchToCurrentThread(runnable);
   }
+
+  // Update the last focus time on any affected documents
+  if (aWindow && aWindow != mFocusedWindow) {
+    const TimeStamp now(TimeStamp::Now());
+    for (nsIDocument* doc = aWindow->GetExtantDoc();
+         doc;
+         doc = doc->GetParentDocument()) {
+      doc->SetLastFocusTime(now);
+    }
+  }
+
   mFocusedWindow = aWindow;
 }
 
 void
 nsFocusManager::MarkUncollectableForCCGeneration(uint32_t aGeneration)
 {
   if (!sInstance) {
     return;
--- a/dom/base/nsIDocument.h
+++ b/dom/base/nsIDocument.h
@@ -2651,16 +2651,18 @@ public:
   void GetDir(nsAString& aDirection) const;
   void SetDir(const nsAString& aDirection);
   nsPIDOMWindowOuter* GetDefaultView() const
   {
     return GetWindow();
   }
   Element* GetActiveElement();
   bool HasFocus(mozilla::ErrorResult& rv) const;
+  mozilla::TimeStamp LastFocusTime() const;
+  void SetLastFocusTime(const mozilla::TimeStamp& aFocusTime);
   // Event handlers are all on nsINode already
   bool MozSyntheticDocument() const
   {
     return IsSyntheticDocument();
   }
   Element* GetCurrentScript();
   void ReleaseCapture() const;
   virtual void MozSetImageElement(const nsAString& aImageElementId,
@@ -3020,16 +3022,20 @@ protected:
   nsTArray<nsAutoPtr<nsPropertyTable> > mExtraPropertyTables;
 
   // Our cached .children collection
   nsCOMPtr<nsIHTMLCollection> mChildrenCollection;
 
   // container for per-context fonts (downloadable, SVG, etc.)
   RefPtr<mozilla::dom::FontFaceSet> mFontFaceSet;
 
+  // Last time this document or a one of its sub-documents was focused.  If
+  // focus has never occurred then mLastFocusTime.IsNull() will be true.
+  mozilla::TimeStamp mLastFocusTime;
+
   // True if BIDI is enabled.
   bool mBidiEnabled : 1;
   // True if a MathML element has ever been owned by this document.
   bool mMathMLEnabled : 1;
 
   // True if this document is the initial document for a window.  This should
   // basically be true only for documents that exist in newly-opened windows or
   // documents created to satisfy a GetDocument() on a window when there's no
--- a/dom/base/nsScriptLoader.cpp
+++ b/dom/base/nsScriptLoader.cpp
@@ -732,17 +732,17 @@ nsScriptLoader::WaitForModuleFetch(nsMod
       promise = new GenericPromise::Private(__func__);
       mFetchingModules.Put(aRequest->mURI, promise);
     }
     return promise;
   }
 
   RefPtr<nsModuleScript> ms;
   MOZ_ALWAYS_TRUE(mFetchedModules.Get(aRequest->mURI, getter_AddRefs(ms)));
-  if (!ms) {
+  if (!ms || ms->InstantiationFailed()) {
     return GenericPromise::CreateAndReject(NS_ERROR_FAILURE, __func__);
   }
 
   return GenericPromise::CreateAndResolve(true, __func__);
 }
 
 nsModuleScript*
 nsScriptLoader::GetFetchedModule(nsIURI* aURL) const
@@ -994,16 +994,17 @@ ResolveRequestedModules(nsModuleLoadRequ
 
   return NS_OK;
 }
 
 void
 nsScriptLoader::StartFetchingModuleDependencies(nsModuleLoadRequest* aRequest)
 {
   MOZ_ASSERT(aRequest->mModuleScript);
+  MOZ_ASSERT(!aRequest->mModuleScript->InstantiationFailed());
   MOZ_ASSERT(!aRequest->IsReadyToRun());
   aRequest->mProgress = nsModuleLoadRequest::Progress::FetchingImports;
 
   nsCOMArray<nsIURI> urls;
   nsresult rv = ResolveRequestedModules(aRequest, urls);
   if (NS_FAILED(rv)) {
     aRequest->LoadFailed();
     return;
--- a/dom/canvas/CanvasRenderingContext2D.cpp
+++ b/dom/canvas/CanvasRenderingContext2D.cpp
@@ -52,17 +52,16 @@
 #include "gfxContext.h"
 #include "gfxPlatform.h"
 #include "gfxFont.h"
 #include "gfxBlur.h"
 #include "gfxPrefs.h"
 #include "gfxUtils.h"
 
 #include "nsFrameLoader.h"
-#include "nsBidi.h"
 #include "nsBidiPresUtils.h"
 #include "Layers.h"
 #include "LayerUserData.h"
 #include "CanvasUtils.h"
 #include "nsIMemoryReporter.h"
 #include "nsStyleUtil.h"
 #include "CanvasImageCache.h"
 
@@ -4271,27 +4270,26 @@ CanvasRenderingContext2D::DrawOrMeasureT
   processor.mDoMeasureBoundingBox = doCalculateBounds || !mIsEntireFrameInvalid;
   processor.mState = &CurrentState();
   processor.mFontgrp = currentFontStyle;
 
   nscoord totalWidthCoord;
 
   // calls bidi algo twice since it needs the full text width and the
   // bounding boxes before rendering anything
-  nsBidi bidiEngine;
   rv = nsBidiPresUtils::ProcessText(textToDraw.get(),
                                     textToDraw.Length(),
                                     isRTL ? NSBIDI_RTL : NSBIDI_LTR,
                                     presShell->GetPresContext(),
                                     processor,
                                     nsBidiPresUtils::MODE_MEASURE,
                                     nullptr,
                                     0,
                                     &totalWidthCoord,
-                                    &bidiEngine);
+                                    &mBidiEngine);
   if (NS_FAILED(rv)) {
     return rv;
   }
 
   float totalWidth = float(totalWidthCoord) / processor.mAppUnitsPerDevPixel;
   if (aWidth) {
     *aWidth = totalWidth;
   }
@@ -4396,17 +4394,17 @@ CanvasRenderingContext2D::DrawOrMeasureT
                                     textToDraw.Length(),
                                     isRTL ? NSBIDI_RTL : NSBIDI_LTR,
                                     presShell->GetPresContext(),
                                     processor,
                                     nsBidiPresUtils::MODE_DRAW,
                                     nullptr,
                                     0,
                                     nullptr,
-                                    &bidiEngine);
+                                    &mBidiEngine);
 
 
   mTarget->SetTransform(oldTransform);
 
   if (aOp == CanvasRenderingContext2D::TextDrawOperation::FILL &&
       !doCalculateBounds) {
     RedrawUser(boundingBox);
     return NS_OK;
--- a/dom/canvas/CanvasRenderingContext2D.h
+++ b/dom/canvas/CanvasRenderingContext2D.h
@@ -24,16 +24,17 @@
 #include "mozilla/UniquePtr.h"
 #include "gfx2DGlue.h"
 #include "imgIEncoder.h"
 #include "nsLayoutUtils.h"
 #include "mozilla/EnumeratedArray.h"
 #include "FilterSupport.h"
 #include "nsSVGEffects.h"
 #include "Layers.h"
+#include "nsBidi.h"
 
 class nsGlobalWindow;
 class nsXULElement;
 
 namespace mozilla {
 namespace gl {
 class SourceSurface;
 } // namespace gl
@@ -859,16 +860,18 @@ protected:
     // fallback element for a11y
     RefPtr<Element> mElement;
     // Path of the hit region in the 2d context coordinate space (not user space)
     RefPtr<gfx::Path> mPath;
   };
 
   nsTArray<RegionInfo> mHitRegionsOptions;
 
+  nsBidi mBidiEngine;
+
   /**
     * Returns true if a shadow should be drawn along with a
     * drawing operation.
     */
   bool NeedToDrawShadow()
   {
     const ContextState& state = CurrentState();
 
--- a/dom/html/HTMLInputElement.cpp
+++ b/dom/html/HTMLInputElement.cpp
@@ -2945,17 +2945,17 @@ HTMLInputElement::CreateEditor()
 {
   nsTextEditorState* state = GetEditorState();
   if (state) {
     return state->PrepareEditor();
   }
   return NS_ERROR_FAILURE;
 }
 
-NS_IMETHODIMP_(nsIContent*)
+NS_IMETHODIMP_(Element*)
 HTMLInputElement::GetRootEditorNode()
 {
   nsTextEditorState* state = GetEditorState();
   if (state) {
     return state->GetRootNode();
   }
   return nullptr;
 }
@@ -6602,27 +6602,40 @@ HTMLInputElement::SetSelectionEnd(int32_
 NS_IMETHODIMP
 HTMLInputElement::GetFiles(nsIDOMFileList** aFileList)
 {
   RefPtr<FileList> list = GetFiles();
   list.forget(aFileList);
   return NS_OK;
 }
 
-nsresult
+NS_IMETHODIMP
 HTMLInputElement::GetSelectionRange(int32_t* aSelectionStart,
                                     int32_t* aSelectionEnd)
 {
-  nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
-  nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-  if (textControlFrame) {
-    return textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
-  }
-
-  return NS_ERROR_FAILURE;
+  // Flush frames, because our editor state will want to work with the frame.
+  if (IsInComposedDoc()) {
+    GetComposedDoc()->FlushPendingNotifications(FlushType::Frames);
+  }
+  if (!GetPrimaryFrame()) {
+    // Can we return a selection range anyway here, now that it lives on our
+    // state?  In fact, could we make this behave more like
+    // GetSelectionDirection, in the sense of working even when we have no
+    // frame, by just delegating entirely to mState?  And then, do we really
+    // need the flush?
+    return NS_ERROR_FAILURE;
+  }
+
+  nsTextEditorState* state = GetEditorState();
+  if (!state) {
+    // Not a text control.
+    return NS_ERROR_FAILURE;
+  }
+
+  return state->GetSelectionRange(aSelectionStart, aSelectionEnd);
 }
 
 static void
 DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
 {
   if (dir == nsITextControlFrame::eNone) {
     aDirection.AssignLiteral("none");
   } else if (dir == nsITextControlFrame::eForward) {
@@ -6637,38 +6650,39 @@ DirectionToName(nsITextControlFrame::Sel
 void
 HTMLInputElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aRv)
 {
   if (!SupportsTextSelection()) {
     aDirection.SetIsVoid(true);
     return;
   }
 
+  nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
+  nsTextEditorState* state = GetEditorState();
+  if (!state) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return;
+  }
+
   nsresult rv = NS_ERROR_FAILURE;
-  nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
-  nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-  if (textControlFrame) {
+  if (formControlFrame) {
     nsITextControlFrame::SelectionDirection dir;
-    rv = textControlFrame->GetSelectionRange(nullptr, nullptr, &dir);
+    rv = state->GetSelectionDirection(&dir);
     if (NS_SUCCEEDED(rv)) {
       DirectionToName(dir, aDirection);
-    }
-  }
-
-  if (NS_FAILED(rv)) {
-    nsTextEditorState* state = GetEditorState();
-    if (state && state->IsSelectionCached()) {
-      DirectionToName(state->GetSelectionProperties().GetDirection(), aDirection);
       return;
     }
   }
-
-  if (NS_FAILED(rv)) {
-    aRv.Throw(rv);
-  }
+  
+  if (state->IsSelectionCached()) {
+    DirectionToName(state->GetSelectionProperties().GetDirection(), aDirection);
+    return;
+  }
+
+  aRv.Throw(rv);
 }
 
 NS_IMETHODIMP
 HTMLInputElement::GetSelectionDirection(nsAString& aDirection)
 {
   ErrorResult rv;
   GetSelectionDirection(aDirection, rv);
   return rv.StealNSResult();
--- a/dom/html/HTMLInputElement.h
+++ b/dom/html/HTMLInputElement.h
@@ -227,24 +227,26 @@ public:
   NS_IMETHOD_(bool) ValueChanged() const override;
   NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
   NS_IMETHOD_(nsIEditor*) GetTextEditor() override;
   NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
   NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
   NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD CreateEditor() override;
-  NS_IMETHOD_(nsIContent*) GetRootEditorNode() override;
+  NS_IMETHOD_(Element*) GetRootEditorNode() override;
   NS_IMETHOD_(Element*) CreatePlaceholderNode() override;
   NS_IMETHOD_(Element*) GetPlaceholderNode() override;
   NS_IMETHOD_(void) UpdatePlaceholderVisibility(bool aNotify) override;
   NS_IMETHOD_(bool) GetPlaceholderVisibility() override;
   NS_IMETHOD_(void) InitializeKeyboardEventListeners() override;
   NS_IMETHOD_(void) OnValueChanged(bool aNotify, bool aWasInteractiveUserChange) override;
   NS_IMETHOD_(bool) HasCachedSelection() override;
+  NS_IMETHOD GetSelectionRange(int32_t* aSelectionStart,
+                               int32_t* aSelectionEnd) override;
 
   void GetDisplayFileName(nsAString& aFileName) const;
 
   const nsTArray<OwningFileOrDirectory>& GetFilesOrDirectoriesInternal() const
   {
     return mFilesOrDirectories;
   }
 
@@ -951,18 +953,16 @@ protected:
    */
   bool IsValueEmpty() const;
 
   void ClearFiles(bool aSetValueChanged);
 
   void SetIndeterminateInternal(bool aValue,
                                 bool aShouldInvalidate);
 
-  nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
-
   /**
    * Called when an attribute is about to be changed
    */
   virtual nsresult BeforeSetAttr(int32_t aNameSpaceID, nsIAtom* aName,
                                  nsAttrValueOrString* aValue,
                                  bool aNotify) override;
   /**
    * Called when an attribute has just been changed
--- a/dom/html/HTMLTextAreaElement.cpp
+++ b/dom/html/HTMLTextAreaElement.cpp
@@ -284,17 +284,17 @@ HTMLTextAreaElement::UnbindFromFrame(nsT
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::CreateEditor()
 {
   return mState.PrepareEditor();
 }
 
-NS_IMETHODIMP_(nsIContent*)
+NS_IMETHODIMP_(Element*)
 HTMLTextAreaElement::GetRootEditorNode()
 {
   return mState.GetRootNode();
 }
 
 NS_IMETHODIMP_(Element*)
 HTMLTextAreaElement::CreatePlaceholderNode()
 {
@@ -835,27 +835,34 @@ HTMLTextAreaElement::SetSelectionEnd(con
     start = end;
   }
   rv = SetSelectionRange(start, end, direction);
   if (NS_FAILED(rv)) {
     aError.Throw(rv);
   }
 }
 
-nsresult
+NS_IMETHODIMP
 HTMLTextAreaElement::GetSelectionRange(int32_t* aSelectionStart,
                                        int32_t* aSelectionEnd)
 {
-  nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
-  nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-  if (textControlFrame) {
-    return textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
+  // Flush frames, because our editor state will want to work with the frame.
+  if (IsInComposedDoc()) {
+    GetComposedDoc()->FlushPendingNotifications(FlushType::Frames);
+  }
+  if (!GetPrimaryFrame()) {
+    // Can we return a selection range anyway here, now that it lives on our
+    // state?  In fact, could we make this behave more like
+    // GetSelectionDirection, in the sense of working even when we have no
+    // frame, by just delegating entirely to mState?  And then, do we really
+    // need the flush?
+    return NS_ERROR_FAILURE;
   }
 
-  return NS_ERROR_FAILURE;
+  return mState.GetSelectionRange(aSelectionStart, aSelectionEnd);
 }
 
 static void
 DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
 {
   if (dir == nsITextControlFrame::eNone) {
     aDirection.AssignLiteral("none");
   } else if (dir == nsITextControlFrame::eForward) {
@@ -875,32 +882,31 @@ HTMLTextAreaElement::GetSelectionDirecti
   return error.StealNSResult();
 }
 
 void
 HTMLTextAreaElement::GetSelectionDirection(nsAString& aDirection, ErrorResult& aError)
 {
   nsresult rv = NS_ERROR_FAILURE;
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(true);
-  nsITextControlFrame* textControlFrame = do_QueryFrame(formControlFrame);
-  if (textControlFrame) {
+  if (formControlFrame) {
     nsITextControlFrame::SelectionDirection dir;
-    rv = textControlFrame->GetSelectionRange(nullptr, nullptr, &dir);
+    rv = mState.GetSelectionDirection(&dir);
     if (NS_SUCCEEDED(rv)) {
       DirectionToName(dir, aDirection);
+      return;
     }
   }
 
-  if (NS_FAILED(rv)) {
-    if (mState.IsSelectionCached()) {
-      DirectionToName(mState.GetSelectionProperties().GetDirection(), aDirection);
-      return;
-    }
-    aError.Throw(rv);
+  if (mState.IsSelectionCached()) {
+    DirectionToName(mState.GetSelectionProperties().GetDirection(), aDirection);
+    return;
   }
+
+  aError.Throw(rv);
 }
 
 NS_IMETHODIMP
 HTMLTextAreaElement::SetSelectionDirection(const nsAString& aDirection)
 {
   ErrorResult error;
   SetSelectionDirection(aDirection, error);
   return error.StealNSResult();
--- a/dom/html/HTMLTextAreaElement.h
+++ b/dom/html/HTMLTextAreaElement.h
@@ -96,24 +96,27 @@ public:
   NS_IMETHOD_(bool) ValueChanged() const override;
   NS_IMETHOD_(void) GetTextEditorValue(nsAString& aValue, bool aIgnoreWrap) const override;
   NS_IMETHOD_(nsIEditor*) GetTextEditor() override;
   NS_IMETHOD_(nsISelectionController*) GetSelectionController() override;
   NS_IMETHOD_(nsFrameSelection*) GetConstFrameSelection() override;
   NS_IMETHOD BindToFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD_(void) UnbindFromFrame(nsTextControlFrame* aFrame) override;
   NS_IMETHOD CreateEditor() override;
-  NS_IMETHOD_(nsIContent*) GetRootEditorNode() override;
+  NS_IMETHOD_(Element*) GetRootEditorNode() override;
   NS_IMETHOD_(Element*) CreatePlaceholderNode() override;
   NS_IMETHOD_(Element*) GetPlaceholderNode() override;
   NS_IMETHOD_(void) UpdatePlaceholderVisibility(bool aNotify) override;
   NS_IMETHOD_(bool) GetPlaceholderVisibility() override;
   NS_IMETHOD_(void) InitializeKeyboardEventListeners() override;
   NS_IMETHOD_(void) OnValueChanged(bool aNotify, bool aWasInteractiveUserChange) override;
   NS_IMETHOD_(bool) HasCachedSelection() override;
+  NS_IMETHOD GetSelectionRange(int32_t* aSelectionStart,
+                               int32_t* aSelectionEnd) override;
+
 
   // nsIContent
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                nsIContent* aBindingParent,
                                bool aCompileEventHandlers) override;
   virtual void UnbindFromTree(bool aDeep = true,
                               bool aNullParent = true) override;
   virtual bool ParseAttribute(int32_t aNamespaceID,
@@ -342,18 +345,16 @@ protected:
   /**
    * Setting the value.
    *
    * @param aValue      String to set.
    * @param aFlags      See nsTextEditorState::SetValueFlags.
    */
   nsresult SetValueInternal(const nsAString& aValue, uint32_t aFlags);
 
-  nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
-
   /**
    * Common method to call from the various mutation observer methods.
    * aContent is a content node that's either the one that changed or its
    * parent; we should only respond to the change if aContent is non-anonymous.
    */
   void ContentChanged(nsIContent* aContent);
 
   virtual nsresult AfterSetAttr(int32_t aNamespaceID, nsIAtom *aName,
--- a/dom/html/nsITextControlElement.h
+++ b/dom/html/nsITextControlElement.h
@@ -134,17 +134,17 @@ public:
    * a frame has been created for the text control element, but the created
    * editor may outlive the frame itself.
    */
   NS_IMETHOD CreateEditor() = 0;
 
   /**
    * Get the anonymous root node for the text control.
    */
-  NS_IMETHOD_(nsIContent*) GetRootEditorNode() = 0;
+  NS_IMETHOD_(mozilla::dom::Element*) GetRootEditorNode() = 0;
 
   /**
    * Create the placeholder anonymous node for the text control and returns it.
    */
   NS_IMETHOD_(mozilla::dom::Element*) CreatePlaceholderNode() = 0;
 
   /**
    * Get the placeholder anonymous node for the text control.
@@ -191,15 +191,21 @@ public:
    *
    * Note that this function has the side effect of making the editor for input
    * elements be initialized eagerly.
    */
   NS_IMETHOD_(bool) HasCachedSelection() = 0;
 
   static already_AddRefed<nsITextControlElement>
   GetTextControlElementFromEditingHost(nsIContent* aHost);
+
+  /**
+   * Get the selection range start and end points.
+   */
+  NS_IMETHOD GetSelectionRange(int32_t* aSelectionStart,
+                               int32_t* aSelectionEnd) = 0;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsITextControlElement,
                               NS_ITEXTCONTROLELEMENT_IID)
 
 #endif // nsITextControlElement_h___
 
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -1544,16 +1544,83 @@ nsTextEditorState::SetSelectionPropertie
     mBoundFrame->SetSelectionRange(aProps.GetStart(),
                                    aProps.GetEnd(),
                                    aProps.GetDirection());
   } else {
     mSelectionProperties = aProps;
   }
 }
 
+nsresult
+nsTextEditorState::GetSelectionRange(int32_t* aSelectionStart,
+                                     int32_t* aSelectionEnd)
+{
+  MOZ_ASSERT(mBoundFrame,
+             "Caller didn't flush out frames and check for a frame?");
+  MOZ_ASSERT(aSelectionStart);
+  MOZ_ASSERT(aSelectionEnd);
+
+  // It's not clear that all the checks here are needed, but the previous
+  // version of this code in nsTextControlFrame was doing them, so we keep them
+  // for now.
+
+  nsresult rv = mBoundFrame->EnsureEditorInitialized();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsISelectionController* selCon = GetSelectionController();
+  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsISelection> selection;
+  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                            getter_AddRefs(selection));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
+
+  dom::Selection* sel = selection->AsSelection();
+  mozilla::dom::Element* root = GetRootNode();
+  NS_ENSURE_STATE(root);
+  nsContentUtils::GetSelectionInTextControl(sel, root,
+                                            *aSelectionStart, *aSelectionEnd);
+  return NS_OK;
+}
+
+nsresult
+nsTextEditorState::GetSelectionDirection(nsITextControlFrame::SelectionDirection* aDirection)
+{
+  MOZ_ASSERT(mBoundFrame,
+             "Caller didn't flush out frames and check for a frame?");
+  MOZ_ASSERT(aDirection);
+
+  // It's not clear that all the checks here are needed, but the previous
+  // version of this code in nsTextControlFrame was doing them, so we keep them
+  // for now.
+
+  nsresult rv = mBoundFrame->EnsureEditorInitialized();
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsISelectionController* selCon = GetSelectionController();
+  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
+
+  nsCOMPtr<nsISelection> selection;
+  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
+                            getter_AddRefs(selection));
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
+
+  dom::Selection* sel = selection->AsSelection();
+  nsDirection direction = sel->GetSelectionDirection();
+  if (direction == eDirNext) {
+    *aDirection = nsITextControlFrame::eForward;
+  } else if (direction == eDirPrevious) {
+    *aDirection = nsITextControlFrame::eBackward;
+  } else {
+    NS_NOTREACHED("Invalid nsDirection enum value");
+  }
+  return NS_OK;
+}
 
 HTMLInputElement*
 nsTextEditorState::GetParentNumberControl(nsFrame* aFrame) const
 {
   MOZ_ASSERT(aFrame);
   nsIContent* content = aFrame->GetContent();
   MOZ_ASSERT(content);
   nsIContent* parent = content->GetParent();
@@ -1616,38 +1683,40 @@ nsTextEditorState::UnbindFromFrame(nsTex
   GetValue(value, true);
 
   if (mRestoringSelection) {
     mRestoringSelection->Revoke();
     mRestoringSelection = nullptr;
   }
 
   // Save our selection state if needed.
-  // Note that nsTextControlFrame::GetSelectionRange attempts to initialize the
+  // Note that GetSelectionRange attempts to initialize the
   // editor before grabbing the range, and because this is not an acceptable
   // side effect for unbinding from a text control frame, we need to call
   // GetSelectionRange before calling DestroyEditor, and only if
   // mEditorInitialized indicates that we actually have an editor available.
   int32_t start = 0, end = 0;
   nsITextControlFrame::SelectionDirection direction =
     nsITextControlFrame::eForward;
   if (mEditorInitialized) {
     HTMLInputElement* number = GetParentNumberControl(aFrame);
     if (number) {
       // If we are inside a number control, cache the selection on the
       // parent control, because this text editor state will be destroyed
       // together with the native anonymous text control.
       SelectionProperties props;
-      mBoundFrame->GetSelectionRange(&start, &end, &direction);
+      GetSelectionRange(&start, &end);
+      GetSelectionDirection(&direction);
       props.SetStart(start);
       props.SetEnd(end);
       props.SetDirection(direction);
       number->SetSelectionProperties(props);
     } else {
-      mBoundFrame->GetSelectionRange(&start, &end, &direction);
+      GetSelectionRange(&start, &end);
+      GetSelectionDirection(&direction);
       mSelectionProperties.SetStart(start);
       mSelectionProperties.SetEnd(end);
       mSelectionProperties.SetDirection(direction);
       mSelectionCached = true;
     }
   }
 
   // Destroy our editor
--- a/dom/html/nsTextEditorState.h
+++ b/dom/html/nsTextEditorState.h
@@ -257,16 +257,22 @@ public:
   };
 
   bool IsSelectionCached() const;
   SelectionProperties& GetSelectionProperties();
   void SetSelectionProperties(SelectionProperties& aProps);
   void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
   bool HasNeverInitializedBefore() const { return !mEverInited; }
 
+  // Get the selection range start and end points in our text.
+  nsresult GetSelectionRange(int32_t* aSelectionStart, int32_t* aSelectionEnd);
+
+  // Get the selection direction
+  nsresult GetSelectionDirection(nsITextControlFrame::SelectionDirection* aDirection);
+
   void UpdateEditableState(bool aNotify) {
     if (mRootNode) {
       mRootNode->UpdateEditableState(aNotify);
     }
   }
 
 private:
   friend class RestoreSelectionState;
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4533,22 +4533,39 @@ ContentParent::CommonCreateWindow(PBrows
   }
 
   nsCOMPtr<nsPIWindowWatcher> pwwatch =
     do_GetService(NS_WINDOWWATCHER_CONTRACTID, &aResult);
   if (NS_WARN_IF(NS_FAILED(aResult))) {
     return IPC_OK();
   }
 
-  if (aSetOpener && thisTabParent) {
-    aResult = pwwatch->OpenWindowWithTabParent(thisTabParent, aFeatures,
-                                               aCalledFromJS, aFullZoom,
-                                               getter_AddRefs(aNewTabParent));
-  } else {
-    aResult = pwwatch->OpenWindowWithoutParent(getter_AddRefs(aNewTabParent));
+  aResult = pwwatch->OpenWindowWithTabParent(aSetOpener ? thisTabParent : nullptr,
+                                             aFeatures, aCalledFromJS, aFullZoom,
+                                             getter_AddRefs(aNewTabParent));
+  if (NS_WARN_IF(NS_FAILED(aResult))) {
+    return IPC_OK();
+  }
+
+  if (aURIToLoad) {
+    nsCOMPtr<mozIDOMWindowProxy> openerWindow;
+    if (aSetOpener && thisTabParent) {
+      openerWindow = thisTabParent->GetParentWindowOuter();
+    }
+    nsCOMPtr<nsIBrowserDOMWindow> newBrowserDOMWin =
+      TabParent::GetFrom(aNewTabParent)->GetBrowserDOMWindow();
+    if (NS_WARN_IF(!newBrowserDOMWin)) {
+      aResult = NS_ERROR_ABORT;
+      return IPC_OK();
+    }
+    nsCOMPtr<mozIDOMWindowProxy> win;
+    aResult = newBrowserDOMWin->OpenURI(aURIToLoad, openerWindow,
+                                        nsIBrowserDOMWindow::OPEN_CURRENTWINDOW,
+                                        nsIBrowserDOMWindow::OPEN_NEW,
+                                        getter_AddRefs(win));
   }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 ContentParent::RecvCreateWindow(PBrowserParent* aThisTab,
                                 PBrowserParent* aNewTab,
--- a/dom/permission/tests/test_permissions_api.html
+++ b/dom/permission/tests/test_permissions_api.html
@@ -169,17 +169,17 @@
   function createIframe() {
     return new Promise((resolve) => {
       const iframe = document.createElement('iframe');
       iframe.src = 'file_empty.html';
       iframe.onload = () => resolve(iframe.contentWindow);
       document.body.appendChild(iframe);
     });
   }
-  debugger;
+
   window.onload = () => {
     enablePrefs()
       .then(createIframe)
       .then(createPermissionTester)
       .then((tester) => {
         return tester
           .checkUnsupportedPermissions()
           .then(() => tester.setPermissions(UNKNOWN_ACTION))
--- a/dom/workers/ServiceWorkerClient.cpp
+++ b/dom/workers/ServiceWorkerClient.cpp
@@ -28,18 +28,19 @@ NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(Se
 NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClient)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient)
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
-ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc)
+ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc, uint32_t aOrdinal)
   : mType(ClientType::Window)
+  , mOrdinal(aOrdinal)
   , mWindowId(0)
   , mFrameType(FrameType::None)
 {
   MOZ_ASSERT(aDoc);
   nsresult rv = aDoc->GetOrCreateId(mClientId);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to get the UUID of the document.");
   }
@@ -54,34 +55,69 @@ ServiceWorkerClientInfo::ServiceWorkerCl
   nsCOMPtr<nsIURI> originalURI = aDoc->GetOriginalURI();
   if (originalURI) {
     nsAutoCString spec;
     originalURI->GetSpec(spec);
     CopyUTF8toUTF16(spec, mUrl);
   }
   mVisibilityState = aDoc->VisibilityState();
 
+  mLastFocusTime = aDoc->LastFocusTime();
+
   ErrorResult result;
   mFocused = aDoc->HasFocus(result);
   if (result.Failed()) {
     NS_WARNING("Failed to get focus information.");
   }
 
+  MOZ_ASSERT_IF(mLastFocusTime.IsNull(), !mFocused);
+  MOZ_ASSERT_IF(mFocused, !mLastFocusTime.IsNull());
+
   RefPtr<nsGlobalWindow> outerWindow = nsGlobalWindow::Cast(aDoc->GetWindow());
   if (!outerWindow) {
     MOZ_ASSERT(mFrameType == FrameType::None);
   } else if (!outerWindow->IsTopLevelWindow()) {
     mFrameType = FrameType::Nested;
   } else if (outerWindow->HadOriginalOpener()) {
     mFrameType = FrameType::Auxiliary;
   } else {
     mFrameType = FrameType::Top_level;
   }
 }
 
+bool
+ServiceWorkerClientInfo::operator<(const ServiceWorkerClientInfo& aRight) const
+{
+  // Note: the mLastFocusTime comparisons are reversed because we need to
+  // put most recently focused values first.  The mOrdinal comparison is
+  // normal, though, because otherwise we want normal creation order.
+
+  if (mLastFocusTime == aRight.mLastFocusTime) {
+    return mOrdinal < aRight.mOrdinal;
+  }
+
+  if (mLastFocusTime.IsNull()) {
+    return false;
+  }
+
+  if (aRight.mLastFocusTime.IsNull()) {
+    return true;
+  }
+
+  return mLastFocusTime > aRight.mLastFocusTime;
+}
+
+bool
+ServiceWorkerClientInfo::operator==(const ServiceWorkerClientInfo& aRight) const
+{
+  return mLastFocusTime == aRight.mLastFocusTime &&
+         mOrdinal == aRight.mOrdinal &&
+         mClientId == aRight.mClientId;
+}
+
 JSObject*
 ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return ClientBinding::Wrap(aCx, this, aGivenProto);
 }
 
 ClientType
 ServiceWorkerClient::Type() const
--- a/dom/workers/ServiceWorkerClient.h
+++ b/dom/workers/ServiceWorkerClient.h
@@ -26,33 +26,38 @@ class ServiceWorkerWindowClient;
 // Used as a container object for information needed to create
 // client objects.
 class ServiceWorkerClientInfo final
 {
   friend class ServiceWorkerClient;
   friend class ServiceWorkerWindowClient;
 
 public:
-  explicit ServiceWorkerClientInfo(nsIDocument* aDoc);
+  explicit ServiceWorkerClientInfo(nsIDocument* aDoc, uint32_t aOrdinal = 0);
 
   const nsString& ClientId() const
   {
     return mClientId;
   }
 
+  bool operator<(const ServiceWorkerClientInfo& aRight) const;
+  bool operator==(const ServiceWorkerClientInfo& aRight) const;
+
 private:
   const mozilla::dom::ClientType mType;
+  const uint32_t mOrdinal;
   nsString mClientId;
   uint64_t mWindowId;
   nsString mUrl;
 
   // Window Clients
   VisibilityState mVisibilityState;
+  FrameType mFrameType;
+  TimeStamp mLastFocusTime;
   bool mFocused;
-  FrameType mFrameType;
 };
 
 class ServiceWorkerClient : public nsISupports,
                             public nsWrapperCache
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClient)
--- a/dom/workers/ServiceWorkerManager.cpp
+++ b/dom/workers/ServiceWorkerManager.cpp
@@ -2969,71 +2969,74 @@ ServiceWorkerManager::GetAllClients(nsIP
 
   nsCOMPtr<nsISimpleEnumerator> enumerator;
   nsresult rv = obs->EnumerateObservers("service-worker-get-client",
                                         getter_AddRefs(enumerator));
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
-  auto ProcessDocument = [&aDocuments](nsIPrincipal* aPrincipal, nsIDocument* aDoc) {
-    if (!aDoc || !aDoc->GetWindow()) {
-      return;
+  // Get a list of Client documents out of the observer service
+  AutoTArray<nsCOMPtr<nsIDocument>, 32> docList;
+  bool loop = true;
+  while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
+    nsCOMPtr<nsISupports> ptr;
+    rv = enumerator->GetNext(getter_AddRefs(ptr));
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      continue;
+    }
+
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
+    if (!doc || !doc->GetWindow()) {
+      continue;
     }
 
     bool equals = false;
-    aPrincipal->Equals(aDoc->NodePrincipal(), &equals);
+    Unused << aPrincipal->Equals(doc->NodePrincipal(), &equals);
     if (!equals) {
-      return;
+      continue;
     }
 
     // Treat http windows with devtools opened as secure if the correct devtools
     // setting is enabled.
-    if (!aDoc->GetWindow()->GetServiceWorkersTestingEnabled() &&
+    if (!doc->GetWindow()->GetServiceWorkersTestingEnabled() &&
         !Preferences::GetBool("dom.serviceWorkers.testing.enabled") &&
-        !IsFromAuthenticatedOrigin(aDoc)) {
-      return;
+        !IsFromAuthenticatedOrigin(doc)) {
+      continue;
     }
 
-    ServiceWorkerClientInfo clientInfo(aDoc);
-    aDocuments.AppendElement(aDoc);
-  };
-
-  // Since it's not simple to check whether a document is in
-  // mControlledDocuments, we take different code paths depending on whether we
-  // need to look at all documents.  The common parts of the two loops are
-  // factored out into the ProcessDocument lambda.
-  if (aIncludeUncontrolled) {
-    bool loop = true;
-    while (NS_SUCCEEDED(enumerator->HasMoreElements(&loop)) && loop) {
-      nsCOMPtr<nsISupports> ptr;
-      rv = enumerator->GetNext(getter_AddRefs(ptr));
-      if (NS_WARN_IF(NS_FAILED(rv))) {
+    // If we are only returning controlled Clients then skip any documents
+    // that are for different registrations.
+    if (!aIncludeUncontrolled) {
+      ServiceWorkerRegistrationInfo* reg = mControlledDocuments.GetWeak(doc);
+      if (!reg || reg->mScope != registration->mScope) {
         continue;
       }
-
-      nsCOMPtr<nsIDocument> doc = do_QueryInterface(ptr);
-      ProcessDocument(aPrincipal, doc);
+    }
+
+    if (!aIncludeUncontrolled && !mControlledDocuments.Contains(doc)) {
+      continue;
     }
-  } else {
-    for (auto iter = mControlledDocuments.Iter(); !iter.Done(); iter.Next()) {
-      ServiceWorkerRegistrationInfo* thisRegistration = iter.UserData();
-      MOZ_ASSERT(thisRegistration);
-      if (!registration->mScope.Equals(thisRegistration->mScope)) {
-        continue;
-      }
-
-      nsCOMPtr<nsIDocument> doc = do_QueryInterface(iter.Key());
-
-      // All controlled documents must have an outer window.
-      MOZ_ASSERT(doc->GetWindow());
-
-      ProcessDocument(aPrincipal, doc);
-    }
-  }
+
+    docList.AppendElement(doc.forget());
+  }
+
+  // The observer service gives us the list in reverse creation order.
+  // We need to maintain creation order, so reverse the list before
+  // processing.
+  docList.Reverse();
+
+  // Finally convert to the list of ServiceWorkerClientInfo objects.
+  uint32_t ordinal = 0;
+  for (uint32_t i = 0; i < docList.Length(); ++i) {
+    aDocuments.AppendElement(ServiceWorkerClientInfo(docList[i], ordinal));
+    ordinal += 1;
+  }
+
+  aDocuments.Sort();
 }
 
 void
 ServiceWorkerManager::MaybeClaimClient(nsIDocument* aDocument,
                                        ServiceWorkerRegistrationInfo* aWorkerRegistration)
 {
   MOZ_ASSERT(aWorkerRegistration);
   MOZ_ASSERT(aWorkerRegistration->GetActive());
--- a/dom/workers/test/serviceworkers/claim_worker_2.js
+++ b/dom/workers/test/serviceworkers/claim_worker_2.js
@@ -16,12 +16,15 @@ onactivate = function(e) {
 
     return claimPromise.then(self.clients.matchAll().then(function(matched) {
       // should be 1
       result.match_count_after = matched.length;
       if (result.match_count_after === 1) {
         matched[0].postMessage(result);
       } else {
         dump("ERROR: claim_worker_2 failed to capture clients.\n");
+        for (let i = 0; i < matched.length; ++i) {
+          dump('### ### matched[' + i + ']: ' + matched[i].url + '\n');
+        }
       }
     }));
   });
 }
--- a/gfx/2d/DrawCommand.h
+++ b/gfx/2d/DrawCommand.h
@@ -367,16 +367,21 @@ PathExtentsToMaxStrokeExtents(const Stro
     styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
   }
 
   styleExpansionFactor *= aStrokeOptions.mLineWidth;
 
   double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
   double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
 
+  // Even if the stroke only partially covers a pixel, it must still render to
+  // full pixels. Round up to compensate for this.
+  dx = ceil(dx);
+  dy = ceil(dy);
+
   Rect result = aRect;
   result.Inflate(dx, dy);
   return result;
 }
 
 class StrokeCommand : public DrawingCommand
 {
 public:
--- a/gfx/2d/DrawTargetRecording.cpp
+++ b/gfx/2d/DrawTargetRecording.cpp
@@ -387,17 +387,17 @@ DrawTargetRecording::FillGlyphs(ScaledFo
     RecordingFontUserData *userData = new RecordingFontUserData;
     userData->refPtr = aFont;
     userData->recorder = mRecorder;
     aFont->AddUserData(reinterpret_cast<UserDataKey*>(mRecorder.get()), userData, 
                        &RecordingFontUserDataDestroyFunc);
   }
 
   mRecorder->RecordEvent(RecordedFillGlyphs(this, aFont, aPattern, aOptions, aBuffer.mGlyphs, aBuffer.mNumGlyphs));
-  mFinalDT->FillGlyphs(aFont, aBuffer, aPattern, aOptions, aRenderingOptions);
+  mFinalDT->FillGlyphs(aFont, aBuffer, *AdjustedPattern(aPattern), aOptions, aRenderingOptions);
 }
 
 void
 DrawTargetRecording::Mask(const Pattern &aSource,
                           const Pattern &aMask,
                           const DrawOptions &aOptions)
 {
   EnsurePatternDependenciesStored(aSource);
--- a/gfx/2d/DrawTargetSkia.cpp
+++ b/gfx/2d/DrawTargetSkia.cpp
@@ -19,16 +19,17 @@
 #include "skia/include/core/SkColorFilter.h"
 #include "skia/include/effects/SkBlurImageFilter.h"
 #include "skia/include/effects/SkLayerRasterizer.h"
 #include "skia/src/core/SkSpecialImage.h"
 #include "Blur.h"
 #include "Logging.h"
 #include "Tools.h"
 #include "DataSurfaceHelpers.h"
+#include "PathHelpers.h"
 #include "Swizzle.h"
 #include <algorithm>
 
 #ifdef USE_SKIA_GPU
 #include "GLDefs.h"
 #include "skia/include/gpu/SkGr.h"
 #include "skia/include/gpu/GrContext.h"
 #include "skia/include/gpu/GrDrawContext.h"
@@ -800,29 +801,109 @@ DrawTargetSkia::Stroke(const Path *aPath
 
   if (!skiaPath->GetPath().isFinite()) {
     return;
   }
 
   mCanvas->drawPath(skiaPath->GetPath(), paint.mPaint);
 }
 
+static Float
+DashPeriodLength(const StrokeOptions& aStrokeOptions)
+{
+  Float length = 0;
+  for (size_t i = 0; i < aStrokeOptions.mDashLength; i++) {
+    length += aStrokeOptions.mDashPattern[i];
+  }
+  if (aStrokeOptions.mDashLength & 1) {
+    // "If an odd number of values is provided, then the list of values is
+    // repeated to yield an even number of values."
+    // Double the length.
+    length += length;
+  }
+  return length;
+}
+
+static inline Float
+RoundDownToMultiple(Float aValue, Float aFactor)
+{
+  return floorf(aValue / aFactor) * aFactor;
+}
+
+static Rect
+UserSpaceStrokeClip(const IntRect &aDeviceClip,
+                    const Matrix &aTransform,
+                    const StrokeOptions &aStrokeOptions)
+{
+  Matrix inverse = aTransform;
+  if (!inverse.Invert()) {
+    return Rect();
+  }
+  Rect deviceClip(aDeviceClip);
+  deviceClip.Inflate(MaxStrokeExtents(aStrokeOptions, aTransform));
+  return inverse.TransformBounds(deviceClip);
+}
+
+static Rect
+ShrinkClippedStrokedRect(const Rect &aStrokedRect, const IntRect &aDeviceClip,
+                         const Matrix &aTransform,
+                         const StrokeOptions &aStrokeOptions)
+{
+  Rect userSpaceStrokeClip =
+    UserSpaceStrokeClip(aDeviceClip, aTransform, aStrokeOptions);
+
+  Rect intersection = aStrokedRect.Intersect(userSpaceStrokeClip);
+  Float dashPeriodLength = DashPeriodLength(aStrokeOptions);
+  if (intersection.IsEmpty() || dashPeriodLength == 0.0f) {
+    return intersection;
+  }
+
+  // Reduce the rectangle side lengths in multiples of the dash period length
+  // so that the visible dashes stay in the same place.
+  Margin insetBy = aStrokedRect - intersection;
+  insetBy.top = RoundDownToMultiple(insetBy.top, dashPeriodLength);
+  insetBy.right = RoundDownToMultiple(insetBy.right, dashPeriodLength);
+  insetBy.bottom = RoundDownToMultiple(insetBy.bottom, dashPeriodLength);
+  insetBy.left = RoundDownToMultiple(insetBy.left, dashPeriodLength);
+
+  Rect shrunkRect = aStrokedRect;
+  shrunkRect.Deflate(insetBy);
+  return shrunkRect;
+}
+
 void
 DrawTargetSkia::StrokeRect(const Rect &aRect,
                            const Pattern &aPattern,
                            const StrokeOptions &aStrokeOptions,
                            const DrawOptions &aOptions)
 {
+  // Stroking large rectangles with dashes is expensive with Skia (fixed
+  // overhead based on the number of dashes, regardless of whether the dashes
+  // are visible), so we try to reduce the size of the stroked rectangle as
+  // much as possible before passing it on to Skia.
+  Rect rect = aRect;
+  if (aStrokeOptions.mDashLength > 0 && !rect.IsEmpty()) {
+    IntRect deviceClip(IntPoint(0, 0), mSize);
+    SkIRect clipBounds;
+    if (mCanvas->getClipDeviceBounds(&clipBounds)) {
+      deviceClip = SkIRectToIntRect(clipBounds);
+    }
+    rect = ShrinkClippedStrokedRect(rect, deviceClip, mTransform, aStrokeOptions);
+    if (rect.IsEmpty()) {
+      return;
+    }
+  }
+
   MarkChanged();
   AutoPaintSetup paint(mCanvas.get(), aOptions, aPattern);
   if (!StrokeOptionsToPaint(paint.mPaint, aStrokeOptions)) {
     return;
   }
 
-  mCanvas->drawRect(RectToSkRect(aRect), paint.mPaint);
+  mCanvas->drawRect(RectToSkRect(rect), paint.mPaint);
 }
 
 void
 DrawTargetSkia::StrokeLine(const Point &aStart,
                            const Point &aEnd,
                            const Pattern &aPattern,
                            const StrokeOptions &aStrokeOptions,
                            const DrawOptions &aOptions)
--- a/gfx/2d/HelpersSkia.h
+++ b/gfx/2d/HelpersSkia.h
@@ -292,16 +292,22 @@ RectToSkIRect(const Rect& aRect)
 }
 
 static inline SkIRect
 IntRectToSkIRect(const IntRect& aRect)
 {
   return SkIRect::MakeXYWH(aRect.x, aRect.y, aRect.width, aRect.height);
 }
 
+static inline IntRect
+SkIRectToIntRect(const SkIRect& aRect)
+{
+  return IntRect(aRect.x(), aRect.y(), aRect.width(), aRect.height());
+}
+
 static inline Point
 SkPointToPoint(const SkPoint &aPoint)
 {
   return Point(SkScalarToFloat(aPoint.x()), SkScalarToFloat(aPoint.y()));
 }
 
 static inline Rect
 SkRectToRect(const SkRect &aRect)
--- a/gfx/2d/PathHelpers.cpp
+++ b/gfx/2d/PathHelpers.cpp
@@ -265,13 +265,19 @@ MaxStrokeExtents(const StrokeOptions& aS
       styleExpansionFactor < M_SQRT2 * aStrokeOptions.mMiterLimit) {
     styleExpansionFactor = M_SQRT2 * aStrokeOptions.mMiterLimit;
   }
 
   styleExpansionFactor *= aStrokeOptions.mLineWidth;
 
   double dx = styleExpansionFactor * hypot(aTransform._11, aTransform._21);
   double dy = styleExpansionFactor * hypot(aTransform._22, aTransform._12);
+
+  // Even if the stroke only partially covers a pixel, it must still render to
+  // full pixels. Round up to compensate for this.
+  dx = ceil(dx);
+  dy = ceil(dy);
+
   return Margin(dy, dx, dy, dx);
 }
 
 } // namespace gfx
 } // namespace mozilla
--- a/gfx/layers/apz/util/APZEventState.cpp
+++ b/gfx/layers/apz/util/APZEventState.cpp
@@ -363,17 +363,17 @@ APZEventState::ProcessTouchEvent(const W
   default:
     NS_WARNING("Unknown touch event type");
   }
 
   if (sentContentResponse &&
         aApzResponse == nsEventStatus_eConsumeDoDefault &&
         gfxPrefs::PointerEventsEnabled()) {
     WidgetTouchEvent cancelEvent(aEvent);
-    cancelEvent.mMessage = eTouchCancel;
+    cancelEvent.mMessage = eTouchPointerCancel;
     cancelEvent.mFlags.mCancelable = false; // mMessage != eTouchCancel;
     for (uint32_t i = 0; i < cancelEvent.mTouches.Length(); ++i) {
       if (mozilla::dom::Touch* touch = cancelEvent.mTouches[i]) {
         touch->convertToPointer = true;
       }
     }
     nsEventStatus status;
     cancelEvent.mWidget->DispatchEvent(&cancelEvent, status);
--- a/gfx/thebes/gfxWindowsPlatform.cpp
+++ b/gfx/thebes/gfxWindowsPlatform.cpp
@@ -707,18 +707,25 @@ gfxWindowsPlatform::GetCommonFallbackFon
             aFontList.AppendElement(kFontMVBoli);
             aFontList.AppendElement(kFontEbrima);
             break;
         case 0x09:
             aFontList.AppendElement(kFontNirmalaUI);
             aFontList.AppendElement(kFontUtsaah);
             aFontList.AppendElement(kFontAparajita);
             break;
+        case 0x0a:
+        case 0x0b:
+        case 0x0c:
+        case 0x0d:
+            aFontList.AppendElement(kFontNirmalaUI);
+            break;
         case 0x0e:
             aFontList.AppendElement(kFontLaoUI);
+            aFontList.AppendElement(kFontLeelawadeeUI);
             break;
         case 0x10:
             aFontList.AppendElement(kFontMyanmarText);
             break;
         case 0x11:
             aFontList.AppendElement(kFontMalgunGothic);
             break;
         case 0x12:
@@ -729,26 +736,27 @@ gfxWindowsPlatform::GetCommonFallbackFon
         case 0x14:
         case 0x15:
         case 0x16:
             aFontList.AppendElement(kFontEuphemia);
             aFontList.AppendElement(kFontSegoeUISymbol);
             break;
         case 0x17:
             aFontList.AppendElement(kFontKhmerUI);
+            aFontList.AppendElement(kFontLeelawadeeUI);
             break;
         case 0x18:  // Mongolian
             aFontList.AppendElement(kFontMongolianBaiti);
             aFontList.AppendElement(kFontEuphemia);
             break;
         case 0x19:
             aFontList.AppendElement(kFontMicrosoftTaiLe);
             aFontList.AppendElement(kFontMicrosoftNewTaiLue);
             aFontList.AppendElement(kFontKhmerUI);
-            break;
+            aFontList.AppendElement(kFontLeelawadeeUI);
             break;
         case 0x1a:
             aFontList.AppendElement(kFontLeelawadeeUI);
             break;
         case 0x1c:
             aFontList.AppendElement(kFontNirmalaUI);
             break;
         case 0x20:  // Symbol ranges
@@ -814,16 +822,17 @@ gfxWindowsPlatform::GetCommonFallbackFon
             break;
         case 0xa8:
              aFontList.AppendElement(kFontMicrosoftPhagsPa);
              aFontList.AppendElement(kFontNirmalaUI);
              break;
         case 0xa9:
              aFontList.AppendElement(kFontMalgunGothic);
              aFontList.AppendElement(kFontJavaneseText);
+             aFontList.AppendElement(kFontLeelawadeeUI);
              break;
         case 0xaa:
              aFontList.AppendElement(kFontMyanmarText);
              break;
         case 0xab:
              aFontList.AppendElement(kFontEbrima);
              aFontList.AppendElement(kFontNyala);
              break;
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -980,17 +980,17 @@ function TypedArraySlice(start, end) {
     // Step 1.
     var O = this;
 
     // Step 2.
     if (!IsObject(O) || !IsTypedArray(O)) {
         return callFunction(CallTypedArrayMethodIfWrapped, O, start, end, "TypedArraySlice");
     }
 
-    GetAttachedArrayBuffer(O);
+    var buffer = GetAttachedArrayBuffer(O);
 
     // Step 3.
     var len = TypedArrayLength(O);
 
     // Step 4.
     var relativeStart = ToInteger(start);
 
     // Step 5.
@@ -1007,27 +1007,42 @@ function TypedArraySlice(start, end) {
                 : std_Math_min(relativeEnd, len);
 
     // Step 8.
     var count = std_Math_max(final - k, 0);
 
     // Step 9.
     var A = TypedArraySpeciesCreateWithLength(O, count);
 
-    // Step 14.a.
-    var n = 0;
+    // Steps 10-13 (Not implemented, bug 1140152).
+
+    // Steps 14-15.
+    if (count > 0) {
+        // Step 14.a.
+        var n = 0;
 
-    // Step 14.b.
-    while (k < final) {
-        // Steps 14.b.i-v.
-        A[n++] = O[k++];
+        // Steps 14.b.ii, 15.b.
+        if (buffer === null) {
+            // A typed array previously using inline storage may acquire a
+            // buffer, so we must check with the source.
+            buffer = ViewedArrayBufferIfReified(O);
+        }
+
+        if (IsDetachedBuffer(buffer))
+            ThrowTypeError(JSMSG_TYPED_ARRAY_DETACHED);
+
+        // Step 14.b.
+        while (k < final) {
+            // Steps 14.b.i-v.
+            A[n++] = O[k++];
+        }
+
+        // FIXME: Implement step 15 (bug 1140152).
     }
 
-    // FIXME: Implement step 15 (bug 1140152).
-
     // Step 16.
     return A;
 }
 
 // ES6 draft rev30 (2014/12/24) 22.2.3.25 %TypedArray%.prototype.some(callbackfn[, thisArg]).
 function TypedArraySome(callbackfn/*, thisArg*/) {
     // Steps 1-2.
     var O = this;
--- a/js/src/devtools/automation/autospider.py
+++ b/js/src/devtools/automation/autospider.py
@@ -41,19 +41,23 @@ parser.add_argument('--dep', action='sto
 parser.add_argument('--platform', '-p', type=str, metavar='PLATFORM',
                     default='', help='build platform')
 parser.add_argument('--timeout', '-t', type=int, metavar='TIMEOUT',
                     default=10800,
                     help='kill job after TIMEOUT seconds')
 parser.add_argument('--objdir', type=str, metavar='DIR',
                     default=env.get('OBJDIR', 'obj-spider'),
                     help='object directory')
-parser.add_argument('--optimize', type=bool, metavar='OPT',
-                    default=None,
-                    help='whether to generate an optimized build. Overrides variant setting.')
+group = parser.add_mutually_exclusive_group()
+group.add_argument('--optimize', action='store_true',
+                   help='generate an optimized build. Overrides variant setting.')
+group.add_argument('--no-optimize', action='store_false',
+                   dest='optimize',
+                   help='generate a non-optimized build. Overrides variant setting.')
+group.set_defaults(optimize=None)
 parser.add_argument('--run-tests', '--tests', type=str, metavar='TESTSUITE',
                     default='',
                     help="comma-separated set of test suites to add to the variant's default set")
 parser.add_argument('--skip-tests', '--skip', type=str, metavar='TESTSUITE',
                     default='',
                     help="comma-separated set of test suites to remove from the variant's default set")
 parser.add_argument('--build-only', '--build',
                     dest='skip_tests', action='store_const', const='all',
--- a/js/src/gc/Nursery.cpp
+++ b/js/src/gc/Nursery.cpp
@@ -139,19 +139,16 @@ js::Nursery::init(uint32_t maxNurseryByt
 
     /* If no chunks are specified then the nursery is permanently disabled. */
     if (maxNurseryChunks_ == 0)
         return true;
 
     if (!mallocedBuffers.init())
         return false;
 
-    if (!cellsWithUid_.init())
-        return false;
-
     freeMallocedBuffersTask = js_new<FreeMallocedBuffersTask>(runtime()->defaultFreeOp());
     if (!freeMallocedBuffersTask || !freeMallocedBuffersTask->init())
         return false;
 
     AutoMaybeStartBackgroundAllocation maybeBgAlloc;
     updateNumChunksLocked(1, maybeBgAlloc, lock);
     if (numChunks() == 0)
         return false;
@@ -817,18 +814,18 @@ js::Nursery::waitBackgroundFreeEnd()
     MOZ_ASSERT(freeMallocedBuffersTask);
     freeMallocedBuffersTask->join();
 }
 
 void
 js::Nursery::sweep()
 {
     /* Sweep unique id's in all in-use chunks. */
-    for (CellsWithUniqueIdSet::Enum e(cellsWithUid_); !e.empty(); e.popFront()) {
-        JSObject* obj = static_cast<JSObject*>(e.front());
+    for (Cell* cell : cellsWithUid_) {
+        JSObject* obj = static_cast<JSObject*>(cell);
         if (!IsForwarded(obj))
             obj->zone()->removeUniqueId(obj);
         else
             MOZ_ASSERT(Forwarded(obj)->zone()->hasUniqueId(Forwarded(obj)));
     }
     cellsWithUid_.clear();
 
     runSweepActions();
--- a/js/src/gc/Nursery.h
+++ b/js/src/gc/Nursery.h
@@ -218,19 +218,17 @@ class Nursery
         mallocedBuffers.remove(buffer);
     }
 
     void waitBackgroundFreeEnd();
 
     MOZ_MUST_USE bool addedUniqueIdToCell(gc::Cell* cell) {
         MOZ_ASSERT(IsInsideNursery(cell));
         MOZ_ASSERT(isEnabled());
-        MOZ_ASSERT(cellsWithUid_.initialized());
-        MOZ_ASSERT(!cellsWithUid_.has(cell));
-        return cellsWithUid_.put(cell);
+        return cellsWithUid_.append(cell);
     }
 
     using SweepThunk = void (*)(void *data);
     void queueSweepAction(SweepThunk thunk, void* data);
 
     MOZ_MUST_USE bool queueDictionaryModeObjectToSweep(NativeObject* obj);
 
     size_t sizeOfHeapCommitted() const {
@@ -383,18 +381,18 @@ class Nursery
      * tenured. It is possible, if rare, for an object that acquired a uid to
      * be dead before the next collection, in which case we need to know to
      * remove it when we sweep.
      *
      * Note: we store the pointers as Cell* here, resulting in an ugly cast in
      *       sweep. This is because this structure is used to help implement
      *       stable object hashing and we have to break the cycle somehow.
      */
-    using CellsWithUniqueIdSet = HashSet<gc::Cell*, PointerHasher<gc::Cell*, 3>, SystemAllocPolicy>;
-    CellsWithUniqueIdSet cellsWithUid_;
+    using CellsWithUniqueIdVector = Vector<gc::Cell*, 8, SystemAllocPolicy>;
+    CellsWithUniqueIdVector cellsWithUid_;
 
     struct SweepAction;
     SweepAction* sweepActions_;
 
     using NativeObjectVector = Vector<NativeObject*, 0, SystemAllocPolicy>;
     NativeObjectVector dictionaryModeObjects_;
 
 #ifdef JS_GC_ZEAL
--- a/js/src/jit-test/tests/basic/dumpStringRepresentation.js
+++ b/js/src/jit-test/tests/basic/dumpStringRepresentation.js
@@ -63,8 +63,11 @@ print("\nr2: now mutated into a dependen
 dumpStringRepresentation(r2);
 
 print("\nr1: now a doubly-dependent string, because of r2's mutation:");
 dumpStringRepresentation(r1);
 
 print("\nt, s: Original leaves, representation unchanged:");
 dumpStringRepresentation(t);
 dumpStringRepresentation(s);
+
+for (var str of representativeStringArray())
+    dumpStringRepresentation(str);
--- a/js/src/jit/BaselineCacheIRCompiler.cpp
+++ b/js/src/jit/BaselineCacheIRCompiler.cpp
@@ -992,17 +992,17 @@ BaselineCacheIRCompiler::emitStoreTypedO
     if (!addFailurePath(&failure))
         return false;
 
     // Compute the address being written to.
     LoadTypedThingData(masm, layout, obj, scratch1);
     masm.addPtr(offsetAddr, scratch1);
     Address dest(scratch1, 0);
 
-    BaselineStoreToTypedArray(cx_, masm, type, val, dest, scratch2, failure->label());
+    StoreToTypedArray(cx_, masm, type, val, dest, scratch2, failure->label());
     return true;
 }
 
 bool
 BaselineCacheIRCompiler::emitStoreDenseElement()
 {
     ObjOperandId objId = reader.objOperandId();
     Int32OperandId indexId = reader.int32OperandId();
@@ -1219,17 +1219,17 @@ BaselineCacheIRCompiler::emitStoreTypedE
 
     // Use ICStubReg as second scratch register. TODO: consider doing the RHS
     // type check/conversion as a separate IR instruction so we can simplify
     // this.
     Register scratch2 = ICStubReg;
     masm.push(scratch2);
 
     Label fail;
-    BaselineStoreToTypedArray(cx_, masm, type, val, dest, scratch2, &fail);
+    StoreToTypedArray(cx_, masm, type, val, dest, scratch2, &fail);
     masm.pop(scratch2);
     masm.jump(&done);
 
     masm.bind(&fail);
     masm.pop(scratch2);
     masm.jump(failure->label());
 
     masm.bind(&done);
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -1104,19 +1104,19 @@ EmitICUnboxedPreBarrier(MacroAssembler& 
 template void
 EmitICUnboxedPreBarrier(MacroAssembler& masm, const Address& address, JSValueType type);
 
 template void
 EmitICUnboxedPreBarrier(MacroAssembler& masm, const BaseIndex& address, JSValueType type);
 
 template <typename T>
 void
-BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
-                          const ValueOperand& value, const T& dest, Register scratch,
-                          Label* failure)
+StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
+                  const ValueOperand& value, const T& dest, Register scratch,
+                  Label* failure)
 {
     Label done;
 
     if (type == Scalar::Float32 || type == Scalar::Float64) {
         masm.ensureDouble(value, FloatReg0, failure);
         if (type == Scalar::Float32) {
             masm.convertDoubleToFloat32(FloatReg0, ScratchFloat32Reg);
             masm.storeToTypedFloatArray(type, ScratchFloat32Reg, dest);
@@ -1167,24 +1167,24 @@ BaselineStoreToTypedArray(JSContext* cx,
             masm.jump(failure);
         }
     }
 
     masm.bind(&done);
 }
 
 template void
-BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
-                          const ValueOperand& value, const Address& dest, Register scratch,
-                          Label* failure);
+StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
+                  const ValueOperand& value, const Address& dest, Register scratch,
+                  Label* failure);
 
 template void
-BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
-                          const ValueOperand& value, const BaseIndex& dest, Register scratch,
-                          Label* failure);
+StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
+                  const ValueOperand& value, const BaseIndex& dest, Register scratch,
+                  Label* failure);
 
 //
 // In_Fallback
 //
 
 static bool
 DoInFallback(JSContext* cx, BaselineFrame* frame, ICIn_Fallback* stub_,
              HandleValue key, HandleValue objValue, MutableHandleValue res)
--- a/js/src/jit/BaselineIC.h
+++ b/js/src/jit/BaselineIC.h
@@ -1713,16 +1713,16 @@ struct IonOsrTempData;
 
 template <typename T>
 void EmitICUnboxedPreBarrier(MacroAssembler &masm, const T& address, JSValueType type);
 
 // Write an arbitrary value to a typed array or typed object address at dest.
 // If the value could not be converted to the appropriate format, jump to
 // failure.
 template <typename T>
-void BaselineStoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
-                               const ValueOperand& value, const T& dest, Register scratch,
-                               Label* failure);
+void StoreToTypedArray(JSContext* cx, MacroAssembler& masm, Scalar::Type type,
+                       const ValueOperand& value, const T& dest, Register scratch,
+                       Label* failure);
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_BaselineIC_h */
--- a/js/src/jit/IonBuilder.cpp
+++ b/js/src/jit/IonBuilder.cpp
@@ -6627,22 +6627,24 @@ void
 IonBuilder::maybeMarkEmpty(MDefinition* ins)
 {
     MOZ_ASSERT(ins->type() == MIRType::Value);
 
     // When one of the operands has no type information, mark the output
     // as having no possible types too. This is to avoid degrading
     // subsequent analysis.
     for (size_t i = 0; i < ins->numOperands(); i++) {
-        if (!ins->emptyResultTypeSet())
+        if (!ins->getOperand(i)->emptyResultTypeSet())
             continue;
 
         TemporaryTypeSet* types = alloc().lifoAlloc()->new_<TemporaryTypeSet>();
-        if (types)
+        if (types) {
             ins->setResultTypeSet(types);
+            return;
+        }
     }
 }
 
 // Return whether property lookups can be performed effectlessly on clasp.
 static bool
 ClassHasEffectlessLookup(const Class* clasp)
 {
     return (clasp == &UnboxedPlainObject::class_) ||
--- a/js/src/jit/IonCacheIRCompiler.cpp
+++ b/js/src/jit/IonCacheIRCompiler.cpp
@@ -385,17 +385,17 @@ IonCacheIRCompiler::init()
         if (numInputs > 1)
             allocator.initInputLocation(1, ic->id());
         break;
       }
       case CacheKind::SetProp:
       case CacheKind::SetElem: {
         IonSetPropertyIC* ic = ic_->asSetPropertyIC();
 
-        available.add(ic->temp1());
+        available.add(ic->temp());
 
         liveRegs_.emplace(ic->liveRegs());
 
         allocator.initInputLocation(0, ic->object(), JSVAL_TYPE_OBJECT);
 
         if (ic->kind() == CacheKind::SetProp) {
             MOZ_ASSERT(numInputs == 2);
             allocator.initInputLocation(1, ic->rhs());
@@ -1235,17 +1235,17 @@ IonCacheIRCompiler::emitStoreTypedObject
     FailurePath* failure;
     if (!addFailurePath(&failure))
         return false;
 
     // Compute the address being written to.
     LoadTypedThingData(masm, layout, obj, scratch1);
     Address dest(scratch1, offset);
 
-    BaselineStoreToTypedArray(cx_, masm, type, val, dest, scratch2, failure->label());
+    StoreToTypedArray(cx_, masm, type, val, dest, scratch2, failure->label());
     return true;
 }
 
 bool
 IonCacheIRCompiler::emitStoreDenseElement()
 {
     Register obj = allocator.useRegister(masm, reader.objOperandId());
     Register index = allocator.useRegister(masm, reader.int32OperandId());
--- a/js/src/jit/IonIC.cpp
+++ b/js/src/jit/IonIC.cpp
@@ -38,17 +38,17 @@ IonIC::scratchRegisterForEntryJump()
         Register temp = asGetPropertyIC()->maybeTemp();
         if (temp != InvalidReg)
             return temp;
         TypedOrValueRegister output = asGetPropertyIC()->output();
         return output.hasValue() ? output.valueReg().scratchReg() : output.typedReg().gpr();
       }
       case CacheKind::SetProp:
       case CacheKind::SetElem:
-        return asSetPropertyIC()->temp1();
+        return asSetPropertyIC()->temp();
       case CacheKind::GetName:
       case CacheKind::In:
         MOZ_CRASH("Baseline-specific for now");
     }
 
     MOZ_CRASH("Invalid kind");
 }
 
--- a/js/src/jit/IonIC.h
+++ b/js/src/jit/IonIC.h
@@ -203,49 +203,49 @@ class IonGetPropertyIC : public IonIC
                                     HandleValue val, HandleValue idVal, MutableHandleValue res);
 };
 
 class IonSetPropertyIC : public IonIC
 {
     LiveRegisterSet liveRegs_;
 
     Register object_;
-    Register temp1_;
+    Register temp_;
     FloatRegister maybeTempDouble_;
     FloatRegister maybeTempFloat32_;
     ConstantOrRegister id_;
     ConstantOrRegister rhs_;
     bool strict_ : 1;
     bool needsTypeBarrier_ : 1;
     bool guardHoles_ : 1;
 
   public:
-    IonSetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, Register object, Register temp1,
+    IonSetPropertyIC(CacheKind kind, LiveRegisterSet liveRegs, Register object, Register temp,
                      FloatRegister maybeTempDouble, FloatRegister maybeTempFloat32,
                      const ConstantOrRegister& id, const ConstantOrRegister& rhs, bool strict,
                      bool needsTypeBarrier, bool guardHoles)
       : IonIC(kind),
         liveRegs_(liveRegs),
         object_(object),
-        temp1_(temp1),
+        temp_(temp),
         maybeTempDouble_(maybeTempDouble),
         maybeTempFloat32_(maybeTempFloat32),
         id_(id),
         rhs_(rhs),
         strict_(strict),
         needsTypeBarrier_(needsTypeBarrier),
         guardHoles_(guardHoles)
     { }
 
     LiveRegisterSet liveRegs() const { return liveRegs_; }
     Register object() const { return object_; }
     ConstantOrRegister id() const { return id_; }
     ConstantOrRegister rhs() const { return rhs_; }
 
-    Register temp1() const { return temp1_; }
+    Register temp() const { return temp_; }
     FloatRegister maybeTempDouble() const { return maybeTempDouble_; }
     FloatRegister maybeTempFloat32() const { return maybeTempFloat32_; }
 
     bool strict() const { return strict_; }
     bool needsTypeBarrier() const { return needsTypeBarrier_; }
     bool guardHoles() const { return guardHoles_; }
 
     static MOZ_MUST_USE bool update(JSContext* cx, HandleScript outerScript, IonSetPropertyIC* ic,
--- a/js/src/jsdate.cpp
+++ b/js/src/jsdate.cpp
@@ -581,86 +581,92 @@ RegionMatches(const char* s1, int s1off,
         s1off++;
         s2off++;
         count--;
     }
 
     return count == 0;
 }
 
-/* ES6 20.3.3.4. */
+// ES2017 draft rev (TODO: Add git hash when PR 642 is merged.)
+// 20.3.3.4
+// Date.UTC(year [, month [, date [, hours [, minutes [, seconds [, ms]]]]]])
 static bool
 date_UTC(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
 
-    // Steps 1-2.
+    // Step 1.
     double y;
     if (!ToNumber(cx, args.get(0), &y))
         return false;
 
-    // Steps 3-4.
+    // Step 2.
     double m;
-    if (!ToNumber(cx, args.get(1), &m))
-        return false;
-
-    // Steps 5-6.
+    if (args.length() >= 2) {
+        if (!ToNumber(cx, args[1], &m))
+            return false;
+    } else {
+        m = 0;
+    }
+
+    // Step 3.
     double dt;
     if (args.length() >= 3) {
         if (!ToNumber(cx, args[2], &dt))
             return false;
     } else {
         dt = 1;
     }
 
-    // Steps 7-8.
+    // Step 4.
     double h;
     if (args.length() >= 4) {
         if (!ToNumber(cx, args[3], &h))
             return false;
     } else {
         h = 0;
     }
 
-    // Steps 9-10.
+    // Step 5.
     double min;
     if (args.length() >= 5) {
         if (!ToNumber(cx, args[4], &min))
             return false;
     } else {
         min = 0;
     }
 
-    // Steps 11-12.
+    // Step 6.
     double s;
     if (args.length() >= 6) {
         if (!ToNumber(cx, args[5], &s))
             return false;
     } else {
         s = 0;
     }
 
-    // Steps 13-14.
+    // Step 7.
     double milli;
     if (args.length() >= 7) {
         if (!ToNumber(cx, args[6], &milli))
             return false;
     } else {
         milli = 0;
     }
 
-    // Step 15.
+    // Step 8.
     double yr = y;
     if (!IsNaN(y)) {
         double yint = ToInteger(y);
         if (0 <= yint && yint <= 99)
             yr = 1900 + yint;
     }
 
-    // Step 16.
+    // Step 9.
     ClippedTime time = TimeClip(MakeDate(MakeDay(yr, m, dt), MakeTime(h, min, s, milli)));
     args.rval().set(TimeValue(time));
     return true;
 }
 
 /*
  * Read and convert decimal digits from s[*i] into *result
  * while *i < limit.
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -1618,19 +1618,18 @@ FunctionConstructor(JSContext* cx, const
     else if (generatorKind != NotGenerator)
         introductionType = "GeneratorFunction";
 
     const char* introducerFilename = filename;
     if (maybeScript && maybeScript->scriptSource()->introducerFilename())
         introducerFilename = maybeScript->scriptSource()->introducerFilename();
 
     CompileOptions options(cx);
-    // Use line 0 to make the function body starts from line 1.
     options.setMutedErrors(mutedErrors)
-           .setFileAndLine(filename, 0)
+           .setFileAndLine(filename, 1)
            .setNoScriptRval(false)
            .setIntroductionInfo(introducerFilename, introductionType, lineno, maybeScript, pcOffset);
 
     StringBuffer sb(cx);
 
     if (!sb.append('('))
         return false;
 
--- a/js/src/proxy/CrossCompartmentWrapper.cpp
+++ b/js/src/proxy/CrossCompartmentWrapper.cpp
@@ -261,25 +261,28 @@ static bool
 CanReify(HandleObject obj)
 {
     return obj->is<PropertyIteratorObject>() &&
            (obj->as<PropertyIteratorObject>().getNativeIterator()->flags & JSITER_ENUMERATE);
 }
 
 struct AutoCloseIterator
 {
-    AutoCloseIterator(JSContext* cx, JSObject* obj) : cx(cx), obj(cx, obj) {}
+    AutoCloseIterator(JSContext* cx, PropertyIteratorObject* obj) : cx(cx), obj(cx, obj) {}
 
-    ~AutoCloseIterator() { if (obj) CloseIterator(cx, obj); }
+    ~AutoCloseIterator() {
+        if (obj)
+            MOZ_ALWAYS_TRUE(CloseIterator(cx, obj));
+    }
 
     void clear() { obj = nullptr; }
 
   private:
     JSContext* cx;
-    RootedObject obj;
+    Rooted<PropertyIteratorObject*> obj;
 };
 
 static bool
 Reify(JSContext* cx, JSCompartment* origin, MutableHandleObject objp)
 {
     Rooted<PropertyIteratorObject*> iterObj(cx, &objp->as<PropertyIteratorObject>());
     NativeIterator* ni = iterObj->getNativeIterator();
 
@@ -305,18 +308,17 @@ Reify(JSContext* cx, JSCompartment* orig
             RootedValue v(cx, StringValue(ni->begin()[i]));
             if (!ValueToId<CanGC>(cx, v, &id))
                 return false;
             keys.infallibleAppend(id);
         }
     }
 
     close.clear();
-    if (!CloseIterator(cx, iterObj))
-        return false;
+    MOZ_ALWAYS_TRUE(CloseIterator(cx, iterObj));
 
     return EnumeratedIdVectorToIterator(cx, obj, ni->flags, keys, objp);
 }
 
 bool
 CrossCompartmentWrapper::enumerate(JSContext* cx, HandleObject wrapper,
                                    MutableHandleObject objp) const
 {
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/slice-detached.js
@@ -0,0 +1,103 @@
+// Tests for detached ArrayBuffer checks in %TypedArray%.prototype.slice ( start, end ).
+
+function* createTypedArrays(lengths = [0, 1, 4, 4096]) {
+    // Test with eagerly created ArrayBuffer.
+    for (let length of lengths) {
+        let buffer = new ArrayBuffer(length * Int32Array.BYTES_PER_ELEMENT);
+        let typedArray = new Int32Array(buffer);
+
+        yield {typedArray, length, buffer() { return buffer; }};
+    }
+
+    // Test with lazily created ArrayBuffer.
+    for (let length of lengths) {
+        let typedArray = new Int32Array(length);
+
+        yield {typedArray, length, buffer() { return typedArray.buffer; }};
+    }
+}
+
+if (typeof detachArrayBuffer === "function") {
+    // ArrayBuffer is detached when entering slice().
+    for (let {typedArray, buffer} of createTypedArrays()) {
+        detachArrayBuffer(buffer());
+        assertThrowsInstanceOf(() => {
+            typedArray.slice(0);
+        }, TypeError, "ArrayBuffer is detached on function entry");
+    }
+
+    // ArrayBuffer is detached when computing ToInteger(start).
+    for (let {typedArray, length, buffer} of createTypedArrays()) {
+        let detached = false;
+        let start = {
+            valueOf() {
+                assertEq(detached, false);
+                detachArrayBuffer(buffer());
+                assertEq(detached, false);
+                detached = true;
+                return 0;
+            }
+        };
+
+        // Doesn't throw an error when no bytes are copied.
+        if (length === 0) {
+            typedArray.slice(start);
+        } else {
+            assertThrowsInstanceOf(() => {
+                typedArray.slice(start);
+            }, TypeError, "ArrayBuffer is detached in ToInteger(start)");
+        }
+        assertEq(detached, true, "detachArrayBuffer was called");
+    }
+
+    // ArrayBuffer is detached when computing ToInteger(end).
+    for (let {typedArray, length, buffer} of createTypedArrays()) {
+        let detached = false;
+        let end = {
+            valueOf() {
+                assertEq(detached, false);
+                detachArrayBuffer(buffer());
+                assertEq(detached, false);
+                detached = true;
+                return length;
+            }
+        };
+
+        // Doesn't throw an error when no bytes are copied.
+        if (length === 0) {
+            typedArray.slice(0, end);
+        } else {
+            assertThrowsInstanceOf(() => {
+                typedArray.slice(0, end);
+            }, TypeError, "ArrayBuffer is detached in ToInteger(end)");
+        }
+        assertEq(detached, true, "detachArrayBuffer was called");
+    }
+
+    // ArrayBuffer is detached in species constructor.
+    for (let {typedArray, length, buffer} of createTypedArrays()) {
+        let detached = false;
+        typedArray.constructor = {
+            [Symbol.species]: function(...args) {
+                assertEq(detached, false);
+                detachArrayBuffer(buffer());
+                assertEq(detached, false);
+                detached = true;
+                return new Int32Array(...args);
+            }
+        };
+
+        // Doesn't throw an error when no bytes are copied.
+        if (length === 0) {
+            typedArray.slice(0);
+        } else {
+            assertThrowsInstanceOf(() => {
+                typedArray.slice(0);
+            }, TypeError, "ArrayBuffer is detached in TypedArraySpeciesCreate(...)");
+        }
+        assertEq(detached, true, "detachArrayBuffer was called");
+    }
+}
+
+if (typeof reportCompare === "function")
+    reportCompare(true, true);
--- a/js/src/tests/jstests.list
+++ b/js/src/tests/jstests.list
@@ -163,19 +163,16 @@ skip script test262/built-ins/global/glo
 skip script test262/built-ins/TypedArray/prototype/every/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/filter/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/find/predicate-may-detach-buffer.js
 skip script test262/built-ins/TypedArray/prototype/findIndex/predicate-may-detach-buffer.js
 skip script test262/built-ins/TypedArray/prototype/forEach/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/map/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/reduceRight/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArray/prototype/reduce/callbackfn-detachbuffer.js
-skip script test262/built-ins/TypedArray/prototype/slice/detached-buffer-custom-ctor-same-targettype.js
-skip script test262/built-ins/TypedArray/prototype/slice/detached-buffer-custom-ctor-other-targettype.js
-skip script test262/built-ins/TypedArray/prototype/slice/detached-buffer-get-ctor.js
 skip script test262/built-ins/TypedArray/prototype/some/callbackfn-detachbuffer.js
 skip script test262/built-ins/TypedArrays/internals/DefineOwnProperty/detached-buffer-realm.js
 skip script test262/built-ins/TypedArrays/internals/DefineOwnProperty/detached-buffer.js
 skip script test262/built-ins/TypedArrays/internals/Get/detached-buffer-realm.js
 skip script test262/built-ins/TypedArrays/internals/Get/detached-buffer.js
 skip script test262/built-ins/TypedArrays/internals/GetOwnProperty/detached-buffer-realm.js
 skip script test262/built-ins/TypedArrays/internals/GetOwnProperty/detached-buffer.js
 skip script test262/built-ins/TypedArrays/internals/HasProperty/detached-buffer-realm.js
@@ -536,19 +533,16 @@ skip script test262/annexB/language/eval
 skip script test262/language/expressions/async-generators/expression-yield-as-statement.js
 skip script test262/language/expressions/async-generators/expression-await-thenable-as-yield-operand.js
 skip script test262/language/expressions/async-generators/expression-yield-as-operand.js
 skip script test262/language/expressions/async-generators/expression-await-as-yield-operand.js
 skip script test262/language/expressions/async-generators/expression-yield-newline.js
 skip script test262/language/expressions/async-generators/expression-await-promise-as-yield-operand.js
 skip script test262/language/expressions/async-generators/expression-yield-star-before-newline.js
 
-# https://bugzilla.mozilla.org/show_bug.cgi?id=1050755
-skip script test262/built-ins/Date/UTC/return-value.js
-
 # SIMD.
 skip script test262/built-ins/Simd/check.js
 skip script test262/built-ins/Simd/from.js
 skip script test262/built-ins/Simd/operators.js
 skip script test262/built-ins/Simd/replace_lane.js
 skip script test262/built-ins/Simd/shuffle.js
 skip script test262/built-ins/Simd/swizzle.js
 
--- a/js/src/vm/String.cpp
+++ b/js/src/vm/String.cpp
@@ -1107,18 +1107,17 @@ JSAtom::dump()
 
 void
 JSExternalString::dumpRepresentation(FILE* fp, int indent) const
 {
     dumpRepresentationHeader(fp, indent, "JSExternalString");
     indent += 2;
 
     fprintf(fp, "%*sfinalizer: ((JSStringFinalizer*) %p)\n", indent, "", externalFinalizer());
-    fprintf(fp, "%*sbase: ", indent, "");
-    base()->dumpRepresentation(fp, indent);
+    dumpRepresentationChars(fp, indent);
 }
 #endif /* DEBUG */
 
 JSLinearString*
 js::NewDependentString(JSContext* cx, JSString* baseArg, size_t start, size_t length)
 {
     if (length == 0)
         return cx->emptyString();
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -7021,25 +7021,26 @@ DispatchPointerFromMouseOrTouch(PresShel
     case eTouchEnd:
       pointerMessage = ePointerUp;
       buttons = WidgetMouseEvent::eNoButtonFlag;
       break;
     case eTouchStart:
       pointerMessage = ePointerDown;
       break;
     case eTouchCancel:
+    case eTouchPointerCancel:
       pointerMessage = ePointerCancel;
       break;
     default:
       return NS_OK;
     }
 
     for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
       mozilla::dom::Touch* touch = touchEvent->mTouches[i];
-      if (!touch || !touch->convertToPointer) {
+      if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
         continue;
       }
 
       WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
                                touchEvent->mWidget);
       event.mIsPrimary = i == 0;
       event.pointerId = touch->Identifier();
       event.mRefPoint = touch->mRefPoint;
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -135,17 +135,18 @@ TouchManager::PreHandleEvent(WidgetEvent
       for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
         Touch* touch = touchEvent->mTouches[i];
         int32_t id = touch->Identifier();
         if (!sCaptureTouchList->Get(id, nullptr)) {
           // If it is not already in the queue, it is a new touch
           touch->mChanged = true;
         }
         touch->mMessage = aEvent->mMessage;
-        TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget) };
+        TouchInfo info = { touch, GetNonAnonymousAncestor(touch->mTarget),
+                           true };
         sCaptureTouchList->Put(id, info);
       }
       break;
     }
     case eTouchMove: {
       // Check for touches that changed. Mark them add to queue
       WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
       WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
@@ -242,16 +243,36 @@ TouchManager::PreHandleEvent(WidgetEvent
         aCurrentEventContent = do_QueryInterface(targetPtr);
         touch->SetTarget(targetPtr);
         sCaptureTouchList->Remove(id);
       }
       // add any touches left in the touch list, but ensure changed=false
       AppendToTouchList(&touches);
       break;
     }
+    case eTouchPointerCancel: {
+      // Don't generate pointer events by touch events after eTouchPointerCancel
+      // is received.
+      WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+      WidgetTouchEvent::TouchArray& touches = touchEvent->mTouches;
+      for (uint32_t i = 0; i < touches.Length(); ++i) {
+        Touch* touch = touches[i];
+        if (!touch) {
+          continue;
+        }
+        int32_t id = touch->Identifier();
+        TouchInfo info;
+        if (!sCaptureTouchList->Get(id, &info)) {
+          continue;
+        }
+        info.mConvertToPointer = false;
+        sCaptureTouchList->Put(id, info);
+      }
+      break;
+    }
     default:
       break;
   }
   return true;
 }
 
 /*static*/ already_AddRefed<nsIContent>
 TouchManager::GetAnyCapturedTouchTarget()
@@ -285,9 +306,29 @@ TouchManager::GetCapturedTouch(int32_t a
   RefPtr<Touch> touch;
   TouchInfo info;
   if (sCaptureTouchList->Get(aId, &info)) {
     touch = info.mTouch;
   }
   return touch.forget();
 }
 
+/*static*/ bool
+TouchManager::ShouldConvertTouchToPointer(const Touch* aTouch,
+                                          const WidgetTouchEvent* aEvent)
+{
+  if (!aTouch || !aTouch->convertToPointer) {
+    return false;
+  }
+  TouchInfo info;
+  if (!sCaptureTouchList->Get(aTouch->Identifier(), &info)) {
+    // This check runs before the TouchManager has the touch registered in its
+    // touch list. It's because we dispatching pointer events before handling
+    // touch events. So we convert eTouchStart to pointerdown even it's not
+    // registered.
+    // Check WidgetTouchEvent::mMessage because Touch::mMessage is assigned when
+    // pre-handling touch events.
+    return aEvent->mMessage == eTouchStart;
+  }
+  return info.mConvertToPointer;
+}
+
 } // namespace mozilla
--- a/layout/base/TouchManager.h
+++ b/layout/base/TouchManager.h
@@ -35,29 +35,31 @@ public:
                       nsEventStatus* aStatus,
                       bool& aTouchIsNew,
                       bool& aIsHandlingUserInput,
                       nsCOMPtr<nsIContent>& aCurrentEventContent);
 
   static already_AddRefed<nsIContent> GetAnyCapturedTouchTarget();
   static bool HasCapturedTouch(int32_t aId);
   static already_AddRefed<dom::Touch> GetCapturedTouch(int32_t aId);
-
+  static bool ShouldConvertTouchToPointer(const dom::Touch* aTouch,
+                                          const WidgetTouchEvent* aEvent);
 private:
   void EvictTouches();
   static void EvictTouchPoint(RefPtr<dom::Touch>& aTouch,
                               nsIDocument* aLimitToDocument = nullptr);
   static void AppendToTouchList(WidgetTouchEvent::TouchArray* aTouchList);
 
   RefPtr<PresShell>   mPresShell;
   nsCOMPtr<nsIDocument> mDocument;
 
   struct TouchInfo
   {
     RefPtr<mozilla::dom::Touch> mTouch;
     nsCOMPtr<nsIContent> mNonAnonymousTarget;
+    bool mConvertToPointer;
   };
   static nsDataHashtable<nsUint32HashKey, TouchInfo>* sCaptureTouchList;
 };
 
 } // namespace mozilla
 
 #endif /* !defined(TouchManager_h_) */
--- a/layout/base/tests/bug970964_inner.html
+++ b/layout/base/tests/bug970964_inner.html
@@ -109,54 +109,68 @@ function runTests() {
   // Test Pointer firing before any mouse/touch original source
 
   var mouseDownTriggered = 0;
   var pointerDownTriggered = 0;
   var touchDownTriggered = 0;
   var touchCancelTriggered = 0;
   var pointerCancelTriggered = 0;
 
-  d0.onmousedown = function(e) {
-    mouseDownTriggered = 1;
-    is(pointerDownTriggered , 1, "Mouse event must be triggered after pointer event!");
-  };
-  d0.ontouchstart = function(e) {
-    touchDownTriggered = 1;
-    is(touchDownTriggered , 1, "Touch event must be triggered after pointer event!");
-  }
-  d0.ontouchcancel = function(e) {
-    touchCancelTriggered = 1;
-    is(pointerCancelTriggered, 1, "Touch cancel event must be triggered after pointer event!");
-  }
-  d0.onpointerdown = function(e) {
-    pointerDownTriggered = 1;
-    is(mouseDownTriggered, 0, "Pointer event must be triggered before mouse event!");
-    is(touchDownTriggered, 0, "Pointer event must be triggered before touch event!");
-  };
-  d0.addEventListener("pointercancel", function(ev) {
-    is(ev.pointerId, 0, "Correct default pointerId");
-    is(ev.bubbles, true, "bubbles should be true");
-    is(ev.cancelable, false, "pointercancel cancelable should be false ");
-    pointerCancelTriggered = 1;
-    is(touchCancelTriggered, 0, "Pointer event must be triggered before touch event!");
+  // Test pointer event generated from mouse event
+  d0.addEventListener("mousedown", (e) => {
+    ++mouseDownTriggered;
+    is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
   }, {once: true});
 
-  // Test pointer event generated from mouse event
+  d0.addEventListener("pointerdown", (e) => {
+    ++pointerDownTriggered;
+    is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+  }, {once: true});
+
   synthesizeMouse(d1, 3, 3, { type: "mousemove"});
   synthesizeMouse(d1, 3, 3, { type: "mousedown"});
   synthesizeMouse(d1, 3, 3, { type: "mouseup"});
 
   // Test pointer event generated from touch event
+  mouseDownTriggered = 0;
   pointerDownTriggered = 0;
-  mouseDownTriggered = 0;
+
+  d0.addEventListener("touchstart", (e) => {
+    ++touchDownTriggered;
+    is(pointerDownTriggered, touchDownTriggered,  "Touch event must be triggered after pointer event!");
+  }, {once: true});
+
+  d0.addEventListener("mousedown", (e) => {
+    ++mouseDownTriggered;
+    is(pointerDownTriggered , mouseDownTriggered, "Mouse event must be triggered after pointer event!");
+  }, {once: true});
+
+  d0.addEventListener("pointerdown", (e) => {
+    ++pointerDownTriggered;
+    is(pointerDownTriggered, touchDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+    is(pointerDownTriggered, mouseDownTriggered + 1, "Pointer event must be triggered before mouse event!");
+  }, {once: true});
+
+  d0.addEventListener("touchcancel", (e) => {
+    ++touchCancelTriggered;
+    is(pointerCancelTriggered, touchCancelTriggered, "Touch cancel event must be triggered after pointer event!");
+  }, {once: true});
+
+  d0.addEventListener("pointercancel", function(ev) {
+    is(ev.pointerId, 0, "Correct default pointerId");
+    is(ev.bubbles, true, "bubbles should be true");
+    is(ev.cancelable, false, "pointercancel cancelable should be false ");
+    ++pointerCancelTriggered;
+    is(pointerCancelTriggered, touchCancelTriggered + 1, "Pointer event must be triggered before touch event!");
+  }, {once: true});
 
   var cwu = SpecialPowers.getDOMWindowUtils(window);
   var event1 = getTouchEventForTarget(d1, cwu, 0);
+  sendTouchEvent(cwu, "touchstart", event1, 0);
   sendTouchEvent(cwu, "touchmove", event1, 0);
-  sendTouchEvent(cwu, "touchstart", event1, 0);
   // Test Touch to Pointer Cancel
   sendTouchEvent(cwu, "touchcancel", event1, 0);
 
   // Check Pointer enter/leave from mouse generated event
   var mouseEnterTriggered = 0;
   var pointerEnterTriggered = 0;
   d2.onpointerenter = function(e) {
     pointerEnterTriggered = 1;
@@ -223,16 +237,17 @@ function runTests() {
   d3.onpointerleave = function(e) {
     ++d3leaveCount;
     is(e.bubbles, false, "bubbles should be false");
     is(e.cancelable, false, "cancelable should be false");
     checkPointerType(e.pointerType);
   };
 
   synthesizeMouse(d1, 3, 3, { type: "mousemove"});
+  sendTouchEvent(cwu, "touchstart", getTouchEventForTarget(d3, cwu, 3), 0);
   sendTouchEvent(cwu, "touchmove", getTouchEventForTarget(d3, cwu, 3), 0);
   is(touchPointerEnterLeaveCount, 1, "Wrong touch enterLeave count for!");
   is(mousePointerEnterLeaveCount, 2, "Wrong mouse enterLeave count for!");
 
   is(d1enterCount, 1, "Wrong enter count for! d1");
   is(d2leaveCount, 1, "Wrong leave count for! d2");
   is(d3enterCount, 1, "Wrong enter count for! d3");
 
@@ -286,18 +301,18 @@ function runTests() {
     if (pointerOutTriggeredForCancelEvent == 0) {
       is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");
       is(e.pointerType, "touch", "Wrong Pointer type, should be touch type");
     } else {
       is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event");
       is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
     }
     pointerOutTriggeredForCancelEvent = 1;
- };
- d1.onpointerleave = function(e) {
+  };
+  d1.onpointerleave = function(e) {
     is(pointerOutTriggeredForCancelEvent, 1, "Pointer Out must be dispatched bedore Pointer leave");
     if (pointerLeaveTriggeredForCancelEvent == 0) {
       is(e.pointerId, 3, "Wrong Pointer type, should be id from Touch event");
       is(e.pointerType, "touch", "Wrong Pointer type, should be touch type");
     } else {
       is(e.pointerId, 0, "Wrong Pointer type, should be id from mouse event");
       is(e.pointerType, "mouse", "Wrong Pointer type, should be mouse type");
     }
--- a/layout/forms/nsITextControlFrame.h
+++ b/layout/forms/nsITextControlFrame.h
@@ -26,19 +26,16 @@ public:
   NS_IMETHOD    GetEditor(nsIEditor **aEditor) = 0;
 
   NS_IMETHOD    SetSelectionStart(int32_t aSelectionStart) = 0;
   NS_IMETHOD    SetSelectionEnd(int32_t aSelectionEnd) = 0;
   
   NS_IMETHOD    SetSelectionRange(int32_t aSelectionStart,
                                   int32_t aSelectionEnd,
                                   SelectionDirection aDirection = eNone) = 0;
-  NS_IMETHOD    GetSelectionRange(int32_t* aSelectionStart,
-                                  int32_t* aSelectionEnd,
-                                  SelectionDirection* aDirection = nullptr) = 0;
 
   NS_IMETHOD    GetOwnedSelectionController(nsISelectionController** aSelCon) = 0;
   virtual nsFrameSelection* GetOwnedFrameSelection() = 0;
 
   virtual nsresult GetPhonetic(nsAString& aPhonetic) = 0;
 
   /**
    * Ensure editor is initialized with the proper flags and the default value.
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -798,25 +798,16 @@ nsTextControlFrame::ScrollSelectionIntoV
     return selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
                                            nsISelectionController::SELECTION_FOCUS_REGION,
                                            nsISelectionController::SCROLL_FIRST_ANCESTOR_ONLY);
   }
 
   return NS_ERROR_FAILURE;
 }
 
-mozilla::dom::Element*
-nsTextControlFrame::GetRootNodeAndInitializeEditor()
-{
-  nsCOMPtr<nsIDOMElement> root;
-  GetRootNodeAndInitializeEditor(getter_AddRefs(root));
-  nsCOMPtr<mozilla::dom::Element> rootElem = do_QueryInterface(root);
-  return rootElem;
-}
-
 nsresult
 nsTextControlFrame::GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement)
 {
   NS_ENSURE_ARG_POINTER(aRootElement);
 
   nsCOMPtr<nsIEditor> editor;
   GetEditor(getter_AddRefs(editor));
   if (!editor)
@@ -920,17 +911,19 @@ nsTextControlFrame::SetSelectionRange(in
 NS_IMETHODIMP
 nsTextControlFrame::SetSelectionStart(int32_t aSelectionStart)
 {
   nsresult rv = EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
   int32_t selStart = 0, selEnd = 0;
 
-  rv = GetSelectionRange(&selStart, &selEnd);
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  MOZ_ASSERT(txtCtrl, "Content not a text control element");
+  rv = txtCtrl->GetSelectionRange(&selStart, &selEnd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aSelectionStart > selEnd) {
     // Collapse to the new start point.
     selEnd = aSelectionStart;
   }
 
   selStart = aSelectionStart;
@@ -941,17 +934,19 @@ nsTextControlFrame::SetSelectionStart(in
 NS_IMETHODIMP
 nsTextControlFrame::SetSelectionEnd(int32_t aSelectionEnd)
 {
   nsresult rv = EnsureEditorInitialized();
   NS_ENSURE_SUCCESS(rv, rv);
 
   int32_t selStart = 0, selEnd = 0;
 
-  rv = GetSelectionRange(&selStart, &selEnd);
+  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+  MOZ_ASSERT(txtCtrl, "Content not a text control element");
+  rv = txtCtrl->GetSelectionRange(&selStart, &selEnd);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (aSelectionEnd < selStart) {
     // Collapse to the new end point.
     selStart = aSelectionEnd;
   }
 
   selEnd = aSelectionEnd;
@@ -1013,68 +1008,16 @@ nsTextControlFrame::OffsetToDOMPoint(int
   } else {
     NS_IF_ADDREF(*aResult = rootNode);
     *aPosition = 0;
   }
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsTextControlFrame::GetSelectionRange(int32_t* aSelectionStart,
-                                      int32_t* aSelectionEnd,
-                                      SelectionDirection* aDirection)
-{
-  // make sure we have an editor
-  nsresult rv = EnsureEditorInitialized();
-  NS_ENSURE_SUCCESS(rv, rv);
-
-  if (aSelectionStart) {
-    *aSelectionStart = 0;
-  }
-  if (aSelectionEnd) {
-    *aSelectionEnd = 0;
-  }
-  if (aDirection) {
-    *aDirection = eNone;
-  }
-
-  nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
-  NS_ASSERTION(txtCtrl, "Content not a text control element");
-  nsISelectionController* selCon = txtCtrl->GetSelectionController();
-  NS_ENSURE_TRUE(selCon, NS_ERROR_FAILURE);
-  nsCOMPtr<nsISelection> selection;
-  rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
-  NS_ENSURE_SUCCESS(rv, rv);
-  NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
-
-  dom::Selection* sel = selection->AsSelection();
-  if (aDirection) {
-    nsDirection direction = sel->GetSelectionDirection();
-    if (direction == eDirNext) {
-      *aDirection = eForward;
-    } else if (direction == eDirPrevious) {
-      *aDirection = eBackward;
-    } else {
-      NS_NOTREACHED("Invalid nsDirection enum value");
-    }
-  }
-
-  if (!aSelectionStart || !aSelectionEnd) {
-    return NS_OK;
-  }
-
-  mozilla::dom::Element* root = GetRootNodeAndInitializeEditor();
-  NS_ENSURE_STATE(root);
-  nsContentUtils::GetSelectionInTextControl(sel, root,
-                                            *aSelectionStart, *aSelectionEnd);
-
-  return NS_OK;
-}
-
 /////END INTERFACE IMPLEMENTATIONS
 
 ////NSIFRAME
 nsresult
 nsTextControlFrame::AttributeChanged(int32_t         aNameSpaceID,
                                      nsIAtom*        aAttribute,
                                      int32_t         aModType)
 {
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -142,19 +142,16 @@ public:
 //==== NSITEXTCONTROLFRAME
 
   NS_IMETHOD    GetEditor(nsIEditor **aEditor) override;
   NS_IMETHOD    SetSelectionStart(int32_t aSelectionStart) override;
   NS_IMETHOD    SetSelectionEnd(int32_t aSelectionEnd) override;
   NS_IMETHOD    SetSelectionRange(int32_t aSelectionStart,
                                   int32_t aSelectionEnd,
                                   SelectionDirection aDirection = eNone) override;
-  NS_IMETHOD    GetSelectionRange(int32_t* aSelectionStart,
-                                  int32_t* aSelectionEnd,
-                                  SelectionDirection* aDirection = nullptr) override;
   NS_IMETHOD    GetOwnedSelectionController(nsISelectionController** aSelCon) override;
   virtual nsFrameSelection* GetOwnedFrameSelection() override;
 
   nsresult GetPhonetic(nsAString& aPhonetic) override;
 
   /**
    * Ensure mEditor is initialized with the proper flags and the default value.
    * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created
@@ -316,19 +313,23 @@ private:
   nsresult SetSelectionInternal(nsIDOMNode *aStartNode, int32_t aStartOffset,
                                 nsIDOMNode *aEndNode, int32_t aEndOffset,
                                 SelectionDirection aDirection = eNone);
   nsresult SelectAllOrCollapseToEndOfText(bool aSelect);
   nsresult SetSelectionEndPoints(int32_t aSelStart, int32_t aSelEnd,
                                  SelectionDirection aDirection = eNone);
 
   /**
-   * Return the root DOM element, and implicitly initialize the editor if needed.
+   * Return the root DOM element, and implicitly initialize the editor if
+   * needed.
+   *
+   * XXXbz This function is slow.  Very slow.  Consider using
+   * EnsureEditorInitialized() if you need that, and
+   * nsITextControlElement::GetRootEditorNode on our content if you need that.
    */
-  mozilla::dom::Element* GetRootNodeAndInitializeEditor();
   nsresult GetRootNodeAndInitializeEditor(nsIDOMElement **aRootElement);
 
   void FinishedInitializer() {
     Properties().Delete(TextControlInitializer());
   }
 
 private:
   // Our first baseline, or NS_INTRINSIC_WIDTH_UNKNOWN if we have a pending
--- a/layout/generic/ViewportFrame.h
+++ b/layout/generic/ViewportFrame.h
@@ -33,25 +33,16 @@ public:
     : nsContainerFrame(aContext)
   {}
   virtual ~ViewportFrame() { } // useful for debugging
 
   virtual void Init(nsIContent*       aContent,
                     nsContainerFrame* aParent,
                     nsIFrame*         aPrevInFlow) override;
 
-  virtual mozilla::WritingMode GetWritingMode() const override
-  {
-    nsIFrame* firstChild = mFrames.FirstChild();
-    if (firstChild) {
-      return firstChild->GetWritingMode();
-    }
-    return nsIFrame::GetWritingMode();
-  }
-
 #ifdef DEBUG
   virtual void AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList) override;
   virtual void InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
                             nsFrameList&    aFrameList) override;
   virtual void RemoveFrame(ChildListID     aListID,
                            nsIFrame*       aOldFrame) override;
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -177,55 +177,60 @@ nsCanvasFrame::SetHasFocus(bool aHasFocu
         sf->AddScrollPositionListener(this);
         mAddedScrollPositionListener = true;
       }
     }
   }
   return NS_OK;
 }
 
-#ifdef DEBUG
 void
 nsCanvasFrame::SetInitialChildList(ChildListID     aListID,
                                    nsFrameList&    aChildList)
 {
   NS_ASSERTION(aListID != kPrincipalList ||
                aChildList.IsEmpty() || aChildList.OnlyChild(),
                "Primary child list can have at most one frame in it");
   nsContainerFrame::SetInitialChildList(aListID, aChildList);
+  MaybePropagateRootElementWritingMode();
 }
 
 void
 nsCanvasFrame::AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList)
 {
+#ifdef DEBUG
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
   if (!mFrames.IsEmpty()) {
     for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
       // We only allow native anonymous child frames to be in principal child
       // list in canvas frame.
       MOZ_ASSERT(e.get()->GetContent()->IsInNativeAnonymousSubtree(),
                  "invalid child list");
     }
   }
   nsFrame::VerifyDirtyBitSet(aFrameList);
+#endif
   nsContainerFrame::AppendFrames(aListID, aFrameList);
+  MaybePropagateRootElementWritingMode();
 }
 
 void
 nsCanvasFrame::InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
                             nsFrameList&    aFrameList)
 {
   // Because we only support a single child frame inserting is the same
   // as appending
   MOZ_ASSERT(!aPrevFrame, "unexpected previous sibling frame");
   AppendFrames(aListID, aFrameList);
+  MaybePropagateRootElementWritingMode();
 }
 
+#ifdef DEBUG
 void
 nsCanvasFrame::RemoveFrame(ChildListID     aListID,
                            nsIFrame*       aOldFrame)
 {
   MOZ_ASSERT(aListID == kPrincipalList, "unexpected child list");
   nsContainerFrame::RemoveFrame(aListID, aOldFrame);
 }
 #endif
@@ -797,15 +802,26 @@ nsCanvasFrame::GetContentForEvent(Widget
       rv = kid->GetContentForEvent(aEvent,
                                    aContent);
     }
   }
 
   return rv;
 }
 
+void
+nsCanvasFrame::MaybePropagateRootElementWritingMode()
+{
+  nsIFrame* child = PrincipalChildList().FirstChild();
+  if (child && child->GetContent() &&
+      child->GetContent() == PresContext()->Document()->GetRootElement()) {
+    nsIFrame* childPrimary = child->GetContent()->GetPrimaryFrame();
+    PropagateRootElementWritingMode(childPrimary->GetWritingMode());
+  }
+}
+
 #ifdef DEBUG_FRAME_DUMP
 nsresult
 nsCanvasFrame::GetFrameName(nsAString& aResult) const
 {
   return MakeFrameName(NS_LITERAL_STRING("Canvas"), aResult);
 }
 #endif
--- a/layout/generic/nsCanvasFrame.h
+++ b/layout/generic/nsCanvasFrame.h
@@ -39,29 +39,24 @@ public:
 
   NS_DECL_QUERYFRAME_TARGET(nsCanvasFrame)
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
-  mozilla::WritingMode GetWritingMode() const override
-  {
-    return nsFrame::GetWritingModeDeferringToRootElem();
-  }
-
-#ifdef DEBUG
   virtual void SetInitialChildList(ChildListID     aListID,
                                    nsFrameList&    aChildList) override;
   virtual void AppendFrames(ChildListID     aListID,
                             nsFrameList&    aFrameList) override;
   virtual void InsertFrames(ChildListID     aListID,
                             nsIFrame*       aPrevFrame,
                             nsFrameList&    aFrameList) override;
+#ifdef DEBUG
   virtual void RemoveFrame(ChildListID     aListID,
                            nsIFrame*       aOldFrame) override;
 #endif
 
   virtual nscoord GetMinISize(nsRenderingContext *aRenderingContext) override;
   virtual nscoord GetPrefISize(nsRenderingContext *aRenderingContext) override;
   virtual void Reflow(nsPresContext*           aPresContext,
                       ReflowOutput&     aDesiredSize,
@@ -120,16 +115,20 @@ public:
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
   virtual nsresult GetContentForEvent(mozilla::WidgetEvent* aEvent,
                                       nsIContent** aContent) override;
 
   nsRect CanvasArea() const;
 
 protected:
+  // Utility function to propagate the WritingMode from our first child to
+  // 'this' and all its ancestors.
+  void MaybePropagateRootElementWritingMode();
+
   // Data members
   bool                      mDoPaintFocus;
   bool                      mAddedScrollPositionListener;
 
   nsCOMPtr<mozilla::dom::Element> mCustomContentContainer;
 };
 
 /**
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -429,16 +429,17 @@ NS_NewEmptyFrame(nsIPresShell* aPresShel
 }
 
 nsFrame::nsFrame(nsStyleContext* aContext)
 {
   MOZ_COUNT_CTOR(nsFrame);
 
   mState = NS_FRAME_FIRST_REFLOW | NS_FRAME_IS_DIRTY;
   mStyleContext = aContext;
+  mWritingMode = WritingMode(mStyleContext);
   mStyleContext->AddRef();
 #ifdef DEBUG
   mStyleContext->FrameAddRef();
 #endif
 }
 
 nsFrame::~nsFrame()
 {
@@ -554,16 +555,18 @@ nsFrame::Init(nsIContent*       aContent
   mContent = aContent;
   mParent = aParent;
 
   if (aContent) {
     NS_ADDREF(aContent);
   }
 
   if (aPrevInFlow) {
+    mWritingMode = aPrevInFlow->GetWritingMode();
+
     // Make sure the general flags bits are the same
     nsFrameState state = aPrevInFlow->GetStateBits();
 
     // Make bits that are currently off (see constructor) the same:
     mState |= state & (NS_FRAME_INDEPENDENT_SELECTION |
                        NS_FRAME_PART_OF_IBSPLIT |
                        NS_FRAME_MAY_BE_TRANSFORMED |
                        NS_FRAME_MAY_HAVE_GENERATED_CONTENT |
@@ -3078,29 +3081,16 @@ nsFrame::FireDOMEvent(const nsAString& a
   if (target) {
     RefPtr<AsyncEventDispatcher> asyncDispatcher =
       new AsyncEventDispatcher(target, aDOMEventName, true, false);
     DebugOnly<nsresult> rv = asyncDispatcher->PostDOMEvent();
     NS_ASSERTION(NS_SUCCEEDED(rv), "AsyncEventDispatcher failed to dispatch");
   }
 }
 
-WritingMode
-nsFrame::GetWritingModeDeferringToRootElem() const
-{
-  Element* rootElem = PresContext()->Document()->GetRootElement();
-  if (rootElem) {
-    nsIFrame* primaryFrame = rootElem->GetPrimaryFrame();
-    if (primaryFrame) {
-      return primaryFrame->GetWritingMode();
-    }
-  }
-  return nsIFrame::GetWritingMode();
-}
-
 nsresult
 nsFrame::HandleEvent(nsPresContext* aPresContext, 
                      WidgetGUIEvent* aEvent,
                      nsEventStatus* aEventStatus)
 {
 
   if (aEvent->mMessage == eMouseMove) {
     // XXX If the second argument of HandleDrag() is WidgetMouseEvent,
--- a/layout/generic/nsFrame.h
+++ b/layout/generic/nsFrame.h
@@ -683,18 +683,16 @@ protected:
   void GetBoxName(nsAutoString& aName) override;
 #endif
 
   nsBoxLayoutMetrics* BoxMetrics() const;
 
   // Fire DOM event. If no aContent argument use frame's mContent.
   void FireDOMEvent(const nsAString& aDOMEventName, nsIContent *aContent = nullptr);
 
-  mozilla::WritingMode GetWritingModeDeferringToRootElem() const;
-
 private:
   void BoxReflow(nsBoxLayoutState& aState,
                  nsPresContext*    aPresContext,
                  ReflowOutput&     aDesiredSize,
                  nsRenderingContext* aRenderingContext,
                  nscoord aX,
                  nscoord aY,
                  nscoord aWidth,
--- a/layout/generic/nsGfxScrollFrame.cpp
+++ b/layout/generic/nsGfxScrollFrame.cpp
@@ -152,48 +152,48 @@ nsHTMLScrollFrame::DestroyFrom(nsIFrame*
   nsContainerFrame::DestroyFrom(aDestructRoot);
 }
 
 void
 nsHTMLScrollFrame::SetInitialChildList(ChildListID  aListID,
                                        nsFrameList& aChildList)
 {
   nsContainerFrame::SetInitialChildList(aListID, aChildList);
-  mHelper.ReloadChildFrames();
+  ReloadChildFrames();
 }
 
 
 void
 nsHTMLScrollFrame::AppendFrames(ChildListID  aListID,
                                 nsFrameList& aFrameList)
 {
   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   mFrames.AppendFrames(nullptr, aFrameList);
-  mHelper.ReloadChildFrames();
+  ReloadChildFrames();
 }
 
 void
 nsHTMLScrollFrame::InsertFrames(ChildListID aListID,
                                 nsIFrame* aPrevFrame,
                                 nsFrameList& aFrameList)
 {
   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == this,
                "inserting after sibling frame with different parent");
   mFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
-  mHelper.ReloadChildFrames();
+  ReloadChildFrames();
 }
 
 void
 nsHTMLScrollFrame::RemoveFrame(ChildListID aListID,
                                nsIFrame* aOldFrame)
 {
   NS_ASSERTION(aListID == kPrincipalList, "Only main list supported");
   mFrames.DestroyFrame(aOldFrame);
-  mHelper.ReloadChildFrames();
+  ReloadChildFrames();
 }
 
 nsSplittableType
 nsHTMLScrollFrame::GetSplittableType() const
 {
   return NS_FRAME_NOT_SPLITTABLE;
 }
 
--- a/layout/generic/nsGfxScrollFrame.h
+++ b/layout/generic/nsGfxScrollFrame.h
@@ -671,24 +671,16 @@ public:
   typedef mozilla::ScrollReflowInput ScrollReflowInput;
   friend nsHTMLScrollFrame* NS_NewHTMLScrollFrame(nsIPresShell* aPresShell,
                                                   nsStyleContext* aContext,
                                                   bool aIsRoot);
 
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
-  virtual mozilla::WritingMode GetWritingMode() const override
-  {
-    if (mHelper.mScrolledFrame) {
-      return mHelper.mScrolledFrame->GetWritingMode();
-    }
-    return nsIFrame::GetWritingMode();
-  }
-
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override {
     mHelper.BuildDisplayList(aBuilder, aDirtyRect, aLists);
   }
 
   bool TryLayout(ScrollReflowInput* aState,
                    ReflowOutput* aKidMetrics,
@@ -1071,16 +1063,24 @@ protected:
   
   /**
    * Override this to return false if computed bsize/min-bsize/max-bsize
    * should NOT be propagated to child content.
    * nsListControlFrame uses this.
    */
   virtual bool ShouldPropagateComputedBSizeToScrolledContent() const { return true; }
 
+  void ReloadChildFrames()
+  {
+    mHelper.ReloadChildFrames();
+    if (mHelper.mScrolledFrame) {
+      mWritingMode = mHelper.mScrolledFrame->GetWritingMode();
+    }
+  }
+
 private:
   friend class mozilla::ScrollFrameHelper;
   ScrollFrameHelper mHelper;
 };
 
 /**
  * The scroll frame creates and manages the scrolling view
  *
--- a/layout/generic/nsIFrame.h
+++ b/layout/generic/nsIFrame.h
@@ -800,20 +800,25 @@ public:
    * If the frame may have moved into or out of a scrollframe's
    * frame subtree, StickyScrollContainer::NotifyReparentedFrameAcrossScrollFrameBoundary
    * must also be called.
    */
   void SetParent(nsContainerFrame* aParent);
 
   /**
    * The frame's writing-mode, used for logical layout computations.
+   * It's usually the 'writing-mode' computed value, but there are exceptions:
+   *   * inner table frames copy the value from the table frame
+   *     (@see nsTableRowGroupFrame::Init, nsTableRowFrame::Init etc)
+   *   * the root element frame propagates its value to its ancestors
+   *     (@see nsCanvasFrame::MaybePropagateRootElementWritingMode)
+   *   * a scrolled frame propagates its value to its ancestor scroll frame
+   *     (@see nsHTMLScrollFrame::ReloadChildFrames)
    */
-  virtual mozilla::WritingMode GetWritingMode() const {
-    return mozilla::WritingMode(StyleContext());
-  }
+  mozilla::WritingMode GetWritingMode() const { return mWritingMode; }
 
   /**
    * Construct a writing mode for line layout in this frame.  This is
    * the writing mode of this frame, except that if this frame is styled with
    * unicode-bidi:plaintext, we reset the direction to the resolved paragraph
    * level of the given subframe (typically the first frame on the line),
    * because the container frame could be split by hard line breaks into
    * multiple paragraphs with different base direction.
@@ -3602,16 +3607,21 @@ private:
       list = new nsTArray<nsWeakPtr>();
       Properties().Set(PaintedPresShellsProperty(), list);
     }
 
     return list;
   }
 
 protected:
+  /**
+   * Copies aRootElemWM to mWritingMode on 'this' and all its ancestors.
+   */
+  inline void PropagateRootElementWritingMode(mozilla::WritingMode aRootElemWM);
+
   void MarkInReflow() {
 #ifdef DEBUG_dbaron_off
     // bug 81268
     NS_ASSERTION(!(mState & NS_FRAME_IN_REFLOW), "frame is already in reflow");
 #endif
     mState |= NS_FRAME_IN_REFLOW;
   }
 
@@ -3641,16 +3651,19 @@ protected:
       return !(*this == aOther);
     }
   };
   union {
     uint32_t     mType;
     VisualDeltas mVisualDeltas;
   } mOverflow;
 
+  /** @see GetWritingMode() */
+  mozilla::WritingMode mWritingMode;
+
   // Helpers
   /**
    * Can we stop inside this frame when we're skipping non-rendered whitespace?
    * @param  aForward [in] Are we moving forward (or backward) in content order.
    * @param  aOffset [in/out] At what offset into the frame to start looking.
    *         on output - what offset was reached (whether or not we found a place to stop).
    * @return STOP: An appropriate offset was found within this frame,
    *         and is given by aOffset.
--- a/layout/generic/nsIFrameInlines.h
+++ b/layout/generic/nsIFrameInlines.h
@@ -155,9 +155,18 @@ nsIFrame::BaselineBOffset(mozilla::Writi
   }
   if (aAlignmentContext == AlignmentContext::eInline) {
     return SynthesizeBaselineBOffsetFromMarginBox(aWM, aBaselineGroup);
   }
   // XXX AlignmentContext::eTable should use content box?
   return SynthesizeBaselineBOffsetFromBorderBox(aWM, aBaselineGroup);
 }
 
+void
+nsIFrame::PropagateRootElementWritingMode(mozilla::WritingMode aRootElemWM)
+{
+  MOZ_ASSERT(GetType() == nsGkAtoms::canvasFrame);
+  for (auto f = this; f; f = f->GetParent()) {
+    f->mWritingMode = aRootElemWM;
+  }
+}
+
 #endif
--- a/layout/generic/nsPageContentFrame.h
+++ b/layout/generic/nsPageContentFrame.h
@@ -39,21 +39,16 @@ public:
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::pageContentFrame
    */
   virtual nsIAtom* GetType() const override;
 
-  mozilla::WritingMode GetWritingMode() const override
-  {
-    return nsFrame::GetWritingModeDeferringToRootElem();
-  }
-
 #ifdef DEBUG_FRAME_DUMP
   // Debugging
   virtual nsresult  GetFrameName(nsAString& aResult) const override;
 #endif
 
 protected:
   explicit nsPageContentFrame(nsStyleContext* aContext) : ViewportFrame(aContext) {}
 
--- a/layout/generic/nsPageFrame.h
+++ b/layout/generic/nsPageFrame.h
@@ -27,21 +27,16 @@ public:
                       ReflowOutput& aDesiredSize,
                       const ReflowInput& aMaxSize,
                       nsReflowStatus&      aStatus) override;
 
   virtual void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists) override;
 
-  mozilla::WritingMode GetWritingMode() const override
-  {
-    return nsFrame::GetWritingModeDeferringToRootElem();
-  }
-
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::pageFrame
    */
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
--- a/layout/generic/nsSimplePageSequenceFrame.h
+++ b/layout/generic/nsSimplePageSequenceFrame.h
@@ -55,21 +55,16 @@ class nsSimplePageSequenceFrame : public
                                   public nsIPageSequenceFrame {
 public:
   friend nsSimplePageSequenceFrame* NS_NewSimplePageSequenceFrame(nsIPresShell* aPresShell,
                                                                   nsStyleContext* aContext);
 
   NS_DECL_QUERYFRAME
   NS_DECL_FRAMEARENA_HELPERS
 
-  mozilla::WritingMode GetWritingMode() const override
-  {
-    return nsFrame::GetWritingModeDeferringToRootElem();
-  }
-
   // nsIFrame
   void Reflow(nsPresContext* aPresContext,
               ReflowOutput& aDesiredSize,
               const ReflowInput& aMaxSize,
               nsReflowStatus& aStatus) override;
 
   void BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                         const nsRect&           aDirtyRect,
--- a/layout/style/nsStyleStruct.cpp
+++ b/layout/style/nsStyleStruct.cpp
@@ -3605,16 +3605,18 @@ nsChangeHint
 nsStyleVisibility::CalcDifference(const nsStyleVisibility& aNewData) const
 {
   nsChangeHint hint = nsChangeHint(0);
 
   if (mDirection != aNewData.mDirection || mWritingMode != aNewData.mWritingMode) {
     // It's important that a change in mWritingMode results in frame
     // reconstruction, because it may affect intrinsic size (see
     // nsSubDocumentFrame::GetIntrinsicISize/BSize).
+    // Also, the used writing-mode value is now a field on nsIFrame and some
+    // classes (e.g. table rows/cells) copy their value from an ancestor.
     hint |= nsChangeHint_ReconstructFrame;
   } else {
     if ((mImageOrientation != aNewData.mImageOrientation)) {
       hint |= nsChangeHint_AllReflowHints |
               nsChangeHint_RepaintFrame;
     }
     if (mVisible != aNewData.mVisible) {
       if ((NS_STYLE_VISIBILITY_COLLAPSE == mVisible) ||
--- a/layout/tables/nsTableCellFrame.cpp
+++ b/layout/tables/nsTableCellFrame.cpp
@@ -87,16 +87,23 @@ nsTableCellFrame::Init(nsIContent*      
   }
 
   if (aPrevInFlow) {
     // Set the column index
     nsTableCellFrame* cellFrame = (nsTableCellFrame*)aPrevInFlow;
     int32_t           colIndex;
     cellFrame->GetColIndex(colIndex);
     SetColIndex(colIndex);
+  } else {
+    // Although the spec doesn't say that writing-mode is not applied to
+    // table-cells, we still override style value here because we want to
+    // make effective writing mode of table structure frames consistent
+    // within a table. The content inside table cells is reflowed by an
+    // anonymous block, hence their writing mode is not affected.
+    mWritingMode = GetTableFrame()->GetWritingMode();
   }
 }
 
 void
 nsTableCellFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   if (HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
     nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
--- a/layout/tables/nsTableCellFrame.h
+++ b/layout/tables/nsTableCellFrame.h
@@ -131,24 +131,16 @@ public:
    * @see nsLayoutAtoms::tableCellFrame
    */
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
-  // Although the spec doesn't say that writing-mode is not applied to
-  // table-cells, we still override this method here because we want to
-  // make effective writing mode of table structure frames consistent
-  // within a table. The content inside table cells is reflowed by an
-  // anonymous block, hence their writing mode is not affected.
-  virtual mozilla::WritingMode GetWritingMode() const override
-    { return GetTableFrame()->GetWritingMode(); }
-
   void BlockDirAlignChild(mozilla::WritingMode aWM, nscoord aMaxAscent);
 
   /*
    * Get the value of vertical-align adjusted for CSS 2's rules for a
    * table cell, which means the result is always
    * NS_STYLE_VERTICAL_ALIGN_{TOP,MIDDLE,BOTTOM,BASELINE}.
    */
   virtual uint8_t GetVerticalAlign() const;
--- a/layout/tables/nsTableColFrame.h
+++ b/layout/tables/nsTableColFrame.h
@@ -28,37 +28,30 @@ public:
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableColFrame* NS_NewTableColFrame(nsIPresShell* aPresShell,
                                               nsStyleContext*  aContext);
 
-  nsTableColGroupFrame* GetTableColGroupFrame() const
+  // nsIFrame overrides
+  virtual void Init(nsIContent*       aContent,
+                    nsContainerFrame* aParent,
+                    nsIFrame*         aPrevInFlow) override
   {
-    nsIFrame* parent = GetParent();
-    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableColGroupFrame);
-    return static_cast<nsTableColGroupFrame*>(parent);
-  }
-
-  nsTableFrame* GetTableFrame() const
-  {
-    return GetTableColGroupFrame()->GetTableFrame();
+    nsSplittableFrame::Init(aContent, aParent, aPrevInFlow);
+    if (!aPrevInFlow) {
+      mWritingMode = GetTableFrame()->GetWritingMode();
+    }
   }
 
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
-  int32_t GetColIndex() const;
-
-  void SetColIndex (int32_t aColIndex);
-
-  nsTableColFrame* GetNextCol() const;
-
   virtual void Reflow(nsPresContext*           aPresContext,
                       ReflowOutput&     aDesiredSize,
                       const ReflowInput& aReflowInput,
                       nsReflowStatus&          aStatus) override;
 
   /**
    * Table columns never paint anything, nor receive events.
    */
@@ -74,18 +67,33 @@ public:
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
   virtual nsSplittableType GetSplittableType() const override;
 
-  virtual mozilla::WritingMode GetWritingMode() const override
-    { return GetTableFrame()->GetWritingMode(); }
+  nsTableColGroupFrame* GetTableColGroupFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableColGroupFrame);
+    return static_cast<nsTableColGroupFrame*>(parent);
+  }
+
+  nsTableFrame* GetTableFrame() const
+  {
+    return GetTableColGroupFrame()->GetTableFrame();
+  }
+
+  int32_t GetColIndex() const;
+
+  void SetColIndex (int32_t aColIndex);
+
+  nsTableColFrame* GetNextCol() const;
 
   /** return the number of the columns the col represents.  always >= 1 */
   int32_t GetSpan();
 
   /** convenience method, calls into cellmap */
   int32_t Count() const;
 
   nscoord GetIStartBorderWidth() const { return mIStartBorderWidth; }
--- a/layout/tables/nsTableColGroupFrame.h
+++ b/layout/tables/nsTableColGroupFrame.h
@@ -11,34 +11,41 @@
 #include "nsTableFrame.h"
 #include "mozilla/WritingModes.h"
 
 class nsTableColFrame;
 
 /**
  * nsTableColGroupFrame
  * data structure to maintain information about a single table cell's frame
- *
- * @author  sclark
  */
 class nsTableColGroupFrame final : public nsContainerFrame
 {
 public:
   NS_DECL_FRAMEARENA_HELPERS
 
-  // default constructor supplied by the compiler
-
   /** instantiate a new instance of nsTableRowFrame.
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableColGroupFrame* NS_NewTableColGroupFrame(nsIPresShell* aPresShell,
                                                         nsStyleContext* aContext);
 
+  // nsIFrame overrides
+  virtual void Init(nsIContent*       aContent,
+                    nsContainerFrame* aParent,
+                    nsIFrame*         aPrevInFlow) override
+  {
+    nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+    if (!aPrevInFlow) {
+      mWritingMode = GetTableFrame()->GetWritingMode();
+    }
+  }
+
   nsTableFrame* GetTableFrame() const
   {
     nsIFrame* parent = GetParent();
     MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableFrame);
     MOZ_ASSERT(!parent->GetPrevInFlow(),
                "Col group should always be in a first-in-flow table frame");
     return static_cast<nsTableFrame*>(parent);
   }
@@ -111,19 +118,16 @@ public:
 
   /**
    * Get the "type" of the frame
    *
    * @see nsGkAtoms::tableColGroupFrame
    */
   virtual nsIAtom* GetType() const override;
 
-  virtual mozilla::WritingMode GetWritingMode() const override
-    { return GetTableFrame()->GetWritingMode(); }
-
   /** Add column frames to the table storages: colframe cache and cellmap
     * this doesn't change the mFrames of the colgroup frame.
     * @param aFirstColIndex - the index at which aFirstFrame should be inserted
     *                         into the colframe cache.
     * @param aResetSubsequentColIndices - the indices of the col frames
     *                                     after the insertion might need
     *                                     an update
     * @param aCols - an iterator that can be used to iterate over the col
--- a/layout/tables/nsTableRowFrame.cpp
+++ b/layout/tables/nsTableRowFrame.cpp
@@ -164,16 +164,18 @@ nsTableRowFrame::Init(nsIContent*       
   NS_ASSERTION(mozilla::StyleDisplay::TableRow == StyleDisplay()->mDisplay,
                "wrong display on table row frame");
 
   if (aPrevInFlow) {
     // Set the row index
     nsTableRowFrame* rowFrame = (nsTableRowFrame*)aPrevInFlow;
 
     SetRowIndex(rowFrame->GetRowIndex());
+  } else {
+    mWritingMode = GetTableFrame()->GetWritingMode();
   }
 }
 
 void
 nsTableRowFrame::DestroyFrom(nsIFrame* aDestructRoot)
 {
   if (HasAnyStateBits(NS_FRAME_CAN_HAVE_ABSPOS_CHILDREN)) {
     nsTableFrame::UnregisterPositionedTablePart(this, aDestructRoot);
--- a/layout/tables/nsTableRowFrame.h
+++ b/layout/tables/nsTableRowFrame.h
@@ -111,19 +111,16 @@ public:
    * @see nsGkAtoms::tableRowFrame
    */
   virtual nsIAtom* GetType() const override;
 
 #ifdef DEBUG_FRAME_DUMP
   virtual nsresult GetFrameName(nsAString& aResult) const override;
 #endif
 
-  virtual mozilla::WritingMode GetWritingMode() const override
-    { return GetTableFrame()->GetWritingMode(); }
- 
   void UpdateBSize(nscoord           aBSize,
                    nscoord           aAscent,
                    nscoord           aDescent,
                    nsTableFrame*     aTableFrame = nullptr,
                    nsTableCellFrame* aCellFrame  = nullptr);
 
   void ResetBSize(nscoord aRowStyleBSize);
 
--- a/layout/tables/nsTableRowGroupFrame.h
+++ b/layout/tables/nsTableRowGroupFrame.h
@@ -46,21 +46,25 @@ public:
     * @param aPresShell the pres shell for this frame
     *
     * @return           the frame that was created
     */
   friend nsTableRowGroupFrame* NS_NewTableRowGroupFrame(nsIPresShell* aPresShell,
                                                         nsStyleContext* aContext);
   virtual ~nsTableRowGroupFrame();
 
-  nsTableFrame* GetTableFrame() const
+  // nsIFrame overrides
+  virtual void Init(nsIContent*       aContent,
+                    nsContainerFrame* aParent,
+                    nsIFrame*         aPrevInFlow) override
   {
-    nsIFrame* parent = GetParent();
-    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableFrame);
-    return static_cast<nsTableFrame*>(parent);
+    nsContainerFrame::Init(aContent, aParent, aPrevInFlow);
+    if (!aPrevInFlow) {
+      mWritingMode = GetTableFrame()->GetWritingMode();
+    }
   }
 
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
 
   /** @see nsIFrame::DidSetStyleContext */
   virtual void DidSetStyleContext(nsStyleContext* aOldStyleContext) override;
 
   virtual void AppendFrames(ChildListID     aListID,
@@ -97,25 +101,29 @@ 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(); }
+  nsTableRowFrame* GetFirstRow();
+  nsTableRowFrame* GetLastRow();
+
+  nsTableFrame* GetTableFrame() const
+  {
+    nsIFrame* parent = GetParent();
+    MOZ_ASSERT(parent && parent->GetType() == nsGkAtoms::tableFrame);
+    return static_cast<nsTableFrame*>(parent);
+  }
 
   /** return the number of child rows (not necessarily == number of child frames) */
   int32_t GetRowCount();
 
   /** return the table-relative row index of the first row in this rowgroup.
     * if there are no rows, -1 is returned.
     */
   int32_t GetStartRowIndex();
new file mode 100644
--- /dev/null
+++ b/media/libyuv/cpu_id.patch
@@ -0,0 +1,34 @@
+diff --git a/media/libyuv/libyuv/source/cpu_id.cc b/media/libyuv/libyuv/source/cpu_id.cc
+--- a/media/libyuv/libyuv/source/cpu_id.cc
++++ b/media/libyuv/libyuv/source/cpu_id.cc
+@@ -172,28 +172,30 @@ LIBYUV_API SAFEBUFFERS int MipsCpuCaps(c
+   if (!f) {
+     // ase enabled if /proc/cpuinfo is unavailable.
+     if (strcmp(ase, " msa") == 0) {
+       return kCpuHasMSA;
+     }
+     if (strcmp(ase, " dspr2") == 0) {
+       return kCpuHasDSPR2;
+     }
++    return 0;
+   }
+   while (fgets(cpuinfo_line, sizeof(cpuinfo_line) - 1, f)) {
+     if (memcmp(cpuinfo_line, "ASEs implemented", 16) == 0) {
+       char* p = strstr(cpuinfo_line, ase);
+       if (p && (p[len] == ' ' || p[len] == '\n')) {
+         fclose(f);
+         if (strcmp(ase, " msa") == 0) {
+           return kCpuHasMSA;
+         }
+         if (strcmp(ase, " dspr2") == 0) {
+           return kCpuHasDSPR2;
+         }
++        return 0;
+       }
+     }
+   }
+   fclose(f);
+   return 0;
+ }
+ 
+ // CPU detect function for SIMD instruction sets.
--- a/media/libyuv/libyuv/source/cpu_id.cc
+++ b/media/libyuv/libyuv/source/cpu_id.cc
@@ -172,28 +172,30 @@ LIBYUV_API SAFEBUFFERS int MipsCpuCaps(c
   if (!f) {
     // ase enabled if /proc/cpuinfo is unavailable.
     if (strcmp(ase, " msa") == 0) {
       return kCpuHasMSA;
     }
     if (strcmp(ase, " dspr2") == 0) {
       return kCpuHasDSPR2;
     }
+    return 0;
   }
   while (fgets(cpuinfo_line, sizeof(cpuinfo_line) - 1, f)) {
     if (memcmp(cpuinfo_line, "ASEs implemented", 16) == 0) {
       char* p = strstr(cpuinfo_line, ase);
       if (p && (p[len] == ' ' || p[len] == '\n')) {
         fclose(f);
         if (strcmp(ase, " msa") == 0) {
           return kCpuHasMSA;
         }
         if (strcmp(ase, " dspr2") == 0) {
           return kCpuHasDSPR2;
         }
+        return 0;
       }
     }
   }
   fclose(f);
   return 0;
 }
 
 // CPU detect function for SIMD instruction sets.
--- a/media/libyuv/libyuv/source/row_any.cc
+++ b/media/libyuv/libyuv/source/row_any.cc
@@ -702,18 +702,18 @@ ANY11P(ARGBShuffleRow_Any_NEON, ARGBShuf
 ANY11P(ARGBShuffleRow_Any_MSA, ARGBShuffleRow_MSA, const uint8*, 4, 4, 7)
 #endif
 #undef ANY11P
 
 // Any 1 to 1 with parameter and shorts.  BPP measures in shorts.
 #define ANY11P16(NAMEANY, ANY_SIMD, T, SBPP, BPP, MASK)            \
   void NAMEANY(const uint16* src_ptr, uint16* dst_ptr, T shuffler, \
                int width) {                                        \
-    SIMD_ALIGNED(uint16 temp[32 * 2]);                             \
-    memset(temp, 0, 64); /* for msan */                            \
+    SIMD_ALIGNED(uint16 temp[64 * 2]);                             \
+    memset(temp, 0, 64*sizeof(uint16)); /* for msan */             \
     int r = width & MASK;                                          \
     int n = width & ~MASK;                                         \
     if (n > 0) {                                                   \
       ANY_SIMD(src_ptr, dst_ptr, shuffler, n);                     \
     }                                                              \
     memcpy(temp, src_ptr + n * SBPP, r * SBPP);                    \
     ANY_SIMD(temp, temp + 64, shuffler, MASK + 1);                 \
     memcpy(dst_ptr + n * BPP, temp + 64, r * BPP);                 \
new file mode 100644
--- /dev/null
+++ b/media/libyuv/row_any.patch
@@ -0,0 +1,24 @@
+diff --git a/media/libyuv/libyuv/source/row_any.cc b/media/libyuv/libyuv/source/row_any.cc
+--- a/media/libyuv/libyuv/source/row_any.cc
++++ b/media/libyuv/libyuv/source/row_any.cc
+@@ -702,18 +702,18 @@ ANY11P(ARGBShuffleRow_Any_NEON, ARGBShuf
+ ANY11P(ARGBShuffleRow_Any_MSA, ARGBShuffleRow_MSA, const uint8*, 4, 4, 7)
+ #endif
+ #undef ANY11P
+ 
+ // Any 1 to 1 with parameter and shorts.  BPP measures in shorts.
+ #define ANY11P16(NAMEANY, ANY_SIMD, T, SBPP, BPP, MASK)            \
+   void NAMEANY(const uint16* src_ptr, uint16* dst_ptr, T shuffler, \
+                int width) {                                        \
+-    SIMD_ALIGNED(uint16 temp[32 * 2]);                             \
+-    memset(temp, 0, 64); /* for msan */                            \
++    SIMD_ALIGNED(uint16 temp[64 * 2]);                             \
++    memset(temp, 0, 64*sizeof(uint16)); /* for msan */             \
+     int r = width & MASK;                                          \
+     int n = width & ~MASK;                                         \
+     if (n > 0) {                                                   \
+       ANY_SIMD(src_ptr, dst_ptr, shuffler, n);                     \
+     }                                                              \
+     memcpy(temp, src_ptr + n * SBPP, r * SBPP);                    \
+     ANY_SIMD(temp, temp + 64, shuffler, MASK + 1);                 \
+     memcpy(dst_ptr + n * BPP, temp + 64, r * BPP);                 \
--- a/media/libyuv/update.py
+++ b/media/libyuv/update.py
@@ -32,16 +32,22 @@ def apply_patches():
     # Patch to fix build errors
     os.system("patch -p3 < fix_build_errors.patch")
     # Patch to make mjpeg printfs optional at build time
     os.system("patch -p3 < make_mjpeg_printfs_optional.patch")
     # Patch to allow disabling of inline ASM and AVX2 code
     os.system("patch -p3 < allow_disabling_asm_avx2.patch")
     # Patch to add H444ToARGB() variant
     os.system("patch -p3 < add_H444ToARGB.patch")
+    # Patch for bug 1342730
+    os.system("patch -p3 < cpu_id.patch")
+    # Patch for bug 1342730
+    os.system("patch -p3 < cpu_id.patch")
+    # Patch for bug 1342732
+    os.system("patch -p3 < row_any.patch")
 
 def update_readme(commit):
     with open('README_MOZILLA') as f:
         readme = f.read()
 
     if 'The git commit ID last used to import was ' in readme:
         new_readme = re.sub('The git commit ID last used to import was [v\.a-f0-9]+',
             'The git commit ID last used to import was %s' % commit, readme)
--- a/netwerk/protocol/http/HttpAuthUtils.cpp
+++ b/netwerk/protocol/http/HttpAuthUtils.cpp
@@ -1,8 +1,12 @@
+/* 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 "mozilla/net/HttpAuthUtils.h"
 #include "mozilla/Tokenizer.h"
 #include "nsIPrefService.h"
 #include "nsIURI.h"
 #include "nsNetUtil.h"
 #include "nsUnicharUtils.h"
 
 namespace mozilla {
--- a/security/nss/TAG-INFO
+++ b/security/nss/TAG-INFO
@@ -1,1 +1,1 @@
-93b99b0936d3
+6511e19a2c6c
--- a/security/nss/automation/taskcluster/docker/setup.sh
+++ b/security/nss/automation/taskcluster/docker/setup.sh
@@ -45,17 +45,17 @@ echo "deb http://ppa.launchpad.net/ubunt
 apt-get -y update
 apt-get install -y --no-install-recommends ${apt_packages[@]}
 
 # 32-bit builds
 ln -s /usr/include/x86_64-linux-gnu/zconf.h /usr/include
 
 # Install clang-3.9 into /usr/local/.
 # FIXME: verify signature
-curl -L http://releases.llvm.org/3.9.0/clang+llvm-3.9.0-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar xJv -C /usr/local --strip-components=1
+curl -L http://releases.llvm.org/3.9.1/clang+llvm-3.9.1-x86_64-linux-gnu-ubuntu-16.04.tar.xz | tar xJv -C /usr/local --strip-components=1
 
 # Install latest Rust (stable).
 su worker -c "curl https://sh.rustup.rs -sSf | sh -s -- -y"
 
 locale-gen en_US.UTF-8
 dpkg-reconfigure locales
 
 # Cleanup.
--- a/security/nss/automation/taskcluster/graph/src/extend.js
+++ b/security/nss/automation/taskcluster/graph/src/extend.js
@@ -348,17 +348,16 @@ async function scheduleFuzzing() {
     cycle: "standard",
     symbol: "Gtest",
     kind: "test"
   }));
 
   // Schedule fuzzing runs.
   let run_base = merge(base, {parent: task_build, kind: "test"});
   scheduleFuzzingRun(run_base, "CertDN", "certDN", 4096);
-  scheduleFuzzingRun(run_base, "Hash", "hash", 4096);
   scheduleFuzzingRun(run_base, "QuickDER", "quickder", 10000);
 
   // Schedule MPI fuzzing runs.
   let mpi_base = merge(run_base, {group: "MPI"});
   let mpi_names = ["add", "addmod", "div", "expmod", "mod", "mulmod", "sqr",
                    "sqrmod", "sub", "submod"];
   for (let name of mpi_names) {
     scheduleFuzzingRun(mpi_base, `MPI (${name})`, `mpi-${name}`, 4096, name);
--- a/security/nss/cmd/pk12util/pk12util.c
+++ b/security/nss/cmd/pk12util/pk12util.c
@@ -610,21 +610,17 @@ P12U_ExportPKCS12Object(char *nn, char *
     p12cxt = p12u_InitContext(PR_FALSE, outfile);
     if (!p12cxt) {
         SECU_PrintError(progName, "Initialization failed: %s", outfile);
         pk12uErrno = PK12UERR_INIT_FILE;
         goto loser;
     }
 
     if (certlist) {
-        CERTCertificate *cert = NULL;
-        node = CERT_LIST_HEAD(certlist);
-        if (node) {
-            cert = node->cert;
-        }
+        CERTCertificate *cert = CERT_LIST_HEAD(certlist)->cert;
         if (cert) {
             slot = cert->slot; /* use the slot from the first matching
                 certificate to create the context . This is for keygen */
         }
     }
     if (!slot) {
         SECU_PrintError(progName, "cert does not have a slot");
         pk12uErrno = PK12UERR_FINDCERTBYNN;
@@ -856,16 +852,19 @@ static void
 p12u_EnableAllCiphers()
 {
     SEC_PKCS12EnableCipher(PKCS12_RC4_40, 1);
     SEC_PKCS12EnableCipher(PKCS12_RC4_128, 1);
     SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_40, 1);
     SEC_PKCS12EnableCipher(PKCS12_RC2_CBC_128, 1);
     SEC_PKCS12EnableCipher(PKCS12_DES_56, 1);
     SEC_PKCS12EnableCipher(PKCS12_DES_EDE3_168, 1);
+    SEC_PKCS12EnableCipher(PKCS12_AES_CBC_128, 1);
+    SEC_PKCS12EnableCipher(PKCS12_AES_CBC_192, 1);
+    SEC_PKCS12EnableCipher(PKCS12_AES_CBC_256, 1);
     SEC_PKCS12SetPreferredCipher(PKCS12_DES_EDE3_168, 1);
 }
 
 static PRUintn
 P12U_Init(char *dir, char *dbprefix, PRBool listonly)
 {
     SECStatus rv;
     PK11_SetPasswordFunc(SECU_GetModulePassword);
--- a/security/nss/cmd/tstclnt/tstclnt.c
+++ b/security/nss/cmd/tstclnt/tstclnt.c
@@ -870,16 +870,20 @@ restartHandshakeAfterServerCertIfNeeded(
         }
     }
     if (rv == SECSuccess) {
         error = 0;
     }
 
     if (SSL_AuthCertificateComplete(fd, error) != SECSuccess) {
         rv = SECFailure;
+    } else {
+        /* restore the original error code, which could be reset by
+         * SSL_AuthCertificateComplete */
+        PORT_SetError(error);
     }
 
     return rv;
 }
 
 char *host = NULL;
 char *nickname = NULL;
 char *cipherString = NULL;
--- a/security/nss/coreconf/coreconf.dep
+++ b/security/nss/coreconf/coreconf.dep
@@ -5,9 +5,8 @@
 
 /*
  * A dummy header file that is a dependency for all the object files.
  * Used to force a full recompilation of NSS in Mozilla's Tinderbox
  * depend builds.  See comments in rules.mk.
  */
 
 #error "Do not include this header file."
-
--- a/security/nss/cpputil/scoped_ptrs.h
+++ b/security/nss/cpputil/scoped_ptrs.h
@@ -23,16 +23,19 @@ struct ScopedDelete {
   }
   void operator()(PK11SlotInfo* slot) { PK11_FreeSlot(slot); }
   void operator()(PK11SymKey* key) { PK11_FreeSymKey(key); }
   void operator()(PRFileDesc* fd) { PR_Close(fd); }
   void operator()(SECAlgorithmID* id) { SECOID_DestroyAlgorithmID(id, true); }
   void operator()(SECItem* item) { SECITEM_FreeItem(item, true); }
   void operator()(SECKEYPublicKey* key) { SECKEY_DestroyPublicKey(key); }
   void operator()(SECKEYPrivateKey* key) { SECKEY_DestroyPrivateKey(key); }
+  void operator()(SECKEYPrivateKeyList* list) {
+    SECKEY_DestroyPrivateKeyList(list);
+  }
 };
 
 template <class T>
 struct ScopedMaybeDelete {
   void operator()(T* ptr) {
     if (ptr) {
       ScopedDelete del;
       del(ptr);
@@ -48,12 +51,13 @@ SCOPED(CERTCertList);
 SCOPED(CERTSubjectPublicKeyInfo);
 SCOPED(PK11SlotInfo);
 SCOPED(PK11SymKey);
 SCOPED(PRFileDesc);
 SCOPED(SECAlgorithmID);
 SCOPED(SECItem);
 SCOPED(SECKEYPublicKey);
 SCOPED(SECKEYPrivateKey);
+SCOPED(SECKEYPrivateKeyList);
 
 #undef SCOPED
 
 #endif  // scoped_ptrs_h__
--- a/security/nss/fuzz/fuzz.gyp
+++ b/security/nss/fuzz/fuzz.gyp
@@ -108,27 +108,16 @@
         'quickder_target.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         'fuzz_base',
       ],
     },
     {
-      'target_name': 'nssfuzz-hash',
-      'type': 'executable',
-      'sources': [
-        'hash_target.cc',
-      ],
-      'dependencies': [
-        '<(DEPTH)/exports.gyp:nss_exports',
-        'fuzz_base',
-      ],
-    },
-    {
       'target_name': 'nssfuzz-certDN',
       'type': 'executable',
       'sources': [
         'certDN_target.cc',
       ],
       'dependencies': [
         '<(DEPTH)/exports.gyp:nss_exports',
         'fuzz_base',
@@ -282,17 +271,16 @@
         }],
       ],
     },
     {
       'target_name': 'nssfuzz',
       'type': 'none',
       'dependencies': [
         'nssfuzz-certDN',
-        'nssfuzz-hash',
         'nssfuzz-pkcs8',
         'nssfuzz-quickder',
         'nssfuzz-tls-client',
       ],
       'conditions': [
         ['OS=="linux"', {
           'dependencies': [
             'nssfuzz-mpi-add',
deleted file mode 100644
--- a/security/nss/fuzz/hash.options
+++ /dev/null
@@ -1,3 +0,0 @@
-[libfuzzer]
-max_len = 4096
-
deleted file mode 100644
--- a/security/nss/fuzz/hash_target.cc
+++ /dev/null
@@ -1,39 +0,0 @@
-/* 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 <memory>
-#include <vector>
-
-#include "hasht.h"
-#include "pk11pub.h"
-#include "secoidt.h"
-#include "shared.h"
-
-const std::vector<SECOidTag> algos = {SEC_OID_MD5, SEC_OID_SHA1, SEC_OID_SHA256,
-                                      SEC_OID_SHA384, SEC_OID_SHA512};
-
-extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
-  uint8_t hashOut[HASH_LENGTH_MAX];
-
-  static std::unique_ptr<NSSDatabase> db(new NSSDatabase());
-  assert(db != nullptr);
-
-  // simple hashing.
-  for (auto algo : algos) {
-    assert(PK11_HashBuf(algo, hashOut, data, size) == SECSuccess);
-  }
-
-  // hashing with context.
-  for (auto algo : algos) {
-    unsigned int len = 0;
-    PK11Context *context = PK11_CreateDigestContext(algo);
-    assert(context != nullptr);
-    assert(PK11_DigestBegin(context) == SECSuccess);
-    assert(PK11_DigestFinal(context, hashOut, &len, HASH_LENGTH_MAX) ==
-           SECSuccess);
-    PK11_DestroyContext(context, PR_TRUE);
-  }
-
-  return 0;
-}
--- a/security/nss/gtests/ssl_gtest/tls_connect.cc
+++ b/security/nss/gtests/ssl_gtest/tls_connect.cc
@@ -169,17 +169,17 @@ void TlsConnectTestBase::ClearServerCach
   SSL_ShutdownServerSessionIDCache();
   SSLInt_ClearSessionTicketKey();
   SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
 }
 
 void TlsConnectTestBase::SetUp() {
   SSL_ConfigServerSessionIDCache(1024, 0, 0, g_working_dir_path.c_str());
   SSLInt_ClearSessionTicketKey();
-  SSLInt_SetTicketLifetime(10);
+  SSLInt_SetTicketLifetime(30);
   ClearStats();
   Init();
 }
 
 void TlsConnectTestBase::TearDown() {
   client_ = nullptr;
   server_ = nullptr;
 
--- a/security/nss/lib/certdb/certdb.c
+++ b/security/nss/lib/certdb/certdb.c
@@ -2554,19 +2554,19 @@ CERT_AddCertToListTail(CERTCertList *cer
 SECStatus
 CERT_AddCertToListHeadWithData(CERTCertList *certs, CERTCertificate *cert,
                                void *appData)
 {
     CERTCertListNode *node;
     CERTCertListNode *head;
 
     head = CERT_LIST_HEAD(certs);
-
-    if (head == NULL)
-        return CERT_AddCertToListTail(certs, cert);
+    if (head == NULL) {
+        goto loser;
+    }
 
     node = (CERTCertListNode *)PORT_ArenaZAlloc(certs->arena,
                                                 sizeof(CERTCertListNode));
     if (node == NULL) {
         goto loser;
     }
 
     PR_INSERT_BEFORE(&node->links, &head->links);
--- a/security/nss/lib/certdb/stanpcertdb.c
+++ b/security/nss/lib/certdb/stanpcertdb.c
@@ -510,38 +510,35 @@ CERT_FindCertByName(CERTCertDBHandle *ha
     return c ? STAN_GetCERTCertificateOrRelease(c) : NULL;
 }
 
 CERTCertificate *
 CERT_FindCertByKeyID(CERTCertDBHandle *handle, SECItem *name, SECItem *keyID)
 {
     CERTCertList *list;
     CERTCertificate *cert = NULL;
-    CERTCertListNode *node, *head;
+    CERTCertListNode *node;
 
     list = CERT_CreateSubjectCertList(NULL, handle, name, 0, PR_FALSE);
     if (list == NULL)
         return NULL;
 
-    node = head = CERT_LIST_HEAD(list);
-    if (head) {
-        do {
-            if (node->cert &&
-                SECITEM_ItemsAreEqual(&node->cert->subjectKeyID, keyID)) {
-                cert = CERT_DupCertificate(node->cert);
-                goto done;
-            }
-            node = CERT_LIST_NEXT(node);
-        } while (node && head != node);
+    node = CERT_LIST_HEAD(list);
+    while (!CERT_LIST_END(node, list)) {
+        if (node->cert &&
+            SECITEM_ItemsAreEqual(&node->cert->subjectKeyID, keyID)) {
+            cert = CERT_DupCertificate(node->cert);
+            goto done;
+        }
+        node = CERT_LIST_NEXT(node);
     }
     PORT_SetError(SEC_ERROR_UNKNOWN_ISSUER);
+
 done:
-    if (list) {
-        CERT_DestroyCertList(list);
-    }
+    CERT_DestroyCertList(list);
     return cert;
 }
 
 CERTCertificate *
 CERT_FindCertByNickname(CERTCertDBHandle *handle, const char *nickname)
 {
     NSSCryptoContext *cc;
     NSSCertificate *c, *ct;
@@ -630,18 +627,17 @@ common_FindCertByNicknameOrEmailAddrForU
                 ct = NULL;
             }
         }
 
         certlist = PK11_FindCertsFromNickname(name, NULL);
         if (certlist) {
             SECStatus rv =
                 CERT_FilterCertListByUsage(certlist, lookingForUsage, PR_FALSE);
-            if (SECSuccess == rv &&
-                !CERT_LIST_END(CERT_LIST_HEAD(certlist), certlist)) {
+            if (SECSuccess == rv && !CERT_LIST_EMPTY(certlist)) {
                 cert = CERT_DupCertificate(CERT_LIST_HEAD(certlist)->cert);
             }
             CERT_DestroyCertList(certlist);
         }
     }
 
     if (cert) {
         c = get_best_temp_or_perm(ct, STAN_GetNSSCertificate(cert));
--- a/security/nss/lib/certhigh/certhigh.c
+++ b/security/nss/lib/certhigh/certhigh.c
@@ -284,17 +284,17 @@ CERT_FindUserCertByUsage(CERTCertDBHandl
 
     /* remove certs with incorrect usage */
     rv = CERT_FilterCertListByUsage(certList, usage, PR_FALSE);
 
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    if (!CERT_LIST_END(CERT_LIST_HEAD(certList), certList)) {
+    if (!CERT_LIST_EMPTY(certList)) {
         cert = CERT_DupCertificate(CERT_LIST_HEAD(certList)->cert);
     }
 
 loser:
     if (certList != NULL) {
         CERT_DestroyCertList(certList);
     }
 
--- a/security/nss/lib/freebl/Makefile
+++ b/security/nss/lib/freebl/Makefile
@@ -227,18 +227,16 @@ ifeq ($(CPU_ARCH),x86_64)
     INTEL_GCM = 1
     MPI_SRCS += mpi_amd64.c mp_comba.c
 endif
 ifeq ($(CPU_ARCH),x86)
     ASFILES  = mpi_x86.s
     DEFINES += -DMP_ASSEMBLY_MULTIPLY -DMP_ASSEMBLY_SQUARE 
     DEFINES += -DMP_ASSEMBLY_DIV_2DX1D -DMP_USE_UINT_DIGIT
     DEFINES += -DMP_IS_LITTLE_ENDIAN
-    # The floating point ECC code doesn't work on Linux x86 (bug 311432).
-    #ECL_USE_FP = 1
 endif
 ifeq ($(CPU_ARCH),arm)
     DEFINES += -DMP_ASSEMBLY_MULTIPLY -DMP_ASSEMBLY_SQUARE 
     DEFINES += -DMP_USE_UINT_DIGIT
     DEFINES += -DSHA_NO_LONG_LONG # avoid 64-bit arithmetic in SHA512
     MPI_SRCS += mpi_arm.c
 endif
 ifeq ($(CPU_ARCH),ppc)
@@ -425,30 +423,28 @@ ifeq ($(CPU_ARCH),sparc)
     endif
     ifdef USE_ABI32_FPU
 	# this builds for Sparc v8+a ABI32_FPU architecture, 64-bit registers, 
 	# 32-bit ABI, it uses FPU code, and 32-bit word size
 	MPI_SRCS += mpi_sparc.c
 	ASFILES  = mpv_sparcv8.s montmulfv8.s
 	DEFINES  += -DMP_NO_MP_WORD -DMP_USE_UINT_DIGIT -DMP_ASSEMBLY_MULTIPLY
 	DEFINES  += -DMP_USING_MONT_MULF -DMP_MONT_USE_MP_MUL
-	ECL_USE_FP = 1
     endif
     ifdef USE_ABI64_INT
 	# this builds for Sparc v9a pure 64-bit architecture
 	# best times are with no MP_ flags specified
     endif
     ifdef USE_ABI64_FPU
 	# this builds for Sparc v9a pure 64-bit architecture
 	# It uses floating point, and 32-bit word size
 	MPI_SRCS += mpi_sparc.c
 	ASFILES   = mpv_sparcv9.s montmulfv9.s
 	DEFINES  += -DMP_NO_MP_WORD -DMP_USE_UINT_DIGIT -DMP_ASSEMBLY_MULTIPLY
 	DEFINES  += -DMP_USING_MONT_MULF -DMP_MONT_USE_MP_MUL
-	ECL_USE_FP = 1
     endif
 
 else
     # Solaris for non-sparc family CPUs
     ifdef NS_USE_GCC
 	LD = gcc
 	AS = gcc
 	ASFLAGS = -x assembler-with-cpp
@@ -486,26 +482,17 @@ else
 	DEFINES += -DMP_ASSEMBLY_DIV_2DX1D
 	ASFILES  = mpi_i86pc.s
  	ifndef NS_USE_GCC
  	   MPCPU_SRCS =
  	   ASFILES += mpcpucache_x86.s
  	endif
     endif
 endif # Solaris for non-sparc family CPUs
-endif # target == SunOS
-
-ifndef NSS_DISABLE_ECC
-    ifdef ECL_USE_FP
-	#enable floating point ECC code	
-	DEFINES  += -DECL_USE_FP
-	ECL_SRCS += ecp_fp160.c ecp_fp192.c ecp_fp224.c ecp_fp.c
-	ECL_HDRS += ecp_fp.h
-    endif
-endif
+endif # target == SunO
 
 # poly1305-donna-x64-sse2-incremental-source.c requires __int128 support
 # in GCC 4.6.0.
 ifdef USE_64
     ifdef CC_IS_CLANG
             HAVE_INT128_SUPPORT = 1
             DEFINES += -DHAVE_INT128_SUPPORT
     else ifeq (1,$(CC_IS_GCC))
--- a/security/nss/lib/nss/nss.def
+++ b/security/nss/lib/nss/nss.def
@@ -1094,12 +1094,13 @@ SECMOD_CreateModuleEx;
 ;+    global:
 PK11_SignWithMechanism;
 PK11_VerifyWithMechanism;
 ;+    local:
 ;+       *;
 ;+};
 ;+NSS_3.30 { 	# NSS 3.30 release
 ;+    global:
+CERT_CompareAVA;
 PK11_HasAttributeSet;
 ;+    local:
 ;+       *;
 ;+};
--- a/security/nss/lib/pk11wrap/pk11cert.c
+++ b/security/nss/lib/pk11wrap/pk11cert.c
@@ -685,18 +685,17 @@ PK11_FindCertsFromEmailAddress(const cha
     rv = PK11_TraverseSlotCerts(FindCertsEmailCallback, &cbparam, NULL);
     if (rv != SECSuccess) {
         CERT_DestroyCertList(cbparam.certList);
         PORT_Free(cbparam.email);
         return NULL;
     }
 
     /* empty list? */
-    if (CERT_LIST_HEAD(cbparam.certList) == NULL ||
-        CERT_LIST_END(CERT_LIST_HEAD(cbparam.certList), cbparam.certList)) {
+    if (CERT_LIST_EMPTY(cbparam.certList)) {
         CERT_DestroyCertList(cbparam.certList);
         cbparam.certList = NULL;
     }
 
     PORT_Free(cbparam.email);
     return cbparam.certList;
 }
 
@@ -819,20 +818,16 @@ PK11_FindCertsFromNickname(const char *n
                     /* CERT_AddCertToListSorted adopts certCert  */
                     CERT_AddCertToListSorted(certList, certCert,
                                              CERT_SortCBValidity, &now);
                 }
             } else {
                 nssCertificate_Destroy(c);
             }
         }
-        if (certList && CERT_LIST_HEAD(certList) == NULL) {
-            CERT_DestroyCertList(certList);
-            certList = NULL;
-        }
         /* all the certs have been adopted or freed, free the  raw array */
         nss_ZFreeIf(foundCerts);
     }
     return certList;
 }
 
 /*
  * extract a key ID for a certificate...
--- a/security/nss/lib/pk11wrap/pk11pbe.c
+++ b/security/nss/lib/pk11wrap/pk11pbe.c
@@ -1,14 +1,15 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "plarena.h"
 
+#include "blapit.h"
 #include "seccomon.h"
 #include "secitem.h"
 #include "secport.h"
 #include "hasht.h"
 #include "pkcs11t.h"
 #include "sechash.h"
 #include "secasn1.h"
 #include "secder.h"
@@ -296,27 +297,59 @@ SEC_PKCS5GetPBEAlgorithm(SECOidTag algTa
             break;
         default:
             return sec_pkcs5v2_get_pbe(algTag);
     }
 
     return SEC_OID_UNKNOWN;
 }
 
+static PRBool
+sec_pkcs5_is_algorithm_v2_aes_algorithm(SECOidTag algorithm)
+{
+    switch (algorithm) {
+        case SEC_OID_AES_128_CBC:
+        case SEC_OID_AES_192_CBC:
+        case SEC_OID_AES_256_CBC:
+            return PR_TRUE;
+        default:
+            return PR_FALSE;
+    }
+}
+
+static int
+sec_pkcs5v2_aes_key_length(SECOidTag algorithm)
+{
+    switch (algorithm) {
+        /* The key length for the AES-CBC-Pad algorithms are
+         * determined from the undelying cipher algorithm.  */
+        case SEC_OID_AES_128_CBC:
+            return AES_128_KEY_LENGTH;
+        case SEC_OID_AES_192_CBC:
+            return AES_192_KEY_LENGTH;
+        case SEC_OID_AES_256_CBC:
+            return AES_256_KEY_LENGTH;
+        default:
+            break;
+    }
+    return 0;
+}
+
 /*
  * get the key length in bytes from a PKCS5 PBE
  */
-int
-sec_pkcs5v2_key_length(SECAlgorithmID *algid)
+static int
+sec_pkcs5v2_key_length(SECAlgorithmID *algid, SECAlgorithmID *cipherAlgId)
 {
     SECOidTag algorithm;
     PLArenaPool *arena = NULL;
     SEC_PKCS5PBEParameter p5_param;
     SECStatus rv;
     int length = -1;
+    SECOidTag cipherAlg = SEC_OID_UNKNOWN;
 
     algorithm = SECOID_GetAlgorithmTag(algid);
     /* sanity check, they should all be PBKDF2 here */
     if (algorithm != SEC_OID_PKCS5_PBKDF2) {
         return -1;
     }
 
     arena = PORT_NewArena(SEC_ASN1_DEFAULT_ARENA_SIZE);
@@ -325,17 +358,22 @@ sec_pkcs5v2_key_length(SECAlgorithmID *a
     }
     PORT_Memset(&p5_param, 0, sizeof(p5_param));
     rv = SEC_ASN1DecodeItem(arena, &p5_param,
                             SEC_PKCS5V2PBEParameterTemplate, &algid->parameters);
     if (rv != SECSuccess) {
         goto loser;
     }
 
-    if (p5_param.keyLength.data != NULL) {
+    if (cipherAlgId)
+        cipherAlg = SECOID_GetAlgorithmTag(cipherAlgId);
+
+    if (sec_pkcs5_is_algorithm_v2_aes_algorithm(cipherAlg)) {
+        length = sec_pkcs5v2_aes_key_length(cipherAlg);
+    } else if (p5_param.keyLength.data != NULL) {
         length = DER_GetInteger(&p5_param.keyLength);
     }
 
 loser:
     if (arena) {
         PORT_FreeArena(arena, PR_FALSE);
     }
     return length;
@@ -370,24 +408,25 @@ SEC_PKCS5GetKeyLength(SECAlgorithmID *al
         case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
             return 5;
         case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
         case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
         case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
         case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
             return 16;
         case SEC_OID_PKCS5_PBKDF2:
-            return sec_pkcs5v2_key_length(algid);
+            return sec_pkcs5v2_key_length(algid, NULL);
         case SEC_OID_PKCS5_PBES2:
         case SEC_OID_PKCS5_PBMAC1: {
             sec_pkcs5V2Parameter *pbeV2_param;
             int length = -1;
             pbeV2_param = sec_pkcs5_v2_get_v2_param(NULL, algid);
             if (pbeV2_param != NULL) {
-                length = sec_pkcs5v2_key_length(&pbeV2_param->pbeAlgId);
+                length = sec_pkcs5v2_key_length(&pbeV2_param->pbeAlgId,
+                                                &pbeV2_param->cipherAlgId);
                 sec_pkcs5_v2_destroy_v2_param(pbeV2_param);
             }
             return length;
         }
 
         default:
             break;
     }
@@ -609,16 +648,18 @@ sec_pkcs5CreateAlgorithmID(SECOidTag alg
             algorithm = sec_pkcs5v2_get_pbe(cipherAlgorithm);
         }
 
         /* set the PKCS5v2 specific parameters */
         if (keyLength == 0) {
             SECOidTag hashAlg = HASH_GetHashOidTagByHMACOidTag(cipherAlgorithm);
             if (hashAlg != SEC_OID_UNKNOWN) {
                 keyLength = HASH_ResultLenByOidTag(hashAlg);
+            } else if (sec_pkcs5_is_algorithm_v2_aes_algorithm(cipherAlgorithm)) {
+                keyLength = sec_pkcs5v2_aes_key_length(cipherAlgorithm);
             } else {
                 CK_MECHANISM_TYPE cryptoMech;
                 cryptoMech = PK11_AlgtagToMechanism(cipherAlgorithm);
                 if (cryptoMech == CKM_INVALID_MECHANISM) {
                     goto loser;
                 }
                 keyLength = PK11_GetMaxKeyLength(cryptoMech);
             }
--- a/security/nss/lib/pkcs12/p12d.c
+++ b/security/nss/lib/pkcs12/p12d.c
@@ -172,47 +172,71 @@ sec_pkcs12_proper_version(sec_PKCS12PFXI
 
 /* retrieve the key for decrypting the safe contents */
 static PK11SymKey *
 sec_pkcs12_decoder_get_decrypt_key(void *arg, SECAlgorithmID *algid)
 {
     SEC_PKCS12DecoderContext *p12dcx = (SEC_PKCS12DecoderContext *)arg;
     PK11SlotInfo *slot;
     PK11SymKey *bulkKey;
+    SECItem *pwitem;
+    SECItem decodedPwitem = { 0 };
+    SECOidTag algorithm;
 
     if (!p12dcx) {
         return NULL;
     }
 
     /* if no slot specified, use the internal key slot */
     if (p12dcx->slot) {
         slot = PK11_ReferenceSlot(p12dcx->slot);
     } else {
         slot = PK11_GetInternalKeySlot();
     }
 
-    bulkKey = PK11_PBEKeyGen(slot, algid, p12dcx->pwitem,
+    algorithm = SECOID_GetAlgorithmTag(algid);
+    pwitem = p12dcx->pwitem;
+
+    /* here we assume that the password is already encoded into
+     * BMPString by the caller.  if the encryption scheme is not the
+     * one defined in PKCS #12, decode the password back into
+     * UTF-8. */
+    if (!sec_pkcs12_is_pkcs12_pbe_algorithm(algorithm)) {
+        if (!sec_pkcs12_convert_item_to_unicode(NULL, &decodedPwitem,
+                                                p12dcx->pwitem,
+                                                PR_TRUE, PR_FALSE, PR_FALSE)) {
+            PORT_SetError(SEC_ERROR_NO_MEMORY);
+            return NULL;
+        }
+        pwitem = &decodedPwitem;
+    }
+
+    bulkKey = PK11_PBEKeyGen(slot, algid, pwitem,
                              PR_FALSE, p12dcx->wincx);
     /* some tokens can't generate PBE keys on their own, generate the
      * key in the internal slot, and let the Import code deal with it,
      * (if the slot can't generate PBEs, then we need to use the internal
      * slot anyway to unwrap). */
     if (!bulkKey && !PK11_IsInternal(slot)) {
         PK11_FreeSlot(slot);
         slot = PK11_GetInternalKeySlot();
-        bulkKey = PK11_PBEKeyGen(slot, algid, p12dcx->pwitem,
+        bulkKey = PK11_PBEKeyGen(slot, algid, pwitem,
                                  PR_FALSE, p12dcx->wincx);
     }
     PK11_FreeSlot(slot);
 
     /* set the password data on the key */
     if (bulkKey) {
         PK11_SetSymKeyUserData(bulkKey, p12dcx->pwitem, NULL);
     }
 
+    if (decodedPwitem.data) {
+        SECITEM_ZfreeItem(&decodedPwitem, PR_FALSE);
+    }
+
     return bulkKey;
 }
 
 /* XXX this needs to be modified to handle enveloped data.  most
  * likely, it should mirror the routines for SMIME in that regard.
  */
 static PRBool
 sec_pkcs12_decoder_decryption_allowed(SECAlgorithmID *algid,
--- a/security/nss/lib/pkcs12/p12e.c
+++ b/security/nss/lib/pkcs12/p12e.c
@@ -5,16 +5,17 @@
 #include "p12t.h"
 #include "p12.h"
 #include "plarena.h"
 #include "secitem.h"
 #include "secoid.h"
 #include "seccomon.h"
 #include "secport.h"
 #include "cert.h"
+#include "secpkcs5.h"
 #include "secpkcs7.h"
 #include "secasn1.h"
 #include "secerr.h"
 #include "pk11func.h"
 #include "p12plcy.h"
 #include "p12local.h"
 #include "prcpucfg.h"
 
@@ -373,29 +374,46 @@ SEC_PKCS12CreatePasswordPrivSafe(SEC_PKC
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         PORT_ArenaRelease(p12ctxt->arena, mark);
         return NULL;
     }
 
     safeInfo->itemCount = 0;
 
     /* create the encrypted safe */
-    safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn,
-                                                   p12ctxt->pwfnarg);
+    if (!SEC_PKCS5IsAlgorithmPBEAlgTag(privAlg) &&
+        PK11_AlgtagToMechanism(privAlg) == CKM_AES_CBC) {
+        safeInfo->cinfo = SEC_PKCS7CreateEncryptedDataWithPBEV2(SEC_OID_PKCS5_PBES2,
+                                                                privAlg,
+                                                                SEC_OID_UNKNOWN,
+                                                                0,
+                                                                p12ctxt->pwfn,
+                                                                p12ctxt->pwfnarg);
+    } else {
+        safeInfo->cinfo = SEC_PKCS7CreateEncryptedData(privAlg, 0, p12ctxt->pwfn,
+                                                       p12ctxt->pwfnarg);
+    }
     if (!safeInfo->cinfo) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         goto loser;
     }
     safeInfo->arena = p12ctxt->arena;
 
-    /* convert the password to unicode */
-    if (!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem,
-                                            PR_TRUE, PR_TRUE, PR_TRUE)) {
-        PORT_SetError(SEC_ERROR_NO_MEMORY);
-        goto loser;
+    if (sec_pkcs12_is_pkcs12_pbe_algorithm(privAlg)) {
+        /* convert the password to unicode */
+        if (!sec_pkcs12_convert_item_to_unicode(NULL, &uniPwitem, pwitem,
+                                                PR_TRUE, PR_TRUE, PR_TRUE)) {
+            PORT_SetError(SEC_ERROR_NO_MEMORY);
+            goto loser;
+        }
+    } else {
+        if (SECITEM_CopyItem(NULL, &uniPwitem, pwitem) != SECSuccess) {
+            PORT_SetError(SEC_ERROR_NO_MEMORY);
+            goto loser;
+        }
     }
     if (SECITEM_CopyItem(p12ctxt->arena, &safeInfo->pwitem, &uniPwitem) != SECSuccess) {
         PORT_SetError(SEC_ERROR_NO_MEMORY);
         goto loser;
     }
 
     /* generate the encryption key */
     slot = PK11_ReferenceSlot(p12ctxt->slot);
--- a/security/nss/lib/pkcs12/p12local.c
+++ b/security/nss/lib/pkcs12/p12local.c
@@ -944,16 +944,43 @@ sec_pkcs12_convert_item_to_unicode(PLAre
         }
         dest->len += 2;
         dest->data[dest->len - 1] = dest->data[dest->len - 2] = 0;
     }
 
     return PR_TRUE;
 }
 
+PRBool
+sec_pkcs12_is_pkcs12_pbe_algorithm(SECOidTag algorithm)
+{
+    switch (algorithm) {
+        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
+        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
+        case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC:
+        case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+        case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
+        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
+        case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_40_BIT_RC4:
+        case SEC_OID_PKCS12_PBE_WITH_SHA1_AND_128_BIT_RC4:
+        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
+        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
+        /* those are actually PKCS #5 v1.5 PBEs, but we
+         * historically treat them in the same way as PKCS #12
+         * PBEs */
+        case SEC_OID_PKCS5_PBE_WITH_MD2_AND_DES_CBC:
+        case SEC_OID_PKCS5_PBE_WITH_SHA1_AND_DES_CBC:
+        case SEC_OID_PKCS5_PBE_WITH_MD5_AND_DES_CBC:
+            return PR_TRUE;
+        default:
+            return PR_FALSE;
+    }
+}
+
 /* pkcs 12 templates */
 static const SEC_ASN1TemplateChooserPtr sec_pkcs12_shroud_chooser =
     sec_pkcs12_choose_shroud_type;
 
 const SEC_ASN1Template SEC_PKCS12CodedSafeBagTemplate[] =
     {
       { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(SEC_PKCS12SafeBag) },
       { SEC_ASN1_OBJECT_ID, offsetof(SEC_PKCS12SafeBag, safeBagType) },
--- a/security/nss/lib/pkcs12/p12local.h
+++ b/security/nss/lib/pkcs12/p12local.h
@@ -50,9 +50,11 @@ extern SEC_PKCS12AuthenticatedSafe *sec_
 
 /* conversion from old to new */
 extern SEC_PKCS12DecoderContext *
 sec_PKCS12ConvertOldSafeToNew(PLArenaPool *arena, PK11SlotInfo *slot,
                               PRBool swapUnicode, SECItem *pwitem,
                               void *wincx, SEC_PKCS12SafeContents *safe,
                               SEC_PKCS12Baggage *baggage);
 
+extern PRBool sec_pkcs12_is_pkcs12_pbe_algorithm(SECOidTag algorithm);
+
 #endif
--- a/security/nss/lib/pkcs12/p12plcy.c
+++ b/security/nss/lib/pkcs12/p12plcy.c
@@ -19,16 +19,19 @@ typedef struct pkcs12SuiteMapStr {
 
 static pkcs12SuiteMap pkcs12SuiteMaps[] = {
     { SEC_OID_RC4, 40, PKCS12_RC4_40, PR_FALSE, PR_FALSE },
     { SEC_OID_RC4, 128, PKCS12_RC4_128, PR_FALSE, PR_FALSE },
     { SEC_OID_RC2_CBC, 40, PKCS12_RC2_CBC_40, PR_FALSE, PR_TRUE },
     { SEC_OID_RC2_CBC, 128, PKCS12_RC2_CBC_128, PR_FALSE, PR_FALSE },
     { SEC_OID_DES_CBC, 64, PKCS12_DES_56, PR_FALSE, PR_FALSE },
     { SEC_OID_DES_EDE3_CBC, 192, PKCS12_DES_EDE3_168, PR_FALSE, PR_FALSE },
+    { SEC_OID_AES_128_CBC, 128, PKCS12_AES_CBC_128, PR_FALSE, PR_FALSE },
+    { SEC_OID_AES_192_CBC, 192, PKCS12_AES_CBC_192, PR_FALSE, PR_FALSE },
+    { SEC_OID_AES_256_CBC, 256, PKCS12_AES_CBC_256, PR_FALSE, PR_FALSE },
     { SEC_OID_UNKNOWN, 0, PKCS12_NULL, PR_FALSE, PR_FALSE },
     { SEC_OID_UNKNOWN, 0, 0L, PR_FALSE, PR_FALSE }
 };
 
 /* determine if algid is an algorithm which is allowed */
 PRBool
 SEC_PKCS12DecryptionAllowed(SECAlgorithmID *algid)
 {
--- a/security/nss/lib/pkcs7/p7create.c
+++ b/security/nss/lib/pkcs7/p7create.c
@@ -1240,8 +1240,61 @@ SEC_PKCS7CreateEncryptedData(SECOidTag a
                                                algorithm, keysize);
     if (rv != SECSuccess) {
         SEC_PKCS7DestroyContentInfo(cinfo);
         return NULL;
     }
 
     return cinfo;
 }
+
+SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateEncryptedDataWithPBEV2(SECOidTag pbe_algorithm,
+                                      SECOidTag cipher_algorithm,
+                                      SECOidTag prf_algorithm,
+                                      int keysize,
+                                      SECKEYGetPasswordKey pwfn, void *pwfn_arg)
+{
+    SEC_PKCS7ContentInfo *cinfo;
+    SECAlgorithmID *algid;
+    SEC_PKCS7EncryptedData *enc_data;
+    SECStatus rv;
+
+    PORT_Assert(SEC_PKCS5IsAlgorithmPBEAlgTag(pbe_algorithm));
+
+    cinfo = sec_pkcs7_create_content_info(SEC_OID_PKCS7_ENCRYPTED_DATA,
+                                          PR_FALSE, pwfn, pwfn_arg);
+    if (cinfo == NULL)
+        return NULL;
+
+    enc_data = cinfo->content.encryptedData;
+    algid = &(enc_data->encContentInfo.contentEncAlg);
+
+    SECAlgorithmID *pbe_algid;
+    pbe_algid = PK11_CreatePBEV2AlgorithmID(pbe_algorithm,
+                                            cipher_algorithm,
+                                            prf_algorithm,
+                                            keysize,
+                                            NSS_PBE_DEFAULT_ITERATION_COUNT,
+                                            NULL);
+    if (pbe_algid == NULL) {
+        rv = SECFailure;
+    } else {
+        rv = SECOID_CopyAlgorithmID(cinfo->poolp, algid, pbe_algid);
+        SECOID_DestroyAlgorithmID(pbe_algid, PR_TRUE);
+    }
+
+    if (rv != SECSuccess) {
+        SEC_PKCS7DestroyContentInfo(cinfo);
+        return NULL;
+    }
+
+    rv = sec_pkcs7_init_encrypted_content_info(&(enc_data->encContentInfo),
+                                               cinfo->poolp,
+                                               SEC_OID_PKCS7_DATA, PR_FALSE,
+                                               cipher_algorithm, keysize);
+    if (rv != SECSuccess) {
+        SEC_PKCS7DestroyContentInfo(cinfo);
+        return NULL;
+    }
+
+    return cinfo;
+}
--- a/security/nss/lib/pkcs7/secpkcs7.h
+++ b/security/nss/lib/pkcs7/secpkcs7.h
@@ -282,16 +282,36 @@ extern SEC_PKCS7ContentInfo *SEC_PKCS7Cr
  * An error results in a return value of NULL and an error set.
  * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
  */
 extern SEC_PKCS7ContentInfo *
 SEC_PKCS7CreateEncryptedData(SECOidTag algorithm, int keysize,
                              SECKEYGetPasswordKey pwfn, void *pwfn_arg);
 
 /*
+ * Create an empty PKCS7 encrypted content info.
+ *
+ * Similar to SEC_PKCS7CreateEncryptedData(), but this is capable of
+ * creating encrypted content for PKCS #5 v2 algorithms.
+ *
+ * "pbe_algorithm" specifies the PBE algorithm to use.
+ * "cipher_algorithm" specifies the bulk encryption algorithm to use.
+ * "prf_algorithm" specifies the PRF algorithm which pbe_algorithm uses.
+ *
+ * An error results in a return value of NULL and an error set.
+ * (Retrieve specific errors via PORT_GetError()/XP_GetError().)
+ */
+extern SEC_PKCS7ContentInfo *
+SEC_PKCS7CreateEncryptedDataWithPBEV2(SECOidTag pbe_algorithm,
+                                      SECOidTag cipher_algorithm,
+                                      SECOidTag prf_algorithm,
+                                      int keysize,
+                                      SECKEYGetPasswordKey pwfn, void *pwfn_arg);
+
+/*
  * All of the following things return SECStatus to signal success or failure.
  * Failure should have a more specific error status available via
  * PORT_GetError()/XP_GetError().
  */
 
 /*
  * Add the specified attribute to the authenticated (i.e. signed) attributes
  * of "cinfo" -- "oidtag" describes the attribute and "value" is the
--- a/security/nss/lib/util/ciferfam.h
+++ b/security/nss/lib/util/ciferfam.h
@@ -47,13 +47,16 @@
 /* PKCS12 "Cipher Suites" */
 
 #define PKCS12_RC2_CBC_40 (CIPHER_FAMILYID_PKCS12 | 0001)
 #define PKCS12_RC2_CBC_128 (CIPHER_FAMILYID_PKCS12 | 0002)
 #define PKCS12_RC4_40 (CIPHER_FAMILYID_PKCS12 | 0011)
 #define PKCS12_RC4_128 (CIPHER_FAMILYID_PKCS12 | 0012)
 #define PKCS12_DES_56 (CIPHER_FAMILYID_PKCS12 | 0021)
 #define PKCS12_DES_EDE3_168 (CIPHER_FAMILYID_PKCS12 | 0022)
+#define PKCS12_AES_CBC_128 (CIPHER_FAMILYID_PKCS12 | 0031)
+#define PKCS12_AES_CBC_192 (CIPHER_FAMILYID_PKCS12 | 0032)
+#define PKCS12_AES_CBC_256 (CIPHER_FAMILYID_PKCS12 | 0033)
 
 /* SMIME version numbers are negative, to avoid colliding with SSL versions */
 #define SMIME_LIBRARY_VERSION_1_0 -0x0100
 
 #endif /* _CIFERFAM_H_ */
--- a/security/nss/lib/util/nssb64d.c
+++ b/security/nss/lib/util/nssb64d.c
@@ -699,56 +699,57 @@ NSSBase64Decoder_Destroy(NSSBase64Decode
  * Return value is NULL on error, the Item (allocated or provided) otherwise.
  */
 SECItem *
 NSSBase64_DecodeBuffer(PLArenaPool *arenaOpt, SECItem *outItemOpt,
                        const char *inStr, unsigned int inLen)
 {
     SECItem *out_item = NULL;
     PRUint32 max_out_len = 0;
-    PRUint32 out_len;
     void *mark = NULL;
-    unsigned char *dummy;
+    unsigned char *dummy = NULL;
 
     if ((outItemOpt != NULL && outItemOpt->data != NULL) || inLen == 0) {
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         return NULL;
     }
 
     if (arenaOpt != NULL)
         mark = PORT_ArenaMark(arenaOpt);
 
     max_out_len = PL_Base64MaxDecodedLength(inLen);
+    if (max_out_len == 0) {
+        goto loser;
+    }
     out_item = SECITEM_AllocItem(arenaOpt, outItemOpt, max_out_len);
     if (out_item == NULL) {
-        if (arenaOpt != NULL)
-            PORT_ArenaRelease(arenaOpt, mark);
-        return NULL;
+        goto loser;
     }
 
     dummy = PL_Base64DecodeBuffer(inStr, inLen, out_item->data,
-                                  max_out_len, &out_len);
+                                  max_out_len, &out_item->len);
     if (dummy == NULL) {
-        if (arenaOpt != NULL) {
-            PORT_ArenaRelease(arenaOpt, mark);
-            if (outItemOpt != NULL) {
-                outItemOpt->data = NULL;
-                outItemOpt->len = 0;
-            }
-        } else {
-            SECITEM_FreeItem(out_item,
-                             (outItemOpt == NULL) ? PR_TRUE : PR_FALSE);
+        goto loser;
+    }
+    if (arenaOpt != NULL) {
+        PORT_ArenaUnmark(arenaOpt, mark);
+    }
+    return out_item;
+
+loser:
+    if (arenaOpt != NULL) {
+        PORT_ArenaRelease(arenaOpt, mark);
+        if (outItemOpt != NULL) {
+            outItemOpt->data = NULL;
+            outItemOpt->len = 0;
         }
-        return NULL;
+    } else if (dummy == NULL) {
+        SECITEM_FreeItem(out_item, (PRBool)(outItemOpt == NULL));
     }
-
-    if (arenaOpt != NULL)
-        PORT_ArenaUnmark(arenaOpt, mark);
-    out_item->len = out_len;
-    return out_item;
+    return NULL;
 }
 
 /*
  * XXX Everything below is deprecated.  If you add new stuff, put it
  * *above*, not below.
  */
 
 /*
new file mode 100644
--- /dev/null
+++ b/security/nss/nss-tool/common/util.cc
@@ -0,0 +1,135 @@
+/* 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 "util.h"
+
+#include <iomanip>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include <prerror.h>
+
+#if defined(__unix__) || defined(__APPLE__)
+#include <termios.h>
+#include <unistd.h>
+#elif defined(WIN32) || defined(_WIN64)
+#include <Windows.h>
+#endif
+
+static std::string GetPassword(const std::string &prompt) {
+  std::cout << prompt << std::endl;
+
+#if defined(__unix__) || defined(__APPLE__)
+  termios oldt;
+  tcgetattr(STDIN_FILENO, &oldt);
+  termios newt = oldt;
+  newt.c_lflag &= ~ECHO;
+  tcsetattr(STDIN_FILENO, TCSANOW, &newt);
+#elif defined(WIN32) || defined(_WIN64)
+  HANDLE hStdin = GetStdHandle(STD_INPUT_HANDLE);
+  DWORD mode = 0;
+  GetConsoleMode(hStdin, &mode);
+  SetConsoleMode(hStdin, mode & (~ENABLE_ECHO_INPUT));
+#endif
+
+  std::string pw;
+  std::getline(std::cin, pw);
+
+#if defined(__unix__) || defined(__APPLE__)
+  tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
+#elif defined(WIN32) || defined(_WIN64)
+  SetConsoleMode(hStdin, mode);
+#endif
+
+  return pw;
+}
+
+static char *GetModulePassword(PK11SlotInfo *slot, int retry, void *arg) {
+  if (arg == nullptr) {
+    return nullptr;
+  }
+
+  PwData *pwData = reinterpret_cast<PwData *>(arg);
+
+  if (retry > 0) {
+    std::cerr << "Incorrect password/PIN entered." << std::endl;
+    return nullptr;
+  }
+
+  switch (pwData->source) {
+    case PW_NONE:
+    case PW_FROMFILE:
+      std::cerr << "Password input method not supported." << std::endl;
+      return nullptr;
+    case PW_PLAINTEXT:
+      return PL_strdup(pwData->data);
+    default:
+      break;
+  }
+
+  std::cerr << "Password check failed:  No password found." << std::endl;
+  return nullptr;
+}
+
+bool InitSlotPassword(void) {
+  ScopedPK11SlotInfo slot(PK11_GetInternalKeySlot());
+  if (slot.get() == nullptr) {
+    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+    return false;
+  }
+
+  std::cout << "Enter a password which will be used to encrypt your keys."
+            << std::endl
+            << std::endl;
+  std::string pw;
+
+  while (true) {
+    pw = GetPassword("Enter new password: ");
+    if (pw == GetPassword("Re-enter password: ")) {
+      break;
+    }
+
+    std::cerr << "Passwords do not match. Try again." << std::endl;
+  }
+
+  SECStatus rv = PK11_InitPin(slot.get(), nullptr, pw.c_str());
+  if (rv != SECSuccess) {
+    std::cerr << "Init db password failed." << std::endl;
+    return false;
+  }
+
+  return true;
+}
+
+bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot) {
+  if (!PK11_NeedLogin(slot.get())) {
+    return true;
+  }
+
+  PK11_SetPasswordFunc(&GetModulePassword);
+  std::string pw = GetPassword("Enter your password: ");
+  PwData pwData = {PW_PLAINTEXT, const_cast<char *>(pw.c_str())};
+  SECStatus rv = PK11_Authenticate(slot.get(), true /*loadCerts*/, &pwData);
+  if (rv != SECSuccess) {
+    std::cerr << "Could not authenticate to token "
+              << PK11_GetTokenName(slot.get()) << ". Failed with error "
+              << PR_ErrorToName(PR_GetError()) << std::endl;
+    return false;
+  }
+  std::cout << std::endl;
+
+  return true;
+}
+
+std::string StringToHex(const ScopedSECItem &input) {
+  std::stringstream ss;
+  ss << "0x";
+  for (size_t i = 0; i < input->len; i++) {
+    ss << std::hex << std::setfill('0') << std::setw(2)
+       << static_cast<int>(input->data[i]);
+  }
+
+  return ss.str();
+}
new file mode 100644
--- /dev/null
+++ b/security/nss/nss-tool/common/util.h
@@ -0,0 +1,23 @@
+/* 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 util_h__
+#define util_h__
+
+#include "scoped_ptrs.h"
+
+#include <secmodt.h>
+#include <string>
+
+enum PwDataType { PW_NONE = 0, PW_FROMFILE = 1, PW_PLAINTEXT = 2 };
+typedef struct {
+  PwDataType source;
+  char *data;
+} PwData;
+
+bool InitSlotPassword(void);
+bool DBLoginIfNeeded(const ScopedPK11SlotInfo &slot);
+std::string StringToHex(const ScopedSECItem &input);
+
+#endif  // util_h__
--- a/security/nss/nss-tool/db/dbtool.cc
+++ b/security/nss/nss-tool/db/dbtool.cc
@@ -1,29 +1,39 @@
 /* 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 "dbtool.h"
 #include "argparse.h"
 #include "scoped_ptrs.h"
+#include "util.h"
 
 #include <dirent.h>
 #include <fstream>
 #include <iomanip>
 #include <iostream>
 #include <memory>
 #include <regex>
 #include <sstream>
 
 #include <cert.h>
 #include <certdb.h>
 #include <nss.h>
+#include <prerror.h>
 #include <prio.h>
 
+const std::vector<std::string> kCommandArgs({"--create", "--list-certs",
+                                             "--import-cert", "--list-keys"});
+
+static bool HasSingleCommandArgument(const ArgParser &parser) {
+  auto pred = [&](const std::string &cmd) { return parser.Has(cmd); };
+  return std::count_if(kCommandArgs.begin(), kCommandArgs.end(), pred) == 1;
+}
+
 static std::string PrintFlags(unsigned int flags) {
   std::stringstream ss;
   if ((flags & CERTDB_VALID_CA) && !(flags & CERTDB_TRUSTED_CA) &&
       !(flags & CERTDB_TRUSTED_CLIENT_CA)) {
     ss << "c";
   }
   if ((flags & CERTDB_TERMINAL_RECORD) && !(flags & CERTDB_TRUSTED)) {
     ss << "p";
@@ -58,29 +68,33 @@ static std::vector<char> ReadFromIstream
     char buf[1024];
     is.read(buf, sizeof(buf));
     certData.insert(certData.end(), buf, buf + is.gcount());
   }
 
   return certData;
 }
 
+static const char *const keyTypeName[] = {"null", "rsa", "dsa", "fortezza",
+                                          "dh",   "kea", "ec"};
+
 void DBTool::Usage() {
   std::cerr << "Usage: nss db [--path <directory>]" << std::endl;
   std::cerr << "  --create" << std::endl;
   std::cerr << "  --list-certs" << std::endl;
   std::cerr << "  --import-cert [<path>] --name <name> [--trusts <trusts>]"
             << std::endl;
+  std::cerr << "  --list-keys" << std::endl;
 }
 
 bool DBTool::Run(const std::vector<std::string> &arguments) {
   ArgParser parser(arguments);
 
-  if (!parser.Has("--create") && !parser.Has("--list-certs") &&
-      !parser.Has("--import-cert")) {
+  if (!HasSingleCommandArgument(parser)) {
+    Usage();
     return false;
   }
 
   PRAccessHow how = PR_ACCESS_READ_OK;
   bool readOnly = true;
   if (parser.Has("--create") || parser.Has("--import-cert")) {
     how = PR_ACCESS_WRITE_OK;
     readOnly = false;
@@ -124,17 +138,22 @@ bool DBTool::Run(const std::vector<std::
   }
 
   bool ret = true;
   if (parser.Has("--list-certs")) {
     ListCertificates();
   } else if (parser.Has("--import-cert")) {
     ret = ImportCertificate(parser);
   } else if (parser.Has("--create")) {
-    std::cout << "DB files created successfully." << std::endl;
+    ret = InitSlotPassword();
+    if (ret) {
+      std::cout << "DB files created successfully." << std::endl;
+    }
+  } else if (parser.Has("--list-keys")) {
+    ret = ListKeys();
   }
 
   // shutdown nss
   if (NSS_Shutdown() != SECSuccess) {
     std::cerr << "NSS Shutdown failed!" << std::endl;
     return false;
   }
 
@@ -228,17 +247,17 @@ bool DBTool::ImportCertificate(const Arg
   SECStatus rv = CERT_DecodeTrustString(&trust, trustString.c_str());
   if (rv != SECSuccess) {
     std::cerr << "Cannot decode trust string!" << std::endl;
     return false;
   }
 
   ScopedPK11SlotInfo slot = ScopedPK11SlotInfo(PK11_GetInternalKeySlot());
   if (slot.get() == nullptr) {
-    std::cerr << "Error: Init PK11SlotInfo failed!\n";
+    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
     return false;
   }
 
   std::vector<char> certData;
   if (derFilePath.empty()) {
     std::cout << "No Certificate file path given, using stdin." << std::endl;
     certData = ReadFromIstream(std::cin);
   } else {
@@ -274,8 +293,77 @@ bool DBTool::ImportCertificate(const Arg
     std::cerr << "Cannot change cert's trust" << std::endl;
     return false;
   }
 
   std::cout << "Certificate import was successful!" << std::endl;
   // TODO show information about imported certificate
   return true;
 }
+
+bool DBTool::ListKeys() {
+  ScopedPK11SlotInfo slot = ScopedPK11SlotInfo(PK11_GetInternalKeySlot());
+  if (slot.get() == nullptr) {
+    std::cerr << "Error: Init PK11SlotInfo failed!" << std::endl;
+    return false;
+  }
+
+  if (!DBLoginIfNeeded(slot)) {
+    return false;
+  }
+
+  ScopedSECKEYPrivateKeyList list(PK11_ListPrivateKeysInSlot(slot.get()));
+  if (list.get() == nullptr) {
+    std::cerr << "Listing private keys failed with error "
+              << PR_ErrorToName(PR_GetError()) << std::endl;
+    return false;
+  }
+
+  SECKEYPrivateKeyListNode *node;
+  int count = 0;
+  for (node = PRIVKEY_LIST_HEAD(list.get());
+       !PRIVKEY_LIST_END(node, list.get()); node = PRIVKEY_LIST_NEXT(node)) {
+    char *keyNameRaw = PK11_GetPrivateKeyNickname(node->key);
+    std::string keyName(keyNameRaw ? "" : keyNameRaw);
+
+    if (keyName.empty()) {
+      ScopedCERTCertificate cert(PK11_GetCertFromPrivateKey(node->key));
+      if (cert.get()) {
+        if (cert->nickname && strlen(cert->nickname) > 0) {
+          keyName = cert->nickname;
+        } else if (cert->emailAddr && strlen(cert->emailAddr) > 0) {
+          keyName = cert->emailAddr;
+        }
+      }
+      if (keyName.empty()) {
+        keyName = "(none)";  // default value
+      }
+    }
+
+    SECKEYPrivateKey *key = node->key;
+    ScopedSECItem keyIDItem(PK11_GetLowLevelKeyIDForPrivateKey(key));
+    if (keyIDItem.get() == nullptr) {
+      std::cerr << "Error: PK11_GetLowLevelKeyIDForPrivateKey failed!"
+                << std::endl;
+      continue;
+    }
+
+    std::string keyID = StringToHex(keyIDItem);
+
+    if (count++ == 0) {
+      // print header
+      std::cout << std::left << std::setw(20) << "<key#, key name>"
+                << std::setw(20) << "key type"
+                << "key id" << std::endl;
+    }
+
+    std::stringstream leftElem;
+    leftElem << "<" << count << ", " << keyName << ">";
+    std::cout << std::left << std::setw(20) << leftElem.str() << std::setw(20)
+              << keyTypeName[key->keyType] << keyID << std::endl;
+  }
+
+  if (count == 0) {
+    std::cout << "No keys found." << std::endl;
+  }
+
+  return true;
+}
--- a/security/nss/nss-tool/db/dbtool.h
+++ b/security/nss/nss-tool/db/dbtool.h
@@ -14,11 +14,12 @@ class DBTool {
   bool Run(const std::vector<std::string>& arguments);
 
   void Usage();
 
  private:
   bool PathHasDBFiles(std::string path);
   void ListCertificates();
   bool ImportCertificate(const ArgParser& parser);
+  bool ListKeys();
 };
 
 #endif  // dbtool_h__
--- a/security/nss/nss-tool/nss_tool.cc
+++ b/security/nss/nss-tool/nss_tool.cc
@@ -28,16 +28,15 @@ int main(int argc, char **argv) {
   }
 
   int exit_code = 0;
   PR_Init(PR_SYSTEM_THREAD, PR_PRIORITY_NORMAL, 1);
 
   std::vector<std::string> arguments(argv + 2, argv + argc);
   DBTool tool;
   if (!tool.Run(arguments)) {
-    tool.Usage();
     exit_code = 1;
   }
 
   PR_Cleanup();
 
   return exit_code;
 }
--- a/security/nss/nss-tool/nss_tool.gyp
+++ b/security/nss/nss-tool/nss_tool.gyp
@@ -8,16 +8,17 @@
   ],
   'targets' : [
     {
       'target_name' : 'nss',
       'type' : 'executable',
       'sources' : [
         'nss_tool.cc',
         'common/argparse.cc',
+        'common/util.cc',
         'db/dbtool.cc',
       ],
       'include_dirs': [
         'common',
       ],
       'dependencies' : [
         '<(DEPTH)/cpputil/cpputil.gyp:cpputil',
         '<(DEPTH)/exports.gyp:dbm_exports',
--- a/security/nss/tests/bogo/bogo.sh
+++ b/security/nss/tests/bogo/bogo.sh
@@ -20,17 +20,17 @@ bogo_init()
     . ./init.sh
   fi
 
   mkdir -p "${HOSTDIR}/bogo"
   cd "${HOSTDIR}/bogo"
   BORING=${BORING:=boringssl}
   if [ ! -d "$BORING" ]; then
     git clone -q https://boringssl.googlesource.com/boringssl "$BORING"
-    git -C "$BORING" checkout -q 004bff3a1412fcc6ba168d4295a942f9b1e0866e
+    git -C "$BORING" checkout -q 5ae416528a0e554aa4df91bdb1e03f75bfc03cd0
   fi
 
   SCRIPTNAME="bogo.sh"
   html_head "bogo test"
 }
 
 bogo_cleanup()
 {
--- a/security/nss/tests/tools/tools.sh
+++ b/security/nss/tests/tools/tools.sh
@@ -268,30 +268,30 @@ tools_p12_export_list_import_all_pkcs5v2
     AES-128-CBC \
     AES-192-CBC \
     AES-256-CBC \
     CAMELLIA-128-CBC \
     CAMELLIA-192-CBC \
     CAMELLIA-256-CBC; do
 
 #---------------------------------------------------------------
-# Bug 452464 - pk12util -o fails when -C option specifies AES or
+# Bug 452464 - pk12util -o fails when -C option specifies
 # Camellia ciphers
 # FIXME Restore these to the list
-#    AES-128-CBC, \
-#    AES-192-CBC, \
-#    AES-256-CBC, \
 #    CAMELLIA-128-CBC, \
 #    CAMELLIA-192-CBC, \
 #    CAMELLIA-256-CBC, \
 #  when 452464 is fixed
 #---------------------------------------------------------------  
     for cert_cipher in \
       RC2-CBC \
       DES-EDE3-CBC \
+      AES-128-CBC \
+      AES-192-CBC \
+      AES-256-CBC \
       null; do
 	  export_list_import ${key_cipher} ${cert_cipher}
 	done
   done
 }
 
 ########################################################################
 # Export using the pkcs12v2pbe ciphers for key and certificate encryption.
--- a/security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp
+++ b/security/sandbox/chromium-shim/sandbox/win/permissionsService.cpp
@@ -10,42 +10,81 @@
 #include <algorithm>
 #include <winternl.h>
 
 namespace mozilla {
 namespace sandboxing {
 
 static const std::wstring ZONE_IDENTIFIER_STR(L":ZONE.IDENTIFIER");
 static const std::wstring ZONE_ID_DATA_STR(L":ZONE.IDENTIFIER:$DATA");
+// Generic name we use for all Flash temp files.
+static const std::wstring FLASH_TEMP_FILENAME(L"FLASHTMP0.TMP");
 
 bool
 StringEndsWith(const std::wstring& str, const std::wstring& strEnding)
 {
   if (strEnding.size() > str.size()) {
     return false;
   }
   return std::equal(strEnding.rbegin(), strEnding.rend(), str.rbegin());
 }
 
+// Returns true if aFilename describes a Flash temp file.  If aFolder is
+// non-null then it is filled with the name of the folder containing
+// the file (with trailing slash).
+bool
+IsFlashTempFile(std::wstring aFilename, std::wstring* aFolder=nullptr)
+{
+  // Assume its a flash file if the base name begins with "FlashTmp",
+  // ends with ".tmp" and has an int in-between them.
+  int slashIdx = aFilename.find_last_of(L'\\');
+  if (slashIdx != std::wstring::npos) {
+    if (aFolder) {
+      *aFolder = aFilename.substr(0, slashIdx + 1);
+    }
+    aFilename = aFilename.substr(slashIdx + 1);
+  } else {
+    *aFolder = L"\\";
+  }
+
+  if (aFilename.compare(0, 8, L"FLASHTMP") != 0) {
+    return false;
+  }
+
+  int idx = 8;
+  int len = aFilename.length();
+  while (idx < len && isdigit(aFilename[idx])) {
+    ++idx;
+  }
+
+  return (len-idx == 4) && aFilename.compare(idx, 4, L".TMP") == 0;
+}
+
 // Converts NT device internal filenames to normal user-space by stripping
-// the prefixes and suffixes from the file name.
+// the prefixes and suffixes from the file name.  Returns containing
+// folder (with trailing slash) in aFolder if non-null.
 std::wstring
-GetPlainFileName(const wchar_t* aNTFileName)
+GetPlainFileName(const wchar_t* aNTFileName, std::wstring* aFolder=nullptr)
 {
   while (*aNTFileName == L'\\' || *aNTFileName == L'.' ||
          *aNTFileName == L'?' || *aNTFileName == L':' ) {
     ++aNTFileName;
   }
   std::wstring nameCopy(aNTFileName);
   std::transform(nameCopy.begin(), nameCopy.end(), nameCopy.begin(), towupper);
   if (StringEndsWith(nameCopy, ZONE_ID_DATA_STR)) {
     nameCopy = nameCopy.substr(0, nameCopy.size() - ZONE_ID_DATA_STR.size());
   } else if (StringEndsWith(nameCopy, ZONE_IDENTIFIER_STR)) {
     nameCopy = nameCopy.substr(0, nameCopy.size() - ZONE_IDENTIFIER_STR.size());
   }
+
+  if (IsFlashTempFile(nameCopy, aFolder) && aFolder) {
+    return *aFolder + FLASH_TEMP_FILENAME;
+  }
+
   return nameCopy;
 }
 
 /* static */ PermissionsService*
 PermissionsService::GetInstance()
 {
   static PermissionsService sPermissionsService;
   return &sPermissionsService;
@@ -57,18 +96,23 @@ PermissionsService::PermissionsService()
 }
 
 void
 PermissionsService::GrantFileAccess(uint32_t aProcessId,
                                     const wchar_t* aFilename,
                                     bool aPermitWrite)
 {
   FilePermissionMap& permissions = mProcessFilePermissions[aProcessId];
-  std::wstring filename = GetPlainFileName(aFilename);
+  std::wstring containingFolder;
+  std::wstring filename = GetPlainFileName(aFilename, &containingFolder);
   permissions[filename] |= aPermitWrite;
+  if (aPermitWrite) {
+    // Also grant write permission to FLASH_TEMP_FILENAME in the same folder.
+    permissions[containingFolder + FLASH_TEMP_FILENAME] = true;
+  }
 }
 
 void
 PermissionsService::SetFileAccessViolationFunc(FileAccessViolationFunc aFavFunc)
 {
   mFileAccessViolationFunc = aFavFunc;
 }
 
@@ -105,16 +149,17 @@ PermissionsService::UserGrantedFileAcces
 
   auto permissions = mProcessFilePermissions.find(aProcessId);
   if (permissions == mProcessFilePermissions.end()) {
     ReportBlockedFile(needsWrite);
     return false;  // process has no special file access at all
   }
 
   std::wstring filename = GetPlainFileName(aFilename);
+
   auto itPermission = permissions->second.find(filename);
   if (itPermission == permissions->second.end()) {
     ReportBlockedFile(needsWrite);
     return false;  // process has no access to this file
   }
 
   // We have read permission.  Check for write permission if requested.
   if (!needsWrite || itPermission->second) {
--- a/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_dispatcher.cc
@@ -221,16 +221,25 @@ bool FilesystemDispatcher::NtQueryAttrib
   params[FileName::BROKER] = ParamPickerMake(broker);
 
   // To evaluate the policy we need to call back to the policy object. We
   // are just middlemen in the operation since is the FileSystemPolicy which
   // knows what to do.
   EvalResult result = policy_base_->EvalPolicy(IPC_NTQUERYATTRIBUTESFILE_TAG,
                                                params.GetBase());
 
+  // If the policies forbid access (any result other than ASK_BROKER),
+  // then check for user-granted access to file.
+  if (ASK_BROKER != result &&
+      mozilla::sandboxing::PermissionsService::GetInstance()->
+        UserGrantedFileAccess(ipc->client_info->process_id, filename,
+                              0, 0)) {
+    result = ASK_BROKER;
+  }
+
   FILE_BASIC_INFORMATION* information =
         reinterpret_cast<FILE_BASIC_INFORMATION*>(info->Buffer());
   NTSTATUS nt_status;
   if (!FileSystemPolicy::QueryAttributesFileAction(result, *ipc->client_info,
                                                    *name, attributes,
                                                    information, &nt_status)) {
     ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
     return true;
@@ -261,16 +270,25 @@ bool FilesystemDispatcher::NtQueryFullAt
   params[FileName::BROKER] = ParamPickerMake(broker);
 
   // To evaluate the policy we need to call back to the policy object. We
   // are just middlemen in the operation since is the FileSystemPolicy which
   // knows what to do.
   EvalResult result = policy_base_->EvalPolicy(
                           IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase());
 
+  // If the policies forbid access (any result other than ASK_BROKER),
+  // then check for user-granted access to file.
+  if (ASK_BROKER != result &&
+      mozilla::sandboxing::PermissionsService::GetInstance()->
+        UserGrantedFileAccess(ipc->client_info->process_id, filename,
+                              0, 0)) {
+    result = ASK_BROKER;
+  }
+
   FILE_NETWORK_OPEN_INFORMATION* information =
         reinterpret_cast<FILE_NETWORK_OPEN_INFORMATION*>(info->Buffer());
   NTSTATUS nt_status;
   if (!FileSystemPolicy::QueryFullAttributesFileAction(result,
                                                        *ipc->client_info,
                                                        *name, attributes,
                                                        information,
                                                        &nt_status)) {
@@ -316,16 +334,26 @@ bool FilesystemDispatcher::NtSetInformat
   params[FileName::BROKER] = ParamPickerMake(broker);
 
   // To evaluate the policy we need to call back to the policy object. We
   // are just middlemen in the operation since is the FileSystemPolicy which
   // knows what to do.
   EvalResult result = policy_base_->EvalPolicy(IPC_NTSETINFO_RENAME_TAG,
                                                params.GetBase());
 
+  // If the policies forbid access (any result other than ASK_BROKER),
+  // then check for user-granted write access to file.  We only permit
+  // the FileRenameInformation action.
+  if (ASK_BROKER != result && info_class == FileRenameInformation &&
+      mozilla::sandboxing::PermissionsService::GetInstance()->
+        UserGrantedFileAccess(ipc->client_info->process_id, filename,
+                              FILE_WRITE_ATTRIBUTES, 0)) {
+    result = ASK_BROKER;
+  }
+
   IO_STATUS_BLOCK* io_status =
         reinterpret_cast<IO_STATUS_BLOCK*>(status->Buffer());
   NTSTATUS nt_status;
   if (!FileSystemPolicy::SetInformationFileAction(result, *ipc->client_info,
                                                   handle, rename_info, length,
                                                   info_class, io_status,
                                                   &nt_status)) {
     ipc->return_info.nt_status = STATUS_ACCESS_DENIED;
--- a/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
+++ b/security/sandbox/chromium/sandbox/win/src/filesystem_interception.cc
@@ -216,19 +216,16 @@ NTSTATUS WINAPI TargetNtQueryAttributesF
     InOutCountedBuffer file_info(file_attributes,
                                  sizeof(FILE_BASIC_INFORMATION));
 
     uint32_t broker = FALSE;
     CountedParameterSet<FileName> params;
     params[FileName::NAME] = ParamPickerMake(name);
     params[FileName::BROKER] = ParamPickerMake(broker);
 
-    if (!QueryBroker(IPC_NTQUERYATTRIBUTESFILE_TAG, params.GetBase()))
-      break;
-
     SharedMemIPCClient ipc(memory);
     CrossCallReturn answer = {0};
     ResultCode code = CrossCall(ipc, IPC_NTQUERYATTRIBUTESFILE_TAG, name,
                                 attributes, file_info, &answer);
 
     if (SBOX_ALL_OK != code)
       break;
 
@@ -282,19 +279,16 @@ NTSTATUS WINAPI TargetNtQueryFullAttribu
     InOutCountedBuffer file_info(file_attributes,
                                  sizeof(FILE_NETWORK_OPEN_INFORMATION));
 
     uint32_t broker = FALSE;
     CountedParameterSet<FileName> params;
     params[FileName::NAME] = ParamPickerMake(name);
     params[FileName::BROKER] = ParamPickerMake(broker);
 
-    if (!QueryBroker(IPC_NTQUERYFULLATTRIBUTESFILE_TAG, params.GetBase()))
-      break;
-
     SharedMemIPCClient ipc(memory);
     CrossCallReturn answer = {0};
     ResultCode code = CrossCall(ipc, IPC_NTQUERYFULLATTRIBUTESFILE_TAG, name,
                                 attributes, file_info, &answer);
 
     if (SBOX_ALL_OK != code)
       break;
 
@@ -361,19 +355,16 @@ NTSTATUS WINAPI TargetNtSetInformationFi
     if (!NT_SUCCESS(ret) || !name)
       break;
 
     uint32_t broker = FALSE;
     CountedParameterSet<FileName> params;
     params[FileName::NAME] = ParamPickerMake(name);
     params[FileName::BROKER] = ParamPickerMake(broker);
 
-    if (!QueryBroker(IPC_NTSETINFO_RENAME_TAG, params.GetBase()))
-      break;
-
     InOutCountedBuffer io_status_buffer(io_status, sizeof(IO_STATUS_BLOCK));
     // This is actually not an InOut buffer, only In, but using InOut facility
     // really helps to simplify the code.
     InOutCountedBuffer file_info_buffer(file_info, length);
 
     SharedMemIPCClient ipc(memory);
     CrossCallReturn answer = {0};
     ResultCode code = CrossCall(ipc, IPC_NTSETINFO_RENAME_TAG, file,
--- a/taskcluster/taskgraph/target_tasks.py
+++ b/taskcluster/taskgraph/target_tasks.py
@@ -205,20 +205,17 @@ def target_tasks_mozilla_beta(full_task_
     """Select the set of tasks required for a promotable beta or release build
     of linux, plus android CI. The candidates build process involves a pipeline
     of builds and signing, but does not include beetmover or balrog jobs."""
     def filter(task):
         platform = task.attributes.get('build_platform')
         if platform in ('android-api-15', 'android-x86'):
             return True
         if platform in ('linux64-nightly', 'linux-nightly'):
-            if task.kind not in [
-                'balrog', 'beetmover', 'beetmover-checksums', 'beetmover-l10n',
-                'checksums-signing',
-            ]:
+            if task.kind not in ['balrog']:
                 return task.attributes.get('nightly', False)
     return [l for l, t in full_task_graph.tasks.iteritems() if filter(t)]
 
 
 @_target_task('mozilla_release_tasks')
 def target_tasks_mozilla_release(full_task_graph, parameters):
     """Select the set of tasks required for a promotable beta or release build
     of linux, plus android CI. The candidates build process involves a pipeline
--- a/taskcluster/taskgraph/util/scriptworker.py
+++ b/taskcluster/taskgraph/util/scriptworker.py
@@ -9,17 +9,16 @@ checked again at the script level.  This
 these scopes automatically by project; this makes pushing to try, forking a
 project branch, and merge day uplifts more user friendly.
 
 In the future, we may adjust scopes by other settings as well, e.g. different
 scopes for `push-to-candidates` rather than `push-to-releases`, even if both
 happen on mozilla-beta and mozilla-release.
 """
 from __future__ import absolute_import, print_function, unicode_literals
-from copy import deepcopy
 import functools
 
 
 """Map signing scope aliases to sets of projects.
 
 Currently m-c and m-a use nightly signing; m-b and m-r use release signing.
 We will need to add esr support at some point. Eventually we want to add
 nuance so certain m-b and m-r tasks use dep or nightly signing, and we only
@@ -44,34 +43,43 @@ SIGNING_SCOPE_ALIAS_TO_PROJECT = [[
 """
 SIGNING_CERT_SCOPES = {
     'all-release-branches': 'project:releng:signing:cert:release-signing',
     'all-nightly-branches': 'project:releng:signing:cert:nightly-signing',
     'default': 'project:releng:signing:cert:dep-signing',
 }
 
 """Map beetmover scope aliases to sets of projects.
-
-Currently this mirrors the signing scope alias behavior.
 """
-BEETMOVER_SCOPE_ALIAS_TO_PROJECT = deepcopy(SIGNING_SCOPE_ALIAS_TO_PROJECT)
+BEETMOVER_SCOPE_ALIAS_TO_PROJECT = [[
+    'all-nightly-branches', set([
+        'mozilla-central',
+        'mozilla-aurora',
+        'jamun',
+        'mozilla-beta',
+        'mozilla-release',
+    ])
+], [
+    'all-release-branches', set([
+    ])
+]]
 
 """Map beetmover tasks aliases to sets of target task methods.
 
 This is a list of list-pairs, for ordering.
 """
 BEETMOVER_SCOPE_ALIAS_TO_TARGET_TASK = [[
     'all-nightly-tasks', set([
         'nightly_fennec',
         'nightly_linux',
+        'mozilla_beta_tasks',
+        'mozilla_release_tasks',
     ])
 ], [
     'all-release-tasks', set([
-        'mozilla_beta_tasks',
-        'mozilla_release_tasks',
     ])
 ]]
 
 """Map the beetmover scope aliases to the actual scopes.
 """
 BEETMOVER_BUCKET_SCOPES = {
     'all-release-branches': 'project:releng:beetmover:bucket:release',
     'all-nightly-branches': 'project:releng:beetmover:bucket:nightly',
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -60269,16 +60269,21 @@
      {}
     ]
    ],
    "service-workers/service-worker/resources/empty-worker.js": [
     [
      {}
     ]
    ],
+   "service-workers/service-worker/resources/empty.html": [
+    [
+     {}
+    ]
+   ],
    "service-workers/service-worker/resources/empty.js": [
     [
      {}
     ]
    ],
    "service-workers/service-worker/resources/end-to-end-worker.js": [
     [
      {}
@@ -118614,16 +118619,22 @@
     ]
    ],
    "service-workers/service-worker/clients-matchall-include-uncontrolled.https.html": [
     [
      "/service-workers/service-worker/clients-matchall-include-uncontrolled.https.html",
      {}
     ]
    ],
+   "service-workers/service-worker/clients-matchall-order.https.html": [
+    [
+     "/service-workers/service-worker/clients-matchall-order.https.html",
+     {}
+    ]
+   ],
    "service-workers/service-worker/clients-matchall.https.html": [
     [
      "/service-workers/service-worker/clients-matchall.https.html",
      {}
     ]
    ],
    "service-workers/service-worker/controller-on-disconnect.https.html": [
     [
@@ -129392,17 +129403,17 @@
      "/webdriver/util/http_request.py",
      {}
     ]
    ]
   }
  },
  "paths": {
   "./.gitignore": [
-   "bfa8422ef9849af857cdbddbe8044c2179953f6d",
+   "a74e35d1ce44dcca7ecb513a8cbd6194fe0f2c58",
    "support"
   ],
   "./.gitmodules": [
    "3deb86ca08a0d6a80a8e897355ba7213dfb6e5bc",
    "support"
   ],
   "./.travis.yml": [
    "68037c26bd4a4c0c1500dd6628ce1d95840c5bb9",
@@ -199779,16 +199790,20 @@
   "service-workers/service-worker/clients-matchall-client-types.https.html": [
    "aaca38d0ad5e6a03775632fcef1657dd40753ae0",
    "testharness"
   ],
   "service-workers/service-worker/clients-matchall-include-uncontrolled.https.html": [
    "a4f4cb575ffea826c642aa3de424c0a0f986fdd0",
    "testharness"
   ],
+  "service-workers/service-worker/clients-matchall-order.https.html": [
+   "b2617b7dce0dce64c1a354a3dc07c67f1fa0adf2",
+   "testharness"
+  ],
   "service-workers/service-worker/clients-matchall.https.html": [
    "ac873d343a0c9d0af058f84d7e8db2809825c64c",
    "testharness"
   ],
   "service-workers/service-worker/controller-on-disconnect.https.html": [
    "fe9bcecbb8c7379a6f8da0420cc15a36a9e8060b",
    "testharness"
   ],
@@ -200152,17 +200167,17 @@
    "3c8866699d99cfaf61c52ca7a5dafc058a8c349d",
    "support"
   ],
   "service-workers/service-worker/resources/clients-matchall-client-types-shared-worker.js": [
    "5478ccfd9942f5ccb8335e6e13b52722b22e06d8",
    "support"
   ],
   "service-workers/service-worker/resources/clients-matchall-worker.js": [
-   "91fbaeac408f5afbfa32c0db412dc9c30bf45744",
+   "88326b2119c68ba656c62a74b9c1af201bca1548",
    "support"
   ],
   "service-workers/service-worker/resources/dummy-shared-worker-interceptor.js": [
    "620e50059fabfdd4b5c61dbb3ed2d8dca872b9bf",
    "support"
   ],
   "service-workers/service-worker/resources/dummy-worker-interceptor.js": [
    "f631d35c4eed6be4a8e6d2cdc5258ac0b169e177",
@@ -200183,16 +200198,20 @@
   "service-workers/service-worker/resources/empty-but-slow-worker.js": [
    "36ecac2f5ab2d3738ca72a7a7d1c605dbec97ff1",
    "support"
   ],
   "service-workers/service-worker/resources/empty-worker.js": [
    "84b3339c3419e318803e51f46d7252d9e8ac183b",
    "support"
   ],
+  "service-workers/service-worker/resources/empty.html": [
+   "90aa64bd32d0dd20f0fda1783b0d9ec4a97b3430",
+   "support"
+  ],
   "service-workers/service-worker/resources/empty.js": [
    "da39a3ee5e6b4b0d3255bfef95601890afd80709",
    "support"
   ],
   "service-workers/service-worker/resources/end-to-end-worker.js": [
    "067f43242350398e20be7061037d4a70ff34d4dc",
    "support"
   ],
--- a/testing/web-platform/meta/pointerevents/idlharness.html.ini
+++ b/testing/web-platform/meta/pointerevents/idlharness.html.ini
@@ -1,182 +1,4 @@
 [idlharness.html]
   type: testharness
-  [Window interface: attribute ongotpointercapture]
-    expected: FAIL
-
-  [Window interface: attribute onlostpointercapture]
-    expected: FAIL
-
-  [Window interface: attribute onpointerdown]
-    expected: FAIL
-
-  [Window interface: attribute onpointermove]
-    expected: FAIL
-
-  [Window interface: attribute onpointerup]
-    expected: FAIL
-
-  [Window interface: attribute onpointercancel]
-    expected: FAIL
-
-  [Window interface: attribute onpointerover]
-    expected: FAIL
-
-  [Window interface: attribute onpointerout]
-    expected: FAIL
-
-  [Window interface: attribute onpointerenter]
-    expected: FAIL
-
-  [Window interface: attribute onpointerleave]
-    expected: FAIL
-
-  [Window interface: window must inherit property "ongotpointercapture" with the proper type (0)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onlostpointercapture" with the proper type (1)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointerdown" with the proper type (2)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointermove" with the proper type (3)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointerup" with the proper type (4)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointercancel" with the proper type (5)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointerover" with the proper type (6)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointerout" with the proper type (7)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointerenter" with the proper type (8)]
-    expected: FAIL
-
-  [Window interface: window must inherit property "onpointerleave" with the proper type (9)]
-    expected: FAIL
-
-  [Navigator interface: attribute maxTouchPoints]
-    expected: FAIL
-
-  [Navigator interface: navigator must inherit property "maxTouchPoints" with the proper type (0)]
-    expected: FAIL
-
-  [Element interface: operation setPointerCapture(long)]
-    expected: FAIL
-
-  [Element interface: operation releasePointerCapture(long)]
-    expected: FAIL
-
-  [Element interface: operation hasPointerCapture(long)]
-    expected: FAIL
-
-  [HTMLElement interface: attribute ongotpointercapture]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onlostpointercapture]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointerdown]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointermove]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointerup]
-    expected: FAIL
+  prefs: [dom.w3c_pointer_events.enabled:true]
 
-  [HTMLElement interface: attribute onpointercancel]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointerover]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointerout]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointerenter]
-    expected: FAIL
-
-  [HTMLElement interface: attribute onpointerleave]
-    expected: FAIL
-
-  [Document interface: attribute ongotpointercapture]
-    expected: FAIL
-
-  [Document interface: attribute onlostpointercapture]
-    expected: FAIL
-
-  [Document interface: attribute onpointerdown]
-    expected: FAIL
-
-  [Document interface: attribute onpointermove]
-    expected: FAIL
-
-  [Document interface: attribute onpointerup]
-    expected: FAIL
-
-  [Document interface: attribute onpointercancel]
-    expected: FAIL
-
-  [Document interface: attribute onpointerover]
-    expected: FAIL
-
-  [Document interface: attribute onpointerout]
-    expected: FAIL
-
-  [Document interface: attribute onpointerenter]
-    expected: FAIL
-
-  [Document interface: attribute onpointerleave]
-    expected: FAIL
-
-  [PointerEvent interface: existence and properties of interface object]
-    expected: FAIL
-
-  [PointerEvent interface object length]
-    expected: FAIL
-
-  [PointerEvent interface object name]
-    expected: FAIL
-
-  [PointerEvent interface: existence and properties of interface prototype object]
-    expected: FAIL
-
-  [PointerEvent interface: existence and properties of interface prototype object's "constructor" property]
-    expected: FAIL
-
-  [PointerEvent interface: attribute pointerId]
-    expected: FAIL
-
-  [PointerEvent interface: attribute width]
-    expected: FAIL
-
-  [PointerEvent interface: attribute height]
-    expected: FAIL
-
-  [PointerEvent interface: attribute pressure]
-    expected: FAIL
-
-  [PointerEvent interface: attribute tangentialPressure]
-    expected: FAIL
-
-  [PointerEvent interface: attribute tiltX]
-    expected: FAIL
-
-  [PointerEvent interface: attribute tiltY]
-    expected: FAIL
-
-  [PointerEvent interface: attribute twist]
-    expected: FAIL
-
-  [PointerEvent interface: attribute pointerType]
-    expected: FAIL
-
-  [PointerEvent interface: attribute isPrimary]
-    expected: FAIL
-
--- a/testing/web-platform/meta/pointerevents/pointerevent_on_event_handlers.html.ini
+++ b/testing/web-platform/meta/pointerevents/pointerevent_on_event_handlers.html.ini
@@ -1,93 +1,4 @@
 [pointerevent_on_event_handlers.html]
   type: testharness
-  expected: TIMEOUT
-  [The default value of onpointerdown is always null]
-    expected: FAIL
-
-  [The onpointerdown content attribute must be compiled into the onpointerdown property]
-    expected: FAIL
-
-  [dispatching a pointerdown event must trigger element.onpointerdown]
-    expected: NOTRUN
-
-  [The default value of onpointerup is always null]
-    expected: FAIL
-
-  [The onpointerup content attribute must be compiled into the onpointerup property]
-    expected: FAIL
-
-  [dispatching a pointerup event must trigger element.onpointerup]
-    expected: NOTRUN
-
-  [The default value of onpointercancel is always null]
-    expected: FAIL
-
-  [The onpointercancel content attribute must be compiled into the onpointercancel property]
-    expected: FAIL
-
-  [dispatching a pointercancel event must trigger element.onpointercancel]
-    expected: NOTRUN
-
-  [The default value of onpointermove is always null]
-    expected: FAIL
-
-  [The onpointermove content attribute must be compiled into the onpointermove property]
-    expected: FAIL
-
-  [dispatching a pointermove event must trigger element.onpointermove]
-    expected: NOTRUN
-
-  [The default value of onpointerover is always null]
-    expected: FAIL
-
-  [The onpointerover content attribute must be compiled into the onpointerover property]
-    expected: FAIL
-
-  [dispatching a pointerover event must trigger element.onpointerover]
-    expected: NOTRUN
+  prefs: [dom.w3c_pointer_events.enabled:true]
 
-  [The default value of onpointerout is always null]
-    expected: FAIL
-
-  [The onpointerout content attribute must be compiled into the onpointerout property]
-    expected: FAIL
-
-  [dispatching a pointerout event must trigger element.onpointerout]
-    expected: NOTRUN
-
-  [The default value of onpointerenter is always null]
-    expected: FAIL
-
-  [The onpointerenter content attribute must be compiled into the onpointerenter property]
-    expected: FAIL
-
-  [dispatching a pointerenter event must trigger element.onpointerenter]
-    expected: NOTRUN
-
-  [The default value of onpointerleave is always null]
-    expected: FAIL
-
-  [The onpointerleave content attribute must be compiled into the onpointerleave property]
-    expected: FAIL
-
-  [dispatching a pointerleave event must trigger element.onpointerleave]
-    expected: NOTRUN
-
-  [The default value of ongotpointercapture is always null]
-    expected: FAIL
-
-  [The ongotpointercapture content attribute must be compiled into the ongotpointercapture property]
-    expected: FAIL
-
-  [dispatching a gotpointercapture event must trigger element.ongotpointercapture]
-    expected: NOTRUN
-
-  [The default value of onlostpointercapture is always null]
-    expected: FAIL
-
-  [The onlostpointercapture content attribute must be compiled into the onlostpointercapture property]
-    expected: FAIL
-
-  [dispatching a lostpointercapture event must trigger element.onlostpointercapture]
-    expected: NOTRUN
-
--- a/testing/web-platform/mozilla/meta/MANIFEST.json
+++ b/testing/web-platform/mozilla/meta/MANIFEST.json
@@ -8,16 +8,26 @@
      {}
     ]
    ],
    "fetch/api/redirect/redirect-referrer-mixed-content.js": [
     [
      {}
     ]
    ],
+   "html/semantics/scripting-1/the-script-element/support/missing_import.js": [
+    [
+     {}
+    ]
+   ],
+   "html/semantics/scripting-1/the-script-element/support/module.js": [
+    [
+     {}
+    ]
+   ],
    "wasm/js/address.wast.js": [
     [
      {}
     ]
    ],
    "wasm/js/binary.wast.js": [
     [
      {}
@@ -327,36 +337,37 @@
     [
      {}
     ]
    ],
    "wasm/js/unwind.wast.js": [
     [
      {}
     ]
-   ],
-   "html/semantics/scripting-1/the-script-element/support/module.js": [
-    [
-     {}
-    ]
    ]
   },
   "testharness": {
    "fetch/api/redirect/redirect-referrer.https.html": [
     [
      "/_mozilla/fetch/api/redirect/redirect-referrer.https.html",
      {}
     ]
    ],
    "html/semantics/scripting-1/the-script-element/create-module-script.html": [
     [
      "/_mozilla/html/semantics/scripting-1/the-script-element/create-module-script.html",
      {}
     ]
    ],
+   "html/semantics/scripting-1/the-script-element/reload-failed-module-script.html": [
+    [
+     "/_mozilla/html/semantics/scripting-1/the-script-element/reload-failed-module-script.html",
+     {}
+    ]
+   ],
    "html/syntax/parsing/math-parse01.html": [
     [
      "/_mozilla/html/syntax/parsing/math-parse01.html",
      {}
     ]
    ],
    "wasm/address.wast.js.html": [
     [
@@ -729,19 +740,27 @@
    "f9d7ec9cf9fa8c847e45664b05482e3f8c191385",
    "support"
   ],
   "fetch/api/redirect/redirect-referrer.https.html": [
    "99cbd16b78771f35e075e4012d8dbc5dce3209c0",
    "testharness"
   ],
   "html/semantics/scripting-1/the-script-element/create-module-script.html": [
-   "139c1ae0c7dafc27124dbc648925a3911b54292a",
+   "3214bced5d81e8001a321aea4c80675b6603b11d",
    "testharness"
   ],
+  "html/semantics/scripting-1/the-script-element/reload-failed-module-script.html": [
+   "5b8cf6809bd8d0797f4172bf9a01f9085c0abbaf",
+   "testharness"
+  ],
+  "html/semantics/scripting-1/the-script-element/support/missing_import.js": [
+   "52d9e4b6332f11467c1a7d3d18893fdf8ce6ff72",
+   "support"
+  ],
   "html/semantics/scripting-1/the-script-element/support/module.js": [
    "3de5a5a1fb12e698509ac14ba44d7b2b474330f4",
    "support"
   ],
   "html/syntax/parsing/math-parse01.html": [
    "9b670028f08b5fab713e19a443b9505cb9de5fd3",
    "testharness"
   ],
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/meta/html/semantics/scripting-1/the-script-element/reload-failed-module-script.html.ini
@@ -0,0 +1,2 @@
+[reload-failed-module-script.html]
+  prefs: [dom.moduleScripts.enabled:true]
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/html/semantics/scripting-1/the-script-element/reload-failed-module-script.html
@@ -0,0 +1,25 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Insert non-async module script</title>
+<script src=/resources/testharness.js></script>
+<script src=/resources/testharnessreport.js></script>
+<script>
+  setup({allow_uncaught_exception: true});
+  var test = async_test("Reload failed module script")
+  function reloadModule() {
+    var script = document.createElement("script");
+    script.onerror = function() {
+      // An error is expected
+      test.done();
+    };
+    script.onload = function() {
+      test.step(() => assert_unreached("Should not load"));
+      test.done();
+    };
+    script.type = "module";
+    script.src = "support/missing_import.js";
+    script.async = false;
+    document.documentElement.appendChild(script);
+  }
+</script>
+<script type="module" src="support/missing_import.js" onerror="reloadModule()"></script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/mozilla/tests/html/semantics/scripting-1/the-script-element/support/missing_import.js
@@ -0,0 +1,2 @@
+// Import a non-existent export to trigger instantiation failure.
+import {not_found} from "./module.js";
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/clients-matchall-order.https.html
@@ -0,0 +1,426 @@
+<!DOCTYPE html>
+<title>Service Worker: Clients.matchAll ordering</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="resources/test-helpers.sub.js"></script>
+<script>
+
+// Utility function for URLs this test will open.
+function makeURL(name, num, type) {
+  let u = new URL('resources/empty.html', location);
+  u.searchParams.set('name', name);
+  if (num !== undefined) {
+    u.searchParams.set('q', num);
+  }
+  if (type === 'nested') {
+    u.searchParams.set('nested', true);
+  }
+  return u.href;
+}
+
+// Non-test URLs that will be open during each test.  The harness URLs
+// are from the WPT harness.  The "extra" URL is a final window opened
+// by the test.
+const EXTRA_URL = makeURL('extra');
+const TEST_HARNESS_URL = location.href;
+const TOP_HARNESS_URL = new URL('/testharness_runner.html', location).href;
+
+// Utility function to open an iframe in the target parent window.  We
+// can't just use with_iframe() because it does not support a configurable
+// parent window.
+function openFrame(parentWindow, url) {
+  return new Promise(resolve => {
+    let frame = parentWindow.document.createElement('iframe');
+    frame.src = url;
+    parentWindow.document.body.appendChild(frame);
+
+    frame.contentWindow.addEventListener('load', evt => {
+      resolve(frame);
+    }, { once: true });
+  });
+}
+
+// Utility function to open a window and wait for it to load.  The
+// window may optionally have a nested iframe as well.  Returns
+// a result like `{ top: <frame ref> nested: <nested frame ref if present> }`.
+function openFrameConfig(opts) {
+  let url = new URL(opts.url, location.href);
+  return openFrame(window, url.href).then(top => {
+    if (!opts.withNested) {
+      return { top: top };
+    }
+
+    url.searchParams.set('nested', true);
+    return openFrame(top.contentWindow, url.href).then(nested => {
+      return { top: top, nested: nested };
+    });
+  });
+}
+
+// Utility function that takes a list of configurations and opens the
+// corresponding windows in sequence.  An array of results is returned.
+function openFrameConfigList(optList) {
+  let resultList = [];
+  function openNextWindow(optList, nextWindow) {
+    if (nextWindow >= optList.length) {
+      return resultList;
+    }
+    return openFrameConfig(optList[nextWindow]).then(result => {
+      resultList.push(result);
+      return openNextWindow(optList, nextWindow + 1);
+    });
+  }
+  return openNextWindow(optList, 0);
+}
+
+// Utility function that focuses the given entry in window result list.
+function executeFocus(frameResultList, opts) {
+  return new Promise(resolve => {
+    let w = frameResultList[opts.index][opts.type];
+    let target = w.contentWindow ? w.contentWindow : w;
+    target.addEventListener('focus', evt => {
+      resolve();
+    }, { once: true });
+    target.focus();
+  });
+}
+
+// Utility function that performs a list of focus commands in sequence
+// based on the window result list.
+function executeFocusList(frameResultList, optList) {
+  function executeNextCommand(frameResultList, optList, nextCommand) {
+    if (nextCommand >= optList.length) {
+      return;
+    }
+    return executeFocus(frameResultList, optList[nextCommand]).then(_ => {
+      return executeNextCommand(frameResultList, optList, nextCommand + 1);
+    });
+  }
+  return executeNextCommand(frameResultList, optList, 0);
+}
+
+// Perform a `clients.matchAll()` in the service worker with the given
+// options dictionary.
+function doMatchAll(worker, options) {
+  return new Promise(resolve => {
+    let channel = new MessageChannel();
+    channel.port1.onmessage = evt => {
+      resolve(evt.data);
+    };
+    worker.postMessage({ port: channel.port2, options: options, disableSort: true },
+                       [channel.port2]);
+  });
+}
+
+// Function that performs a single test case.  It takes a configuration object
+// describing the windows to open, how to focus them, the matchAll options,
+// and the resulting expectations.  See the test cases for examples of how to
+// use this.
+function matchAllOrderTest(t, opts) {
+  let script = 'resources/clients-matchall-worker.js';
+  let worker;
+  let frameResultList;
+  let extraWindowResult;
+  return service_worker_unregister_and_register(t, script, opts.scope).then(swr => {
+    worker = swr.installing;
+    return wait_for_state(t, worker, 'activated');
+  }).then(_ => {
+    return openFrameConfigList(opts.frameConfigList);
+  }).then(results => {
+    frameResultList = results;
+    return openFrameConfig({ url: EXTRA_URL });
+  }).then(result => {
+    extraWindowResult = result;
+    return executeFocusList(frameResultList, opts.focusConfigList);
+  }).then(_ => {
+    return doMatchAll(worker, opts.matchAllOptions);
+  }).then(data => {
+    assert_equals(data.length, opts.expected.length);
+    for (let i = 0; i < data.length; ++i) {
+      assert_equals(data[i][2], opts.expected[i], 'expected URL index ' + i);
+    }
+  }).then(_ => {
+    frameResultList.forEach(result => result.top.remove());
+    extraWindowResult.top.remove();
+  }).then(_ => {
+    return service_worker_unregister_and_done(t, opts.scope);
+  }).catch(e => {
+    if (frameResultList) {
+      frameResultList.forEach(result => result.top.remove());
+    }
+    if (extraWindowResult) {
+      extraWindowResult.top.remove();
+    }
+    throw(e);
+  });
+}
+
+// ----------
+// Test cases
+// ----------
+
+promise_test(t => {
+  let name = 'no-focus-controlled-windows';
+  let opts = {
+    scope: makeURL(name),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: false },
+      { url: makeURL(name, 1), withNested: false },
+      { url: makeURL(name, 2), withNested: false },
+    ],
+
+    focusConfigList: [
+      // no focus commands
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: false
+    },
+
+    expected: [
+      makeURL(name, 0),
+      makeURL(name, 1),
+      makeURL(name, 2),
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns non-focused controlled windows in creation order.');
+
+promise_test(t => {
+  let name = 'focus-controlled-windows-1';
+  let opts = {
+    scope: makeURL(name),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: false },
+      { url: makeURL(name, 1), withNested: false },
+      { url: makeURL(name, 2), withNested: false },
+    ],
+
+    focusConfigList: [
+      { index: 0, type: 'top' },
+      { index: 1, type: 'top' },
+      { index: 2, type: 'top' },
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: false
+    },
+
+    expected: [
+      makeURL(name, 2),
+      makeURL(name, 1),
+      makeURL(name, 0),
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns controlled windows in focus order.  Case 1.');
+
+promise_test(t => {
+  let name = 'focus-controlled-windows-2';
+  let opts = {
+    scope: makeURL(name),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: false },
+      { url: makeURL(name, 1), withNested: false },
+      { url: makeURL(name, 2), withNested: false },
+    ],
+
+    focusConfigList: [
+      { index: 2, type: 'top' },
+      { index: 1, type: 'top' },
+      { index: 0, type: 'top' },
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: false
+    },
+
+    expected: [
+      makeURL(name, 0),
+      makeURL(name, 1),
+      makeURL(name, 2),
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns controlled windows in focus order.  Case 2.');
+
+promise_test(t => {
+  let name = 'no-focus-uncontrolled-windows';
+  let opts = {
+    scope: makeURL(name + '-outofscope'),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: false },
+      { url: makeURL(name, 1), withNested: false },
+      { url: makeURL(name, 2), withNested: false },
+    ],
+
+    focusConfigList: [
+      // no focus commands
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: true
+    },
+
+    expected: [
+      // The harness windows have been focused, so appear first
+      TEST_HARNESS_URL,
+      TOP_HARNESS_URL,
+
+      // Test frames have not been focused, so appear in creation order
+      makeURL(name, 0),
+      makeURL(name, 1),
+      makeURL(name, 2),
+      EXTRA_URL,
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns non-focused uncontrolled windows in creation order.');
+
+promise_test(t => {
+  let name = 'focus-uncontrolled-windows-1';
+  let opts = {
+    scope: makeURL(name + '-outofscope'),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: false },
+      { url: makeURL(name, 1), withNested: false },
+      { url: makeURL(name, 2), withNested: false },
+    ],
+
+    focusConfigList: [
+      { index: 0, type: 'top' },
+      { index: 1, type: 'top' },
+      { index: 2, type: 'top' },
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: true
+    },
+
+    expected: [
+      // The test harness window is a parent of all test frames.  It will
+      // always have the same focus time or later as its frames.  So it
+      // appears first.
+      TEST_HARNESS_URL,
+
+      makeURL(name, 2),
+      makeURL(name, 1),
+      makeURL(name, 0),
+
+      // The overall harness has been focused
+      TOP_HARNESS_URL,
+
+      // The extra frame was never focused
+      EXTRA_URL,
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns uncontrolled windows in focus order.  Case 1.');
+
+promise_test(t => {
+  let name = 'focus-uncontrolled-windows-2';
+  let opts = {
+    scope: makeURL(name + '-outofscope'),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: false },
+      { url: makeURL(name, 1), withNested: false },
+      { url: makeURL(name, 2), withNested: false },
+    ],
+
+    focusConfigList: [
+      { index: 2, type: 'top' },
+      { index: 1, type: 'top' },
+      { index: 0, type: 'top' },
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: true
+    },
+
+    expected: [
+      // The test harness window is a parent of all test frames.  It will
+      // always have the same focus time or later as its frames.  So it
+      // appears first.
+      TEST_HARNESS_URL,
+
+      makeURL(name, 0),
+      makeURL(name, 1),
+      makeURL(name, 2),
+
+      // The overall harness has been focused
+      TOP_HARNESS_URL,
+
+      // The extra frame was never focused
+      EXTRA_URL,
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns uncontrolled windows in focus order.  Case 2.');
+
+promise_test(t => {
+  let name = 'focus-controlled-nested-windows';
+  let opts = {
+    scope: makeURL(name),
+
+    frameConfigList: [
+      { url: makeURL(name, 0), withNested: true },
+      { url: makeURL(name, 1), withNested: true },
+      { url: makeURL(name, 2), withNested: true },
+    ],
+
+    focusConfigList: [
+      { index: 0, type: 'top' },
+
+      // Note, some browsers don't let programmatic focus of a frame unless
+      // an ancestor window is already focused.  So focus the window and
+      // then the frame.
+      { index: 1, type: 'top' },
+      { index: 1, type: 'nested' },
+
+      { index: 2, type: 'top' },
+    ],
+
+    matchAllOptions: {
+      includeUncontrolled: false
+    },
+
+    expected: [
+      // Focus order for window 2, but not its frame.  We only focused
+      // the window.
+      makeURL(name, 2),
+
+      // Window 1 is next via focus order, but the window is always
+      // shown first here.  The window gets its last focus time updated
+      // when the frame is focused.  Since the times match between the
+      // two it falls back to creation order.  The window was created
+      // before the frame.  This behavior is being discussed in:
+      // https://github.com/w3c/ServiceWorker/issues/1080
+      makeURL(name, 1),
+      makeURL(name, 1, 'nested'),
+
+      // Focus order for window 0, but not its frame.  We only focused
+      // the window.
+      makeURL(name, 0),
+
+      // Creation order of the frames since they are not focused by
+      // default when they are created.
+      makeURL(name, 0, 'nested'),
+      makeURL(name, 2, 'nested'),
+    ],
+  };
+
+  return matchAllOrderTest(t, opts);
+}, 'Clients.matchAll() returns controlled windows and frames in focus order.');
+</script>
--- a/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/clients-matchall-worker.js
@@ -14,15 +14,17 @@ self.onmessage = function(e) {
           }
           message.push([client.visibilityState,
                         client.focused,
                         client.url,
                         client.type,
                         frame_type]);
         });
       // Sort by url
-      message.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });
+      if (!e.data.disableSort) {
+        message.sort(function(a, b) { return a[2] > b[2] ? 1 : -1; });
+      }
       port.postMessage(message);
     })
   .catch(e => {
       port.postMessage('clients.matchAll() rejected: ' + e);
     })
 };
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/service-workers/service-worker/resources/empty.html
@@ -0,0 +1,6 @@
+<!doctype html>
+<html>
+<body>
+hello world
+</body>
+</html>
--- a/toolkit/components/find/nsFind.cpp
+++ b/toolkit/components/find/nsFind.cpp
@@ -112,16 +112,23 @@ public:
   virtual nsINode* GetCurrentNode() override;
   virtual bool IsDone() override;
   virtual nsresult PositionAt(nsINode* aCurNode) override;
 
 protected:
   virtual ~nsFindContentIterator() {}
 
 private:
+  static already_AddRefed<nsIDOMRange> CreateRange(nsINode* aNode)
+  {
+    RefPtr<nsRange> range = new nsRange(aNode);
+    range->SetMaySpanAnonymousSubtrees(true);
+    return range.forget();
+  }
+
   nsCOMPtr<nsIContentIterator> mOuterIterator;
   nsCOMPtr<nsIContentIterator> mInnerIterator;
   // Can't use a range here, since we want to represent part of the flattened
   // tree, including native anonymous content.
   nsCOMPtr<nsIDOMNode> mStartNode;
   int32_t mStartOffset;
   nsCOMPtr<nsIDOMNode> mEndNode;
   int32_t mEndOffset;
@@ -276,17 +283,17 @@ nsFindContentIterator::Reset()
   }
 
   // Note: OK to just set up the outer iterator here; if our range has a native
   // anonymous endpoint we'll end up setting up an inner iterator, and reset the
   // outer one in the process.
   nsCOMPtr<nsINode> node = do_QueryInterface(mStartNode);
   NS_ENSURE_TRUE_VOID(node);
 
-  nsCOMPtr<nsIDOMRange> range = nsFind::CreateRange(node);
+  nsCOMPtr<nsIDOMRange> range = CreateRange(node);
   range->SetStart(mStartNode, mStartOffset);
   range->SetEnd(mEndNode, mEndOffset);
   mOuterIterator->Init(range);
 
   if (!mFindBackward) {
     if (mStartOuterContent != startContent) {
       // the start node was an anonymous text node
       SetupInnerIterator(mStartOuterContent);
@@ -377,18 +384,18 @@ nsFindContentIterator::SetupInnerIterato
   editor->GetFlags(&editorFlags);
   if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask) {
     return;
   }
 
   nsCOMPtr<nsIDOMElement> rootElement;
   editor->GetRootElement(getter_AddRefs(rootElement));
 
-  nsCOMPtr<nsIDOMRange> innerRange = nsFind::CreateRange(aContent);
-  nsCOMPtr<nsIDOMRange> outerRange = nsFind::CreateRange(aContent);
+  nsCOMPtr<nsIDOMRange> innerRange = CreateRange(aContent);
+  nsCOMPtr<nsIDOMRange> outerRange = CreateRange(aContent);
   if (!innerRange || !outerRange) {
     return;
   }
 
   // now create the inner-iterator
   mInnerIterator = do_CreateInstance(kCPreContentIteratorCID);
 
   if (mInnerIterator) {
@@ -1265,17 +1272,17 @@ nsFind::Find(const char16_t* aPatText, n
 
           // If a word break isn't there when it needs to be, reset search.
           if (!mWordBreaker->BreakInBetween(&c, 1, &nextChar, 1)) {
             matchAnchorNode = nullptr;
             continue;
           }
         }
 
-        nsCOMPtr<nsIDOMRange> range = CreateRange(tc);
+        nsCOMPtr<nsIDOMRange> range = new nsRange(tc);
         if (range) {
           int32_t matchStartOffset, matchEndOffset;
           // convert char index to range point:
           int32_t mao = matchAnchorOffset + (mFindBackward ? 1 : 0);
           if (mFindBackward) {
             startParent = do_QueryInterface(tc);
             endParent = matchAnchorNode;
             matchStartOffset = findex;
@@ -1369,17 +1376,8 @@ nsFind::Find(const char16_t* aPatText, n
 
 #endif
   }
 
   // Out of nodes, and didn't match.
   ResetAll();
   return NS_OK;
 }
-
-/* static */
-already_AddRefed<nsIDOMRange>
-nsFind::CreateRange(nsINode* aNode)
-{
-  RefPtr<nsRange> range = new nsRange(aNode);
-  range->SetMaySpanAnonymousSubtrees(true);
-  return range.forget();
-}
--- a/toolkit/components/find/nsFind.h
+++ b/toolkit/components/find/nsFind.h
@@ -29,18 +29,16 @@ class nsFind : public nsIFind
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIFIND
   NS_DECL_CYCLE_COLLECTION_CLASS(nsFind)
 
   nsFind();
 
-  static already_AddRefed<nsIDOMRange> CreateRange(nsINode* aNode);
-
 protected:
   virtual ~nsFind();
 
   // Parameters set from the interface:
   //nsCOMPtr<nsIDOMRange> mRange;   // search only in this range
   bool mFindBackward;
   bool mCaseSensitive;
 
--- a/toolkit/components/find/nsWebBrowserFind.cpp
+++ b/toolkit/components/find/nsWebBrowserFind.cpp
@@ -723,21 +723,21 @@ nsWebBrowserFind::SearchInFrame(nsPIDOMW
 
   // Now make sure the content (for actual finding) and frame (for
   // selection) models are up to date.
   theDoc->FlushPendingNotifications(FlushType::Frames);
 
   nsCOMPtr<nsISelection> sel = GetFrameSelection(aWindow);
   NS_ENSURE_ARG_POINTER(sel);
 
-  nsCOMPtr<nsIDOMRange> searchRange = nsFind::CreateRange(theDoc);
+  nsCOMPtr<nsIDOMRange> searchRange = new nsRange(theDoc);
   NS_ENSURE_ARG_POINTER(searchRange);
-  nsCOMPtr<nsIDOMRange> startPt = nsFind::CreateRange(theDoc);
+  nsCOMPtr<nsIDOMRange> startPt = new nsRange(theDoc);
   NS_ENSURE_ARG_POINTER(startPt);
-  nsCOMPtr<nsIDOMRange> endPt = nsFind::CreateRange(theDoc);
+  nsCOMPtr<nsIDOMRange> endPt = new nsRange(theDoc);
   NS_ENSURE_ARG_POINTER(endPt);
 
   nsCOMPtr<nsIDOMRange> foundRange;
 
   nsCOMPtr<nsIDOMDocument> domDoc = do_QueryInterface(theDoc);
   MOZ_ASSERT(domDoc);
 
   // If !aWrapping, search from selection to end
--- a/toolkit/components/windowwatcher/nsPIWindowWatcher.idl
+++ b/toolkit/components/windowwatcher/nsPIWindowWatcher.idl
@@ -84,25 +84,16 @@ interface nsPIWindowWatcher : nsISupport
                                  in boolean aDialog,
                                  in boolean aNavigate,
                                  in nsISupports aArgs,
                                  in boolean aIsPopupSpam,
                                  in boolean aForceNoOpener,
                                  in nsIDocShellLoadInfo aLoadInfo);
 
   /**
-   * Opens a new window using the most recent non-private browser
-   * window as its parent.
-   *
-   * @return the nsITabParent of the initial browser for the newly opened
-   *         window.
-   */
-  nsITabParent openWindowWithoutParent();
-
-  /**
    * Opens a new window so that the window that aOpeningTab belongs to
    * is set as the parent window. The newly opened window will also
    * inherit load context information from aOpeningTab.
    *
    * @param aOpeningTab
    *        The nsITabParent that is requesting the new window be opened.
    * @param aFeatures
    *        Window features if called with window.open or similar.
--- a/toolkit/components/windowwatcher/nsWindowWatcher.cpp
+++ b/toolkit/components/windowwatcher/nsWindowWatcher.cpp
@@ -469,22 +469,16 @@ CheckUserContextCompatibility(nsIDocShel
   // principal. In this case, we consider everything ok.
   if (nsContentUtils::IsSystemPrincipal(subjectPrincipal)) {
     return true;
   }
 
   return subjectPrincipal->GetUserContextId() == userContextId;
 }
 
-NS_IMETHODIMP
-nsWindowWatcher::OpenWindowWithoutParent(nsITabParent** aResult)
-{
-  return OpenWindowWithTabParent(nullptr, EmptyCString(), true, 1.0f, aResult);
-}
-
 nsresult
 nsWindowWatcher::CreateChromeWindow(const nsACString& aFeatures,
                                     nsIWebBrowserChrome* aParentChrome,
                                     uint32_t aChromeFlags,
                                     nsITabParent* aOpeningTabParent,
                                     mozIDOMWindowProxy* aOpener,
                                     nsIWebBrowserChrome** aResult)
 {
--- a/toolkit/components/workerloader/require.js
+++ b/toolkit/components/workerloader/require.js
@@ -129,20 +129,23 @@
         xhr.send();
 
 
         let source = xhr.responseText;
         if (source == "") {
           // There doesn't seem to be a better way to detect that the file couldn't be found
           throw new Error("Could not find module " + path);
         }
+        // Use `Function` to leave this scope, use `eval` to start the line
+        // number from 1 that is observed by `source` and the error message
+        // thrown from the module, and also use `arguments` for accessing
+        // `source` and `uri` to avoid polluting the module's environment.
         let code = new Function("exports", "require", "module",
-          source + "\n//# sourceURL=" + uri + "\n"
-        );
-        code(exports, require, module);
+          `eval(arguments[3] + "\\n//# sourceURL=" + arguments[4] + "\\n")`);
+        code(exports, require, module, source, uri);
       } catch (ex) {
         // Module loading has failed, exports should not be made available
         // after all.
         modules.delete(path);
         throw ex;
       }
 
       Object.freeze(module.exports);
--- a/toolkit/components/workerloader/tests/moduleE-throws-during-require.js
+++ b/toolkit/components/workerloader/tests/moduleE-throws-during-require.js
@@ -1,12 +1,12 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env commonjs */
 
 // Skip a few lines
-// 5
-// 6
 // 7
 // 8
 // 9
+// 10
+// 11
 throw new Error("Let's see if this error is obtained with the right origin");
--- a/toolkit/components/workerloader/tests/moduleG-throws-later.js
+++ b/toolkit/components/workerloader/tests/moduleG-throws-later.js
@@ -1,14 +1,14 @@
 /* Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/publicdomain/zero/1.0/ */
 
 /* eslint-env commonjs */
 
 // Skip a few lines
-// 5
-// 6
 // 7
 // 8
 // 9
+// 10
+// 11
 exports.doThrow = function doThrow() {
   Array.prototype.sort.apply("foo"); // This will raise a native TypeError
 };
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -393,16 +393,17 @@ NS_EVENT_MESSAGE(eFullscreenChange)
 NS_EVENT_MESSAGE(eFullscreenError)
 NS_EVENT_MESSAGE(eMozFullscreenChange)
 NS_EVENT_MESSAGE(eMozFullscreenError)
 
 NS_EVENT_MESSAGE(eTouchStart)
 NS_EVENT_MESSAGE(eTouchMove)
 NS_EVENT_MESSAGE(eTouchEnd)
 NS_EVENT_MESSAGE(eTouchCancel)
+NS_EVENT_MESSAGE(eTouchPointerCancel)
 
 // Pointerlock DOM API
 NS_EVENT_MESSAGE(ePointerLockChange)
 NS_EVENT_MESSAGE(ePointerLockError)
 NS_EVENT_MESSAGE(eMozPointerLockChange)
 NS_EVENT_MESSAGE(eMozPointerLockError)
 
 // eWheel is the event message of DOM wheel event.
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -434,17 +434,18 @@ WidgetEvent::IsAllowedToDispatchDOMEvent
 
     case eWheelEventClass: {
       // wheel event whose all delta values are zero by user pref applied, it
       // shouldn't cause a DOM event.
       const WidgetWheelEvent* wheelEvent = AsWheelEvent();
       return wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0 ||
              wheelEvent->mDeltaZ != 0.0;
     }
-
+    case eTouchEventClass:
+      return mMessage != eTouchPointerCancel;
     // Following events are handled in EventStateManager, so, we don't need to
     // dispatch DOM event for them into the DOM tree.
     case eQueryContentEventClass:
     case eSelectionEventClass:
     case eContentCommandEventClass:
       return false;
 
     default:
--- a/widget/cocoa/crashtests/crashtests.list
+++ b/widget/cocoa/crashtests/crashtests.list
@@ -1,9 +1,9 @@
-load 373122-1.html
+skip-if(!cocoaWidget) load 373122-1.html # bug 1300017
 load 397209-1.html
 load 403296-1.xhtml
 load 419737-1.html
 load 435223-1.html
 load 444260-1.xul
 load 444864-1.html
 load 449111-1.html
 load 460349-1.xhtml
--- a/xpcom/threads/AbstractThread.h
+++ b/xpcom/threads/AbstractThread.h
@@ -56,20 +56,16 @@ public:
   enum DispatchFailureHandling { AssertDispatchSuccess, DontAssertDispatchSuccess };
   enum DispatchReason { NormalDispatch, TailDispatch };
   virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
                         DispatchFailureHandling aHandling = AssertDispatchSuccess,
                         DispatchReason aReason = NormalDispatch) = 0;
 
   virtual bool IsCurrentThreadIn() = 0;
 
-  // Returns true if dispatch is generally reliable. This is used to guard
-  // against FlushableTaskQueues, which should go away.
-  virtual bool IsDispatchReliable() { return true; }
-
   // Returns a TaskDispatcher that will dispatch its tasks when the currently-
   // running tasks pops off the stack.
   //
   // May only be called when running within the it is invoked up, and only on
   // threads which support it.
   virtual TaskDispatcher& TailDispatcher() = 0;
 
   // Returns true if we have tail tasks scheduled, or if this isn't known.
--- a/xpcom/threads/MozPromise.h
+++ b/xpcom/threads/MozPromise.h
@@ -346,17 +346,19 @@ protected:
       RefPtr<ThenValueBase> mThenValue;
       RefPtr<MozPromise> mPromise;
     };
 
     ThenValueBase(AbstractThread* aResponseTarget,
                   const char* aCallSite)
       : mResponseTarget(aResponseTarget)
       , mCallSite(aCallSite)
-    { }
+    {
+      MOZ_ASSERT(aResponseTarget);
+    }
 
 #ifdef PROMISE_DEBUG
     ~ThenValueBase()
     {
       mMagic1 = 0;
       mMagic2 = 0;
     }
 #endif
@@ -689,18 +691,18 @@ protected:
     Maybe<ResolveRejectFunction> mResolveRejectFunction; // Only accessed and deleted on dispatch thread.
   };
 
 public:
   void ThenInternal(AbstractThread* aResponseThread, ThenValueBase* aThenValue,
                     const char* aCallSite)
   {
     PROMISE_ASSERT(mMagic1 == sMagic && mMagic2 == sMagic && mMagic3 == sMagic && mMagic4 == mMutex.mLock);
+    MOZ_ASSERT(aResponseThread);
     MutexAutoLock lock(mMutex);
-    MOZ_ASSERT(aResponseThread->IsDispatchReliable());
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveRequest);
     mHaveRequest = true;
     PROMISE_LOG("%s invoking Then() [this=%p, aThenValue=%p, isPending=%d]",
                 aCallSite, this, aThenValue, (int) IsPending());
     if (!IsPending()) {
       aThenValue->Dispatch(this);
     } else {
       mThenValues.AppendElement(aThenValue);
@@ -723,17 +725,20 @@ private:
 
     ThenCommand(AbstractThread* aResponseThread,
                 const char* aCallSite,
                 already_AddRefed<ThenValueBase> aThenValue,
                 MozPromise* aReceiver)
       : mResponseThread(aResponseThread)
       , mCallSite(aCallSite)
       , mThenValue(aThenValue)
-      , mReceiver(aReceiver) {}
+      , mReceiver(aReceiver)
+    {
+      MOZ_ASSERT(aResponseThread);
+    }
 
     ThenCommand(ThenCommand&& aOther) = default;
 
   public:
     ~ThenCommand()
     {
       // Issue the request now if the return value of Then() is not used.
       if (mThenValue) {
@@ -1237,25 +1242,26 @@ template<typename... Storages,
          typename PromiseType, typename ThisType, typename... ArgTypes,
          typename... ActualArgTypes>
 static RefPtr<PromiseType>
 InvokeAsyncImpl(AbstractThread* aTarget, ThisType* aThisVal,
                 const char* aCallerName,
                 RefPtr<PromiseType>(ThisType::*aMethod)(ArgTypes...),
                 ActualArgTypes&&... aArgs)
 {
+  MOZ_ASSERT(aTarget);
+
   typedef RefPtr<PromiseType>(ThisType::*MethodType)(ArgTypes...);
   typedef detail::MethodCall<PromiseType, MethodType, ThisType, Storages...> MethodCallType;
   typedef detail::ProxyRunnable<PromiseType, MethodType, ThisType, Storages...> ProxyRunnableType;
 
   MethodCallType* methodCall =
     new MethodCallType(aMethod, aThisVal, Forward<ActualArgTypes>(aArgs)...);
   RefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
   RefPtr<ProxyRunnableType> r = new ProxyRunnableType(p, methodCall);
-  MOZ_ASSERT(aTarget->IsDispatchReliable());
   aTarget->Dispatch(r.forget());
   return p.forget();
 }
 
 constexpr bool Any()
 {
   return false;
 }
@@ -1358,24 +1364,24 @@ static auto
 InvokeAsync(AbstractThread* aTarget, const char* aCallerName,
             AllowInvokeAsyncFunctionLVRef, Function&& aFunction)
   -> decltype(aFunction())
 {
   static_assert(IsRefcountedSmartPointer<decltype(aFunction())>::value
                 && IsMozPromise<typename RemoveSmartPointer<
                                            decltype(aFunction())>::Type>::value,
                 "Function object must return RefPtr<MozPromise>");
+  MOZ_ASSERT(aTarget);
   typedef typename RemoveSmartPointer<decltype(aFunction())>::Type PromiseType;
   typedef detail::ProxyFunctionRunnable<Function, PromiseType> ProxyRunnableType;
 
   RefPtr<typename PromiseType::Private> p =
     new (typename PromiseType::Private)(aCallerName);
   RefPtr<ProxyRunnableType> r =
     new ProxyRunnableType(p, Forward<Function>(aFunction));
-  MOZ_ASSERT(aTarget->IsDispatchReliable());
   aTarget->Dispatch(r.forget());
   return p.forget();
 }
 
 } // namespace detail
 
 // Invoke a function object (e.g., lambda) asynchronously.
 // Return a promise that the function should eventually resolve or reject.