merge mozilla-inbound to mozilla-central a=merge
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Wed, 01 Mar 2017 11:40:59 +0100
changeset 345277 34c6c2f302e7b48e3ad2cec575cbd34d423a9d32
parent 345213 e1b741382dc0262efb123c0188037264ae887d48 (current diff)
parent 345276 81c5898574dcd877c0d42b5554ee3f941d6ba55e (diff)
child 345278 11472ba7397aec9f70677c763657149ea74f3ab9
child 345319 33c9d4c02376826733a4a35687e1e8be21b58f4d
child 345397 49292318198c59a0e53f0af3f9959c8cf4c9dee0
child 345792 9059ef02d91ba49bb3cf3b9cdd4c92bc99a4d4b0
push id31434
push usercbook@mozilla.com
push dateWed, 01 Mar 2017 10:41:13 +0000
treeherdermozilla-central@34c6c2f302e7 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone54.0a1
first release with
nightly linux32
34c6c2f302e7 / 54.0a1 / 20170301110155 / files
nightly linux64
34c6c2f302e7 / 54.0a1 / 20170301110155 / files
nightly mac
34c6c2f302e7 / 54.0a1 / 20170301030202 / files
nightly win32
34c6c2f302e7 / 54.0a1 / 20170301030202 / files
nightly win64
34c6c2f302e7 / 54.0a1 / 20170301030202 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
merge mozilla-inbound to mozilla-central a=merge
security/nss/fuzz/hash.options
security/nss/fuzz/hash_target.cc
taskcluster/taskgraph/util/scriptworker.py
testing/web-platform/meta/fetch/api/policies/referrer-origin-service-worker.https.html.ini
xpcom/threads/MozPromise.h
--- 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
@@ -782,20 +782,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.
@@ -3584,16 +3589,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;
   }
 
@@ -3623,16 +3633,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.