Merge m-i to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 13 Sep 2015 11:58:18 -0700
changeset 262240 9ed17db42e3e
parent 262206 3ea2f8d4e8b6 (current diff)
parent 262239 b9783ad77d9d (diff)
child 262241 2535776a1f22
child 262248 b185d4bce7d9
child 262254 503da58c2e6c
push id29366
push userphilringnalda@gmail.com
push date2015-09-13 18:58 +0000
treeherdermozilla-central@9ed17db42e3e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone43.0a1
first release with
nightly linux32
9ed17db42e3e / 43.0a1 / 20150914030205 / files
nightly linux64
9ed17db42e3e / 43.0a1 / 20150914030205 / files
nightly mac
9ed17db42e3e / 43.0a1 / 20150914030205 / files
nightly win32
9ed17db42e3e / 43.0a1 / 20150914030205 / files
nightly win64
9ed17db42e3e / 43.0a1 / 20150914030205 / files
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
releases
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge m-i to m-c, a=merge
dom/security/nsCORSListenerProxy.cpp
dom/security/nsCORSListenerProxy.h
testing/marionette/update-smoketests/flash-template.sh
testing/marionette/update-smoketests/run-smoketests.py
testing/marionette/update-smoketests/smoketest.py
testing/marionette/update-smoketests/stage-update.py
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -27,17 +27,17 @@
 
 #filter substitution
 
 #ifdef XP_MACOSX
 ; Mac bundle stuff
 @APPNAME@/Contents/Info.plist
 @APPNAME@/Contents/PkgInfo
 @APPNAME@/Contents/Plug-Ins/
-@RESPATH@/b2g.icns
+@RESPATH@/@MOZ_APP_NAME@.icns
 @RESPATH@/@LPROJ_ROOT@.lproj/*
 #endif
 
 [@AB_CD@]
 @RESPATH@/chrome/@AB_CD@@JAREXT@
 @RESPATH@/chrome/@AB_CD@.manifest
 @RESPATH@/@PREF_DIR@/b2g-l10n.js
 @RESPATH@/searchplugins/*
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1742,16 +1742,19 @@ pref("social.shareDirectory", "https://a
 pref("dom.identity.enabled", false);
 
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
 // 1 = allow MITM for certificate pinning checks.
 pref("security.cert_pinning.enforcement_level", 1);
 
+// 2 = allow SHA-1 only before 2016-01-01
+pref("security.pki.sha1_enforcement_level", 2);
+
 // Required blocklist freshness for OneCRL OCSP bypass
 // (default is 1.25x extensions.blocklist.interval, or 30 hours)
 pref("security.onecrl.maximum_staleness_in_seconds", 108000);
 
 // Override the Gecko-default value of false for Firefox.
 pref("plain_text.wrap_long_lines", true);
 
 // If this turns true, Moz*Gesture events are not called stopPropagation()
--- a/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
+++ b/browser/components/privatebrowsing/test/browser/browser_privatebrowsing_popupblocker.js
@@ -47,21 +47,24 @@ add_task(function* test() {
       });
 
     }, false);
 
     aWindow.gBrowser.selectedBrowser.loadURI(testURI);
   }
 
   let win1 = yield BrowserTestUtils.openNewBrowserWindow();
+  yield new Promise(resolve => waitForFocus(resolve, win1));
   yield new Promise(resolve => testPopupBlockerMenuItem(false, win1, resolve));
 
   let win2 = yield BrowserTestUtils.openNewBrowserWindow({private: true});
+  yield new Promise(resolve => waitForFocus(resolve, win2));
   yield new Promise(resolve => testPopupBlockerMenuItem(true, win2, resolve));
 
   let win3 = yield BrowserTestUtils.openNewBrowserWindow();
+  yield new Promise(resolve => waitForFocus(resolve, win3));
   yield new Promise(resolve => testPopupBlockerMenuItem(false, win3, resolve));
 
   // Cleanup
   yield BrowserTestUtils.closeWindow(win1);
   yield BrowserTestUtils.closeWindow(win2);
   yield BrowserTestUtils.closeWindow(win3);
 });
--- a/browser/components/search/test/browser_private_search_perwindowpb.js
+++ b/browser/components/search/test/browser_private_search_perwindowpb.js
@@ -52,17 +52,19 @@ function test() {
       }
     };
     Services.search.addEngine(engineURL + "426329.xml",
                               Ci.nsISearchEngine.DATA_XML,
                               "data:image/x-icon,%00", false, installCallback);
   }
 
   function testOnWindow(aIsPrivate, aCallback) {
-    let win = whenNewWindowLoaded({ private: aIsPrivate }, aCallback);
+    let win = whenNewWindowLoaded({ private: aIsPrivate }, function() {
+      waitForFocus(aCallback, win);
+    });
     windowsToClose.push(win);
   }
 
   addEngine(function() {
     testOnWindow(false, function(win) {
       performSearch(win, false, function() {
         testOnWindow(true, function(win) {
           performSearch(win, true, function() {
--- a/build/clang-plugin/clang-plugin.cpp
+++ b/build/clang-plugin/clang-plugin.cpp
@@ -279,16 +279,18 @@ private:
 static CustomTypeAnnotation StackClass =
     CustomTypeAnnotation("moz_stack_class", "stack");
 static CustomTypeAnnotation GlobalClass =
     CustomTypeAnnotation("moz_global_class", "global");
 static CustomTypeAnnotation NonHeapClass =
     CustomTypeAnnotation("moz_nonheap_class", "non-heap");
 static CustomTypeAnnotation HeapClass =
     CustomTypeAnnotation("moz_heap_class", "heap");
+static CustomTypeAnnotation NonTemporaryClass =
+    CustomTypeAnnotation("moz_non_temporary_class", "non-temporary");
 static CustomTypeAnnotation MustUse =
     CustomTypeAnnotation("moz_must_use", "must-use");
 static CustomTypeAnnotation NonMemMovable =
   CustomTypeAnnotation("moz_non_memmovable", "non-memmove()able");
 
 class MozChecker : public ASTConsumer, public RecursiveASTVisitor<MozChecker> {
   DiagnosticsEngine &Diag;
   const CompilerInstance &CI;
@@ -840,16 +842,17 @@ bool isPlacementNew(const CXXNewExpr *Ex
 
 DiagnosticsMatcher::DiagnosticsMatcher() {
   astMatcher.addMatcher(varDecl().bind("node"), &scopeChecker);
   astMatcher.addMatcher(newExpr().bind("node"), &scopeChecker);
   astMatcher.addMatcher(materializeTemporaryExpr().bind("node"), &scopeChecker);
   astMatcher.addMatcher(
       callExpr(callee(functionDecl(heapAllocator()))).bind("node"),
       &scopeChecker);
+  astMatcher.addMatcher(parmVarDecl().bind("parm_vardecl"), &scopeChecker);
 
   astMatcher.addMatcher(
       callExpr(allOf(hasDeclaration(noArithmeticExprInArgs()),
                      anyOf(hasDescendant(
                                binaryOperator(
                                    allOf(binaryArithmeticOperator(),
                                          hasLHS(hasDescendant(declRefExpr())),
                                          hasRHS(hasDescendant(declRefExpr()))))
@@ -968,25 +971,45 @@ DiagnosticsMatcher::DiagnosticsMatcher()
 enum AllocationVariety {
   AV_None,
   AV_Global,
   AV_Automatic,
   AV_Temporary,
   AV_Heap,
 };
 
+// XXX Currently the Decl* in the AutomaticTemporaryMap is unused, but it
+// probably will be used at some point in the future, in order to produce better
+// error messages.
+typedef DenseMap<const MaterializeTemporaryExpr *, const Decl *> AutomaticTemporaryMap;
+AutomaticTemporaryMap AutomaticTemporaries;
+
 void DiagnosticsMatcher::ScopeChecker::run(
     const MatchFinder::MatchResult &Result) {
   DiagnosticsEngine &Diag = Result.Context->getDiagnostics();
 
   // There are a variety of different reasons why something could be allocated
   AllocationVariety Variety = AV_None;
   SourceLocation Loc;
   QualType T;
 
+  if (const ParmVarDecl *D = Result.Nodes.getNodeAs<ParmVarDecl>("parm_vardecl")) {
+    if (const Expr *Default = D->getDefaultArg()) {
+      if (const MaterializeTemporaryExpr *E = dyn_cast<MaterializeTemporaryExpr>(Default)) {
+        // We have just found a ParmVarDecl which has, as its default argument,
+        // a MaterializeTemporaryExpr. We mark that MaterializeTemporaryExpr as
+        // automatic, by adding it to the AutomaticTemporaryMap.
+        // Reporting on this type will occur when the MaterializeTemporaryExpr
+        // is matched against.
+        AutomaticTemporaries[E] = D;
+      }
+    }
+    return;
+  }
+
   // Determine the type of allocation which we detected
   if (const VarDecl *D = Result.Nodes.getNodeAs<VarDecl>("node")) {
     if (D->hasGlobalStorage()) {
       Variety = AV_Global;
     } else {
       Variety = AV_Automatic;
     }
     T = D->getType();
@@ -995,19 +1018,49 @@ void DiagnosticsMatcher::ScopeChecker::r
     // New allocates things on the heap.
     // We don't consider placement new to do anything, as it doesn't actually
     // allocate the storage, and thus gives us no useful information.
     if (!isPlacementNew(E)) {
       Variety = AV_Heap;
       T = E->getAllocatedType();
       Loc = E->getLocStart();
     }
-  } else if (const Expr *E =
+  } else if (const MaterializeTemporaryExpr *E =
                  Result.Nodes.getNodeAs<MaterializeTemporaryExpr>("node")) {
-    Variety = AV_Temporary;
+    // Temporaries can actually have varying storage durations, due to temporary
+    // lifetime extension. We consider the allocation variety of this temporary
+    // to be the same as the allocation variety of its lifetime.
+
+    // XXX We maybe should mark these lifetimes as being due to a temporary
+    // which has had its lifetime extended, to improve the error messages.
+    switch (E->getStorageDuration()) {
+    case SD_FullExpression:
+      {
+        // Check if this temporary is allocated as a default argument!
+        // if it is, we want to pretend that it is automatic.
+        AutomaticTemporaryMap::iterator AutomaticTemporary = AutomaticTemporaries.find(E);
+        if (AutomaticTemporary != AutomaticTemporaries.end()) {
+          Variety = AV_Automatic;
+        } else {
+          Variety = AV_Temporary;
+        }
+      }
+      break;
+    case SD_Automatic:
+      Variety = AV_Automatic;
+      break;
+    case SD_Thread:
+    case SD_Static:
+      Variety = AV_Global;
+      break;
+    case SD_Dynamic:
+      assert(false && "I don't think that this ever should occur...");
+      Variety = AV_Heap;
+      break;
+    }
     T = E->getType().getUnqualifiedType();
     Loc = E->getLocStart();
   } else if (const CallExpr *E = Result.Nodes.getNodeAs<CallExpr>("node")) {
     T = E->getType()->getPointeeType();
     if (!T.isNull()) {
       // This will always allocate on the heap, as the heapAllocator() check
       // was made in the matcher
       Variety = AV_Heap;
@@ -1019,16 +1072,18 @@ void DiagnosticsMatcher::ScopeChecker::r
   unsigned StackID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "variable of type %0 only valid on the stack");
   unsigned GlobalID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "variable of type %0 only valid as global");
   unsigned HeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "variable of type %0 only valid on the heap");
   unsigned NonHeapID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Error, "variable of type %0 is not valid on the heap");
+  unsigned NonTemporaryID = Diag.getDiagnosticIDs()->getCustomDiagID(
+      DiagnosticIDs::Error, "variable of type %0 is not valid in a temporary");
 
   unsigned StackNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Note,
       "value incorrectly allocated in an automatic variable");
   unsigned GlobalNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Note, "value incorrectly allocated in a global variable");
   unsigned HeapNoteID = Diag.getDiagnosticIDs()->getCustomDiagID(
       DiagnosticIDs::Note, "value incorrectly allocated on the heap");
@@ -1048,16 +1103,18 @@ void DiagnosticsMatcher::ScopeChecker::r
   case AV_Automatic:
     GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, StackNoteID);
     HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, StackNoteID);
     break;
 
   case AV_Temporary:
     GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, TemporaryNoteID);
     HeapClass.reportErrorIfPresent(Diag, T, Loc, HeapID, TemporaryNoteID);
+    NonTemporaryClass.reportErrorIfPresent(Diag, T, Loc,
+                                           NonTemporaryID, TemporaryNoteID);
     break;
 
   case AV_Heap:
     GlobalClass.reportErrorIfPresent(Diag, T, Loc, GlobalID, HeapNoteID);
     StackClass.reportErrorIfPresent(Diag, T, Loc, StackID, HeapNoteID);
     NonHeapClass.reportErrorIfPresent(Diag, T, Loc, NonHeapID, HeapNoteID);
     break;
   }
new file mode 100644
--- /dev/null
+++ b/build/clang-plugin/tests/TestNonTemporaryClass.cpp
@@ -0,0 +1,70 @@
+#define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class")))
+#define MOZ_IMPLICIT __attribute__((annotate("moz_implicit")))
+
+#include <stddef.h>
+
+struct MOZ_NON_TEMPORARY_CLASS NonTemporary {
+  int i;
+  NonTemporary() {}
+  MOZ_IMPLICIT NonTemporary(int i) {}
+  NonTemporary(int i, int j) {}
+  void *operator new(size_t x) throw() { return 0; }
+  void *operator new(size_t blah, char *buffer) { return buffer; }
+};
+
+template <class T>
+struct MOZ_NON_TEMPORARY_CLASS TemplateClass {
+  T i;
+};
+
+void gobble(void *) { }
+
+void gobbleref(const NonTemporary&) { }
+
+template <class T>
+void gobbleanyref(const T&) { }
+
+void misuseNonTemporaryClass(int len) {
+  NonTemporary invalid;
+  NonTemporary alsoInvalid[2];
+  static NonTemporary invalidStatic;
+  static NonTemporary alsoInvalidStatic[2];
+
+  gobble(&invalid);
+  gobble(&invalidStatic);
+  gobble(&alsoInvalid[0]);
+
+  gobbleref(NonTemporary()); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(NonTemporary(10, 20)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(NonTemporary(10)); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleref(10); // expected-error {{variable of type 'NonTemporary' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleanyref(TemplateClass<int>()); // expected-error {{variable of type 'TemplateClass<int>' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+
+  gobble(new NonTemporary);
+  gobble(new NonTemporary[10]);
+  gobble(new TemplateClass<int>);
+  gobble(len <= 5 ? &invalid : new NonTemporary);
+
+  char buffer[sizeof(NonTemporary)];
+  gobble(new (buffer) NonTemporary);
+}
+
+void defaultArg(const NonTemporary& arg = NonTemporary()) {
+}
+
+NonTemporary invalidStatic;
+struct RandomClass {
+  NonTemporary nonstaticMember; // expected-note {{'RandomClass' is a non-temporary type because member 'nonstaticMember' is a non-temporary type 'NonTemporary'}}
+  static NonTemporary staticMember;
+};
+struct MOZ_NON_TEMPORARY_CLASS RandomNonTemporaryClass {
+  NonTemporary nonstaticMember;
+  static NonTemporary staticMember;
+};
+
+struct BadInherit : NonTemporary {}; // expected-note {{'BadInherit' is a non-temporary type because it inherits from a non-temporary type 'NonTemporary'}}
+
+void useStuffWrongly() {
+  gobbleanyref(BadInherit()); // expected-error {{variable of type 'BadInherit' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+  gobbleanyref(RandomClass()); // expected-error {{variable of type 'RandomClass' is not valid in a temporary}} expected-note {{value incorrectly allocated in a temporary}}
+}
--- a/build/clang-plugin/tests/moz.build
+++ b/build/clang-plugin/tests/moz.build
@@ -19,15 +19,16 @@ SOURCES += [
     'TestNeedsNoVTableType.cpp',
     'TestNoAddRefReleaseOnReturn.cpp',
     'TestNoArithmeticExprInArgument.cpp',
     'TestNoAutoType.cpp',
     'TestNoDuplicateRefCntMember.cpp',
     'TestNoExplicitMoveConstructor.cpp',
     'TestNonHeapClass.cpp',
     'TestNonMemMovable.cpp',
+    'TestNonTemporaryClass.cpp',
     'TestNoRefcountedInsideLambdas.cpp',
     'TestStackClass.cpp',
     'TestTrivialCtorDtor.cpp',
 ]
 
 DISABLE_STL_WRAPPING = True
 NO_VISIBILITY_FLAGS = True
--- a/docshell/base/timeline/AutoGlobalTimelineMarker.h
+++ b/docshell/base/timeline/AutoGlobalTimelineMarker.h
@@ -24,17 +24,17 @@ namespace mozilla {
 // Example usage:
 //
 //     {
 //       AutoGlobalTimelineMarker marker("Cycle Collection");
 //       nsCycleCollector* cc = GetCycleCollector();
 //       cc->Collect();
 //       ...
 //     }
-class MOZ_STACK_CLASS AutoGlobalTimelineMarker
+class MOZ_RAII AutoGlobalTimelineMarker
 {
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
 
   // The name of the marker we are adding.
   const char* mName;
 
 public:
   explicit AutoGlobalTimelineMarker(const char* aName
--- a/docshell/base/timeline/AutoTimelineMarker.h
+++ b/docshell/base/timeline/AutoTimelineMarker.h
@@ -23,17 +23,17 @@ namespace mozilla {
 //
 // Example usage:
 //
 //     {
 //       AutoTimelineMarker marker(mDocShell, "Parse CSS");
 //       nsresult rv = ParseTheCSSFile(mFile);
 //       ...
 //     }
-class MOZ_STACK_CLASS AutoTimelineMarker
+class MOZ_RAII AutoTimelineMarker
 {
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
 
   // The name of the marker we are adding.
   const char* mName;
 
   // The docshell that is associated with this marker.
   nsRefPtr<nsDocShell> mDocShell;
--- a/dom/base/EventSource.cpp
+++ b/dom/base/EventSource.cpp
@@ -30,17 +30,16 @@
 #include "nsJSUtils.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIScriptError.h"
 #include "mozilla/dom/EncodingUtils.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsContentUtils.h"
 #include "mozilla/Preferences.h"
 #include "xpcpublic.h"
-#include "nsCORSListenerProxy.h"
 #include "nsWrapperCacheInlines.h"
 #include "mozilla/Attributes.h"
 #include "nsError.h"
 
 namespace mozilla {
 namespace dom {
 
 #define REPLACEMENT_CHAR     (char16_t)0xFFFD
--- a/dom/base/FragmentOrElement.cpp
+++ b/dom/base/FragmentOrElement.cpp
@@ -809,31 +809,31 @@ nsIContent::PreHandleEvent(EventChainPre
       case eImageAbort:
       case eLoadError:
       case eFormSelect:
       case eFormChange:
       case eLoad:
       case eFormReset:
       case eResize:
       case eScroll:
+      case NS_SELECT_START:
         stopEvent = true;
         break;
       case eUnidentifiedEvent:
         if (aVisitor.mDOMEvent) {
           nsAutoString eventType;
           aVisitor.mDOMEvent->GetType(eventType);
           if (eventType.EqualsLiteral("abort") ||
               eventType.EqualsLiteral("error") ||
               eventType.EqualsLiteral("select") ||
               eventType.EqualsLiteral("change") ||
               eventType.EqualsLiteral("load") ||
               eventType.EqualsLiteral("reset") ||
               eventType.EqualsLiteral("resize") ||
-              eventType.EqualsLiteral("scroll") ||
-              eventType.EqualsLiteral("selectstart")) {
+              eventType.EqualsLiteral("scroll")) {
             stopEvent = true;
           }
         }
         break;
       default:
         break;
     }
 
--- a/dom/base/ImportManager.cpp
+++ b/dom/base/ImportManager.cpp
@@ -5,17 +5,16 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "ImportManager.h"
 
 #include "mozilla/EventListenerManager.h"
 #include "HTMLLinkElement.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
-#include "nsCORSListenerProxy.h"
 #include "nsIChannel.h"
 #include "nsIContentPolicy.h"
 #include "nsIContentSecurityPolicy.h"
 #include "nsIDocument.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMEvent.h"
 #include "nsIPrincipal.h"
 #include "nsIScriptObjectPrincipal.h"
--- a/dom/base/Navigator.cpp
+++ b/dom/base/Navigator.cpp
@@ -63,16 +63,17 @@
 #include "nsIIdleObserver.h"
 #include "nsIPermissionManager.h"
 #include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "nsStringStream.h"
 #include "nsComponentManagerUtils.h"
 #include "nsIStringStream.h"
 #include "nsIHttpChannel.h"
+#include "nsIHttpChannelInternal.h"
 #include "TimeManager.h"
 #include "DeviceStorage.h"
 #include "nsIDOMNavigatorSystemMessages.h"
 #include "nsStreamUtils.h"
 #include "nsIAppsService.h"
 #include "mozIApplication.h"
 #include "WidgetUtils.h"
 
@@ -1317,28 +1318,34 @@ Navigator::SendBeacon(const nsAString& a
       !contentType.Equals(MULTIPART_FORM_DATA) &&
       !contentType.Equals(TEXT_PLAIN)) {
 
     // we need to set the sameOriginChecker as a notificationCallback
     // so we can tell the channel not to follow redirects
     nsCOMPtr<nsIInterfaceRequestor> soc = nsContentUtils::SameOriginChecker();
     channel->SetNotificationCallbacks(soc);
 
-    nsCOMPtr<nsIChannel> preflightChannel;
+    nsCOMPtr<nsIHttpChannelInternal> internalChannel =
+      do_QueryInterface(channel);
+    if (!internalChannel) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return false;
+    }
     nsTArray<nsCString> unsafeHeaders;
     unsafeHeaders.AppendElement(NS_LITERAL_CSTRING("Content-Type"));
-    rv = NS_StartCORSPreflight(channel,
-                               beaconListener,
-                               doc->NodePrincipal(),
-                               true,
-                               unsafeHeaders,
-                               getter_AddRefs(preflightChannel));
-  } else {
-    rv = channel->AsyncOpen2(beaconListener);
+    rv = internalChannel->SetCorsPreflightParameters(unsafeHeaders,
+                                                     true,
+                                                     doc->NodePrincipal());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      aRv.Throw(rv);
+      return false;
+    }
   }
+
+  rv = channel->AsyncOpen2(beaconListener);
   if (NS_FAILED(rv)) {
     aRv.Throw(rv);
     return false;
   }
   // make the beaconListener hold a strong reference to the loadgroup
   // which is released in ::OnStartRequest
   beaconListener->SetLoadGroup(loadGroup);
 
--- a/dom/base/ScriptSettings.h
+++ b/dom/base/ScriptSettings.h
@@ -407,17 +407,17 @@ private:
 
 } // namespace dom
 
 /**
  * Use AutoJSContext when you need a JS context on the stack but don't have one
  * passed as a parameter. AutoJSContext will take care of finding the most
  * appropriate JS context and release it when leaving the stack.
  */
-class MOZ_STACK_CLASS AutoJSContext {
+class MOZ_RAII AutoJSContext {
 public:
   explicit AutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   operator JSContext*() const;
 
 protected:
   explicit AutoJSContext(bool aSafe MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
   // We need this Init() method because we can't use delegating constructor for
@@ -429,17 +429,17 @@ protected:
   dom::AutoJSAPI mJSAPI;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /**
  * Use ThreadsafeAutoJSContext when you want an AutoJSContext but might be
  * running on a worker thread.
  */
-class MOZ_STACK_CLASS ThreadsafeAutoJSContext {
+class MOZ_RAII ThreadsafeAutoJSContext {
 public:
   explicit ThreadsafeAutoJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   operator JSContext*() const;
 
 private:
   JSContext* mCx; // Used on workers.  Null means mainthread.
   Maybe<JSAutoRequest> mRequest; // Used on workers.
   Maybe<AutoJSContext> mAutoJSContext; // Used on main thread.
@@ -447,27 +447,27 @@ private:
 };
 
 /**
  * AutoSafeJSContext is similar to AutoJSContext but will only return the safe
  * JS context. That means it will never call nsContentUtils::GetCurrentJSContext().
  *
  * Note - This is deprecated. Please use AutoJSAPI instead.
  */
-class MOZ_STACK_CLASS AutoSafeJSContext : public AutoJSContext {
+class MOZ_RAII AutoSafeJSContext : public AutoJSContext {
 public:
   explicit AutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
 private:
   JSAutoCompartment mAc;
 };
 
 /**
  * Like AutoSafeJSContext but can be used safely on worker threads.
  */
-class MOZ_STACK_CLASS ThreadsafeAutoSafeJSContext {
+class MOZ_RAII ThreadsafeAutoSafeJSContext {
 public:
   explicit ThreadsafeAutoSafeJSContext(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   operator JSContext*() const;
 
 private:
   JSContext* mCx; // Used on workers.  Null means mainthread.
   Maybe<JSAutoRequest> mRequest; // Used on workers.
   Maybe<AutoSafeJSContext> mAutoSafeJSContext; // Used on main thread.
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2646,17 +2646,17 @@ private:
   static nsString* sAltText;
   static nsString* sModifierSeparator;
 
 #if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
   static bool sDOMWindowDumpEnabled;
 #endif
 };
 
-class MOZ_STACK_CLASS nsAutoScriptBlocker {
+class MOZ_RAII nsAutoScriptBlocker {
 public:
   explicit nsAutoScriptBlocker(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     nsContentUtils::AddScriptBlocker();
   }
   ~nsAutoScriptBlocker() {
     nsContentUtils::RemoveScriptBlocker();
   }
--- a/dom/base/nsFrameLoader.cpp
+++ b/dom/base/nsFrameLoader.cpp
@@ -629,17 +629,17 @@ AllDescendantsOfType(nsIDocShellTreeItem
 
   return true;
 }
 
 /**
  * A class that automatically sets mInShow to false when it goes
  * out of scope.
  */
-class MOZ_STACK_CLASS AutoResetInShow {
+class MOZ_RAII AutoResetInShow {
   private:
     nsFrameLoader* mFrameLoader;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
     explicit AutoResetInShow(nsFrameLoader* aFrameLoader MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mFrameLoader(aFrameLoader)
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@@ -984,17 +984,17 @@ nsFrameLoader::SwapWithOtherRemoteLoader
 
   mInSwap = aOther->mInSwap = false;
 
   unused << mRemoteBrowser->SendSwappedWithOtherRemoteLoader();
   unused << aOther->mRemoteBrowser->SendSwappedWithOtherRemoteLoader();
   return NS_OK;
 }
 
-class MOZ_STACK_CLASS AutoResetInFrameSwap final
+class MOZ_RAII AutoResetInFrameSwap final
 {
 public:
   AutoResetInFrameSwap(nsFrameLoader* aThisFrameLoader,
                        nsFrameLoader* aOtherFrameLoader,
                        nsDocShell* aThisDocShell,
                        nsDocShell* aOtherDocShell,
                        EventTarget* aThisEventTarget,
                        EventTarget* aOtherEventTarget
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -877,16 +877,18 @@ GK_ATOM(onrequestmediaplaystatus, "onreq
 GK_ATOM(onreset, "onreset")
 GK_ATOM(onresuming, "onresuming")
 GK_ATOM(onresize, "onresize")
 GK_ATOM(onrtchange, "onrtchange")
 GK_ATOM(onscanningstatechanged, "onscanningstatechanged")
 GK_ATOM(onscostatuschanged, "onscostatuschanged")
 GK_ATOM(onscroll, "onscroll")
 GK_ATOM(onselect, "onselect")
+GK_ATOM(onselectionchange, "onselectionchange")
+GK_ATOM(onselectstart, "onselectstart")
 GK_ATOM(onsending, "onsending")
 GK_ATOM(onsent, "onsent")
 GK_ATOM(onset, "onset")
 GK_ATOM(onshow, "onshow")
 GK_ATOM(onshutter, "onshutter")
 GK_ATOM(onstatechange, "onstatechange")
 GK_ATOM(onstatuschanged, "onstatuschanged")
 GK_ATOM(onstkcommand, "onstkcommand")
--- a/dom/base/nsRange.cpp
+++ b/dom/base/nsRange.cpp
@@ -26,16 +26,17 @@
 #include "nsGenericDOMDataNode.h"
 #include "nsTextFrame.h"
 #include "nsFontFaceList.h"
 #include "mozilla/dom/DocumentFragment.h"
 #include "mozilla/dom/DocumentType.h"
 #include "mozilla/dom/RangeBinding.h"
 #include "mozilla/dom/DOMRect.h"
 #include "mozilla/dom/ShadowRoot.h"
+#include "mozilla/dom/Selection.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Likely.h"
 #include "nsCSSFrameConstructor.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 JSObject*
@@ -189,16 +190,36 @@ nsRange::~nsRange()
 
   // Maybe we can remove Detach() -- bug 702948.
   Telemetry::Accumulate(Telemetry::DOM_RANGE_DETACHED, mIsDetached);
 
   // we want the side effects (releases and list removals)
   DoSetRange(nullptr, 0, nullptr, 0, nullptr);
 }
 
+nsRange::nsRange(nsINode* aNode)
+  : mRoot(nullptr)
+  , mStartOffset(0)
+  , mEndOffset(0)
+  , mIsPositioned(false)
+  , mIsDetached(false)
+  , mMaySpanAnonymousSubtrees(false)
+  , mIsGenerated(false)
+  , mStartOffsetWasIncremented(false)
+  , mEndOffsetWasIncremented(false)
+  , mEnableGravitationOnElementRemoval(true)
+#ifdef DEBUG
+  , mAssertNextInsertOrAppendIndex(-1)
+  , mAssertNextInsertOrAppendNode(nullptr)
+#endif
+{
+  MOZ_ASSERT(aNode, "range isn't in a document!");
+  mOwner = aNode->OwnerDoc();
+}
+
 /* static */
 nsresult
 nsRange::CreateRange(nsINode* aStartParent, int32_t aStartOffset,
                      nsINode* aEndParent, int32_t aEndOffset,
                      nsRange** aRange)
 {
   nsCOMPtr<nsIDOMNode> startDomNode = do_QueryInterface(aStartParent);
   nsCOMPtr<nsIDOMNode> endDomNode = do_QueryInterface(aEndParent);
@@ -264,23 +285,28 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner);
   tmp->Reset();
+
+  // This needs to be unlinked after Reset() is called, as it controls
+  // the result of IsInSelection() which is used by tmp->Reset().
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection);
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOwner)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStartParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEndParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRoot)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelection)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(nsRange)
   NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 static void MarkDescendants(nsINode* aNode)
@@ -874,34 +900,62 @@ nsRange::DoSetRange(nsINode* aStartN, in
     if (newCommonAncestor != oldCommonAncestor) {
       if (oldCommonAncestor) {
         UnregisterCommonAncestor(oldCommonAncestor);
       }
       if (newCommonAncestor) {
         RegisterCommonAncestor(newCommonAncestor);
       } else {
         NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes");
-        mInSelection = false;
+        mSelection = nullptr;
       }
     }
   }
 
-  // This needs to be the last thing this function does.  See comment
-  // in ParentChainChanged.
+  // This needs to be the last thing this function does, other than notifying
+  // selection listeners. See comment in ParentChainChanged.
   mRoot = aRoot;
+
+  // Notify any selection listeners. This has to occur last because otherwise the world
+  // could be observed by a selection listener while the range was in an invalid state.
+  if (mSelection) {
+    mSelection->NotifySelectionListeners();
+  }
 }
 
 static int32_t
 IndexOf(nsINode* aChild)
 {
   nsINode* parent = aChild->GetParentNode();
 
   return parent ? parent->IndexOf(aChild) : -1;
 }
 
+void
+nsRange::SetSelection(mozilla::dom::Selection* aSelection)
+{
+  if (mSelection == aSelection) {
+    return;
+  }
+  // At least one of aSelection and mSelection must be null
+  // aSelection will be null when we are removing from a selection
+  // and a range can't be in more than one selection at a time,
+  // thus mSelection must be null too.
+  MOZ_ASSERT(!aSelection || !mSelection);
+
+  mSelection = aSelection;
+  nsINode* commonAncestor = GetCommonAncestor();
+  NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
+  if (mSelection) {
+    RegisterCommonAncestor(commonAncestor);
+  } else {
+    UnregisterCommonAncestor(commonAncestor);
+  }
+}
+
 nsINode*
 nsRange::GetCommonAncestor() const
 {
   return mIsPositioned ?
     nsContentUtils::GetCommonAncestor(mStartParent, mEndParent) :
     nullptr;
 }
 
--- a/dom/base/nsRange.h
+++ b/dom/base/nsRange.h
@@ -23,50 +23,32 @@
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 class ErrorResult;
 namespace dom {
 class DocumentFragment;
 class DOMRect;
 class DOMRectList;
+class Selection;
 } // namespace dom
 } // namespace mozilla
 
 class nsRange final : public nsIDOMRange,
                       public nsStubMutationObserver,
                       public nsWrapperCache
 {
   typedef mozilla::ErrorResult ErrorResult;
   typedef mozilla::dom::DOMRect DOMRect;
   typedef mozilla::dom::DOMRectList DOMRectList;
 
   virtual ~nsRange();
 
 public:
-  explicit nsRange(nsINode* aNode)
-    : mRoot(nullptr)
-    , mStartOffset(0)
-    , mEndOffset(0)
-    , mIsPositioned(false)
-    , mIsDetached(false)
-    , mMaySpanAnonymousSubtrees(false)
-    , mInSelection(false)
-    , mIsGenerated(false)
-    , mStartOffsetWasIncremented(false)
-    , mEndOffsetWasIncremented(false)
-    , mEnableGravitationOnElementRemoval(true)
-#ifdef DEBUG
-    , mAssertNextInsertOrAppendIndex(-1)
-    , mAssertNextInsertOrAppendNode(nullptr)
-#endif
-  {
-    MOZ_ASSERT(aNode, "range isn't in a document!");
-    mOwner = aNode->OwnerDoc();
-  }
+  explicit nsRange(nsINode* aNode);
 
   static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset,
                               nsIDOMNode* aEndParent, int32_t aEndOffset,
                               nsRange** aRange);
   static nsresult CreateRange(nsIDOMNode* aStartParent, int32_t aStartOffset,
                               nsIDOMNode* aEndParent, int32_t aEndOffset,
                               nsIDOMRange** aRange);
   static nsresult CreateRange(nsINode* aStartParent, int32_t aStartOffset,
@@ -124,41 +106,28 @@ public:
   }
 
   void SetMaySpanAnonymousSubtrees(bool aMaySpanAnonymousSubtrees)
   {
     mMaySpanAnonymousSubtrees = aMaySpanAnonymousSubtrees;
   }
   
   /**
-   * Return true iff this range is part of at least one Selection object
+   * Return true iff this range is part of a Selection object
    * and isn't detached.
    */
   bool IsInSelection() const
   {
-    return mInSelection;
+    return !!mSelection;
   }
 
   /**
    * Called when the range is added/removed from a Selection.
    */
-  void SetInSelection(bool aInSelection)
-  {
-    if (mInSelection == aInSelection) {
-      return;
-    }
-    mInSelection = aInSelection;
-    nsINode* commonAncestor = GetCommonAncestor();
-    NS_ASSERTION(commonAncestor, "unexpected disconnected nodes");
-    if (mInSelection) {
-      RegisterCommonAncestor(commonAncestor);
-    } else {
-      UnregisterCommonAncestor(commonAncestor);
-    }
-  }
+  void SetSelection(mozilla::dom::Selection* aSelection);
 
   /**
    * Return true if this range was generated.
    * @see SetIsGenerated
    */
   bool IsGenerated() const
   {
     return mIsGenerated;
@@ -344,23 +313,23 @@ protected:
 #endif
     static bool mIsNested;
   };
 
   nsCOMPtr<nsIDocument> mOwner;
   nsCOMPtr<nsINode> mRoot;
   nsCOMPtr<nsINode> mStartParent;
   nsCOMPtr<nsINode> mEndParent;
+  nsRefPtr<mozilla::dom::Selection> mSelection;
   int32_t mStartOffset;
   int32_t mEndOffset;
 
   bool mIsPositioned : 1;
   bool mIsDetached : 1;
   bool mMaySpanAnonymousSubtrees : 1;
-  bool mInSelection : 1;
   bool mIsGenerated : 1;
   bool mStartOffsetWasIncremented : 1;
   bool mEndOffsetWasIncremented : 1;
   bool mEnableGravitationOnElementRemoval : 1;
 #ifdef DEBUG
   int32_t  mAssertNextInsertOrAppendIndex;
   nsINode* mAssertNextInsertOrAppendNode;
 #endif
--- a/dom/base/nsXMLHttpRequest.cpp
+++ b/dom/base/nsXMLHttpRequest.cpp
@@ -481,17 +481,16 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_B
   return tmp->IsBlack();
 NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
 
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(nsXMLHttpRequest,
                                                   nsXHREventTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContext)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannel)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseXML)
-  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCORSPreflightChannel)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mXMLParserStreamListener)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mResponseBlob)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDOMBlob)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNotificationCallbacks)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChannelEventSink)
@@ -503,17 +502,16 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(nsXMLHttpRequest,
                                                 nsXHREventTarget)
   tmp->mResultArrayBuffer = nullptr;
   tmp->mArrayBufferBuilder.reset();
   tmp->mResultJSON.setUndefined();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannel)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseXML)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mCORSPreflightChannel)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mXMLParserStreamListener)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mResponseBlob)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDOMBlob)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNotificationCallbacks)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChannelEventSink)
@@ -1205,19 +1203,16 @@ nsXMLHttpRequest::GetStatusText(nsCStrin
 
 void
 nsXMLHttpRequest::CloseRequestWithError(const nsAString& aType,
                                         const uint32_t aFlag)
 {
   if (mChannel) {
     mChannel->Cancel(NS_BINDING_ABORTED);
   }
-  if (mCORSPreflightChannel) {
-    mCORSPreflightChannel->Cancel(NS_BINDING_ABORTED);
-  }
   if (mTimeoutTimer) {
     mTimeoutTimer->Cancel();
   }
   uint32_t responseLength = mResponseBody.Length();
   ResetResponse();
   mState |= aFlag;
 
   // If we're in the destructor, don't risk dispatching an event.
@@ -2342,17 +2337,16 @@ nsXMLHttpRequest::ChangeStateToDone()
   }
 
   if (mErrorLoad) {
     // By nulling out channel here we make it so that Send() can test
     // for that and throw. Also calling the various status
     // methods/members will not throw.
     // This matches what IE does.
     mChannel = nullptr;
-    mCORSPreflightChannel = nullptr;
   }
 }
 
 static nsresult
 GetRequestBody(nsIDOMDocument* aDoc, nsIInputStream** aResult,
                uint64_t* aContentLength, nsACString& aContentType,
                nsACString& aCharset)
 {
@@ -2886,46 +2880,43 @@ nsXMLHttpRequest::Send(nsIVariant* aVari
   mRequestSentTime = PR_Now();
   StartTimeoutTimer();
 
   // Set up the preflight if needed
   if (mState & XML_HTTP_REQUEST_NEED_AC_PREFLIGHT) {
     // Check to see if this initial OPTIONS request has already been cached
     // in our special Access Control Cache.
 
-    rv = NS_StartCORSPreflight(mChannel, listener,
-                               mPrincipal, withCredentials,
-                               mCORSUnsafeHeaders,
-                               getter_AddRefs(mCORSPreflightChannel));
+    rv = internalHttpChannel->SetCorsPreflightParameters(mCORSUnsafeHeaders,
+                                                         withCredentials, mPrincipal);
     NS_ENSURE_SUCCESS(rv, rv);
   }
-  else {
-    mIsMappedArrayBuffer = false;
-    if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
-        Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
-      nsCOMPtr<nsIURI> uri;
-      nsAutoCString scheme;
-
-      rv = mChannel->GetURI(getter_AddRefs(uri));
-      if (NS_SUCCEEDED(rv)) {
-        uri->GetScheme(scheme);
-        if (scheme.LowerCaseEqualsLiteral("app") ||
-            scheme.LowerCaseEqualsLiteral("jar")) {
-          mIsMappedArrayBuffer = true;
-        }
+
+  mIsMappedArrayBuffer = false;
+  if (mResponseType == XML_HTTP_RESPONSE_TYPE_ARRAYBUFFER &&
+      Preferences::GetBool("dom.mapped_arraybuffer.enabled", false)) {
+    nsCOMPtr<nsIURI> uri;
+    nsAutoCString scheme;
+
+    rv = mChannel->GetURI(getter_AddRefs(uri));
+    if (NS_SUCCEEDED(rv)) {
+      uri->GetScheme(scheme);
+      if (scheme.LowerCaseEqualsLiteral("app") ||
+          scheme.LowerCaseEqualsLiteral("jar")) {
+        mIsMappedArrayBuffer = true;
       }
     }
-    // Start reading from the channel
-    rv = mChannel->AsyncOpen(listener, nullptr);
   }
 
-  if (NS_FAILED(rv)) {
+  // Start reading from the channel
+  rv = mChannel->AsyncOpen(listener, nullptr);
+
+  if (NS_WARN_IF(NS_FAILED(rv))) {
     // Drop our ref to the channel to avoid cycles
     mChannel = nullptr;
-    mCORSPreflightChannel = nullptr;
     return rv;
   }
 
   // Either AsyncOpen was called, or CORS will open the channel later.
   mWaitingForOnStopRequest = true;
 
   // If we're synchronous, spin an event loop here and wait
   if (!(mState & XML_HTTP_REQUEST_ASYNC)) {
@@ -3008,29 +2999,16 @@ nsXMLHttpRequest::SetRequestHeader(const
   NS_ASSERTION(mChannel, "mChannel must be valid if we're OPENED.");
 
   // Step 3
   // Make sure we don't store an invalid header name in mCORSUnsafeHeaders
   if (!NS_IsValidHTTPToken(header)) {
     return NS_ERROR_DOM_SYNTAX_ERR;
   }
 
-  // Check that we haven't already opened the channel. We can't rely on
-  // the channel throwing from mChannel->SetRequestHeader since we might
-  // still be waiting for mCORSPreflightChannel to actually open mChannel
-  if (mCORSPreflightChannel) {
-    bool pending;
-    nsresult rv = mCORSPreflightChannel->IsPending(&pending);
-    NS_ENSURE_SUCCESS(rv, rv);
-    
-    if (pending) {
-      return NS_ERROR_IN_PROGRESS;
-    }
-  }
-
   if (!mChannel)             // open() initializes mChannel, and open()
     return NS_ERROR_FAILURE; // must be called before first setRequestHeader()
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(mChannel);
   if (!httpChannel) {
     return NS_OK;
   }
 
--- a/dom/base/nsXMLHttpRequest.h
+++ b/dom/base/nsXMLHttpRequest.h
@@ -638,17 +638,16 @@ protected:
                 const mozilla::dom::Optional<nsAString>& password);
 
   already_AddRefed<nsXMLHttpRequestXPCOMifier> EnsureXPCOMifier();
 
   nsCOMPtr<nsISupports> mContext;
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<nsIChannel> mChannel;
   nsCOMPtr<nsIDocument> mResponseXML;
-  nsCOMPtr<nsIChannel> mCORSPreflightChannel;
   nsTArray<nsCString> mCORSUnsafeHeaders;
 
   nsCOMPtr<nsIStreamListener> mXMLParserStreamListener;
 
   // used to implement getAllResponseHeaders()
   class nsHeaderVisitor : public nsIHttpHeaderVisitor
   {
   public:
--- a/dom/bindings/BindingUtils.h
+++ b/dom/bindings/BindingUtils.h
@@ -2261,17 +2261,17 @@ template<typename T>
 void DoTraceSequence(JSTracer* trc, InfallibleTArray<T>& seq)
 {
   SequenceTracer<T>::TraceSequence(trc, seq.Elements(),
                                    seq.Elements() + seq.Length());
 }
 
 // Rooter class for sequences; this is what we mostly use in the codegen
 template<typename T>
-class MOZ_STACK_CLASS SequenceRooter : private JS::CustomAutoRooter
+class MOZ_RAII SequenceRooter : private JS::CustomAutoRooter
 {
 public:
   SequenceRooter(JSContext *aCx, FallibleTArray<T>* aSequence
                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
       mFallibleArray(aSequence),
       mSequenceType(eFallibleArray)
   {
@@ -2320,17 +2320,17 @@ public:
     Nullable<nsTArray<T> >* mNullableArray;
   };
 
   SequenceType mSequenceType;
 };
 
 // Rooter class for MozMap; this is what we mostly use in the codegen.
 template<typename T>
-class MOZ_STACK_CLASS MozMapRooter : private JS::CustomAutoRooter
+class MOZ_RAII MozMapRooter : private JS::CustomAutoRooter
 {
 public:
   MozMapRooter(JSContext *aCx, MozMap<T>* aMozMap
                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : JS::CustomAutoRooter(aCx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
       mMozMap(aMozMap),
       mMozMapType(eMozMap)
   {
@@ -2366,18 +2366,18 @@ private:
     MozMap<T>* mMozMap;
     Nullable<MozMap<T>>* mNullableMozMap;
   };
 
   MozMapType mMozMapType;
 };
 
 template<typename T>
-class MOZ_STACK_CLASS RootedUnion : public T,
-                                    private JS::CustomAutoRooter
+class MOZ_RAII RootedUnion : public T,
+                             private JS::CustomAutoRooter
 {
 public:
   explicit RootedUnion(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     T(),
     JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
   {
   }
 
--- a/dom/bindings/RootedDictionary.h
+++ b/dom/bindings/RootedDictionary.h
@@ -10,34 +10,34 @@
 #include "mozilla/GuardObjects.h"
 #include "mozilla/dom/Nullable.h"
 #include "jsapi.h"
 
 namespace mozilla {
 namespace dom {
 
 template<typename T>
-class MOZ_STACK_CLASS RootedDictionary : public T,
+class MOZ_RAII RootedDictionary : public T,
                                          private JS::CustomAutoRooter
 {
 public:
   explicit RootedDictionary(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     T(),
     JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
   {
   }
 
   virtual void trace(JSTracer *trc) override
   {
     this->TraceDictionary(trc);
   }
 };
 
 template<typename T>
-class MOZ_STACK_CLASS NullableRootedDictionary : public Nullable<T>,
+class MOZ_RAII NullableRootedDictionary : public Nullable<T>,
                                                  private JS::CustomAutoRooter
 {
 public:
   explicit NullableRootedDictionary(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     Nullable<T>(),
     JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
   {
   }
--- a/dom/bindings/TypedArray.h
+++ b/dom/bindings/TypedArray.h
@@ -322,17 +322,17 @@ class TypedArrayCreator
     }
 
   private:
     const ArrayType& mArray;
 };
 
 // A class for rooting an existing TypedArray struct
 template<typename ArrayType>
-class MOZ_STACK_CLASS TypedArrayRooter : private JS::CustomAutoRooter
+class MOZ_RAII TypedArrayRooter : private JS::CustomAutoRooter
 {
 public:
   TypedArrayRooter(JSContext* cx,
                    ArrayType* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
     mArray(aArray)
   {
   }
@@ -344,17 +344,17 @@ public:
 
 private:
   TypedArrayObjectStorage* const mArray;
 };
 
 // And a specialization for dealing with nullable typed arrays
 template<typename Inner> struct Nullable;
 template<typename ArrayType>
-class MOZ_STACK_CLASS TypedArrayRooter<Nullable<ArrayType> > :
+class MOZ_RAII TypedArrayRooter<Nullable<ArrayType> > :
     private JS::CustomAutoRooter
 {
 public:
   TypedArrayRooter(JSContext* cx,
                    Nullable<ArrayType>* aArray MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     JS::CustomAutoRooter(cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT),
     mArray(aArray)
   {
@@ -368,18 +368,18 @@ public:
   }
 
 private:
   Nullable<ArrayType>* const mArray;
 };
 
 // Class for easily setting up a rooted typed array object on the stack
 template<typename ArrayType>
-class MOZ_STACK_CLASS RootedTypedArray : public ArrayType,
-                                         private TypedArrayRooter<ArrayType>
+class MOZ_RAII RootedTypedArray : public ArrayType,
+                                  private TypedArrayRooter<ArrayType>
 {
 public:
   explicit RootedTypedArray(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
     ArrayType(),
     TypedArrayRooter<ArrayType>(cx, this
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
   {
   }
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -345,16 +345,20 @@ EVENT(pointerleave,
 EVENT(gotpointercapture,
       ePointerGotCapture,
       EventNameType_All,
       ePointerEventClass)
 EVENT(lostpointercapture,
       ePointerLostCapture,
       EventNameType_All,
       ePointerEventClass)
+EVENT(selectstart,
+      NS_SELECT_START,
+      EventNameType_HTMLXUL,
+      eBasicEventClass)
 
 // Not supported yet; probably never because "wheel" is a better idea.
 // EVENT(mousewheel)
 EVENT(pause,
       ePause,
       EventNameType_HTML,
       eBasicEventClass)
 EVENT(play,
@@ -579,16 +583,20 @@ TOUCH_EVENT(touchcancel,
             NS_TOUCH_CANCEL,
             EventNameType_All,
             eTouchEventClass)
 
 DOCUMENT_ONLY_EVENT(readystatechange,
                     eReadyStateChange,
                     EventNameType_HTMLXUL,
                     eBasicEventClass)
+DOCUMENT_ONLY_EVENT(selectionchange,
+                    NS_SELECTION_CHANGE,
+                    EventNameType_HTMLXUL,
+                    eBasicEventClass)
 
 NON_IDL_EVENT(MozMouseHittest,
               eMouseHitTest,
               EventNameType_None,
               eMouseEventClass)
 
 NON_IDL_EVENT(DOMAttrModified,
               eLegacyAttrModified,
--- a/dom/fetch/FetchDriver.cpp
+++ b/dom/fetch/FetchDriver.cpp
@@ -613,28 +613,29 @@ FetchDriver::HttpFetch(bool aCORSFlag, b
   // If preflight is required, start a "CORS preflight fetch"
   // https://fetch.spec.whatwg.org/#cors-preflight-fetch-0. All the
   // implementation is handled by NS_StartCORSPreflight, we just set up the
   // unsafeHeaders so they can be verified against the response's
   // "Access-Control-Allow-Headers" header.
   if (aCORSPreflightFlag) {
     MOZ_ASSERT(mRequest->Mode() != RequestMode::No_cors,
                "FetchDriver::ContinueFetch() should ensure that the request is not no-cors");
-    nsCOMPtr<nsIChannel> preflightChannel;
+    MOZ_ASSERT(httpChan, "CORS preflight can only be used with HTTP channels");
     nsAutoTArray<nsCString, 5> unsafeHeaders;
     mRequest->Headers()->GetUnsafeHeaders(unsafeHeaders);
 
-    rv = NS_StartCORSPreflight(chan, listener, mPrincipal,
-                               useCredentials,
-                               unsafeHeaders,
-                               getter_AddRefs(preflightChannel));
-  } else {
-    rv = chan->AsyncOpen(listener, nullptr);
+    nsCOMPtr<nsIHttpChannelInternal> internalChan = do_QueryInterface(httpChan);
+    rv = internalChan->SetCorsPreflightParameters(unsafeHeaders, useCredentials, mPrincipal);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return FailWithNetworkError();
+    }
   }
 
+  rv = chan->AsyncOpen(listener, nullptr);
+
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return FailWithNetworkError();
   }
 
   // Step 4 onwards of "HTTP Fetch" is handled internally by Necko.
   return NS_OK;
 }
 
--- a/dom/html/HTMLMediaElement.cpp
+++ b/dom/html/HTMLMediaElement.cpp
@@ -48,17 +48,16 @@
 
 #include "MediaError.h"
 #include "MediaDecoder.h"
 #include "nsICategoryManager.h"
 #include "MediaResource.h"
 
 #include "nsIContentPolicy.h"
 #include "nsContentPolicyUtils.h"
-#include "nsCORSListenerProxy.h"
 #include "nsCycleCollectionParticipant.h"
 #include "nsICachingChannel.h"
 #include "nsLayoutUtils.h"
 #include "nsVideoFrame.h"
 #include "Layers.h"
 #include <limits>
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsMediaFragmentURIParser.h"
--- a/dom/html/nsTextEditorState.cpp
+++ b/dom/html/nsTextEditorState.cpp
@@ -88,16 +88,19 @@ public:
   {
   }
 
   NS_IMETHOD Run() {
     if (!mTextEditorState) {
       return NS_OK;
     }
 
+    AutoHideSelectionChanges hideSelectionChanges
+      (mFrame->GetConstFrameSelection());
+
     if (mFrame) {
       // SetSelectionRange leads to Selection::AddRange which flushes Layout -
       // need to block script to avoid nested PrepareEditor calls (bug 642800).
       nsAutoScriptBlocker scriptBlocker;
        nsTextEditorState::SelectionProperties& properties =
          mTextEditorState->GetSelectionProperties();
        mFrame->SetSelectionRange(properties.mStart,
                                  properties.mEnd,
@@ -1243,16 +1246,18 @@ nsTextEditorState::PrepareEditor(const n
     return NS_OK;
   }
 
   if (mEditorInitialized) {
     // Do not initialize the editor multiple times.
     return NS_OK;
   }
 
+  AutoHideSelectionChanges hideSelectionChanges(GetConstFrameSelection());
+
   // Don't attempt to initialize recursively!
   InitializationGuard guard(*this);
   if (guard.IsInitializingRecursively()) {
     return NS_ERROR_NOT_INITIALIZED;
   }
 
   // Note that we don't check mEditor here, because we might already have one
   // around, in which case we don't create a new one, and we'll just tie the
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -557,47 +557,26 @@ nsresult ChannelMediaResource::OpenChann
 
   if (aStreamListener) {
     *aStreamListener = mListener;
     NS_ADDREF(*aStreamListener);
   } else {
     nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
     NS_ENSURE_SUCCESS(rv, rv);
 
-    nsCOMPtr<nsIStreamListener> listener = mListener.get();
+    rv = SetupChannelHeaders();
+    NS_ENSURE_SUCCESS(rv, rv);
 
-    // Ensure that if we're loading cross domain, that the server is sending
-    // an authorizing Access-Control header.
+    rv = mChannel->AsyncOpen2(mListener);
+    NS_ENSURE_SUCCESS(rv, rv);
+    
+    // Tell the media element that we are fetching data from a channel.
     MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
     NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     dom::HTMLMediaElement* element = owner->GetMediaElement();
-    NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
-    if (element->ShouldCheckAllowOrigin()) {
-      nsRefPtr<nsCORSListenerProxy> crossSiteListener =
-        new nsCORSListenerProxy(mListener,
-                                element->NodePrincipal(),
-                                false);
-      NS_ENSURE_TRUE(crossSiteListener, NS_ERROR_OUT_OF_MEMORY);
-      rv = crossSiteListener->Init(mChannel, DataURIHandling::Allow);
-      NS_ENSURE_SUCCESS(rv, rv);
-      listener = crossSiteListener;
-    } else {
-      rv = nsContentUtils::GetSecurityManager()->
-        CheckLoadURIWithPrincipal(element->NodePrincipal(),
-                                  mURI,
-                                  nsIScriptSecurityManager::STANDARD);
-      NS_ENSURE_SUCCESS(rv, rv);
-    }
-
-    rv = SetupChannelHeaders();
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mChannel->AsyncOpen(listener, nullptr);
-    NS_ENSURE_SUCCESS(rv, rv);
-    // Tell the media element that we are fetching data from a channel.
     element->DownloadResumed(true);
   }
 
   return NS_OK;
 }
 
 nsresult ChannelMediaResource::SetupChannelHeaders()
 {
@@ -864,24 +843,19 @@ ChannelMediaResource::RecreateChannel()
   dom::HTMLMediaElement* element = owner->GetMediaElement();
   if (!element) {
     // The decoder is being shut down, so don't bother opening a new channel
     return NS_OK;
   }
   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
   NS_ENSURE_TRUE(loadGroup, NS_ERROR_NULL_POINTER);
 
-  nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
-  if (nsContentUtils::ChannelShouldInheritPrincipal(element->NodePrincipal(),
-                                                    mURI,
-                                                    false, // aInheritForAboutBlank
-                                                    false // aForceInherit
-                                                    )) {
-    securityFlags = nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
-  }
+  nsSecurityFlags securityFlags = element->ShouldCheckAllowOrigin()
+                                  ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
+                                  : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
 
   MOZ_ASSERT(element->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
   nsContentPolicyType contentPolicyType = element->IsHTMLElement(nsGkAtoms::audio) ?
     nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
   nsresult rv = NS_NewChannel(getter_AddRefs(mChannel),
                               mURI,
                               element,
@@ -1369,30 +1343,26 @@ nsresult FileMediaResource::Open(nsIStre
       NS_ENSURE_SUCCESS(rv, rv);
 
       rv = NS_NewLocalFileInputStream(
         getter_AddRefs(mInput), file, -1, -1, nsIFileInputStream::SHARE_DELETE);
     } else if (IsBlobURI(mURI)) {
       rv = NS_GetStreamForBlobURI(mURI, getter_AddRefs(mInput));
     }
   } else {
-    // Ensure that we never load a local file from some page on a
-    // web server.
-    MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
-    NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
-    dom::HTMLMediaElement* element = owner->GetMediaElement();
-    NS_ENSURE_TRUE(element, NS_ERROR_FAILURE);
 
-    rv = nsContentUtils::GetSecurityManager()->
-           CheckLoadURIWithPrincipal(element->NodePrincipal(),
-                                     mURI,
-                                     nsIScriptSecurityManager::STANDARD);
-    NS_ENSURE_SUCCESS(rv, rv);
-
-    rv = mChannel->Open(getter_AddRefs(mInput));
+#ifdef DEBUG
+    {
+      nsCOMPtr<nsILoadInfo> loadInfo = mChannel->GetLoadInfo();
+      MOZ_ASSERT((loadInfo->GetSecurityMode() &
+                 nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) == 0,
+                 "can not enforce CORS when calling Open2()");
+    }
+#endif
+    rv = mChannel->Open2(getter_AddRefs(mInput));
   }
   NS_ENSURE_SUCCESS(rv, rv);
 
   mSeekable = do_QueryInterface(mInput);
   if (!mSeekable) {
     // XXX The file may just be a .url or similar
     // shortcut that points to a Web site. We need to fix this by
     // doing an async open and waiting until we locate the real resource,
@@ -1446,24 +1416,19 @@ already_AddRefed<MediaResource> FileMedi
   dom::HTMLMediaElement* element = owner->GetMediaElement();
   if (!element) {
     // The decoder is being shut down, so we can't clone
     return nullptr;
   }
   nsCOMPtr<nsILoadGroup> loadGroup = element->GetDocumentLoadGroup();
   NS_ENSURE_TRUE(loadGroup, nullptr);
 
-  nsSecurityFlags securityFlags = nsILoadInfo::SEC_NORMAL;
-  if (nsContentUtils::ChannelShouldInheritPrincipal(element->NodePrincipal(),
-                                                    mURI,
-                                                    false, // aInheritForAboutBlank
-                                                    false // aForceInherit
-                                                    )) {
-    securityFlags = nsILoadInfo::SEC_FORCE_INHERIT_PRINCIPAL;
-  }
+  nsSecurityFlags securityFlags = element->ShouldCheckAllowOrigin()
+                                  ? nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS
+                                  : nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_INHERITS;
 
   MOZ_ASSERT(element->IsAnyOfHTMLElements(nsGkAtoms::audio, nsGkAtoms::video));
   nsContentPolicyType contentPolicyType = element->IsHTMLElement(nsGkAtoms::audio) ?
     nsIContentPolicy::TYPE_INTERNAL_AUDIO : nsIContentPolicy::TYPE_INTERNAL_VIDEO;
 
   nsCOMPtr<nsIChannel> channel;
   nsresult rv =
     NS_NewChannel(getter_AddRefs(channel),
--- a/dom/media/MediaResource.h
+++ b/dom/media/MediaResource.h
@@ -789,17 +789,17 @@ protected:
 
 /**
  * RAII class that handles pinning and unpinning for MediaResource and derived.
  * This should be used when making calculations that involve potentially-cached
  * MediaResource data, so that the state of the world can't change out from under
  * us.
  */
 template<class T>
-class MOZ_STACK_CLASS AutoPinned {
+class MOZ_RAII AutoPinned {
  public:
   explicit AutoPinned(T* aResource MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mResource(aResource) {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mResource);
     mResource->Pin();
   }
 
   ~AutoPinned() {
--- a/dom/plugins/ipc/PluginModuleParent.cpp
+++ b/dom/plugins/ipc/PluginModuleParent.cpp
@@ -301,17 +301,17 @@ public:
     }
 
     static bool
     IsLoadModuleOnStack()
     {
         return sIsLoadModuleOnStack;
     }
 
-    class MOZ_STACK_CLASS NotifyLoadingModule
+    class MOZ_RAII NotifyLoadingModule
     {
     public:
         explicit NotifyLoadingModule(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
         {
             MOZ_GUARD_OBJECT_NOTIFIER_INIT;
             PluginModuleMapping::sIsLoadModuleOnStack = true;
         }
 
--- a/dom/security/moz.build
+++ b/dom/security/moz.build
@@ -13,22 +13,20 @@ EXPORTS.mozilla.dom += [
     'nsCSPUtils.h',
     'nsMixedContentBlocker.h',
     'SRICheck.h',
     'SRIMetadata.h',
 ]
 
 EXPORTS += [
     'nsContentSecurityManager.h',
-    'nsCORSListenerProxy.h'
 ]
 
 UNIFIED_SOURCES += [
     'nsContentSecurityManager.cpp',
-    'nsCORSListenerProxy.cpp',
     'nsCSPContext.cpp',
     'nsCSPParser.cpp',
     'nsCSPService.cpp',
     'nsCSPUtils.cpp',
     'nsMixedContentBlocker.cpp',
     'SRICheck.cpp',
     'SRIMetadata.cpp',
 ]
--- a/dom/security/nsCSPContext.cpp
+++ b/dom/security/nsCSPContext.cpp
@@ -24,22 +24,24 @@
 #include "nsIObjectInputStream.h"
 #include "nsIObjectOutputStream.h"
 #include "nsIObserver.h"
 #include "nsIObserverService.h"
 #include "nsIStringStream.h"
 #include "nsIUploadChannel.h"
 #include "nsIScriptError.h"
 #include "nsIWebNavigation.h"
+#include "nsMimeTypes.h"
 #include "nsNetUtil.h"
 #include "nsNullPrincipal.h"
 #include "nsIContentPolicy.h"
 #include "nsSupportsPrimitives.h"
 #include "nsThreadUtils.h"
 #include "nsString.h"
+#include "nsStringStream.h"
 #include "mozilla/Logging.h"
 #include "mozilla/dom/CSPReportBinding.h"
 #include "mozilla/dom/CSPDictionariesBinding.h"
 #include "mozilla/net/ReferrerPolicy.h"
 #include "nsINetworkInterceptController.h"
 
 using namespace mozilla;
 
--- a/dom/svg/DOMSVGLength.cpp
+++ b/dom/svg/DOMSVGLength.cpp
@@ -61,17 +61,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsIDOMSVGLength)
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeLengthNotifier
 // Stack-based helper class to pair calls to WillChangeLengthList and
 // DidChangeLengthList.
-class MOZ_STACK_CLASS AutoChangeLengthNotifier
+class MOZ_RAII AutoChangeLengthNotifier
 {
 public:
   explicit AutoChangeLengthNotifier(DOMSVGLength* aLength MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mLength(aLength)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mLength, "Expecting non-null length");
     MOZ_ASSERT(mLength->HasOwner(),
--- a/dom/svg/DOMSVGLengthList.cpp
+++ b/dom/svg/DOMSVGLengthList.cpp
@@ -74,17 +74,17 @@ DOMSVGLengthList::WrapObject(JSContext *
 {
   return mozilla::dom::SVGLengthListBinding::Wrap(cx, this, aGivenProto);
 }
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeLengthListNotifier
 // Stack-based helper class to pair calls to WillChangeLengthList and
 // DidChangeLengthList.
-class MOZ_STACK_CLASS AutoChangeLengthListNotifier
+class MOZ_RAII AutoChangeLengthListNotifier
 {
 public:
   explicit AutoChangeLengthListNotifier(DOMSVGLengthList* aLengthList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mLengthList(aLengthList)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mLengthList, "Expecting non-null lengthList");
     mEmptyOrOldValue =
--- a/dom/svg/DOMSVGNumber.cpp
+++ b/dom/svg/DOMSVGNumber.cpp
@@ -48,17 +48,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeNumberNotifier
 // Stack-based helper class to pair calls to WillChangeNumberList and
 // DidChangeNumberList.
-class MOZ_STACK_CLASS AutoChangeNumberNotifier
+class MOZ_RAII AutoChangeNumberNotifier
 {
 public:
   explicit AutoChangeNumberNotifier(DOMSVGNumber* aNumber MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mNumber(aNumber)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mNumber, "Expecting non-null number");
     MOZ_ASSERT(mNumber->HasOwner(),
--- a/dom/svg/DOMSVGNumberList.cpp
+++ b/dom/svg/DOMSVGNumberList.cpp
@@ -75,17 +75,17 @@ DOMSVGNumberList::WrapObject(JSContext *
 {
   return mozilla::dom::SVGNumberListBinding::Wrap(cx, this, aGivenProto);
 }
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeNumberListNotifier
 // Stack-based helper class to pair calls to WillChangeNumberList and
 // DidChangeNumberList.
-class MOZ_STACK_CLASS AutoChangeNumberListNotifier
+class MOZ_RAII AutoChangeNumberListNotifier
 {
 public:
   explicit AutoChangeNumberListNotifier(DOMSVGNumberList* aNumberList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mNumberList(aNumberList)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mNumberList, "Expecting non-null numberList");
     mEmptyOrOldValue =
--- a/dom/svg/DOMSVGPathSeg.cpp
+++ b/dom/svg/DOMSVGPathSeg.cpp
@@ -40,17 +40,17 @@ NS_IMPL_CYCLE_COLLECTION_TRACE_END
 
 NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(DOMSVGPathSeg, AddRef)
 NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(DOMSVGPathSeg, Release)
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangePathSegNotifier
 // Stack-based helper class to pair calls to WillChangePathSegList
 // and DidChangePathSegList.
-class MOZ_STACK_CLASS AutoChangePathSegNotifier
+class MOZ_RAII AutoChangePathSegNotifier
 {
 public:
   explicit AutoChangePathSegNotifier(DOMSVGPathSeg* aPathSeg MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mPathSeg(aPathSeg)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mPathSeg, "Expecting non-null pathSeg");
     MOZ_ASSERT(mPathSeg->HasOwner(),
--- a/dom/svg/DOMSVGPathSegList.cpp
+++ b/dom/svg/DOMSVGPathSegList.cpp
@@ -50,17 +50,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangePathSegListNotifier
 // Stack-based helper class to pair calls to WillChangePathSegList and
 // DidChangePathSegList.
-class MOZ_STACK_CLASS AutoChangePathSegListNotifier
+class MOZ_RAII AutoChangePathSegListNotifier
 {
 public:
   explicit AutoChangePathSegListNotifier(DOMSVGPathSegList* aPathSegList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mPathSegList(aPathSegList)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mPathSegList, "Expecting non-null pathSegList");
     mEmptyOrOldValue =
--- a/dom/svg/DOMSVGPoint.cpp
+++ b/dom/svg/DOMSVGPoint.cpp
@@ -18,17 +18,17 @@ using namespace mozilla;
 using namespace mozilla::gfx;
 
 namespace mozilla {
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangePointNotifier
 // Stack-based helper class to pair calls to WillChangePointList and
 // DidChangePointList.
-class MOZ_STACK_CLASS AutoChangePointNotifier
+class MOZ_RAII AutoChangePointNotifier
 {
 public:
   explicit AutoChangePointNotifier(DOMSVGPoint* aPoint MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mPoint(aPoint)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mPoint, "Expecting non-null point");
     MOZ_ASSERT(mPoint->HasOwner(),
--- a/dom/svg/DOMSVGPointList.cpp
+++ b/dom/svg/DOMSVGPointList.cpp
@@ -68,17 +68,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangePointListNotifier
 // Stack-based helper class to pair calls to WillChangePointList and
 // DidChangePointList.
-class MOZ_STACK_CLASS AutoChangePointListNotifier
+class MOZ_RAII AutoChangePointListNotifier
 {
 public:
   explicit AutoChangePointListNotifier(DOMSVGPointList* aPointList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mPointList(aPointList)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mPointList, "Expecting non-null pointList");
     mEmptyOrOldValue =
--- a/dom/svg/DOMSVGStringList.cpp
+++ b/dom/svg/DOMSVGStringList.cpp
@@ -38,17 +38,17 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
   NS_INTERFACE_MAP_ENTRY(nsISupports)
 NS_INTERFACE_MAP_END
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeStringListNotifier
 // Stack-based helper class to pair calls to WillChangeStringListList and
 // DidChangeStringListList.
-class MOZ_STACK_CLASS AutoChangeStringListNotifier
+class MOZ_RAII AutoChangeStringListNotifier
 {
 public:
   explicit AutoChangeStringListNotifier(DOMSVGStringList* aStringList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mStringList(aStringList)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mStringList, "Expecting non-null stringList");
     mEmptyOrOldValue =
--- a/dom/svg/DOMSVGTransformList.cpp
+++ b/dom/svg/DOMSVGTransformList.cpp
@@ -76,17 +76,17 @@ DOMSVGTransformList::WrapObject(JSContex
 {
   return mozilla::dom::SVGTransformListBinding::Wrap(cx, this, aGivenProto);
 }
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeTransformListNotifier
 // Stack-based helper class to pair calls to WillChangeTransformList and
 // DidChangeTransformList.
-class MOZ_STACK_CLASS AutoChangeTransformListNotifier
+class MOZ_RAII AutoChangeTransformListNotifier
 {
 public:
   explicit AutoChangeTransformListNotifier(DOMSVGTransformList* aTransformList MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mTransformList(aTransformList)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mTransformList, "Expecting non-null transformList");
     mEmptyOrOldValue =
--- a/dom/svg/SVGSVGElement.h
+++ b/dom/svg/SVGSVGElement.h
@@ -402,17 +402,17 @@ private:
   bool     mHasChildrenOnlyTransform;
   bool     mUseCurrentView;
 };
 
 } // namespace dom
 
 // Helper class to automatically manage temporary changes to an SVG document's
 // state for rendering purposes.
-class MOZ_STACK_CLASS AutoSVGRenderingState
+class MOZ_RAII AutoSVGRenderingState
 {
 public:
   AutoSVGRenderingState(const Maybe<SVGImageContext>& aSVGContext,
                         float aFrameTime,
                         dom::SVGSVGElement* aRootElem
                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mHaveOverrides(aSVGContext.isSome() &&
                      aSVGContext->GetPreserveAspectRatio().isSome())
--- a/dom/svg/SVGTransform.cpp
+++ b/dom/svg/SVGTransform.cpp
@@ -66,17 +66,17 @@ SVGTransform::WrapObject(JSContext* aCx,
 {
   return SVGTransformBinding::Wrap(aCx, this, aGivenProto);
 }
 
 //----------------------------------------------------------------------
 // Helper class: AutoChangeTransformNotifier
 // Stack-based helper class to pair calls to WillChangeTransformList
 // and DidChangeTransformList.
-class MOZ_STACK_CLASS AutoChangeTransformNotifier
+class MOZ_RAII AutoChangeTransformNotifier
 {
 public:
   explicit AutoChangeTransformNotifier(SVGTransform* aTransform MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mTransform(aTransform)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     MOZ_ASSERT(mTransform, "Expecting non-null transform");
     if (mTransform->HasOwner()) {
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/frameSelectEvents.html
@@ -0,0 +1,414 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Testing Selection Events</title>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  </head>
+
+  <body>
+    <div id="normal">
+      <span id="inner">A bunch of text in a span inside of a div which should be selected</span>
+    </div>
+
+    <div id="ce" contenteditable>
+      This is a random block of text
+    </div>
+
+    <input type="text" id="input" value="XXXXXXXXXXXXXXXXXXX" width="200"> <br>
+
+    <textarea id="textarea" width="200">XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX</textarea>
+
+    <script>
+      // Call the testing methods from the parent window
+      var is = parent.is;
+      var ok = parent.ok;
+
+      // spin() spins the event loop for two cycles, giving time for
+      // selectionchange events to be fired, and handled by our listeners.
+      function spin() {
+        return new Promise(function(a) {
+          parent.SimpleTest.executeSoon(function() {
+            parent.SimpleTest.executeSoon(a)
+          });
+        });
+      }
+
+      // The main test
+      parent.add_task(function *() {
+        yield spin();
+
+        var selectstart = 0;
+        var selectionchange = 0;
+        var inputSelectionchange = 0;
+        var textareaSelectionchange = 0;
+
+        var cancel = false;
+        var selectstartTarget = null;
+
+        document.addEventListener('selectstart', function(aEvent) {
+          console.log("originaltarget", aEvent.originalTarget, "new", selectstartTarget);
+          is(aEvent.originalTarget, selectstartTarget,
+             "The original target of selectstart");
+          selectstartTarget = null;
+
+          console.log(selectstart);
+          selectstart++;
+
+          if (cancel) {
+            aEvent.preventDefault();
+          }
+        });
+        document.addEventListener('selectionchange', function(aEvent) {
+          is(aEvent.originalTarget, document,
+             "The original target of selectionchange should be the document");
+          console.log(selectionchange);
+          selectionchange++;
+        });
+
+        function elt(aId) { return document.getElementById(aId); }
+        function reset() {
+          selectstart = 0;
+          selectionchange = 0;
+          inputSelectionchange = 0;
+          textareaSelectionchange = 0;
+          cancel = false;
+        }
+
+        elt("input").addEventListener('selectionchange', function(aEvent) {
+          is (aEvent.originalTarget, elt("input"),
+              "The original target of selectionchange should be the input");
+          console.log(inputSelectionchange);
+          inputSelectionchange++;
+        });
+        elt("textarea").addEventListener('selectionchange', function(aEvent) {
+          is (aEvent.originalTarget, elt("textarea"),
+              "The original target of selectionchange should be the textarea");
+          console.log(textareaSelectionchange);
+          textareaSelectionchange++;
+        });
+        function* mouseAction(aElement, aOffset, aType,
+                              aSelStart, aSelChng, aISelChng, aTSelChng,
+                              aYOffset)
+        {
+          if (aType == "click") { // You can simulate a click event by sending undefined
+            aType = undefined;
+          }
+          if (!aYOffset) {
+            aYOffset = 10;
+          }
+          synthesizeMouse(aElement, aOffset, aYOffset, { type: aType });
+          yield spin();
+
+          is(selectstart, aSelStart,
+             "SelStart Mouse Action (" + aOffset + " - " + aType + ")");
+          is(selectionchange, aSelChng,
+             "SelChng Mouse Action (" + aOffset + " - " + aType + ")");
+          is(inputSelectionchange, aISelChng || 0,
+             "ISelChng Mouse Action (" + aOffset + " - " + aType + ")");
+          is(textareaSelectionchange, aTSelChng || 0,
+             "TSelChng Mouse Action (" + aOffset + " - " + aType + ")");
+          reset();
+        }
+
+        function* keyAction(aKey, aShift, aAccel,
+                            aSelStart, aSelChng, aISelChng, aTSelChng)
+        {
+          synthesizeKey(aKey, { shiftKey: aShift, accelKey: aAccel });
+          yield spin();
+          is(selectstart, aSelStart,
+             "SelStart Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")");
+          is(selectionchange, aSelChng,
+             "SelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")");
+          is(inputSelectionchange, aISelChng || 0,
+             "ISelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")");
+          is(textareaSelectionchange, aTSelChng || 0,
+             "TSelChng Key Action (" + aKey + " - " + aShift + " - " + aAccel + ")");
+          reset();
+        }
+
+        var selection = document.getSelection();
+        function isCollapsed() {
+          is(selection.isCollapsed, true, "Selection is collapsed");
+        }
+        function isNotCollapsed() {
+          is(selection.isCollapsed, false, "Selection is not collapsed");
+        }
+
+        // Focus the contenteditable text
+        yield* mouseAction(elt("ce"), 100, "click", 0, 1);
+        isCollapsed();
+
+        // Move the selection to the right, this should only fire selectstart once
+        selectstartTarget = elt("ce").firstChild;
+        yield* keyAction("VK_RIGHT", true, false, 1, 1);
+        isNotCollapsed();
+        yield* keyAction("VK_RIGHT", true, false, 0, 1);
+        isNotCollapsed();
+
+        // Move it back so that the selection is empty again
+        yield* keyAction("VK_LEFT", true, false, 0, 1);
+        isNotCollapsed();
+        yield* keyAction("VK_LEFT", true, false, 0, 1);
+        isCollapsed();
+
+        // Going from empty to non-empty should fire selectstart again
+        selectstartTarget = elt("ce").firstChild;
+        yield* keyAction("VK_LEFT", true, false, 1, 1);
+        isNotCollapsed();
+
+        function* mouseMoves(aElement, aTarget) {
+          // Select a region
+          yield* mouseAction(aElement, 50, "mousedown", 0, 1);
+          isCollapsed();
+
+          selectstartTarget = aTarget;
+          yield* mouseAction(aElement, 100, "mousemove", 1, 1);
+          isNotCollapsed();
+
+          // Moving it more shouldn't trigger a start (move back to empty)
+          yield* mouseAction(aElement, 75, "mousemove", 0, 1);
+          isNotCollapsed();
+          yield* mouseAction(aElement, 50, "mousemove", 0, 1);
+          isCollapsed();
+
+          // Wiggling the mouse a little such that it doesn't select any
+          // characters shouldn't trigger a selection
+          yield* mouseAction(aElement, 50, "mousemove", 0, 0, 0, 0, 11);
+          isCollapsed();
+
+          // Moving the mouse again from an empty selection should trigger a
+          // selectstart
+          selectstartTarget = aTarget;
+          yield* mouseAction(aElement, 25, "mousemove", 1, 1);
+          isNotCollapsed();
+
+          // Releasing the mouse shouldn't do anything
+          yield* mouseAction(aElement, 25, "mouseup", 0, 0);
+          isNotCollapsed();
+
+          // And neither should moving your mouse around when the mouse
+          // button isn't pressed
+          yield* mouseAction(aElement, 50, "mousemove", 0, 0);
+          isNotCollapsed();
+
+          // Clicking in an random location should move the selection, but not perform a
+          // selectstart
+          yield* mouseAction(aElement, 50, "click", 0, 1);
+          isCollapsed();
+
+          // Clicking there again should do nothing
+          yield* mouseAction(aElement, 50, "click", 0, 0);
+          isCollapsed();
+
+          // Selecting a region, and canceling the selectstart should mean that the
+          // selection remains collapsed
+          yield* mouseAction(aElement, 75, "mousedown", 0, 1);
+          isCollapsed();
+          cancel = true;
+          selectstartTarget = aTarget;
+          yield* mouseAction(aElement, 100, "mousemove", 1, 1);
+          isCollapsed();
+          yield* mouseAction(aElement, 100, "mouseup", 0, 0);
+          isCollapsed();
+        }
+
+        // Should work both on normal
+        yield* mouseMoves(elt("inner"), elt("inner").firstChild);
+        // and contenteditable fields
+        yield* mouseMoves(elt("ce"), elt("ce").firstChild);
+        // and fields with elements in them
+        yield* mouseMoves(elt("normal"), elt("inner").firstChild);
+
+        yield* mouseAction(elt("inner"), 50, "click", 0, 1);
+        isCollapsed();
+
+        reset();
+        // Select all should fire both selectstart and change
+        selectstartTarget = document.body;
+        yield* keyAction("A", false, true, 1, 1);
+        isNotCollapsed();
+
+        // Clear the selection
+        yield* mouseAction(elt("inner"), 50, "click", 0, 1);
+        isCollapsed();
+
+        // Even if we already have a selection
+        yield* mouseAction(elt("inner"), 75, "mousedown", 0, 1);
+        isCollapsed();
+        selectstartTarget = elt("inner").firstChild;
+        yield* mouseAction(elt("inner"), 100, "mousemove", 1, 1);
+        isNotCollapsed();
+        yield* mouseAction(elt("inner"), 100, "mouseup", 0, 0);
+        isNotCollapsed();
+
+        selectstartTarget = document.body;
+        yield* keyAction("A", false, true, 1, 1);
+        isNotCollapsed();
+
+        // Clear the selection
+        yield* mouseAction(elt("inner"), 50, "click", 0, 1);
+        isCollapsed();
+
+        // Make sure that a synthesized selection change doesn't fire selectstart
+        var s = document.getSelection();
+        s.removeAllRanges();
+        yield spin();
+        is(selectstart, 0, "Synthesized range removals shouldn't fire selectstart");
+        is(selectionchange, 1, "Synthesized range removals should change selectionchange");
+        reset();
+        isCollapsed();
+
+        var range = document.createRange();
+        range.selectNode(elt("inner"));
+        s.addRange(range);
+        yield spin();
+        is(selectstart, 0, "Synthesized range additions shouldn't fire selectstart");
+        is(selectionchange, 1, "Synthesized range additions should change selectionchange");
+        reset();
+        isNotCollapsed();
+
+        // Change the range, without replacing
+        range.selectNode(elt("ce"));
+        yield spin();
+        is(selectstart, 0, "Synthesized range mutations shouldn't fire selectstart");
+        is(selectionchange, 1, "Synthesized range mutations should change selectionchange");
+        reset();
+        isNotCollapsed();
+
+        // Remove the range
+        s.removeAllRanges();
+        yield spin();
+        is(selectstart, 0, "Synthesized range removal");
+        is(selectionchange, 1, "Synthesized range removal");
+        reset();
+        isCollapsed();
+
+
+        /*
+           Selection events mouse move on input type=text
+        */
+
+        // Select a region
+
+        yield* mouseAction(elt("input"), 50, "mousedown", 0, 1, 1, 0);
+
+        selectstartTarget = elt("input");
+        yield* mouseAction(elt("input"), 100, "mousemove", 1, 0, 1, 0);
+
+        // Moving it more shouldn't trigger a start (move back to empty)
+        yield* mouseAction(elt("input"), 75, "mousemove", 0, 0, 1, 0);
+        yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 1, 0);
+
+        // Wiggling the mouse a little such that it doesn't select any
+        // characters shouldn't trigger a selection
+        yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 0, 0, 11);
+
+        // Moving the mouse again from an empty selection should trigger a
+        // selectstart
+        selectstartTarget = elt("input");
+        yield* mouseAction(elt("input"), 25, "mousemove", 1, 0, 1, 0);
+
+        // Releasing the mouse shouldn't do anything
+        yield* mouseAction(elt("input"), 25, "mouseup", 0, 0, 0, 0);
+
+        // And neither should moving your mouse around when the mouse
+        // button isn't pressed
+        yield* mouseAction(elt("input"), 50, "mousemove", 0, 0, 0, 0);
+
+        // Clicking in an random location should move the selection, but
+        // not perform a selectstart
+        yield* mouseAction(elt("input"), 50, "click", 0, 0, 1, 0);
+
+        // Clicking there again should do nothing
+        yield* mouseAction(elt("input"), 50, "click", 0, 0, 0, 0);
+
+        // Selecting a region, and canceling the selectstart should mean that the
+        // selection remains collapsed
+        yield* mouseAction(elt("input"), 75, "mousedown", 0, 0, 1, 0);
+        cancel = true;
+        selectstartTarget = elt("input");
+        yield* mouseAction(elt("input"), 100, "mousemove", 1, 0, 1, 0);
+        yield* mouseAction(elt("input"), 100, "mouseup", 0, 0, 0, 0);
+
+
+        // Select a region
+        // XXX For some reason we fire 2 selectchange events on the body
+        // when switching from the input to the text area.
+        yield* mouseAction(elt("textarea"), 50, "mousedown", 0, 2, 0, 1);
+
+        selectstartTarget = elt("textarea");
+        yield* mouseAction(elt("textarea"), 100, "mousemove", 1, 0, 0, 1);
+
+        // Moving it more shouldn't trigger a start (move back to empty)
+        yield* mouseAction(elt("textarea"), 75, "mousemove", 0, 0, 0, 1);
+        yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 1);
+
+        // Wiggling the mouse a little such that it doesn't select any
+        // characters shouldn't trigger a selection
+        yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 0, 11);
+
+        // Moving the mouse again from an empty selection should trigger a
+        // selectstart
+        selectstartTarget = elt("textarea");
+        yield* mouseAction(elt("textarea"), 25, "mousemove", 1, 0, 0, 1);
+
+        // Releasing the mouse shouldn't do anything
+        yield* mouseAction(elt("textarea"), 25, "mouseup", 0, 0, 0, 0);
+
+        // And neither should moving your mouse around when the mouse
+        // button isn't pressed
+        yield* mouseAction(elt("textarea"), 50, "mousemove", 0, 0, 0, 0);
+
+        // Clicking in an random location should move the selection, but not perform a
+        // selectstart
+        yield* mouseAction(elt("textarea"), 50, "click", 0, 0, 0, 1);
+
+        // Clicking there again should do nothing
+        yield* mouseAction(elt("textarea"), 50, "click", 0, 0, 0, 0);
+
+        // Selecting a region, and canceling the selectstart should mean that the
+        // selection remains collapsed
+        yield* mouseAction(elt("textarea"), 75, "mousedown", 0, 0, 0, 1);
+        cancel = true;
+        selectstartTarget = elt("textarea");
+        yield* mouseAction(elt("textarea"), 100, "mousemove", 1, 0, 0, 1);
+        yield* mouseAction(elt("textarea"), 100, "mouseup", 0, 0, 0, 0);
+
+        // Marking the input and textarea as display: none and then as visible again
+        // shouldn't trigger any changes, although the nodes will be re-framed
+        elt("input").setAttribute("style", "display: none;");
+        yield spin();
+        is(selectstart, 0, "idn - ss 1");
+        is(selectionchange, 0, "idn - sc 1");
+        is(inputSelectionchange, 0, "idn - isc 1");
+        is(textareaSelectionchange, 0, "idn - tsc 1");
+        reset();
+
+        elt("input").setAttribute("style", "");
+        yield spin();
+        is(selectstart, 0, "idn - ss 2");
+        is(selectionchange, 0, "idn - sc 2");
+        is(inputSelectionchange, 0, "idn - isc 2");
+        is(textareaSelectionchange, 0, "idn - tsc 2");
+        reset();
+
+        elt("textarea").setAttribute("style", "display: none;");
+        yield spin();
+        is(selectstart, 0, "tdn - ss 1");
+        is(selectionchange, 0, "tdn - sc 1");
+        is(inputSelectionchange, 0, "tdn - isc 1");
+        is(textareaSelectionchange, 0, "tdn - tsc 1");
+        reset();
+
+        elt("textarea").setAttribute("style", "");
+        yield spin();
+        is(selectstart, 0, "tdn - ss 2");
+        is(selectionchange, 0, "tdn - sc 2");
+        is(inputSelectionchange, 0, "tdn - isc 2");
+        is(textareaSelectionchange, 0, "tdn - tsc 2");
+        reset();
+      });
+    </script>
+  </body>
+</html>
--- a/dom/tests/mochitest/general/mochitest.ini
+++ b/dom/tests/mochitest/general/mochitest.ini
@@ -39,16 +39,17 @@ support-files =
   test_bug1012662_common.js
   frameStorageAllowed.html
   frameStoragePrevented.html
   frameStorageChrome.html
   frameStorageNullprincipal.sjs
   workerStorageAllowed.js
   workerStoragePrevented.js
   storagePermissionsUtils.js
+  frameSelectEvents.html
 
 [test_497898.html]
 skip-if = ((buildapp == 'mulet' || buildapp == 'b2g') && toolkit != 'gonk') || toolkit == 'android' #Bug 931116, b2g desktop specific, initial triage
 [test_bug504220.html]
 [test_bug628069_1.html]
 skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop specific, initial triage
 [test_bug628069_2.html]
 [test_bug631440.html]
@@ -115,8 +116,10 @@ skip-if = buildapp == 'b2g' || buildapp 
 [test_storagePermissionsAccept.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsRejectForeign.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsReject.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
 [test_storagePermissionsLimitForeign.html]
 skip-if = buildapp == 'b2g' # Bug 1184427 - no SSL certs on b2g
+[test_selectevents.html]
+skip-if = buildapp == 'b2g' || buildapp == 'mulet' # Mouse doesn't select in the same way on b2g
new file mode 100644
--- /dev/null
+++ b/dom/tests/mochitest/general/test_selectevents.html
@@ -0,0 +1,32 @@
+<!doctype html>
+<html>
+  <head>
+    <title>Testing Selection Events</title>
+    <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+    <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
+    <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+  </head>
+
+  <body>
+    <iframe width="500"></iframe>
+    <script>
+      add_task(function* () {
+        // Push the correct preferences for the test
+        yield new Promise((done) => {
+          SpecialPowers.pushPrefEnv({'set': [['dom.select_events.enabled', true]]}, done);
+        });
+
+        // Start the actual test
+        yield new Promise((done) => {
+          var iframe = document.querySelector('iframe');
+          iframe.addEventListener('load', done);
+          iframe.setAttribute('src', 'frameSelectEvents.html');
+        });
+
+        // The child iframe will call add_task before we reach this point,
+        // and will handle the rest of the test.
+      });
+    </script>
+  </body>
+</html>
--- a/dom/webidl/Document.webidl
+++ b/dom/webidl/Document.webidl
@@ -146,16 +146,17 @@ partial interface Document {
 
   // Gecko extensions?
                 attribute EventHandler onwheel;
                 attribute EventHandler oncopy;
                 attribute EventHandler oncut;
                 attribute EventHandler onpaste;
                 attribute EventHandler onbeforescriptexecute;
                 attribute EventHandler onafterscriptexecute;
+
   /**
    * True if this document is synthetic : stand alone image, video, audio file,
    * etc.
    */
   [Func="IsChromeOrXBL"] readonly attribute boolean mozSyntheticDocument;
   /**
    * Returns the script element whose script is currently being processed.
    *
--- a/dom/webidl/EventHandler.webidl
+++ b/dom/webidl/EventHandler.webidl
@@ -84,16 +84,19 @@ interface GlobalEventHandlers {
            //(Not implemented)attribute EventHandler onsort;
            attribute EventHandler onstalled;
            attribute EventHandler onsubmit;
            attribute EventHandler onsuspend;
            attribute EventHandler ontimeupdate;
            attribute EventHandler onvolumechange;
            attribute EventHandler onwaiting;
 
+           [Pref="dom.select_events.enabled"]
+           attribute EventHandler onselectstart;
+
            // Pointer events handlers
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onpointercancel;
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onpointerdown;
            [Pref="dom.w3c_pointer_events.enabled"]
            attribute EventHandler onpointerup;
            [Pref="dom.w3c_pointer_events.enabled"]
--- a/dom/workers/XMLHttpRequest.cpp
+++ b/dom/workers/XMLHttpRequest.cpp
@@ -538,17 +538,17 @@ class EventRunnable final : public MainT
   bool mProgressEvent;
   bool mLengthComputable;
   bool mUseCachedArrayBufferResponse;
   nsresult mResponseTextResult;
   nsresult mStatusResult;
   nsresult mResponseResult;
 
 public:
-  class StateDataAutoRooter : private JS::CustomAutoRooter
+  class MOZ_RAII StateDataAutoRooter : private JS::CustomAutoRooter
   {
     XMLHttpRequest::StateData* mStateData;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit StateDataAutoRooter(JSContext* aCx, XMLHttpRequest::StateData* aData
                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : CustomAutoRooter(aCx), mStateData(aData)
--- a/editor/libeditor/nsEditorUtils.h
+++ b/editor/libeditor/nsEditorUtils.h
@@ -26,17 +26,17 @@ namespace dom {
 class Selection;
 } // namespace dom
 } // namespace mozilla
 
 /***************************************************************************
  * stack based helper class for batching a collection of txns inside a
  * placeholder txn.
  */
-class MOZ_STACK_CLASS nsAutoPlaceHolderBatch
+class MOZ_RAII nsAutoPlaceHolderBatch
 {
   private:
     nsCOMPtr<nsIEditor> mEd;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
     nsAutoPlaceHolderBatch(nsIEditor *aEd, nsIAtom *atom MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mEd(do_QueryInterface(aEd))
     {
@@ -53,33 +53,33 @@ class MOZ_STACK_CLASS nsAutoPlaceHolderB
     }
 };
 
 /***************************************************************************
  * stack based helper class for batching a collection of txns.
  * Note: I changed this to use placeholder batching so that we get
  * proper selection save/restore across undo/redo.
  */
-class MOZ_STACK_CLASS nsAutoEditBatch : public nsAutoPlaceHolderBatch
+class MOZ_RAII nsAutoEditBatch : public nsAutoPlaceHolderBatch
 {
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
     explicit nsAutoEditBatch(nsIEditor *aEd MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : nsAutoPlaceHolderBatch(aEd, nullptr)
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
     ~nsAutoEditBatch() {}
 };
 
 /***************************************************************************
  * stack based helper class for saving/restoring selection.  Note that this
  * assumes that the nodes involved are still around afterwards!
  */
-class MOZ_STACK_CLASS nsAutoSelectionReset
+class MOZ_RAII nsAutoSelectionReset
 {
   private:
     /** ref-counted reference to the selection that we are supposed to restore */
     nsRefPtr<mozilla::dom::Selection> mSel;
     nsEditor *mEd;  // non-owning ref to nsEditor
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
@@ -91,17 +91,17 @@ class MOZ_STACK_CLASS nsAutoSelectionRes
 
     /** Abort: cancel selection saver */
     void Abort();
 };
 
 /***************************************************************************
  * stack based helper class for StartOperation()/EndOperation() sandwich
  */
-class MOZ_STACK_CLASS nsAutoRules
+class MOZ_RAII nsAutoRules
 {
   public:
 
   nsAutoRules(nsEditor *ed, EditAction action,
               nsIEditor::EDirection aDirection
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mEd(ed), mDoNothing(false)
   {
@@ -126,17 +126,17 @@ class MOZ_STACK_CLASS nsAutoRules
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 
 /***************************************************************************
  * stack based helper class for turning off active selection adjustment
  * by low level transactions
  */
-class MOZ_STACK_CLASS nsAutoTxnsConserveSelection
+class MOZ_RAII nsAutoTxnsConserveSelection
 {
   public:
 
   explicit nsAutoTxnsConserveSelection(nsEditor *ed MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mEd(ed), mOldState(true)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     if (mEd)
@@ -158,17 +158,17 @@ class MOZ_STACK_CLASS nsAutoTxnsConserve
   nsEditor *mEd;
   bool mOldState;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /***************************************************************************
  * stack based helper class for batching reflow and paint requests.
  */
-class MOZ_STACK_CLASS nsAutoUpdateViewBatch
+class MOZ_RAII nsAutoUpdateViewBatch
 {
   public:
 
   explicit nsAutoUpdateViewBatch(nsEditor *ed MOZ_GUARD_OBJECT_NOTIFIER_PARAM) : mEd(ed)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     NS_ASSERTION(mEd, "null mEd pointer!");
 
@@ -192,34 +192,34 @@ class MOZ_STACK_CLASS nsAutoUpdateViewBa
  *****************************************************************************/
 
 class nsBoolDomIterFunctor
 {
   public:
     virtual bool operator()(nsINode* aNode) const = 0;
 };
 
-class MOZ_STACK_CLASS nsDOMIterator
+class MOZ_RAII nsDOMIterator
 {
   public:
     explicit nsDOMIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
 
     explicit nsDOMIterator(nsINode& aNode MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     virtual ~nsDOMIterator();
 
     nsresult Init(nsRange& aRange);
 
     void AppendList(const nsBoolDomIterFunctor& functor,
                     nsTArray<mozilla::OwningNonNull<nsINode>>& arrayOfNodes) const;
   protected:
     nsCOMPtr<nsIContentIterator> mIter;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class MOZ_STACK_CLASS nsDOMSubtreeIterator : public nsDOMIterator
+class MOZ_RAII nsDOMSubtreeIterator : public nsDOMIterator
 {
   public:
     explicit nsDOMSubtreeIterator(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
     virtual ~nsDOMSubtreeIterator();
 
     nsresult Init(nsRange& aRange);
 };
 
--- a/editor/libeditor/nsTextEditRules.cpp
+++ b/editor/libeditor/nsTextEditRules.cpp
@@ -826,16 +826,24 @@ nsTextEditRules::WillDeleteSelection(Sel
 
   // if there is only bogus content, cancel the operation
   if (mBogusNode) {
     *aCancel = true;
     return NS_OK;
   }
 
   nsresult res = NS_OK;
+  // If the current selection is empty (e.g the user presses backspace with
+  // a collapsed selection), then we want to avoid sending the selectstart
+  // event to the user, so we hide selection changes. However, we still
+  // want to send a single selectionchange event to the document, so we
+  // batch the selectionchange events, such that a single event fires after
+  // the AutoHideSelectionChanges destructor has been run.
+  SelectionBatcher selectionBatcher(aSelection);
+  AutoHideSelectionChanges hideSelection(aSelection);
   nsAutoScriptBlocker scriptBlocker;
 
   if (IsPasswordEditor())
   {
     NS_ENSURE_STATE(mEditor);
     res = mEditor->ExtendSelectionForDelete(aSelection, &aCollapsedAction);
     NS_ENSURE_SUCCESS(res, res);
 
--- a/extensions/spellcheck/src/mozInlineSpellChecker.cpp
+++ b/extensions/spellcheck/src/mozInlineSpellChecker.cpp
@@ -941,23 +941,30 @@ mozInlineSpellChecker::ReplaceWord(nsIDO
   NS_ENSURE_TRUE(newword.Length() != 0, NS_ERROR_FAILURE);
 
   nsCOMPtr<nsIDOMRange> range;
   nsresult res = GetMisspelledWord(aNode, aOffset, getter_AddRefs(range));
   NS_ENSURE_SUCCESS(res, res); 
 
   if (range)
   {
+    // This range was retrieved from the spellchecker selection. As
+    // ranges cannot be shared between selections, we must clone it
+    // before adding it to the editor's selection.
+    nsCOMPtr<nsIDOMRange> editorRange;
+    res = range->CloneRange(getter_AddRefs(editorRange));
+    NS_ENSURE_SUCCESS(res, res);
+
     nsAutoPlaceHolderBatch phb(editor, nullptr);
   
     nsCOMPtr<nsISelection> selection;
     res = editor->GetSelection(getter_AddRefs(selection));
     NS_ENSURE_SUCCESS(res, res);
     selection->RemoveAllRanges();
-    selection->AddRange(range);
+    selection->AddRange(editorRange);
     editor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
 
     nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryReferent(mEditor));
     if (textEditor)
       textEditor->InsertText(newword);
   }
 
   return NS_OK;
--- a/ipc/glue/MessageChannel.cpp
+++ b/ipc/glue/MessageChannel.cpp
@@ -272,17 +272,17 @@ private:
     // Disable harmful methods.
     CxxStackFrame() = delete;
     CxxStackFrame(const CxxStackFrame&) = delete;
     CxxStackFrame& operator=(const CxxStackFrame&) = delete;
 };
 
 namespace {
 
-class MOZ_STACK_CLASS MaybeScriptBlocker {
+class MOZ_RAII MaybeScriptBlocker {
 public:
     explicit MaybeScriptBlocker(MessageChannel *aChannel, bool aBlock
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : mBlocked(aChannel->ShouldBlockScripts() && aBlock)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         if (mBlocked) {
             nsContentUtils::AddScriptBlocker();
--- a/ipc/glue/Neutering.h
+++ b/ipc/glue/Neutering.h
@@ -18,17 +18,17 @@ namespace mozilla {
 namespace ipc {
 
 /**
  * This class is a RAII wrapper around Window neutering. As long as a
  * NeuteredWindowRegion object is instantiated, Win32 windows belonging to the
  * current thread will be neutered. It is safe to nest multiple instances of
  * this class.
  */
-class MOZ_STACK_CLASS NeuteredWindowRegion
+class MOZ_RAII NeuteredWindowRegion
 {
 public:
   explicit NeuteredWindowRegion(bool aDoNeuter MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~NeuteredWindowRegion();
 
   /**
    * This function clears any backlog of nonqueued messages that are pending for
    * the current thread.
@@ -41,17 +41,17 @@ private:
 };
 
 /**
  * This class is analagous to MutexAutoUnlock for Mutex; it is an RAII class
  * that is to be instantiated within a NeuteredWindowRegion, thus temporarily
  * disabling neutering for the remainder of its enclosing block.
  * @see NeuteredWindowRegion
  */
-class MOZ_STACK_CLASS DeneuteredWindowRegion
+class MOZ_RAII DeneuteredWindowRegion
 {
 public:
   DeneuteredWindowRegion(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
   ~DeneuteredWindowRegion();
 
 private:
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   bool mReneuter;
--- a/js/ipc/CPOWTimer.h
+++ b/js/ipc/CPOWTimer.h
@@ -15,17 +15,17 @@
  * A stopwatch measuring the duration of a CPOW call.
  *
  * As the process is consuming neither user time nor system time
  * during a CPOW call, we measure such durations using wallclock time.
  *
  * This stopwatch is active iff JSRuntime::stopwatch.isActive is set.
  * Upon destruction, update JSRuntime::stopwatch.data.totalCPOWTime.
  */
-class MOZ_STACK_CLASS CPOWTimer final {
+class MOZ_RAII CPOWTimer final {
   public:
     explicit inline CPOWTimer(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~CPOWTimer();
 
   private:
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     /**
--- a/js/public/RootingAPI.h
+++ b/js/public/RootingAPI.h
@@ -649,17 +649,17 @@ namespace JS {
  * Local variable of type T whose value is always rooted. This is typically
  * used for local variables, or for non-rooted values being passed to a
  * function that requires a handle, e.g. Foo(Root<T>(cx, x)).
  *
  * If you want to add additional methods to Rooted for a specific
  * specialization, define a RootedBase<T> specialization containing them.
  */
 template <typename T>
-class MOZ_STACK_CLASS Rooted : public js::RootedBase<T>
+class MOZ_RAII Rooted : public js::RootedBase<T>
 {
     static_assert(!mozilla::IsConvertible<T, Traceable*>::value,
                   "Rooted takes pointer or Traceable types but not Traceable* type");
 
     /* Note: CX is a subclass of either ContextFriendFields or PerThreadDataFriendFields. */
     void registerWithRootLists(js::RootLists& roots) {
         js::ThingRootKind kind = js::RootKind<T>::rootKind();
         this->stack = &roots.stackRoots_[kind];
@@ -772,17 +772,17 @@ class HandleBase<JSObject*>
 {
   public:
     template <class U>
     JS::Handle<U*> as() const;
 };
 
 /* Interface substitute for Rooted<T> which does not root the variable's memory. */
 template <typename T>
-class FakeRooted : public RootedBase<T>
+class MOZ_RAII FakeRooted : public RootedBase<T>
 {
   public:
     template <typename CX>
     explicit FakeRooted(CX* cx
                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : ptr(GCMethods<T>::initial())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/ds/LifoAlloc.h
+++ b/js/src/ds/LifoAlloc.h
@@ -477,17 +477,17 @@ class LifoAlloc
         // Return a Mark at the current position of the Enum.
         Mark mark() {
             alloc_->markCount++;
             return Mark(chunk_, position_);
         }
     };
 };
 
-class LifoAllocScope
+class MOZ_NON_TEMPORARY_CLASS LifoAllocScope
 {
     LifoAlloc*      lifoAlloc;
     LifoAlloc::Mark mark;
     bool            shouldRelease;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit LifoAllocScope(LifoAlloc* lifoAlloc
--- a/js/src/gc/GCInternals.h
+++ b/js/src/gc/GCInternals.h
@@ -87,17 +87,17 @@ class IncrementalSafety
     }
 };
 
 IncrementalSafety
 IsIncrementalGCSafe(JSRuntime* rt);
 
 #ifdef JS_GC_ZEAL
 
-class AutoStopVerifyingBarriers
+class MOZ_RAII AutoStopVerifyingBarriers
 {
     GCRuntime* gc;
     bool restartPreVerifier;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     AutoStopVerifyingBarriers(JSRuntime* rt, bool isShutdown
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -147,17 +147,17 @@ struct MovingTracer : JS::CallbackTracer
         MOZ_ASSERT(!RelocationOverlay::isCellForwarded(thing.asCell()));
     }
 
 #ifdef DEBUG
     TracerKind getTracerKind() const override { return TracerKind::Moving; }
 #endif
 };
 
-class AutoMaybeStartBackgroundAllocation
+class MOZ_RAII AutoMaybeStartBackgroundAllocation
 {
   private:
     JSRuntime* runtime;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoMaybeStartBackgroundAllocation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
       : runtime(nullptr)
@@ -171,17 +171,17 @@ class AutoMaybeStartBackgroundAllocation
 
     ~AutoMaybeStartBackgroundAllocation() {
         if (runtime)
             runtime->gc.startBackgroundAllocTaskIfIdle();
     }
 };
 
 // In debug builds, set/unset the GC sweeping flag for the current thread.
-struct AutoSetThreadIsSweeping
+struct MOZ_RAII AutoSetThreadIsSweeping
 {
 #ifdef DEBUG
     explicit AutoSetThreadIsSweeping(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
       : threadData_(js::TlsPerThreadData.get())
     {
         MOZ_ASSERT(!threadData_->gcSweeping);
         threadData_->gcSweeping = true;
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/gc/Statistics.h
+++ b/js/src/gc/Statistics.h
@@ -326,33 +326,33 @@ struct Statistics
 
     UniqueChars formatJsonDescription(uint64_t timestamp);
     UniqueChars formatJsonSliceDescription(unsigned i, const SliceData& slice);
     UniqueChars formatJsonPhaseTimes(const PhaseTimeTable phaseTimes);
 
     double computeMMU(int64_t resolution) const;
 };
 
-struct AutoGCSlice
+struct MOZ_RAII AutoGCSlice
 {
     AutoGCSlice(Statistics& stats, const ZoneGCStats& zoneStats, JSGCInvocationKind gckind,
                 SliceBudget budget, JS::gcreason::Reason reason
                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : stats(stats)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         stats.beginSlice(zoneStats, gckind, budget, reason);
     }
     ~AutoGCSlice() { stats.endSlice(); }
 
     Statistics& stats;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-struct AutoPhase
+struct MOZ_RAII AutoPhase
 {
     AutoPhase(Statistics& stats, Phase phase
               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : stats(stats), task(nullptr), phase(phase), enabled(true)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         stats.beginPhase(phase);
     }
@@ -386,17 +386,17 @@ struct AutoPhase
 
     Statistics& stats;
     const GCParallelTask* task;
     Phase phase;
     bool enabled;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-struct AutoSCC
+struct MOZ_RAII AutoSCC
 {
     AutoSCC(Statistics& stats, unsigned scc
             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : stats(stats), scc(scc)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         start = stats.beginSCC();
     }
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -513,17 +513,17 @@ FinishAllOffThreadCompilations(JSCompart
         IonBuilder* builder = finished[i];
         if (builder->compartment == CompileCompartment::get(comp)) {
             FinishOffThreadBuilder(nullptr, builder);
             HelperThreadState().remove(finished, &i);
         }
     }
 }
 
-class AutoLazyLinkExitFrame
+class MOZ_RAII AutoLazyLinkExitFrame
 {
     JitActivation* jitActivation_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoLazyLinkExitFrame(JitActivation* jitActivation
                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : jitActivation_(jitActivation)
--- a/js/src/jit/JitSpewer.cpp
+++ b/js/src/jit/JitSpewer.cpp
@@ -70,17 +70,17 @@ class IonSpewer
     void lockOutput() {
         PR_Lock(outputLock_);
     }
     void unlockOutput() {
         PR_Unlock(outputLock_);
     }
 };
 
-class AutoLockIonSpewerOutput
+class MOZ_RAII AutoLockIonSpewerOutput
 {
   private:
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   public:
     explicit AutoLockIonSpewerOutput(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM);
     ~AutoLockIonSpewerOutput();
 };
 
--- a/js/src/jit/mips32/CodeGenerator-mips32.cpp
+++ b/js/src/jit/mips32/CodeGenerator-mips32.cpp
@@ -500,17 +500,17 @@ CodeGeneratorMIPS::visitMulI(LMulI* ins)
 
         if (mul->canBeNegativeZero()) {
             Label done;
             masm.ma_b(dest, dest, &done, Assembler::NonZero, ShortJump);
 
             // Result is -0 if lhs or rhs is negative.
             // In that case result must be double value so bailout
             Register scratch = SecondScratchReg;
-            masm.ma_or(scratch, ToRegister(lhs), ToRegister(rhs));
+            masm.as_or(scratch, ToRegister(lhs), ToRegister(rhs));
             bailoutCmp32(Assembler::Signed, scratch, scratch, ins->snapshot());
 
             masm.bind(&done);
         }
     }
 }
 
 void
@@ -791,29 +791,29 @@ CodeGeneratorMIPS::visitBitOpI(LBitOpI* 
     const LAllocation* rhs = ins->getOperand(1);
     const LDefinition* dest = ins->getDef(0);
     // all of these bitops should be either imm32's, or integer registers.
     switch (ins->bitop()) {
       case JSOP_BITOR:
         if (rhs->isConstant())
             masm.ma_or(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
         else
-            masm.ma_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
+            masm.as_or(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
         break;
       case JSOP_BITXOR:
         if (rhs->isConstant())
             masm.ma_xor(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
         else
-            masm.ma_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
+            masm.as_xor(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
         break;
       case JSOP_BITAND:
         if (rhs->isConstant())
             masm.ma_and(ToRegister(dest), ToRegister(lhs), Imm32(ToInt32(rhs)));
         else
-            masm.ma_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
+            masm.as_and(ToRegister(dest), ToRegister(lhs), ToRegister(rhs));
         break;
       default:
         MOZ_CRASH("unexpected binary opcode");
     }
 }
 
 void
 CodeGeneratorMIPS::visitShiftI(LShiftI* ins)
@@ -1642,17 +1642,17 @@ CodeGeneratorMIPS::visitCompareBitwiseAn
 }
 
 void
 CodeGeneratorMIPS::visitBitAndAndBranch(LBitAndAndBranch* lir)
 {
     if (lir->right()->isConstant())
         masm.ma_and(ScratchRegister, ToRegister(lir->left()), Imm32(ToInt32(lir->right())));
     else
-        masm.ma_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right()));
+        masm.as_and(ScratchRegister, ToRegister(lir->left()), ToRegister(lir->right()));
     emitBranch(ScratchRegister, ScratchRegister, Assembler::NonZero, lir->ifTrue(),
                lir->ifFalse());
 }
 
 void
 CodeGeneratorMIPS::visitAsmJSUInt32ToDouble(LAsmJSUInt32ToDouble* lir)
 {
     masm.convertUInt32ToDouble(ToRegister(lir->input()), ToFloatRegister(lir->output()));
--- a/js/src/jit/mips32/MacroAssembler-mips32-inl.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32-inl.h
@@ -20,17 +20,17 @@ void
 MacroAssembler::not32(Register reg)
 {
     ma_not(reg, reg);
 }
 
 void
 MacroAssembler::and32(Register src, Register dest)
 {
-    ma_and(dest, dest, src);
+    as_and(dest, dest, src);
 }
 
 void
 MacroAssembler::and32(Imm32 imm, Register dest)
 {
     ma_and(dest, imm);
 }
 
--- a/js/src/jit/mips32/MacroAssembler-mips32.cpp
+++ b/js/src/jit/mips32/MacroAssembler-mips32.cpp
@@ -389,22 +389,16 @@ MacroAssemblerMIPS::ma_not(Register rd, 
 // And.
 void
 MacroAssemblerMIPS::ma_and(Register rd, Register rs)
 {
     as_and(rd, rd, rs);
 }
 
 void
-MacroAssemblerMIPS::ma_and(Register rd, Register rs, Register rt)
-{
-    as_and(rd, rs, rt);
-}
-
-void
 MacroAssemblerMIPS::ma_and(Register rd, Imm32 imm)
 {
     ma_and(rd, rd, imm);
 }
 
 void
 MacroAssemblerMIPS::ma_and(Register rd, Register rs, Imm32 imm)
 {
@@ -419,22 +413,16 @@ MacroAssemblerMIPS::ma_and(Register rd, 
 // Or.
 void
 MacroAssemblerMIPS::ma_or(Register rd, Register rs)
 {
     as_or(rd, rd, rs);
 }
 
 void
-MacroAssemblerMIPS::ma_or(Register rd, Register rs, Register rt)
-{
-    as_or(rd, rs, rt);
-}
-
-void
 MacroAssemblerMIPS::ma_or(Register rd, Imm32 imm)
 {
     ma_or(rd, rd, imm);
 }
 
 void
 MacroAssemblerMIPS::ma_or(Register rd, Register rs, Imm32 imm)
 {
@@ -449,22 +437,16 @@ MacroAssemblerMIPS::ma_or(Register rd, R
 // xor
 void
 MacroAssemblerMIPS::ma_xor(Register rd, Register rs)
 {
     as_xor(rd, rd, rs);
 }
 
 void
-MacroAssemblerMIPS::ma_xor(Register rd, Register rs, Register rt)
-{
-    as_xor(rd, rs, rt);
-}
-
-void
 MacroAssemblerMIPS::ma_xor(Register rd, Imm32 imm)
 {
     ma_xor(rd, rd, imm);
 }
 
 void
 MacroAssemblerMIPS::ma_xor(Register rd, Register rs, Imm32 imm)
 {
@@ -486,22 +468,16 @@ MacroAssemblerMIPS::ma_addu(Register rd,
         as_addiu(rd, rs, imm.value);
     } else {
         ma_li(ScratchRegister, imm);
         as_addu(rd, rs, ScratchRegister);
     }
 }
 
 void
-MacroAssemblerMIPS::ma_addu(Register rd, Register rs, Register rt)
-{
-    as_addu(rd, rs, rt);
-}
-
-void
 MacroAssemblerMIPS::ma_addu(Register rd, Register rs)
 {
     as_addu(rd, rd, rs);
 }
 
 void
 MacroAssemblerMIPS::ma_addu(Register rd, Imm32 imm)
 {
@@ -545,22 +521,16 @@ MacroAssemblerMIPS::ma_addTestOverflow(R
     } else {
         ma_li(ScratchRegister, imm);
         ma_addTestOverflow(rd, rs, ScratchRegister, overflow);
     }
 }
 
 // Subtract.
 void
-MacroAssemblerMIPS::ma_subu(Register rd, Register rs, Register rt)
-{
-    as_subu(rd, rs, rt);
-}
-
-void
 MacroAssemblerMIPS::ma_subu(Register rd, Register rs, Imm32 imm)
 {
     if (Imm16::IsInSignedRange(-imm.value)) {
         as_addiu(rd, rs, -imm.value);
     } else {
         ma_li(ScratchRegister, imm);
         as_subu(rd, rs, ScratchRegister);
     }
@@ -573,17 +543,17 @@ MacroAssemblerMIPS::ma_subu(Register rd,
 }
 
 void
 MacroAssemblerMIPS::ma_subTestOverflow(Register rd, Register rs, Register rt, Label* overflow)
 {
     Label goodSubtraction;
     // Use second scratch. The instructions generated by ma_b don't use the
     // second scratch register.
-    ma_subu(rd, rs, rt);
+    as_subu(rd, rs, rt);
 
     as_xor(ScratchRegister, rs, rt); // If same sign, no overflow
     ma_b(ScratchRegister, Imm32(0), &goodSubtraction, Assembler::GreaterThanOrEqual, ShortJump);
 
     // If different sign, then overflow
     as_xor(ScratchRegister, rs, rd);
     ma_b(ScratchRegister, Imm32(0), overflow, Assembler::LessThan);
 
@@ -1539,17 +1509,17 @@ void
 MacroAssemblerMIPSCompat::sub32(Imm32 imm, Register dest)
 {
     ma_subu(dest, dest, imm);
 }
 
 void
 MacroAssemblerMIPSCompat::sub32(Register src, Register dest)
 {
-    ma_subu(dest, dest, src);
+    as_subu(dest, dest, src);
 }
 
 void
 MacroAssemblerMIPSCompat::addPtr(Register src, Register dest)
 {
     ma_addu(dest, src);
 }
 
@@ -1558,17 +1528,17 @@ MacroAssemblerMIPSCompat::addPtr(const A
 {
     loadPtr(src, ScratchRegister);
     ma_addu(dest, ScratchRegister);
 }
 
 void
 MacroAssemblerMIPSCompat::subPtr(Register src, Register dest)
 {
-    ma_subu(dest, dest, src);
+    as_subu(dest, dest, src);
 }
 
 void
 MacroAssemblerMIPSCompat::move32(Imm32 imm, Register dest)
 {
     ma_li(dest, imm);
 }
 
@@ -2539,17 +2509,17 @@ void
 MacroAssemblerMIPSCompat::loadConstantDouble(double dp, FloatRegister dest)
 {
     ma_lid(dest, dp);
 }
 
 void
 MacroAssemblerMIPSCompat::branchTestInt32Truthy(bool b, const ValueOperand& value, Label* label)
 {
-    ma_and(ScratchRegister, value.payloadReg(), value.payloadReg());
+    as_and(ScratchRegister, value.payloadReg(), value.payloadReg());
     ma_b(ScratchRegister, ScratchRegister, label, b ? NonZero : Zero);
 }
 
 void
 MacroAssemblerMIPSCompat::branchTestStringTruthy(bool b, const ValueOperand& value, Label* label)
 {
     Register string = value.payloadReg();
     ma_lw(SecondScratchReg, Address(string, JSString::offsetOfLength()));
--- a/js/src/jit/mips32/MacroAssembler-mips32.h
+++ b/js/src/jit/mips32/MacroAssembler-mips32.h
@@ -105,29 +105,26 @@ class MacroAssemblerMIPS : public Assemb
 
     // Negate
     void ma_negu(Register rd, Register rs);
 
     void ma_not(Register rd, Register rs);
 
     // and
     void ma_and(Register rd, Register rs);
-    void ma_and(Register rd, Register rs, Register rt);
     void ma_and(Register rd, Imm32 imm);
     void ma_and(Register rd, Register rs, Imm32 imm);
 
     // or
     void ma_or(Register rd, Register rs);
-    void ma_or(Register rd, Register rs, Register rt);
     void ma_or(Register rd, Imm32 imm);
     void ma_or(Register rd, Register rs, Imm32 imm);
 
     // xor
     void ma_xor(Register rd, Register rs);
-    void ma_xor(Register rd, Register rs, Register rt);
     void ma_xor(Register rd, Imm32 imm);
     void ma_xor(Register rd, Register rs, Imm32 imm);
 
     // load
     void ma_load(Register dest, Address address, LoadStoreSize size = SizeWord,
                  LoadStoreExtension extension = SignExtend);
     void ma_load(Register dest, const BaseIndex& src, LoadStoreSize size = SizeWord,
                  LoadStoreExtension extension = SignExtend);
@@ -138,24 +135,22 @@ class MacroAssemblerMIPS : public Assemb
     void ma_store(Register data, const BaseIndex& dest, LoadStoreSize size = SizeWord,
                   LoadStoreExtension extension = SignExtend);
     void ma_store(Imm32 imm, const BaseIndex& dest, LoadStoreSize size = SizeWord,
                   LoadStoreExtension extension = SignExtend);
 
     // arithmetic based ops
     // add
     void ma_addu(Register rd, Register rs, Imm32 imm);
-    void ma_addu(Register rd, Register rs, Register rt);
     void ma_addu(Register rd, Register rs);
     void ma_addu(Register rd, Imm32 imm);
     void ma_addTestOverflow(Register rd, Register rs, Register rt, Label* overflow);
     void ma_addTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow);
 
     // subtract
-    void ma_subu(Register rd, Register rs, Register rt);
     void ma_subu(Register rd, Register rs, Imm32 imm);
     void ma_subu(Register rd, Imm32 imm);
     void ma_subTestOverflow(Register rd, Register rs, Register rt, Label* overflow);
     void ma_subTestOverflow(Register rd, Register rs, Imm32 imm, Label* overflow);
 
     // multiplies.  For now, there are only few that we care about.
     void ma_mult(Register rs, Imm32 imm);
     void ma_mul_branch_overflow(Register rd, Register rs, Register rt, Label* overflow);
--- a/js/src/jit/mips32/SharedIC-mips32.cpp
+++ b/js/src/jit/mips32/SharedIC-mips32.cpp
@@ -92,23 +92,23 @@ ICBinaryArith_Int32::Compiler::generateS
             masm.ma_b(scratchReg, Imm32(0), &done, Assembler::NotEqual, ShortJump);
             masm.ma_b(R0.payloadReg(), Imm32(0), &failure, Assembler::LessThan, ShortJump);
             masm.bind(&done);
             masm.tagValue(JSVAL_TYPE_INT32, scratchReg, R0);
         }
         break;
       }
       case JSOP_BITOR:
-        masm.ma_or(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
+        masm.as_or(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
         break;
       case JSOP_BITXOR:
-        masm.ma_xor(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
+        masm.as_xor(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
         break;
       case JSOP_BITAND:
-        masm.ma_and(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
+        masm.as_and(R0.payloadReg() , R0.payloadReg(), R1.payloadReg());
         break;
       case JSOP_LSH:
         // MIPS will only use 5 lowest bits in R1 as shift offset.
         masm.ma_sll(R0.payloadReg(), R0.payloadReg(), R1.payloadReg());
         break;
       case JSOP_RSH:
         masm.ma_sra(R0.payloadReg(), R0.payloadReg(), R1.payloadReg());
         break;
--- a/js/src/jit/mips32/Trampoline-mips32.cpp
+++ b/js/src/jit/mips32/Trampoline-mips32.cpp
@@ -436,17 +436,17 @@ JitRuntime::generateArgumentsRectifier(J
 
     // Load the number of |undefined|s to push into t1.
     masm.loadPtr(Address(StackPointer, RectifierFrameLayout::offsetOfCalleeToken()),
                  calleeTokenReg);
     masm.mov(calleeTokenReg, numArgsReg);
     masm.andPtr(Imm32(CalleeTokenMask), numArgsReg);
     masm.load16ZeroExtend(Address(numArgsReg, JSFunction::offsetOfNargs()), numArgsReg);
 
-    masm.ma_subu(t1, numArgsReg, s3);
+    masm.as_subu(t1, numArgsReg, s3);
 
     // Get the topmost argument.
     masm.ma_sll(t0, s3, Imm32(3)); // t0 <- nargs * 8
     masm.as_addu(t2, sp, t0); // t2 <- sp + nargs * 8
     masm.addPtr(Imm32(sizeof(RectifierFrameLayout)), t2);
 
     {
         Label notConstructing;
@@ -1186,17 +1186,17 @@ JitRuntime::generateProfilerExitFrameTai
 
         // returning directly to an IonJS frame.  Store return addr to frame
         // in lastProfilingCallSite.
         masm.loadPtr(Address(StackPointer, JitFrameLayout::offsetOfReturnAddress()), scratch2);
         masm.storePtr(scratch2, lastProfilingCallSite);
 
         // Store return frame in lastProfilingFrame.
         // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size();
-        masm.ma_addu(scratch2, StackPointer, scratch1);
+        masm.as_addu(scratch2, StackPointer, scratch1);
         masm.ma_addu(scratch2, scratch2, Imm32(JitFrameLayout::Size()));
         masm.storePtr(scratch2, lastProfilingFrame);
         masm.ret();
     }
 
     //
     // JitFrame_BaselineStub
     //
@@ -1220,17 +1220,17 @@ JitRuntime::generateProfilerExitFrameTai
     //    FP -----> ReturnAddr          |
     //
     // We take advantage of the fact that the stub frame saves the frame
     // pointer pointing to the baseline frame, so a bunch of calculation can
     // be avoided.
     //
     masm.bind(&handle_BaselineStub);
     {
-        masm.ma_addu(scratch3, StackPointer, scratch1);
+        masm.as_addu(scratch3, StackPointer, scratch1);
         Address stubFrameReturnAddr(scratch3,
                                     JitFrameLayout::Size() +
                                     BaselineStubFrameLayout::offsetOfReturnAddress());
         masm.loadPtr(stubFrameReturnAddr, scratch2);
         masm.storePtr(scratch2, lastProfilingCallSite);
 
         Address stubFrameSavedFramePtr(scratch3,
                                        JitFrameLayout::Size() - (2 * sizeof(void*)));
@@ -1277,17 +1277,17 @@ JitRuntime::generateProfilerExitFrameTai
     //              ActualArgc      |
     //              CalleeToken     |- JitFrameLayout::Size()
     //              Descriptor      |
     //    FP -----> ReturnAddr      |
     //
     masm.bind(&handle_Rectifier);
     {
         // scratch2 := StackPointer + Descriptor.size*1 + JitFrameLayout::Size();
-        masm.ma_addu(scratch2, StackPointer, scratch1);
+        masm.as_addu(scratch2, StackPointer, scratch1);
         masm.add32(Imm32(JitFrameLayout::Size()), scratch2);
         masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfDescriptor()), scratch3);
         masm.ma_srl(scratch1, scratch3, Imm32(FRAMESIZE_SHIFT));
         masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch3);
 
         // Now |scratch1| contains Rect-Descriptor.Size
         // and |scratch2| points to Rectifier frame
         // and |scratch3| contains Rect-Descriptor.Type
@@ -1298,32 +1298,32 @@ JitRuntime::generateProfilerExitFrameTai
                       &handle_Rectifier_BaselineStub);
 
         // Handle Rectifier <- IonJS
         // scratch3 := RectFrame[ReturnAddr]
         masm.loadPtr(Address(scratch2, RectifierFrameLayout::offsetOfReturnAddress()), scratch3);
         masm.storePtr(scratch3, lastProfilingCallSite);
 
         // scratch3 := RectFrame + Rect-Descriptor.Size + RectifierFrameLayout::Size()
-        masm.ma_addu(scratch3, scratch2, scratch1);
+        masm.as_addu(scratch3, scratch2, scratch1);
         masm.add32(Imm32(RectifierFrameLayout::Size()), scratch3);
         masm.storePtr(scratch3, lastProfilingFrame);
         masm.ret();
 
         // Handle Rectifier <- BaselineStub <- BaselineJS
         masm.bind(&handle_Rectifier_BaselineStub);
 #ifdef DEBUG
         {
             Label checkOk;
             masm.branch32(Assembler::Equal, scratch3, Imm32(JitFrame_BaselineStub), &checkOk);
             masm.assumeUnreachable("Unrecognized frame preceding baselineStub.");
             masm.bind(&checkOk);
         }
 #endif
-        masm.ma_addu(scratch3, scratch2, scratch1);
+        masm.as_addu(scratch3, scratch2, scratch1);
         Address stubFrameReturnAddr(scratch3, RectifierFrameLayout::Size() +
                                               BaselineStubFrameLayout::offsetOfReturnAddress());
         masm.loadPtr(stubFrameReturnAddr, scratch2);
         masm.storePtr(scratch2, lastProfilingCallSite);
 
         Address stubFrameSavedFramePtr(scratch3,
                                        RectifierFrameLayout::Size() - (2 * sizeof(void*)));
         masm.loadPtr(stubFrameSavedFramePtr, scratch2);
@@ -1345,17 +1345,17 @@ JitRuntime::generateProfilerExitFrameTai
     //              ... accessor frame data & args ... |- Descriptor.Size
     //              ActualArgc      |
     //              CalleeToken     |- JitFrameLayout::Size()
     //              Descriptor      |
     //    FP -----> ReturnAddr      |
     masm.bind(&handle_IonAccessorIC);
     {
         // scratch2 := StackPointer + Descriptor.size + JitFrameLayout::Size()
-        masm.ma_addu(scratch2, StackPointer, scratch1);
+        masm.as_addu(scratch2, StackPointer, scratch1);
         masm.addPtr(Imm32(JitFrameLayout::Size()), scratch2);
 
         // scratch3 := AccFrame-Descriptor.Size
         masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfDescriptor()), scratch3);
 #ifdef DEBUG
         // Assert previous frame is an IonJS frame.
         masm.movePtr(scratch3, scratch1);
         masm.and32(Imm32((1 << FRAMETYPE_BITS) - 1), scratch1);
@@ -1369,17 +1369,17 @@ JitRuntime::generateProfilerExitFrameTai
         masm.rshiftPtr(Imm32(FRAMESIZE_SHIFT), scratch3);
 
         // lastProfilingCallSite := AccFrame-ReturnAddr
         masm.loadPtr(Address(scratch2, IonAccessorICFrameLayout::offsetOfReturnAddress()), scratch1);
         masm.storePtr(scratch1, lastProfilingCallSite);
 
         // lastProfilingFrame := AccessorFrame + AccFrame-Descriptor.Size +
         //                       IonAccessorICFrameLayout::Size()
-        masm.ma_addu(scratch1, scratch2, scratch3);
+        masm.as_addu(scratch1, scratch2, scratch3);
         masm.addPtr(Imm32(IonAccessorICFrameLayout::Size()), scratch1);
         masm.storePtr(scratch1, lastProfilingFrame);
         masm.ret();
     }
 
     //
     // JitFrame_Entry
     //
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -62,17 +62,17 @@ class JS_PUBLIC_API(AutoCheckRequestDept
 
 # define CHECK_REQUEST(cx) \
     ((void) 0)
 
 #endif /* JS_DEBUG */
 
 /* AutoValueArray roots an internal fixed-size array of Values. */
 template <size_t N>
-class AutoValueArray : public AutoGCRooter
+class MOZ_RAII AutoValueArray : public AutoGCRooter
 {
     const size_t length_;
     Value elements_[N];
 
   public:
     explicit AutoValueArray(JSContext* cx
                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, VALARRAY), length_(N)
@@ -94,17 +94,17 @@ class AutoValueArray : public AutoGCRoot
         MOZ_ASSERT(i < N);
         return MutableHandleValue::fromMarkedLocation(&elements_[i]);
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 template<class T>
-class AutoVectorRooterBase : protected AutoGCRooter
+class MOZ_RAII AutoVectorRooterBase : protected AutoGCRooter
 {
     typedef js::Vector<T, 8> VectorImpl;
     VectorImpl vector;
 
   public:
     explicit AutoVectorRooterBase(JSContext* cx, ptrdiff_t tag
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, tag), vector(cx)
@@ -191,17 +191,17 @@ class AutoVectorRooterBase : protected A
         for (size_t i = oldLength; i < vector.length(); ++i, ++t)
             memset(t, 0, sizeof(T));
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 template <typename T>
-class MOZ_STACK_CLASS AutoVectorRooter : public AutoVectorRooterBase<T>
+class MOZ_RAII AutoVectorRooter : public AutoVectorRooterBase<T>
 {
   public:
     explicit AutoVectorRooter(JSContext* cx
                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : AutoVectorRooterBase<T>(cx, this->GetTag(T()))
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
@@ -220,17 +220,17 @@ typedef AutoVectorRooter<Value> AutoValu
 typedef AutoVectorRooter<jsid> AutoIdVector;
 typedef AutoVectorRooter<JSObject*> AutoObjectVector;
 
 using ValueVector = js::TraceableVector<JS::Value>;
 using IdVector = js::TraceableVector<jsid>;
 using ScriptVector = js::TraceableVector<JSScript*>;
 
 template<class Key, class Value>
-class AutoHashMapRooter : protected AutoGCRooter
+class MOZ_RAII AutoHashMapRooter : protected AutoGCRooter
 {
   private:
     typedef js::HashMap<Key, Value> HashMapImpl;
 
   public:
     explicit AutoHashMapRooter(JSContext* cx, ptrdiff_t tag
                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, tag), map(cx)
@@ -344,17 +344,17 @@ class AutoHashMapRooter : protected Auto
     AutoHashMapRooter& operator=(const AutoHashMapRooter& hmr) = delete;
 
     HashMapImpl map;
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 template<class T>
-class AutoHashSetRooter : protected AutoGCRooter
+class MOZ_RAII AutoHashSetRooter : protected AutoGCRooter
 {
   private:
     typedef js::HashSet<T> HashSetImpl;
 
   public:
     explicit AutoHashSetRooter(JSContext* cx, ptrdiff_t tag
                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, tag), set(cx)
@@ -455,17 +455,17 @@ class AutoHashSetRooter : protected Auto
     HashSetImpl set;
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * Custom rooting behavior for internal and external clients.
  */
-class JS_PUBLIC_API(CustomAutoRooter) : private AutoGCRooter
+class MOZ_RAII JS_PUBLIC_API(CustomAutoRooter) : private AutoGCRooter
 {
   public:
     template <typename CX>
     explicit CustomAutoRooter(CX* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : AutoGCRooter(cx, CUSTOM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
@@ -1065,17 +1065,17 @@ namespace js {
 void
 AssertHeapIsIdle(JSRuntime* rt);
 
 void
 AssertHeapIsIdle(JSContext* cx);
 
 } /* namespace js */
 
-class JSAutoRequest
+class MOZ_RAII JSAutoRequest
 {
   public:
     explicit JSAutoRequest(JSContext* cx
                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mContext(cx)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         JS_BeginRequest(mContext);
@@ -1404,31 +1404,31 @@ JS_RefreshCrossCompartmentWrappers(JSCon
  *   }
  *
  * Note: these calls must still execute in a LIFO manner w.r.t all other
  * enter/leave calls on the context. Furthermore, only the return value of a
  * JS_EnterCompartment call may be passed as the 'oldCompartment' argument of
  * the corresponding JS_LeaveCompartment call.
  */
 
-class JS_PUBLIC_API(JSAutoCompartment)
+class MOZ_RAII JS_PUBLIC_API(JSAutoCompartment)
 {
     JSContext* cx_;
     JSCompartment* oldCompartment_;
   public:
     JSAutoCompartment(JSContext* cx, JSObject* target
                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     JSAutoCompartment(JSContext* cx, JSScript* target
                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~JSAutoCompartment();
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class JS_PUBLIC_API(JSAutoNullableCompartment)
+class MOZ_RAII JS_PUBLIC_API(JSAutoNullableCompartment)
 {
     JSContext* cx_;
     JSCompartment* oldCompartment_;
   public:
     explicit JSAutoNullableCompartment(JSContext* cx, JSObject* targetOrNull
                                        MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~JSAutoNullableCompartment();
 
@@ -4222,17 +4222,17 @@ JS_GetStringEncodingLength(JSContext* cx
  * encoded into bytes with no error reported. Otherwise it returns the number
  * of bytes that are necessary to encode the string. If that exceeds the
  * length parameter, the string will be cut and only length bytes will be
  * written into the buffer.
  */
 JS_PUBLIC_API(size_t)
 JS_EncodeStringToBuffer(JSContext* cx, JSString* str, char* buffer, size_t length);
 
-class JSAutoByteString
+class MOZ_RAII JSAutoByteString
 {
   public:
     JSAutoByteString(JSContext* cx, JSString* str
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mBytes(JS_EncodeString(cx, str))
     {
         MOZ_ASSERT(cx);
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@@ -5034,17 +5034,17 @@ GetScriptedCallerGlobal(JSContext* cx);
  * popped from the stack.
  */
 extern JS_PUBLIC_API(void)
 HideScriptedCaller(JSContext* cx);
 
 extern JS_PUBLIC_API(void)
 UnhideScriptedCaller(JSContext* cx);
 
-class AutoHideScriptedCaller
+class MOZ_RAII AutoHideScriptedCaller
 {
   public:
     explicit AutoHideScriptedCaller(JSContext* cx
                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mContext(cx)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         HideScriptedCaller(mContext);
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -28,17 +28,17 @@ namespace jit {
 class JitContext;
 class DebugModeOSRVolatileJitFrameIterator;
 } // namespace jit
 
 typedef HashSet<JSObject*> ObjectSet;
 typedef HashSet<Shape*> ShapeSet;
 
 /* Detects cycles when traversing an object graph. */
-class AutoCycleDetector
+class MOZ_RAII AutoCycleDetector
 {
     JSContext* cx;
     RootedObject obj;
     bool cyclic;
     uint32_t hashsetGenerationAtInit;
     ObjectSet::AddPtr hashsetAddPointer;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
@@ -461,17 +461,17 @@ struct JSContext : public js::ExclusiveC
      * threshold when p is not null. The function takes the pointer and not
      * a boolean flag to minimize the amount of code in its inlined callers.
      */
     JS_FRIEND_API(void) checkMallocGCPressure(void* p);
 }; /* struct JSContext */
 
 namespace js {
 
-struct AutoResolving {
+struct MOZ_RAII AutoResolving {
   public:
     enum Kind {
         LOOKUP,
         WATCH
     };
 
     AutoResolving(JSContext* cx, HandleObject obj, HandleId id, Kind kind = LOOKUP
                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -665,17 +665,17 @@ CheckForInterrupt(JSContext* cx)
 /************************************************************************/
 
 typedef JS::AutoVectorRooter<PropertyName*> AutoPropertyNameVector;
 
 using ShapeVector = js::TraceableVector<Shape*>;
 using StringVector = js::TraceableVector<JSString*>;
 
 /* AutoArrayRooter roots an external array of Values. */
-class AutoArrayRooter : private JS::AutoGCRooter
+class MOZ_RAII AutoArrayRooter : private JS::AutoGCRooter
 {
   public:
     AutoArrayRooter(JSContext* cx, size_t len, Value* vec
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : JS::AutoGCRooter(cx, len), array(vec)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         MOZ_ASSERT(tag_ >= 0);
@@ -744,17 +744,17 @@ class AutoAssertNoException
     {
         MOZ_ASSERT_IF(!hadException, !cx->isExceptionPending());
     }
 };
 
 /* Exposed intrinsics for the JITs. */
 bool intrinsic_IsSuspendedStarGenerator(JSContext* cx, unsigned argc, Value* vp);
 
-class AutoLockForExclusiveAccess
+class MOZ_RAII AutoLockForExclusiveAccess
 {
     JSRuntime* runtime;
 
     void init(JSRuntime* rt) {
         runtime = rt;
         if (runtime->numExclusiveThreads) {
             runtime->assertCanLock(ExclusiveAccessLock);
             PR_Lock(runtime->exclusiveAccessLock);
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -180,17 +180,17 @@ typedef HashMap<CrossCompartmentKey, Rea
 struct ImmediateMetadata { };
 struct DelayMetadata { };
 using PendingMetadata = ReadBarrieredObject;
 
 using NewObjectMetadataState = mozilla::Variant<ImmediateMetadata,
                                                 DelayMetadata,
                                                 PendingMetadata>;
 
-class MOZ_STACK_CLASS AutoSetNewObjectMetadata : private JS::CustomAutoRooter
+class MOZ_RAII AutoSetNewObjectMetadata : private JS::CustomAutoRooter
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
 
     JSContext* cx_;
     NewObjectMetadataState prevState_;
 
     AutoSetNewObjectMetadata(const AutoSetNewObjectMetadata& aOther) = delete;
     void operator=(const AutoSetNewObjectMetadata& aOther) = delete;
@@ -759,17 +759,17 @@ ExclusiveContext::global() const
      * on-stack will be marked automatically, so there's no need for a read
      * barrier on it. Once the compartment is popped, the handle is no longer
      * safe to use.
      */
     MOZ_ASSERT(compartment_, "Caller needs to enter a compartment first");
     return Handle<GlobalObject*>::fromMarkedLocation(compartment_->global_.unsafeGet());
 }
 
-class AssertCompartmentUnchanged
+class MOZ_RAII AssertCompartmentUnchanged
 {
   public:
     explicit AssertCompartmentUnchanged(JSContext* cx
                                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : cx(cx), oldCompartment(cx->compartment())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
@@ -857,30 +857,30 @@ struct WrapperValue
     Value get() const { return value; }
     operator const Value&() const { return value; }
     JSObject& toObject() const { return value.toObject(); }
 
   private:
     Value value;
 };
 
-class AutoWrapperVector : public JS::AutoVectorRooterBase<WrapperValue>
+class MOZ_RAII AutoWrapperVector : public JS::AutoVectorRooterBase<WrapperValue>
 {
   public:
     explicit AutoWrapperVector(JSContext* cx
                                MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : AutoVectorRooterBase<WrapperValue>(cx, WRAPVECTOR)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class AutoWrapperRooter : private JS::AutoGCRooter {
+class MOZ_RAII AutoWrapperRooter : private JS::AutoGCRooter {
   public:
     AutoWrapperRooter(JSContext* cx, WrapperValue v
                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : JS::AutoGCRooter(cx, WRAPPER), value(v)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
 
--- a/js/src/jsfriendapi.h
+++ b/js/src/jsfriendapi.h
@@ -2689,17 +2689,17 @@ typedef void
 
 /*
  * Sets a callback that is run whenever js-ctypes is about to be used when
  * calling into C.
  */
 JS_FRIEND_API(void)
 SetCTypesActivityCallback(JSRuntime* rt, CTypesActivityCallback cb);
 
-class JS_FRIEND_API(AutoCTypesActivityCallback) {
+class MOZ_RAII JS_FRIEND_API(AutoCTypesActivityCallback) {
   private:
     JSContext* cx;
     CTypesActivityCallback callback;
     CTypesActivityType endType;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     AutoCTypesActivityCallback(JSContext* cx, CTypesActivityType beginType,
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -1369,17 +1369,17 @@ class ZoneList
 
 JSObject*
 NewMemoryStatisticsObject(JSContext* cx);
 
 } /* namespace gc */
 
 #ifdef DEBUG
 /* Use this to avoid assertions when manipulating the wrapper map. */
-class AutoDisableProxyCheck
+class MOZ_RAII AutoDisableProxyCheck
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
     gc::GCRuntime& gc;
 
   public:
     explicit AutoDisableProxyCheck(JSRuntime* rt
                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoDisableProxyCheck();
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -163,17 +163,17 @@ template <class T, class U>
 MOZ_ALWAYS_INLINE T&
 ImplicitCast(U& u)
 {
     T& t = u;
     return t;
 }
 
 template<typename T>
-class AutoScopedAssign
+class MOZ_RAII AutoScopedAssign
 {
   public:
     AutoScopedAssign(T* addr, const T& value
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : addr_(addr), old(*addr_)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         *addr_ = value;
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -3395,17 +3395,17 @@ runOffThreadScript(JSContext* cx, unsign
 
     RootedScript script(cx, JS::FinishOffThreadScript(cx, rt, token));
     if (!script)
         return false;
 
     return JS_ExecuteScript(cx, script, args.rval());
 }
 
-struct FreeOnReturn
+struct MOZ_RAII FreeOnReturn
 {
     JSContext* cx;
     const char* ptr;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     explicit FreeOnReturn(JSContext* cx, const char* ptr = nullptr
                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : cx(cx), ptr(ptr)
--- a/js/src/vm/Debugger.cpp
+++ b/js/src/vm/Debugger.cpp
@@ -1819,17 +1819,17 @@ Debugger::slowPathPromiseHook(JSContext*
     // Promise hooks are infallible and we ignore errors from uncaught
     // exceptions by design.
     MOZ_ASSERT(status == JSTRAP_CONTINUE);
 }
 
 
 /*** Debugger code invalidation for observing execution ******************************************/
 
-class MOZ_STACK_CLASS ExecutionObservableCompartments : public Debugger::ExecutionObservableSet
+class MOZ_RAII ExecutionObservableCompartments : public Debugger::ExecutionObservableSet
 {
     HashSet<JSCompartment*> compartments_;
     HashSet<Zone*> zones_;
 
   public:
     explicit ExecutionObservableCompartments(JSContext* cx
                                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : compartments_(cx),
@@ -1853,17 +1853,17 @@ class MOZ_STACK_CLASS ExecutionObservabl
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 // Given a particular AbstractFramePtr F that has become observable, this
 // represents the stack frames that need to be bailed out or marked as
 // debuggees, and the scripts that need to be recompiled, taking inlining into
 // account.
-class MOZ_STACK_CLASS ExecutionObservableFrame : public Debugger::ExecutionObservableSet
+class MOZ_RAII ExecutionObservableFrame : public Debugger::ExecutionObservableSet
 {
     AbstractFramePtr frame_;
 
   public:
     explicit ExecutionObservableFrame(AbstractFramePtr frame
                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : frame_(frame)
     {
@@ -1914,17 +1914,17 @@ class MOZ_STACK_CLASS ExecutionObservabl
         // frame debuggee marking; so this overload doesn't need a parallel to
         // the just-so inlining logic above.
         return iter.hasUsableAbstractFramePtr() && iter.abstractFramePtr() == frame_;
     }
 
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
-class MOZ_STACK_CLASS ExecutionObservableScript : public Debugger::ExecutionObservableSet
+class MOZ_RAII ExecutionObservableScript : public Debugger::ExecutionObservableSet
 {
     RootedScript script_;
 
   public:
     ExecutionObservableScript(JSContext* cx, JSScript* script
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : script_(cx, script)
     {
--- a/js/src/vm/HelperThreads.h
+++ b/js/src/vm/HelperThreads.h
@@ -407,33 +407,33 @@ struct AutoEnqueuePendingParseTasksAfter
     explicit AutoEnqueuePendingParseTasksAfterGC(const gc::GCRuntime& gc) : gc_(gc) {}
     ~AutoEnqueuePendingParseTasksAfterGC();
 };
 
 /* Start a compression job for the specified token. */
 bool
 StartOffThreadCompression(ExclusiveContext* cx, SourceCompressionTask* task);
 
-class AutoLockHelperThreadState
+class MOZ_RAII AutoLockHelperThreadState
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoLockHelperThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         HelperThreadState().lock();
     }
 
     ~AutoLockHelperThreadState() {
         HelperThreadState().unlock();
     }
 };
 
-class AutoUnlockHelperThreadState
+class MOZ_RAII AutoUnlockHelperThreadState
 {
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
 
     explicit AutoUnlockHelperThreadState(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/src/vm/Interpreter.cpp
+++ b/js/src/vm/Interpreter.cpp
@@ -370,17 +370,17 @@ ExecuteState::pushInterpreterFrame(JSCon
 }
 namespace js {
 // Implementation of per-performance group performance measurement.
 //
 //
 // All mutable state is stored in `Runtime::stopwatch` (per-process
 // performance stats and logistics) and in `PerformanceGroup` (per
 // group performance stats).
-class AutoStopwatch final
+class MOZ_RAII AutoStopwatch final
 {
     // The context with which this object was initialized.
     // Non-null.
     JSContext* const cx_;
 
     // An indication of the number of times we have entered the event
     // loop.  Used only for comparison.
     uint64_t iteration_;
--- a/js/src/vm/RegExpStatics.h
+++ b/js/src/vm/RegExpStatics.h
@@ -181,17 +181,17 @@ class RegExpStatics
         return offsetof(RegExpStatics, lazyIndex);
     }
 
     static size_t offsetOfPendingLazyEvaluation() {
         return offsetof(RegExpStatics, pendingLazyEvaluation);
     }
 };
 
-class AutoRegExpStaticsBuffer : private JS::CustomAutoRooter
+class MOZ_RAII AutoRegExpStaticsBuffer : private JS::CustomAutoRooter
 {
   public:
     explicit AutoRegExpStaticsBuffer(JSContext* cx
                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : CustomAutoRooter(cx), statics(RegExpStatics::InitBuffer())
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     }
--- a/js/src/vm/Runtime.h
+++ b/js/src/vm/Runtime.h
@@ -1859,17 +1859,17 @@ FreeOp::freeLater(void* p)
 }
 
 /*
  * RAII class that takes the GC lock while it is live.
  *
  * Note that the lock may be temporarily released by use of AutoUnlockGC when
  * passed a non-const reference to this class.
  */
-class MOZ_STACK_CLASS AutoLockGC
+class MOZ_RAII AutoLockGC
 {
   public:
     explicit AutoLockGC(JSRuntime* rt
                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : runtime_(rt), wasUnlocked_(false)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         lock();
@@ -1898,17 +1898,17 @@ class MOZ_STACK_CLASS AutoLockGC
     JSRuntime* runtime_;
     mozilla::DebugOnly<bool> wasUnlocked_;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     AutoLockGC(const AutoLockGC&) = delete;
     AutoLockGC& operator=(const AutoLockGC&) = delete;
 };
 
-class MOZ_STACK_CLASS AutoUnlockGC
+class MOZ_RAII AutoUnlockGC
 {
   public:
     explicit AutoUnlockGC(AutoLockGC& lock
                           MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : lock(lock)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         lock.unlock();
@@ -1921,17 +1921,17 @@ class MOZ_STACK_CLASS AutoUnlockGC
   private:
     AutoLockGC& lock;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     AutoUnlockGC(const AutoUnlockGC&) = delete;
     AutoUnlockGC& operator=(const AutoUnlockGC&) = delete;
 };
 
-class MOZ_STACK_CLASS AutoKeepAtoms
+class MOZ_RAII AutoKeepAtoms
 {
     PerThreadData* pt;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
   public:
     explicit AutoKeepAtoms(PerThreadData* pt
                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : pt(pt)
@@ -2090,17 +2090,17 @@ class RuntimeAllocPolicy
     void free_(void* p) { js_free(p); }
     void reportAllocOverflow() const {}
 };
 
 extern const JSSecurityCallbacks NullSecurityCallbacks;
 
 // Debugging RAII class which marks the current thread as performing an Ion
 // compilation, for use by CurrentThreadCan{Read,Write}CompilationData
-class AutoEnterIonCompilation
+class MOZ_RAII AutoEnterIonCompilation
 {
   public:
     explicit AutoEnterIonCompilation(bool safeForMinorGC
                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM) {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
 #ifdef DEBUG
         PerThreadData* pt = js::TlsPerThreadData.get();
--- a/js/src/vm/SPSProfiler.h
+++ b/js/src/vm/SPSProfiler.h
@@ -222,17 +222,17 @@ class AutoSPSLock
   private:
     PRLock* lock_;
 };
 
 /*
  * This class is used to suppress profiler sampling during
  * critical sections where stack state is not valid.
  */
-class AutoSuppressProfilerSampling
+class MOZ_RAII AutoSuppressProfilerSampling
 {
   public:
     explicit AutoSuppressProfilerSampling(JSContext* cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     explicit AutoSuppressProfilerSampling(JSRuntime* rt MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
 
     ~AutoSuppressProfilerSampling();
 
   private:
@@ -255,17 +255,17 @@ SPSProfiler::stringsReset()
     strings.clear();
 }
 
 /*
  * This class is used in RunScript() to push the marker onto the sampling stack
  * that we're about to enter JS function calls. This is the only time in which a
  * valid stack pointer is pushed to the sampling stack.
  */
-class SPSEntryMarker
+class MOZ_RAII SPSEntryMarker
 {
   public:
     explicit SPSEntryMarker(JSRuntime* rt,
                             JSScript* script
                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~SPSEntryMarker();
 
   private:
@@ -274,17 +274,17 @@ class SPSEntryMarker
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 /*
  * This class is used in the interpreter to bound regions where the baseline JIT
  * being entered via OSR.  It marks the current top pseudostack entry as
  * OSR-ed
  */
-class SPSBaselineOSRMarker
+class MOZ_RAII SPSBaselineOSRMarker
 {
   public:
     explicit SPSBaselineOSRMarker(JSRuntime* rt, bool hasSPSFrame
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~SPSBaselineOSRMarker();
 
   private:
     SPSProfiler* profiler;
--- a/js/src/vm/SavedStacks.h
+++ b/js/src/vm/SavedStacks.h
@@ -176,17 +176,17 @@ class SavedStacks {
     double          allocationSamplingProbability;
     uint32_t        allocationSkipCount;
     uint64_t        rngState;
     bool            creatingSavedFrame;
 
     // Similar to mozilla::ReentrancyGuard, but instead of asserting against
     // reentrancy, just change the behavior of SavedStacks::saveCurrentStack to
     // return a nullptr SavedFrame.
-    struct MOZ_STACK_CLASS AutoReentrancyGuard {
+    struct MOZ_RAII AutoReentrancyGuard {
         MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
         SavedStacks& stacks;
 
         explicit AutoReentrancyGuard(SavedStacks& stacks MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
             : stacks(stacks)
         {
             MOZ_GUARD_OBJECT_NOTIFIER_INIT;
             stacks.creatingSavedFrame = true;
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -820,17 +820,17 @@ CloneNestedScopeObject(JSContext* cx, Ha
 /*****************************************************************************/
 
 // A scope iterator describes the active scopes starting from a dynamic scope,
 // static scope pair. This pair may be derived from the current point of
 // execution in a frame. If derived in such a fashion, the ScopeIter tracks
 // whether the current scope is within the extent of this initial frame.
 // Here, "frame" means a single activation of: a function, eval, or global
 // code.
-class ScopeIter
+class MOZ_RAII ScopeIter
 {
     StaticScopeIter<CanGC> ssi_;
     RootedObject scope_;
     AbstractFramePtr frame_;
 
     void incrementStaticScopeIter();
     void settle();
 
--- a/js/src/vm/Shape.h
+++ b/js/src/vm/Shape.h
@@ -977,17 +977,17 @@ class AccessorShape : public Shape
 
 inline
 StackBaseShape::StackBaseShape(Shape* shape)
   : flags(shape->getObjectFlags()),
     clasp(shape->getObjectClass()),
     compartment(shape->compartment())
 {}
 
-class AutoRooterGetterSetter
+class MOZ_RAII AutoRooterGetterSetter
 {
     class Inner : private JS::CustomAutoRooter
     {
       public:
         inline Inner(ExclusiveContext* cx, uint8_t attrs, GetterOp* pgetter_, SetterOp* psetter_);
 
       private:
         virtual void trace(JSTracer* trc);
--- a/js/src/vm/TraceLogging.h
+++ b/js/src/vm/TraceLogging.h
@@ -434,17 +434,17 @@ inline void TraceLogStartEventPrivate(Tr
 inline void TraceLogStopEventPrivate(TraceLoggerThread* logger, uint32_t id) {
 #ifdef JS_TRACE_LOGGING
     if (logger)
         logger->stopEvent(id);
 #endif
 }
 
 // Automatic logging at the start and end of function call.
-class AutoTraceLog
+class MOZ_RAII AutoTraceLog
 {
 #ifdef JS_TRACE_LOGGING
     TraceLoggerThread* logger;
     union {
         const TraceLoggerEvent* event;
         TraceLoggerTextId id;
     } payload;
     bool isEvent;
--- a/js/src/vm/TraceLoggingGraph.cpp
+++ b/js/src/vm/TraceLoggingGraph.cpp
@@ -19,17 +19,17 @@
 #  define TRACE_LOG_DIR "/tmp/"
 # endif
 #endif
 
 using mozilla::NativeEndian;
 
 TraceLoggerGraphState* traceLoggerGraphState = nullptr;
 
-class AutoTraceLoggerGraphStateLock
+class MOZ_RAII AutoTraceLoggerGraphStateLock
 {
   TraceLoggerGraphState* graph;
 
   public:
     explicit AutoTraceLoggerGraphStateLock(TraceLoggerGraphState* graph MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : graph(graph)
     {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/js/xpconnect/src/xpcprivate.h
+++ b/js/xpconnect/src/xpcprivate.h
@@ -3035,17 +3035,17 @@ private:
     // Complete stackframe where the error happened.
     // Must be SavedFrame object.
     JS::Heap<JSObject*>  mStack;
 };
 
 /******************************************************************************
  * Handles pre/post script processing.
  */
-class MOZ_STACK_CLASS AutoScriptEvaluate
+class MOZ_RAII AutoScriptEvaluate
 {
 public:
     /**
      * Saves the JSContext as well as initializing our state
      * @param cx The JSContext, this can be null, we don't do anything then
      */
     explicit AutoScriptEvaluate(JSContext * cx MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
          : mJSContext(cx), mEvaluated(false) {
@@ -3072,17 +3072,17 @@ private:
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
     // No copying or assignment allowed
     AutoScriptEvaluate(const AutoScriptEvaluate&) = delete;
     AutoScriptEvaluate & operator =(const AutoScriptEvaluate&) = delete;
 };
 
 /***************************************************************************/
-class MOZ_STACK_CLASS AutoResolveName
+class MOZ_RAII AutoResolveName
 {
 public:
     AutoResolveName(XPCCallContext& ccx, JS::HandleId name
                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM) :
           mOld(ccx, XPCJSRuntime::Get()->SetResolveName(name))
 #ifdef DEBUG
           ,mCheck(ccx, name)
 #endif
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -41,16 +41,17 @@
 #include "nsContentList.h"
 #include "nsAttrValueInlines.h"
 #include "mozilla/dom/Selection.h"
 #include "nsContentUtils.h"
 #include "nsTextNode.h"
 #include "nsStyleSet.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/MathAlgorithms.h"
+#include "nsFrameSelection.h"
 
 #define DEFAULT_COLUMN_WIDTH 20
 
 using namespace mozilla;
 
 nsIFrame*
 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
@@ -254,16 +255,23 @@ nsTextControlFrame::EnsureEditorInitiali
   // blockers don't prevent the sink flushing out content and notifying in the
   // process, which can destroy frames.
   doc->FlushPendingNotifications(Flush_ContentAndNotify);
   NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE);
 
   // Make sure that editor init doesn't do things that would kill us off
   // (especially off the script blockers it'll create for its DOM mutations).
   {
+    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
+    MOZ_ASSERT(txtCtrl, "Content not a text control element");
+
+    // Hide selection changes during the initialization, as webpages should not
+    // be aware of these initializations
+    AutoHideSelectionChanges hideSelectionChanges(txtCtrl->GetConstFrameSelection());
+
     nsAutoScriptBlocker scriptBlocker;
 
     // Time to mess with our security context... See comments in GetValue()
     // for why this is needed.
     mozilla::dom::AutoNoJSAPI nojsapi;
 
     // Make sure that we try to focus the content even if the method fails
     class EnsureSetFocus {
@@ -283,18 +291,16 @@ nsTextControlFrame::EnsureEditorInitiali
     // Make sure we are not being called again until we're finished.
     // If reentrancy happens, just pretend that we don't have an editor.
     const EditorInitializerEntryTracker tracker(*this);
     NS_ASSERTION(!tracker.EnteredMoreThanOnce(),
                  "EnsureEditorInitialized has been called while a previous call was in progress");
 #endif
 
     // Create an editor for the frame, if one doesn't already exist
-    nsCOMPtr<nsITextControlElement> txtCtrl = do_QueryInterface(GetContent());
-    NS_ASSERTION(txtCtrl, "Content not a text control element");
     nsresult rv = txtCtrl->CreateEditor();
     NS_ENSURE_SUCCESS(rv, rv);
     NS_ENSURE_STATE(weakFrame.IsAlive());
 
     // Set mEditorHasBeenInitialized so that subsequent calls will use the
     // editor.
     mEditorHasBeenInitialized = true;
 
--- a/layout/generic/Selection.h
+++ b/layout/generic/Selection.h
@@ -103,17 +103,17 @@ public:
   nsresult      SubtractRange(RangeData* aRange, nsRange* aSubtract,
                               nsTArray<RangeData>* aOutput);
   /**
    * AddItem adds aRange to this Selection.  If mApplyUserSelectStyle is true,
    * then aRange is first scanned for -moz-user-select:none nodes and split up
    * into multiple ranges to exclude those before adding the resulting ranges
    * to this Selection.
    */
-  nsresult      AddItem(nsRange* aRange, int32_t* aOutIndex);
+  nsresult      AddItem(nsRange* aRange, int32_t* aOutIndex, bool aNoStartSelect = false);
   nsresult      RemoveItem(nsRange* aRange);
   nsresult      RemoveCollapsedRanges();
   nsresult      Clear(nsPresContext* aPresContext);
   nsresult      Collapse(nsINode* aParentNode, int32_t aOffset);
   nsresult      Extend(nsINode* aParentNode, int32_t aOffset);
   nsRange*      GetRangeAt(int32_t aIndex);
 
   // Get the anchor-to-focus range if we don't care which end is
@@ -199,41 +199,45 @@ public:
                             bool aAllowAdjacent,
                             nsTArray<nsRefPtr<nsRange>>& aReturn,
                             mozilla::ErrorResult& aRv);
 
   void ScrollIntoView(int16_t aRegion, bool aIsSynchronous,
                       int16_t aVPercent, int16_t aHPercent,
                       mozilla::ErrorResult& aRv);
 
+  void AddSelectionChangeBlocker();
+  void RemoveSelectionChangeBlocker();
+  bool IsBlockingSelectionChangeEvents() const;
 private:
   friend class ::nsAutoScrollTimer;
 
   // Note: DoAutoScroll might destroy arbitrary frames etc.
   nsresult DoAutoScroll(nsIFrame *aFrame, nsPoint& aPoint);
 
 public:
   SelectionType GetType(){return mType;}
   void          SetType(SelectionType aType){mType = aType;}
 
   nsresult     NotifySelectionListeners();
 
   friend struct AutoApplyUserSelectStyle;
-  struct MOZ_STACK_CLASS AutoApplyUserSelectStyle
+  struct MOZ_RAII AutoApplyUserSelectStyle
   {
     explicit AutoApplyUserSelectStyle(Selection* aSelection
                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mSavedValue(aSelection->mApplyUserSelectStyle)
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       aSelection->mApplyUserSelectStyle = true;
     }
     AutoRestore<bool> mSavedValue;
     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
   };
+
 private:
   friend struct mozilla::AutoPrepareFocusRange;
   class ScrollSelectionIntoViewEvent;
   friend class ScrollSelectionIntoViewEvent;
 
   class ScrollSelectionIntoViewEvent : public nsRunnable {
   public:
     NS_DECL_NSIRUNNABLE
@@ -277,16 +281,18 @@ private:
                             nsINode* aEndNode, int32_t aEndOffset,
                             int32_t aRangeIndex);
   nsresult GetIndicesForInterval(nsINode* aBeginNode, int32_t aBeginOffset,
                                  nsINode* aEndNode, int32_t aEndOffset,
                                  bool aAllowAdjacent,
                                  int32_t* aStartIndex, int32_t* aEndIndex);
   RangeData* FindRangeData(nsIDOMRange* aRange);
 
+  void UserSelectRangesToAdd(nsRange* aItem, nsTArray<nsRefPtr<nsRange> >& rangesToAdd);
+
   /**
    * Helper method for AddItem.
    */
   nsresult AddItemInternal(nsRange* aRange, int32_t* aOutIndex);
 
   // These are the ranges inside this selection. They are kept sorted in order
   // of DOM start position.
   //
@@ -307,19 +313,24 @@ private:
   nsRefPtr<nsAutoScrollTimer> mAutoScrollTimer;
   nsCOMArray<nsISelectionListener> mSelectionListeners;
   nsRevocableEventPtr<ScrollSelectionIntoViewEvent> mScrollEvent;
   CachedOffsetForFrame *mCachedOffsetForFrame;
   nsDirection mDirection;
   SelectionType mType;
   /**
    * True if the current selection operation was initiated by user action.
-   * It determines whether we exclude -moz-user-select:none nodes or not.
+   * It determines whether we exclude -moz-user-select:none nodes or not,
+   * as well as whether selectstart events will be fired.
    */
   bool mApplyUserSelectStyle;
+
+  // Non-zero if we don't want any changes we make to the selection to be
+  // visible to content. If non-zero, content won't be notified about changes.
+  uint32_t mSelectionChangeBlockerCount;
 };
 
 // Stack-class to turn on/off selection batching.
 class MOZ_STACK_CLASS SelectionBatcher final
 {
 private:
   nsRefPtr<Selection> mSelection;
 public:
@@ -334,12 +345,39 @@ public:
   ~SelectionBatcher()
   {
     if (mSelection) {
       mSelection->EndBatchChanges();
     }
   }
 };
 
+class MOZ_STACK_CLASS AutoHideSelectionChanges final
+{
+private:
+  nsRefPtr<Selection> mSelection;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
+public:
+  explicit AutoHideSelectionChanges(const nsFrameSelection* aFrame);
+
+  explicit AutoHideSelectionChanges(Selection* aSelection
+                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+    : mSelection(aSelection)
+  {
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+    mSelection = aSelection;
+    if (mSelection) {
+      mSelection->AddSelectionChangeBlocker();
+    }
+  }
+
+  ~AutoHideSelectionChanges()
+  {
+    if (mSelection) {
+      mSelection->RemoveSelectionChangeBlocker();
+    }
+  }
+};
+
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_Selection_h__
new file mode 100644
--- /dev/null
+++ b/layout/generic/SelectionChangeListener.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_SelectionChangeListener_h_
+#define mozilla_SelectionChangeListener_h_
+
+#include "nsISelectionListener.h"
+#include "nsISelectionPrivate.h"
+#include "mozilla/Attributes.h"
+
+namespace mozilla {
+namespace dom {
+
+class SelectionChangeListener final : public nsISelectionListener
+{
+public:
+  // SelectionChangeListener has to participate in cycle collection because
+  // it holds strong references to nsINodes in its mOldRanges array.
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(SelectionChangeListener)
+  NS_DECL_NSISELECTIONLISTENER
+
+  // This field is used to keep track of the ranges which were present in the
+  // selection when the selectionchange event was previously fired. This allows
+  // for the selectionchange event to only be fired when a selection is actually
+  // changed.
+  struct RawRangeData
+  {
+    // These properties are not void*s to avoid the potential situation where the
+    // nsINode is freed, and a new nsINode is allocated with the same address, which
+    // could potentially break the comparison logic. In reality, this is extremely
+    // unlikely to occur (potentially impossible), but these nsCOMPtrs are safer.
+    // They are never dereferenced.
+    nsCOMPtr<nsINode> mStartParent;
+    nsCOMPtr<nsINode> mEndParent;
+
+    // XXX These are int32_ts on nsRange, but uint32_ts in the return value
+    // of GetStart_, so I use uint32_ts here. See bug 1194256.
+    uint32_t mStartOffset;
+    uint32_t mEndOffset;
+
+    explicit RawRangeData(const nsRange* aRange);
+    bool Equals(const nsRange* aRange);
+  };
+
+private:
+  nsTArray<RawRangeData> mOldRanges;
+
+  ~SelectionChangeListener() {}
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // mozilla_SelectionChangeListener_h_
--- a/layout/generic/nsFlexContainerFrame.cpp
+++ b/layout/generic/nsFlexContainerFrame.cpp
@@ -3618,17 +3618,17 @@ nsFlexContainerFrame::Reflow(nsPresConte
                  contentBoxMainSize, availableBSizeForContent,
                  struts, axisTracker);
   }
 }
 
 // RAII class to clean up a list of FlexLines.
 // Specifically, this removes each line from the list, deletes all the
 // FlexItems in its list, and deletes the FlexLine.
-class MOZ_STACK_CLASS AutoFlexLineListClearer
+class MOZ_RAII AutoFlexLineListClearer
 {
 public:
   explicit AutoFlexLineListClearer(LinkedList<FlexLine>& aLines
                                    MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   : mLines(aLines)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   }
--- a/layout/generic/nsFrameSelection.h
+++ b/layout/generic/nsFrameSelection.h
@@ -747,11 +747,13 @@ private:
   bool mChangesDuringBatching;
   bool mNotifyFrames;
   bool mDragSelectingCells;
   bool mDragState;   //for drag purposes
   bool mMouseDoubleDownState; //has the doubleclick down happened
   bool mDesiredPosSet;
 
   int8_t mCaretMovementStyle;
+
+  static bool sSelectionEventsEnabled;
 };
 
 #endif /* nsFrameSelection_h___ */
--- a/layout/generic/nsSelection.cpp
+++ b/layout/generic/nsSelection.cpp
@@ -63,27 +63,29 @@ static NS_DEFINE_CID(kFrameTraversalCID,
 #include "nsITimer.h"
 #include "nsFrameManager.h"
 // notifications
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 
 #include "nsISelectionController.h"//for the enums
 #include "nsAutoCopyListener.h"
+#include "SelectionChangeListener.h"
 #include "nsCopySupport.h"
 #include "nsIClipboard.h"
 #include "nsIFrameInlines.h"
 
 #include "nsIBidiKeyboard.h"
 
 #include "nsError.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/ShadowRoot.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/SelectionBinding.h"
+#include "mozilla/AsyncEventDispatcher.h"
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //#define DEBUG_TABLE 1
 
 static bool IsValidSelectionPoint(nsFrameSelection *aFrameSel, nsINode *aNode);
 
@@ -327,17 +329,17 @@ IsValidSelectionPoint(nsFrameSelection *
     return false; //not in the right content. tLimiter said so
   }
 
   limiter = aFrameSel->GetAncestorLimiter();
   return !limiter || nsContentUtils::ContentIsDescendantOf(aNode, limiter);
 }
 
 namespace mozilla {
-struct MOZ_STACK_CLASS AutoPrepareFocusRange
+struct MOZ_RAII AutoPrepareFocusRange
 {
   AutoPrepareFocusRange(Selection* aSelection,
                         bool aContinueSelection,
                         bool aMultipleSelection
                         MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
@@ -407,17 +409,17 @@ struct MOZ_STACK_CLASS AutoPrepareFocusR
     aSelection->mAnchorFocusRange = range;
 
     // Remove all generated ranges (including the old mAnchorFocusRange).
     nsRefPtr<nsPresContext> presContext = aSelection->GetPresContext();
     size_t i = len;
     while (i--) {
       range = aSelection->mRanges[i].mRange;
       if (range->IsGenerated()) {
-        range->SetInSelection(false);
+        range->SetSelection(nullptr);
         aSelection->selectFrames(presContext, range, false);
         aSelection->mRanges.RemoveElementAt(i);
       }
     }
     if (aSelection->mFrameSelection) {
       aSelection->mFrameSelection->InvalidateDesiredPos();
     }
   }
@@ -809,16 +811,26 @@ void
 nsFrameSelection::Init(nsIPresShell *aShell, nsIContent *aLimiter)
 {
   mShell = aShell;
   mDragState = false;
   mDesiredPosSet = false;
   mLimiter = aLimiter;
   mCaretMovementStyle =
     Preferences::GetInt("bidi.edit.caret_movement_style", 2);
+
+  // This should only ever be initialized on the main thread, so we are OK here.
+  static bool prefCachesInitialized = false;
+  if (!prefCachesInitialized) {
+    prefCachesInitialized = true;
+
+    Preferences::AddBoolVarCache(&sSelectionEventsEnabled,
+                                 "dom.select_events.enabled", false);
+  }
+
   // Set touch caret as selection listener
   nsRefPtr<TouchCaret> touchCaret = mShell->GetTouchCaret();
   if (touchCaret) {
     int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
     if (mDomSelections[index]) {
       mDomSelections[index]->AddSelectionListener(touchCaret);
     }
   }
@@ -834,17 +846,30 @@ nsFrameSelection::Init(nsIPresShell *aSh
 
   nsRefPtr<AccessibleCaretEventHub> eventHub = mShell->GetAccessibleCaretEventHub();
   if (eventHub) {
     int8_t index = GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
     if (mDomSelections[index]) {
       mDomSelections[index]->AddSelectionListener(eventHub);
     }
   }
-}
+
+  if (sSelectionEventsEnabled) {
+    int8_t index =
+      GetIndexFromSelectionType(nsISelectionController::SELECTION_NORMAL);
+    if (mDomSelections[index]) {
+      // The Selection instance will hold a strong reference to its selectionchangelistener
+      // so we don't have to worry about that!
+      nsRefPtr<SelectionChangeListener> listener = new SelectionChangeListener;
+      mDomSelections[index]->AddSelectionListener(listener);
+    }
+  }
+}
+
+bool nsFrameSelection::sSelectionEventsEnabled = false;
 
 nsresult
 nsFrameSelection::MoveCaret(nsDirection       aDirection,
                             bool              aContinueSelection,
                             nsSelectionAmount aAmount,
                             CaretMovementStyle aMovementStyle)
 {
   bool visualMovement = aMovementStyle == eVisual ||
@@ -3302,35 +3327,37 @@ nsFrameSelection::DisconnectFromPresShel
 
 // note: this can return a nil anchor node
 
 Selection::Selection()
   : mCachedOffsetForFrame(nullptr)
   , mDirection(eDirNext)
   , mType(nsISelectionController::SELECTION_NORMAL)
   , mApplyUserSelectStyle(false)
+  , mSelectionChangeBlockerCount(0)
 {
 }
 
 Selection::Selection(nsFrameSelection* aList)
   : mFrameSelection(aList)
   , mCachedOffsetForFrame(nullptr)
   , mDirection(eDirNext)
   , mType(nsISelectionController::SELECTION_NORMAL)
   , mApplyUserSelectStyle(false)
+  , mSelectionChangeBlockerCount(0)
 {
 }
 
 Selection::~Selection()
 {
   setAnchorFocusRange(-1);
 
   uint32_t count = mRanges.Length();
   for (uint32_t i = 0; i < count; ++i) {
-    mRanges[i].mRange->SetInSelection(false);
+    mRanges[i].mRange->SetSelection(nullptr);
   }
 
   if (mAutoScrollTimer) {
     mAutoScrollTimer->Stop();
     mAutoScrollTimer = nullptr;
   }
 
   mScrollEvent.Revoke();
@@ -3642,40 +3669,96 @@ Selection::SubtractRange(RangeData* aRan
         return NS_ERROR_OUT_OF_MEMORY;
       (*aOutput)[0].mTextRangeStyle = aRange->mTextRangeStyle;
     }
   }
 
   return NS_OK;
 }
 
+void
+Selection::UserSelectRangesToAdd(nsRange* aItem, nsTArray<nsRefPtr<nsRange>>& aRangesToAdd)
+{
+  aItem->ExcludeNonSelectableNodes(&aRangesToAdd);
+  if (aRangesToAdd.IsEmpty()) {
+    ErrorResult err;
+    nsINode* node = aItem->GetStartContainer(err);
+    if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
+      // A contenteditable node with user-select:none, for example.
+      // Allow it to have a collapsed selection (for the caret).
+      aItem->Collapse(GetDirection() == eDirPrevious);
+      aRangesToAdd.AppendElement(aItem);
+    }
+  }
+}
+
 nsresult
-Selection::AddItem(nsRange* aItem, int32_t* aOutIndex)
+Selection::AddItem(nsRange* aItem, int32_t* aOutIndex, bool aNoStartSelect)
 {
   if (!aItem)
     return NS_ERROR_NULL_POINTER;
   if (!aItem->IsPositioned())
     return NS_ERROR_UNEXPECTED;
 
   NS_ASSERTION(aOutIndex, "aOutIndex can't be null");
 
+  // XXX Rename mApplyUserSelectStyle? Not the best name (as it is also being
+  // used to detect here whether the event is user initiated for the purposes of
+  // dispatching the selectstart event).
   if (mApplyUserSelectStyle) {
     nsAutoTArray<nsRefPtr<nsRange>, 4> rangesToAdd;
-    aItem->ExcludeNonSelectableNodes(&rangesToAdd);
-    if (rangesToAdd.IsEmpty()) {
-      ErrorResult err;
-      nsINode* node = aItem->GetStartContainer(err);
-      if (node && node->IsContent() && node->AsContent()->GetEditingHost()) {
-        // A contenteditable node with user-select:none, for example.
-        // Allow it to have a collapsed selection (for the caret).
-        aItem->Collapse(GetDirection() == eDirPrevious);
-        rangesToAdd.AppendElement(aItem);
+    *aOutIndex = -1;
+
+    if (!aNoStartSelect && mType == nsISelectionController::SELECTION_NORMAL &&
+        nsFrameSelection::sSelectionEventsEnabled && Collapsed() &&
+        !IsBlockingSelectionChangeEvents()) {
+      // First, we generate the ranges to add with a scratch range, which is a
+      // clone of the original range passed in. We do this seperately, because the
+      // selectstart event could have caused the world to change, and required
+      // ranges to be re-generated
+      nsRefPtr<nsRange> scratchRange = aItem->CloneRange();
+      UserSelectRangesToAdd(scratchRange, rangesToAdd);
+      bool newRangesNonEmpty = rangesToAdd.Length() > 1 ||
+        (rangesToAdd.Length() == 1 && !rangesToAdd[0]->Collapsed());
+
+      MOZ_ASSERT(!newRangesNonEmpty || nsContentUtils::IsSafeToRunScript());
+      if (newRangesNonEmpty && nsContentUtils::IsSafeToRunScript()) {
+        // We consider a selection to be starting if we are currently collapsed,
+        // and the selection is becoming uncollapsed, and this is caused by a user
+        // initiated event.
+        bool defaultAction = true;
+
+        // Get the first element which isn't in a native anonymous subtree
+        nsCOMPtr<nsINode> target = aItem->GetStartParent();
+        while (target && target->IsInNativeAnonymousSubtree()) {
+          target = target->GetParent();
+        }
+
+        nsContentUtils::DispatchTrustedEvent(GetParentObject(), target,
+                                             NS_LITERAL_STRING("selectstart"),
+                                             true, true, &defaultAction);
+
+        if (!defaultAction) {
+          return NS_OK;
+        }
+
+        // As we just dispatched an event to the DOM, something could have
+        // changed under our feet. Re-generate the rangesToAdd array, and ensure
+        // that the range we are about to add is still valid.
+        if (!aItem->IsPositioned()) {
+          return NS_ERROR_UNEXPECTED;
+        }
       }
+
+      // The scratch ranges we generated may be invalid now, throw them out
+      rangesToAdd.ClearAndRetainStorage();
     }
-    *aOutIndex = -1;
+
+    // Generate the ranges to add
+    UserSelectRangesToAdd(aItem, rangesToAdd);
     size_t newAnchorFocusIndex =
       GetDirection() == eDirPrevious ? 0 : rangesToAdd.Length() - 1;
     for (size_t i = 0; i < rangesToAdd.Length(); ++i) {
       int32_t index;
       nsresult rv = AddItemInternal(rangesToAdd[i], &index);
       NS_ENSURE_SUCCESS(rv, rv);
       if (i == newAnchorFocusIndex) {
         *aOutIndex = index;
@@ -3697,17 +3780,17 @@ Selection::AddItemInternal(nsRange* aIte
   MOZ_ASSERT(aOutIndex);
 
   *aOutIndex = -1;
 
   // a common case is that we have no ranges yet
   if (mRanges.Length() == 0) {
     if (!mRanges.AppendElement(RangeData(aItem)))
       return NS_ERROR_OUT_OF_MEMORY;
-    aItem->SetInSelection(true);
+    aItem->SetSelection(this);
 
     *aOutIndex = 0;
     return NS_OK;
   }
 
   int32_t startIndex, endIndex;
   nsresult rv = GetIndicesForInterval(aItem->GetStartParent(),
                                       aItem->StartOffset(),
@@ -3737,17 +3820,17 @@ Selection::AddItemInternal(nsRange* aIte
     *aOutIndex = startIndex;
     return NS_OK;
   }
 
   if (startIndex == endIndex) {
     // The new range doesn't overlap any existing ranges
     if (!mRanges.InsertElementAt(startIndex, RangeData(aItem)))
       return NS_ERROR_OUT_OF_MEMORY;
-    aItem->SetInSelection(true);
+    aItem->SetSelection(this);
     *aOutIndex = startIndex;
     return NS_OK;
   }
 
   // We now know that at least 1 existing range overlaps with the range that
   // we are trying to add. In fact, the only ranges of interest are those at
   // the two end points, startIndex and endIndex - 1 (which may point to the
   // same range) as these may partially overlap the new range. Any ranges
@@ -3759,17 +3842,17 @@ Selection::AddItemInternal(nsRange* aIte
 
   if (endIndex - 1 != startIndex) {
     if (!overlaps.InsertElementAt(1, mRanges[endIndex - 1]))
       return NS_ERROR_OUT_OF_MEMORY;
   }
 
   // Remove all the overlapping ranges
   for (int32_t i = startIndex; i < endIndex; ++i) {
-    mRanges[i].mRange->SetInSelection(false);
+    mRanges[i].mRange->SetSelection(nullptr);
   }
   mRanges.RemoveElementsAt(startIndex, endIndex - startIndex);
 
   nsTArray<RangeData> temp;
   for (int32_t i = overlaps.Length() - 1; i >= 0; i--) {
     nsresult rv = SubtractRange(&overlaps[i], aItem, &temp);
     NS_ENSURE_SUCCESS(rv, rv);
   }
@@ -3784,17 +3867,17 @@ Selection::AddItemInternal(nsRange* aIte
   if (!temp.InsertElementAt(insertionPoint, RangeData(aItem)))
     return NS_ERROR_OUT_OF_MEMORY;
 
   // Merge the leftovers back in to mRanges
   if (!mRanges.InsertElementsAt(startIndex, temp))
     return NS_ERROR_OUT_OF_MEMORY;
 
   for (uint32_t i = 0; i < temp.Length(); ++i) {
-    temp[i].mRange->SetInSelection(true);
+    temp[i].mRange->SetSelection(this);
   }
 
   *aOutIndex = startIndex + insertionPoint;
   return NS_OK;
 }
 
 nsresult
 Selection::RemoveItem(nsRange* aItem)
@@ -3813,17 +3896,17 @@ Selection::RemoveItem(nsRange* aItem)
       idx = (int32_t)i;
       break;
     }
   }
   if (idx < 0)
     return NS_ERROR_INVALID_ARG;
 
   mRanges.RemoveElementAt(idx);
-  aItem->SetInSelection(false);
+  aItem->SetSelection(nullptr);
   return NS_OK;
 }
 
 nsresult
 Selection::RemoveCollapsedRanges()
 {
   uint32_t i = 0;
   while (i < mRanges.Length()) {
@@ -3838,17 +3921,17 @@ Selection::RemoveCollapsedRanges()
 }
 
 nsresult
 Selection::Clear(nsPresContext* aPresContext)
 {
   setAnchorFocusRange(-1);
 
   for (uint32_t i = 0; i < mRanges.Length(); ++i) {
-    mRanges[i].mRange->SetInSelection(false);
+    mRanges[i].mRange->SetSelection(nullptr);
     selectFrames(aPresContext, mRanges[i].mRange, false);
   }
   mRanges.Clear();
 
   // Reset direction so for more dependable table selection range handling
   SetDirection(eDirNext);
 
   // If this was an ATTENTION selection, change it back to normal now
@@ -5059,22 +5142,24 @@ Selection::GetRangeAt(int32_t aIndex)
 /*
 utility function
 */
 nsresult
 Selection::SetAnchorFocusToRange(nsRange* aRange)
 {
   NS_ENSURE_STATE(mAnchorFocusRange);
 
+  bool collapsed = Collapsed();
+
   nsresult res = RemoveItem(mAnchorFocusRange);
   if (NS_FAILED(res))
     return res;
 
   int32_t aOutIndex = -1;
-  res = AddItem(aRange, &aOutIndex);
+  res = AddItem(aRange, &aOutIndex, !collapsed);
   if (NS_FAILED(res))
     return res;
   setAnchorFocusRange(aOutIndex);
 
   return NS_OK;
 }
 
 void
@@ -5466,29 +5551,26 @@ Selection::SelectAllChildren(nsIDOMNode*
   NS_ENSURE_TRUE(node, NS_ERROR_INVALID_ARG);
   SelectAllChildren(*node, result);
   return result.StealNSResult();
 }
 
 void
 Selection::SelectAllChildren(nsINode& aNode, ErrorResult& aRv)
 {
-  if (mFrameSelection)
-  {
+  if (mFrameSelection) {
     mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
   }
+  SelectionBatcher batch(this);
+
   Collapse(aNode, 0, aRv);
   if (aRv.Failed()) {
     return;
   }
 
-  if (mFrameSelection)
-  {
-    mFrameSelection->PostReason(nsISelectionListener::SELECTALL_REASON);
-  }
   Extend(aNode, aNode.GetChildCount(), aRv);
 }
 
 NS_IMETHODIMP
 Selection::ContainsNode(nsIDOMNode* aNode, bool aAllowPartial, bool* aYes)
 {
   if (!aYes) {
     return NS_ERROR_NULL_POINTER;
@@ -5921,17 +6003,36 @@ NS_IMETHODIMP
 Selection::EndBatchChanges()
 {
   if (mFrameSelection)
     mFrameSelection->EndBatchChanges();
 
   return NS_OK;
 }
 
-
+void
+Selection::AddSelectionChangeBlocker()
+{
+  mSelectionChangeBlockerCount++;
+}
+
+void
+Selection::RemoveSelectionChangeBlocker()
+{
+  MOZ_ASSERT(mSelectionChangeBlockerCount > 0,
+             "mSelectionChangeBlockerCount has an invalid value - "
+             "maybe you have a mismatched RemoveSelectionChangeBlocker?");
+  mSelectionChangeBlockerCount--;
+}
+
+bool
+Selection::IsBlockingSelectionChangeEvents() const
+{
+  return mSelectionChangeBlockerCount > 0;
+}
 
 NS_IMETHODIMP
 Selection::DeleteFromDocument()
 {
   ErrorResult result;
   DeleteFromDocument(result);
   return result.StealNSResult();
 }
@@ -6154,16 +6255,23 @@ Selection::SetSelectionDirection(nsDirec
 }
 
 JSObject*
 Selection::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
   return mozilla::dom::SelectionBinding::Wrap(aCx, this, aGivenProto);
 }
 
+// AutoHideSelectionChanges
+AutoHideSelectionChanges::AutoHideSelectionChanges(const nsFrameSelection* aFrame)
+  : AutoHideSelectionChanges(aFrame ?
+                             aFrame->GetSelection(nsISelectionController::SELECTION_NORMAL) :
+                             nullptr)
+{}
+
 // nsAutoCopyListener
 
 nsAutoCopyListener* nsAutoCopyListener::sInstance = nullptr;
 
 NS_IMPL_ISUPPORTS(nsAutoCopyListener, nsISelectionListener)
 
 /*
  * What we do now:
@@ -6213,8 +6321,136 @@ nsAutoCopyListener::NotifySelectionChang
 
   nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
   NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
 
   // call the copy code
   return nsCopySupport::HTMLCopy(aSel, doc,
                                  nsIClipboard::kSelectionClipboard, false);
 }
+
+// SelectionChangeListener
+
+SelectionChangeListener::RawRangeData::RawRangeData(const nsRange* aRange)
+{
+  mozilla::ErrorResult rv;
+  mStartParent = aRange->GetStartContainer(rv);
+  rv.SuppressException();
+  mEndParent = aRange->GetEndContainer(rv);
+  rv.SuppressException();
+  mStartOffset = aRange->GetStartOffset(rv);
+  rv.SuppressException();
+  mEndOffset = aRange->GetEndOffset(rv);
+  rv.SuppressException();
+}
+
+bool
+SelectionChangeListener::RawRangeData::Equals(const nsRange* aRange)
+{
+  mozilla::ErrorResult rv;
+  bool eq = mStartParent == aRange->GetStartContainer(rv);
+  rv.SuppressException();
+  eq = eq && mEndParent == aRange->GetEndContainer(rv);
+  rv.SuppressException();
+  eq = eq && mStartOffset == aRange->GetStartOffset(rv);
+  rv.SuppressException();
+  eq = eq && mEndOffset == aRange->GetEndOffset(rv);
+  rv.SuppressException();
+  return eq;
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+                            SelectionChangeListener::RawRangeData& aField,
+                            const char* aName,
+                            uint32_t aFlags = 0)
+{
+  ImplCycleCollectionTraverse(aCallback, aField.mStartParent, "mStartParent", aFlags);
+  ImplCycleCollectionTraverse(aCallback, aField.mEndParent, "mEndParent", aFlags);
+}
+
+NS_IMPL_CYCLE_COLLECTION_CLASS(SelectionChangeListener)
+
+NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(SelectionChangeListener)
+  tmp->mOldRanges.Clear();
+NS_IMPL_CYCLE_COLLECTION_UNLINK_END
+
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(SelectionChangeListener)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mOldRanges);
+NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SelectionChangeListener)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+  NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(SelectionChangeListener)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(SelectionChangeListener)
+
+NS_IMETHODIMP
+SelectionChangeListener::NotifySelectionChanged(nsIDOMDocument* aDoc,
+                                                nsISelection* aSel, int16_t aReason)
+{
+  // This cast is valid as nsISelection is a builtinclass which is only
+  // implemented by Selection.
+  nsRefPtr<Selection> sel = static_cast<Selection*>(aSel);
+
+  // Check if the ranges have actually changed
+  // Don't bother checking this if we are hiding changes.
+  if (mOldRanges.Length() == sel->RangeCount() && !sel->IsBlockingSelectionChangeEvents()) {
+    bool changed = false;
+
+    for (size_t i = 0; i < mOldRanges.Length(); i++) {
+      if (!mOldRanges[i].Equals(sel->GetRangeAt(i))) {
+        changed = true;
+        break;
+      }
+    }
+
+    if (!changed) {
+      return NS_OK;
+    }
+  }
+
+  // The ranges have actually changed, update the mOldRanges array
+  mOldRanges.ClearAndRetainStorage();
+  for (size_t i = 0; i < sel->RangeCount(); i++) {
+    mOldRanges.AppendElement(RawRangeData(sel->GetRangeAt(i)));
+  }
+
+  // If we are hiding changes, then don't do anything else. We do this after we
+  // update mOldRanges so that changes after the changes stop being hidden don't
+  // incorrectly trigger a change, even though they didn't change anything
+  if (sel->IsBlockingSelectionChangeEvents()) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsINode> target;
+
+  // Check if we should be firing this event to a different node than the
+  // document. The limiter of the nsFrameSelection will be within the native
+  // anonymous subtree of the node we want to fire the event on. We need to
+  // climb up the parent chain to escape the native anonymous subtree, and then
+  // fire the event.
+  if (nsFrameSelection* fs = sel->GetFrameSelection()) {
+    if (nsCOMPtr<nsIContent> root = fs->GetLimiter()) {
+      while (root && root->IsInNativeAnonymousSubtree()) {
+        root = root->GetParent();
+      }
+
+      target = root.forget();
+    }
+  }
+
+  // If we didn't get a target before, we can instead fire the event at the document.
+  if (!target) {
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDoc);
+    target = doc.forget();
+  }
+
+  if (target) {
+    nsRefPtr<AsyncEventDispatcher> asyncDispatcher =
+      new AsyncEventDispatcher(target, NS_LITERAL_STRING("selectionchange"), false);
+    asyncDispatcher->PostDOMEvent();
+  }
+
+  return NS_OK;
+}
--- a/layout/style/nsCSSParser.cpp
+++ b/layout/style/nsCSSParser.cpp
@@ -423,17 +423,17 @@ protected:
   void ReleaseScanner(void);
 
   /**
    * This is a RAII class which behaves like an "AutoRestore<>" for our parser
    * input state. When instantiated, this class saves the current parser input
    * state (in a CSSParserInputState object), and it restores the parser to
    * that state when destructed, unless "DoNotRestore()" has been called.
   */
-  class MOZ_STACK_CLASS nsAutoCSSParserInputStateRestorer {
+  class MOZ_RAII nsAutoCSSParserInputStateRestorer {
     public:
       explicit nsAutoCSSParserInputStateRestorer(CSSParserImpl* aParser
                                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : mParser(aParser),
           mShouldRestore(true)
       {
         MOZ_GUARD_OBJECT_NOTIFIER_INIT;
         mParser->SaveInputState(mSavedState);
@@ -471,17 +471,17 @@ protected:
    * so it doesn't really matter which reporter we use. We suppress reporting
    * because this class is only used with CSS that is synthesized & didn't
    * come directly from an author, and it would be confusing if we reported
    * syntax errors for CSS that an author didn't provide.
    *
    * XXXdholbert we could also change this & report errors, if needed. Might
    * want to customize the error reporting somehow though.
    */
-  class MOZ_STACK_CLASS nsAutoScannerChanger {
+  class MOZ_RAII nsAutoScannerChanger {
     public:
       nsAutoScannerChanger(CSSParserImpl* aParser,
                            const nsAString& aStringToScan
                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
         : mParser(aParser),
           mOriginalScanner(aParser->mScanner),
           mStringScanner(aStringToScan, 0),
           mParserStateRestorer(aParser),
--- a/layout/style/nsRuleProcessorData.h
+++ b/layout/style/nsRuleProcessorData.h
@@ -220,17 +220,17 @@ struct MOZ_STACK_CLASS TreeMatchContext 
   bool IsWithinStyleScopeForSelectorMatching() const
   {
     NS_ASSERTION(mForScopedStyle, "only call IsWithinScopeForSelectorMatching "
                                   "when mForScopedStyle is true");
     return mCurrentStyleScope;
   }
 
   /* Helper class for maintaining the ancestor state */
-  class MOZ_STACK_CLASS AutoAncestorPusher {
+  class MOZ_RAII AutoAncestorPusher {
   public:
     explicit AutoAncestorPusher(TreeMatchContext& aTreeMatchContext
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mPushedAncestor(false)
       , mPushedStyleScope(false)
       , mTreeMatchContext(aTreeMatchContext)
       , mElement(nullptr)
     {
@@ -290,17 +290,17 @@ struct MOZ_STACK_CLASS TreeMatchContext 
    * code for special cases where child element style is modified based on
    * parent display value.
    *
    * The optional second parameter aSkipParentDisplayBasedStyleFixup allows
    * this class to be instantiated but only conditionally activated (e.g.
    * in cases where we may or may not want to be skipping flex/grid-item
    * style fixup for a particular chunk of code).
    */
-  class MOZ_STACK_CLASS AutoParentDisplayBasedStyleFixupSkipper {
+  class MOZ_RAII AutoParentDisplayBasedStyleFixupSkipper {
   public:
     explicit AutoParentDisplayBasedStyleFixupSkipper(TreeMatchContext& aTreeMatchContext,
                                                      bool aSkipParentDisplayBasedStyleFixup = true
                                                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
       : mAutoRestorer(aTreeMatchContext.mSkippingParentDisplayBasedStyleFixup)
     {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       if (aSkipParentDisplayBasedStyleFixup) {
--- a/layout/svg/nsSVGClipPathFrame.h
+++ b/layout/svg/nsSVGClipPathFrame.h
@@ -99,17 +99,17 @@ public:
    */
   gfxMatrix GetClipPathTransform(nsIFrame* aClippedFrame);
 
  private:
   // A helper class to allow us to paint clip paths safely. The helper
   // automatically sets and clears the mInUse flag on the clip path frame
   // (to prevent nasty reference loops). It's easy to mess this up
   // and break things, so this helper makes the code far more robust.
-  class MOZ_STACK_CLASS AutoClipPathReferencer
+  class MOZ_RAII AutoClipPathReferencer
   {
   public:
     explicit AutoClipPathReferencer(nsSVGClipPathFrame *aFrame
                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
        : mFrame(aFrame) {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       NS_ASSERTION(!mFrame->mInUse, "reference loop!");
       mFrame->mInUse = true;
--- a/layout/svg/nsSVGFilterFrame.cpp
+++ b/layout/svg/nsSVGFilterFrame.cpp
@@ -22,17 +22,17 @@ using namespace mozilla::dom;
 nsIFrame*
 NS_NewSVGFilterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
 {
   return new (aPresShell) nsSVGFilterFrame(aContext);
 }
 
 NS_IMPL_FRAMEARENA_HELPERS(nsSVGFilterFrame)
 
-class MOZ_STACK_CLASS nsSVGFilterFrame::AutoFilterReferencer
+class MOZ_RAII nsSVGFilterFrame::AutoFilterReferencer
 {
 public:
   explicit AutoFilterReferencer(nsSVGFilterFrame *aFrame MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mFrame(aFrame)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     // Reference loops should normally be detected in advance and handled, so
     // we're not expecting to encounter them here
--- a/layout/svg/nsSVGGradientFrame.cpp
+++ b/layout/svg/nsSVGGradientFrame.cpp
@@ -19,17 +19,17 @@
 // XXX Tight coupling with content classes ahead!
 
 using namespace mozilla;
 using namespace mozilla::dom;
 
 //----------------------------------------------------------------------
 // Helper classes
 
-class MOZ_STACK_CLASS nsSVGGradientFrame::AutoGradientReferencer
+class MOZ_RAII nsSVGGradientFrame::AutoGradientReferencer
 {
 public:
   explicit AutoGradientReferencer(nsSVGGradientFrame *aFrame
                                   MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mFrame(aFrame)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     // Reference loops should normally be detected in advance and handled, so
--- a/layout/svg/nsSVGMarkerFrame.h
+++ b/layout/svg/nsSVGMarkerFrame.h
@@ -105,17 +105,17 @@ private:
   // nsSVGContainerFrame methods:
   virtual gfxMatrix GetCanvasTM() override;
 
   // A helper class to allow us to paint markers safely. The helper
   // automatically sets and clears the mInUse flag on the marker frame (to
   // prevent nasty reference loops) as well as the reference to the marked
   // frame and its coordinate context. It's easy to mess this up
   // and break things, so this helper makes the code far more robust.
-  class MOZ_STACK_CLASS AutoMarkerReferencer
+  class MOZ_RAII AutoMarkerReferencer
   {
   public:
     AutoMarkerReferencer(nsSVGMarkerFrame *aFrame,
                          nsSVGPathGeometryFrame *aMarkedFrame
                          MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     ~AutoMarkerReferencer();
   private:
     nsSVGMarkerFrame *mFrame;
--- a/layout/svg/nsSVGMaskFrame.h
+++ b/layout/svg/nsSVGMaskFrame.h
@@ -95,17 +95,17 @@ private:
    * returns the resulting transform.
    */
   gfxMatrix GetMaskTransform(nsIFrame* aMaskedFrame);
 
   // A helper class to allow us to paint masks safely. The helper
   // automatically sets and clears the mInUse flag on the mask frame
   // (to prevent nasty reference loops). It's easy to mess this up
   // and break things, so this helper makes the code far more robust.
-  class MOZ_STACK_CLASS AutoMaskReferencer
+  class MOZ_RAII AutoMaskReferencer
   {
   public:
     explicit AutoMaskReferencer(nsSVGMaskFrame *aFrame
                                 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
        : mFrame(aFrame) {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       NS_ASSERTION(!mFrame->mInUse, "reference loop!");
       mFrame->mInUse = true;
--- a/layout/svg/nsSVGPatternFrame.cpp
+++ b/layout/svg/nsSVGPatternFrame.cpp
@@ -27,17 +27,17 @@
 
 using namespace mozilla;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
 //----------------------------------------------------------------------
 // Helper classes
 
-class MOZ_STACK_CLASS nsSVGPatternFrame::AutoPatternReferencer
+class MOZ_RAII nsSVGPatternFrame::AutoPatternReferencer
 {
 public:
   explicit AutoPatternReferencer(nsSVGPatternFrame *aFrame
                                  MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mFrame(aFrame)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     // Reference loops should normally be detected in advance and handled, so
--- a/layout/svg/nsSVGUtils.h
+++ b/layout/svg/nsSVGUtils.h
@@ -132,17 +132,17 @@ public:
 private:
   Rect mBBox;
   bool mIsEmpty;
 };
 
 // GRRR WINDOWS HATE HATE HATE
 #undef CLIP_MASK
 
-class MOZ_STACK_CLASS SVGAutoRenderState
+class MOZ_RAII SVGAutoRenderState
 {
   typedef mozilla::gfx::DrawTarget DrawTarget;
 
 public:
   explicit SVGAutoRenderState(DrawTarget* aDrawTarget
                               MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
   ~SVGAutoRenderState();
 
--- a/mfbt/Attributes.h
+++ b/mfbt/Attributes.h
@@ -403,16 +403,26 @@
  *   class inherits from this class, then it is considered to be a non-heap class
  *   as well, although this attribute need not be provided in such cases.
  * MOZ_HEAP_CLASS: Applies to all classes. Any class with this annotation is
  *   expected to live on the heap, so it is a compile-time error to use it, or
  *   an array of such objects, as the type of a variable declaration, or as a
  *   temporary object. If a member of another class uses this class, or if
  *   another class inherits from this class, then it is considered to be a heap
  *   class as well, although this attribute need not be provided in such cases.
+ * MOZ_NON_TEMPORARY_CLASS: Applies to all classes. Any class with this
+ *   annotation is expected not to live in a temporary. If a member of another
+ *   class uses this class or if another class inherits from this class, then it
+ *   is considered to be a non-temporary class as well, although this attribute
+ *   need not be provided in such cases.
+ * MOZ_RAII: Applies to all classes. Any class with this annotation is assumed
+ *   to be a RAII guard, which is expected to live on the stack in an automatic
+ *   allocation. It is prohibited from being allocated in a temporary, static
+ *   storage, or on the heap. This is a combination of MOZ_STACK_CLASS and
+ *   MOZ_NON_TEMPORARY_CLASS.
  * MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS: Applies to all classes that are
  *   intended to prevent introducing static initializers.  This attribute
  *   currently makes it a compile-time error to instantiate these classes
  *   anywhere other than at the global scope, or as a static member of a class.
  * MOZ_TRIVIAL_CTOR_DTOR: Applies to all classes that must have both a trivial
  *   constructor and a trivial destructor.  Setting this attribute on a class
  *   makes it a compile-time error for that class to get a non-trivial
  *   constructor or destructor for any reason.
@@ -482,16 +492,17 @@
  *   be used with types which are intended to be implicitly constructed into other
  *   other types before being assigned to variables.
  */
 #ifdef MOZ_CLANG_PLUGIN
 #  define MOZ_MUST_OVERRIDE __attribute__((annotate("moz_must_override")))
 #  define MOZ_STACK_CLASS __attribute__((annotate("moz_stack_class")))
 #  define MOZ_NONHEAP_CLASS __attribute__((annotate("moz_nonheap_class")))
 #  define MOZ_HEAP_CLASS __attribute__((annotate("moz_heap_class")))
+#  define MOZ_NON_TEMPORARY_CLASS __attribute__((annotate("moz_non_temporary_class")))
 #  define MOZ_TRIVIAL_CTOR_DTOR __attribute__((annotate("moz_trivial_ctor_dtor")))
 #  ifdef DEBUG
      /* in debug builds, these classes do have non-trivial constructors. */
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class")))
 #  else
 #    define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS __attribute__((annotate("moz_global_class"))) \
             MOZ_TRIVIAL_CTOR_DTOR
 #  endif
@@ -518,16 +529,17 @@
     _Pragma("clang diagnostic ignored \"-Wgcc-compat\"") \
     __attribute__((annotate("moz_heap_allocator"))) \
     _Pragma("clang diagnostic pop")
 #else
 #  define MOZ_MUST_OVERRIDE /* nothing */
 #  define MOZ_STACK_CLASS /* nothing */
 #  define MOZ_NONHEAP_CLASS /* nothing */
 #  define MOZ_HEAP_CLASS /* nothing */
+#  define MOZ_NON_TEMPORARY_CLASS /* nothing */
 #  define MOZ_TRIVIAL_CTOR_DTOR /* nothing */
 #  define MOZ_ONLY_USED_TO_AVOID_STATIC_CONSTRUCTORS /* nothing */
 #  define MOZ_IMPLICIT /* nothing */
 #  define MOZ_NO_ARITHMETIC_EXPR_IN_ARGUMENT /* nothing */
 #  define MOZ_HEAP_ALLOCATOR /* nothing */
 #  define MOZ_OWNING_REF /* nothing */
 #  define MOZ_NON_OWNING_REF /* nothing */
 #  define MOZ_UNSAFE_REF(reason) /* nothing */
@@ -535,16 +547,18 @@
 #  define MOZ_MUST_USE /* nothing */
 #  define MOZ_NEEDS_NO_VTABLE_TYPE /* nothing */
 #  define MOZ_NON_MEMMOVABLE /* nothing */
 #  define MOZ_NEEDS_MEMMOVABLE_TYPE /* nothing */
 #  define MOZ_INHERIT_TYPE_ANNOTATIONS_FROM_TEMPLATE_ARGS /* nothing */
 #  define MOZ_NON_AUTOABLE /* nothing */
 #endif /* MOZ_CLANG_PLUGIN */
 
+#define MOZ_RAII MOZ_NON_TEMPORARY_CLASS MOZ_STACK_CLASS
+
 /*
  * MOZ_HAVE_REF_QUALIFIERS is defined for compilers that support C++11's rvalue
  * qualifier, "&&".
  */
 #if defined(_MSC_VER) && _MSC_VER >= 1900
 #  define MOZ_HAVE_REF_QUALIFIERS
 #elif defined(__clang__)
 // All supported Clang versions
--- a/mfbt/ReentrancyGuard.h
+++ b/mfbt/ReentrancyGuard.h
@@ -11,17 +11,17 @@
 
 #include "mozilla/Assertions.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 
 namespace mozilla {
 
 /* Useful for implementing containers that assert non-reentrancy */
-class ReentrancyGuard
+class MOZ_RAII ReentrancyGuard
 {
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 #ifdef DEBUG
   bool& mEntered;
 #endif
 
 public:
   template<class T>
--- a/mfbt/ScopeExit.h
+++ b/mfbt/ScopeExit.h
@@ -81,17 +81,17 @@
  */
 
 #include "mozilla/GuardObjects.h"
 #include "mozilla/Move.h"
 
 namespace mozilla {
 
 template <typename ExitFunction>
-class ScopeExit {
+class MOZ_STACK_CLASS ScopeExit {
   ExitFunction mExitFunction;
   bool mExecuteOnDestruction;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 
 public:
   explicit ScopeExit(ExitFunction&& cleanup
                      MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
    : mExitFunction(cleanup)
--- a/mfbt/Scoped.h
+++ b/mfbt/Scoped.h
@@ -72,17 +72,17 @@ namespace mozilla {
  *     // Returns the value corresponding to the uninitialized or freed state
  *     const static type empty();
  *     // Release resources corresponding to the wrapped value
  *     // This function is responsible for not releasing an |empty| value
  *     const static void release(type);
  *   }
  */
 template<typename Traits>
-class Scoped
+class MOZ_NON_TEMPORARY_CLASS Scoped
 {
 public:
   typedef typename Traits::type Resource;
 
   explicit Scoped(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM)
     : mValue(Traits::empty())
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
@@ -182,17 +182,17 @@ private:
  * This allows to implement templates such as ScopedFreePtr.
  *
  * @param name The name of the class to define.
  * @param Traits A struct implementing clean-up. See the implementations
  * for more details.
  */
 #define SCOPED_TEMPLATE(name, Traits)                                         \
 template<typename Type>                                                       \
-struct name : public mozilla::Scoped<Traits<Type> >                           \
+struct MOZ_NON_TEMPORARY_CLASS name : public mozilla::Scoped<Traits<Type> >   \
 {                                                                             \
   typedef mozilla::Scoped<Traits<Type> > Super;                               \
   typedef typename Super::Resource Resource;                                  \
   name& operator=(Resource aRhs)                                              \
   {                                                                           \
     Super::operator=(aRhs);                                                   \
     return *this;                                                             \
   }                                                                           \
--- a/mobile/android/app/mobile.js
+++ b/mobile/android/app/mobile.js
@@ -479,16 +479,19 @@ pref("security.alternate_certificate_err
 pref("security.warn_viewing_mixed", false); // Warning is disabled.  See Bug 616712.
 
 // Block insecure active content on https pages
 pref("security.mixed_content.block_active_content", true);
 
 // Enable pinning
 pref("security.cert_pinning.enforcement_level", 1);
 
+// Allow SHA-1 certificates only before 2016-01-01
+pref("security.pki.sha1_enforcement_level", 2);
+
 // Required blocklist freshness for OneCRL OCSP bypass
 // (default is 1.25x extensions.blocklist.interval, or 30 hours)
 pref("security.onecrl.maximum_staleness_in_seconds", 108000);
 
 // Only fetch OCSP for EV certificates
 pref("security.OCSP.enabled", 2);
 
 // Override some named colors to avoid inverse OS themes
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -129,16 +129,23 @@ pref("dom.fileHandle.enabled", true);
 
 // Whether or not the Permissions API is enabled.
 #ifdef NIGHTLY_BUILD
 pref("dom.permissions.enabled", true);
 #else
 pref("dom.permissions.enabled", false);
 #endif
 
+// Whether or not selection events are enabled
+#ifdef NIGHTLY_BUILD
+pref("dom.select_events.enabled", true);
+#else
+pref("dom.select_events.enabled", false);
+#endif
+
 // Whether or not Web Workers are enabled.
 pref("dom.workers.enabled", true);
 // The number of workers per domain allowed to run concurrently.
 pref("dom.workers.maxPerDomain", 20);
 
 // Whether or not Shared Web Workers are enabled.
 pref("dom.workers.sharedWorkers.enabled", true);
 
--- a/mozglue/build/WindowsDllBlocklist.h
+++ b/mozglue/build/WindowsDllBlocklist.h
@@ -13,17 +13,17 @@
 #include "nscore.h"
 
 #define HAS_DLL_BLOCKLIST
 
 NS_IMPORT void DllBlocklist_Initialize();
 NS_IMPORT void DllBlocklist_SetInXPCOMLoadOnMainThread(bool inXPCOMLoadOnMainThread);
 NS_IMPORT void DllBlocklist_WriteNotes(HANDLE file);
 
-class AutoSetXPCOMLoadOnMainThread
+class MOZ_RAII AutoSetXPCOMLoadOnMainThread
 {
   public:
     AutoSetXPCOMLoadOnMainThread(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM) {
       MOZ_GUARD_OBJECT_NOTIFIER_INIT;
       DllBlocklist_SetInXPCOMLoadOnMainThread(true);
     }
 
     ~AutoSetXPCOMLoadOnMainThread() {
--- a/netwerk/ipc/NeckoChannelParams.ipdlh
+++ b/netwerk/ipc/NeckoChannelParams.ipdlh
@@ -53,16 +53,29 @@ union OptionalLoadInfoArgs
 //-----------------------------------------------------------------------------
 
 union OptionalHttpResponseHead
 {
   void_t;
   nsHttpResponseHead;
 };
 
+struct CorsPreflightArgs
+{
+  bool          withCredentials;
+  PrincipalInfo preflightPrincipal;
+  nsCString[]   unsafeHeaders;
+};
+
+union OptionalCorsPreflightArgs
+{
+  void_t;
+  CorsPreflightArgs;
+};
+
 struct HttpChannelOpenArgs
 {
   URIParams                   uri;
   // - TODO: bug 571161: unclear if any HTTP channel clients ever
   // set originalURI != uri (about:credits?); also not clear if
   // chrome channel would ever need to know.  Get rid of next arg?
   OptionalURIParams           original;
   OptionalURIParams           doc;
@@ -89,16 +102,17 @@ struct HttpChannelOpenArgs
   bool                        allowSpdy;
   bool                        allowAltSvc;
   OptionalFileDescriptorSet   fds;
   OptionalLoadInfoArgs        loadInfo;
   OptionalHttpResponseHead    synthesizedResponseHead;
   nsCString                   synthesizedSecurityInfoSerialization;
   uint32_t                    cacheKey;
   nsCString                   schedulingContextID;
+  OptionalCorsPreflightArgs   preflightArgs;
 };
 
 struct HttpChannelConnectArgs
 {
   uint32_t channelId;
   bool shouldIntercept;
 };
 
--- a/netwerk/protocol/http/HttpBaseChannel.cpp
+++ b/netwerk/protocol/http/HttpBaseChannel.cpp
@@ -85,16 +85,18 @@ HttpBaseChannel::HttpBaseChannel()
   , mHttpHandler(gHttpHandler)
   , mReferrerPolicy(REFERRER_POLICY_NO_REFERRER_WHEN_DOWNGRADE)
   , mRedirectCount(0)
   , mForcePending(false)
   , mCorsIncludeCredentials(false)
   , mCorsMode(nsIHttpChannelInternal::CORS_MODE_NO_CORS)
   , mRedirectMode(nsIHttpChannelInternal::REDIRECT_MODE_FOLLOW)
   , mOnStartRequestCalled(false)
+  , mRequireCORSPreflight(false)
+  , mWithCredentials(false)
 {
   LOG(("Creating HttpBaseChannel @%x\n", this));
 
   // Subfields of unions cannot be targeted in an initializer list.
 #ifdef MOZ_VALGRIND
   // Zero the entire unions so that Valgrind doesn't complain when we send them
   // to another process.
   memset(&mSelfAddr, 0, sizeof(NetAddr));
@@ -2328,16 +2330,26 @@ HttpBaseChannel::SetupReplacementChannel
 
   // Propagate our loadinfo if needed.
   newChannel->SetLoadInfo(mLoadInfo);
 
   nsCOMPtr<nsIHttpChannel> httpChannel = do_QueryInterface(newChannel);
   if (!httpChannel)
     return NS_OK; // no other options to set
 
+  // Preserve the CORS preflight information.
+  nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
+  if (mRequireCORSPreflight && httpInternal) {
+    rv = httpInternal->SetCorsPreflightParameters(mUnsafeHeaders, mWithCredentials,
+                                                  mPreflightPrincipal);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+  }
+
   if (preserveMethod) {
     nsCOMPtr<nsIUploadChannel> uploadChannel =
       do_QueryInterface(httpChannel);
     nsCOMPtr<nsIUploadChannel2> uploadChannel2 =
       do_QueryInterface(httpChannel);
     if (mUploadStream && (uploadChannel2 || uploadChannel)) {
       // rewind upload stream
       nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(mUploadStream);
@@ -2397,17 +2409,16 @@ HttpBaseChannel::SetupReplacementChannel
     nsresult hasHeader = mRequestHead.GetHeader(nsHttp::Accept, oldAcceptValue);
     if (NS_SUCCEEDED(hasHeader)) {
       httpChannel->SetRequestHeader(NS_LITERAL_CSTRING("Accept"),
                                     oldAcceptValue,
                                     false);
     }
   }
 
-  nsCOMPtr<nsIHttpChannelInternal> httpInternal = do_QueryInterface(newChannel);
   if (httpInternal) {
     // Convey third party cookie and spdy flags.
     httpInternal->SetThirdPartyFlags(mThirdPartyFlags);
     httpInternal->SetAllowSpdy(mAllowSpdy);
     httpInternal->SetAllowAltSvc(mAllowAltSvc);
 
     // update the DocumentURI indicator since we are being redirected.
     // if this was a top-level document channel, then the new channel
@@ -2821,11 +2832,25 @@ HttpBaseChannel::EnsureSchedulingContext
         return false;
     }
 
     // Set the load group connection scope on the transaction
     rootLoadGroup->GetSchedulingContextID(&mSchedulingContextID);
     return true;
 }
 
+NS_IMETHODIMP
+HttpBaseChannel::SetCorsPreflightParameters(const nsTArray<nsCString>& aUnsafeHeaders,
+                                            bool aWithCredentials,
+                                            nsIPrincipal* aPrincipal)
+{
+  ENSURE_CALLED_BEFORE_CONNECT();
+
+  mRequireCORSPreflight = true;
+  mUnsafeHeaders = aUnsafeHeaders;
+  mWithCredentials = aWithCredentials;
+  mPreflightPrincipal = aPrincipal;
+  return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
 
--- a/netwerk/protocol/http/HttpBaseChannel.h
+++ b/netwerk/protocol/http/HttpBaseChannel.h
@@ -196,16 +196,19 @@ public:
   NS_IMETHOD GetCorsIncludeCredentials(bool* aInclude) override;
   NS_IMETHOD SetCorsIncludeCredentials(bool aInclude) override;
   NS_IMETHOD GetCorsMode(uint32_t* aCorsMode) override;
   NS_IMETHOD SetCorsMode(uint32_t aCorsMode) override;
   NS_IMETHOD GetRedirectMode(uint32_t* aRedirectMode) override;
   NS_IMETHOD SetRedirectMode(uint32_t aRedirectMode) override;
   NS_IMETHOD GetTopWindowURI(nsIURI **aTopWindowURI) override;
   NS_IMETHOD GetProxyURI(nsIURI **proxyURI) override;
+  NS_IMETHOD SetCorsPreflightParameters(const nsTArray<nsCString>& unsafeHeaders,
+                                        bool aWithCredentials,
+                                        nsIPrincipal* aPrincipal) override;
 
   inline void CleanRedirectCacheChainIfNecessary()
   {
       mRedirectedCachekeys = nullptr;
   }
   NS_IMETHOD HTTPUpgrade(const nsACString & aProtocolName,
                          nsIHttpUpgradeListener *aListener) override;
 
@@ -439,16 +442,21 @@ protected:
   // than once.
   bool mOnStartRequestCalled;
 
   // The network interface id that's associated with this channel.
   nsCString mNetworkInterfaceId;
 
   nsID mSchedulingContextID;
   bool EnsureSchedulingContextID();
+
+  bool                              mRequireCORSPreflight;
+  bool                              mWithCredentials;
+  nsTArray<nsCString>               mUnsafeHeaders;
+  nsCOMPtr<nsIPrincipal>            mPreflightPrincipal;
 };
 
 // Share some code while working around C++'s absurd inability to handle casting
 // of member functions between base/derived types.
 // - We want to store member function pointer to call at resume time, but one
 //   such function--HandleAsyncAbort--we want to share between the
 //   nsHttpChannel/HttpChannelChild.  Can't define it in base class, because
 //   then we'd have to cast member function ptr between base/derived class
--- a/netwerk/protocol/http/HttpChannelChild.cpp
+++ b/netwerk/protocol/http/HttpChannelChild.cpp
@@ -1663,16 +1663,30 @@ HttpChannelChild::ContinueAsyncOpen()
       gNeckoChild->Manager()->SendPFileDescriptorSetConstructor(fds[0]);
     for (uint32_t i = 1; i < fds.Length(); ++i) {
       unused << fdSet->SendAddFileDescriptor(fds[i]);
     }
 
     optionalFDs = fdSet;
   }
 
+  OptionalCorsPreflightArgs optionalCorsPreflightArgs;
+  if (mRequireCORSPreflight) {
+    CorsPreflightArgs args;
+    args.withCredentials() = mWithCredentials;
+    args.unsafeHeaders() = mUnsafeHeaders;
+    nsresult rv = PrincipalToPrincipalInfo(mPreflightPrincipal, &args.preflightPrincipal());
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+    optionalCorsPreflightArgs = args;
+  } else {
+    optionalCorsPreflightArgs = mozilla::void_t();
+  }
+
   nsCOMPtr<mozIThirdPartyUtil> util(do_GetService(THIRDPARTYUTIL_CONTRACTID));
   if (util) {
     bool thirdParty;
     nsresult rv = util->IsThirdPartyChannel(this, nullptr, &thirdParty);
     if (NS_FAILED(rv)) {
       // If we couldn't compute whether this is a third-party load, assume that
       // it is.
       thirdParty = true;
@@ -1684,16 +1698,18 @@ HttpChannelChild::ContinueAsyncOpen()
     nsCOMPtr<nsIURI> uri;
     GetTopWindowURI(getter_AddRefs(uri));
   }
 
   SerializeURI(mTopWindowURI, openArgs.topWindowURI());
 
   openArgs.fds() = optionalFDs;
 
+  openArgs.preflightArgs() = optionalCorsPreflightArgs;
+
   openArgs.uploadStreamHasHeaders() = mUploadStreamHasHeaders;
   openArgs.priority() = mPriority;
   openArgs.classOfService() = mClassOfService;
   openArgs.redirectionLimit() = mRedirectionLimit;
   openArgs.allowPipelining() = mAllowPipelining;
   openArgs.allowSTS() = mAllowSTS;
   openArgs.thirdPartyFlags() = mThirdPartyFlags;
   openArgs.resumeAt() = mSendResumeAt;
@@ -2152,16 +2168,36 @@ NS_IMETHODIMP HttpChannelChild::AddCooki
 }
 
 NS_IMETHODIMP HttpChannelChild::GetClientSetRequestHeaders(RequestHeaderTuples **aRequestHeaders)
 {
   *aRequestHeaders = &mClientSetRequestHeaders;
   return NS_OK;
 }
 
+NS_IMETHODIMP
+HttpChannelChild::RemoveCorsPreflightCacheEntry(nsIURI* aURI,
+                                                nsIPrincipal* aPrincipal)
+{
+  URIParams uri;
+  SerializeURI(aURI, uri);
+  PrincipalInfo principalInfo;
+  nsresult rv = PrincipalToPrincipalInfo(aPrincipal, &principalInfo);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  bool result = false;
+  // Be careful to not attempt to send a message to the parent after the
+  // actor has been destroyed.
+  if (mIPCOpen) {
+    result = SendRemoveCorsPreflightCacheEntry(uri, principalInfo);
+  }
+  return result ? NS_OK : NS_ERROR_FAILURE;
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelChild::nsIDivertableChannel
 //-----------------------------------------------------------------------------
 NS_IMETHODIMP
 HttpChannelChild::DivertToParent(ChannelDiverterChild **aChild)
 {
   LOG(("HttpChannelChild::DivertToParent [this=%p]\n", this));
   MOZ_RELEASE_ASSERT(aChild);
--- a/netwerk/protocol/http/HttpChannelParent.cpp
+++ b/netwerk/protocol/http/HttpChannelParent.cpp
@@ -29,16 +29,17 @@
 #include "nsIContentPolicy.h"
 #include "mozilla/ipc/BackgroundUtils.h"
 #include "nsIOService.h"
 #include "nsICachingChannel.h"
 #include "mozilla/LoadInfo.h"
 #include "nsIHttpHeaderVisitor.h"
 #include "nsQueryObject.h"
 #include "mozilla/BasePrincipal.h"
+#include "nsCORSListenerProxy.h"
 
 using mozilla::BasePrincipal;
 using mozilla::OriginAttributes;
 using namespace mozilla::dom;
 using namespace mozilla::ipc;
 
 namespace mozilla {
 namespace net {
@@ -121,17 +122,17 @@ HttpChannelParent::Init(const HttpChanne
                        a.requestMethod(), a.uploadStream(),
                        a.uploadStreamHasHeaders(), a.priority(), a.classOfService(),
                        a.redirectionLimit(), a.allowPipelining(), a.allowSTS(),
                        a.thirdPartyFlags(), a.resumeAt(), a.startPos(),
                        a.entityID(), a.chooseApplicationCache(),
                        a.appCacheClientID(), a.allowSpdy(), a.allowAltSvc(), a.fds(),
                        a.loadInfo(), a.synthesizedResponseHead(),
                        a.synthesizedSecurityInfoSerialization(),
-                       a.cacheKey(), a.schedulingContextID());
+                       a.cacheKey(), a.schedulingContextID(), a.preflightArgs());
   }
   case HttpChannelCreationArgs::THttpChannelConnectArgs:
   {
     const HttpChannelConnectArgs& cArgs = aArgs.get_HttpChannelConnectArgs();
     return ConnectChannel(cArgs.channelId(), cArgs.shouldIntercept());
   }
   default:
     NS_NOTREACHED("unknown open type");
@@ -279,17 +280,18 @@ HttpChannelParent::DoAsyncOpen(  const U
                                  const nsCString&           appCacheClientID,
                                  const bool&                allowSpdy,
                                  const bool&                allowAltSvc,
                                  const OptionalFileDescriptorSet& aFds,
                                  const OptionalLoadInfoArgs& aLoadInfoArgs,
                                  const OptionalHttpResponseHead& aSynthesizedResponseHead,
                                  const nsCString&           aSecurityInfoSerialization,
                                  const uint32_t&            aCacheKey,
-                                 const nsCString&           aSchedulingContextID)
+                                 const nsCString&           aSchedulingContextID,
+                                 const OptionalCorsPreflightArgs& aCorsPreflightArgs)
 {
   nsCOMPtr<nsIURI> uri = DeserializeURI(aURI);
   if (!uri) {
     // URIParams does MOZ_ASSERT if null, but we need to protect opt builds from
     // null deref here.
     return false;
   }
   nsCOMPtr<nsIURI> originalUri = DeserializeURI(aOriginalURI);
@@ -386,16 +388,28 @@ HttpChannelParent::DoAsyncOpen(  const U
     MOZ_ASSERT(!fds.IsEmpty());
 
     unused << fdSetActor->Send__delete__(fdSetActor);
   } else if (aFds.type() == OptionalFileDescriptorSet::TArrayOfFileDescriptor) {
     const_cast<OptionalFileDescriptorSet&>(aFds).
       get_ArrayOfFileDescriptor().SwapElements(fds);
   }
 
+  if (aCorsPreflightArgs.type() == OptionalCorsPreflightArgs::TCorsPreflightArgs) {
+    const CorsPreflightArgs& args = aCorsPreflightArgs.get_CorsPreflightArgs();
+    nsCOMPtr<nsIPrincipal> preflightPrincipal =
+      PrincipalInfoToPrincipal(args.preflightPrincipal());
+    rv = mChannel->SetCorsPreflightParameters(args.unsafeHeaders(),
+                                              args.withCredentials(),
+                                              preflightPrincipal);
+    if (NS_FAILED(rv)) {
+      return SendFailedAsyncOpen(rv);
+    }
+  }
+
   nsCOMPtr<nsIInputStream> stream = DeserializeInputStream(uploadStream, fds);
   if (stream) {
     mChannel->InternalSetUploadStream(stream);
     mChannel->SetUploadStreamHasHeaders(uploadStreamHasHeaders);
   }
 
   if (aSynthesizedResponseHead.type() == OptionalHttpResponseHead::TnsHttpResponseHead) {
     mSynthesizedResponseHead = new nsHttpResponseHead(aSynthesizedResponseHead.get_nsHttpResponseHead());
@@ -921,16 +935,34 @@ HttpChannelParent::DivertComplete()
   if (NS_WARN_IF(NS_FAILED(rv))) {
     FailDiversion(NS_ERROR_UNEXPECTED);
     return;
   }
 
   mParentListener = nullptr;
 }
 
+bool
+HttpChannelParent::RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
+  const mozilla::ipc::PrincipalInfo& requestingPrincipal)
+{
+  nsCOMPtr<nsIURI> deserializedURI = DeserializeURI(uri);
+  if (!deserializedURI) {
+    return false;
+  }
+  nsCOMPtr<nsIPrincipal> principal =
+    PrincipalInfoToPrincipal(requestingPrincipal);
+  if (!principal) {
+    return false;
+  }
+  nsCORSListenerProxy::RemoveFromCorsPreflightCache(deserializedURI,
+                                                    principal);
+  return true;
+}
+
 //-----------------------------------------------------------------------------
 // HttpChannelParent::nsIRequestObserver
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 HttpChannelParent::OnStartRequest(nsIRequest *aRequest, nsISupports *aContext)
 {
   LOG(("HttpChannelParent::OnStartRequest [this=%p, aRequest=%p]\n",
--- a/netwerk/protocol/http/HttpChannelParent.h
+++ b/netwerk/protocol/http/HttpChannelParent.h
@@ -115,17 +115,18 @@ protected:
                    const nsCString&           appCacheClientID,
                    const bool&                allowSpdy,
                    const bool&                allowAltSvc,
                    const OptionalFileDescriptorSet& aFds,
                    const OptionalLoadInfoArgs& aLoadInfoArgs,
                    const OptionalHttpResponseHead& aSynthesizedResponseHead,
                    const nsCString&           aSecurityInfoSerialization,
                    const uint32_t&            aCacheKey,
-                   const nsCString&           aSchedulingContextID);
+                   const nsCString&           aSchedulingContextID,
+                   const OptionalCorsPreflightArgs& aCorsPreflightArgs);
 
   virtual bool RecvSetPriority(const uint16_t& priority) override;
   virtual bool RecvSetClassOfService(const uint32_t& cos) override;
   virtual bool RecvSetCacheTokenCachedCharset(const nsCString& charset) override;
   virtual bool RecvSuspend() override;
   virtual bool RecvResume() override;
   virtual bool RecvCancel(const nsresult& status) override;
   virtual bool RecvRedirect2Verify(const nsresult& result,
@@ -135,16 +136,18 @@ protected:
                                                    const int32_t& no) override;
   virtual bool RecvDocumentChannelCleanup() override;
   virtual bool RecvMarkOfflineCacheEntryAsForeign() override;
   virtual bool RecvDivertOnDataAvailable(const nsCString& data,
                                          const uint64_t& offset,
                                          const uint32_t& count) override;
   virtual bool RecvDivertOnStopRequest(const nsresult& statusCode) override;
   virtual bool RecvDivertComplete() override;
+  virtual bool RecvRemoveCorsPreflightCacheEntry(const URIParams& uri,
+                                                 const mozilla::ipc::PrincipalInfo& requestingPrincipal) override;
   virtual void ActorDestroy(ActorDestroyReason why) override;
 
   // Supporting function for ADivertableParentChannel.
   nsresult ResumeForDiversion();
 
   // Asynchronously calls NotifyDiversionFailed.
   void FailDiversion(nsresult aErrorCode, bool aSkipResume = true);
 
--- a/netwerk/protocol/http/PHttpChannel.ipdl
+++ b/netwerk/protocol/http/PHttpChannel.ipdl
@@ -3,16 +3,17 @@
 
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include protocol PNecko;
 include InputStreamParams;
 include URIParams;
+include PBackgroundSharedTypes;
 
 include protocol PBlob; //FIXME: bug #792908
 
 include "mozilla/net/NeckoMessageUtils.h";
 
 using RequestHeaderTuples from "mozilla/net/PHttpChannelParams.h";
 using class nsHttpHeaderArray from "nsHttpHeaderArray.h";
 using class nsHttpResponseHead from "nsHttpResponseHead.h";
@@ -74,16 +75,21 @@ parent:
                         uint32_t  count);
 
   // Divert OnStopRequest to the parent.
   DivertOnStopRequest(nsresult statusCode);
 
   // Child has no more events/messages to divert to the parent.
   DivertComplete();
 
+  // Child has detected a CORS check failure, so needs to tell the parent
+  // to remove any matching entry from the CORS preflight cache.
+  RemoveCorsPreflightCacheEntry(URIParams uri,
+                                PrincipalInfo requestingPrincipal);
+
   __delete__();
 
 child:
   OnStartRequest(nsresult            channelStatus,
                  nsHttpResponseHead  responseHead,
                  bool                useResponseHead,
                  nsHttpHeaderArray   requestHeaders,
                  bool                isFromCache,
--- a/netwerk/protocol/http/moz.build
+++ b/netwerk/protocol/http/moz.build
@@ -16,16 +16,17 @@ XPIDL_SOURCES += [
     'nsIHttpEventSink.idl',
     'nsIHttpHeaderVisitor.idl',
     'nsIHttpProtocolHandler.idl',
 ]
 
 XPIDL_MODULE = 'necko_http'
 
 EXPORTS += [
+    'nsCORSListenerProxy.h',
     'nsHttp.h',
     'nsHttpAtomList.h',
     'nsHttpHeaderArray.h',
     'nsHttpRequestHead.h',
     'nsHttpResponseHead.h',
 ]
 
 EXPORTS.mozilla.net += [
@@ -57,16 +58,17 @@ UNIFIED_SOURCES += [
     'Http2Session.cpp',
     'Http2Stream.cpp',
     'HttpBaseChannel.cpp',
     'HttpChannelChild.cpp',
     'HttpChannelParent.cpp',
     'HttpChannelParentListener.cpp',
     'HttpInfo.cpp',
     'InterceptedChannel.cpp',
+    'nsCORSListenerProxy.cpp',
     'nsHttp.cpp',
     'nsHttpActivityDistributor.cpp',
     'nsHttpAuthManager.cpp',
     'nsHttpBasicAuth.cpp',
     'nsHttpChannel.cpp',
     'nsHttpChunkedDecoder.cpp',
     'nsHttpConnection.cpp',
     'nsHttpConnectionInfo.cpp',
rename from dom/security/nsCORSListenerProxy.cpp
rename to netwerk/protocol/http/nsCORSListenerProxy.cpp
--- a/dom/security/nsCORSListenerProxy.cpp
+++ b/netwerk/protocol/http/nsCORSListenerProxy.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Assertions.h"
 #include "mozilla/LinkedList.h"
 
 #include "nsCORSListenerProxy.h"
 #include "nsIChannel.h"
 #include "nsIHttpChannel.h"
+#include "nsIHttpChannelChild.h"
 #include "nsIHttpChannelInternal.h"
 #include "nsError.h"
 #include "nsContentUtils.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsNetUtil.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsServiceManagerUtils.h"
 #include "nsMimeTypes.h"
@@ -34,16 +35,17 @@
 #include "nsILoadGroup.h"
 #include "nsILoadContext.h"
 #include "nsIConsoleService.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMWindowUtils.h"
 #include "nsIDOMWindow.h"
 #include "nsINetworkInterceptController.h"
 #include "nsNullPrincipal.h"
+#include "nsICorsPreflightCallback.h"
 #include <algorithm>
 
 using namespace mozilla;
 
 #define PREFLIGHT_CACHE_SIZE 100
 
 static bool gDisableCORS = false;
 static bool gDisableCORSPrivateData = false;
@@ -495,25 +497,34 @@ nsCORSListenerProxy::Init(nsIChannel* aC
 NS_IMETHODIMP
 nsCORSListenerProxy::OnStartRequest(nsIRequest* aRequest,
                                     nsISupports* aContext)
 {
   MOZ_ASSERT(mInited, "nsCORSListenerProxy has not been initialized properly");
   nsresult rv = CheckRequestApproved(aRequest);
   mRequestApproved = NS_SUCCEEDED(rv);
   if (!mRequestApproved) {
-    if (sPreflightCache) {
-      nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
-      if (channel) {
-        nsCOMPtr<nsIURI> uri;
-        NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
-        if (uri) {
+    nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
+    if (channel) {
+      nsCOMPtr<nsIURI> uri;
+      NS_GetFinalChannelURI(channel, getter_AddRefs(uri));
+      if (uri) {
+        if (sPreflightCache) {
           // OK to use mRequestingPrincipal since preflights never get
           // redirected.
           sPreflightCache->RemoveEntries(uri, mRequestingPrincipal);
+        } else {
+          nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
+            do_QueryInterface(channel);
+          if (httpChannelChild) {
+            rv = httpChannelChild->RemoveCorsPreflightCacheEntry(uri, mRequestingPrincipal);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+              return rv;
+            }
+          }
         }
       }
     }
 
     aRequest->Cancel(NS_ERROR_DOM_BAD_URI);
     mOuterListener->OnStartRequest(aRequest, aContext);
 
     return NS_ERROR_DOM_BAD_URI;
@@ -726,23 +737,32 @@ nsCORSListenerProxy::AsyncOnChannelRedir
                                             uint32_t aFlags,
                                             nsIAsyncVerifyRedirectCallback *cb)
 {
   nsresult rv;
   if (!NS_IsInternalSameURIRedirect(aOldChannel, aNewChannel, aFlags) &&
       !NS_IsHSTSUpgradeRedirect(aOldChannel, aNewChannel, aFlags)) {
     rv = CheckRequestApproved(aOldChannel);
     if (NS_FAILED(rv)) {
-      if (sPreflightCache) {
-        nsCOMPtr<nsIURI> oldURI;
-        NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
-        if (oldURI) {
+      nsCOMPtr<nsIURI> oldURI;
+      NS_GetFinalChannelURI(aOldChannel, getter_AddRefs(oldURI));
+      if (oldURI) {
+        if (sPreflightCache) {
           // OK to use mRequestingPrincipal since preflights never get
           // redirected.
           sPreflightCache->RemoveEntries(oldURI, mRequestingPrincipal);
+        } else {
+          nsCOMPtr<nsIHttpChannelChild> httpChannelChild =
+            do_QueryInterface(aOldChannel);
+          if (httpChannelChild) {
+            rv = httpChannelChild->RemoveCorsPreflightCacheEntry(oldURI, mRequestingPrincipal);
+            if (NS_WARN_IF(NS_FAILED(rv))) {
+              return rv;
+            }
+          }
         }
       }
       aOldChannel->Cancel(NS_ERROR_DOM_BAD_URI);
       return NS_ERROR_DOM_BAD_URI;
     }
 
     if (mHasBeenCrossSite) {
       // Once we've been cross-site, cross-origin redirects reset our source
@@ -1040,21 +1060,21 @@ class nsCORSPreflightListener final : pu
                                       public nsIInterfaceRequestor,
                                       public nsIChannelEventSink
 {
 public:
   nsCORSPreflightListener(nsIChannel* aOuterChannel,
                           nsIStreamListener* aOuterListener,
                           nsISupports* aOuterContext,
                           nsIPrincipal* aReferrerPrincipal,
-                          const nsACString& aRequestMethod,
+                          nsICorsPreflightCallback* aCallback,
                           bool aWithCredentials)
    : mOuterChannel(aOuterChannel), mOuterListener(aOuterListener),
      mOuterContext(aOuterContext), mReferrerPrincipal(aReferrerPrincipal),
-     mRequestMethod(aRequestMethod), mWithCredentials(aWithCredentials)
+     mCallback(aCallback), mWithCredentials(aWithCredentials)
   { }
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
 
@@ -1062,17 +1082,17 @@ private:
   ~nsCORSPreflightListener() {}
 
   void AddResultToCache(nsIRequest* aRequest);
 
   nsCOMPtr<nsIChannel> mOuterChannel;
   nsCOMPtr<nsIStreamListener> mOuterListener;
   nsCOMPtr<nsISupports> mOuterContext;
   nsCOMPtr<nsIPrincipal> mReferrerPrincipal;
-  nsCString mRequestMethod;
+  nsCOMPtr<nsICorsPreflightCallback> mCallback;
   bool mWithCredentials;
 };
 
 NS_IMPL_ISUPPORTS(nsCORSPreflightListener, nsIStreamListener,
                   nsIRequestObserver, nsIInterfaceRequestor,
                   nsIChannelEventSink)
 
 void
@@ -1198,55 +1218,33 @@ nsCORSPreflightListener::OnStartRequest(
   if (NS_SUCCEEDED(rv)) {
     rv = status;
   }
 
   if (NS_SUCCEEDED(rv)) {
     // Everything worked, try to cache and then fire off the actual request.
     AddResultToCache(aRequest);
 
-    nsCOMPtr<nsILoadInfo> loadInfo = mOuterChannel->GetLoadInfo();
-    MOZ_ASSERT(loadInfo, "can not perform CORS preflight without a loadInfo");
-    if (!loadInfo) {
-      return NS_ERROR_FAILURE;
-    }
-    nsSecurityFlags securityMode = loadInfo->GetSecurityMode();
-
-    MOZ_ASSERT(securityMode == 0 ||
-               securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS,
-               "how did we end up here?");
-
-    if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
-      MOZ_ASSERT(!mOuterContext, "AsyncOpen(2) does not take context as a second arg");
-      rv = mOuterChannel->AsyncOpen2(mOuterListener);
-    }
-    else {
-      rv = mOuterChannel->AsyncOpen(mOuterListener, mOuterContext);
-    }
+    mCallback->OnPreflightSucceeded();
+  } else {
+    mCallback->OnPreflightFailed(rv);
   }
 
-  if (NS_FAILED(rv)) {
-    mOuterChannel->Cancel(rv);
-    mOuterListener->OnStartRequest(mOuterChannel, mOuterContext);
-    mOuterListener->OnStopRequest(mOuterChannel, mOuterContext, rv);
-    
-    return rv;
-  }
-
-  return NS_OK;
+  return rv;
 }
 
 NS_IMETHODIMP
 nsCORSPreflightListener::OnStopRequest(nsIRequest *aRequest,
                                        nsISupports *aContext,
                                        nsresult aStatus)
 {
   mOuterChannel = nullptr;
   mOuterListener = nullptr;
   mOuterContext = nullptr;
+  mCallback = nullptr;
   return NS_OK;
 }
 
 /** nsIStreamListener methods **/
 
 NS_IMETHODIMP
 nsCORSPreflightListener::OnDataAvailable(nsIRequest *aRequest,
                                          nsISupports *ctxt,
@@ -1274,23 +1272,34 @@ nsCORSPreflightListener::AsyncOnChannelR
 }
 
 NS_IMETHODIMP
 nsCORSPreflightListener::GetInterface(const nsIID & aIID, void **aResult)
 {
   return QueryInterface(aIID, aResult);
 }
 
+void
+nsCORSListenerProxy::RemoveFromCorsPreflightCache(nsIURI* aURI,
+                                                  nsIPrincipal* aRequestingPrincipal)
+{
+  MOZ_ASSERT(XRE_IsParentProcess());
+  if (sPreflightCache) {
+    sPreflightCache->RemoveEntries(aURI, aRequestingPrincipal);
+  }
+}
+
 nsresult
-NS_StartCORSPreflight(nsIChannel* aRequestChannel,
-                      nsIStreamListener* aListener,
-                      nsIPrincipal* aPrincipal,
-                      bool aWithCredentials,
-                      nsTArray<nsCString>& aUnsafeHeaders,
-                      nsIChannel** aPreflightChannel)
+nsCORSListenerProxy::StartCORSPreflight(nsIChannel* aRequestChannel,
+                                        nsIStreamListener* aListener,
+                                        nsIPrincipal* aPrincipal,
+                                        nsICorsPreflightCallback* aCallback,
+                                        bool aWithCredentials,
+                                        nsTArray<nsCString>& aUnsafeHeaders,
+                                        nsIChannel** aPreflightChannel)
 {
   *aPreflightChannel = nullptr;
 
   nsAutoCString method;
   nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(aRequestChannel));
   NS_ENSURE_TRUE(httpChannel, NS_ERROR_UNEXPECTED);
   httpChannel->GetRequestMethod(method);
 
@@ -1311,21 +1320,18 @@ NS_StartCORSPreflight(nsIChannel* aReque
              "how did we end up here?");
 
   nsPreflightCache::CacheEntry* entry =
     sPreflightCache ?
     sPreflightCache->GetEntry(uri, aPrincipal, aWithCredentials, false) :
     nullptr;
 
   if (entry && entry->CheckRequest(method, aUnsafeHeaders)) {
-    // We have a cached preflight result, just start the original channel
-    if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
-      return aRequestChannel->AsyncOpen2(aListener);
-    }
-    return aRequestChannel->AsyncOpen(aListener, nullptr);
+    aCallback->OnPreflightSucceeded();
+    return NS_OK;
   }
 
   // Either it wasn't cached or the cached result has expired. Build a
   // channel for the OPTIONS request.
 
   nsCOMPtr<nsILoadGroup> loadGroup;
   rv = aRequestChannel->GetLoadGroup(getter_AddRefs(loadGroup));
   NS_ENSURE_SUCCESS(rv, rv);
@@ -1358,17 +1364,17 @@ NS_StartCORSPreflight(nsIChannel* aReque
     // here and allow service workers to intercept CORS preflights, then that
     // check won't be safe any more.
     preInternal->ForceNoIntercept();
   }
   
   // Set up listener which will start the original channel
   nsCOMPtr<nsIStreamListener> preflightListener =
     new nsCORSPreflightListener(aRequestChannel, aListener, nullptr, aPrincipal,
-                                method, aWithCredentials);
+                                aCallback, aWithCredentials);
   NS_ENSURE_TRUE(preflightListener, NS_ERROR_OUT_OF_MEMORY);
 
   // Start preflight
   if (securityMode == nsILoadInfo::SEC_REQUIRE_CORS_DATA_INHERITS) {
     rv = preflightChannel->AsyncOpen2(preflightListener);
   }
   else {
     nsRefPtr<nsCORSListenerProxy> corsListener =
rename from dom/security/nsCORSListenerProxy.h
rename to netwerk/protocol/http/nsCORSListenerProxy.h
--- a/dom/security/nsCORSListenerProxy.h
+++ b/netwerk/protocol/http/nsCORSListenerProxy.h
@@ -17,24 +17,24 @@
 #include "nsIChannelEventSink.h"
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIThreadRetargetableStreamListener.h"
 #include "mozilla/Attributes.h"
 
 class nsIURI;
 class nsIPrincipal;
 class nsINetworkInterceptController;
+class nsICorsPreflightCallback;
 
-nsresult
-NS_StartCORSPreflight(nsIChannel* aRequestChannel,
-                      nsIStreamListener* aListener,
-                      nsIPrincipal* aPrincipal,
-                      bool aWithCredentials,
-                      nsTArray<nsCString>& aACUnsafeHeaders,
-                      nsIChannel** aPreflightChannel);
+namespace mozilla {
+namespace net {
+class HttpChannelParent;
+class nsHttpChannel;
+}
+}
 
 enum class DataURIHandling
 {
   Allow,
   Disallow
 };
 
 class nsCORSListenerProxy final : public nsIStreamListener,
@@ -42,21 +42,16 @@ class nsCORSListenerProxy final : public
                                   public nsIChannelEventSink,
                                   public nsIAsyncVerifyRedirectCallback,
                                   public nsIThreadRetargetableStreamListener
 {
 public:
   nsCORSListenerProxy(nsIStreamListener* aOuter,
                       nsIPrincipal* aRequestingPrincipal,
                       bool aWithCredentials);
-  nsCORSListenerProxy(nsIStreamListener* aOuter,
-                      nsIPrincipal* aRequestingPrincipal,
-                      bool aWithCredentials,
-                      const nsCString& aPreflightMethod,
-                      const nsTArray<nsCString>& aPreflightHeaders);
 
   NS_DECL_ISUPPORTS
   NS_DECL_NSIREQUESTOBSERVER
   NS_DECL_NSISTREAMLISTENER
   NS_DECL_NSIINTERFACEREQUESTOR
   NS_DECL_NSICHANNELEVENTSINK
   NS_DECL_NSIASYNCVERIFYREDIRECTCALLBACK
   NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
@@ -66,16 +61,38 @@ public:
 
   static void Shutdown();
 
   nsresult Init(nsIChannel* aChannel, DataURIHandling aAllowDataURI);
 
   void SetInterceptController(nsINetworkInterceptController* aInterceptController);
 
 private:
+  // Only HttpChannelParent can call RemoveFromCorsPreflightCache
+  friend class mozilla::net::HttpChannelParent;
+  // Only nsHttpChannel can invoke CORS preflights
+  friend class mozilla::net::nsHttpChannel;
+
+  static void RemoveFromCorsPreflightCache(nsIURI* aURI,
+                                           nsIPrincipal* aRequestingPrincipal);
+
+  nsCORSListenerProxy(nsIStreamListener* aOuter,
+                      nsIPrincipal* aRequestingPrincipal,
+                      bool aWithCredentials,
+                      const nsCString& aPreflightMethod,
+                      const nsTArray<nsCString>& aPreflightHeaders);
+
+  static nsresult StartCORSPreflight(nsIChannel* aRequestChannel,
+                                     nsIStreamListener* aListener,
+                                     nsIPrincipal* aPrincipal,
+                                     nsICorsPreflightCallback* aCallback,
+                                     bool aWithCredentials,
+                                     nsTArray<nsCString>& aACUnsafeHeaders,
+                                     nsIChannel** aPreflightChannel);
+
   ~nsCORSListenerProxy();
 
   nsresult UpdateChannel(nsIChannel* aChannel, DataURIHandling aAllowDataURI);
   nsresult CheckRequestApproved(nsIRequest* aRequest);
 
   nsCOMPtr<nsIStreamListener> mOuterListener;
   // The principal that originally kicked off the request
   nsCOMPtr<nsIPrincipal> mRequestingPrincipal;
--- a/netwerk/protocol/http/nsHttpChannel.cpp
+++ b/netwerk/protocol/http/nsHttpChannel.cpp
@@ -256,16 +256,17 @@ nsHttpChannel::nsHttpChannel()
     , mCacheEntryIsReadOnly(false)
     , mCacheEntryIsWriteOnly(false)
     , mCacheEntriesToWaitFor(0)
     , mHasQueryString(0)
     , mConcurentCacheAccess(0)
     , mIsPartialRequest(0)
     , mHasAutoRedirectVetoNotifier(0)
     , mIsPackagedAppResource(0)
+    , mIsCorsPreflightDone(0)
     , mPushedStream(nullptr)
     , mLocalBlocklist(false)
     , mWarningReporter(nullptr)
     , mDidReval(false)
 {
     LOG(("Creating nsHttpChannel [this=%p]\n", this));
     mChannelCreationTime = PR_Now();
     mChannelCreationTimestamp = TimeStamp::Now();
@@ -442,16 +443,36 @@ nsHttpChannel::Connect()
     }
 
     return ContinueConnect();
 }
 
 nsresult
 nsHttpChannel::ContinueConnect()
 {
+    // If we need to start a CORS preflight, do it now!
+    // Note that it is important to do this before the early returns below.
+    if (!mIsCorsPreflightDone && mRequireCORSPreflight &&
+        mInterceptCache != INTERCEPTED) {
+        MOZ_ASSERT(!mPreflightChannel);
+        nsresult rv =
+            nsCORSListenerProxy::StartCORSPreflight(this, mListener,
+                                                    mPreflightPrincipal, this,
+                                                    mWithCredentials,
+                                                    mUnsafeHeaders,
+                                                    getter_AddRefs(mPreflightChannel));
+        return rv;
+    }
+
+    MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight &&
+                         mInterceptCache != INTERCEPTED) ||
+                       mIsCorsPreflightDone,
+                       "CORS preflight must have been finished by the time we "
+                       "do the rest of ContinueConnect");
+
     // we may or may not have a cache entry at this point
     if (mCacheEntry) {
         // read straight from the cache if possible...
         if (mCachedContentIsValid) {
             nsRunnableMethod<nsHttpChannel> *event = nullptr;
             if (!mCachedContentIsPartial) {
                 AsyncCall(&nsHttpChannel::AsyncOnExamineCachedResponse, &event);
             }
@@ -936,16 +957,22 @@ CallTypeSniffers(void *aClosure, const u
   if (!newType.IsEmpty()) {
     chan->SetContentType(newType);
   }
 }
 
 nsresult
 nsHttpChannel::CallOnStartRequest()
 {
+    MOZ_RELEASE_ASSERT(!(mRequireCORSPreflight &&
+                         mInterceptCache != INTERCEPTED) ||
+                       mIsCorsPreflightDone,
+                       "CORS preflight must have been finished by the time we "
+                       "call OnStartRequest");
+
     nsresult rv;
 
     mTracingEnabled = false;
 
     // Allow consumers to override our content type
     if (mLoadFlags & LOAD_CALL_CONTENT_SNIFFERS) {
         // NOTE: We can have both a txn pump and a cache pump when the cache
         // content is partial. In that case, we need to read from the cache,
@@ -4841,32 +4868,35 @@ NS_INTERFACE_MAP_BEGIN(nsHttpChannel)
     NS_INTERFACE_MAP_ENTRY(nsIHttpAuthenticableChannel)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheContainer)
     NS_INTERFACE_MAP_ENTRY(nsIApplicationCacheChannel)
     NS_INTERFACE_MAP_ENTRY(nsIAsyncVerifyRedirectCallback)
     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableRequest)
     NS_INTERFACE_MAP_ENTRY(nsIThreadRetargetableStreamListener)
     NS_INTERFACE_MAP_ENTRY(nsIDNSListener)
     NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
+    NS_INTERFACE_MAP_ENTRY(nsICorsPreflightCallback)
     // we have no macro that covers this case.
     if (aIID.Equals(NS_GET_IID(nsHttpChannel)) ) {
         AddRef();
         *aInstancePtr = this;
         return NS_OK;
     } else
 NS_INTERFACE_MAP_END_INHERITING(HttpBaseChannel)
 
 //-----------------------------------------------------------------------------
 // nsHttpChannel::nsIRequest
 //-----------------------------------------------------------------------------
 
 NS_IMETHODIMP
 nsHttpChannel::Cancel(nsresult status)
 {
     MOZ_ASSERT(NS_IsMainThread());
+    // We should never have a pump open while a CORS preflight is in progress.
+    MOZ_ASSERT_IF(mPreflightChannel, !mCachePump);
 
     LOG(("nsHttpChannel::Cancel [this=%p status=%x]\n", this, status));
     if (mCanceled) {
         LOG(("  ignoring; already canceled\n"));
         return NS_OK;
     }
     if (mWaitingForRedirectCallback) {
         LOG(("channel canceled during wait for redirect callback"));
@@ -4879,16 +4909,18 @@ nsHttpChannel::Cancel(nsresult status)
         gHttpHandler->CancelTransaction(mTransaction, status);
     if (mTransactionPump)
         mTransactionPump->Cancel(status);
     mCacheInputStream.CloseAndRelease();
     if (mCachePump)
         mCachePump->Cancel(status);
     if (mAuthProvider)
         mAuthProvider->Cancel(status);
+    if (mPreflightChannel)
+        mPreflightChannel->Cancel(status);
     return NS_OK;
 }
 
 NS_IMETHODIMP
 nsHttpChannel::Suspend()
 {
     NS_ENSURE_TRUE(mIsPending, NS_ERROR_NOT_AVAILABLE);
 
@@ -6966,10 +6998,32 @@ nsHttpChannel::OnPush(const nsACString &
 }
 
 void
 nsHttpChannel::SetCouldBeSynthesized()
 {
   mResponseCouldBeSynthesized = true;
 }
 
+NS_IMETHODIMP
+nsHttpChannel::OnPreflightSucceeded()
+{
+    MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
+    mIsCorsPreflightDone = 1;
+    mPreflightChannel = nullptr;
+
+    return ContinueConnect();
+}
+
+NS_IMETHODIMP
+nsHttpChannel::OnPreflightFailed(nsresult aError)
+{
+    MOZ_ASSERT(mRequireCORSPreflight, "Why did a preflight happen?");
+    mIsCorsPreflightDone = 1;
+    mPreflightChannel = nullptr;
+
+    CloseCacheEntry(true);
+    AsyncAbort(aError);
+    return NS_OK;
+}
+
 } // namespace net
 } // namespace mozilla
--- a/netwerk/protocol/http/nsHttpChannel.h
+++ b/netwerk/protocol/http/nsHttpChannel.h
@@ -19,16 +19,17 @@
 #include "nsIAsyncVerifyRedirectCallback.h"
 #include "nsIThreadRetargetableRequest.h"
 #include "nsIThreadRetargetableStreamListener.h"
 #include "nsWeakReference.h"
 #include "TimingStruct.h"
 #include "AutoClose.h"
 #include "nsIStreamListener.h"
 #include "nsISupportsPrimitives.h"
+#include "nsICorsPreflightCallback.h"
 
 class nsDNSPrefetch;
 class nsICancelable;
 class nsIHttpChannelAuthProvider;
 class nsInputStreamPump;
 class nsISSLStatus;
 
 namespace mozilla { namespace net {
@@ -64,16 +65,17 @@ class nsHttpChannel final : public HttpB
                           , public nsIProtocolProxyCallback
                           , public nsIHttpAuthenticableChannel
                           , public nsIApplicationCacheChannel
                           , public nsIAsyncVerifyRedirectCallback
                           , public nsIThreadRetargetableRequest
                           , public nsIThreadRetargetableStreamListener
                           , public nsIDNSListener
                           , public nsSupportsWeakReference
+                          , public nsICorsPreflightCallback
 {
 public:
     NS_DECL_ISUPPORTS_INHERITED
     NS_DECL_NSIREQUESTOBSERVER
     NS_DECL_NSISTREAMLISTENER
     NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER
     NS_DECL_NSICACHEINFOCHANNEL
     NS_DECL_NSICACHINGCHANNEL
@@ -144,16 +146,19 @@ public:
     // nsITimedChannel
     NS_IMETHOD GetDomainLookupStart(mozilla::TimeStamp *aDomainLookupStart) override;
     NS_IMETHOD GetDomainLookupEnd(mozilla::TimeStamp *aDomainLookupEnd) override;
     NS_IMETHOD GetConnectStart(mozilla::TimeStamp *aConnectStart) override;
     NS_IMETHOD GetConnectEnd(mozilla::TimeStamp *aConnectEnd) override;
     NS_IMETHOD GetRequestStart(mozilla::TimeStamp *aRequestStart) override;
     NS_IMETHOD GetResponseStart(mozilla::TimeStamp *aResponseStart) override;
     NS_IMETHOD GetResponseEnd(mozilla::TimeStamp *aResponseEnd) override;
+    // nsICorsPreflightCallback
+    NS_IMETHOD OnPreflightSucceeded() override;
+    NS_IMETHOD OnPreflightFailed(nsresult aError) override;
 
     nsresult AddSecurityMessage(const nsAString& aMessageTag,
                                 const nsAString& aMessageCategory) override;
 
     void SetWarningReporter(HttpChannelSecurityWarningReporter* aReporter)
       { mWarningReporter = aReporter; }
 
 public: /* internal necko use only */
@@ -481,16 +486,20 @@ private:
     uint32_t                          mIsPartialRequest : 1;
     // true iff there is AutoRedirectVetoNotifier on the stack
     uint32_t                          mHasAutoRedirectVetoNotifier : 1;
     // Whether fetching the content is meant to be handled by the
     // packaged app service, which behaves like a caching layer.
     // Upon successfully fetching the package, the resource will be placed in
     // the cache, and served by calling OnCacheEntryAvailable.
     uint32_t                          mIsPackagedAppResource : 1;
+    // True if CORS preflight has been performed
+    uint32_t                          mIsCorsPreflightDone : 1;
+
+    nsCOMPtr<nsIChannel>              mPreflightChannel;
 
     nsTArray<nsContinueRedirectionFunc> mRedirectFuncStack;
 
     // Needed for accurate DNS timing
     nsRefPtr<nsDNSPrefetch>           mDNSPrefetch;
 
     Http2PushedStream                 *mPushedStream;
     // True if the channel's principal was found on a phishing, malware, or
--- a/netwerk/protocol/http/nsHttpNTLMAuth.cpp
+++ b/netwerk/protocol/http/nsHttpNTLMAuth.cpp
@@ -16,16 +16,17 @@
 
 //-----------------------------------------------------------------------------
 
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsIHttpAuthenticableChannel.h"
 #include "nsIURI.h"
 #ifdef XP_WIN
+#include "nsIChannel.h"
 #include "nsIX509Cert.h"
 #include "nsISSLStatus.h"
 #include "nsISSLStatusProvider.h"
 #endif
 #include "mozilla/Attributes.h"
 
 namespace mozilla {
 namespace net {
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/http/nsICorsPreflightCallback.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsICorsPreflightCallback_h__
+#define nsICorsPreflightCallback_h__
+
+#include "nsISupports.h"
+#include "nsID.h"
+#include "nsError.h"
+
+#define NS_ICORSPREFLIGHTCALLBACK_IID \
+  { 0x3758cfbb, 0x259f, 0x4074, \
+      { 0xa8, 0xc0, 0x98, 0xe0, 0x4b, 0x3c, 0xc0, 0xe3 } }
+
+class nsICorsPreflightCallback : public nsISupports
+{
+public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(nsICorsPreflightCallback);
+  NS_IMETHOD OnPreflightSucceeded() = 0;
+  NS_IMETHOD OnPreflightFailed(nsresult aError) = 0;
+};
+
+NS_DEFINE_STATIC_IID_ACCESSOR(nsICorsPreflightCallback, NS_ICORSPREFLIGHTCALLBACK_IID);
+
+#endif
--- a/netwerk/protocol/http/nsIHttpChannelChild.idl
+++ b/netwerk/protocol/http/nsIHttpChannelChild.idl
@@ -2,20 +2,27 @@
 /* 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 "nsISupports.idl"
 
 [ptr] native RequestHeaderTuples(mozilla::net::RequestHeaderTuples);
 
-[uuid(3842c5e9-b5b1-400c-8eb7-936a3316ff21)]
+interface nsIPrincipal;
+interface nsIURI;
+
+[uuid(81acb360-edd2-428e-935f-300a32efb649)]
 interface nsIHttpChannelChild : nsISupports
 {
   void addCookiesToRequest();
 
   // Mark this channel as requiring an interception; this will propagate
   // to the corresponding parent channel when a redirect occurs.
   void forceIntercepted();
 
   // Headers that the channel client has set via SetRequestHeader.
   readonly attribute RequestHeaderTuples clientSetRequestHeaders;
+
+  // This method is called by nsCORSListenerProxy if we need to remove
+  // an entry from the CORS preflight cache in the parent process.
+  void removeCorsPreflightCacheEntry(in nsIURI aURI, in nsIPrincipal aRequestingPrincipal);
 };
--- a/netwerk/protocol/http/nsIHttpChannelInternal.idl
+++ b/netwerk/protocol/http/nsIHttpChannelInternal.idl
@@ -6,44 +6,45 @@
 #include "nsISupports.idl"
 
 %{C++
 template<class T> class nsTArray;
 template<class T> class nsCOMArray;
 class nsCString;
 %}
 [ptr] native StringArray(nsTArray<nsCString>);
+[ref] native StringArrayRef(const nsTArray<nsCString>);
 [ref] native securityMessagesArray(nsCOMArray<nsISecurityConsoleMessage>);
 
 interface nsIAsyncInputStream;
 interface nsIAsyncOutputStream;
 interface nsIPrincipal;
 interface nsIProxyInfo;
 interface nsISecurityConsoleMessage;
 interface nsISocketTransport;
 interface nsIURI;
 
 /**
  * The callback interface for nsIHttpChannelInternal::HTTPUpgrade()
  */
 
-[scriptable, uuid(7b48d081-1dc1-4d08-b7a5-81491bf28c0e)]
+[scriptable, uuid(5b515449-ab64-4dba-b3cd-da8fc2f83064)]
 interface nsIHttpUpgradeListener : nsISupports
 {
     void onTransportAvailable(in nsISocketTransport   aTransport,
                               in nsIAsyncInputStream  aSocketIn,
                               in nsIAsyncOutputStream aSocketOut);
 };
 
 /**
  * Dumping ground for http.  This interface will never be frozen.  If you are
  * using any feature exposed by this interface, be aware that this interface
  * will change and you will be broken.  You have been warned.
  */
-[scriptable, uuid(e2eebad8-e51f-473a-bbb7-8e2829376625)]
+[scriptable, uuid(46ef729f-4c9b-4084-b9e2-498992a31aee)]
 
 interface nsIHttpChannelInternal : nsISupports
 {
     /**
      * An http channel can own a reference to the document URI
      */
     attribute nsIURI documentURI;
 
@@ -259,9 +260,18 @@ interface nsIHttpChannelInternal : nsISu
      */
     attribute ACString networkInterfaceId;
 
     /**
      * Read the proxy URI, which, if non-null, will be used to resolve
      * proxies for this channel.
      */
     readonly attribute nsIURI proxyURI;
+
+    /**
+     * Make cross-origin CORS loads happen with a CORS preflight, and specify
+     * the CORS preflight parameters.
+     */
+    [noscript]
+    void setCorsPreflightParameters(in StringArrayRef unsafeHeaders,
+                                    in boolean withCredentials,
+                                    in nsIPrincipal preflightPrincipal);
 };
--- a/rdf/base/nsRDFXMLDataSource.cpp
+++ b/rdf/base/nsRDFXMLDataSource.cpp
@@ -478,22 +478,22 @@ RDFXMLDataSourceImpl::BlockingParse(nsIU
     
     nsCOMPtr<nsIChannel> channel;
     nsCOMPtr<nsIRequest> request;
 
     // Null LoadGroup ?
     rv = NS_NewChannel(getter_AddRefs(channel),
                        aURL,
                        nsContentUtils::GetSystemPrincipal(),
-                       nsILoadInfo::SEC_NORMAL,
+                       nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL,
                        nsIContentPolicy::TYPE_OTHER);
 
     if (NS_FAILED(rv)) return rv;
     nsCOMPtr<nsIInputStream> in;
-    rv = channel->Open(getter_AddRefs(in));
+    rv = channel->Open2(getter_AddRefs(in));
 
     // Report success if the file doesn't exist, but propagate other errors.
     if (rv == NS_ERROR_FILE_NOT_FOUND) return NS_OK;
     if (NS_FAILED(rv)) return rv;
 
     if (! in) {
         NS_ERROR("no input stream");
         return NS_ERROR_FAILURE;
--- a/security/apps/AppTrustDomain.cpp
+++ b/security/apps/AppTrustDomain.cpp
@@ -240,17 +240,19 @@ AppTrustDomain::IsChainValid(const DERAr
                                                             mCertChain);
   if (srv != SECSuccess) {
     return MapPRErrorCodeToResult(PR_GetError());
   }
   return Success;
 }
 
 Result
-AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA)
+AppTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm,
+                                              EndEntityOrCA,
+                                              Time)
 {
   // TODO: We should restrict signatures to SHA-256 or better.
   return Success;
 }
 
 Result
 AppTrustDomain::CheckRSAPublicKeyModulusSizeInBits(
   EndEntityOrCA /*endEntityOrCA*/, unsigned int modulusSizeInBits)
--- a/security/apps/AppTrustDomain.h
+++ b/security/apps/AppTrustDomain.h
@@ -36,17 +36,18 @@ public:
                                  mozilla::pkix::Time time,
                                  mozilla::pkix::Duration validityDuration,
                     /*optional*/ const mozilla::pkix::Input* stapledOCSPresponse,
                     /*optional*/ const mozilla::pkix::Input* aiaExtension) override;
   virtual Result IsChainValid(const mozilla::pkix::DERArray& certChain,
                               mozilla::pkix::Time time) override;
   virtual Result CheckSignatureDigestAlgorithm(
                    mozilla::pkix::DigestAlgorithm digestAlg,
-                   mozilla::pkix::EndEntityOrCA endEntityOrCA) override;
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   mozilla::pkix::Time notBefore) override;
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    unsigned int modulusSizeInBits) override;
   virtual Result VerifyRSAPKCS1SignedDigest(
                    const mozilla::pkix::SignedDigest& signedDigest,
                    mozilla::pkix::Input subjectPublicKeyInfo) override;
   virtual Result CheckECDSACurveIsAcceptable(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
--- a/security/certverifier/CertVerifier.cpp
+++ b/security/certverifier/CertVerifier.cpp
@@ -28,22 +28,24 @@ namespace mozilla { namespace psm {
 
 const CertVerifier::Flags CertVerifier::FLAG_LOCAL_ONLY = 1;
 const CertVerifier::Flags CertVerifier::FLAG_MUST_BE_EV = 2;
 
 CertVerifier::CertVerifier(OcspDownloadConfig odc,
                            OcspStrictConfig osc,
                            OcspGetConfig ogc,
                            uint32_t certShortLifetimeInDays,
-                           PinningMode pinningMode)
+                           PinningMode pinningMode,
+                           SHA1Mode sha1Mode)
   : mOCSPDownloadConfig(odc)
   , mOCSPStrict(osc == ocspStrict)
   , mOCSPGETEnabled(ogc == ocspGetEnabled)
   , mCertShortLifetimeInDays(certShortLifetimeInDays)
   , mPinningMode(pinningMode)
+  , mSHA1Mode(sha1Mode)
 {
 }
 
 CertVerifier::~CertVerifier()
 {
 }
 
 void
@@ -208,18 +210,18 @@ CertVerifier::VerifyCert(CERTCertificate
     case certificateUsageSSLClient: {
       // XXX: We don't really have a trust bit for SSL client authentication so
       // just use trustEmail as it is the closest alternative.
       NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       AcceptAllAlgorithms, nullptr, nullptr,
-                                       builtChain);
+                                       AcceptAllAlgorithms, SHA1Mode::Allowed,
+                                       nullptr, nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_clientAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
@@ -268,18 +270,18 @@ CertVerifier::VerifyCert(CERTCertificate
         if (pinningTelemetryInfo) {
           pinningTelemetryInfo->Reset();
         }
         NSSCertDBTrustDomain
           trustDomain(trustSSL, evOCSPFetching,
                       mOCSPCache, pinArg, ocspGETConfig,
                       mCertShortLifetimeInDays, mPinningMode, MIN_RSA_BITS,
                       ValidityCheckingMode::CheckForEV,
-                      digestAlgorithmOptions[i], pinningTelemetryInfo, hostname,
-                      builtChain);
+                      digestAlgorithmOptions[i], mSHA1Mode,
+                      pinningTelemetryInfo, hostname, builtChain);
         rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                           KeyUsage::digitalSignature,// (EC)DHE
                                           KeyUsage::keyEncipherment, // RSA
                                           KeyUsage::keyAgreement,    // (EC)DH
                                           KeyPurposeId::id_kp_serverAuth,
                                           evPolicy, stapledOCSPResponse,
                                           ocspStaplingStatus);
         if (rv == Success) {
@@ -323,23 +325,29 @@ CertVerifier::VerifyCert(CERTCertificate
       for (size_t i=0; i<keySizeOptionsCount && rv != Success; i++) {
         for (size_t j=0; j<digestAlgorithmOptionsCount && rv != Success; j++) {
 
           // invalidate any telemetry info relating to failed chains
           if (pinningTelemetryInfo) {
             pinningTelemetryInfo->Reset();
           }
 
+          // If we're not going to do SHA-1 in any case, don't try
+          if (mSHA1Mode == SHA1Mode::Forbidden &&
+              digestAlgorithmOptions[i] != DisableSHA1Everywhere) {
+            continue;
+          }
+
           NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
                                            mOCSPCache, pinArg, ocspGETConfig,
                                            mCertShortLifetimeInDays,
                                            mPinningMode, keySizeOptions[i],
                                            ValidityCheckingMode::CheckingOff,
                                            digestAlgorithmOptions[j],
-                                           pinningTelemetryInfo,
+                                           mSHA1Mode, pinningTelemetryInfo,
                                            hostname, builtChain);
           rv = BuildCertChainForOneKeyUsage(trustDomain, certDER, time,
                                             KeyUsage::digitalSignature,//(EC)DHE
                                             KeyUsage::keyEncipherment,//RSA
                                             KeyUsage::keyAgreement,//(EC)DH
                                             KeyPurposeId::id_kp_serverAuth,
                                             CertPolicyId::anyPolicy,
                                             stapledOCSPResponse,
@@ -351,52 +359,58 @@ CertVerifier::VerifyCert(CERTCertificate
             if (sigDigestStatus) {
               *sigDigestStatus = digestAlgorithmStatuses[j];
             }
           }
         }
       }
 
       if (rv == Success) {
+        // If SHA-1 is forbidden by preference, don't accumulate SHA-1
+        // telemetry, to avoid skewing the results.
+        if (sigDigestStatus && mSHA1Mode == SHA1Mode::Forbidden) {
+          *sigDigestStatus = SignatureDigestStatus::NeverChecked;
+        }
+
         break;
       }
 
       if (keySizeStatus) {
         *keySizeStatus = KeySizeStatus::AlreadyBad;
       }
-      if (sigDigestStatus) {
+      if (sigDigestStatus && mSHA1Mode != SHA1Mode::Forbidden) {
         *sigDigestStatus = SignatureDigestStatus::AlreadyBad;
       }
 
       break;
     }
 
     case certificateUsageSSLCA: {
       NSSCertDBTrustDomain trustDomain(trustSSL, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       AcceptAllAlgorithms, nullptr, nullptr,
-                                       builtChain);
+                                       AcceptAllAlgorithms, mSHA1Mode,
+                                       nullptr, nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeCA, KeyUsage::keyCertSign,
                           KeyPurposeId::id_kp_serverAuth,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
     case certificateUsageEmailSigner: {
       NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       AcceptAllAlgorithms, nullptr, nullptr,
-                                       builtChain);
+                                       AcceptAllAlgorithms, SHA1Mode::Allowed,
+                                       nullptr, nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, certDER, time,
                             EndEntityOrCA::MustBeEndEntity,
@@ -411,18 +425,18 @@ CertVerifier::VerifyCert(CERTCertificate
       // TODO: The higher level S/MIME processing should pass in which key
       // usage it is trying to verify for, and base its algorithm choices
       // based on the result of the verification(s).
       NSSCertDBTrustDomain trustDomain(trustEmail, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       AcceptAllAlgorithms, nullptr, nullptr,
-                                       builtChain);
+                                       AcceptAllAlgorithms, SHA1Mode::Allowed,
+                                       nullptr, nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::keyEncipherment, // RSA
                           KeyPurposeId::id_kp_emailProtection,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       if (rv == Result::ERROR_INADEQUATE_KEY_USAGE) {
         rv = BuildCertChain(trustDomain, certDER, time,
                             EndEntityOrCA::MustBeEndEntity,
@@ -434,18 +448,18 @@ CertVerifier::VerifyCert(CERTCertificate
     }
 
     case certificateUsageObjectSigner: {
       NSSCertDBTrustDomain trustDomain(trustObjectSigning, defaultOCSPFetching,
                                        mOCSPCache, pinArg, ocspGETConfig,
                                        mCertShortLifetimeInDays,
                                        pinningDisabled, MIN_RSA_BITS_WEAK,
                                        ValidityCheckingMode::CheckingOff,
-                                       AcceptAllAlgorithms, nullptr, nullptr,
-                                       builtChain);
+                                       AcceptAllAlgorithms, SHA1Mode::Allowed,
+                                       nullptr, nullptr, builtChain);
       rv = BuildCertChain(trustDomain, certDER, time,
                           EndEntityOrCA::MustBeEndEntity,
                           KeyUsage::digitalSignature,
                           KeyPurposeId::id_kp_codeSigning,
                           CertPolicyId::anyPolicy, stapledOCSPResponse);
       break;
     }
 
@@ -466,41 +480,41 @@ CertVerifier::VerifyCert(CERTCertificate
         keyUsage = KeyUsage::digitalSignature;
         eku = KeyPurposeId::id_kp_OCSPSigning;
       }
 
       NSSCertDBTrustDomain sslTrust(trustSSL, defaultOCSPFetching, mOCSPCache,
                                     pinArg, ocspGETConfig, mCertShortLifetimeInDays,
                                     pinningDisabled, MIN_RSA_BITS_WEAK,
                                     ValidityCheckingMode::CheckingOff,
-                                    AcceptAllAlgorithms, nullptr, nullptr,
-                                    builtChain);
+                                    AcceptAllAlgorithms, SHA1Mode::Allowed,
+                                    nullptr, nullptr, builtChain);
       rv = BuildCertChain(sslTrust, certDER, time, endEntityOrCA,
                           keyUsage, eku, CertPolicyId::anyPolicy,
                           stapledOCSPResponse);
       if (rv == Result::ERROR_UNKNOWN_ISSUER) {
         NSSCertDBTrustDomain emailTrust(trustEmail, defaultOCSPFetching,
                                         mOCSPCache, pinArg, ocspGETConfig,
                                         mCertShortLifetimeInDays,
                                         pinningDisabled, MIN_RSA_BITS_WEAK,
                                         ValidityCheckingMode::CheckingOff,
-                                        AcceptAllAlgorithms, nullptr, nullptr,
-                                        builtChain);
+                                        AcceptAllAlgorithms, SHA1Mode::Allowed,
+                                        nullptr, nullptr, builtChain);
         rv = BuildCertChain(emailTrust, certDER, time, endEntityOrCA,
                             keyUsage, eku, CertPolicyId::anyPolicy,
                             stapledOCSPResponse);
         if (rv == Result::ERROR_UNKNOWN_ISSUER) {
           NSSCertDBTrustDomain objectSigningTrust(trustObjectSigning,
                                                   defaultOCSPFetching, mOCSPCache,
                                                   pinArg, ocspGETConfig,
                                                   mCertShortLifetimeInDays,
                                                   pinningDisabled,
                                                   MIN_RSA_BITS_WEAK,
                                                   ValidityCheckingMode::CheckingOff,
-                                                  AcceptAllAlgorithms,
+                                                  AcceptAllAlgorithms, SHA1Mode::Allowed,
                                                   nullptr, nullptr, builtChain);
           rv = BuildCertChain(objectSigningTrust, certDER, time,
                               endEntityOrCA, keyUsage, eku,
                               CertPolicyId::anyPolicy, stapledOCSPResponse);
         }
       }
 
       break;
--- a/security/certverifier/CertVerifier.h
+++ b/security/certverifier/CertVerifier.h
@@ -97,36 +97,43 @@ public:
 
   enum PinningMode {
     pinningDisabled = 0,
     pinningAllowUserCAMITM = 1,
     pinningStrict = 2,
     pinningEnforceTestMode = 3
   };
 
+  enum class SHA1Mode {
+    Allowed = 0,
+    Forbidden = 1,
+    OnlyBefore2016 = 2
+  };
+
   enum OcspDownloadConfig {
     ocspOff = 0,
     ocspOn = 1,
     ocspEVOnly = 2
   };
   enum OcspStrictConfig { ocspRelaxed = 0, ocspStrict };
   enum OcspGetConfig { ocspGetDisabled = 0, ocspGetEnabled = 1 };
 
   CertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
                OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
-               PinningMode pinningMode);
+               PinningMode pinningMode, SHA1Mode sha1Mode);
   ~CertVerifier();
 
   void ClearOCSPCache() { mOCSPCache.Clear(); }
 
   const OcspDownloadConfig mOCSPDownloadConfig;
   const bool mOCSPStrict;
   const bool mOCSPGETEnabled;
   const uint32_t mCertShortLifetimeInDays;
   const PinningMode mPinningMode;
+  const SHA1Mode mSHA1Mode;
 
 private:
   OCSPCache mOCSPCache;
 };
 
 void InitCertVerifierLog();
 SECStatus IsCertBuiltInRoot(CERTCertificate* cert, bool& result);
 mozilla::pkix::Result CertListContainsExpectedKeys(
--- a/security/certverifier/NSSCertDBTrustDomain.cpp
+++ b/security/certverifier/NSSCertDBTrustDomain.cpp
@@ -46,29 +46,31 @@ NSSCertDBTrustDomain::NSSCertDBTrustDoma
                                            OCSPCache& ocspCache,
              /*optional but shouldn't be*/ void* pinArg,
                                            CertVerifier::OcspGetConfig ocspGETConfig,
                                            uint32_t certShortLifetimeInDays,
                                            CertVerifier::PinningMode pinningMode,
                                            unsigned int minRSABits,
                                            ValidityCheckingMode validityCheckingMode,
                                            SignatureDigestOption signatureDigestOption,
+                                           CertVerifier::SHA1Mode sha1Mode,
                               /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo,
                               /*optional*/ const char* hostname,
                               /*optional*/ ScopedCERTCertList* builtChain)
   : mCertDBTrustType(certDBTrustType)
   , mOCSPFetching(ocspFetching)
   , mOCSPCache(ocspCache)
   , mPinArg(pinArg)
   , mOCSPGetConfig(ocspGETConfig)
   , mCertShortLifetimeInDays(certShortLifetimeInDays)
   , mPinningMode(pinningMode)
   , mMinRSABits(minRSABits)
   , mValidityCheckingMode(validityCheckingMode)
   , mSignatureDigestOption(signatureDigestOption)
+  , mSHA1Mode(sha1Mode)
   , mPinningTelemetryInfo(pinningTelemetryInfo)
   , mHostname(hostname)
   , mBuiltChain(builtChain)
   , mCertBlocklist(do_GetService(NS_CERTBLOCKLIST_CONTRACTID))
   , mOCSPStaplingStatus(CertVerifier::OCSP_STAPLING_NEVER_CHECKED)
 {
 }
 
@@ -808,32 +810,54 @@ NSSCertDBTrustDomain::IsChainValid(const
     *mBuiltChain = certList.forget();
   }
 
   return Success;
 }
 
 Result
 NSSCertDBTrustDomain::CheckSignatureDigestAlgorithm(DigestAlgorithm aAlg,
-                                                    EndEntityOrCA endEntityOrCA)
+                                                    EndEntityOrCA endEntityOrCA,
+                                                    Time notBefore)
 {
+  // (new Date("2016-01-01T00:00:00Z")).getTime() / 1000
+  static const Time JANUARY_FIRST_2016 = TimeFromEpochInSeconds(1451606400);
+
   MOZ_LOG(gCertVerifierLog, LogLevel::Debug,
           ("NSSCertDBTrustDomain: CheckSignatureDigestAlgorithm"));
   if (aAlg == DigestAlgorithm::sha1) {
+    // First check based on SHA1Mode
+    switch (mSHA1Mode) {
+      case CertVerifier::SHA1Mode::Forbidden:
+        MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
+        return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+      case CertVerifier::SHA1Mode::OnlyBefore2016:
+        if (JANUARY_FIRST_2016 <= notBefore) {
+          MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("Post-2015 SHA-1 certificate rejected"));
+          return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
+        }
+        break;
+      case CertVerifier::SHA1Mode::Allowed:
+      default:
+        break;
+    }
+
+    // Then check the signatureDigestOption values
     if (mSignatureDigestOption == DisableSHA1Everywhere) {
+      MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("SHA-1 certificate rejected"));
       return Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED;
     }
 
     if (endEntityOrCA == EndEntityOrCA::MustBeCA) {
-    MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("CA cert is SHA1"));
+      MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("CA cert is SHA-1"));
       return mSignatureDigestOption == DisableSHA1ForCA
              ? Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
              : Success;
     } else {
-    MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("EE cert is SHA1"));
+      MOZ_LOG(gCertVerifierLog, LogLevel::Debug, ("EE cert is SHA-1"));
       return mSignatureDigestOption == DisableSHA1ForEE
              ? Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED
              : Success;
     }
   }
   return Success;
 }
 
--- a/security/certverifier/NSSCertDBTrustDomain.h
+++ b/security/certverifier/NSSCertDBTrustDomain.h
@@ -64,34 +64,36 @@ public:
 
   NSSCertDBTrustDomain(SECTrustType certDBTrustType, OCSPFetching ocspFetching,
                        OCSPCache& ocspCache, void* pinArg,
                        CertVerifier::OcspGetConfig ocspGETConfig,
                        uint32_t certShortLifetimeInDays,
                        CertVerifier::PinningMode pinningMode,
                        unsigned int minRSABits,
                        ValidityCheckingMode validityCheckingMode,
-                       SignatureDigestOption,
+                       SignatureDigestOption signatureDigestOption,
+                       CertVerifier::SHA1Mode sha1Mode,
           /*optional*/ PinningTelemetryInfo* pinningTelemetryInfo = nullptr,
           /*optional*/ const char* hostname = nullptr,
       /*optional out*/ ScopedCERTCertList* builtChain = nullptr);
 
   virtual Result FindIssuer(mozilla::pkix::Input encodedIssuerName,
                             IssuerChecker& checker,
                             mozilla::pkix::Time time) override;
 
   virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                               const mozilla::pkix::CertPolicyId& policy,
                               mozilla::pkix::Input candidateCertDER,
                               /*out*/ mozilla::pkix::TrustLevel& trustLevel)
                               override;
 
   virtual Result CheckSignatureDigestAlgorithm(
                    mozilla::pkix::DigestAlgorithm digestAlg,
-                   mozilla::pkix::EndEntityOrCA endEntityOrCA) override;
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   mozilla::pkix::Time notBefore) override;
 
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    unsigned int modulusSizeInBits) override;
 
   virtual Result VerifyRSAPKCS1SignedDigest(
                    const mozilla::pkix::SignedDigest& signedDigest,
                    mozilla::pkix::Input subjectPublicKeyInfo) override;
@@ -150,16 +152,17 @@ private:
   OCSPCache& mOCSPCache; // non-owning!
   void* mPinArg; // non-owning!
   const CertVerifier::OcspGetConfig mOCSPGetConfig;
   const uint32_t mCertShortLifetimeInDays;
   CertVerifier::PinningMode mPinningMode;
   const unsigned int mMinRSABits;
   ValidityCheckingMode mValidityCheckingMode;
   SignatureDigestOption mSignatureDigestOption;
+  CertVerifier::SHA1Mode mSHA1Mode;
   PinningTelemetryInfo* mPinningTelemetryInfo;
   const char* mHostname; // non-owning - only used for pinning checks
   ScopedCERTCertList* mBuiltChain; // non-owning
   nsCOMPtr<nsICertBlocklist> mCertBlocklist;
   CertVerifier::OCSPStaplingStatus mOCSPStaplingStatus;
 };
 
 } } // namespace mozilla::psm
--- a/security/certverifier/OCSPVerificationTrustDomain.cpp
+++ b/security/certverifier/OCSPVerificationTrustDomain.cpp
@@ -46,17 +46,17 @@ OCSPVerificationTrustDomain::CheckRevoca
                                              const Input*)
 {
   // We do not expect this to be called for OCSP signers
   return Result::FATAL_ERROR_LIBRARY_FAILURE;
 }
 
 Result
 OCSPVerificationTrustDomain::CheckSignatureDigestAlgorithm(
-  DigestAlgorithm aAlg, EndEntityOrCA aEEOrCA)
+  DigestAlgorithm aAlg, EndEntityOrCA aEEOrCA, Time notBefore)
 {
   // The reason for wrapping the NSSCertDBTrustDomain in an
   // OCSPVerificationTrustDomain is to allow us to bypass the weaker signature
   // algorithm check - thus all allowable signature digest algorithms should
   // always be accepted. This is only needed while we gather telemetry on SHA-1.
   return Success;
 }
 
--- a/security/certverifier/OCSPVerificationTrustDomain.h
+++ b/security/certverifier/OCSPVerificationTrustDomain.h
@@ -24,17 +24,18 @@ public:
   virtual Result GetCertTrust(mozilla::pkix::EndEntityOrCA endEntityOrCA,
                               const mozilla::pkix::CertPolicyId& policy,
                               mozilla::pkix::Input candidateCertDER,
                       /*out*/ mozilla::pkix::TrustLevel& trustLevel)
                               override;
 
   virtual Result CheckSignatureDigestAlgorithm(
                    mozilla::pkix::DigestAlgorithm digestAlg,
-                   mozilla::pkix::EndEntityOrCA endEntityOrCA) override;
+                   mozilla::pkix::EndEntityOrCA endEntityOrCA,
+                   mozilla::pkix::Time notBefore) override;
 
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    mozilla::pkix::EndEntityOrCA endEntityOrCA,
                    unsigned int modulusSizeInBits) override;
 
   virtual Result VerifyRSAPKCS1SignedDigest(
                    const mozilla::pkix::SignedDigest& signedDigest,
                    mozilla::pkix::Input subjectPublicKeyInfo) override;
--- a/security/manager/ssl/SharedCertVerifier.h
+++ b/security/manager/ssl/SharedCertVerifier.h
@@ -16,18 +16,18 @@ class SharedCertVerifier : public mozill
 protected:
   ~SharedCertVerifier();
 
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(SharedCertVerifier)
 
   SharedCertVerifier(OcspDownloadConfig odc, OcspStrictConfig osc,
                      OcspGetConfig ogc, uint32_t certShortLifetimeInDays,
-                     PinningMode pinningMode)
+                     PinningMode pinningMode, SHA1Mode sha1Mode)
     : mozilla::psm::CertVerifier(odc, osc, ogc, certShortLifetimeInDays,
-                                 pinningMode)
+                                 pinningMode, sha1Mode)
   {
   }
 };
 
 } } // namespace mozilla::psm
 
 #endif // mozilla_psm__SharedCertVerifier_h
--- a/security/manager/ssl/nsIX509CertDB.idl
+++ b/security/manager/ssl/nsIX509CertDB.idl
@@ -41,17 +41,17 @@ interface nsIVerifySignedManifestCallbac
   void verifySignedManifestFinished(in nsresult rv,
                                     in nsIX509Cert aSignerCert);
 };
 
 /**
  * This represents a service to access and manipulate
  * X.509 certificates stored in a database.
  */
-[scriptable, uuid(c9fdec46-5c4c-4b1d-a0ca-c2bc10151b69)]
+[scriptable, uuid(3fe3702b-766b-47dd-8f77-c08c3a339a74)]
 interface nsIX509CertDB : nsISupports {
 
   /**
    *  Constants that define which usages a certificate
    *  is trusted for.
    */
   const unsigned long UNTRUSTED       =      0;
   const unsigned long TRUSTED_SSL     = 1 << 0;
@@ -380,22 +380,31 @@ interface nsIX509CertDB : nsISupports {
    *  On success, the call returns 0, the chain built during verification,
    *  and whether the cert is good for EV usage.
    *  On failure, the call returns the PRErrorCode for the verification failure
    *
    *  @param aCert Obtain the stored trust of this certificate
    *  @param aUsage a integer representing the usage from NSS
    *  @param aFlags flags as described above
    *  @param aHostname the (optional) hostname to verify for
+   *  @param aTime the time at which to verify, in seconds since the epoch
    *  @param aVerifiedChain chain of verification up to the root if success
    *  @param aHasEVPolicy bool that signified that the cert was an EV cert
    *  @return 0 if success or the value or the error code for the verification
    *          failure
    */
   int32_t /*PRErrorCode*/
+    verifyCertAtTime(in nsIX509Cert aCert,
+                     in int64_t /*SECCertificateUsage*/ aUsage,
+                     in uint32_t aFlags,
+                     in string aHostname,
+                     in uint64_t aTime,
+                     out nsIX509CertList aVerifiedChain,
+                     out bool aHasEVPolicy);
+  int32_t /*PRErrorCode*/
     verifyCertNow(in nsIX509Cert aCert,
                   in int64_t /*SECCertificateUsage*/ aUsage,
                   in uint32_t aFlags,
                   in string aHostname,
                   out nsIX509CertList aVerifiedChain,
                   out bool aHasEVPolicy);
 
   // Clears the OCSP cache for the current certificate verification
--- a/security/manager/ssl/nsNSSCertificateDB.cpp
+++ b/security/manager/ssl/nsNSSCertificateDB.cpp
@@ -9,16 +9,17 @@
 
 #include "nsNSSComponent.h"
 #include "nsNSSCertificateDB.h"
 
 #include "CertVerifier.h"
 #include "ExtendedValidation.h"
 #include "NSSCertDBTrustDomain.h"
 #include "pkix/pkixtypes.h"
+#include "pkix/Time.h"
 #include "nsNSSComponent.h"
 #include "mozilla/Base64.h"
 #include "nsCOMPtr.h"
 #include "nsNSSCertificate.h"
 #include "nsNSSHelper.h"
 #include "nsNSSCertHelper.h"
 #include "nsCRT.h"
 #include "nsICertificateDialogs.h"
@@ -1693,39 +1694,36 @@ nsNSSCertificateDB::GetCerts(nsIX509Cert
   // nsNSSCertList 1) adopts certList, and 2) handles the nullptr case fine.
   // (returns an empty list) 
   nssCertList = new nsNSSCertList(certList, locker);
 
   nssCertList.forget(_retval);
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
-                                  int64_t /*SECCertificateUsage*/ aUsage,
-                                  uint32_t aFlags,
-                                  const char* aHostname,
-                                  nsIX509CertList** aVerifiedChain,
-                                  bool* aHasEVPolicy,
-                                  int32_t* /*PRErrorCode*/ _retval )
+nsresult
+VerifyCertAtTime(nsIX509Cert* aCert,
+                 int64_t /*SECCertificateUsage*/ aUsage,
+                 uint32_t aFlags,
+                 const char* aHostname,
+                 mozilla::pkix::Time aTime,
+                 nsIX509CertList** aVerifiedChain,
+                 bool* aHasEVPolicy,
+                 int32_t* /*PRErrorCode*/ _retval,
+                 const nsNSSShutDownPreventionLock& locker)
 {
   NS_ENSURE_ARG_POINTER(aCert);
   NS_ENSURE_ARG_POINTER(aHasEVPolicy);
   NS_ENSURE_ARG_POINTER(aVerifiedChain);
   NS_ENSURE_ARG_POINTER(_retval);
 
   *aVerifiedChain = nullptr;
   *aHasEVPolicy = false;
   *_retval = PR_UNKNOWN_ERROR;
 
-  nsNSSShutDownPreventionLock locker;
-  if (isAlreadyShutDown()) {
-    return NS_ERROR_NOT_AVAILABLE;
-  }
-
 #ifndef MOZ_NO_EV_CERTS
   EnsureIdentityInfoLoaded();
 #endif
 
   ScopedCERTCertificate nssCert(aCert->GetCert());
   if (!nssCert) {
     return NS_ERROR_INVALID_ARG;
   }
@@ -1735,25 +1733,25 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
 
   ScopedCERTCertList resultChain;
   SECOidTag evOidPolicy;
   SECStatus srv;
 
   if (aHostname && aUsage == certificateUsageSSLServer) {
     srv = certVerifier->VerifySSLServerCert(nssCert,
                                             nullptr, // stapledOCSPResponse
-                                            mozilla::pkix::Now(),
+                                            aTime,
                                             nullptr, // Assume no context
                                             aHostname,
                                             false, // don't save intermediates
                                             aFlags,
                                             &resultChain,
                                             &evOidPolicy);
   } else {
-    srv = certVerifier->VerifyCert(nssCert, aUsage, mozilla::pkix::Now(),
+    srv = certVerifier->VerifyCert(nssCert, aUsage, aTime,
                                    nullptr, // Assume no context
                                    aHostname,
                                    aFlags,
                                    nullptr, // stapledOCSPResponse
                                    &resultChain,
                                    &evOidPolicy);
   }
 
@@ -1775,16 +1773,55 @@ nsNSSCertificateDB::VerifyCertNow(nsIX50
     *_retval = error;
   }
   nssCertList.forget(aVerifiedChain);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsNSSCertificateDB::VerifyCertNow(nsIX509Cert* aCert,
+                                  int64_t /*SECCertificateUsage*/ aUsage,
+                                  uint32_t aFlags,
+                                  const char* aHostname,
+                                  nsIX509CertList** aVerifiedChain,
+                                  bool* aHasEVPolicy,
+                                  int32_t* /*PRErrorCode*/ _retval)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname,
+                            mozilla::pkix::Now(),
+                            aVerifiedChain, aHasEVPolicy, _retval, locker);
+}
+
+NS_IMETHODIMP
+nsNSSCertificateDB::VerifyCertAtTime(nsIX509Cert* aCert,
+                                     int64_t /*SECCertificateUsage*/ aUsage,
+                                     uint32_t aFlags,
+                                     const char* aHostname,
+                                     uint64_t aTime,
+                                     nsIX509CertList** aVerifiedChain,
+                                     bool* aHasEVPolicy,
+                                     int32_t* /*PRErrorCode*/ _retval)
+{
+  nsNSSShutDownPreventionLock locker;
+  if (isAlreadyShutDown()) {
+    return NS_ERROR_NOT_AVAILABLE;
+  }
+
+  return ::VerifyCertAtTime(aCert, aUsage, aFlags, aHostname,
+                            mozilla::pkix::TimeFromEpochInSeconds(aTime),
+                            aVerifiedChain, aHasEVPolicy, _retval, locker);
+}
+
+NS_IMETHODIMP
 nsNSSCertificateDB::ClearOCSPCache()
 {
   nsNSSShutDownPreventionLock locker;
   if (isAlreadyShutDown()) {
     return NS_ERROR_NOT_AVAILABLE;
   }
 
   RefPtr<SharedCertVerifier> certVerifier(GetDefaultCertVerifier());
--- a/security/manager/ssl/nsNSSComponent.cpp
+++ b/security/manager/ssl/nsNSSComponent.cpp
@@ -883,26 +883,33 @@ void nsNSSComponent::setValidationOption
   CertVerifier::PinningMode pinningMode =
     static_cast<CertVerifier::PinningMode>
       (Preferences::GetInt("security.cert_pinning.enforcement_level",
                            CertVerifier::pinningDisabled));
   if (pinningMode > CertVerifier::pinningEnforceTestMode) {
     pinningMode = CertVerifier::pinningDisabled;
   }
 
+  CertVerifier::SHA1Mode sha1Mode = static_cast<CertVerifier::SHA1Mode>
+      (Preferences::GetInt("security.pki.sha1_enforcement_level",
+                           static_cast<int32_t>(CertVerifier::SHA1Mode::Allowed)));
+  if (sha1Mode > CertVerifier::SHA1Mode::OnlyBefore2016) {
+    sha1Mode = CertVerifier::SHA1Mode::Allowed;
+  }
+
   CertVerifier::OcspDownloadConfig odc;
   CertVerifier::OcspStrictConfig osc;
   CertVerifier::OcspGetConfig ogc;
   uint32_t certShortLifetimeInDays;
 
   GetRevocationBehaviorFromPrefs(&odc, &osc, &ogc, &certShortLifetimeInDays,
                                  lock);
   mDefaultCertVerifier = new SharedCertVerifier(odc, osc, ogc,
                                                 certShortLifetimeInDays,
-                                                pinningMode);
+                                                pinningMode, sha1Mode);
 }
 
 // Enable the TLS versions given in the prefs, defaulting to TLS 1.0 (min) and
 // TLS 1.2 (max) when the prefs aren't set or set to invalid values.
 nsresult
 nsNSSComponent::setEnabledTLSVersions()
 {
   // keep these values in sync with security-prefs.js
@@ -1365,17 +1372,18 @@ nsNSSComponent::Observe(nsISupports* aSu
                                                 ALPN_ENABLED_DEFAULT));
     } else if (prefName.Equals("security.ssl.disable_session_identifiers")) {
       ConfigureTLSSessionIdentifiers();
     } else if (prefName.EqualsLiteral("security.OCSP.enabled") ||
                prefName.EqualsLiteral("security.OCSP.require") ||
                prefName.EqualsLiteral("security.OCSP.GET.enabled") ||
                prefName.EqualsLiteral("security.pki.cert_short_lifetime_in_days") ||
                prefName.EqualsLiteral("security.ssl.enable_ocsp_stapling") ||
-               prefName.EqualsLiteral("security.cert_pinning.enforcement_level")) {
+               prefName.EqualsLiteral("security.cert_pinning.enforcement_level") ||
+               prefName.EqualsLiteral("security.pki.sha1_enforcement_level")) {
       MutexAutoLock lock(mutex);
       setValidationOptions(false, lock);
     } else {
       clearSessionCache = false;
     }
     if (clearSessionCache)
       SSL_ClearSessionCache();
   }
--- a/security/manager/ssl/tests/unit/head_psm.js
+++ b/security/manager/ssl/tests/unit/head_psm.js
@@ -150,16 +150,30 @@ function setCertTrust(cert, trustString)
 function getXPCOMStatusFromNSS(statusNSS) {
   let nssErrorsService = Cc["@mozilla.org/nss_errors_service;1"]
                            .getService(Ci.nsINSSErrorsService);
   return nssErrorsService.getXPCOMFromNSSError(statusNSS);
 }
 
 // certdb implements nsIX509CertDB. See nsIX509CertDB.idl for documentation.
 // In particular, hostname is optional.
+function checkCertErrorGenericAtTime(certdb, cert, expectedError, usage, time,
+                                     /*optional*/ hasEVPolicy,
+                                     /*optional*/ hostname) {
+  do_print(`cert cn=${cert.commonName}`);
+  do_print(`cert issuer cn=${cert.issuerCommonName}`);
+  let verifiedChain = {};
+  let error = certdb.verifyCertAtTime(cert, usage, NO_FLAGS, hostname, time,
+                                      verifiedChain, hasEVPolicy || {});
+  Assert.equal(error, expectedError,
+               "Actual and expected error should match");
+}
+
+// certdb implements nsIX509CertDB. See nsIX509CertDB.idl for documentation.
+// In particular, hostname is optional.
 function checkCertErrorGeneric(certdb, cert, expectedError, usage,
                                /*optional*/ hasEVPolicy,
                                /*optional*/ hostname) {
   do_print(`cert cn=${cert.commonName}`);
   do_print(`cert issuer cn=${cert.issuerCommonName}`);
   let verifiedChain = {};
   let error = certdb.verifyCertNow(cert, usage, NO_FLAGS, hostname,
                                    verifiedChain, hasEVPolicy || {});
--- a/security/manager/ssl/tests/unit/moz.build
+++ b/security/manager/ssl/tests/unit/moz.build
@@ -4,16 +4,17 @@
 # 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/.
 
 DIRS += ['tlsserver']
 TEST_DIRS += [
     'test_cert_eku',
     'test_cert_embedded_null',
     'test_cert_keyUsage',
+    'test_cert_sha1',
     'test_cert_signatures',
     'test_cert_trust',
     'test_cert_version',
     'test_ev_certs',
     'test_intermediate_basic_usage_constraints',
     'test_keysize',
     'test_keysize_ev',
     'test_name_constraints',
--- a/security/manager/ssl/tests/unit/pycert.py
+++ b/security/manager/ssl/tests/unit/pycert.py
@@ -11,17 +11,17 @@ signed x509 certificate with the desired
 The input format is as follows:
 
 issuer:<issuer distinguished name specification>
 subject:<subject distinguished name specification>
 [version:{1,2,3,4}]
 [validity:<YYYYMMDD-YYYYMMDD|duration in days>]
 [issuerKey:<key specification>]
 [subjectKey:<key specification>]
-[signature:{sha256WithRSAEncryption,ecdsaWithSHA256}]
+[signature:{sha1WithRSAEncryption,sha256WithRSAEncryption,ecdsaWithSHA256}]
 [extension:<extension name:<extension-specific data>>]
 [...]
 
 Known extensions are:
 basicConstraints:[cA],[pathLenConstraint]
 keyUsage:[digitalSignature,nonRepudiation,keyEncipherment,
           dataEncipherment,keyAgreement,keyCertSign,cRLSign]
 extKeyUsage:[serverAuth,clientAuth,codeSigning,emailProtection
@@ -254,24 +254,32 @@ def stringToDN(string, tag=None):
     name.setComponentByPosition(0, rdns)
     return name
 
 def stringToAlgorithmIdentifier(string):
     """Helper function that converts a description of an algorithm
     to a representation usable by the pyasn1 package"""
     algorithmIdentifier = rfc2459.AlgorithmIdentifier()
     algorithm = None
-    if string == 'sha256WithRSAEncryption':
+    name = None
+    if string == 'sha1WithRSAEncryption':
+        name = 'SHA-1'
+        algorithm = rfc2459.sha1WithRSAEncryption
+    elif string == 'sha256WithRSAEncryption':
+        name = 'SHA-256'
         algorithm = univ.ObjectIdentifier('1.2.840.113549.1.1.11')
     elif string == 'ecdsaWithSHA256':
+        # Note that this value is only used by pykey.py to tell if
+        # ECDSA is allowed.  It does not conform to the pyECC syntax.
+        name = 'SHA-256'
         algorithm = univ.ObjectIdentifier('1.2.840.10045.4.3.2')
     else:
         raise UnknownAlgorithmTypeError(string)
     algorithmIdentifier.setComponentByName('algorithm', algorithm)
-    return algorithmIdentifier
+    return (algorithmIdentifier, name)
 
 def datetimeToTime(dt):
     """Takes a datetime object and returns an rfc2459.Time object with
     that time as its value as a GeneralizedTime"""
     time = rfc2459.Time()
     time.setComponentByName('generalTime', useful.GeneralizedTime(dt.strftime('%Y%m%d%H%M%SZ')))
     return time
 
@@ -532,17 +540,18 @@ class Certificate:
 
     def getNotAfter(self):
         return datetimeToTime(self.notAfter)
 
     def getSubject(self):
         return stringToDN(self.subject)
 
     def toDER(self):
-        signatureOID = self.getSignature()
+        (signatureOID, hashAlg) = self.getSignature()
+
         tbsCertificate = rfc2459.TBSCertificate()
         tbsCertificate.setComponentByName('version', self.getVersion())
         tbsCertificate.setComponentByName('serialNumber', self.getSerialNumber())
         tbsCertificate.setComponentByName('signature', signatureOID)
         tbsCertificate.setComponentByName('issuer', self.getIssuer())
         tbsCertificate.setComponentByName('validity', self.getValidity())
         tbsCertificate.setComponentByName('subject', self.getSubject())
         tbsCertificate.setComponentByName('subjectPublicKeyInfo',
@@ -554,17 +563,18 @@ class Certificate:
             for extension in self.extensions:
                 extensions.setComponentByPosition(count, extension)
                 count += 1
             tbsCertificate.setComponentByName('extensions', extensions)
         certificate = rfc2459.Certificate()
         certificate.setComponentByName('tbsCertificate', tbsCertificate)
         certificate.setComponentByName('signatureAlgorithm', signatureOID)
         tbsDER = encoder.encode(tbsCertificate)
-        certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER))
+
+        certificate.setComponentByName('signatureValue', self.issuerKey.sign(tbsDER, hashAlg))
         return encoder.encode(certificate)
 
     def toPEM(self):
         output = '-----BEGIN CERTIFICATE-----'
         der = self.toDER()
         b64 = base64.b64encode(der)
         while b64:
             output += '\n' + b64[:64]
--- a/security/manager/ssl/tests/unit/pykey.py
+++ b/security/manager/ssl/tests/unit/pykey.py
@@ -61,16 +61,22 @@ class UnknownBaseError(Exception):
 
 class UnknownKeySpecificationError(UnknownBaseError):
     """Helper exception type to handle unknown key specifications."""
 
     def __init__(self, value):
         UnknownBaseError.__init__(self, value)
         self.category = 'key specification'
 
+class ParameterError(UnknownBaseError):
+    """Exception type indicating that the key was misconfigured"""
+
+    def __init__(self, value):
+        UnknownBaseError.__init__(self, value)
+        self.category = 'key parameter'
 
 class RSAPublicKey(univ.Sequence):
     """Helper type for encoding an RSA public key"""
     componentType = namedtype.NamedTypes(
         namedtype.NamedType('N', univ.Integer()),
         namedtype.NamedType('E', univ.Integer()))
 
 
@@ -549,22 +555,22 @@ class RSAKey:
         spki.setComponentByName('algorithm', algorithmIdentifier)
         rsaKey = RSAPublicKey()
         rsaKey.setComponentByName('N', univ.Integer(self.RSA_N))
         rsaKey.setComponentByName('E', univ.Integer(self.RSA_E))
         subjectPublicKey = univ.BitString(byteStringToHexifiedBitString(encoder.encode(rsaKey)))
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
 
-    def sign(self, data):
+    def sign(self, data, digest):
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
         rsaPrivateKey = rsa.PrivateKey(self.RSA_N, self.RSA_E, self.RSA_D, self.RSA_P, self.RSA_Q)
-        signature = rsa.sign(data, rsaPrivateKey, 'SHA-256')
+        signature = rsa.sign(data, rsaPrivateKey, digest)
         return byteStringToHexifiedBitString(signature)
 
 
 ecPublicKey = univ.ObjectIdentifier('1.2.840.10045.2.1')
 secp256k1 = univ.ObjectIdentifier('1.3.132.0.10')
 secp224r1 = univ.ObjectIdentifier('1.3.132.0.33')
 secp256r1 = univ.ObjectIdentifier('1.2.840.10045.3.1.7')
 secp384r1 = univ.ObjectIdentifier('1.3.132.0.34')
@@ -660,20 +666,24 @@ class ECCKey:
         _, _, points = encoding.Decoder(encoded).int(8).int(2).point(2).out()
         # '04' indicates that the points are in uncompressed form.
         hexifiedBitString = "'%s%s%s'H" % ('04', longToEvenLengthHexString(points[0]),
                                            longToEvenLengthHexString(points[1]))
         subjectPublicKey = univ.BitString(hexifiedBitString)
         spki.setComponentByName('subjectPublicKey', subjectPublicKey)
         return spki
 
-    def sign(self, data):
+    def sign(self, data, digest):
         """Returns a hexified bit string representing a
         signature by this key over the specified data.
         Intended for use with pyasn1.type.univ.BitString"""
+        # This should really only be used with SHA-256
+        if digest != "SHA-256":
+            raise ParameterError(digest)
+
         # There is some non-determinism in ECDSA signatures. Work around
         # this by patching ecc.ecdsa.urandom to not be random.
         with mock.patch('ecc.ecdsa.urandom', side_effect=notRandom):
             # For some reason Key.sign returns an encoded point.
             # Decode it so we can encode it as a BITSTRING consisting
             # of a SEQUENCE of two INTEGERs.
             # Also patch in secp256k1 if applicable.
             if self.keyOID == secp256k1:
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1.js
@@ -0,0 +1,89 @@
+// -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
+// 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/.
+
+// Tests the rejection of SHA-1 certificates based on the preference
+// security.pki.sha1_enforcement_level.
+
+"use strict";
+
+do_get_profile(); // must be called before getting nsIX509CertDB
+const certdb = Cc["@mozilla.org/security/x509certdb;1"]
+                 .getService(Ci.nsIX509CertDB);
+
+// We need to test as if we are at a fixed time, so that we are testing the
+// 2016 checks on SHA-1, not the notBefore checks.
+//
+// (new Date("2016-03-01")).getTime() / 1000
+const VALIDATION_TIME = 1456790400;
+
+function certFromFile(certName) {
+  return constructCertFromFile("test_cert_sha1/" + certName + ".pem");
+}
+
+function loadCertWithTrust(certName, trustString) {
+  addCertFromFile(certdb, "test_cert_sha1/" + certName + ".pem", trustString);
+}
+
+function checkEndEntity(cert, expectedResult) {
+  checkCertErrorGenericAtTime(certdb, cert, expectedResult,
+                              certificateUsageSSLServer, VALIDATION_TIME);
+}
+
+function checkIntermediate(cert, expectedResult) {
+  checkCertErrorGenericAtTime(certdb, cert, expectedResult,
+                              certificateUsageSSLCA, VALIDATION_TIME);
+}
+
+function run_test() {
+  loadCertWithTrust("ca", "CTu,,");
+  loadCertWithTrust("int-pre", ",,");
+  loadCertWithTrust("int-post", ",,");
+
+  // Test cases per pref setting
+  //
+  // root  intermed.  end entity
+  // ===========================
+  // root
+  //  |
+  //  +--- pre-2016             <--- (a)
+  //  |       |
+  //  |       +----- pre-2016   <--- (b)
+  //  |       +----- post-2016  <--- (c)
+  //  |
+  //  +--- post-2016            <--- (d)
+  //          |
+  //          +----- post-2016  <--- (e)
+  //
+  // Expected outcomes (accept / reject):
+  //
+  //                     a   b   c   d   e
+  // allowed=0          Acc Acc Acc Acc Acc
+  // forbidden=1        Rej Rej Rej Rej Rej
+  // onlyBefore2016=2   Acc Acc Rej Rej Rej
+
+  // SHA-1 allowed
+  Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 0);
+  checkIntermediate(certFromFile("int-pre"), PRErrorCodeSuccess);
+  checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+  checkEndEntity(certFromFile("ee-post_int-pre"), PRErrorCodeSuccess);
+  checkIntermediate(certFromFile("int-post"), PRErrorCodeSuccess);
+  checkEndEntity(certFromFile("ee-post_int-post"), PRErrorCodeSuccess);
+
+  // SHA-1 forbidden
+  Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 1);
+  checkIntermediate(certFromFile("int-pre"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  checkEndEntity(certFromFile("ee-pre_int-pre"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  checkEndEntity(certFromFile("ee-post_int-pre"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  checkIntermediate(certFromFile("int-post"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  checkEndEntity(certFromFile("ee-post_int-post"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+
+  // SHA-1 allowed only before 2016
+  Services.prefs.setIntPref("security.pki.sha1_enforcement_level", 2);
+  checkIntermediate(certFromFile("int-pre"), PRErrorCodeSuccess);
+  checkEndEntity(certFromFile("ee-pre_int-pre"), PRErrorCodeSuccess);
+  checkEndEntity(certFromFile("ee-post_int-pre"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  checkIntermediate(certFromFile("int-post"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+  checkEndEntity(certFromFile("ee-post_int-post"), SEC_ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED);
+}
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ca.pem.certspec
@@ -0,0 +1,6 @@
+issuer:ca
+subject:ca
+validity:20100101-20500101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-post.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-post
+subject:ee-post
+validity:20160102-20170201
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-post_int-pre.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-pre
+subject:ee-post
+validity:20160101-20170201
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/ee-pre_int-pre.pem.certspec
@@ -0,0 +1,4 @@
+issuer:int-pre
+subject:ee-pre
+validity:20150101-20170201
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/int-post.pem.certspec
@@ -0,0 +1,6 @@
+issuer:ca
+subject:int-post
+validity:20160101-20260101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/int-pre.pem.certspec
@@ -0,0 +1,6 @@
+issuer:ca
+subject:int-pre
+validity:20100101-20200101
+extension:keyUsage:keyCertSign,cRLSign
+extension:basicConstraints:cA,
+signature:sha1WithRSAEncryption
new file mode 100644
--- /dev/null
+++ b/security/manager/ssl/tests/unit/test_cert_sha1/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+test_certificates = (
+    'ca.pem',
+    'int-pre.pem',
+    'ee-pre_int-pre.pem',
+    'ee-post_int-pre.pem',
+    'int-post.pem',
+    'ee-post_int-post.pem',
+)
+
+for test_certificate in test_certificates:
+    input_file = test_certificate + '.certspec'
+    GENERATED_FILES += [test_certificate]
+    props = GENERATED_FILES[test_certificate]
+    props.script = '../pycert.py'
+    props.inputs = [input_file]
+    TEST_HARNESS_FILES.xpcshell.security.manager.ssl.tests.unit.test_cert_sha1 += ['!%s' % test_certificate]
--- a/security/manager/ssl/tests/unit/xpcshell.ini
+++ b/security/manager/ssl/tests/unit/xpcshell.ini
@@ -7,16 +7,17 @@ support-files =
   test_signed_apps/**
   tlsserver/**
   test_cert_signatures/**
   test_certviewer_invalid_oids/**
   test_ev_certs/**
   test_getchain/**
   test_intermediate_basic_usage_constraints/**
   test_name_constraints/**
+  test_cert_sha1/**
   test_cert_trust/**
   test_cert_version/**
   test_cert_eku/**
   test_cert_embedded_null/**
   test_ocsp_url/**
   test_ocsp_fetch_method/**
   test_keysize/**
   test_keysize_ev/**
@@ -102,16 +103,18 @@ tags = addons
 [test_cert_eku-OS_SA.js]
 [test_cert_eku-OS_TS.js]
 [test_cert_eku-SA.js]
 [test_cert_eku-SA_TS.js]
 [test_cert_eku-TS.js]
 
 [test_cert_embedded_null.js]
 
+[test_cert_sha1.js]
+
 [test_pinning.js]
 run-sequentially = hardcoded ports
 # This test can take longer than 300 seconds on B2G emulator debug builds, so
 # give it enough time to finish. See bug 1081128.
 requesttimeoutfactor = 2
 [test_ocsp_url.js]
 # OCSP requests in this test time out on slow B2G Emulator debug builds.
 # See Bug 1147725.
--- a/security/pkix/include/pkix/pkixtypes.h
+++ b/security/pkix/include/pkix/pkixtypes.h
@@ -273,17 +273,18 @@ public:
                     /*optional*/ const Input* aiaExtension) = 0;
 
   // Check that the given digest algorithm is acceptable for use in signatures.
   //
   // Return Success if the algorithm is acceptable,
   // Result::ERROR_CERT_SIGNATURE_ALGORITHM_DISABLED if the algorithm is not
   // acceptable, or another error code if another error occurred.
   virtual Result CheckSignatureDigestAlgorithm(DigestAlgorithm digestAlg,
-                                               EndEntityOrCA endEntityOrCA) = 0;
+                                               EndEntityOrCA endEntityOrCA,
+                                               Time notBefore) = 0;
 
   // Check that the RSA public key size is acceptable.
   //
   // Return Success if the key size is acceptable,
   // Result::ERROR_INADEQUATE_KEY_SIZE if the key size is not acceptable,
   // or another error code if another error occurred.
   virtual Result CheckRSAPublicKeyModulusSizeInBits(
                    EndEntityOrCA endEntityOrCA,
--- a/security/pkix/lib/pkixbuild.cpp
+++ b/security/pkix/lib/pkixbuild.cpp
@@ -221,19 +221,19 @@ PathBuildingStep::Check(Input potentialI
   // responders are allowed to forget about expired certificates, and many OCSP
   // responders return an error when asked for the status of an expired
   // certificate.
   if (deferredSubjectError != Result::ERROR_EXPIRED_CERTIFICATE) {
     CertID certID(subject.GetIssuer(), potentialIssuer.GetSubjectPublicKeyInfo(),
                   subject.GetSerialNumber());
     Time notBefore(Time::uninitialized);
     Time notAfter(Time::uninitialized);
-    // This should never fail. If we're here, we've already checked that the
-    // given time is in the certificate's validity period.
-    rv = CheckValidity(subject.GetValidity(), time, &notBefore, &notAfter);
+    // This should never fail. If we're here, we've already parsed the validity
+    // and checked that the given time is in the certificate's validity period.
+    rv = ParseValidity(subject.GetValidity(), &notBefore, &notAfter);
     if (rv != Success) {
       return rv;
     }
     Duration validityDuration(notAfter, notBefore);
     rv = trustDomain.CheckRevocation(subject.endEntityOrCA, certID, time,
                                      validityDuration, stapledOCSPResponse,
                                      subject.GetAuthorityInfoAccess());
     if (rv != Success) {
--- a/security/pkix/lib/pkixcheck.cpp
+++ b/security/pkix/lib/pkixcheck.cpp
@@ -30,16 +30,17 @@
 namespace mozilla { namespace pkix {
 
 // 4.1.1.2 signatureAlgorithm
 // 4.1.2.3 signature
 
 Result
 CheckSignatureAlgorithm(TrustDomain& trustDomain,
                         EndEntityOrCA endEntityOrCA,
+                        Time notBefore,
                         const der::SignedDataWithSignature& signedData,
                         Input signatureValue)
 {
   // 4.1.1.2. signatureAlgorithm
   der::PublicKeyAlgorithm publicKeyAlg;
   DigestAlgorithm digestAlg;
   Reader signatureAlgorithmReader(signedData.algorithm);
   Result rv = der::SignatureAlgorithmIdentifierValue(signatureAlgorithmReader,
@@ -86,17 +87,18 @@ CheckSignatureAlgorithm(TrustDomain& tru
   // SHA-1 and/or too-small RSA keys. With this in mind, we ask the trust
   // domain early on if it knows it will reject the signature purely based on
   // the digest algorithm and/or the RSA key size (if an RSA signature). This
   // is a good optimization because it completely avoids calling
   // trustDomain.FindIssuers (which may be slow) for such rejected certs, and
   // more generally it short-circuits any path building with them (which, of
   // course, is even slower).
 
-  rv = trustDomain.CheckSignatureDigestAlgorithm(digestAlg, endEntityOrCA);
+  rv = trustDomain.CheckSignatureDigestAlgorithm(digestAlg, endEntityOrCA,
+                                                 notBefore);
   if (rv != Success) {
     return rv;
   }
 
   switch (publicKeyAlg) {
     case der::PublicKeyAlgorithm::RSA_PKCS1:
     {
       // The RSA computation may give a result that requires fewer bytes to
@@ -120,17 +122,17 @@ CheckSignatureAlgorithm(TrustDomain& tru
   }
 
   return Success;
 }
 
 // 4.1.2.5 Validity
 
 Result
-CheckValidity(Input encodedValidity, Time time,
+ParseValidity(Input encodedValidity,
               /*optional out*/ Time* notBeforeOut,
               /*optional out*/ Time* notAfterOut)
 {
   Reader validity(encodedValidity);
   Time notBefore(Time::uninitialized);
   if (der::TimeChoice(validity, notBefore) != Success) {
     return Result::ERROR_INVALID_DER_TIME;
   }
@@ -143,30 +145,37 @@ CheckValidity(Input encodedValidity, Tim
   if (der::End(validity) != Success) {
     return Result::ERROR_INVALID_DER_TIME;
   }
 
   if (notBefore > notAfter) {
     return Result::ERROR_INVALID_DER_TIME;
   }
 
+  if (notBeforeOut) {
+    *notBeforeOut = notBefore;
+  }
+  if (notAfterOut) {
+    *notAfterOut = notAfter;
+  }
+
+  return Success;
+}
+
+Result
+CheckValidity(Time time, Time notBefore, Time notAfter)
+{
   if (time < notBefore) {
     return Result::ERROR_NOT_YET_VALID_CERTIFICATE;
   }
 
   if (time > notAfter) {
     return Result::ERROR_EXPIRED_CERTIFICATE;
   }
 
-  if (notBeforeOut) {
-    *notBeforeOut = notBefore;
-  }
-  if (notAfterOut) {
-    *notAfterOut = notAfter;
-  }
   return Success;
 }
 
 // 4.1.2.7 Subject Public Key Info
 
 Result
 CheckSubjectPublicKeyInfo(Reader& input, TrustDomain& trustDomain,
                           EndEntityOrCA endEntityOrCA)
@@ -839,29 +848,40 @@ CheckIssuerIndependentProperties(TrustDo
   // processing we do on a distrusted cert, in case it is trying to exploit
   // some bug in our processing.
   rv = trustDomain.GetCertTrust(endEntityOrCA, requiredPolicy, cert.GetDER(),
                                 trustLevel);
   if (rv != Success) {
     return rv;
   }
 
+  // IMPORTANT: We parse the validity interval here, so that we can use the
+  // notBefore and notAfter values in checks for things that might be deprecated
+  // over time. However, we must not fail for semantic errors until the end of
+  // this method, in order to preserve error ranking.
+  Time notBefore(Time::uninitialized);
+  Time notAfter(Time::uninitialized);
+  rv = ParseValidity(cert.GetValidity(), &notBefore, &notAfter);
+  if (rv != Success) {
+    return rv;
+  }
+
   if (trustLevel == TrustLevel::TrustAnchor &&
       endEntityOrCA == EndEntityOrCA::MustBeEndEntity &&
       requiredEKUIfPresent == KeyPurposeId::id_kp_OCSPSigning) {
     // OCSP signer certificates can never be trust anchors, especially
     // since we don't support designated OCSP responders. All of the checks
     // below that are dependent on trustLevel rely on this overriding of the
     // trust level for OCSP signers.
     trustLevel = TrustLevel::InheritsTrust;
   }
 
   switch (trustLevel) {
     case TrustLevel::InheritsTrust:
-      rv = CheckSignatureAlgorithm(trustDomain, endEntityOrCA,
+      rv = CheckSignatureAlgorithm(trustDomain, endEntityOrCA, notBefore,
                                    cert.GetSignedData(), cert.GetSignature());
       if (rv != Success) {
         return rv;
       }
       break;
 
     case TrustLevel::TrustAnchor:
       // We don't even bother checking signatureAlgorithm or signature for
@@ -940,21 +960,19 @@ CheckIssuerIndependentProperties(TrustDo
 
   // 4.2.1.13. CRL Distribution Points is not supported, though the
   //           TrustDomain's CheckRevocation method may parse it and process it
   //           on its own.
 
   // 4.2.1.14. Inhibit anyPolicy is implicitly supported; see the documentation
   //           about policy enforcement in pkix.h.
 
-  // IMPORTANT: This check must come after the other checks in order for error
-  // ranking to work correctly.
-  Time notBefore(Time::uninitialized);
-  Time notAfter(Time::uninitialized);
-  rv = CheckValidity(cert.GetValidity(), time, &notBefore, &notAfter);
+  // IMPORTANT: Even though we parse validity above, we wait until this point to
+  // check it, so that error ranking works correctly.
+  rv = CheckValidity(time, notBefore, notAfter);
   if (rv != Success) {
     return rv;
   }
 
   rv = trustDomain.CheckValidityIsAcceptable(notBefore, notAfter, endEntityOrCA,
                                              requiredEKUIfPresent);
   if (rv != Success) {
     return rv;
--- a/security/pkix/lib/pkixcheck.h
+++ b/security/pkix/lib/pkixcheck.h
@@ -40,15 +40,21 @@ Result CheckIssuerIndependentProperties(
           const CertPolicyId& requiredPolicy,
           unsigned int subCACount,
           /*out*/ TrustLevel& trustLevel);
 
 Result CheckNameConstraints(Input encodedNameConstraints,
                             const BackCert& firstChild,
                             KeyPurposeId requiredEKUIfPresent);
 
-Result CheckValidity(Input encodedValidity, Time time,
+// ParseValidity and CheckValidity are usually used together.  First you parse
+// the dates from the DER Validity sequence, then you compare them to the time
+// at which you are validating.  They are separate so that the notBefore and
+// notAfter times can be used for other things before they are checked against
+// the time of validation.
+Result ParseValidity(Input encodedValidity,
                      /*optional out*/ Time* notBeforeOut = nullptr,
                      /*optional out*/ Time* notAfterOut = nullptr);
+Result CheckValidity(Time time, Time notBefore, Time notAfter);
 
 } } // namespace mozilla::pkix
 
 #endif // mozilla_pkix_pkixcheck_h
--- a/security/pkix/test/gtest/moz.build
+++ b/security/pkix/test/gtest/moz.build
@@ -6,16 +6,17 @@
 
 SOURCES += [
     'pkixbuild_tests.cpp',
     'pkixcert_extension_tests.cpp',
     'pkixcert_signature_algorithm_tests.cpp',
     'pkixcheck_CheckKeyUsage_tests.cpp',
     'pkixcheck_CheckSignatureAlgorithm_tests.cpp',
     'pkixcheck_CheckValidity_tests.cpp',
+    'pkixcheck_ParseValidity_tests.cpp',
 
     # The naming conventions are described in ./README.txt.
 
     'pkixder_input_tests.cpp',
     'pkixder_pki_types_tests.cpp',
     'pkixder_universal_types_tests.cpp',
     'pkixgtest.cpp',
     'pkixnames_tests.cpp',
--- a/security/pkix/test/gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp
+++ b/security/pkix/test/gtest/pkixcheck_CheckSignatureAlgorithm_tests.cpp
@@ -27,16 +27,17 @@
 
 using namespace mozilla::pkix;
 using namespace mozilla::pkix::test;
 
 namespace mozilla { namespace pkix {
 
 extern Result CheckSignatureAlgorithm(
                 TrustDomain& trustDomain, EndEntityOrCA endEntityOrCA,
+                Time notBefore,
                 const der::SignedDataWithSignature& signedData,
                 Input signatureValue);
 
 } } // namespace mozilla::pkix
 
 struct CheckSignatureAlgorithmTestParams
 {
   ByteString signatureAlgorithmValue;
@@ -198,17 +199,18 @@ public:
   explicit pkixcheck_CheckSignatureAlgorithm_TrustDomain(
              unsigned int publicKeySizeInBits)
     : publicKeySizeInBits(publicKeySizeInBits)
     , checkedDigestAlgorithm(false)
     , checkedModulusSizeInBits(false)
   {
   }
 
-  Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA) override
+  Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time)
+    override
   {
     checkedDigestAlgorithm = true;
     return Success;
   }
 
   Result CheckRSAPublicKeyModulusSizeInBits(EndEntityOrCA endEntityOrCA,
                                             unsigned int modulusSizeInBits)
     override
@@ -221,16 +223,17 @@ public:
 
   const unsigned int publicKeySizeInBits;
   bool checkedDigestAlgorithm;
   bool checkedModulusSizeInBits;
 };
 
 TEST_P(pkixcheck_CheckSignatureAlgorithm, CheckSignatureAlgorithm)
 {
+  const Time now(Now());
   const CheckSignatureAlgorithmTestParams& params(GetParam());
 
   Input signatureValueInput;
   ASSERT_EQ(Success,
             signatureValueInput.Init(params.signatureValue.data(),
                                      params.signatureValue.length()));
 
   pkixcheck_CheckSignatureAlgorithm_TrustDomain
@@ -243,17 +246,17 @@ TEST_P(pkixcheck_CheckSignatureAlgorithm
 
   ByteString dummySignature(params.signatureLengthInBytes, 0xDE);
   ASSERT_EQ(Success,
             signedData.signature.Init(dummySignature.data(),
                                       dummySignature.length()));
 
   ASSERT_EQ(params.expectedResult,
             CheckSignatureAlgorithm(trustDomain, EndEntityOrCA::MustBeEndEntity,
-                                    signedData, signatureValueInput));
+                                    now, signedData, signatureValueInput));
   ASSERT_EQ(params.expectedResult == Success,
             trustDomain.checkedDigestAlgorithm);
   ASSERT_EQ(params.expectedResult == Success,
             trustDomain.checkedModulusSizeInBits);
 }
 
 INSTANTIATE_TEST_CASE_P(
   pkixcheck_CheckSignatureAlgorithm, pkixcheck_CheckSignatureAlgorithm,
--- a/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
+++ b/security/pkix/test/gtest/pkixcheck_CheckValidity_tests.cpp
@@ -51,101 +51,77 @@ static const Time NOW(YMDHMS(2016, 12, 3
   0x17, 13,                               /* tag, length */ \
   '2', '1', '0', '1', '0', '1',           /* 2021-01-01 */ \
   '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
 
 static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
 
 class pkixcheck_CheckValidity : public ::testing::Test { };
 
-TEST_F(pkixcheck_CheckValidity, BothEmptyNull)
-{
-  static const uint8_t DER[] = {
-    0x17/*UTCTime*/, 0/*length*/,
-    0x17/*UTCTime*/, 0/*length*/,
-  };
-  static const Input validity(DER);
-  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckValidity, NotBeforeEmptyNull)
-{
-  static const uint8_t DER[] = {
-    0x17/*UTCTime*/, 0x00/*length*/,
-    NEWER_UTCTIME
-  };
-  static const Input validity(DER);
-  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW));
-}
-
-TEST_F(pkixcheck_CheckValidity, NotAfterEmptyNull)
-{
-  static const uint8_t DER[] = {
-    NEWER_UTCTIME,
-    0x17/*UTCTime*/, 0x00/*length*/,
-  };
-  static const Input validity(DER);
-  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW));
-}
-
 static const uint8_t OLDER_UTCTIME_NEWER_UTCTIME_DATA[] = {
   OLDER_UTCTIME,
   NEWER_UTCTIME,
 };
 static const Input
 OLDER_UTCTIME_NEWER_UTCTIME(OLDER_UTCTIME_NEWER_UTCTIME_DATA);
 
 TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_UTCTIME)
 {
-  ASSERT_EQ(Success, CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, NOW));
+  static Time notBefore(Time::uninitialized);
+  static Time notAfter(Time::uninitialized);
+  ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
+  ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
 }
 
 TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_GENERALIZEDTIME)
 {
   static const uint8_t DER[] = {
     OLDER_GENERALIZEDTIME,
     NEWER_GENERALIZEDTIME,
   };
   static const Input validity(DER);
-  ASSERT_EQ(Success, CheckValidity(validity, NOW));
+  static Time notBefore(Time::uninitialized);
+  static Time notAfter(Time::uninitialized);
+  ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
+  ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
 }
 
 TEST_F(pkixcheck_CheckValidity, Valid_GENERALIZEDTIME_UTCTIME)
 {
   static const uint8_t DER[] = {
     OLDER_GENERALIZEDTIME,
     NEWER_UTCTIME,
   };
   static const Input validity(DER);
-  ASSERT_EQ(Success, CheckValidity(validity, NOW));
+  static Time notBefore(Time::uninitialized);
+  static Time notAfter(Time::uninitialized);
+  ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
+  ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
 }
 
 TEST_F(pkixcheck_CheckValidity, Valid_UTCTIME_GENERALIZEDTIME)
 {
   static const uint8_t DER[] = {
     OLDER_UTCTIME,
     NEWER_GENERALIZEDTIME,
   };
   static const Input validity(DER);
-  ASSERT_EQ(Success, CheckValidity(validity, NOW));
+  static Time notBefore(Time::uninitialized);
+  static Time notAfter(Time::uninitialized);
+  ASSERT_EQ(Success, ParseValidity(validity, &notBefore, &notAfter));
+  ASSERT_EQ(Success, CheckValidity(NOW, notBefore, notAfter));
 }
 
 TEST_F(pkixcheck_CheckValidity, InvalidBeforeNotBefore)
 {
-  ASSERT_EQ(Result::ERROR_NOT_YET_VALID_CERTIFICATE,
-            CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, PAST_TIME));
+  static Time notBefore(Time::uninitialized);
+  static Time notAfter(Time::uninitialized);
+  ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
+  ASSERT_EQ(Result::ERROR_NOT_YET_VALID_CERTIFICATE, CheckValidity(PAST_TIME, notBefore, notAfter));
 }
 
 TEST_F(pkixcheck_CheckValidity, InvalidAfterNotAfter)
 {
-  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE,
-            CheckValidity(OLDER_UTCTIME_NEWER_UTCTIME, FUTURE_TIME));
+  static Time notBefore(Time::uninitialized);
+  static Time notAfter(Time::uninitialized);
+  ASSERT_EQ(Success, ParseValidity(OLDER_UTCTIME_NEWER_UTCTIME, &notBefore, &notAfter));
+  ASSERT_EQ(Result::ERROR_EXPIRED_CERTIFICATE, CheckValidity(FUTURE_TIME, notBefore, notAfter));
 }
-
-TEST_F(pkixcheck_CheckValidity, InvalidNotAfterBeforeNotBefore)
-{
-  static const uint8_t DER[] = {
-    NEWER_UTCTIME,
-    OLDER_UTCTIME,
-  };
-  static const Input validity(DER);
-  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, CheckValidity(validity, NOW));
-}
new file mode 100644
--- /dev/null
+++ b/security/pkix/test/gtest/pkixcheck_ParseValidity_tests.cpp
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This code is made available to you under your choice of the following sets
+ * of licensing terms:
+ */
+/* 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/.
+ */
+/* Copyright 2014 Mozilla Contributors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "pkixcheck.h"
+#include "pkixgtest.h"
+
+using namespace mozilla::pkix;
+using namespace mozilla::pkix::test;
+
+#define OLDER_UTCTIME \
+  0x17, 13,                               /* tag, length */ \
+  '9', '9', '0', '1', '0', '1',           /* (19)99-01-01 */ \
+  '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
+
+#define NEWER_UTCTIME \
+  0x17, 13,                               /* tag, length */ \
+  '2', '1', '0', '1', '0', '1',           /* 2021-01-01 */ \
+  '0', '0', '0', '0', '0', '0', 'Z'       /* 00:00:00Z */
+
+static const Time FUTURE_TIME(YMDHMS(2025, 12, 31, 12, 23, 56));
+
+class pkixcheck_ParseValidity : public ::testing::Test { };
+
+TEST_F(pkixcheck_ParseValidity, BothEmptyNull)
+{
+  static const uint8_t DER[] = {
+    0x17/*UTCTime*/, 0/*length*/,
+    0x17/*UTCTime*/, 0/*length*/,
+  };
+  static const Input validity(DER);
+  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
+}
+
+TEST_F(pkixcheck_ParseValidity, NotBeforeEmptyNull)
+{
+  static const uint8_t DER[] = {
+    0x17/*UTCTime*/, 0x00/*length*/,
+    NEWER_UTCTIME
+  };
+  static const Input validity(DER);
+  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
+}
+
+TEST_F(pkixcheck_ParseValidity, NotAfterEmptyNull)
+{
+  static const uint8_t DER[] = {
+    NEWER_UTCTIME,
+    0x17/*UTCTime*/, 0x00/*length*/,
+  };
+  static const Input validity(DER);
+  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
+}
+
+TEST_F(pkixcheck_ParseValidity, InvalidNotAfterBeforeNotBefore)
+{
+  static const uint8_t DER[] = {
+    NEWER_UTCTIME,
+    OLDER_UTCTIME,
+  };
+  static const Input validity(DER);
+  ASSERT_EQ(Result::ERROR_INVALID_DER_TIME, ParseValidity(validity));
+}
--- a/security/pkix/test/gtest/pkixgtest.h
+++ b/security/pkix/test/gtest/pkixgtest.h
@@ -121,17 +121,18 @@ public:
   Result DigestBuf(Input, DigestAlgorithm, /*out*/ uint8_t*, size_t) override
   {
     ADD_FAILURE();
     return NotReached("DigestBuf should not be called",
                       Result::FATAL_ERROR_LIBRARY_FAILURE);
   }
 
   Result CheckSignatureDigestAlgorithm(DigestAlgorithm,
-                                       EndEntityOrCA) override
+                                       EndEntityOrCA,
+                                       Time) override
   {
     ADD_FAILURE();
     return NotReached("CheckSignatureDigestAlgorithm should not be called",
                       Result::FATAL_ERROR_LIBRARY_FAILURE);
   }
 
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
@@ -174,17 +175,18 @@ public:
 class DefaultCryptoTrustDomain : public EverythingFailsByDefaultTrustDomain
 {
   Result DigestBuf(Input item, DigestAlgorithm digestAlg,
                    /*out*/ uint8_t* digestBuf, size_t digestBufLen) override
   {
     return TestDigestBuf(item, digestAlg, digestBuf, digestBufLen);
   }
 
-  Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA) override
+  Result CheckSignatureDigestAlgorithm(DigestAlgorithm, EndEntityOrCA, Time)
+                                       override
   {
     return Success;
   }
 
   Result CheckECDSACurveIsAcceptable(EndEntityOrCA, NamedCurve) override
   {
     return Success;
   }
deleted file mode 100644
--- a/testing/marionette/update-smoketests/flash-template.sh
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/bash
-# 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/.
-#
-# This script is run by the update smoketest frontend
-
-ADB=${ADB:-adb}
-DEVICE=$1
-
-run_adb() {
-    $ADB -s $DEVICE $@
-}
-
-run_adb push %(flash_zip)s %(sdcard)s/_flash.zip
-run_adb shell 'echo -n "--update_package=%(sdcard_recovery)s/_flash.zip" > /cache/recovery/command'
-run_adb reboot recovery
deleted file mode 100755
--- a/testing/marionette/update-smoketests/run-smoketests.py
+++ /dev/null
@@ -1,56 +0,0 @@
-#!/usr/bin/env python
-#
-# 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/.
-
-import argparse
-import os
-import sys
-import tempfile
-
-from smoketest import *
-
-def main():
-    parser = argparse.ArgumentParser(prog="run-smoketests.py")
-    parser.add_argument("--build-dir", required=True,
-        help="directory that contains builds with build ID subdirectories")
-    parser.add_argument("--run-dir", default=None,
-        help="directory where partials and testvars are generated. default: "
-             "create a temp directory")
-    parser.add_argument("--tests", action='append',
-        help="which smoketest(s) to run. by default all tests are run")
-    parser.add_argument("build_ids", nargs="+", metavar="BUILD_ID",
-        help="a list of build IDs to run smoketests against. the IDs will be "
-             "sorted numerically, and partials will be generated from each to "
-             "the last update. this list of partials will be tested along with "
-             "a full update from each build to the last")
-    args = parser.parse_args()
-
-    try:
-        b2g = find_b2g()
-    except EnvironmentError, e:
-        parser.exit(1, "This tool must be run while inside the B2G directory, "
-                       "or B2G_HOME must be set in the environment.")
-
-    if not os.path.exists(args.build_dir):
-        parser.exit(1, "Build dir doesn't exist: %s" % args.build_dir)
-
-    if len(args.build_ids) < 2:
-        parser.exit(1, "This script requires at least two build IDs")
-
-    for test in args.tests:
-        if not os.path.exists(test):
-            parser.exit(1, "Smoketest does not exist: %s" % test)
-
-    try:
-        config = SmokeTestConfig(args.build_dir)
-        runner = SmokeTestRunner(config, b2g, run_dir=args.run_dir)
-        runner.run_smoketests(args.build_ids, args.tests)
-    except KeyError, e:
-        parser.exit(1, "Error: %s" % e)
-    except SmokeTestError, e:
-        parser.exit(1, "Error: %s" % e)
-
-if __name__ == "__main__":
-    main()
deleted file mode 100644
--- a/testing/marionette/update-smoketests/smoketest.py
+++ /dev/null
@@ -1,195 +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/.
-
-import json
-import os
-import subprocess
-import sys
-import tempfile
-import threading
-import zipfile
-
-from ConfigParser import ConfigParser
-
-this_dir = os.path.abspath(os.path.dirname(__file__))
-marionette_dir = os.path.dirname(this_dir)
-marionette_client_dir = os.path.join(marionette_dir, 'client', 'marionette')
-
-def find_b2g():
-    sys.path.append(marionette_client_dir)
-    from b2gbuild import B2GBuild
-    return B2GBuild()
-
-class DictObject(dict):
-    def __getattr__(self, item):
-        try:
-            return self.__getitem__(item)
-        except KeyError:
-            raise AttributeError(item)
-
-    def __getitem__(self, item):
-        value = dict.__getitem__(self, item)
-        if isinstance(value, dict):
-            return DictObject(value)
-        return value
-
-class SmokeTestError(Exception):
-    pass
-
-class SmokeTestConfigError(SmokeTestError):
-    def __init__(self, message):
-        SmokeTestError.__init__(self, 'smoketest-config.json: ' + message)
-
-class SmokeTestConfig(DictObject):
-    TOP_LEVEL_REQUIRED = ('devices', 'public_key', 'private_key')
-    DEVICE_REQUIRED    = ('system_fs_type', 'system_location', 'data_fs_type',
-                          'data_location', 'sdcard', 'sdcard_recovery',
-                          'serials')
-
-    def __init__(self, build_dir):
-        self.top_dir = build_dir
-        self.build_data = {}
-        self.flash_template = None
-
-        with open(os.path.join(build_dir, 'smoketest-config.json')) as f:
-            DictObject.__init__(self, json.loads(f.read()))
-
-        for required in self.TOP_LEVEL_REQUIRED:
-            if required not in self:
-                raise SmokeTestConfigError('No "%s" found' % required)
-
-        if len(self.devices) == 0:
-            raise SmokeTestConfigError('No devices found')
-
-        for name, device in self.devices.iteritems():
-            for required in self.DEVICE_REQUIRED:
-                if required not in device:
-                    raise SmokeTestConfigError('No "%s" found in device "%s"' % (required, name))
-
-    def get_build_data(self, device, build_id):
-        if device in self.build_data:
-            if build_id in self.build_data[device]:
-                return self.build_data[device][build_id]
-        else:
-            self.build_data[device] = {}
-
-        build_dir = os.path.join(self.top_dir, device, build_id)
-        flash_zip = os.path.join(build_dir, 'flash.zip')
-        with zipfile.ZipFile(flash_zip) as zip:
-            app_ini = ConfigParser()
-            app_ini.readfp(zip.open('system/b2g/application.ini'))
-            platform_ini = ConfigParser()
-            platform_ini.readfp(zip.open('system/b2g/platform.ini'))
-
-        build_data = self.build_data[device][build_id] = DictObject({
-            'app_version': app_ini.get('App', 'version'),
-            'app_build_id': app_ini.get('App', 'buildid'),
-            'platform_build_id': platform_ini.get('Build', 'buildid'),
-            'platform_milestone': platform_ini.get('Build', 'milestone'),
-            'complete_mar': os.path.join(build_dir, 'complete.mar'),
-            'flash_script': os.path.join(build_dir, 'flash.sh')
-        })
-
-        return build_data
-
-class SmokeTestRunner(object):
-    DEVICE_TIMEOUT = 30
-
-    def __init__(self, config, b2g, run_dir=None):
-        self.config = config
-        self.b2g = b2g
-        self.run_dir = run_dir or tempfile.mkdtemp()
-
-        update_tools = self.b2g.import_update_tools()
-        self.b2g_config = update_tools.B2GConfig()
-
-    def run_b2g_update_test(self, serial, testvars, tests):
-        b2g_update_test = os.path.join(marionette_client_dir,
-                                       'venv_b2g_update_test.sh')
-
-        if not tests:
-            tests = [os.path.join(marionette_client_dir, 'tests',
-                                  'update-tests.ini')]
-
-        args = ['bash', b2g_update_test, sys.executable,
-                '--homedir', self.b2g.homedir,
-                '--address', 'localhost:2828',
-                '--type', 'b2g+smoketest',
-                '--device', serial,
-                '--testvars', testvars]
-        args.extend(tests)
-
-        print ' '.join(args)
-        subprocess.check_call(args)
-
-    def build_testvars(self, device, start_id, finish_id):
-        run_dir = os.path.join(self.run_dir, device, start_id, finish_id)
-        if not os.path.exists(run_dir):
-            os.makedirs(run_dir)
-
-        start_data = self.config.get_build_data(device, start_id)
-        finish_data = self.config.get_build_data(device, finish_id)
-
-        partial_mar = os.path.join(run_dir, 'partial.mar')
-        if not os.path.exists(partial_mar):
-            build_gecko_mar = os.path.join(self.b2g.update_tools,
-                                           'build-gecko-mar.py')
-            subprocess.check_call([sys.executable, build_gecko_mar,
-                                   '--from', start_data.complete_mar,
-                                   '--to', finish_data.complete_mar,
-                                   partial_mar])
-        finish_data['partial_mar'] = partial_mar
-
-        testvars = os.path.join(run_dir, 'testvars.json')
-        if not os.path.exists(testvars):
-            open(testvars, 'w').write(json.dumps({
-                'start': start_data,
-                'finish': finish_data
-            }))
-
-        return testvars
-
-    def wait_for_device(self, device):
-        for serial in self.config.devices[device].serials:
-            proc = subprocess.Popen([self.b2g.adb_path, '-s', serial,
-                                     'wait-for-device'])
-            def wait_for_adb():
-                proc.communicate()
-
-            thread = threading.Thread(target=wait_for_adb)
-            thread.start()
-            thread.join(self.DEVICE_TIMEOUT)
-
-            if thread.isAlive():
-                print >>sys.stderr, '%s device %s is not recognized by ADB, ' \
-                                    'trying next device' % (device, serial)
-                proc.kill()
-                thread.join()
-                continue
-
-            return serial
-        return None
-
-    def run_smoketests_for_device(self, device, start_id, finish_id, tests):
-        testvars = self.build_testvars(device, start_id, finish_id)
-        serial = self.wait_for_device(device)
-        if not serial:
-            raise SmokeTestError('No connected serials for device "%s" could ' \
-                                 'be found' % device)
-
-        try:
-            self.run_b2g_update_test(serial, testvars, tests)
-        except subprocess.CalledProcessError:
-            print >>sys.stderr, 'SMOKETEST-FAIL | START=%s | FINISH=%s | ' \
-                                'DEVICE=%s/%s | %s' % (start_id, finish_id,
-                                                       device, serial, testvars)
-
-    def run_smoketests(self, build_ids, tests):
-        build_ids.sort()
-
-        latest_build_id = build_ids.pop(-1)
-        for build_id in build_ids:
-            for device in self.config.devices:
-                self.run_smoketests_for_device(device, build_id,
-                                               latest_build_id, tests)
deleted file mode 100755
--- a/testing/marionette/update-smoketests/stage-update.py
+++ /dev/null
@@ -1,95 +0,0 @@
-#!/usr/bin/env python
-#
-# 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/.
-
-import argparse
-import json
-import os
-import shutil
-import stat
-import subprocess
-import sys
-
-from ConfigParser import ConfigParser
-from smoketest import *
-
-this_dir = os.path.abspath(os.path.dirname(__file__))
-
-def stage_update(device, stage_dir):
-    config = SmokeTestConfig(stage_dir)
-    if device not in config.devices:
-        raise SmokeTestConfigError('Device "%s" not found' % device)
-
-    device_data = config.devices[device]
-
-    b2g_config = b2g.import_update_tools().B2GConfig()
-    target_out_dir = os.path.join(b2g.homedir, 'out', 'target', 'product', device)
-    app_ini = os.path.join(b2g_config.gecko_objdir, 'dist', 'bin',
-                           'application.ini')
-    gecko_mar = os.path.join(b2g_config.gecko_objdir, 'dist', 'b2g-update',
-                             'b2g-gecko-update.mar')
-
-    build_data = ConfigParser()
-    build_data.read(app_ini)
-    build_id = build_data.get('App', 'buildid')
-    app_version = build_data.get('App', 'version')
-
-    build_dir = os.path.join(config.top_dir, device, build_id)
-    if not os.path.exists(build_dir):
-        os.makedirs(build_dir)
-
-    if not os.path.exists(build_dir):
-        raise SmokeTestError('Couldn\'t create directory: %s' % build_dir)
-
-    build_flash_fota = os.path.join(b2g.update_tools, 'build-flash-fota.py')
-
-    flash_zip = os.path.join(build_dir, 'flash.zip')
-
-    print 'Building flash zip for device %s, version %s, build %s...' % \
-          (device, app_version, build_id)
-
-    subprocess.check_call([sys.executable, build_flash_fota,
-        '--system-dir', os.path.join(target_out_dir, 'system'),
-        '--system-fs-type', device_data.system_fs_type,
-        '--system-location', device_data.system_location,
-        '--data-fs-type', device_data.data_fs_type,
-        '--data-location', device_data.data_location,
-        '--output', flash_zip])
-
-    complete_mar = os.path.join(build_dir, 'complete.mar')
-    shutil.copy(gecko_mar, complete_mar)
-
-    flash_template = open(os.path.join(this_dir, 'flash-template.sh')).read()
-    flash_script = os.path.join(build_dir, 'flash.sh')
-    open(os.path.join(build_dir, 'flash.sh'), 'w').write(flash_template % {
-        'flash_zip': flash_zip,
-        'sdcard': device_data.sdcard,
-        'sdcard_recovery': device_data.sdcard_recovery
-    })
-    os.chmod(flash_script, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR |
-                           stat.S_IRGRP | stat.S_IXGRP |
-                           stat.S_IROTH | stat.S_IXOTH)
-
-def main():
-    parser = argparse.ArgumentParser(prog='stage-update.py')
-    parser.add_argument('device', help='device name for staging')
-    parser.add_argument('stage_dir',
-        help='directory to stage update and flash script for smoketests')
-    args = parser.parse_args()
-
-    try:
-        global b2g
-        b2g = find_b2g()
-    except EnvironmentError, e:
-        parser.exit(1, 'This tool must be run while inside the B2G directory, '
-                       'or B2G_HOME must be set in the environment.')
-
-    try:
-        stage_update(args.device, args.stage_dir)
-    except SmokeTestError, e:
-        parser.exit(1, str(e))
-
-if __name__ == '__main__':
-    main()
--- a/testing/mozharness/configs/unittests/linux_unittest.py
+++ b/testing/mozharness/configs/unittests/linux_unittest.py
@@ -121,16 +121,17 @@ config = {
                 "%(binary_path)s"
             ],
             "run_filename": "test.py",
             "testsdir": "mozbase"
         },
         "mozmill": {
             "options": [
                 "--binary=%(binary_path)s",
+                "--testing-modules-dir=test/modules",
                 "--symbols-path=%(symbols_path)s"
             ],
             "run_filename": "runtestlist.py",
             "testsdir": "mozmill"
         },
         "reftest": {
             "options": [
                 "--appname=%(binary_path)s",
--- a/testing/mozharness/configs/unittests/mac_unittest.py
+++ b/testing/mozharness/configs/unittests/mac_unittest.py
@@ -87,16 +87,17 @@ config = {
                 "%(binary_path)s"
             ],
             "run_filename": "test.py",
             "testsdir": "mozbase"
         },
         "mozmill": {
             "options": [
                 "--binary=%(binary_path)s",
+                "--testing-modules-dir=test/modules",
                 "--symbols-path=%(symbols_path)s"
             ],
             "run_filename": "runtestlist.py",
             "testsdir": "mozmill"
         },
         "reftest": {
             "options": [
                 "--appname=%(binary_path)s",
--- a/testing/mozharness/configs/unittests/win_unittest.py
+++ b/testing/mozharness/configs/unittests/win_unittest.py
@@ -96,16 +96,17 @@ config = {
                 "%(binary_path)s"
             ],
             "run_filename": "test.py",
             "testsdir": "mozbase"
         },
         "mozmill": {
             "options": [
                 "--binary=%(binary_path)s",
+                "--testing-modules-dir=test/modules",
                 "--symbols-path=%(symbols_path)s"
             ],
             "run_filename": "runtestlist.py",
             "testsdir": "mozmill"
         },
         "reftest": {
             "options": [
                 "--appname=%(binary_path)s",
--- a/testing/talos/talos/talos_process.py
+++ b/testing/talos/talos/talos_process.py
@@ -102,19 +102,20 @@ def run_browser(command, timeout=None, o
                 logging.info(
                     "Browser shutdown timed out after {0} seconds, terminating"
                     " process.".format(wait_for_quit_timeout)
                 )
     finally:
         # this also handle KeyboardInterrupt
         # ensure early the process is really terminated
         context.kill_process()
+        return_code = proc.wait(1)
 
     reader.output.append(
         "__startBeforeLaunchTimestamp%d__endBeforeLaunchTimestamp"
         % first_time)
     reader.output.append(
         "__startAfterTerminationTimestamp%d__endAfterTerminationTimestamp"
         % (int(time.time()) * 1000))
 
-    logging.info("Browser exited with error code: {0}".format(proc.returncode))
+    logging.info("Browser exited with error code: {0}".format(return_code))
     context.output = reader.output
     return context
--- a/toolkit/components/osfile/NativeOSFileInternals.cpp
+++ b/toolkit/components/osfile/NativeOSFileInternals.cpp
@@ -86,17 +86,17 @@ struct ScopedArrayBufferContentsTraits {
   }
   const static void release(type ptr) {
     js_free(ptr.data);
     ptr.data = nullptr;
     ptr.nbytes = 0;
   }
 };
 
-struct ScopedArrayBufferContents: public Scoped<ScopedArrayBufferContentsTraits> {
+struct MOZ_NON_TEMPORARY_CLASS ScopedArrayBufferContents: public Scoped<ScopedArrayBufferContentsTraits> {
   explicit ScopedArrayBufferContents(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM):
     Scoped<ScopedArrayBufferContentsTraits>(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_TO_PARENT)
   { }
   explicit ScopedArrayBufferContents(const ArrayBufferContents& v
                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM):
     Scoped<ScopedArrayBufferContentsTraits>(v MOZ_GUARD_OBJECT_NOTIFIER_PARAM_TO_PARENT)
   { }
   ScopedArrayBufferContents& operator=(ArrayBufferContents ptr) {
--- a/toolkit/components/telemetry/Telemetry.h
+++ b/toolkit/components/telemetry/Telemetry.h
@@ -113,17 +113,17 @@ struct AccumulateDelta_impl<Microsecond>
   }
   static void compute(ID id, const nsCString& key, TimeStamp start, TimeStamp end = TimeStamp::Now()) {
     Accumulate(id, key, static_cast<uint32_t>((end - start).ToMicroseconds()));
   }
 };
 
 
 template<ID id, TimerResolution res = Millisecond>
-class AutoTimer {
+class MOZ_RAII AutoTimer {
 public:
   explicit AutoTimer(TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
      : start(aStart)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   }
 
   explicit AutoTimer(const nsCString& aKey, TimeStamp aStart = TimeStamp::Now() MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
@@ -143,17 +143,17 @@ public:
 
 private:
   const TimeStamp start;
   const nsCString key;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 template<ID id>
-class AutoCounter {
+class MOZ_RAII AutoCounter {
 public:
   explicit AutoCounter(uint32_t counterStart = 0 MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : counter(counterStart)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
   }
 
   ~AutoCounter() {
--- a/toolkit/devtools/heapsnapshot/AutoMemMap.h
+++ b/toolkit/devtools/heapsnapshot/AutoMemMap.h
@@ -23,17 +23,17 @@ namespace devtools {
 //       if (NS_FAILED(mm.init("/path/to/desired/file"))) {
 //          // Handle the error however you see fit.
 //         return false;
 //       }
 //
 //       doStuffWithMappedMemory(mm.address());
 //     }
 //     // The memory is automatically unmapped when the AutoMemMap leaves scope.
-class MOZ_STACK_CLASS AutoMemMap
+class MOZ_RAII AutoMemMap
 {
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER;
 
   PRFileInfo64 fileInfo;
   PRFileDesc*  fd;
   PRFileMap*   fileMap;
   void*        addr;
 
--- a/tools/profiler/public/GeckoProfilerImpl.h
+++ b/tools/profiler/public/GeckoProfilerImpl.h
@@ -372,17 +372,17 @@ static inline void profiler_tracing(cons
 #else
 #define PROFILE_DEFAULT_INTERVAL 1
 #endif
 #define PROFILE_DEFAULT_FEATURES NULL
 #define PROFILE_DEFAULT_FEATURE_COUNT 0
 
 namespace mozilla {
 
-class MOZ_STACK_CLASS GeckoProfilerTracingRAII {
+class MOZ_RAII GeckoProfilerTracingRAII {
 public:
   GeckoProfilerTracingRAII(const char* aCategory, const char* aInfo,
                            mozilla::UniquePtr<ProfilerBacktrace> aBacktrace
                            MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mCategory(aCategory)
     , mInfo(aInfo)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -419,8 +419,13 @@ NS_EVENT_MESSAGE(eGamepadAxisMove,      
 NS_EVENT_MESSAGE(eGamepadConnected,      eGamepadEventFirst + 3)
 NS_EVENT_MESSAGE(eGamepadDisconnected,   eGamepadEventFirst + 4)
 NS_EVENT_MESSAGE(eGamepadEventLast,      eGamepadDisconnected)
 #endif
 
 // input and beforeinput events.
 NS_EVENT_MESSAGE(NS_EDITOR_EVENT_START,   6100)
 NS_EVENT_MESSAGE(NS_EDITOR_INPUT,         NS_EDITOR_EVENT_START)
+
+// selection events
+NS_EVENT_MESSAGE(NS_SELECT_EVENT_START,   6200)
+NS_EVENT_MESSAGE(NS_SELECT_START,         NS_SELECT_EVENT_START)
+NS_EVENT_MESSAGE(NS_SELECTION_CHANGE,     NS_SELECT_EVENT_START + 1)
--- a/xpcom/glue/AutoRestore.h
+++ b/xpcom/glue/AutoRestore.h
@@ -22,17 +22,17 @@ namespace mozilla {
  *     mIsPainting = true;
  *
  *     // ... your code here ...
  *
  *     // mIsPainting is reset to its old value at the end of this block
  *   }
  */
 template<class T>
-class MOZ_STACK_CLASS AutoRestore
+class MOZ_RAII AutoRestore
 {
 private:
   T& mLocation;
   T mValue;
   MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 public:
   explicit AutoRestore(T& aValue MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mLocation(aValue)
--- a/xpcom/glue/Mutex.h
+++ b/xpcom/glue/Mutex.h
@@ -140,17 +140,17 @@ private:
 /**
  * MutexAutoLock
  * Acquires the Mutex when it enters scope, and releases it when it leaves
  * scope.
  *
  * MUCH PREFERRED to bare calls to Mutex.Lock and Unlock.
  */
 template<typename T>
-class MOZ_STACK_CLASS BaseAutoLock
+class MOZ_RAII BaseAutoLock
 {
 public:
   /**
    * Constructor
    * The constructor aquires the given lock.  The destructor
    * releases the lock.
    *
    * @param aLock A valid mozilla::Mutex* returned by
@@ -185,17 +185,17 @@ typedef BaseAutoLock<OffTheBooksMutex> O
 /**
  * MutexAutoUnlock
  * Releases the Mutex when it enters scope, and re-acquires it when it leaves
  * scope.
  *
  * MUCH PREFERRED to bare calls to Mutex.Unlock and Lock.
  */
 template<typename T>
-class MOZ_STACK_CLASS BaseAutoUnlock
+class MOZ_RAII BaseAutoUnlock
 {
 public:
   explicit BaseAutoUnlock(T& aLock MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
     : mLock(&aLock)
   {
     MOZ_GUARD_OBJECT_NOTIFIER_INIT;
     NS_ASSERTION(mLock, "null lock");
     mLock->Unlock();