Merge inbound to m-c. a=merge
authorRyan VanderMeulen <ryanvm@gmail.com>
Thu, 16 Apr 2015 16:18:13 -0400
changeset 239524 51e3cb11a258b9f22dfcdbf89ac99d1c56a1125e
parent 239479 701c4a82fc5610ccc77c7dea483aaefd9dec106e (current diff)
parent 239523 1b6352f8ae6dccab8f43df682d325ce176b03f36 (diff)
child 239525 725af2a560d806778b8b1e535bba3e23d1d99f31
child 239542 9f6bc0ab8205667e097bb7f8c42c51d5ac69606d
child 239580 7d8051a8dd014f26df66856ea1c19e961499c44c
child 239665 c75413a564b27ca2f641d8aacd4e535d8f5b3120
push id28600
push userryanvm@gmail.com
push dateThu, 16 Apr 2015 20:25:17 +0000
treeherdermozilla-central@51e3cb11a258 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone40.0a1
first release with
nightly linux32
51e3cb11a258 / 40.0a1 / 20150417030200 / files
nightly linux64
51e3cb11a258 / 40.0a1 / 20150417030200 / files
nightly mac
51e3cb11a258 / 40.0a1 / 20150417030200 / files
nightly win32
51e3cb11a258 / 40.0a1 / 20150417030200 / files
nightly win64
51e3cb11a258 / 40.0a1 / 20150417030200 / 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 inbound to m-c. a=merge
js/src/jit-test/tests/closures/bug540136.js
js/src/jit-test/tests/closures/bug540348.js
js/src/jit-test/tests/jaeger/bug583684.js
js/src/jit-test/tests/jaeger/bug719758.js
--- a/browser/base/content/test/popupNotifications/browser.ini
+++ b/browser/base/content/test/popupNotifications/browser.ini
@@ -1,12 +1,12 @@
 [DEFAULT]
 support-files =
   head.js
 
 [browser_popupNotification.js]
-skip-if = (os == "linux" && debug) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
+skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
 [browser_popupNotification_2.js]
-skip-if = (os == "linux" && debug) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
+skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
 [browser_popupNotification_3.js]
-skip-if = (os == "linux" && debug) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
+skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
 [browser_popupNotification_4.js]
-skip-if = (os == "linux" && debug) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
+skip-if = (os == "linux" && (debug || asan)) || e10s # e10s - Bug ?????? - popup notification test probably confused re content process notifications etc
--- a/browser/components/customizableui/CustomizableUI.jsm
+++ b/browser/components/customizableui/CustomizableUI.jsm
@@ -235,18 +235,19 @@ let CustomizableUIInternal = {
 #if defined(MOZ_WIDGET_GTK) || defined(MOZ_WIDGET_QT)
         return true;
 #else
         // This is duplicated logic from /browser/base/jar.mn
         // for win6BrowserOverlay.xul.
         return Services.appinfo.OS == "WINNT" &&
                Services.sysinfo.getProperty("version") != "5.1";
 #endif
+#else
+        return false;
 #endif
-        return false;
       }
     }, true);
 #endif
     this.registerArea(CustomizableUI.AREA_TABSTRIP, {
       legacy: true,
       type: CustomizableUI.TYPE_TOOLBAR,
       defaultPlacements: [
         "tabbrowser-tabs",
--- a/browser/devtools/shared/widgets/CubicBezierWidget.js
+++ b/browser/devtools/shared/widgets/CubicBezierWidget.js
@@ -166,17 +166,17 @@ BezierCanvas.prototype = {
       this.ctx.lineTo(xy[0], xy[1]);
       this.ctx.moveTo(1,1);
       this.ctx.lineTo(xy[2], xy[3]);
 
       this.ctx.stroke();
       this.ctx.closePath();
 
       var circle = function(ctx, cx, cy, r) {
-        return ctx.beginPath();
+        ctx.beginPath();
         ctx.arc(cx, cy, r, 0, 2*Math.PI, !1);
         ctx.closePath();
       };
 
       circle(this.ctx, xy[0], xy[1], 1.5 * defaultSettings.handleThickness);
       this.ctx.fill();
       circle(this.ctx, xy[2], xy[3], 1.5 * defaultSettings.handleThickness);
       this.ctx.fill();
--- a/build/Makefile.in
+++ b/build/Makefile.in
@@ -2,24 +2,16 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 USE_RCS_MK := 1
 include $(topsrcdir)/config/makefiles/makeutils.mk
 
 ifdef MOZ_APP_BASENAME
-DIST_FILES = $(srcdir)/application.ini
-
-ifneq (android,$(MOZ_WIDGET_TOOLKIT))
-ifdef MOZ_UPDATER
-DIST_FILES += update-settings.ini
-endif
-endif
-
 ifdef LIBXUL_SDK
 APP_INI_DEPS = $(LIBXUL_DIST)/bin/platform.ini
 else
 APP_INI_DEPS = $(topsrcdir)/config/milestone.txt
 endif
 
 APP_BUILDID := $(shell cat $(DEPTH)/config/buildid)
 APP_INI_DEPS += $(DEPTH)/config/buildid
--- a/build/moz.build
+++ b/build/moz.build
@@ -65,8 +65,13 @@ if CONFIG['MOZ_DMD']:
 
 # Put a useful .gdbinit in the bin directory, to be picked up automatically
 # by GDB when we debug executables there.
 FINAL_TARGET_FILES += [TOPSRCDIR + '/.gdbinit']
 
 # Install the clang-cl runtime library for ASAN next to the binaries we produce.
 if CONFIG['MOZ_ASAN'] and CONFIG['CLANG_CL']:
     FINAL_TARGET_FILES += [CONFIG['MOZ_CLANG_RT_ASAN_LIB_PATH']]
+
+if CONFIG['MOZ_APP_BASENAME']:
+    DIST_FILES += ['application.ini']
+    if CONFIG['MOZ_WIDGET_TOOLKIT'] != 'android' and CONFIG['MOZ_UPDATER']:
+        DIST_FILES += ['update-settings.ini']
--- a/build/unix/elfhack/moz.build
+++ b/build/unix/elfhack/moz.build
@@ -9,16 +9,20 @@ DIRS += ['inject']
 
 if not CONFIG['CROSS_COMPILE']:
     SOURCES += [
         'dummy.c',
         'test-array.c',
         'test-ctors.c',
     ]
 
+    if '-flto' in CONFIG['OS_CFLAGS']:
+        SOURCES['test-array.c'].flags += ['-fno-lto']
+        SOURCES['test-ctors.c'].flags += ['-fno-lto']
+
 HOST_SOURCES += [
     'elf.cpp',
     'elfhack.cpp',
 ]
 
 HostProgram('elfhack')
 
 DEFINES['ELFHACK_BUILD'] = True
--- a/build/unix/stdc++compat/stdc++compat.cpp
+++ b/build/unix/stdc++compat/stdc++compat.cpp
@@ -16,17 +16,18 @@
    GLIBCXX_3.4.12 is from gcc 4.4.1 (147138)
    GLIBCXX_3.4.13 is from gcc 4.4.2 (151127)
    GLIBCXX_3.4.14 is from gcc 4.5.0 (151126)
    GLIBCXX_3.4.15 is from gcc 4.6.0 (160071)
    GLIBCXX_3.4.16 is from gcc 4.6.1 (172240)
    GLIBCXX_3.4.17 is from gcc 4.7.0 (174383)
    GLIBCXX_3.4.18 is from gcc 4.8.0 (190787)
    GLIBCXX_3.4.19 is from gcc 4.8.1 (199309)
-   GLIBCXX_3.4.20 is from gcc 4.9.0 (199307) */
+   GLIBCXX_3.4.20 is from gcc 4.9.0 (199307)
+   GLIBCXX_3.4.21 is from gcc 5.0 (210290) */
 
 #define GLIBCXX_VERSION(a, b, c) (((a) << 16) | ((b) << 8) | (c))
 
 namespace std {
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 9)
     /* Instantiate these templates to avoid GLIBCXX_3.4.9 symbol versions */
     template ostream& ostream::_M_insert(double);
     template ostream& ostream::_M_insert(long);
@@ -54,19 +55,24 @@ namespace std {
     template string::basic_string(string&&);
     template string& string::operator=(string&&);
     template wstring::basic_string(wstring&&);
     template wstring& wstring::operator=(wstring&&);
     template string& string::assign(string&&);
     template wstring& wstring::assign(wstring&&);
 #endif /* __GXX_EXPERIMENTAL_CXX0X__ */
 #endif /* (__GNUC__ == 4) && (__GNUC_MINOR__ >= 5) */
+#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 16)
+    /* Instantiate these templates to avoid GLIBCXX_3.4.16 symbol versions
+     * depending on compiler optimizations */
+    template int string::_S_compare(size_type, size_type);
+#endif
 }
 
-namespace std __attribute__((visibility("default"))) {
+namespace std MOZ_EXPORT {
 #if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 14)
     /* Hack to avoid GLIBCXX_3.4.14 symbol versions */
     struct _List_node_base
     {
         void hook(_List_node_base * const __position) throw ();
 
         void unhook() throw ();
 
@@ -171,8 +177,21 @@ namespace std __attribute__((visibility(
    but that's equivalent, version-wise. Those calls are added by the compiler
    itself on `new Class[n]` calls. */
 extern "C" void
 __cxa_throw_bad_array_new_length()
 {
     MOZ_CRASH();
 }
 #endif
+
+#if MOZ_LIBSTDCXX_VERSION >= GLIBCXX_VERSION(3, 4, 21)
+/* While we generally don't build with exceptions, we have some host tools
+ * that do use them. libstdc++ from GCC 5.0 added exception constructors with
+ * char const* argument. Older versions only have a constructor with
+ * std::string. */
+namespace std {
+    runtime_error::runtime_error(char const* s)
+    : runtime_error(std::string(s))
+    {
+    }
+}
+#endif
--- a/config/rules.mk
+++ b/config/rules.mk
@@ -706,16 +706,19 @@ ifdef MSMANIFEST_TOOL
 endif	# MSVC with manifest tool
 else
 ifeq ($(HOST_CPP_PROG_LINK),1)
 	$(EXPAND_LIBS_EXEC) -- $(HOST_CXX) -o $@ $(HOST_CXXFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
 else
 	$(EXPAND_LIBS_EXEC) -- $(HOST_CC) -o $@ $(HOST_CFLAGS) $(HOST_LDFLAGS) $(HOST_PROGOBJS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
 endif # HOST_CPP_PROG_LINK
 endif
+ifndef CROSS_COMPILE
+	$(call CHECK_STDCXX,$@)
+endif
 
 #
 # This is an attempt to support generation of multiple binaries
 # in one directory, it assumes everything to compile Foo is in
 # Foo.o (from either Foo.c or Foo.cpp).
 #
 # SIMPLE_PROGRAMS = Foo Bar
 # creates Foo.o Bar.o, links with LIBS to create Foo, Bar.
@@ -748,16 +751,19 @@ ifeq (WINNT_,$(HOST_OS_ARCH)_$(GNU_CC))
 	$(EXPAND_LIBS_EXEC) -- $(HOST_LD) -NOLOGO -OUT:$@ -PDB:$(HOST_PDBFILE) $< $(WIN32_EXE_LDFLAGS) $(HOST_LIBS) $(HOST_EXTRA_LIBS)
 else
 ifneq (,$(HOST_CPPSRCS)$(USE_HOST_CXX))
 	$(EXPAND_LIBS_EXEC) -- $(HOST_CXX) $(HOST_OUTOPTION)$@ $(HOST_CXXFLAGS) $(INCLUDES) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS)
 else
 	$(EXPAND_LIBS_EXEC) -- $(HOST_CC) $(HOST_OUTOPTION)$@ $(HOST_CFLAGS) $(INCLUDES) $< $(HOST_LIBS) $(HOST_EXTRA_LIBS)
 endif
 endif
+ifndef CROSS_COMPILE
+	$(call CHECK_STDCXX,$@)
+endif
 
 ifdef DTRACE_PROBE_OBJ
 EXTRA_DEPS += $(DTRACE_PROBE_OBJ)
 OBJS += $(DTRACE_PROBE_OBJ)
 endif
 
 $(filter %.$(LIB_SUFFIX),$(LIBRARY)): $(OBJS) $(STATIC_LIBS_DEPS) $(filter %.$(LIB_SUFFIX),$(EXTRA_LIBS)) $(EXTRA_DEPS) $(GLOBAL_DEPS)
 	$(REPORT_BUILD)
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5031,17 +5031,17 @@ nsContentUtils::AddScriptBlocker()
 #ifdef DEBUG
 static bool sRemovingScriptBlockers = false;
 #endif
 
 /* static */
 void
 nsContentUtils::RemoveScriptBlocker()
 {
-  MOZ_ASSERT(NS_IsMainThread());
+  MOZ_DIAGNOSTIC_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sRemovingScriptBlockers);
   NS_ASSERTION(sScriptBlockerCount != 0, "Negative script blockers");
   --sScriptBlockerCount;
   if (sScriptBlockerCount) {
     return;
   }
 
   if (!sBlockedScriptRunners) {
--- a/dom/base/nsDeprecatedOperationList.h
+++ b/dom/base/nsDeprecatedOperationList.h
@@ -35,8 +35,9 @@ DEPRECATED_OPERATION(UseOfCaptureEvents)
 DEPRECATED_OPERATION(UseOfReleaseEvents)
 DEPRECATED_OPERATION(UseOfDOM3LoadMethod)
 DEPRECATED_OPERATION(ShowModalDialog)
 DEPRECATED_OPERATION(Window_Content)
 DEPRECATED_OPERATION(SyncXMLHttpRequest)
 DEPRECATED_OPERATION(DataContainerEvent)
 DEPRECATED_OPERATION(Window_Controllers)
 DEPRECATED_OPERATION(ImportXULIntoContent)
+DEPRECATED_OPERATION(PannerNodeDoppler)
--- a/dom/base/nsGlobalWindow.cpp
+++ b/dom/base/nsGlobalWindow.cpp
@@ -13052,17 +13052,18 @@ nsGlobalWindow::SuspendTimeouts(uint32_t
         // passing null for the context, since this shouldn't actually release this
         // timeout.
         t->Release();
       }
     }
 
     // Suspend all of the AudioContexts for this window
     for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
-      mAudioContexts[i]->Suspend();
+      ErrorResult dummy;
+      nsRefPtr<Promise> d = mAudioContexts[i]->Suspend(dummy);
     }
   }
 
   // Suspend our children as well.
   nsCOMPtr<nsIDocShell> docShell = GetDocShell();
   if (docShell) {
     int32_t childCount = 0;
     docShell->GetChildCount(&childCount);
@@ -13112,17 +13113,18 @@ nsGlobalWindow::ResumeTimeouts(bool aTha
     if (ac) {
       for (uint32_t i = 0; i < mEnabledSensors.Length(); i++)
         ac->AddWindowListener(mEnabledSensors[i], this);
     }
     EnableGamepadUpdates();
 
     // Resume all of the AudioContexts for this window
     for (uint32_t i = 0; i < mAudioContexts.Length(); ++i) {
-      mAudioContexts[i]->Resume();
+      ErrorResult dummy;
+      nsRefPtr<Promise> d = mAudioContexts[i]->Resume(dummy);
     }
 
     // Thaw all of the workers for this window.
     mozilla::dom::workers::ThawWorkersForWindow(this);
 
     // Restore all of the timeouts, using the stored time remaining
     // (stored in timeout->mTimeRemaining).
 
--- a/dom/bindings/ErrorResult.h
+++ b/dom/bindings/ErrorResult.h
@@ -42,20 +42,16 @@ ThrowErrorMessage(JSContext* aCx, const 
 } // namespace dom
 
 class ErrorResult {
 public:
   ErrorResult() {
     mResult = NS_OK;
 
 #ifdef DEBUG
-    // ErrorResult is extremely performance-sensitive code, where literally
-    // every machine instruction matters. Initialize mMessage only to suppress
-    // a debug-only warning from gcc 4.6.
-    mMessage = nullptr;
     mMightHaveUnreportedJSException = false;
     mHasMessage = false;
 #endif
   }
 
 #ifdef DEBUG
   ~ErrorResult() {
     MOZ_ASSERT_IF(IsErrorWithMessage(), !mMessage);
--- a/dom/canvas/crashtests/crashtests.list
+++ b/dom/canvas/crashtests/crashtests.list
@@ -9,16 +9,16 @@ load texImage2D.html
 load 729116.html
 load 745699-1.html
 load 746813-1.html
 # this test crashes in a bunch places still
 #load 745818-large-source.html
 load 743499-negative-size.html
 skip-if(Android) load 767337-1.html
 skip-if(Android||B2G) load 780392-1.html # bug 833371 for B2G
-skip-if(Android||B2G) load 789933-1.html # bug 833371
+skip-if(Android||B2G) skip-if(gtk2Widget&&isDebugBuild) load 789933-1.html # bug 833371 for B2G, bug 1155252 for linux
 load 794463-1.html
 load 802926-1.html
 load 896047-1.html
 load 896047-2.html
 load 916128-1.html
 load 934939-1.html
 load 1099143-1.html
--- a/dom/datastore/DataStoreCursorImpl.jsm
+++ b/dom/datastore/DataStoreCursorImpl.jsm
@@ -165,16 +165,22 @@ this.DataStoreCursor.prototype = {
       this._state = STATE_REVISION_INIT;
       this.stateMachine(aStore, aRevisionStore, aResolve, aReject);
       return;
     }
 
     let self = this;
     let request = aRevisionStore.openCursor(null, 'prev');
     request.onsuccess = function(aEvent) {
+      if (aEvent.target.result === undefined) {
+        aReject(self._window.DOMError("InvalidRevision",
+                                      "The DataStore is corrupted"));
+        return;
+      }
+
       self._revision = aEvent.target.result.value;
       self._objectId = 0;
       self._state = STATE_SEND_ALL;
       aResolve(self.createTask('clear', null, '', null));
     }
   },
 
   stateMachineRevisionInit: function(aStore, aRevisionStore, aResolve, aReject) {
--- a/dom/datastore/DataStoreService.cpp
+++ b/dom/datastore/DataStoreService.cpp
@@ -537,19 +537,20 @@ private:
   uint32_t mAppId;
   nsString mName;
   nsString mManifestURL;
 };
 
 // This DataStoreDBCallback is called when DataStoreDB opens the DataStore DB.
 // Then the first revision will be created if it's needed.
 class FirstRevisionIdCallback final : public DataStoreDBCallback
+                                    , public nsIDOMEventListener
 {
 public:
-  NS_INLINE_DECL_REFCOUNTING(FirstRevisionIdCallback)
+  NS_DECL_ISUPPORTS
 
   FirstRevisionIdCallback(uint32_t aAppId, const nsAString& aName,
                           const nsAString& aManifestURL)
     : mAppId(aAppId)
     , mName(aName)
     , mManifestURL(aManifestURL)
   {
     AssertIsInMainProcess();
@@ -563,65 +564,155 @@ public:
     MOZ_ASSERT(NS_IsMainThread());
     MOZ_ASSERT(aDb);
 
     if (aStatus == Error) {
       NS_WARNING("Failed to create the first revision.");
       return;
     }
 
+    ErrorResult error;
+
     if (aStatus == Success) {
-      nsRefPtr<DataStoreService> service = DataStoreService::Get();
-      MOZ_ASSERT(service);
+      mTxn = aDb->Transaction();
+
+      nsRefPtr<IDBObjectStore> store =
+      mTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), error);
+      if (NS_WARN_IF(error.Failed())) {
+        return;
+      }
 
-      nsresult rv = service->EnableDataStore(mAppId, mName, mManifestURL);
+      AutoSafeJSContext cx;
+      mRequest = store->OpenCursor(cx, JS::UndefinedHandleValue,
+                                   IDBCursorDirection::Prev, error);
+      if (NS_WARN_IF(error.Failed())) {
+        return;
+      }
+
+      nsresult rv;
+      rv = mRequest->EventTarget::AddEventListener(NS_LITERAL_STRING("success"),
+                                                   this, false);
       if (NS_FAILED(rv)) {
-        NS_WARNING("Failed to enable a DataStore.");
+        NS_WARNING("Failed to add an EventListener.");
+        return;
       }
 
       return;
     }
 
     // The DB has just been created.
 
+    error = CreateFirstRevision(aDb->Transaction());
+    if (error.Failed()) {
+      NS_WARNING("Failed to add a revision to a DataStore.");
+    }
+  }
+
+  nsresult
+  CreateFirstRevision(IDBTransaction* aTxn)
+  {
+    MOZ_ASSERT(aTxn);
+
     ErrorResult error;
     nsRefPtr<IDBObjectStore> store =
-      aDb->Transaction()->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION),
-                                      error);
-    if (error.Failed()) {
-      NS_WARNING("Failed to get an ObjectStore object.");
-      return;
+      aTxn->ObjectStore(NS_LITERAL_STRING(DATASTOREDB_REVISION), error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
     }
     MOZ_ASSERT(store);
 
     nsRefPtr<RevisionAddedEnableStoreCallback> callback =
       new RevisionAddedEnableStoreCallback(mAppId, mName, mManifestURL);
 
     // Note: this cx is only used for rooting and AddRevision, neither of which
     // actually care which compartment we're in.
     AutoSafeJSContext cx;
 
     // If the revision doesn't exist, let's create it.
     nsRefPtr<DataStoreRevision> revision = new DataStoreRevision();
     nsresult rv = revision->AddRevision(cx, store, 0,
                                         DataStoreRevision::RevisionVoid,
                                         callback);
-    if (NS_FAILED(rv)) {
-      NS_WARNING("Failed to add a revision to a DataStore.");
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
+  }
+
+  // nsIDOMEventListener
+  NS_IMETHOD
+  HandleEvent(nsIDOMEvent* aEvent)
+  {
+    AssertIsInMainProcess();
+    MOZ_ASSERT(NS_IsMainThread());
+
+    nsRefPtr<IDBRequest> request;
+    request.swap(mRequest);
+
+    nsRefPtr<IDBTransaction> txn;
+    txn.swap(mTxn);
+
+    request->RemoveEventListener(NS_LITERAL_STRING("success"), this, false);
+
+    nsString type;
+    nsresult rv = aEvent->GetType(type);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
     }
+
+#ifdef DEBUG
+    MOZ_ASSERT(type.EqualsASCII("success"));
+#endif
+
+    AutoSafeJSContext cx;
+
+    ErrorResult error;
+    JS::Rooted<JS::Value> result(cx);
+    request->GetResult(cx, &result, error);
+    if (NS_WARN_IF(error.Failed())) {
+      return error.ErrorCode();
+    }
+
+    // This means that the content is a IDBCursor, so the first revision already
+    // exists.
+    if (result.isObject()) {
+#ifdef DEBUG
+      IDBCursor* cursor = nullptr;
+      error = UNWRAP_OBJECT(IDBCursor, &result.toObject(), cursor);
+      MOZ_ASSERT(!error.Failed());
+#endif
+
+      nsRefPtr<DataStoreService> service = DataStoreService::Get();
+      MOZ_ASSERT(service);
+
+      return service->EnableDataStore(mAppId, mName, mManifestURL);
+    }
+
+    rv = CreateFirstRevision(txn);
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return rv;
+    }
+
+    return NS_OK;
   }
 
 private:
   ~FirstRevisionIdCallback() {}
 
+  nsRefPtr<IDBRequest> mRequest;
+  nsRefPtr<IDBTransaction> mTxn;
+
   uint32_t mAppId;
   nsString mName;
   nsString mManifestURL;
 };
 
+NS_IMPL_ISUPPORTS(FirstRevisionIdCallback, nsIDOMEventListener)
+
 // This class calls the 'retrieveRevisionId' method of the DataStore object for
 // any DataStore in the 'mResults' array. When all of them are called, the
 // promise is resolved with 'mResults'.
 // The reson why this has to be done is because DataStore are object that can be
 // created in any thread and in any process. The first revision has been
 // created, but they don't know its value yet.
 class RetrieveRevisionsCounter
 {
--- a/dom/indexedDB/test/extensions/Makefile.in
+++ b/dom/indexedDB/test/extensions/Makefile.in
@@ -1,17 +1,12 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DIST_FILES = \
-  bootstrap.js \
-  install.rdf \
-  $(NULL)
-
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
 
 GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
--- a/dom/indexedDB/test/extensions/moz.build
+++ b/dom/indexedDB/test/extensions/moz.build
@@ -1,7 +1,12 @@
 # -*- 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/.
 
 XPI_NAME = 'indexedDB'
+
+DIST_FILES += [
+    'bootstrap.js',
+    'install.rdf',
+]
--- a/dom/locales/en-US/chrome/dom/dom.properties
+++ b/dom/locales/en-US/chrome/dom/dom.properties
@@ -158,8 +158,10 @@ Window_ControllersWarning=window.control
 ImportXULIntoContentWarning=Importing XUL nodes into a content document is deprecated. This functionality may be removed soon.
 XMLDocumentLoadPrincipalMismatch=Use of document.load forbidden on Documents that come from other Windows. Only the Window in which a Document was created is allowed to call .load on that Document. Preferably, use XMLHttpRequest instead.
 # LOCALIZATION NOTE: Do not translate "IndexedDB".
 IndexedDBTransactionAbortNavigation=An IndexedDB transaction that was not yet complete has been aborted due to page navigation.
 # LOCALIZATION NOTE (WillChangeBudgetWarning): Do not translate Will-change, %1$S,%2$S,%3$S are numbers.
 WillChangeBudgetWarning=Will-change memory consumption is too high. Surface area covers %1$S pixels, budget is the document surface area multiplied by %2$S (%3$S pixels). All occurences of will-change in the document are ignored when over budget.
 # LOCALIZATION NOTE: Do not translate "ServiceWorker".
 HittingMaxWorkersPerDomain=A ServiceWorker could not be started immediately because other documents in the same origin are already using the maximum number of workers. The ServiceWorker is now queued and will be started after some of the other workers have completed.
+# LOCALIZATION NOTE: Do no translate "setVelocity", "PannerNode", "AudioListener", "speedOfSound" and "dopplerFactor"
+PannerNodeDopplerWarning=Use of setVelocity on the PannerNode and AudioListener, and speedOfSound and dopplerFactor on the AudioListener are deprecated and those members will be removed. For more help https://developer.mozilla.org/en-US/docs/Web/API/AudioListener#Deprecated_features
--- a/dom/media/AbstractThread.cpp
+++ b/dom/media/AbstractThread.cpp
@@ -6,50 +6,57 @@
 
 #include "AbstractThread.h"
 
 #include "MediaTaskQueue.h"
 #include "nsThreadUtils.h"
 
 #include "mozilla/ClearOnShutdown.h"
 #include "mozilla/StaticPtr.h"
+#include "mozilla/unused.h"
 
 namespace mozilla {
 
 StaticRefPtr<AbstractThread> sMainThread;
 
-template<>
-nsresult
-AbstractThreadImpl<nsIThread>::Dispatch(already_AddRefed<nsIRunnable> aRunnable)
+class XPCOMThreadWrapper : public AbstractThread
 {
-  MediaTaskQueue::AssertInTailDispatchIfNeeded();
-  nsCOMPtr<nsIRunnable> r = aRunnable;
-  return mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
-}
+public:
+  explicit XPCOMThreadWrapper(nsIThread* aTarget) : mTarget(aTarget) {}
 
-template<>
-bool
-AbstractThreadImpl<nsIThread>::IsCurrentThreadIn()
-{
-  bool in = NS_GetCurrentThread() == mTarget;
-  MOZ_ASSERT_IF(in, MediaTaskQueue::GetCurrentQueue() == nullptr);
-  return in;
-}
+  virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+                        DispatchFailureHandling aFailureHandling = AssertDispatchSuccess) override
+  {
+    MediaTaskQueue::AssertInTailDispatchIfNeeded();
+    nsCOMPtr<nsIRunnable> r = aRunnable;
+    nsresult rv = mTarget->Dispatch(r, NS_DISPATCH_NORMAL);
+    MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
+    unused << rv;
+  }
+
+  virtual bool IsCurrentThreadIn() override
+  {
+    bool in = NS_GetCurrentThread() == mTarget;
+    MOZ_ASSERT_IF(in, MediaTaskQueue::GetCurrentQueue() == nullptr);
+    return in;
+  }
+
+private:
+  nsRefPtr<nsIThread> mTarget;
+};
 
 void
 AbstractThread::MaybeTailDispatch(already_AddRefed<nsIRunnable> aRunnable,
-                                  bool aAssertDispatchSuccess)
+                                  DispatchFailureHandling aFailureHandling)
 {
   MediaTaskQueue* currentQueue = MediaTaskQueue::GetCurrentQueue();
   if (currentQueue && currentQueue->RequiresTailDispatch()) {
-    currentQueue->TailDispatcher().AddTask(this, Move(aRunnable), aAssertDispatchSuccess);
+    currentQueue->TailDispatcher().AddTask(this, Move(aRunnable), aFailureHandling);
   } else {
-    nsresult rv = Dispatch(Move(aRunnable));
-    MOZ_DIAGNOSTIC_ASSERT(!aAssertDispatchSuccess || NS_SUCCEEDED(rv));
-    unused << rv;
+    Dispatch(Move(aRunnable), aFailureHandling);
   }
 }
 
 
 AbstractThread*
 AbstractThread::MainThread()
 {
   MOZ_ASSERT(sMainThread);
@@ -59,13 +66,13 @@ AbstractThread::MainThread()
 void
 AbstractThread::InitStatics()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(!sMainThread);
   nsCOMPtr<nsIThread> mainThread;
   NS_GetMainThread(getter_AddRefs(mainThread));
   MOZ_DIAGNOSTIC_ASSERT(mainThread);
-  sMainThread = new AbstractThreadImpl<nsIThread>(mainThread.get());
+  sMainThread = new XPCOMThreadWrapper(mainThread.get());
   ClearOnShutdown(&sMainThread);
 }
 
 } // namespace mozilla
--- a/dom/media/AbstractThread.h
+++ b/dom/media/AbstractThread.h
@@ -27,40 +27,36 @@ namespace mozilla {
  * and AbstractThread::MainThread. If you add support for another thread that is
  * not the MainThread, you'll need to figure out how to make it unique such that
  * comparing AbstractThread pointers is equivalent to comparing nsIThread pointers.
  */
 class AbstractThread
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AbstractThread);
-  virtual nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable) = 0;
+
+  enum DispatchFailureHandling { AssertDispatchSuccess, DontAssertDispatchSuccess };
+  virtual void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+                        DispatchFailureHandling aHandling = AssertDispatchSuccess) = 0;
   virtual bool IsCurrentThreadIn() = 0;
 
   // Convenience method for dispatching a runnable when we may be running on
   // a thread that requires runnables to be dispatched with tail dispatch.
   void MaybeTailDispatch(already_AddRefed<nsIRunnable> aRunnable,
-                         bool aAssertDispatchSuccess = true);
+                         DispatchFailureHandling aFailureHandling = AssertDispatchSuccess);
+
+  // Returns true if dispatch is generally reliable. This is used to guard
+  // against FlushableMediaTaskQueues, which should go away.
+  virtual bool IsDispatchReliable() { return true; }
 
   // Convenience method for getting an AbstractThread for the main thread.
   static AbstractThread* MainThread();
 
   // Must be called exactly once during startup.
   static void InitStatics();
 
 protected:
   virtual ~AbstractThread() {}
 };
 
-template<typename TargetType>
-class AbstractThreadImpl : public AbstractThread
-{
-public:
-  explicit AbstractThreadImpl(TargetType* aTarget) : mTarget(aTarget) {}
-  virtual nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable);
-  virtual bool IsCurrentThreadIn();
-private:
-  nsRefPtr<TargetType> mTarget;
-};
-
 } // namespace mozilla
 
 #endif
--- a/dom/media/AudioSink.cpp
+++ b/dom/media/AudioSink.cpp
@@ -251,17 +251,21 @@ AudioSink::Drain()
 }
 
 void
 AudioSink::Cleanup()
 {
   AssertCurrentThreadInMonitor();
   nsRefPtr<AudioStream> audioStream;
   audioStream.swap(mAudioStream);
-  mStateMachine->DispatchOnAudioSinkComplete();
+  // Suppress the callback when the stop is requested by MediaDecoderStateMachine.
+  // See Bug 115334.
+  if (!mStopAudioThread) {
+    mStateMachine->DispatchOnAudioSinkComplete();
+  }
 
   ReentrantMonitorAutoExit exit(GetReentrantMonitor());
   audioStream->Shutdown();
 }
 
 bool
 AudioSink::ExpectMoreAudioData()
 {
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -18,17 +18,17 @@ extern PRLogModuleInfo* gMediaStreamGrap
 #endif
 
 // We don't use NSPR log here because we want this interleaved with adb logcat
 // on Android/B2G
 // #define ENABLE_LIFECYCLE_LOG
 #ifdef ENABLE_LIFECYCLE_LOG
 #ifdef ANDROID
 #include "android/log.h"
-#define LIFECYCLE_LOG(args...)  __android_log_print(ANDROID_LOG_INFO, "Gecko - MSG" , ## __VA_ARGS__); printf(__VA_ARGS__);printf("\n");
+#define LIFECYCLE_LOG(...)  __android_log_print(ANDROID_LOG_INFO, "Gecko - MSG" , __VA_ARGS__); printf(__VA_ARGS__);printf("\n");
 #else
 #define LIFECYCLE_LOG(...) printf(__VA_ARGS__);printf("\n");
 #endif
 #else
 #define LIFECYCLE_LOG(...)
 #endif
 
 namespace mozilla {
@@ -90,19 +90,16 @@ void GraphDriver::SwitchAtNextIteration(
       mPreviousDriver &&
       mPreviousDriver->AsAudioCallbackDriver()->IsSwitchingDevice() &&
       mPreviousDriver != aNextDriver) {
     return;
   }
   LIFECYCLE_LOG("Switching to new driver: %p (%s)",
       aNextDriver, aNextDriver->AsAudioCallbackDriver() ?
       "AudioCallbackDriver" : "SystemClockDriver");
-  // Sometimes we switch twice to a new driver per iteration, this is probably a
-  // bug.
-  MOZ_ASSERT(!mNextDriver || mNextDriver->AsAudioCallbackDriver());
   mNextDriver = aNextDriver;
 }
 
 void GraphDriver::EnsureImmediateWakeUpLocked()
 {
   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
   mWaitState = WAITSTATE_WAKING_UP;
   mGraphImpl->mGraphDriverAsleep = false; // atomic
@@ -140,17 +137,17 @@ public:
     LIFECYCLE_LOG("MediaStreamGraphShutdownThreadRunnable for graph %p",
         mDriver->GraphImpl());
     // We can't release an audio driver on the main thread, because it can be
     // blocking.
     if (mDriver->AsAudioCallbackDriver()) {
       LIFECYCLE_LOG("Releasing audio driver off main thread.");
       nsRefPtr<AsyncCubebTask> releaseEvent =
         new AsyncCubebTask(mDriver->AsAudioCallbackDriver(),
-                           AsyncCubebTask::SHUTDOWN);
+                           AsyncCubebOperation::SHUTDOWN);
       mDriver = nullptr;
       releaseEvent->Dispatch();
     } else {
       LIFECYCLE_LOG("Dropping driver reference for SystemClockDriver.");
       mDriver = nullptr;
     }
     return NS_OK;
   }
@@ -158,17 +155,17 @@ private:
   nsRefPtr<GraphDriver> mDriver;
 };
 
 void GraphDriver::Shutdown()
 {
   if (AsAudioCallbackDriver()) {
     LIFECYCLE_LOG("Releasing audio driver off main thread (GraphDriver::Shutdown).\n");
     nsRefPtr<AsyncCubebTask> releaseEvent =
-      new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebTask::SHUTDOWN);
+      new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN);
     releaseEvent->Dispatch();
   } else {
     Stop();
   }
 }
 
 ThreadedDriver::ThreadedDriver(MediaStreamGraphImpl* aGraphImpl)
   : GraphDriver(aGraphImpl)
@@ -199,17 +196,17 @@ public:
                     mDriver->mPreviousDriver.get(),
                     mDriver->GraphImpl());
       MOZ_ASSERT(!mDriver->AsAudioCallbackDriver());
       // Stop and release the previous driver off-main-thread, but only if we're
       // not in the situation where we've fallen back to a system clock driver
       // because the osx audio stack is currently switching output device.
       if (!mDriver->mPreviousDriver->AsAudioCallbackDriver()->IsSwitchingDevice()) {
         nsRefPtr<AsyncCubebTask> releaseEvent =
-          new AsyncCubebTask(mDriver->mPreviousDriver->AsAudioCallbackDriver(), AsyncCubebTask::SHUTDOWN);
+          new AsyncCubebTask(mDriver->mPreviousDriver->AsAudioCallbackDriver(), AsyncCubebOperation::SHUTDOWN);
         mDriver->mPreviousDriver = nullptr;
         releaseEvent->Dispatch();
       }
     } else {
       MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
       MOZ_ASSERT(mDriver->mGraphImpl->MessagesQueued(), "Don't start a graph without messages queued.");
       mDriver->mGraphImpl->SwapMessageQueues();
     }
@@ -500,73 +497,70 @@ AsyncCubebTask::Run()
     // the Dispatch(), and Dispatch/PutEvent itself doesn't hold a ref; it
     // assumes the caller does.
     return NS_OK;
   }
 
   MOZ_ASSERT(mDriver);
 
   switch(mOperation) {
-    case AsyncCubebOperation::INIT:
+    case AsyncCubebOperation::INIT: {
       LIFECYCLE_LOG("AsyncCubebOperation::INIT\n");
       mDriver->Init();
+      mDriver->CompleteAudioContextOperations(mOperation);
       break;
-    case AsyncCubebOperation::SHUTDOWN:
+    }
+    case AsyncCubebOperation::SHUTDOWN: {
       LIFECYCLE_LOG("AsyncCubebOperation::SHUTDOWN\n");
       mDriver->Stop();
+
+      mDriver->CompleteAudioContextOperations(mOperation);
+
       mDriver = nullptr;
       mShutdownGrip = nullptr;
       break;
-    case AsyncCubebOperation::SLEEP: {
-      {
-        LIFECYCLE_LOG("AsyncCubebOperation::SLEEP\n");
-        MonitorAutoLock mon(mDriver->mGraphImpl->GetMonitor());
-        // We might just have been awoken
-        if (mDriver->mGraphImpl->mNeedAnotherIteration) {
-          mDriver->mPauseRequested = false;
-          mDriver->mWaitState = AudioCallbackDriver::WAITSTATE_RUNNING;
-          mDriver->mGraphImpl->mGraphDriverAsleep = false	; // atomic
-          break;
-        }
-        mDriver->Stop();
-        mDriver->mGraphImpl->mGraphDriverAsleep = true; // atomic
-        mDriver->mWaitState = AudioCallbackDriver::WAITSTATE_WAITING_INDEFINITELY;
-        mDriver->mPauseRequested = false;
-        mDriver->mGraphImpl->GetMonitor().Wait(PR_INTERVAL_NO_TIMEOUT);
-      }
-      STREAM_LOG(PR_LOG_DEBUG, ("Restarting audio stream from sleep."));
-      mDriver->StartStream();
-      break;
     }
     default:
       MOZ_CRASH("Operation not implemented.");
   }
 
   // and now kill this thread
   NS_DispatchToMainThread(this);
 
   return NS_OK;
 }
 
+StreamAndPromiseForOperation::StreamAndPromiseForOperation(MediaStream* aStream,
+                                          void* aPromise,
+                                          dom::AudioContextOperation aOperation)
+  : mStream(aStream)
+  , mPromise(aPromise)
+  , mOperation(aOperation)
+{
+  // MOZ_ASSERT(aPromise);
+}
+
 AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl, dom::AudioChannel aChannel)
   : GraphDriver(aGraphImpl)
   , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
   , mStarted(false)
   , mAudioChannel(aChannel)
   , mInCallback(false)
   , mPauseRequested(false)
 #ifdef XP_MACOSX
   , mCallbackReceivedWhileSwitching(0)
 #endif
 {
   STREAM_LOG(PR_LOG_DEBUG, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
 }
 
 AudioCallbackDriver::~AudioCallbackDriver()
-{}
+{
+  MOZ_ASSERT(mPromisesForOperation.IsEmpty());
+}
 
 void
 AudioCallbackDriver::Init()
 {
   cubeb_stream_params params;
   uint32_t latency;
 
   MOZ_ASSERT(!NS_IsMainThread(),
@@ -646,22 +640,28 @@ AudioCallbackDriver::Resume()
 void
 AudioCallbackDriver::Start()
 {
   // If this is running on the main thread, we can't open the stream directly,
   // because it is a blocking operation.
   if (NS_IsMainThread()) {
     STREAM_LOG(PR_LOG_DEBUG, ("Starting audio threads for MediaStreamGraph %p from a new thread.", mGraphImpl));
     nsRefPtr<AsyncCubebTask> initEvent =
-      new AsyncCubebTask(this, AsyncCubebTask::INIT);
+      new AsyncCubebTask(this, AsyncCubebOperation::INIT);
     initEvent->Dispatch();
   } else {
     STREAM_LOG(PR_LOG_DEBUG, ("Starting audio threads for MediaStreamGraph %p from the previous driver's thread", mGraphImpl));
     Init();
 
+    // Check if we need to resolve promises because the driver just got switched
+    // because of a resuming AudioContext
+    if (!mPromisesForOperation.IsEmpty()) {
+      CompleteAudioContextOperations(AsyncCubebOperation::INIT);
+    }
+
     if (mPreviousDriver) {
       nsCOMPtr<nsIRunnable> event =
         new MediaStreamGraphShutdownThreadRunnable(mPreviousDriver);
       mPreviousDriver = nullptr;
       NS_DispatchToMainThread(event);
     }
   }
 }
@@ -699,17 +699,17 @@ AudioCallbackDriver::Revive()
   if (mNextDriver) {
     mNextDriver->SetGraphTime(this, mIterationStart, mIterationEnd,
                               mStateComputedTime, mNextStateComputedTime);
     mGraphImpl->SetCurrentDriver(mNextDriver);
     mNextDriver->Start();
   } else {
     STREAM_LOG(PR_LOG_DEBUG, ("Starting audio threads for MediaStreamGraph %p from a new thread.", mGraphImpl));
     nsRefPtr<AsyncCubebTask> initEvent =
-      new AsyncCubebTask(this, AsyncCubebTask::INIT);
+      new AsyncCubebTask(this, AsyncCubebOperation::INIT);
     initEvent->Dispatch();
   }
 }
 
 void
 AudioCallbackDriver::GetIntervalForIteration(GraphTime& aFrom,
                                              GraphTime& aTo)
 {
@@ -724,30 +724,16 @@ AudioCallbackDriver::GetCurrentTime()
     NS_WARNING("Could not get current time from cubeb.");
   }
 
   return mSampleRate * position;
 }
 
 void AudioCallbackDriver::WaitForNextIteration()
 {
-#if 0
-  mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
-
-  // We can't block on the monitor in the audio callback, so we kick off a new
-  // thread that will pause the audio stream, and restart it when unblocked.
-  // We don't want to sleep when we haven't started the driver yet.
-  if (!mGraphImpl->mNeedAnotherIteration && mAudioStream && mGraphImpl->Running()) {
-    STREAM_LOG(PR_LOG_DEBUG+1, ("AudioCallbackDriver going to sleep"));
-    mPauseRequested = true;
-    nsRefPtr<AsyncCubebTask> sleepEvent =
-      new AsyncCubebTask(this, AsyncCubebTask::SLEEP);
-    sleepEvent->Dispatch();
-  }
-#endif
 }
 
 void
 AudioCallbackDriver::WakeUp()
 {
   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
   mGraphImpl->GetMonitor().Notify();
 }
@@ -1069,10 +1055,52 @@ AudioCallbackDriver::IterationDuration()
 }
 
 bool
 AudioCallbackDriver::IsStarted() {
   mGraphImpl->GetMonitor().AssertCurrentThreadOwns();
   return mStarted;
 }
 
+void
+AudioCallbackDriver::EnqueueStreamAndPromiseForOperation(MediaStream* aStream,
+                                          void* aPromise,
+                                          dom::AudioContextOperation aOperation)
+{
+  MonitorAutoLock mon(mGraphImpl->GetMonitor());
+  mPromisesForOperation.AppendElement(StreamAndPromiseForOperation(aStream,
+                                                                   aPromise,
+                                                                   aOperation));
+}
+
+void AudioCallbackDriver::CompleteAudioContextOperations(AsyncCubebOperation aOperation)
+{
+  nsAutoTArray<StreamAndPromiseForOperation, 1> array;
+
+  // We can't lock for the whole function because AudioContextOperationCompleted
+  // will grab the monitor
+  {
+    MonitorAutoLock mon(GraphImpl()->GetMonitor());
+    array.SwapElements(mPromisesForOperation);
+  }
+
+  for (int32_t i = array.Length() - 1; i >= 0; i--) {
+    StreamAndPromiseForOperation& s = array[i];
+    if ((aOperation == AsyncCubebOperation::INIT &&
+         s.mOperation == AudioContextOperation::Resume) ||
+        (aOperation == AsyncCubebOperation::SHUTDOWN &&
+         s.mOperation != AudioContextOperation::Resume)) {
+
+      GraphImpl()->AudioContextOperationCompleted(s.mStream,
+                                                  s.mPromise,
+                                                  s.mOperation);
+      array.RemoveElementAt(i);
+    }
+  }
+
+  if (!array.IsEmpty()) {
+    MonitorAutoLock mon(GraphImpl()->GetMonitor());
+    mPromisesForOperation.AppendElements(array);
+  }
+}
+
 
 } // namepace mozilla
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -8,16 +8,17 @@
 
 #include "nsAutoPtr.h"
 #include "nsAutoRef.h"
 #include "AudioBufferUtils.h"
 #include "AudioMixer.h"
 #include "AudioSegment.h"
 #include "SelfRef.h"
 #include "mozilla/Atomics.h"
+#include "AudioContext.h"
 
 struct cubeb_stream;
 
 template <>
 class nsAutoRefTraits<cubeb_stream> : public nsPointerRefTraits<cubeb_stream>
 {
 public:
   static void Release(cubeb_stream* aStream) { cubeb_stream_destroy(aStream); }
@@ -316,16 +317,31 @@ public:
     return this;
   }
 
 private:
   // Time, in GraphTime, for each iteration
   GraphTime mSlice;
 };
 
+struct StreamAndPromiseForOperation
+{
+  StreamAndPromiseForOperation(MediaStream* aStream,
+                               void* aPromise,
+                               dom::AudioContextOperation aOperation);
+  nsRefPtr<MediaStream> mStream;
+  void* mPromise;
+  dom::AudioContextOperation mOperation;
+};
+
+enum AsyncCubebOperation {
+  INIT,
+  SHUTDOWN
+};
+
 /**
  * This is a graph driver that is based on callback functions called by the
  * audio api. This ensures minimal audio latency, because it means there is no
  * buffering happening: the audio is generated inside the callback.
  *
  * This design is less flexible than running our own thread:
  * - We have no control over the thread:
  * - It cannot block, and it has to run for a shorter amount of time than the
@@ -387,16 +403,22 @@ public:
                              uint32_t aChannels,
                              uint32_t aFrames,
                              uint32_t aSampleRate) override;
 
   virtual AudioCallbackDriver* AsAudioCallbackDriver() override {
     return this;
   }
 
+  /* Enqueue a promise that is going to be resolved when a specific operation
+   * occurs on the cubeb stream. */
+  void EnqueueStreamAndPromiseForOperation(MediaStream* aStream,
+                                         void* aPromise,
+                                         dom::AudioContextOperation aOperation);
+
   bool IsSwitchingDevice() {
 #ifdef XP_MACOSX
     return mSelfReference;
 #else
     return false;
 #endif
   }
 
@@ -409,16 +431,18 @@ public:
 
   /* Whether the underlying cubeb stream has been started. See comment for
    * mStarted for details. */
   bool IsStarted();
 
   /* Tell the driver whether this process is using a microphone or not. This is
    * thread safe. */
   void SetMicrophoneActive(bool aActive);
+
+  void CompleteAudioContextOperations(AsyncCubebOperation aOperation);
 private:
   /**
    * On certain MacBookPro, the microphone is located near the left speaker.
    * We need to pan the sound output to the right speaker if we are using the
    * mic and the built-in speaker, or we will have terrible echo.  */
   void PanOutputIfNeeded(bool aMicrophoneActive);
   /**
    * This is called when the output device used by the cubeb stream changes. */
@@ -466,16 +490,17 @@ private:
     explicit AutoInCallback(AudioCallbackDriver* aDriver);
     ~AutoInCallback();
     AudioCallbackDriver* mDriver;
   };
 
   /* Thread for off-main-thread initialization and
    * shutdown of the audio stream. */
   nsCOMPtr<nsIThread> mInitShutdownThread;
+  nsAutoTArray<StreamAndPromiseForOperation, 1> mPromisesForOperation;
   dom::AudioChannel mAudioChannel;
   Atomic<bool> mInCallback;
   /* A thread has been created to be able to pause and restart the audio thread,
    * but has not done so yet. This indicates that the callback should return
    * early */
   bool mPauseRequested;
   /**
    * True if microphone is being used by this process. This is synchronized by
@@ -493,22 +518,16 @@ private:
    * since OSX seems to still call us _sometimes_. */
   uint32_t mCallbackReceivedWhileSwitching;
 #endif
 };
 
 class AsyncCubebTask : public nsRunnable
 {
 public:
-  enum AsyncCubebOperation {
-    INIT,
-    SHUTDOWN,
-    SLEEP
-  };
-
 
   AsyncCubebTask(AudioCallbackDriver* aDriver, AsyncCubebOperation aOperation);
 
   nsresult Dispatch()
   {
     // Can't add 'this' as the event to run, since mThread may not be set yet
     nsresult rv = NS_NewNamedThread("CubebOperation", getter_AddRefs(mThread));
     if (NS_SUCCEEDED(rv)) {
--- a/dom/media/MediaPromise.h
+++ b/dom/media/MediaPromise.h
@@ -234,17 +234,17 @@ protected:
       PROMISE_LOG("%s Then() call made from %s [Runnable=%p, Promise=%p, ThenValue=%p]",
                   resolved ? "Resolving" : "Rejecting", ThenValueBase::mCallSite,
                   runnable.get(), aPromise, this);
 
       // Promise consumers are allowed to disconnect the Consumer object and
       // then shut down the thread or task queue that the promise result would
       // be dispatched on. So we unfortunately can't assert that promise
       // dispatch succeeds. :-(
-      aDispatcher.AddTask(mResponseTarget, runnable.forget(), /* aAssertDispatchSuccess = */ false);
+      aDispatcher.AddTask(mResponseTarget, runnable.forget(), AbstractThread::DontAssertDispatchSuccess);
     }
 
 #ifdef DEBUG
   void AssertOnDispatchThread()
   {
     MOZ_ASSERT(mResponseTarget->IsCurrentThreadIn());
   }
 #else
@@ -316,16 +316,17 @@ public:
     // is invoked. This case is rare, but it _can_ happen, which makes it a ripe
     // target for race bugs. So we do an extra assertion here to make sure our
     // caller is using tail dispatch correctly no matter what, rather than
     // relying on the assertion in Dispatch(), which may be called extremely
     // infrequently.
     aDispatcher.AssertIsTailDispatcherIfRequired();
 
     MutexAutoLock lock(mMutex);
+    MOZ_ASSERT(aResponseThread->IsDispatchReliable());
     MOZ_DIAGNOSTIC_ASSERT(!IsExclusive || !mHaveConsumer);
     mHaveConsumer = true;
     nsRefPtr<ThenValueBase> thenValue = new ThenValue<ThisType, ResolveMethodType, RejectMethodType>(
                                               aResponseThread, aThisVal, aResolveMethod, aRejectMethod, aCallSite);
     PROMISE_LOG("%s invoking Then() [this=%p, thenValue=%p, aThisVal=%p, isPending=%d]",
                 aCallSite, this, thenValue.get(), aThisVal, (int) IsPending());
     if (!IsPending()) {
       thenValue->Dispatch(this, aDispatcher);
@@ -665,16 +666,17 @@ private:
 
 template<typename PromiseType>
 static nsRefPtr<PromiseType>
 ProxyInternal(AbstractThread* aTarget, MethodCallBase<PromiseType>* aMethodCall, const char* aCallerName,
               TaskDispatcher& aDispatcher)
 {
   nsRefPtr<typename PromiseType::Private> p = new (typename PromiseType::Private)(aCallerName);
   nsRefPtr<ProxyRunnable<PromiseType>> r = new ProxyRunnable<PromiseType>(p, aMethodCall);
+  MOZ_ASSERT(aTarget->IsDispatchReliable());
   aDispatcher.AddTask(aTarget, r.forget());
   return Move(p);
 }
 
 } // namespace detail
 
 template<typename PromiseType, typename ThisType>
 static nsRefPtr<PromiseType>
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -19,16 +19,17 @@
 #include "prlog.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
+#include "mozilla/dom/AudioContextBinding.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
 #include "mozilla/unused.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
 #endif
 
@@ -97,22 +98,41 @@ MediaStreamGraphImpl::FinishStream(Media
   // Force at least one more iteration of the control loop, since we rely
   // on UpdateCurrentTimeForStreams to notify our listeners once the stream end
   // has been reached.
   EnsureNextIteration();
 
   SetStreamOrderDirty();
 }
 
+static const GraphTime START_TIME_DELAYED = -1;
+
 void
 MediaStreamGraphImpl::AddStream(MediaStream* aStream)
 {
-  aStream->mBufferStartTime = IterationEnd();
-  mStreams.AppendElement(aStream);
-  STREAM_LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
+  // Check if we're adding a stream to a suspended context, in which case, we
+  // add it to mSuspendedStreams, and delay setting mBufferStartTime
+  bool contextSuspended = false;
+  if (aStream->AsAudioNodeStream()) {
+    for (uint32_t i = 0; i < mSuspendedStreams.Length(); i++) {
+      if (aStream->AudioContextId() == mSuspendedStreams[i]->AudioContextId()) {
+        contextSuspended = true;
+      }
+    }
+  }
+
+  if (contextSuspended) {
+    aStream->mBufferStartTime = START_TIME_DELAYED;
+    mSuspendedStreams.AppendElement(aStream);
+    STREAM_LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph, in the suspended stream array", aStream));
+  } else {
+    aStream->mBufferStartTime = IterationEnd();
+    mStreams.AppendElement(aStream);
+    STREAM_LOG(PR_LOG_DEBUG, ("Adding media stream %p to the graph", aStream));
+  }
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::RemoveStream(MediaStream* aStream)
 {
   // Remove references in mStreamUpdates before we allow aStream to die.
@@ -126,16 +146,18 @@ MediaStreamGraphImpl::RemoveStream(Media
       }
     }
   }
 
   // Ensure that mFirstCycleBreaker and mMixer are updated when necessary.
   SetStreamOrderDirty();
 
   mStreams.RemoveElement(aStream);
+  mSuspendedStreams.RemoveElement(aStream);
+
   NS_RELEASE(aStream); // probably destroying it
 
   STREAM_LOG(PR_LOG_DEBUG, ("Removing media stream %p from the graph", aStream));
 }
 
 void
 MediaStreamGraphImpl::UpdateConsumptionState(SourceMediaStream* aStream)
 {
@@ -277,17 +299,17 @@ MediaStreamGraphImpl::UpdateBufferSuffic
     }
   }
 
   for (uint32_t i = 0; i < runnables.Length(); ++i) {
     // This dispatch was observed to fail in test_video_dimensions.html on
     // win8 64 debug when invoked from noop_resampler::fill on the cubeb audio
     // thread.
     nsCOMPtr<nsIRunnable> r = runnables[i].mRunnable;
-    runnables[i].mTarget->MaybeTailDispatch(r.forget(), /* aAssertDispatchSuccess = */ false);
+    runnables[i].mTarget->MaybeTailDispatch(r.forget(), AbstractThread::DontAssertDispatchSuccess);
   }
 }
 
 StreamTime
 MediaStreamGraphImpl::GraphTimeToStreamTime(MediaStream* aStream,
                                             GraphTime aTime)
 {
   MOZ_ASSERT(aTime <= CurrentDriver()->StateComputedTime(),
@@ -375,59 +397,74 @@ MediaStreamGraphImpl::IterationEnd()
   return CurrentDriver()->IterationEnd();
 }
 
 void
 MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime, GraphTime aNextCurrentTime)
 {
   nsTArray<MediaStream*> streamsReadyToFinish;
   nsAutoTArray<bool,800> streamHasOutput;
+
+  nsTArray<MediaStream*>* runningAndSuspendedPair[2];
+  runningAndSuspendedPair[0] = &mStreams;
+  runningAndSuspendedPair[1] = &mSuspendedStreams;
+
   streamHasOutput.SetLength(mStreams.Length());
-  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
-    MediaStream* stream = mStreams[i];
-
-    // Calculate blocked time and fire Blocked/Unblocked events
-    GraphTime blockedTime = 0;
-    GraphTime t = aPrevCurrentTime;
-    // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called
-    // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == stream end time|
-    while (t <= aNextCurrentTime) {
-      GraphTime end;
-      bool blocked = stream->mBlocked.GetAt(t, &end);
-      if (blocked) {
-        blockedTime += std::min(end, aNextCurrentTime) - t;
-      }
-      if (blocked != stream->mNotifiedBlocked) {
-        for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
-          MediaStreamListener* l = stream->mListeners[j];
-          l->NotifyBlockingChanged(this,
-              blocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
+
+  for (uint32_t array = 0; array < 2; array++) {
+    for (uint32_t i = 0; i < runningAndSuspendedPair[array]->Length(); ++i) {
+      MediaStream* stream = (*runningAndSuspendedPair[array])[i];
+
+      // Calculate blocked time and fire Blocked/Unblocked events
+      GraphTime blockedTime = 0;
+      GraphTime t = aPrevCurrentTime;
+      // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called
+      // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime ==
+      // stream end time|
+      while (t <= aNextCurrentTime) {
+        GraphTime end;
+        bool blocked = stream->mBlocked.GetAt(t, &end);
+        if (blocked) {
+          blockedTime += std::min(end, aNextCurrentTime) - t;
         }
-        stream->mNotifiedBlocked = blocked;
+        if (blocked != stream->mNotifiedBlocked) {
+          for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
+            MediaStreamListener* l = stream->mListeners[j];
+            l->NotifyBlockingChanged(this, blocked
+                                             ? MediaStreamListener::BLOCKED
+                                             : MediaStreamListener::UNBLOCKED);
+          }
+          stream->mNotifiedBlocked = blocked;
+        }
+        t = end;
       }
-      t = end;
+
+      stream->AdvanceTimeVaryingValuesToCurrentTime(aNextCurrentTime,
+                                                    blockedTime);
+      // Advance mBlocked last so that implementations of
+      // AdvanceTimeVaryingValuesToCurrentTime can rely on the value of
+      // mBlocked.
+      stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime);
+
+      if (runningAndSuspendedPair[array] == &mStreams) {
+        streamHasOutput[i] = blockedTime < aNextCurrentTime - aPrevCurrentTime;
+        // Make this an assertion when bug 957832 is fixed.
+        NS_WARN_IF_FALSE(
+          !streamHasOutput[i] || !stream->mNotifiedFinished,
+          "Shouldn't have already notified of finish *and* have output!");
+
+        if (stream->mFinished && !stream->mNotifiedFinished) {
+          streamsReadyToFinish.AppendElement(stream);
+        }
+      }
+      STREAM_LOG(PR_LOG_DEBUG + 1,
+                 ("MediaStream %p bufferStartTime=%f blockedTime=%f", stream,
+                  MediaTimeToSeconds(stream->mBufferStartTime),
+                  MediaTimeToSeconds(blockedTime)));
     }
-
-
-    stream->AdvanceTimeVaryingValuesToCurrentTime(aNextCurrentTime, blockedTime);
-    // Advance mBlocked last so that implementations of
-    // AdvanceTimeVaryingValuesToCurrentTime can rely on the value of mBlocked.
-    stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime);
-
-    streamHasOutput[i] = blockedTime < aNextCurrentTime - aPrevCurrentTime;
-    // Make this an assertion when bug 957832 is fixed.
-    NS_WARN_IF_FALSE(!streamHasOutput[i] || !stream->mNotifiedFinished,
-      "Shouldn't have already notified of finish *and* have output!");
-
-    if (stream->mFinished && !stream->mNotifiedFinished) {
-      streamsReadyToFinish.AppendElement(stream);
-    }
-    STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p bufferStartTime=%f blockedTime=%f",
-                                stream, MediaTimeToSeconds(stream->mBufferStartTime),
-                                MediaTimeToSeconds(blockedTime)));
   }
 
 
   for (uint32_t i = 0; i < streamHasOutput.Length(); ++i) {
     if (!streamHasOutput[i]) {
       continue;
     }
     MediaStream* stream = mStreams[i];
@@ -515,28 +552,38 @@ MediaStreamGraphImpl::MarkConsumed(Media
     return;
   }
   // Mark all the inputs to this stream as consumed
   for (uint32_t i = 0; i < ps->mInputs.Length(); ++i) {
     MarkConsumed(ps->mInputs[i]->mSource);
   }
 }
 
+bool
+MediaStreamGraphImpl::StreamSuspended(MediaStream* aStream)
+{
+  // Only AudioNodeStreams can be suspended, so we can shortcut here.
+  return aStream->AsAudioNodeStream() &&
+         mSuspendedStreams.IndexOf(aStream) != mSuspendedStreams.NoIndex;
+}
+
+namespace {
+  // Value of mCycleMarker for unvisited streams in cycle detection.
+  const uint32_t NOT_VISITED = UINT32_MAX;
+  // Value of mCycleMarker for ordered streams in muted cycles.
+  const uint32_t IN_MUTED_CYCLE = 1;
+}
+
 void
 MediaStreamGraphImpl::UpdateStreamOrder()
 {
 #ifdef MOZ_WEBRTC
   bool shouldAEC = false;
 #endif
   bool audioTrackPresent = false;
-  // Value of mCycleMarker for unvisited streams in cycle detection.
-  const uint32_t NOT_VISITED = UINT32_MAX;
-  // Value of mCycleMarker for ordered streams in muted cycles.
-  const uint32_t IN_MUTED_CYCLE = 1;
-
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
     stream->mIsConsumed = false;
     stream->mInBlockingSet = false;
 #ifdef MOZ_WEBRTC
     if (stream->AsSourceStream() &&
         stream->AsSourceStream()->NeedsMixing()) {
       shouldAEC = true;
@@ -642,35 +689,45 @@ MediaStreamGraphImpl::UpdateStreamOrder(
     if (ps->mCycleMarker == NOT_VISITED) {
       // Record the position on the visited stack, so that any searches
       // finding this stream again know how much of the stack is in the cycle.
       ps->mCycleMarker = nextStackMarker;
       --nextStackMarker;
       // Not-visited input streams should be processed first.
       // SourceMediaStreams have already been ordered.
       for (uint32_t i = inputs.Length(); i--; ) {
+        if (StreamSuspended(inputs[i]->mSource)) {
+          continue;
+        }
         auto input = inputs[i]->mSource->AsProcessedStream();
         if (input && input->mCycleMarker == NOT_VISITED) {
-          input->remove();
-          dfsStack.insertFront(input);
+          // It can be that this stream has an input which is from a suspended
+          // AudioContext.
+          if (input->isInList()) {
+            input->remove();
+            dfsStack.insertFront(input);
+          }
         }
       }
       continue;
     }
 
     // Returning from DFS.  Pop from dfsStack.
     ps->remove();
 
     // cycleStackMarker keeps track of the highest marker value on any
     // upstream stream, if any, found receiving input, directly or indirectly,
     // from the visited stack (and so from |ps|, making a cycle).  In a
     // variation from Tarjan's SCC algorithm, this does not include |ps|
     // unless it is part of the cycle.
     uint32_t cycleStackMarker = 0;
     for (uint32_t i = inputs.Length(); i--; ) {
+      if (StreamSuspended(inputs[i]->mSource)) {
+        continue;
+      }
       auto input = inputs[i]->mSource->AsProcessedStream();
       if (input) {
         cycleStackMarker = std::max(cycleStackMarker, input->mCycleMarker);
       }
     }
 
     if (cycleStackMarker <= IN_MUTED_CYCLE) {
       // All inputs have been ordered and their stack markers have been removed.
@@ -756,40 +813,47 @@ MediaStreamGraphImpl::UpdateStreamOrder(
 
 void
 MediaStreamGraphImpl::RecomputeBlocking(GraphTime aEndBlockingDecisions)
 {
   bool blockingDecisionsWillChange = false;
 
   STREAM_LOG(PR_LOG_DEBUG+1, ("Media graph %p computing blocking for time %f",
                               this, MediaTimeToSeconds(CurrentDriver()->StateComputedTime())));
-  for (uint32_t i = 0; i < mStreams.Length(); ++i) {
-    MediaStream* stream = mStreams[i];
-    if (!stream->mInBlockingSet) {
-      // Compute a partition of the streams containing 'stream' such that we can
-      // compute the blocking status of each subset independently.
-      nsAutoTArray<MediaStream*,10> streamSet;
-      AddBlockingRelatedStreamsToSet(&streamSet, stream);
+  nsTArray<MediaStream*>* runningAndSuspendedPair[2];
+  runningAndSuspendedPair[0] = &mStreams;
+  runningAndSuspendedPair[1] = &mSuspendedStreams;
+
+  for (uint32_t array = 0; array < 2; array++) {
+    for (uint32_t i = 0; i < (*runningAndSuspendedPair[array]).Length(); ++i) {
+      MediaStream* stream = (*runningAndSuspendedPair[array])[i];
+      if (!stream->mInBlockingSet) {
+        // Compute a partition of the streams containing 'stream' such that we
+        // can
+        // compute the blocking status of each subset independently.
+        nsAutoTArray<MediaStream*, 10> streamSet;
+        AddBlockingRelatedStreamsToSet(&streamSet, stream);
+
+        GraphTime end;
+        for (GraphTime t = CurrentDriver()->StateComputedTime();
+             t < aEndBlockingDecisions; t = end) {
+          end = GRAPH_TIME_MAX;
+          RecomputeBlockingAt(streamSet, t, aEndBlockingDecisions, &end);
+          if (end < GRAPH_TIME_MAX) {
+            blockingDecisionsWillChange = true;
+          }
+        }
+      }
 
       GraphTime end;
-      for (GraphTime t = CurrentDriver()->StateComputedTime();
-           t < aEndBlockingDecisions; t = end) {
-        end = GRAPH_TIME_MAX;
-        RecomputeBlockingAt(streamSet, t, aEndBlockingDecisions, &end);
-        if (end < GRAPH_TIME_MAX) {
-          blockingDecisionsWillChange = true;
-        }
+      stream->mBlocked.GetAt(IterationEnd(), &end);
+      if (end < GRAPH_TIME_MAX) {
+        blockingDecisionsWillChange = true;
       }
     }
-
-    GraphTime end;
-    stream->mBlocked.GetAt(IterationEnd(), &end);
-    if (end < GRAPH_TIME_MAX) {
-      blockingDecisionsWillChange = true;
-    }
   }
   STREAM_LOG(PR_LOG_DEBUG+1, ("Media graph %p computed blocking for interval %f to %f",
                               this, MediaTimeToSeconds(CurrentDriver()->StateComputedTime()),
                               MediaTimeToSeconds(aEndBlockingDecisions)));
 
   CurrentDriver()->UpdateStateComputedTime(aEndBlockingDecisions);
 
   if (blockingDecisionsWillChange) {
@@ -993,24 +1057,16 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
     AudioSegment* audio = track->Get<AudioSegment>();
     AudioSegment output;
 
     // offset and audioOutput.mLastTickWritten can differ by at most one sample,
     // because of the rounding issue. We track that to ensure we don't skip a
     // sample. One sample may be played twice, but this should not happen
     // again during an unblocked sequence of track samples.
     StreamTime offset = GraphTimeToStreamTime(aStream, aFrom);
-    if (audioOutput.mLastTickWritten &&
-        audioOutput.mLastTickWritten != offset) {
-      // If there is a global underrun of the MSG, this property won't hold, and
-      // we reset the sample count tracking.
-      if (offset - audioOutput.mLastTickWritten == 1) {
-        offset = audioOutput.mLastTickWritten;
-      }
-    }
 
     // We don't update aStream->mBufferStartTime here to account for time spent
     // blocked. Instead, we'll update it in UpdateCurrentTimeForStreams after
     // the blocked period has completed. But we do need to make sure we play
     // from the right offsets in the stream buffer, even if we've already
     // written silence for some amount of blocked time after the current time.
     GraphTime t = aFrom;
     while (ticksNeeded) {
@@ -1032,37 +1088,49 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
         output.InsertNullDataAtStart(toWrite);
         ticksWritten += toWrite;
         STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld blocking-silence samples for %f to %f (%ld to %ld)\n",
                                     aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
                                     offset, offset + toWrite));
       } else {
         StreamTime endTicksNeeded = offset + toWrite;
         StreamTime endTicksAvailable = audio->GetDuration();
-        STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p writing %ld samples for %f to %f (samples %ld to %ld)\n",
-                                     aStream, toWrite, MediaTimeToSeconds(t), MediaTimeToSeconds(end),
-                                     offset, endTicksNeeded));
 
         if (endTicksNeeded <= endTicksAvailable) {
+          STREAM_LOG(PR_LOG_DEBUG + 1,
+                     ("MediaStream %p writing %ld samples for %f to %f "
+                      "(samples %ld to %ld)\n",
+                      aStream, toWrite, MediaTimeToSeconds(t),
+                      MediaTimeToSeconds(end), offset, endTicksNeeded));
           output.AppendSlice(*audio, offset, endTicksNeeded);
           ticksWritten += toWrite;
           offset = endTicksNeeded;
         } else {
           // MOZ_ASSERT(track->IsEnded(), "Not enough data, and track not ended.");
           // If we are at the end of the track, maybe write the remaining
           // samples, and pad with/output silence.
           if (endTicksNeeded > endTicksAvailable &&
               offset < endTicksAvailable) {
             output.AppendSlice(*audio, offset, endTicksAvailable);
+            STREAM_LOG(PR_LOG_DEBUG + 1,
+                       ("MediaStream %p writing %ld samples for %f to %f "
+                        "(samples %ld to %ld)\n",
+                        aStream, toWrite, MediaTimeToSeconds(t),
+                        MediaTimeToSeconds(end), offset, endTicksNeeded));
             uint32_t available = endTicksAvailable - offset;
             ticksWritten += available;
             toWrite -= available;
             offset = endTicksAvailable;
           }
           output.AppendNullData(toWrite);
+          STREAM_LOG(PR_LOG_DEBUG + 1,
+                     ("MediaStream %p writing %ld padding slsamples for %f to "
+                      "%f (samples %ld to %ld)\n",
+                      aStream, toWrite, MediaTimeToSeconds(t),
+                      MediaTimeToSeconds(end), offset, endTicksNeeded));
           ticksWritten += toWrite;
         }
         output.ApplyVolume(volume);
       }
       t = end;
     }
     audioOutput.mLastTickWritten = offset;
 
@@ -1784,17 +1852,17 @@ MediaStreamGraphImpl::EnsureStableStateE
   mPostedRunInStableStateEvent = true;
   nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this, true);
   NS_DispatchToMainThread(event);
 }
 
 void
 MediaStreamGraphImpl::AppendMessage(ControlMessage* aMessage)
 {
-  NS_ASSERTION(NS_IsMainThread(), "main thread only");
+  MOZ_ASSERT(NS_IsMainThread(), "main thread only");
   NS_ASSERTION(!aMessage->GetStream() ||
                !aMessage->GetStream()->IsDestroyed(),
                "Stream already destroyed");
 
   if (mDetectedNotRunning &&
       mLifecycleState > LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP) {
     // The graph control loop is not running and main thread cleanup has
     // happened. From now on we can't append messages to mCurrentTaskMessageQueue,
@@ -2144,16 +2212,56 @@ MediaStream::ChangeExplicitBlockerCount(
   // stream has been destroyed since then.
   if (mMainThreadDestroyed) {
     return;
   }
   GraphImpl()->AppendMessage(new Message(this, aDelta));
 }
 
 void
+MediaStream::BlockStreamIfNeeded()
+{
+  class Message : public ControlMessage {
+  public:
+    explicit Message(MediaStream* aStream) : ControlMessage(aStream)
+    { }
+    virtual void Run()
+    {
+      mStream->BlockStreamIfNeededImpl(
+          mStream->GraphImpl()->CurrentDriver()->StateComputedTime());
+    }
+  };
+
+  if (mMainThreadDestroyed) {
+    return;
+  }
+  GraphImpl()->AppendMessage(new Message(this));
+}
+
+void
+MediaStream::UnblockStreamIfNeeded()
+{
+  class Message : public ControlMessage {
+  public:
+    explicit Message(MediaStream* aStream) : ControlMessage(aStream)
+    { }
+    virtual void Run()
+    {
+      mStream->UnblockStreamIfNeededImpl(
+          mStream->GraphImpl()->CurrentDriver()->StateComputedTime());
+    }
+  };
+
+  if (mMainThreadDestroyed) {
+    return;
+  }
+  GraphImpl()->AppendMessage(new Message(this));
+}
+
+void
 MediaStream::AddListenerImpl(already_AddRefed<MediaStreamListener> aListener)
 {
   MediaStreamListener* listener = *mListeners.AppendElement() = aListener;
   listener->NotifyBlockingChanged(GraphImpl(),
     mNotifiedBlocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
   if (mNotifiedFinished) {
     listener->NotifyEvent(GraphImpl(), MediaStreamListener::EVENT_FINISHED);
   }
@@ -3026,46 +3134,319 @@ MediaStreamGraph::CreateTrackUnionStream
 
 AudioNodeExternalInputStream*
 MediaStreamGraph::CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aSampleRate) {
     aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
   }
-  AudioNodeExternalInputStream* stream = new AudioNodeExternalInputStream(aEngine, aSampleRate);
+  AudioNodeExternalInputStream* stream = new AudioNodeExternalInputStream(
+      aEngine, aSampleRate, aEngine->NodeMainThread()->Context()->Id());
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
 AudioNodeStream*
 MediaStreamGraph::CreateAudioNodeStream(AudioNodeEngine* aEngine,
                                         AudioNodeStreamKind aKind,
                                         TrackRate aSampleRate)
 {
   MOZ_ASSERT(NS_IsMainThread());
   if (!aSampleRate) {
     aSampleRate = aEngine->NodeMainThread()->Context()->SampleRate();
   }
-  AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind, aSampleRate);
+  // MediaRecorders use an AudioNodeStream, but no AudioNode
+  AudioNode* node = aEngine->NodeMainThread();
+  dom::AudioContext::AudioContextId contextIdForStream = node ? node->Context()->Id() :
+                                                                NO_AUDIO_CONTEXT;
+  AudioNodeStream* stream = new AudioNodeStream(aEngine, aKind, aSampleRate,
+                                                contextIdForStream);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   if (aEngine->HasNode()) {
     stream->SetChannelMixingParametersImpl(aEngine->NodeMainThread()->ChannelCount(),
                                            aEngine->NodeMainThread()->ChannelCountModeValue(),
                                            aEngine->NodeMainThread()->ChannelInterpretationValue());
   }
   graph->AppendMessage(new CreateMessage(stream));
   return stream;
 }
 
+class GraphStartedRunnable final : public nsRunnable
+{
+public:
+  GraphStartedRunnable(AudioNodeStream* aStream, MediaStreamGraph* aGraph)
+  : mStream(aStream)
+  , mGraph(aGraph)
+  { }
+
+  NS_IMETHOD Run() {
+    mGraph->NotifyWhenGraphStarted(mStream);
+    return NS_OK;
+  }
+
+private:
+  nsRefPtr<AudioNodeStream> mStream;
+  MediaStreamGraph* mGraph;
+};
+
+void
+MediaStreamGraph::NotifyWhenGraphStarted(AudioNodeStream* aStream)
+{
+  class GraphStartedNotificationControlMessage : public ControlMessage
+  {
+  public:
+    explicit GraphStartedNotificationControlMessage(AudioNodeStream* aStream)
+      : ControlMessage(aStream)
+    {
+    }
+    virtual void Run()
+    {
+      // This runs on the graph thread, so when this runs, and the current
+      // driver is an AudioCallbackDriver, we know the audio hardware is
+      // started. If not, we are going to switch soon, keep reposting this
+      // ControlMessage.
+      MediaStreamGraphImpl* graphImpl = mStream->GraphImpl();
+      if (graphImpl->CurrentDriver()->AsAudioCallbackDriver()) {
+        nsCOMPtr<nsIRunnable> event = new dom::StateChangeTask(
+            mStream->AsAudioNodeStream(), nullptr, AudioContextState::Running);
+        NS_DispatchToMainThread(event);
+      } else {
+        nsCOMPtr<nsIRunnable> event = new GraphStartedRunnable(
+            mStream->AsAudioNodeStream(), mStream->Graph());
+        NS_DispatchToMainThread(event);
+      }
+    }
+    virtual void RunDuringShutdown()
+    {
+      MOZ_ASSERT(false, "We should be reviving the graph?");
+    }
+  };
+
+  MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
+  graphImpl->AppendMessage(new GraphStartedNotificationControlMessage(aStream));
+}
+
+void
+MediaStreamGraphImpl::ResetVisitedStreamState()
+{
+  // Reset the visited/consumed/blocked state of the streams.
+  nsTArray<MediaStream*>* runningAndSuspendedPair[2];
+  runningAndSuspendedPair[0] = &mStreams;
+  runningAndSuspendedPair[1] = &mSuspendedStreams;
+
+  for (uint32_t array = 0; array < 2; array++) {
+    for (uint32_t i = 0; i < runningAndSuspendedPair[array]->Length(); ++i) {
+      ProcessedMediaStream* ps =
+        (*runningAndSuspendedPair[array])[i]->AsProcessedStream();
+      if (ps) {
+        ps->mCycleMarker = NOT_VISITED;
+        ps->mIsConsumed = false;
+        ps->mInBlockingSet = false;
+      }
+    }
+  }
+}
+
+void
+MediaStreamGraphImpl::StreamSetForAudioContext(dom::AudioContext::AudioContextId aAudioContextId,
+                                  mozilla::LinkedList<MediaStream>& aStreamSet)
+{
+   nsTArray<MediaStream*>* runningAndSuspendedPair[2];
+   runningAndSuspendedPair[0] = &mStreams;
+   runningAndSuspendedPair[1] = &mSuspendedStreams;
+
+  for (uint32_t array = 0; array < 2; array++) {
+    for (uint32_t i = 0; i < runningAndSuspendedPair[array]->Length(); ++i) {
+      MediaStream* stream = (*runningAndSuspendedPair[array])[i];
+      if (aAudioContextId == stream->AudioContextId()) {
+        aStreamSet.insertFront(stream);
+      }
+    }
+  }
+}
+
+void
+MediaStreamGraphImpl::MoveStreams(AudioContextOperation aAudioContextOperation,
+                                  mozilla::LinkedList<MediaStream>& aStreamSet)
+{
+  // For our purpose, Suspend and Close are equivalent: we want to remove the
+  // streams from the set of streams that are going to be processed.
+  nsTArray<MediaStream*>& from =
+    aAudioContextOperation == AudioContextOperation::Resume ? mSuspendedStreams
+                                                            : mStreams;
+  nsTArray<MediaStream*>& to =
+    aAudioContextOperation == AudioContextOperation::Resume ? mStreams
+                                                            : mSuspendedStreams;
+
+  MediaStream* stream;
+  while ((stream = aStreamSet.getFirst())) {
+    // It is posible to not find the stream here, if there has been two
+    // suspend/resume/close calls in a row.
+    auto i = from.IndexOf(stream);
+    if (i != from.NoIndex) {
+      from.RemoveElementAt(i);
+      to.AppendElement(stream);
+    }
+
+    // If streams got added during a period where an AudioContext was suspended,
+    // set their buffer start time to the appropriate value now:
+    if (aAudioContextOperation == AudioContextOperation::Resume &&
+        stream->mBufferStartTime == START_TIME_DELAYED) {
+      stream->mBufferStartTime = IterationEnd();
+    }
+
+    stream->remove();
+  }
+  STREAM_LOG(PR_LOG_DEBUG, ("Moving streams between suspended and running"
+      "state: mStreams: %d, mSuspendedStreams: %d\n", mStreams.Length(),
+      mSuspendedStreams.Length()));
+#ifdef DEBUG
+  // The intersection of the two arrays should be null.
+  for (uint32_t i = 0; i < mStreams.Length(); i++) {
+    for (uint32_t j = 0; j < mSuspendedStreams.Length(); j++) {
+      MOZ_ASSERT(
+        mStreams[i] != mSuspendedStreams[j],
+        "The suspended stream set and running stream set are not disjoint.");
+    }
+  }
+#endif
+}
+
+void
+MediaStreamGraphImpl::AudioContextOperationCompleted(MediaStream* aStream,
+                                                     void* aPromise,
+                                                     AudioContextOperation aOperation)
+{
+  // This can be called from the thread created to do cubeb operation, or the
+  // MSG thread. The pointers passed back here are refcounted, so are still
+  // alive.
+  MonitorAutoLock lock(mMonitor);
+
+  AudioContextState state;
+  switch (aOperation) {
+    case Suspend: state = AudioContextState::Suspended; break;
+    case Resume: state = AudioContextState::Running; break;
+    case Close: state = AudioContextState::Closed; break;
+    default: MOZ_CRASH("Not handled.");
+  }
+
+  nsCOMPtr<nsIRunnable> event = new dom::StateChangeTask(
+      aStream->AsAudioNodeStream(), aPromise, state);
+  NS_DispatchToMainThread(event);
+}
+
+void
+MediaStreamGraphImpl::ApplyAudioContextOperationImpl(AudioNodeStream* aStream,
+                                               AudioContextOperation aOperation,
+                                               void* aPromise)
+{
+  MOZ_ASSERT(CurrentDriver()->OnThread());
+  mozilla::LinkedList<MediaStream> streamSet;
+
+  SetStreamOrderDirty();
+
+  ResetVisitedStreamState();
+
+  StreamSetForAudioContext(aStream->AudioContextId(), streamSet);
+
+  MoveStreams(aOperation, streamSet);
+  MOZ_ASSERT(!streamSet.getFirst(),
+      "Streams should be removed from the list after having been moved.");
+
+  // If we have suspended the last AudioContext, and we don't have other
+  // streams that have audio, this graph will automatically switch to a
+  // SystemCallbackDriver, because it can't find a MediaStream that has an audio
+  // track. When resuming, force switching to an AudioCallbackDriver. It would
+  // have happened at the next iteration anyways, but doing this now save
+  // some time.
+  if (aOperation == AudioContextOperation::Resume) {
+    if (!CurrentDriver()->AsAudioCallbackDriver()) {
+      AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+      driver->EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
+      mMixer.AddCallback(driver);
+      CurrentDriver()->SwitchAtNextIteration(driver);
+    } else {
+      // We are resuming a context, but we are already using an
+      // AudioCallbackDriver, we can resolve the promise now.
+      AudioContextOperationCompleted(aStream, aPromise, aOperation);
+    }
+  }
+  // Close, suspend: check if we are going to switch to a
+  // SystemAudioCallbackDriver, and pass the promise to the AudioCallbackDriver
+  // if that's the case, so it can notify the content.
+  // This is the same logic as in UpdateStreamOrder, but it's simpler to have it
+  // here as well so we don't have to store the Promise(s) on the Graph.
+  if (aOperation != AudioContextOperation::Resume) {
+    bool audioTrackPresent = false;
+    for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+      MediaStream* stream = mStreams[i];
+      if (stream->AsAudioNodeStream()) {
+        audioTrackPresent = true;
+      }
+      for (StreamBuffer::TrackIter tracks(stream->GetStreamBuffer(), MediaSegment::AUDIO);
+          !tracks.IsEnded(); tracks.Next()) {
+        audioTrackPresent = true;
+      }
+    }
+    if (!audioTrackPresent && CurrentDriver()->AsAudioCallbackDriver()) {
+      CurrentDriver()->AsAudioCallbackDriver()->
+        EnqueueStreamAndPromiseForOperation(aStream, aPromise, aOperation);
+
+      SystemClockDriver* driver = new SystemClockDriver(this);
+      CurrentDriver()->SwitchAtNextIteration(driver);
+    } else {
+      // We are closing or suspending an AudioContext, but something else is
+      // using the audio stream, we can resolve the promise now.
+      AudioContextOperationCompleted(aStream, aPromise, aOperation);
+    }
+  }
+}
+
+void
+MediaStreamGraph::ApplyAudioContextOperation(AudioNodeStream* aNodeStream,
+                                             AudioContextOperation aOperation,
+                                             void* aPromise)
+{
+  class AudioContextOperationControlMessage : public ControlMessage
+  {
+  public:
+    AudioContextOperationControlMessage(AudioNodeStream* aStream,
+                                        AudioContextOperation aOperation,
+                                        void* aPromise)
+      : ControlMessage(aStream)
+      , mAudioContextOperation(aOperation)
+      , mPromise(aPromise)
+    {
+    }
+    virtual void Run()
+    {
+      mStream->GraphImpl()->ApplyAudioContextOperationImpl(
+        mStream->AsAudioNodeStream(), mAudioContextOperation, mPromise);
+    }
+    virtual void RunDuringShutdown()
+    {
+      MOZ_ASSERT(false, "We should be reviving the graph?");
+    }
+
+  private:
+    AudioContextOperation mAudioContextOperation;
+    void* mPromise;
+  };
+
+  MediaStreamGraphImpl* graphImpl = static_cast<MediaStreamGraphImpl*>(this);
+  graphImpl->AppendMessage(
+    new AudioContextOperationControlMessage(aNodeStream, aOperation, aPromise));
+}
+
 bool
 MediaStreamGraph::IsNonRealtime() const
 {
   const MediaStreamGraphImpl* impl = static_cast<const MediaStreamGraphImpl*>(this);
   MediaStreamGraphImpl* graph;
 
   return !gGraphs.Get(impl->AudioChannel(), &graph) || graph != impl;
 }
--- a/dom/media/MediaStreamGraph.h
+++ b/dom/media/MediaStreamGraph.h
@@ -17,16 +17,17 @@
 #include "VideoSegment.h"
 #include "MainThreadUtils.h"
 #include "MediaTaskQueue.h"
 #include "nsAutoRef.h"
 #include "GraphDriver.h"
 #include <speex/speex_resampler.h>
 #include "mozilla/dom/AudioChannelBinding.h"
 #include "DOMMediaStream.h"
+#include "AudioContext.h"
 
 class nsIRunnable;
 
 template <>
 class nsAutoRefTraits<SpeexResamplerState> : public nsPointerRefTraits<SpeexResamplerState>
 {
   public:
   static void Release(SpeexResamplerState* aState) { speex_resampler_destroy(aState); }
@@ -313,16 +314,17 @@ class CameraPreviewMediaStream;
  * for those objects in arbitrary order and the MediaStreamGraph has to be able
  * to handle this.
  */
 class MediaStream : public mozilla::LinkedListElement<MediaStream> {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaStream)
 
   explicit MediaStream(DOMMediaStream* aWrapper);
+  virtual dom::AudioContext::AudioContextId AudioContextId() const { return 0; }
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~MediaStream()
   {
     MOZ_COUNT_DTOR(MediaStream);
     NS_ASSERTION(mMainThreadDestroyed, "Should have been destroyed already");
     NS_ASSERTION(mMainThreadListeners.IsEmpty(),
@@ -359,16 +361,18 @@ public:
   // Since a stream can be played multiple ways, we need to be able to
   // play to multiple VideoFrameContainers.
   // Only the first enabled video track is played.
   virtual void AddVideoOutput(VideoFrameContainer* aContainer);
   virtual void RemoveVideoOutput(VideoFrameContainer* aContainer);
   // Explicitly block. Useful for example if a media element is pausing
   // and we need to stop its stream emitting its buffered data.
   virtual void ChangeExplicitBlockerCount(int32_t aDelta);
+  void BlockStreamIfNeeded();
+  void UnblockStreamIfNeeded();
   // Events will be dispatched by calling methods of aListener.
   virtual void AddListener(MediaStreamListener* aListener);
   virtual void RemoveListener(MediaStreamListener* aListener);
   // A disabled track has video replaced by black, and audio replaced by
   // silence.
   void SetTrackEnabled(TrackID aTrackID, bool aEnabled);
   // Events will be dispatched by calling methods of aListener. It is the
   // responsibility of the caller to remove aListener before it is destroyed.
@@ -460,16 +464,32 @@ public:
   void RemoveVideoOutputImpl(VideoFrameContainer* aContainer)
   {
     mVideoOutputs.RemoveElement(aContainer);
   }
   void ChangeExplicitBlockerCountImpl(GraphTime aTime, int32_t aDelta)
   {
     mExplicitBlockerCount.SetAtAndAfter(aTime, mExplicitBlockerCount.GetAt(aTime) + aDelta);
   }
+  void BlockStreamIfNeededImpl(GraphTime aTime)
+  {
+    bool blocked = mExplicitBlockerCount.GetAt(aTime) > 0;
+    if (blocked) {
+      return;
+    }
+    ChangeExplicitBlockerCountImpl(aTime, 1);
+  }
+  void UnblockStreamIfNeededImpl(GraphTime aTime)
+  {
+    bool blocked = mExplicitBlockerCount.GetAt(aTime) > 0;
+    if (!blocked) {
+      return;
+    }
+    ChangeExplicitBlockerCountImpl(aTime, -1);
+  }
   void AddListenerImpl(already_AddRefed<MediaStreamListener> aListener);
   void RemoveListenerImpl(MediaStreamListener* aListener);
   void RemoveAllListenersImpl();
   virtual void SetTrackEnabledImpl(TrackID aTrackID, bool aEnabled);
   /**
    * Returns true when this stream requires the contents of its inputs even if
    * its own outputs are not being consumed. This is used to signal inputs to
    * this stream that they are being consumed; when they're not being consumed,
@@ -1222,16 +1242,31 @@ public:
   AudioNodeStream* CreateAudioNodeStream(AudioNodeEngine* aEngine,
                                          AudioNodeStreamKind aKind,
                                          TrackRate aSampleRate = 0);
 
   AudioNodeExternalInputStream*
   CreateAudioNodeExternalInputStream(AudioNodeEngine* aEngine,
                                      TrackRate aSampleRate = 0);
 
+  /* From the main thread, ask the MSG to send back an event when the graph
+   * thread is running, and audio is being processed. */
+  void NotifyWhenGraphStarted(AudioNodeStream* aNodeStream);
+  /* From the main thread, suspend, resume or close an AudioContext.
+   * aNodeStream is the stream of the DestinationNode of the AudioContext.
+   *
+   * This can possibly pause the graph thread, releasing system resources, if
+   * all streams have been suspended/closed.
+   *
+   * When the operation is complete, aPromise is resolved.
+   */
+  void ApplyAudioContextOperation(AudioNodeStream* aNodeStream,
+                                  dom::AudioContextOperation aState,
+                                  void * aPromise);
+
   bool IsNonRealtime() const;
   /**
    * Start processing non-realtime for a specific number of ticks.
    */
   void StartNonRealtimeProcessing(uint32_t aTicksToProcess);
 
   /**
    * Media graph thread only.
--- a/dom/media/MediaStreamGraphImpl.h
+++ b/dom/media/MediaStreamGraphImpl.h
@@ -243,16 +243,59 @@ public:
   /**
    * Update "have enough data" flags in aStream.
    */
   void UpdateBufferSufficiencyState(SourceMediaStream* aStream);
   /**
    * Mark aStream and all its inputs (recursively) as consumed.
    */
   static void MarkConsumed(MediaStream* aStream);
+
+  /**
+   * Given the Id of an AudioContext, return the set of all MediaStreams that
+   * are part of this context.
+   */
+  void StreamSetForAudioContext(dom::AudioContext::AudioContextId aAudioContextId,
+                                mozilla::LinkedList<MediaStream>& aStreamSet);
+
+  /**
+   * Called when a suspend/resume/close operation has been completed, on the
+   * graph thread.
+   */
+  void AudioContextOperationCompleted(MediaStream* aStream,
+                                      void* aPromise,
+                                      dom::AudioContextOperation aOperation);
+
+  /**
+   * Apply and AudioContext operation (suspend/resume/closed), on the graph
+   * thread.
+   */
+  void ApplyAudioContextOperationImpl(AudioNodeStream* aStream,
+                                      dom::AudioContextOperation aOperation,
+                                      void* aPromise);
+
+  /*
+   * Move streams from the mStreams to mSuspendedStream if suspending/closing an
+   * AudioContext, or the inverse when resuming an AudioContext.
+   */
+  void MoveStreams(dom::AudioContextOperation aAudioContextOperation,
+                   mozilla::LinkedList<MediaStream>& aStreamSet);
+
+  /*
+   * Reset some state about the streams before suspending them, or resuming
+   * them.
+   */
+  void ResetVisitedStreamState();
+
+  /*
+   * True if a stream is suspended, that is, is not in mStreams, but in
+   * mSuspendedStream.
+   */
+  bool StreamSuspended(MediaStream* aStream);
+
   /**
    * Sort mStreams so that every stream not in a cycle is after any streams
    * it depends on, and every stream in a cycle is marked as being in a cycle.
    * Also sets mIsConsumed on every stream.
    */
   void UpdateStreamOrder();
   /**
    * Compute the blocking states of streams from mStateComputedTime
@@ -363,17 +406,20 @@ public:
   void FinishStream(MediaStream* aStream);
   /**
    * Compute how much stream data we would like to buffer for aStream.
    */
   StreamTime GetDesiredBufferEnd(MediaStream* aStream);
   /**
    * Returns true when there are no active streams.
    */
-  bool IsEmpty() { return mStreams.IsEmpty() && mPortCount == 0; }
+  bool IsEmpty()
+  {
+    return mStreams.IsEmpty() && mSuspendedStreams.IsEmpty() && mPortCount == 0;
+  }
 
   // For use by control messages, on graph thread only.
   /**
    * Identify which graph update index we are currently processing.
    */
   int64_t GetProcessingGraphUpdateIndex() { return mProcessingGraphUpdateIndex; }
   /**
    * Add aStream to the graph and initializes its graph-specific state.
@@ -483,16 +529,23 @@ public:
 
   /**
    * The graph keeps a reference to each stream.
    * References are maintained manually to simplify reordering without
    * unnecessary thread-safe refcount changes.
    */
   nsTArray<MediaStream*> mStreams;
   /**
+   * This stores MediaStreams that are part of suspended AudioContexts.
+   * mStreams and mSuspendStream are disjoint sets: a stream is either suspended
+   * or not suspended. Suspended streams are not ordered in UpdateStreamOrder,
+   * and are therefore not doing any processing.
+   */
+  nsTArray<MediaStream*> mSuspendedStreams;
+  /**
    * Streams from mFirstCycleBreaker to the end of mStreams produce output
    * before they receive input.  They correspond to DelayNodes that are in
    * cycles.
    */
   uint32_t mFirstCycleBreaker;
   /**
    * Date of the last time we updated the main thread with the graph state.
    */
--- a/dom/media/MediaTaskQueue.cpp
+++ b/dom/media/MediaTaskQueue.cpp
@@ -35,52 +35,36 @@ MediaTaskQueue::MediaTaskQueue(Temporary
 
 MediaTaskQueue::~MediaTaskQueue()
 {
   MonitorAutoLock mon(mQueueMonitor);
   MOZ_ASSERT(mIsShutdown);
   MOZ_COUNT_DTOR(MediaTaskQueue);
 }
 
-nsresult
-MediaTaskQueue::Dispatch(TemporaryRef<nsIRunnable> aRunnable)
-{
-  AssertInTailDispatchIfNeeded(); // Do this before acquiring the monitor.
-  MonitorAutoLock mon(mQueueMonitor);
-  return DispatchLocked(aRunnable, AbortIfFlushing);
-}
-
 TaskDispatcher&
 MediaTaskQueue::TailDispatcher()
 {
   MOZ_ASSERT(IsCurrentThreadIn());
   MOZ_ASSERT(mTailDispatcher);
   return *mTailDispatcher;
 }
 
 nsresult
-MediaTaskQueue::ForceDispatch(TemporaryRef<nsIRunnable> aRunnable)
-{
-  AssertInTailDispatchIfNeeded(); // Do this before acquiring the monitor.
-  MonitorAutoLock mon(mQueueMonitor);
-  return DispatchLocked(aRunnable, Forced);
-}
-
-nsresult
-MediaTaskQueue::DispatchLocked(TemporaryRef<nsIRunnable> aRunnable,
-                               DispatchMode aMode)
+MediaTaskQueue::DispatchLocked(already_AddRefed<nsIRunnable> aRunnable, DispatchMode aMode)
 {
   mQueueMonitor.AssertCurrentThreadOwns();
+  nsCOMPtr<nsIRunnable> r = aRunnable;
   if (mIsFlushing && aMode == AbortIfFlushing) {
     return NS_ERROR_ABORT;
   }
   if (mIsShutdown) {
     return NS_ERROR_FAILURE;
   }
-  mTasks.push(TaskQueueEntry(aRunnable, aMode == Forced));
+  mTasks.push(r.forget());
   if (mIsRunning) {
     return NS_OK;
   }
   RefPtr<nsIRunnable> runner(new Runner(this));
   nsresult rv = mPool->Dispatch(runner, NS_DISPATCH_NORMAL);
   if (NS_FAILED(rv)) {
     NS_WARNING("Failed to dispatch runnable to run MediaTaskQueue");
     return rv;
@@ -104,36 +88,34 @@ public:
     {
       MonitorAutoLock mon(mMonitor);
       mDone = true;
       mon.NotifyAll();
     }
     return rv;
   }
 
-  nsresult WaitUntilDone() {
+  void WaitUntilDone() {
     MonitorAutoLock mon(mMonitor);
     while (!mDone) {
       mon.Wait();
     }
-    return NS_OK;
   }
 private:
   RefPtr<nsIRunnable> mRunnable;
   Monitor mMonitor;
   bool mDone;
 };
 
-nsresult
+void
 MediaTaskQueue::SyncDispatch(TemporaryRef<nsIRunnable> aRunnable) {
   NS_WARNING("MediaTaskQueue::SyncDispatch is dangerous and deprecated. Stop using this!");
   RefPtr<MediaTaskQueueSyncRunnable> task(new MediaTaskQueueSyncRunnable(aRunnable));
-  nsresult rv = Dispatch(task);
-  NS_ENSURE_SUCCESS(rv, rv);
-  return task->WaitUntilDone();
+  Dispatch(task);
+  task->WaitUntilDone();
 }
 
 void
 MediaTaskQueue::AwaitIdle()
 {
   MonitorAutoLock mon(mQueueMonitor);
   AwaitIdleLocked();
 }
@@ -182,35 +164,32 @@ FlushableMediaTaskQueue::Flush()
 
 nsresult
 FlushableMediaTaskQueue::FlushAndDispatch(TemporaryRef<nsIRunnable> aRunnable)
 {
   AssertInTailDispatchIfNeeded(); // Do this before acquiring the monitor.
   MonitorAutoLock mon(mQueueMonitor);
   AutoSetFlushing autoFlush(this);
   FlushLocked();
-  nsresult rv = DispatchLocked(aRunnable, IgnoreFlushing);
+  nsCOMPtr<nsIRunnable> r = dont_AddRef(aRunnable.take());
+  nsresult rv = DispatchLocked(r.forget(), IgnoreFlushing);
   NS_ENSURE_SUCCESS(rv, rv);
   AwaitIdleLocked();
   return NS_OK;
 }
 
 void
 FlushableMediaTaskQueue::FlushLocked()
 {
   mQueueMonitor.AssertCurrentThreadOwns();
   MOZ_ASSERT(mIsFlushing);
 
-  // Clear the tasks, but preserve those with mForceDispatch by re-appending
-  // them to the queue.
-  size_t numTasks = mTasks.size();
-  for (size_t i = 0; i < numTasks; ++i) {
-    if (mTasks.front().mForceDispatch) {
-      mTasks.push(mTasks.front());
-    }
+  // Clear the tasks. If this strikes you as awful, stop using a
+  // FlushableMediaTaskQueue.
+  while (!mTasks.empty()) {
     mTasks.pop();
   }
 }
 
 bool
 MediaTaskQueue::IsEmpty()
 {
   MonitorAutoLock mon(mQueueMonitor);
@@ -233,17 +212,17 @@ MediaTaskQueue::Runner::Run()
     MonitorAutoLock mon(mQueue->mQueueMonitor);
     MOZ_ASSERT(mQueue->mIsRunning);
     if (mQueue->mTasks.size() == 0) {
       mQueue->mIsRunning = false;
       mQueue->mShutdownPromise.ResolveIfExists(true, __func__);
       mon.NotifyAll();
       return NS_OK;
     }
-    event = mQueue->mTasks.front().mRunnable;
+    event = mQueue->mTasks.front();
     mQueue->mTasks.pop();
   }
   MOZ_ASSERT(event);
 
   // Note that dropping the queue monitor before running the task, and
   // taking the monitor again after the task has run ensures we have memory
   // fences enforced. This means that if the object we're calling wasn't
   // designed to be threadsafe, it will be, provided we're only calling it
--- a/dom/media/MediaTaskQueue.h
+++ b/dom/media/MediaTaskQueue.h
@@ -6,16 +6,17 @@
 
 #ifndef MediaTaskQueue_h_
 #define MediaTaskQueue_h_
 
 #include <queue>
 #include "mozilla/RefPtr.h"
 #include "mozilla/Monitor.h"
 #include "mozilla/ThreadLocal.h"
+#include "mozilla/unused.h"
 #include "SharedThreadPool.h"
 #include "nsThreadUtils.h"
 #include "MediaPromise.h"
 #include "TaskDispatcher.h"
 
 class nsIRunnable;
 
 namespace mozilla {
@@ -35,17 +36,22 @@ public:
   static void InitStatics();
 
   // Returns the task queue that the caller is currently running in, or null
   // if the caller is not running in a MediaTaskQueue.
   static MediaTaskQueue* GetCurrentQueue() { return sCurrentQueueTLS.get(); }
 
   explicit MediaTaskQueue(TemporaryRef<SharedThreadPool> aPool, bool aRequireTailDispatch = false);
 
-  nsresult Dispatch(TemporaryRef<nsIRunnable> aRunnable);
+  void Dispatch(TemporaryRef<nsIRunnable> aRunnable,
+                DispatchFailureHandling aFailureHandling = AssertDispatchSuccess)
+  {
+    nsCOMPtr<nsIRunnable> r = dont_AddRef(aRunnable.take());
+    return Dispatch(r.forget(), aFailureHandling);
+  }
 
   // Returns a TaskDispatcher that will dispatch its tasks when the currently-
   // running tasks pops off the stack.
   //
   // May only be called when running within the task queue it is invoked up.
   TaskDispatcher& TailDispatcher();
 
   // Returns true if this task queue requires all dispatches performed by its
@@ -69,29 +75,28 @@ public:
     MOZ_ASSERT(!currentQueue->mTailDispatcher,
                "Not allowed to dispatch tasks directly from this task queue - use TailDispatcher()");
   }
 #else
   static void AssertInTailDispatchIfNeeded() {}
 #endif
 
   // For AbstractThread.
-  nsresult Dispatch(already_AddRefed<nsIRunnable> aRunnable) override
+  void Dispatch(already_AddRefed<nsIRunnable> aRunnable,
+                DispatchFailureHandling aFailureHandling = AssertDispatchSuccess) override
   {
-    RefPtr<nsIRunnable> r(aRunnable);
-    return ForceDispatch(r);
+    MonitorAutoLock mon(mQueueMonitor);
+    nsresult rv = DispatchLocked(Move(aRunnable), AbortIfFlushing);
+    MOZ_DIAGNOSTIC_ASSERT(aFailureHandling == DontAssertDispatchSuccess || NS_SUCCEEDED(rv));
+    unused << rv;
   }
 
-  // This should only be used for things that absolutely can't afford to be
-  // flushed. Normal operations should use Dispatch.
-  nsresult ForceDispatch(TemporaryRef<nsIRunnable> aRunnable);
-
   // DEPRECATED! Do not us, if a flush happens at the same time, this function
   // can hang and block forever!
-  nsresult SyncDispatch(TemporaryRef<nsIRunnable> aRunnable);
+  void SyncDispatch(TemporaryRef<nsIRunnable> aRunnable);
 
   // Puts the queue in a shutdown state and returns immediately. The queue will
   // remain alive at least until all the events are drained, because the Runners
   // hold a strong reference to the task queue, and one of them is always held
   // by the threadpool event queue when the task queue is non-empty.
   //
   // The returned promise is resolved when the queue goes empty.
   nsRefPtr<ShutdownPromise> BeginShutdown();
@@ -113,37 +118,27 @@ protected:
   virtual ~MediaTaskQueue();
 
 
   // Blocks until all task finish executing. Called internally by methods
   // that need to wait until the task queue is idle.
   // mQueueMonitor must be held.
   void AwaitIdleLocked();
 
-  enum DispatchMode { AbortIfFlushing, IgnoreFlushing, Forced };
+  enum DispatchMode { AbortIfFlushing, IgnoreFlushing };
 
-  nsresult DispatchLocked(TemporaryRef<nsIRunnable> aRunnable,
-                          DispatchMode aMode);
+  nsresult DispatchLocked(already_AddRefed<nsIRunnable> aRunnable, DispatchMode aMode);
 
   RefPtr<SharedThreadPool> mPool;
 
   // Monitor that protects the queue and mIsRunning;
   Monitor mQueueMonitor;
 
-  struct TaskQueueEntry {
-    RefPtr<nsIRunnable> mRunnable;
-    bool mForceDispatch;
-
-    explicit TaskQueueEntry(TemporaryRef<nsIRunnable> aRunnable,
-                            bool aForceDispatch = false)
-      : mRunnable(aRunnable), mForceDispatch(aForceDispatch) {}
-  };
-
   // Queue of tasks to run.
-  std::queue<TaskQueueEntry> mTasks;
+  std::queue<nsCOMPtr<nsIRunnable>> mTasks;
 
   // The thread currently running the task queue. We store a reference
   // to this so that IsCurrentThreadIn() can tell if the current thread
   // is the thread currently running in the task queue.
   //
   // This may be read on any thread, but may only be written on mRunningThread.
   // The thread can't die while we're running in it, and we only use it for
   // pointer-comparison with the current thread anyway - so we make it atomic
@@ -212,16 +207,18 @@ protected:
 
 class FlushableMediaTaskQueue : public MediaTaskQueue
 {
 public:
   explicit FlushableMediaTaskQueue(TemporaryRef<SharedThreadPool> aPool) : MediaTaskQueue(aPool) {}
   nsresult FlushAndDispatch(TemporaryRef<nsIRunnable> aRunnable);
   void Flush();
 
+  bool IsDispatchReliable() override { return false; }
+
 private:
 
   class MOZ_STACK_CLASS AutoSetFlushing
   {
   public:
     explicit AutoSetFlushing(FlushableMediaTaskQueue* aTaskQueue) : mTaskQueue(aTaskQueue)
     {
       mTaskQueue->mQueueMonitor.AssertCurrentThreadOwns();
--- a/dom/media/TaskDispatcher.h
+++ b/dom/media/TaskDispatcher.h
@@ -38,17 +38,17 @@ class TaskDispatcher
 public:
   TaskDispatcher() {}
   virtual ~TaskDispatcher() {}
 
   virtual void AddStateChangeTask(AbstractThread* aThread,
                                   already_AddRefed<nsIRunnable> aRunnable) = 0;
   virtual void AddTask(AbstractThread* aThread,
                        already_AddRefed<nsIRunnable> aRunnable,
-                       bool aAssertDispatchSuccess = true) = 0;
+                       AbstractThread::DispatchFailureHandling aFailureHandling = AbstractThread::AssertDispatchSuccess) = 0;
 
 #ifdef DEBUG
   void AssertIsTailDispatcherIfRequired();
 #else
   void AssertIsTailDispatcherIfRequired() {}
 #endif
 };
 
@@ -60,60 +60,60 @@ class MOZ_STACK_CLASS AutoTaskDispatcher
 {
 public:
   AutoTaskDispatcher() {}
   ~AutoTaskDispatcher()
   {
     for (size_t i = 0; i < mTaskGroups.Length(); ++i) {
       UniquePtr<PerThreadTaskGroup> group(Move(mTaskGroups[i]));
       nsRefPtr<AbstractThread> thread = group->mThread;
-      bool assertDispatchSuccess = group->mAssertDispatchSuccess;
+
+      AbstractThread::DispatchFailureHandling failureHandling = group->mFailureHandling;
       nsCOMPtr<nsIRunnable> r = new TaskGroupRunnable(Move(group));
-      nsresult rv = thread->Dispatch(r.forget());
-      MOZ_DIAGNOSTIC_ASSERT(!assertDispatchSuccess || NS_SUCCEEDED(rv));
-      unused << assertDispatchSuccess;
-      unused << rv;
+      thread->Dispatch(r.forget(), failureHandling);
     }
   }
 
   void AddStateChangeTask(AbstractThread* aThread,
                           already_AddRefed<nsIRunnable> aRunnable) override
   {
     EnsureTaskGroup(aThread).mStateChangeTasks.AppendElement(aRunnable);
   }
 
   void AddTask(AbstractThread* aThread,
                already_AddRefed<nsIRunnable> aRunnable,
-               bool aAssertDispatchSuccess) override
+               AbstractThread::DispatchFailureHandling aFailureHandling) override
   {
     PerThreadTaskGroup& group = EnsureTaskGroup(aThread);
     group.mRegularTasks.AppendElement(aRunnable);
 
     // The task group needs to assert dispatch success if any of the runnables
     // it's dispatching want to assert it.
-    group.mAssertDispatchSuccess = group.mAssertDispatchSuccess || aAssertDispatchSuccess;
+    if (aFailureHandling == AbstractThread::AssertDispatchSuccess) {
+      group.mFailureHandling = AbstractThread::AssertDispatchSuccess;
+    }
   }
 
 private:
 
   struct PerThreadTaskGroup
   {
   public:
     explicit PerThreadTaskGroup(AbstractThread* aThread)
-      : mThread(aThread), mAssertDispatchSuccess(false)
+      : mThread(aThread), mFailureHandling(AbstractThread::DontAssertDispatchSuccess)
     {
       MOZ_COUNT_CTOR(PerThreadTaskGroup);
     }
 
     ~PerThreadTaskGroup() { MOZ_COUNT_DTOR(PerThreadTaskGroup); }
 
     nsRefPtr<AbstractThread> mThread;
     nsTArray<nsCOMPtr<nsIRunnable>> mStateChangeTasks;
     nsTArray<nsCOMPtr<nsIRunnable>> mRegularTasks;
-    bool mAssertDispatchSuccess;
+    AbstractThread::DispatchFailureHandling mFailureHandling;
   };
 
   class TaskGroupRunnable : public nsRunnable
   {
     public:
       explicit TaskGroupRunnable(UniquePtr<PerThreadTaskGroup>&& aTasks) : mTasks(Move(aTasks)) {}
 
       NS_IMETHODIMP Run()
--- a/dom/media/TrackUnionStream.cpp
+++ b/dom/media/TrackUnionStream.cpp
@@ -19,20 +19,20 @@
 #include "prlog.h"
 #include "mozilla/Attributes.h"
 #include "TrackUnionStream.h"
 #include "ImageContainer.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
 #include "AudioNodeExternalInputStream.h"
+#include "webaudio/MediaStreamAudioDestinationNode.h"
 #include <algorithm>
 #include "DOMMediaStream.h"
 #include "GeckoProfiler.h"
-#include "mozilla/unused.h"
 #ifdef MOZ_WEBRTC
 #include "AudioOutputObserver.h"
 #endif
 
 using namespace mozilla::layers;
 using namespace mozilla::dom;
 using namespace mozilla::gfx;
 
@@ -270,22 +270,26 @@ TrackUnionStream::TrackUnionStream(DOMMe
       if (interval.mInputIsBlocked) {
         // Maybe the input track ended?
         segment->AppendNullData(ticks);
         STREAM_LOG(PR_LOG_DEBUG+1, ("TrackUnionStream %p appending %lld ticks of null data to track %d",
                    this, (long long)ticks, outputTrack->GetID()));
       } else if (InMutedCycle()) {
         segment->AppendNullData(ticks);
       } else {
-        MOZ_ASSERT(outputTrack->GetEnd() == GraphTimeToStreamTime(interval.mStart),
-                   "Samples missing");
-        StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
-        segment->AppendSlice(*aInputTrack->GetSegment(),
-                             std::min(inputTrackEndPoint, inputStart),
-                             std::min(inputTrackEndPoint, inputEnd));
+        if (GraphImpl()->StreamSuspended(source)) {
+          segment->AppendNullData(aTo - aFrom);
+        } else {
+          MOZ_ASSERT(outputTrack->GetEnd() == GraphTimeToStreamTime(interval.mStart),
+                     "Samples missing");
+          StreamTime inputStart = source->GraphTimeToStreamTime(interval.mStart);
+          segment->AppendSlice(*aInputTrack->GetSegment(),
+                               std::min(inputTrackEndPoint, inputStart),
+                               std::min(inputTrackEndPoint, inputEnd));
+        }
       }
       ApplyTrackDisabling(outputTrack->GetID(), segment);
       for (uint32_t j = 0; j < mListeners.Length(); ++j) {
         MediaStreamListener* l = mListeners[j];
         l->NotifyQueuedTrackChanges(Graph(), outputTrack->GetID(),
                                     outputStart, 0, *segment);
       }
       outputTrack->GetSegment()->AppendFrom(segment);
--- a/dom/media/mediasource/TrackBuffer.cpp
+++ b/dom/media/mediasource/TrackBuffer.cpp
@@ -559,22 +559,17 @@ TrackBuffer::QueueInitializeDecoder(Sour
     mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
     return false;
   }
 
   RefPtr<nsIRunnable> task =
     NS_NewRunnableMethodWithArg<SourceBufferDecoder*>(this,
                                                       &TrackBuffer::InitializeDecoder,
                                                       aDecoder);
-  if (NS_FAILED(mTaskQueue->Dispatch(task))) {
-    MSE_DEBUG("failed to enqueue decoder initialization task");
-    RemoveDecoder(aDecoder);
-    mInitializationPromise.RejectIfExists(NS_ERROR_FAILURE, __func__);
-    return false;
-  }
+  mTaskQueue->Dispatch(task);
   return true;
 }
 
 void
 TrackBuffer::InitializeDecoder(SourceBufferDecoder* aDecoder)
 {
   if (!mParentDecoder) {
     MSE_DEBUG("decoder was shutdown. Aborting initialization.");
--- a/dom/media/tests/mochitest/identity/mochitest.ini
+++ b/dom/media/tests/mochitest/identity/mochitest.ini
@@ -1,13 +1,13 @@
 [DEFAULT]
 # strictContentSandbox - bug 1042735, Android 2.3 - bug 981881
 # won't run on b2g desktop tests - bug 1119993
 # broken HTTPS on b2g emulator - bug 1135339
-skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || android_version == '18' || (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'b2g' && toolkit == 'gonk')
+skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || android_version == '18' || (buildapp == 'b2g' && toolkit != 'gonk') || (buildapp == 'b2g' && toolkit == 'gonk') || buildapp == 'mulet'
 support-files =
   /.well-known/idp-proxy/idp.js
   identityPcTest.js
 
 [test_idpproxy.html]
 support-files =
   /.well-known/idp-proxy/idp-redirect-http.js
   /.well-known/idp-proxy/idp-redirect-http.js^headers^
--- a/dom/media/tests/mochitest/mochitest.ini
+++ b/dom/media/tests/mochitest/mochitest.ini
@@ -1,193 +1,193 @@
 [DEFAULT]
 # strictContentSandbox - bug 1042735, Android 2.3 - bug 981881
-skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || android_version == '18'
+skip-if = (os == 'win' && strictContentSandbox) || android_version == '10' || android_version == '18' || (buildapp == 'mulet')
 support-files =
   head.js
   dataChannel.js
   mediaStreamPlayback.js
   network.js
   nonTrickleIce.js
   pc.js
   templates.js
   NetworkPreparationChromeScript.js
   blacksilence.js
   turnConfig.js
 
 [test_dataChannel_basicAudio.html]
-skip-if = toolkit == 'gonk' # Bug 962984 for debug, bug 963244 for opt
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Bug 962984 for debug, bug 963244 for opt
 [test_dataChannel_basicAudioVideo.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicAudioVideoNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicAudioVideoCombined.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_basicDataOnly.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_dataChannel_basicVideo.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_dataChannel_bug1013809.html]
-skip-if = toolkit == 'gonk' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_dataChannel_noOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_getUserMedia_basicAudio.html]
-skip-if = (toolkit == 'gonk' && debug) # debug-only failure
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
 [test_getUserMedia_basicVideo.html]
-skip-if = (toolkit == 'gonk' && debug) # debug-only failure
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
 [test_getUserMedia_basicVideo_playAfterLoadedmetadata.html]
-skip-if = (toolkit == 'gonk' && debug) # debug-only failure
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure
 [test_getUserMedia_basicScreenshare.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no screenshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_getUserMedia_basicWindowshare.html]
 skip-if = buildapp == 'mulet' || buildapp == 'b2g' || toolkit == 'android' # no windowshare on b2g/android # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_getUserMedia_basicVideoAudio.html]
-skip-if = (toolkit == 'gonk' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet' && debug) # debug-only failure, turned an intermittent (bug 962579) into a permanant orange
 [test_getUserMedia_constraints.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_callbacks.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' || buildapp == 'mulet' # Bug 1063290, intermittent timeout # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' || buildapp == 'mulet' # Bug 1063290, intermittent timeout # TC: Bug 1144079 - Re-enable Mulet mochitests and reftests taskcluster-specific disables.
 [test_getUserMedia_gumWithinGum.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_playAudioTwice.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_playVideoAudioTwice.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout # bug 926558, debug-only failure
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout # bug 926558, debug-only failure
 [test_getUserMedia_playVideoTwice.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_stopAudioStream.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_stopAudioStreamWithFollowupAudio.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_stopVideoAudioStream.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout # bug 926558, debug-only failure
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout # bug 926558, debug-only failure
 [test_getUserMedia_stopVideoAudioStreamWithFollowupVideoAudio.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_stopVideoStream.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_stopVideoStreamWithFollowupVideo.html]
-skip-if = toolkit == 'gonk' || toolkit == 'android' # Bug 1063290, intermittent timeout
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' || toolkit == 'android' # Bug 1063290, intermittent timeout
 [test_getUserMedia_peerIdentity.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 1021776, too --ing slow on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 1021776, too --ing slow on b2g)
 [test_peerConnection_addCandidateInHaveLocalOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_basicAudio.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_basicAudioVideo.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicAudioVideoCombined.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicAudioVideoNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_basicVideo.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_basicScreenshare.html]
 # no screenshare on b2g/android
 # frequent timeouts/crashes on e10s (bug 1048455)
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_basicWindowshare.html]
 # no screenshare on b2g/android
 # frequent timeouts/crashes on e10s (bug 1048455)
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || toolkit == 'android' || e10s # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_basicH264Video.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_bug822674.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_bug825703.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_bug827843.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_bug834153.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_bug1013809.html]
-skip-if = toolkit == 'gonk' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g emulator seems to be too slow (Bug 1016498 and 1008080)
 [test_peerConnection_bug1042791.html]
 skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC
 [test_peerConnection_capturedVideo.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_close.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_errorCallbacks.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_forwarding_basicAudioVideoCombined.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_noTrickleAnswer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_noTrickleOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_noTrickleOfferAnswer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_offerRequiresReceiveAudio.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideo.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_offerRequiresReceiveVideoAudio.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_promiseSendOnly.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_callbacks.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_replaceTrack.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_syncSetDescription.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_setLocalAnswerInHaveLocalOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_setLocalAnswerInStable.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_setLocalOfferInHaveRemoteOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_setRemoteAnswerInHaveRemoteOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_setRemoteAnswerInStable.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_setRemoteOfferInHaveLocalOffer.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_throwInCallbacks.html]
-skip-if = toolkit == 'gonk' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g)
 [test_peerConnection_toJSON.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 
 [test_peerConnection_twoAudioStreams.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_twoAudioTracksInOneStream.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_twoAudioVideoStreams.html]
-skip-if = (toolkit == 'gonk') # b2g (Bug 1059867)
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet') # b2g (Bug 1059867)
 [test_peerConnection_twoAudioVideoStreamsCombined.html]
-skip-if = (toolkit == 'gonk') # b2g (Bug 1059867)
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet') # b2g (Bug 1059867)
 [test_peerConnection_twoVideoStreams.html]
-skip-if = (toolkit == 'gonk') # b2g (Bug 1059867)
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet') # b2g (Bug 1059867)
 [test_peerConnection_twoVideoTracksInOneStream.html]
-skip-if = (toolkit == 'gonk') # b2g (Bug 1059867)
+skip-if = (toolkit == 'gonk' || buildapp == 'mulet') # b2g (Bug 1059867)
 [test_peerConnection_addSecondAudioStream.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_answererAddSecondAudioStream.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_removeAudioTrack.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_removeThenAddAudioTrack.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_addSecondVideoStream.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_removeVideoTrack.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_removeThenAddVideoTrack.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_replaceVideoThenRenegotiate.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_addSecondAudioStreamNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_removeThenAddAudioTrackNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_addSecondVideoStreamNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_removeThenAddVideoTrackNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_addDataChannel.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_addDataChannelNoBundle.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 [test_peerConnection_webAudio.html]
-skip-if = toolkit == 'gonk' # b2g (Bug 1059867)
+skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867)
 
 # Bug 950317: Hack for making a cleanup hook after finishing all WebRTC cases
 [test_zmedia_cleanup.html]
--- a/dom/media/webaudio/AudioContext.cpp
+++ b/dom/media/webaudio/AudioContext.cpp
@@ -4,18 +4,18 @@
  * 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 "AudioContext.h"
 
 #include "nsPIDOMWindow.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/dom/AnalyserNode.h"
+#include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/AudioContextBinding.h"
-#include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/OfflineAudioContextBinding.h"
 #include "mozilla/dom/OwningNonNull.h"
 #include "MediaStreamGraph.h"
 #include "AudioChannelService.h"
 #include "AudioDestinationNode.h"
 #include "AudioBufferSourceNode.h"
 #include "AudioBuffer.h"
 #include "GainNode.h"
@@ -37,16 +37,20 @@
 #include "OscillatorNode.h"
 #include "nsNetUtil.h"
 #include "AudioStream.h"
 #include "mozilla/dom/Promise.h"
 
 namespace mozilla {
 namespace dom {
 
+// 0 is a special value that MediaStreams use to denote they are not part of a
+// AudioContext.
+static dom::AudioContext::AudioContextId gAudioContextId = 1;
+
 NS_IMPL_CYCLE_COLLECTION_CLASS(AudioContext)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(AudioContext)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDestination)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
   if (!tmp->mIsStarted) {
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mActiveNodes)
   }
@@ -80,22 +84,25 @@ static float GetSampleRateForAudioContex
 
 AudioContext::AudioContext(nsPIDOMWindow* aWindow,
                            bool aIsOffline,
                            AudioChannel aChannel,
                            uint32_t aNumberOfChannels,
                            uint32_t aLength,
                            float aSampleRate)
   : DOMEventTargetHelper(aWindow)
+  , mId(gAudioContextId++)
   , mSampleRate(GetSampleRateForAudioContext(aIsOffline, aSampleRate))
+  , mAudioContextState(AudioContextState::Suspended)
   , mNumberOfChannels(aNumberOfChannels)
   , mNodeCount(0)
   , mIsOffline(aIsOffline)
   , mIsStarted(!aIsOffline)
   , mIsShutDown(false)
+  , mCloseCalled(false)
 {
   aWindow->AddAudioContext(this);
 
   // Note: AudioDestinationNode needs an AudioContext that must already be
   // bound to the window.
   mDestination = new AudioDestinationNode(this, aIsOffline, aChannel,
                                           aNumberOfChannels, aLength, aSampleRate);
   // We skip calling SetIsOnlyNodeForContext and the creation of the
@@ -192,19 +199,32 @@ AudioContext::Constructor(const GlobalOb
                                                    aLength,
                                                    aSampleRate);
 
   RegisterWeakMemoryReporter(object);
 
   return object.forget();
 }
 
+bool AudioContext::CheckClosed(ErrorResult& aRv)
+{
+  if (mAudioContextState == AudioContextState::Closed) {
+    aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return true;
+  }
+  return false;
+}
+
 already_AddRefed<AudioBufferSourceNode>
-AudioContext::CreateBufferSource()
+AudioContext::CreateBufferSource(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<AudioBufferSourceNode> bufferNode =
     new AudioBufferSourceNode(this);
   return bufferNode.forget();
 }
 
 already_AddRefed<AudioBuffer>
 AudioContext::CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
                            uint32_t aLength, float aSampleRate,
@@ -242,16 +262,20 @@ bool IsValidBufferSize(uint32_t aBufferS
 already_AddRefed<MediaStreamAudioDestinationNode>
 AudioContext::CreateMediaStreamDestination(ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<MediaStreamAudioDestinationNode> node =
       new MediaStreamAudioDestinationNode(this);
   return node.forget();
 }
 
 already_AddRefed<ScriptProcessorNode>
 AudioContext::CreateScriptProcessor(uint32_t aBufferSize,
                                     uint32_t aNumberOfInputChannels,
@@ -261,32 +285,44 @@ AudioContext::CreateScriptProcessor(uint
   if ((aNumberOfInputChannels == 0 && aNumberOfOutputChannels == 0) ||
       aNumberOfInputChannels > WebAudioUtils::MaxChannelCount ||
       aNumberOfOutputChannels > WebAudioUtils::MaxChannelCount ||
       !IsValidBufferSize(aBufferSize)) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<ScriptProcessorNode> scriptProcessor =
     new ScriptProcessorNode(this, aBufferSize, aNumberOfInputChannels,
                             aNumberOfOutputChannels);
   return scriptProcessor.forget();
 }
 
 already_AddRefed<AnalyserNode>
-AudioContext::CreateAnalyser()
+AudioContext::CreateAnalyser(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<AnalyserNode> analyserNode = new AnalyserNode(this);
   return analyserNode.forget();
 }
 
 already_AddRefed<StereoPannerNode>
-AudioContext::CreateStereoPanner()
+AudioContext::CreateStereoPanner(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<StereoPannerNode> stereoPannerNode = new StereoPannerNode(this);
   return stereoPannerNode.forget();
 }
 
 already_AddRefed<MediaElementAudioSourceNode>
 AudioContext::CreateMediaElementSource(HTMLMediaElement& aMediaElement,
                                        ErrorResult& aRv)
 {
@@ -295,16 +331,21 @@ AudioContext::CreateMediaElementSource(H
     return nullptr;
   }
 #ifdef MOZ_EME
   if (aMediaElement.ContainsRestrictedContent()) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
 #endif
+
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<DOMMediaStream> stream = aMediaElement.MozCaptureStream(aRv,
                                                                    mDestination->Stream()->Graph());
   if (aRv.Failed()) {
     return nullptr;
   }
   nsRefPtr<MediaElementAudioSourceNode> mediaElementAudioSourceNode =
     new MediaElementAudioSourceNode(this, stream);
   return mediaElementAudioSourceNode.forget();
@@ -313,108 +354,154 @@ AudioContext::CreateMediaElementSource(H
 already_AddRefed<MediaStreamAudioSourceNode>
 AudioContext::CreateMediaStreamSource(DOMMediaStream& aMediaStream,
                                       ErrorResult& aRv)
 {
   if (mIsOffline) {
     aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
     return nullptr;
   }
+
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<MediaStreamAudioSourceNode> mediaStreamAudioSourceNode =
     new MediaStreamAudioSourceNode(this, &aMediaStream);
   return mediaStreamAudioSourceNode.forget();
 }
 
 already_AddRefed<GainNode>
-AudioContext::CreateGain()
+AudioContext::CreateGain(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<GainNode> gainNode = new GainNode(this);
   return gainNode.forget();
 }
 
 already_AddRefed<WaveShaperNode>
-AudioContext::CreateWaveShaper()
+AudioContext::CreateWaveShaper(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<WaveShaperNode> waveShaperNode = new WaveShaperNode(this);
   return waveShaperNode.forget();
 }
 
 already_AddRefed<DelayNode>
 AudioContext::CreateDelay(double aMaxDelayTime, ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   if (aMaxDelayTime > 0. && aMaxDelayTime < 180.) {
     nsRefPtr<DelayNode> delayNode = new DelayNode(this, aMaxDelayTime);
     return delayNode.forget();
   }
+
   aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
   return nullptr;
 }
 
 already_AddRefed<PannerNode>
-AudioContext::CreatePanner()
+AudioContext::CreatePanner(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<PannerNode> pannerNode = new PannerNode(this);
   mPannerNodes.PutEntry(pannerNode);
   return pannerNode.forget();
 }
 
 already_AddRefed<ConvolverNode>
-AudioContext::CreateConvolver()
+AudioContext::CreateConvolver(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<ConvolverNode> convolverNode = new ConvolverNode(this);
   return convolverNode.forget();
 }
 
 already_AddRefed<ChannelSplitterNode>
 AudioContext::CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv)
 {
   if (aNumberOfOutputs == 0 ||
       aNumberOfOutputs > WebAudioUtils::MaxChannelCount) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<ChannelSplitterNode> splitterNode =
     new ChannelSplitterNode(this, aNumberOfOutputs);
   return splitterNode.forget();
 }
 
 already_AddRefed<ChannelMergerNode>
 AudioContext::CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv)
 {
   if (aNumberOfInputs == 0 ||
       aNumberOfInputs > WebAudioUtils::MaxChannelCount) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<ChannelMergerNode> mergerNode =
     new ChannelMergerNode(this, aNumberOfInputs);
   return mergerNode.forget();
 }
 
 already_AddRefed<DynamicsCompressorNode>
-AudioContext::CreateDynamicsCompressor()
+AudioContext::CreateDynamicsCompressor(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<DynamicsCompressorNode> compressorNode =
     new DynamicsCompressorNode(this);
   return compressorNode.forget();
 }
 
 already_AddRefed<BiquadFilterNode>
-AudioContext::CreateBiquadFilter()
+AudioContext::CreateBiquadFilter(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<BiquadFilterNode> filterNode =
     new BiquadFilterNode(this);
   return filterNode.forget();
 }
 
 already_AddRefed<OscillatorNode>
-AudioContext::CreateOscillator()
+AudioContext::CreateOscillator(ErrorResult& aRv)
 {
+  if (CheckClosed(aRv)) {
+    return nullptr;
+  }
+
   nsRefPtr<OscillatorNode> oscillatorNode =
     new OscillatorNode(this);
   return oscillatorNode.forget();
 }
 
 already_AddRefed<PeriodicWave>
 AudioContext::CreatePeriodicWave(const Float32Array& aRealData,
                                  const Float32Array& aImagData,
@@ -592,32 +679,249 @@ AudioContext::Shutdown()
   mActiveNodes.Clear();
 
   // For offline contexts, we can destroy the MediaStreamGraph at this point.
   if (mIsOffline && mDestination) {
     mDestination->OfflineShutdown();
   }
 }
 
+AudioContextState AudioContext::State() const
+{
+  return mAudioContextState;
+}
+
+StateChangeTask::StateChangeTask(AudioContext* aAudioContext,
+                                 void* aPromise,
+                                 AudioContextState aNewState)
+  : mAudioContext(aAudioContext)
+  , mPromise(aPromise)
+  , mAudioNodeStream(nullptr)
+  , mNewState(aNewState)
+{
+  MOZ_ASSERT(NS_IsMainThread(),
+             "This constructor should be used from the main thread.");
+}
+
+StateChangeTask::StateChangeTask(AudioNodeStream* aStream,
+                                 void* aPromise,
+                                 AudioContextState aNewState)
+  : mAudioContext(nullptr)
+  , mPromise(aPromise)
+  , mAudioNodeStream(aStream)
+  , mNewState(aNewState)
+{
+  MOZ_ASSERT(!NS_IsMainThread(),
+             "This constructor should be used from the graph thread.");
+}
+
+NS_IMETHODIMP
+StateChangeTask::Run()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  if (!mAudioContext && !mAudioNodeStream) {
+    return NS_OK;
+  }
+  if (mAudioNodeStream) {
+    AudioNode* node = mAudioNodeStream->Engine()->NodeMainThread();
+    if (!node) {
+      return NS_OK;
+    }
+    mAudioContext = node->Context();
+    if (!mAudioContext) {
+      return NS_OK;
+    }
+  }
+
+  mAudioContext->OnStateChanged(mPromise, mNewState);
+  // We have can't call Release() on the AudioContext on the MSG thread, so we
+  // unref it here, on the main thread.
+  mAudioContext = nullptr;
+
+  return NS_OK;
+}
+
+/* This runnable allows to fire the "statechange" event */
+class OnStateChangeTask final : public nsRunnable
+{
+public:
+  explicit OnStateChangeTask(AudioContext* aAudioContext)
+    : mAudioContext(aAudioContext)
+  {}
+
+  NS_IMETHODIMP
+  Run() override
+  {
+    nsCOMPtr<nsPIDOMWindow> parent = do_QueryInterface(mAudioContext->GetParentObject());
+    if (!parent) {
+      return NS_ERROR_FAILURE;
+    }
+
+    nsIDocument* doc = parent->GetExtantDoc();
+    if (!doc) {
+      return NS_ERROR_FAILURE;
+    }
+
+    return nsContentUtils::DispatchTrustedEvent(doc,
+                                static_cast<DOMEventTargetHelper*>(mAudioContext),
+                                NS_LITERAL_STRING("statechange"),
+                                false, false);
+  }
+
+private:
+  nsRefPtr<AudioContext> mAudioContext;
+};
+
+
+
 void
-AudioContext::Suspend()
+AudioContext::OnStateChanged(void* aPromise, AudioContextState aNewState)
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  MOZ_ASSERT((mAudioContextState == AudioContextState::Suspended &&
+              aNewState == AudioContextState::Running)   ||
+             (mAudioContextState == AudioContextState::Running   &&
+              aNewState == AudioContextState::Suspended) ||
+             (mAudioContextState == AudioContextState::Running   &&
+              aNewState == AudioContextState::Closed)    ||
+             (mAudioContextState == AudioContextState::Suspended &&
+              aNewState == AudioContextState::Closed)    ||
+             (mAudioContextState == aNewState),
+             "Invalid AudioContextState transition");
+
+  MOZ_ASSERT(
+    mIsOffline || aPromise || aNewState == AudioContextState::Running,
+    "We should have a promise here if this is a real-time AudioContext."
+    "Or this is the first time we switch to \"running\".");
+
+  if (aPromise) {
+    Promise* promise = reinterpret_cast<Promise*>(aPromise);
+    promise->MaybeResolve(JS::UndefinedHandleValue);
+    DebugOnly<bool> rv = mPromiseGripArray.RemoveElement(promise);
+    MOZ_ASSERT(rv, "Promise wasn't in the grip array?");
+  }
+
+  if (mAudioContextState != aNewState) {
+    nsRefPtr<OnStateChangeTask> onStateChangeTask =
+      new OnStateChangeTask(this);
+    NS_DispatchToMainThread(onStateChangeTask);
+  }
+
+  mAudioContextState = aNewState;
+}
+
+already_AddRefed<Promise>
+AudioContext::Suspend(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
+  nsRefPtr<Promise> promise;
+  promise = Promise::Create(parentObject, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+  if (mIsOffline) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return promise.forget();
+  }
+
+  if (mAudioContextState == AudioContextState::Closed ||
+      mCloseCalled) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return promise.forget();
+  }
+
+  if (mAudioContextState == AudioContextState::Suspended) {
+    promise->MaybeResolve(JS::UndefinedHandleValue);
+    return promise.forget();
+  }
+
   MediaStream* ds = DestinationStream();
   if (ds) {
-    ds->ChangeExplicitBlockerCount(1);
+    ds->BlockStreamIfNeeded();
   }
+
+  mPromiseGripArray.AppendElement(promise);
+  Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
+                                      AudioContextOperation::Suspend, promise);
+
+  return promise.forget();
 }
 
-void
-AudioContext::Resume()
+already_AddRefed<Promise>
+AudioContext::Resume(ErrorResult& aRv)
 {
+  nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
+  nsRefPtr<Promise> promise;
+  promise = Promise::Create(parentObject, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
+  }
+
+  if (mIsOffline) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return promise.forget();
+  }
+
+  if (mAudioContextState == AudioContextState::Closed ||
+      mCloseCalled) {
+    promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return promise.forget();
+  }
+
+  if (mAudioContextState == AudioContextState::Running) {
+    promise->MaybeResolve(JS::UndefinedHandleValue);
+    return promise.forget();
+  }
+
   MediaStream* ds = DestinationStream();
   if (ds) {
-    ds->ChangeExplicitBlockerCount(-1);
+    ds->UnblockStreamIfNeeded();
+  }
+
+  mPromiseGripArray.AppendElement(promise);
+  Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
+                                      AudioContextOperation::Resume, promise);
+
+  return promise.forget();
+}
+
+already_AddRefed<Promise>
+AudioContext::Close(ErrorResult& aRv)
+{
+  nsCOMPtr<nsIGlobalObject> parentObject = do_QueryInterface(GetParentObject());
+  nsRefPtr<Promise> promise;
+  promise = Promise::Create(parentObject, aRv);
+  if (aRv.Failed()) {
+    return nullptr;
   }
+
+  if (mIsOffline) {
+    promise->MaybeReject(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
+    return promise.forget();
+  }
+
+  if (mAudioContextState == AudioContextState::Closed) {
+    promise->MaybeResolve(NS_ERROR_DOM_INVALID_STATE_ERR);
+    return promise.forget();
+  }
+
+  mCloseCalled = true;
+
+  mPromiseGripArray.AppendElement(promise);
+  Graph()->ApplyAudioContextOperation(DestinationStream()->AsAudioNodeStream(),
+                                      AudioContextOperation::Close, promise);
+
+  MediaStream* ds = DestinationStream();
+  if (ds) {
+    ds->BlockStreamIfNeeded();
+  }
+
+  return promise.forget();
 }
 
 void
 AudioContext::UpdateNodeCount(int32_t aDelta)
 {
   bool firstNode = mNodeCount == 0;
   mNodeCount += aDelta;
   MOZ_ASSERT(mNodeCount >= 0);
@@ -648,16 +952,19 @@ AudioContext::StartRendering(ErrorResult
   if (mIsStarted) {
     aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
     return nullptr;
   }
 
   mIsStarted = true;
   nsRefPtr<Promise> promise = Promise::Create(parentObject, aRv);
   mDestination->StartRendering(promise);
+
+  OnStateChanged(nullptr, AudioContextState::Running);
+
   return promise.forget();
 }
 
 void
 AudioContext::Mute() const
 {
   MOZ_ASSERT(!mIsOffline);
   if (mDestination) {
--- a/dom/media/webaudio/AudioContext.h
+++ b/dom/media/webaudio/AudioContext.h
@@ -30,19 +30,22 @@
 class nsPIDOMWindow;
 
 namespace mozilla {
 
 class DOMMediaStream;
 class ErrorResult;
 class MediaStream;
 class MediaStreamGraph;
+class AudioNodeEngine;
+class AudioNodeStream;
 
 namespace dom {
 
+enum class AudioContextState : uint32_t;
 class AnalyserNode;
 class AudioBuffer;
 class AudioBufferSourceNode;
 class AudioDestinationNode;
 class AudioListener;
 class AudioNode;
 class BiquadFilterNode;
 class ChannelMergerNode;
@@ -59,41 +62,65 @@ class MediaStreamAudioSourceNode;
 class OscillatorNode;
 class PannerNode;
 class ScriptProcessorNode;
 class StereoPannerNode;
 class WaveShaperNode;
 class PeriodicWave;
 class Promise;
 
+/* This runnable allows the MSG to notify the main thread when audio is actually
+ * flowing */
+class StateChangeTask final : public nsRunnable
+{
+public:
+  /* This constructor should be used when this event is sent from the main
+   * thread. */
+  StateChangeTask(AudioContext* aAudioContext, void* aPromise, AudioContextState aNewState);
+
+  /* This constructor should be used when this event is sent from the audio
+   * thread. */
+  StateChangeTask(AudioNodeStream* aStream, void* aPromise, AudioContextState aNewState);
+
+  NS_IMETHOD Run() override;
+
+private:
+  nsRefPtr<AudioContext> mAudioContext;
+  void* mPromise;
+  nsRefPtr<AudioNodeStream> mAudioNodeStream;
+  AudioContextState mNewState;
+};
+
+enum AudioContextOperation { Suspend, Resume, Close };
+
 class AudioContext final : public DOMEventTargetHelper,
                            public nsIMemoryReporter
 {
   AudioContext(nsPIDOMWindow* aParentWindow,
                bool aIsOffline,
                AudioChannel aChannel,
                uint32_t aNumberOfChannels = 0,
                uint32_t aLength = 0,
                float aSampleRate = 0.0f);
   ~AudioContext();
 
 public:
+  typedef uint64_t AudioContextId;
+
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AudioContext,
                                            DOMEventTargetHelper)
   MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
 
   nsPIDOMWindow* GetParentObject() const
   {
     return GetOwner();
   }
 
   void Shutdown(); // idempotent
-  void Suspend();
-  void Resume();
 
   virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
 
   using DOMEventTargetHelper::DispatchTrustedEvent;
 
   // Constructor for regular AudioContext
   static already_AddRefed<AudioContext>
   Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
@@ -119,76 +146,96 @@ public:
     return mDestination;
   }
 
   float SampleRate() const
   {
     return mSampleRate;
   }
 
+  AudioContextId Id() const
+  {
+    return mId;
+  }
+
   double CurrentTime() const;
 
   AudioListener* Listener();
 
-  already_AddRefed<AudioBufferSourceNode> CreateBufferSource();
+  AudioContextState State() const;
+  // Those three methods return a promise to content, that is resolved when an
+  // (possibly long) operation is completed on the MSG (and possibly other)
+  // thread(s). To avoid having to match the calls and asychronous result when
+  // the operation is completed, we keep a reference to the promises on the main
+  // thread, and then send the promises pointers down the MSG thread, as a void*
+  // (to make it very clear that the pointer is to merely be treated as an ID).
+  // When back on the main thread, we can resolve or reject the promise, by
+  // casting it back to a `Promise*` while asserting we're back on the main
+  // thread and removing the reference we added.
+  already_AddRefed<Promise> Suspend(ErrorResult& aRv);
+  already_AddRefed<Promise> Resume(ErrorResult& aRv);
+  already_AddRefed<Promise> Close(ErrorResult& aRv);
+  IMPL_EVENT_HANDLER(statechange)
+
+  already_AddRefed<AudioBufferSourceNode> CreateBufferSource(ErrorResult& aRv);
 
   already_AddRefed<AudioBuffer>
   CreateBuffer(JSContext* aJSContext, uint32_t aNumberOfChannels,
                uint32_t aLength, float aSampleRate,
                ErrorResult& aRv);
 
   already_AddRefed<MediaStreamAudioDestinationNode>
   CreateMediaStreamDestination(ErrorResult& aRv);
 
   already_AddRefed<ScriptProcessorNode>
   CreateScriptProcessor(uint32_t aBufferSize,
                         uint32_t aNumberOfInputChannels,
                         uint32_t aNumberOfOutputChannels,
                         ErrorResult& aRv);
 
   already_AddRefed<StereoPannerNode>
-  CreateStereoPanner();
+  CreateStereoPanner(ErrorResult& aRv);
 
   already_AddRefed<AnalyserNode>
-  CreateAnalyser();
+  CreateAnalyser(ErrorResult& aRv);
 
   already_AddRefed<GainNode>
-  CreateGain();
+  CreateGain(ErrorResult& aRv);
 
   already_AddRefed<WaveShaperNode>
-  CreateWaveShaper();
+  CreateWaveShaper(ErrorResult& aRv);
 
   already_AddRefed<MediaElementAudioSourceNode>
   CreateMediaElementSource(HTMLMediaElement& aMediaElement, ErrorResult& aRv);
   already_AddRefed<MediaStreamAudioSourceNode>
   CreateMediaStreamSource(DOMMediaStream& aMediaStream, ErrorResult& aRv);
 
   already_AddRefed<DelayNode>
   CreateDelay(double aMaxDelayTime, ErrorResult& aRv);
 
   already_AddRefed<PannerNode>
-  CreatePanner();
+  CreatePanner(ErrorResult& aRv);
 
   already_AddRefed<ConvolverNode>
-  CreateConvolver();
+  CreateConvolver(ErrorResult& aRv);
 
   already_AddRefed<ChannelSplitterNode>
   CreateChannelSplitter(uint32_t aNumberOfOutputs, ErrorResult& aRv);
 
   already_AddRefed<ChannelMergerNode>
   CreateChannelMerger(uint32_t aNumberOfInputs, ErrorResult& aRv);
 
   already_AddRefed<DynamicsCompressorNode>
-  CreateDynamicsCompressor();
+  CreateDynamicsCompressor(ErrorResult& aRv);
 
   already_AddRefed<BiquadFilterNode>
-  CreateBiquadFilter();
+  CreateBiquadFilter(ErrorResult& aRv);
 
   already_AddRefed<OscillatorNode>
-  CreateOscillator();
+  CreateOscillator(ErrorResult& aRv);
 
   already_AddRefed<PeriodicWave>
   CreatePeriodicWave(const Float32Array& aRealData, const Float32Array& aImagData,
                      ErrorResult& aRv);
 
   already_AddRefed<Promise>
   DecodeAudioData(const ArrayBuffer& aBuffer,
                   const Optional<OwningNonNull<DecodeSuccessCallback> >& aSuccessCallback,
@@ -239,16 +286,18 @@ public:
     return aTime - ExtraCurrentTime();
   }
 
   double StreamTimeToDOMTime(double aTime) const
   {
     return aTime + ExtraCurrentTime();
   }
 
+  void OnStateChanged(void* aPromise, AudioContextState aNewState);
+
   IMPL_EVENT_HANDLER(mozinterruptbegin)
   IMPL_EVENT_HANDLER(mozinterruptend)
 
 private:
   /**
    * Returns the amount of extra time added to the current time of the
    * AudioDestinationNode's MediaStream to get this AudioContext's currentTime.
    * Must be subtracted from all DOM API parameter times that are on the same
@@ -261,35 +310,49 @@ private:
   void ShutdownDecoder();
 
   size_t SizeOfIncludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
   NS_IMETHOD CollectReports(nsIHandleReportCallback* aHandleReport,
                             nsISupports* aData, bool aAnonymize) override;
 
   friend struct ::mozilla::WebAudioDecodeJob;
 
+  bool CheckClosed(ErrorResult& aRv);
+
 private:
+  // Each AudioContext has an id, that is passed down the MediaStreams that
+  // back the AudioNodes, so we can easily compute the set of all the
+  // MediaStreams for a given context, on the MediasStreamGraph side.
+  const AudioContextId mId;
   // Note that it's important for mSampleRate to be initialized before
   // mDestination, as mDestination's constructor needs to access it!
   const float mSampleRate;
+  AudioContextState mAudioContextState;
   nsRefPtr<AudioDestinationNode> mDestination;
   nsRefPtr<AudioListener> mListener;
   nsTArray<nsRefPtr<WebAudioDecodeJob> > mDecodeJobs;
+  // This array is used to keep the suspend/resume/close promises alive until
+  // they are resolved, so we can safely pass them accross threads.
+  nsTArray<nsRefPtr<Promise>> mPromiseGripArray;
   // See RegisterActiveNode.  These will keep the AudioContext alive while it
   // is rendering and the window remains alive.
   nsTHashtable<nsRefPtrHashKey<AudioNode> > mActiveNodes;
   // Hashsets containing all the PannerNodes, to compute the doppler shift.
   // These are weak pointers.
   nsTHashtable<nsPtrHashKey<PannerNode> > mPannerNodes;
   // Number of channels passed in the OfflineAudioContext ctor.
   uint32_t mNumberOfChannels;
   // Number of nodes that currently exist for this AudioContext
   int32_t mNodeCount;
   bool mIsOffline;
   bool mIsStarted;
   bool mIsShutDown;
+  // Close has been called, reject suspend and resume call.
+  bool mCloseCalled;
 };
 
+static const dom::AudioContext::AudioContextId NO_AUDIO_CONTEXT = 0;
+
 }
 }
 
 #endif
 
--- a/dom/media/webaudio/AudioDestinationNode.cpp
+++ b/dom/media/webaudio/AudioDestinationNode.cpp
@@ -1,15 +1,16 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* 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 "AudioDestinationNode.h"
+#include "AudioContext.h"
 #include "mozilla/dom/AudioDestinationNodeBinding.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "AudioChannelAgent.h"
 #include "AudioChannelService.h"
 #include "AudioNodeEngine.h"
 #include "AudioNodeStream.h"
@@ -171,19 +172,21 @@ public:
       return;
     }
     for (uint32_t i = 0; i < mInputChannels.Length(); ++i) {
       renderedBuffer->SetRawChannelContents(i, mInputChannels[i]);
     }
 
     aNode->ResolvePromise(renderedBuffer);
 
-    nsRefPtr<OnCompleteTask> task =
+    nsRefPtr<OnCompleteTask> onCompleteTask =
       new OnCompleteTask(context, renderedBuffer);
-    NS_DispatchToMainThread(task);
+    NS_DispatchToMainThread(onCompleteTask);
+
+    context->OnStateChanged(nullptr, AudioContextState::Closed);
   }
 
   virtual size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override
   {
     size_t amount = AudioNodeEngine::SizeOfExcludingThis(aMallocSizeOf);
     amount += mInputChannels.SizeOfExcludingThis(aMallocSizeOf);
     return amount;
   }
@@ -362,16 +365,20 @@ AudioDestinationNode::AudioDestinationNo
                             new OfflineDestinationNodeEngine(this, aNumberOfChannels,
                                                              aLength, aSampleRate) :
                             static_cast<AudioNodeEngine*>(new DestinationNodeEngine(this));
 
   mStream = graph->CreateAudioNodeStream(engine, MediaStreamGraph::EXTERNAL_STREAM);
   mStream->AddMainThreadListener(this);
   mStream->AddAudioOutput(&gWebAudioOutputKey);
 
+  if (!aIsOffline) {
+    graph->NotifyWhenGraphStarted(mStream->AsAudioNodeStream());
+  }
+
   if (aChannel != AudioChannel::Normal) {
     ErrorResult rv;
     SetMozAudioChannelType(aChannel, rv);
   }
 }
 
 AudioDestinationNode::~AudioDestinationNode()
 {
--- a/dom/media/webaudio/AudioNodeExternalInputStream.cpp
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.cpp
@@ -7,18 +7,18 @@
 #include "AudioNodeExternalInputStream.h"
 #include "AudioChannelFormat.h"
 #include "mozilla/dom/MediaStreamAudioSourceNode.h"
 
 using namespace mozilla::dom;
 
 namespace mozilla {
 
-AudioNodeExternalInputStream::AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate)
-  : AudioNodeStream(aEngine, MediaStreamGraph::INTERNAL_STREAM, aSampleRate)
+AudioNodeExternalInputStream::AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate, uint32_t aContextId)
+  : AudioNodeStream(aEngine, MediaStreamGraph::INTERNAL_STREAM, aSampleRate, aContextId)
 {
   MOZ_COUNT_CTOR(AudioNodeExternalInputStream);
 }
 
 AudioNodeExternalInputStream::~AudioNodeExternalInputStream()
 {
   MOZ_COUNT_DTOR(AudioNodeExternalInputStream);
 }
--- a/dom/media/webaudio/AudioNodeExternalInputStream.h
+++ b/dom/media/webaudio/AudioNodeExternalInputStream.h
@@ -15,17 +15,17 @@ namespace mozilla {
 /**
  * This is a MediaStream implementation that acts for a Web Audio node but
  * unlike other AudioNodeStreams, supports any kind of MediaStream as an
  * input --- handling any number of audio tracks and handling blocking of
  * the input MediaStream.
  */
 class AudioNodeExternalInputStream : public AudioNodeStream {
 public:
-  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate);
+  AudioNodeExternalInputStream(AudioNodeEngine* aEngine, TrackRate aSampleRate, uint32_t aContextId);
 protected:
   ~AudioNodeExternalInputStream();
 
 public:
   virtual void ProcessInput(GraphTime aFrom, GraphTime aTo, uint32_t aFlags) override;
 
 private:
   /**
--- a/dom/media/webaudio/AudioNodeStream.cpp
+++ b/dom/media/webaudio/AudioNodeStream.cpp
@@ -22,20 +22,22 @@ namespace mozilla {
  * for regular audio contexts, and the rate requested by the web content
  * for offline audio contexts.
  * Each chunk in the track is a single block of WEBAUDIO_BLOCK_SIZE samples.
  * Note: This must be a different value than MEDIA_STREAM_DEST_TRACK_ID
  */
 
 AudioNodeStream::AudioNodeStream(AudioNodeEngine* aEngine,
                                  MediaStreamGraph::AudioNodeStreamKind aKind,
-                                 TrackRate aSampleRate)
+                                 TrackRate aSampleRate,
+                                 AudioContext::AudioContextId aContextId)
   : ProcessedMediaStream(nullptr),
     mEngine(aEngine),
     mSampleRate(aSampleRate),
+    mAudioContextId(aContextId),
     mKind(aKind),
     mNumberOfInputChannels(2),
     mMarkAsFinishedAfterThisBlock(false),
     mAudioParamStream(false),
     mPassThrough(false)
 {
   MOZ_ASSERT(NS_IsMainThread());
   mChannelCountMode = ChannelCountMode::Max;
--- a/dom/media/webaudio/AudioNodeStream.h
+++ b/dom/media/webaudio/AudioNodeStream.h
@@ -42,17 +42,18 @@ public:
 
   typedef nsAutoTArray<AudioChunk, 1> OutputChunks;
 
   /**
    * Transfers ownership of aEngine to the new AudioNodeStream.
    */
   AudioNodeStream(AudioNodeEngine* aEngine,
                   MediaStreamGraph::AudioNodeStreamKind aKind,
-                  TrackRate aSampleRate);
+                  TrackRate aSampleRate,
+                  AudioContext::AudioContextId aContextId);
 
 protected:
   ~AudioNodeStream();
 
 public:
   // Control API
   /**
    * Sets a parameter that's a time relative to some stream's played time.
@@ -116,16 +117,17 @@ public:
   virtual bool IsIntrinsicallyConsumed() const override
   {
     return true;
   }
 
   // Any thread
   AudioNodeEngine* Engine() { return mEngine; }
   TrackRate SampleRate() const { return mSampleRate; }
+  AudioContext::AudioContextId AudioContextId() const override { return mAudioContextId; }
 
   /**
    * Convert a time in seconds on the destination stream to ticks
    * on this stream, including fractional position between ticks.
    */
   double FractionalTicksFromDestinationTime(AudioNodeStream* aDestination,
                                             double aSeconds);
   /**
@@ -142,16 +144,17 @@ public:
                                   StreamTime aPosition);
 
   size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const override;
   size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const override;
 
   void SizeOfAudioNodesIncludingThis(MallocSizeOf aMallocSizeOf,
                                      AudioNodeSizes& aUsage) const;
 
+
 protected:
   void AdvanceOutputSegment();
   void FinishOutput();
   void AccumulateInputChunk(uint32_t aInputIndex, const AudioChunk& aChunk,
                             AudioChunk* aBlock,
                             nsTArray<float>* aDownmixBuffer);
   void UpMixDownMixChunk(const AudioChunk* aChunk, uint32_t aOutputChannelCount,
                          nsTArray<const void*>& aOutputChannels,
@@ -161,18 +164,21 @@ protected:
   void ObtainInputBlock(AudioChunk& aTmpChunk, uint32_t aPortIndex);
 
   // The engine that will generate output for this node.
   nsAutoPtr<AudioNodeEngine> mEngine;
   // The last block produced by this node.
   OutputChunks mLastChunks;
   // The stream's sampling rate
   const TrackRate mSampleRate;
+  // This is necessary to be able to find all the nodes for a given
+  // AudioContext. It is set on the main thread, in the constructor.
+  const AudioContext::AudioContextId mAudioContextId;
   // Whether this is an internal or external stream
-  MediaStreamGraph::AudioNodeStreamKind mKind;
+  const MediaStreamGraph::AudioNodeStreamKind mKind;
   // The number of input channels that this stream requires. 0 means don't care.
   uint32_t mNumberOfInputChannels;
   // The mixing modes
   ChannelCountMode mChannelCountMode;
   ChannelInterpretation mChannelInterpretation;
   // Whether the stream should be marked as finished as soon
   // as the current time range has been computed block by block.
   bool mMarkAsFinishedAfterThisBlock;
--- a/dom/media/webaudio/MediaStreamAudioSourceNode.h
+++ b/dom/media/webaudio/MediaStreamAudioSourceNode.h
@@ -30,16 +30,17 @@ public:
     switch (aIndex) {
     case ENABLE:
       mEnabled = !!aValue;
       break;
     default:
       NS_ERROR("MediaStreamAudioSourceNodeEngine bad parameter index");
     }
   }
+
 private:
   bool mEnabled;
 };
 
 class MediaStreamAudioSourceNode : public AudioNode,
                                    public DOMMediaStream::PrincipalChangeObserver
 {
 public:
--- a/dom/media/webaudio/moz.build
+++ b/dom/media/webaudio/moz.build
@@ -29,16 +29,17 @@ EXPORTS += [
     'AudioParamTimeline.h',
     'MediaBufferDecoder.h',
     'ThreeDPoint.h',
     'WebAudioUtils.h',
 ]
 
 EXPORTS.mozilla += [
     'FFTBlock.h',
+    'MediaStreamAudioDestinationNode.h',
 ]
 
 EXPORTS.mozilla.dom += [
     'AnalyserNode.h',
     'AudioBuffer.h',
     'AudioBufferSourceNode.h',
     'AudioContext.h',
     'AudioDestinationNode.h',
--- a/dom/media/webaudio/test/mochitest.ini
+++ b/dom/media/webaudio/test/mochitest.ini
@@ -40,16 +40,18 @@ support-files =
 skip-if = (toolkit == 'android' && (processor == 'x86' || debug)) || os == 'win' # bug 1127845, bug 1138468
 [test_audioBufferSourceNodeNoStart.html]
 [test_audioBufferSourceNodeNullBuffer.html]
 [test_audioBufferSourceNodeOffset.html]
 skip-if = (toolkit == 'gonk') || (toolkit == 'android') || debug #bug 906752
 [test_audioBufferSourceNodePassThrough.html]
 [test_audioBufferSourceNodeRate.html]
 [test_AudioContext.html]
+skip-if = android_version == '10' # bug 1138462
+[test_audioContextSuspendResumeClose.html]
 [test_audioDestinationNode.html]
 [test_AudioListener.html]
 [test_audioParamExponentialRamp.html]
 [test_audioParamGain.html]
 [test_audioParamLinearRamp.html]
 [test_audioParamSetCurveAtTime.html]
 [test_audioParamSetCurveAtTimeZeroDuration.html]
 [test_audioParamSetTargetAtTime.html]
new file mode 100644
--- /dev/null
+++ b/dom/media/webaudio/test/test_audioContextSuspendResumeClose.html
@@ -0,0 +1,400 @@
+<!DOCTYPE HTML>
+<html>
+<head>
+  <title>Test suspend, resume and close method of the AudioContext</title>
+  <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="webaudio.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
+</head>
+<body>
+<pre id="test">
+<script class="testbody" type="text/javascript">
+
+SimpleTest.requestCompleteLog();
+
+function tryToToCreateNodeOnClosedContext(ctx) {
+  ok(ctx.state, "closed", "The context is in closed state");
+
+  [ { name: "createBufferSource" },
+    { name: "createMediaStreamDestination",
+      onOfflineAudioContext: false},
+    { name: "createScriptProcessor" },
+    { name: "createStereoPanner" },
+    { name: "createAnalyser" },
+    { name: "createGain" },
+    { name: "createDelay" },
+    { name: "createBiquadFilter" },
+    { name: "createWaveShaper" },
+    { name: "createPanner" },
+    { name: "createConvolver" },
+    { name: "createChannelSplitter" },
+    { name: "createChannelMerger" },
+    { name: "createDynamicsCompressor" },
+    { name: "createOscillator" },
+    { name: "createMediaElementSource",
+      args: [new Audio()],
+      onOfflineAudioContext: false },
+    { name: "createMediaStreamSource",
+      args: [new Audio().mozCaptureStream()],
+      onOfflineAudioContext: false } ].forEach(function(e) {
+
+      if (e.onOfflineAudioContext == false &&
+          ctx instanceof OfflineAudioContext) {
+        return;
+      }
+
+      expectException(function() {
+        ctx[e.name].apply(ctx, e.args);
+      }, DOMException.INVALID_STATE_ERR);
+    });
+}
+
+function loadFile(url, callback) {
+  var xhr = new XMLHttpRequest();
+  xhr.open("GET", url, true);
+  xhr.responseType = "arraybuffer";
+  xhr.onload = function() {
+    callback(xhr.response);
+  };
+  xhr.send();
+}
+
+// createBuffer, createPeriodicWave and decodeAudioData should work on a context
+// that has `state` == "closed"
+function tryLegalOpeerationsOnClosedContext(ctx) {
+  ok(ctx.state, "closed", "The context is in closed state");
+
+  [ { name: "createBuffer",
+      args: [1, 44100, 44100] },
+    { name: "createPeriodicWave",
+      args: [new Float32Array(10), new Float32Array(10)] }
+  ].forEach(function(e) {
+    expectNoException(function() {
+      ctx[e.name].apply(ctx, e.args);
+    });
+  });
+  loadFile("ting-44.1k-1ch.ogg", function(buf) {
+    ctx.decodeAudioData(buf).then(function(decodedBuf) {
+      ok(true, "decodeAudioData on a closed context should work, it did.")
+      todo(false, "0 " + (ctx instanceof OfflineAudioContext ? "Offline" : "Realtime"));
+      finish();
+    }).catch(function(e){
+      ok(false, "decodeAudioData on a closed context should work, it did not");
+      finish();
+    });
+  });
+}
+
+// Test that MediaStreams that are the output of a suspended AudioContext are
+// producing silence
+// ac1 produce a sine fed to a MediaStreamAudioDestinationNode
+// ac2 is connected to ac1 with a MediaStreamAudioSourceNode, and check that
+// there is silence when ac1 is suspended
+function testMultiContextOutput() {
+  var ac1 = new AudioContext(),
+      ac2 = new AudioContext();
+
+  var osc1 = ac1.createOscillator(),
+      mediaStreamDestination1 = ac1.createMediaStreamDestination();
+
+  var mediaStreamAudioSourceNode2 =
+    ac2.createMediaStreamSource(mediaStreamDestination1.stream),
+    sp2 = ac2.createScriptProcessor(),
+    suspendCalled = false,
+    silentBuffersInARow = 0;
+
+
+  sp2.onaudioprocess = function(e) {
+    if (!suspendCalled) {
+      ac1.suspend();
+      suspendCalled = true;
+    } else {
+      // Wait until the context that produce the tone is actually suspended. It
+      // can be that the second context receives a little amount of data because
+      // of the buffering between the two contexts.
+      if (ac1.state == "suspended") {
+        var input = e.inputBuffer.getChannelData(0);
+        var silent = true;
+        for (var i = 0; i < input.length; i++) {
+          if (input[i] != 0.0) {
+            silent = false;
+          }
+        }
+
+        if (silent) {
+          silentBuffersInARow++;
+          if (silentBuffersInARow == 10) {
+            ok(true,
+                "MediaStreams produce silence when their input is blocked.");
+            sp2.onaudioprocess = null;
+            ac1.close();
+            ac2.close();
+            todo(false,"1");
+            finish();
+          }
+        } else {
+          is(silentBuffersInARow, 0,
+              "No non silent buffer inbetween silent buffers.");
+        }
+      }
+    }
+  }
+
+  osc1.connect(mediaStreamDestination1);
+
+  mediaStreamAudioSourceNode2.connect(sp2);
+  osc1.start();
+}
+
+
+// Test that there is no buffering between contexts when connecting a running
+// AudioContext to a suspended AudioContext. Our ScriptProcessorNode does some
+// buffering internally, so we ensure this by using a very very low frequency
+// on a sine, and oberve that the phase has changed by a big enough margin.
+function testMultiContextInput() {
+  var ac1 = new AudioContext(),
+      ac2 = new AudioContext();
+
+  var osc1 = ac1.createOscillator(),
+      mediaStreamDestination1 = ac1.createMediaStreamDestination(),
+      sp1 = ac1.createScriptProcessor();
+
+  var mediaStreamAudioSourceNode2 =
+    ac2.createMediaStreamSource(mediaStreamDestination1.stream),
+    sp2 = ac2.createScriptProcessor(),
+    resumed = false,
+    suspended = false,
+    countEventOnFirstSP = true,
+    eventReceived = 0;
+
+
+  osc1.frequency.value = 0.0001;
+
+  // We keep a first ScriptProcessor to get a periodic callback, since we can't
+  // use setTimeout anymore.
+  sp1.onaudioprocess = function(e) {
+    if (countEventOnFirstSP) {
+      eventReceived++;
+    }
+    if (eventReceived > 3 && suspended) {
+      countEventOnFirstSP = false;
+      eventReceived = 0;
+      ac2.resume().then(function() {
+        resumed = true;
+      });
+    }
+  }
+
+  sp2.onaudioprocess = function(e) {
+    var inputBuffer = e.inputBuffer.getChannelData(0);
+    if (!resumed) {
+      // save the last value of the buffer before suspending.
+      sp2.value = inputBuffer[inputBuffer.length - 1];
+      ac2.suspend().then(function() {
+        suspended = true;
+      });
+    } else {
+      eventReceived++;
+      if (eventReceived == 3) {
+        var delta = Math.abs(inputBuffer[1] - sp2.value),
+            theoreticalIncrement = 2048 * 3 * Math.PI * 2 * osc1.frequency.value / ac1.sampleRate;
+        ok(delta >= theoreticalIncrement,
+            "Buffering did not occur when the context was suspended (delta:" + delta + " increment: " + theoreticalIncrement+")");
+        ac1.close();
+        ac2.close();
+        sp1.onaudioprocess = null;
+        sp2.onaudioprocess = null;
+        todo(false, "2");
+        finish();
+      }
+    }
+  }
+
+  osc1.connect(mediaStreamDestination1);
+  osc1.connect(sp1);
+
+  mediaStreamAudioSourceNode2.connect(sp2);
+  osc1.start();
+}
+
+// Test that ScriptProcessorNode's onaudioprocess don't get called while the
+// context is suspended/closed. It is possible that we get the handler called
+// exactly once after suspend, because the event has already been sent to the
+// event loop.
+function testScriptProcessNodeSuspended() {
+  var ac = new AudioContext();
+  var sp = ac.createScriptProcessor();
+  var remainingIterations = 30;
+  var afterResume = false;
+  sp.onaudioprocess = function() {
+    ok(ac.state == "running" || remainingIterations == 3, "If onaudioprocess is called, the context" +
+        " must be running (was " + ac.state + ", remainingIterations:" + remainingIterations +")");
+    remainingIterations--;
+    if (!afterResume) {
+      if (remainingIterations == 0) {
+        ac.suspend().then(function() {
+          ac.resume().then(function() {
+            remainingIterations = 30;
+            afterResume = true;
+          });
+        });
+      }
+    } else {
+      sp.onaudioprocess = null;
+      todo(false,"3");
+      finish();
+    }
+  }
+  sp.connect(ac.destination);
+}
+
+// Take an AudioContext, make sure it switches to running when the audio starts
+// flowing, and then, call suspend, resume and close on it, tracking its state.
+function testAudioContext() {
+  var ac = new AudioContext();
+  is(ac.state, "suspended", "AudioContext should start in suspended state.");
+  var stateTracker = {
+    previous: ac.state,
+     // no promise for the initial suspended -> running
+    initial: {  handler: false },
+    suspend: { promise: false, handler: false },
+    resume: { promise: false, handler: false },
+    close: { promise: false, handler: false }
+  };
+
+  function initialSuspendToRunning() {
+    ok(stateTracker.previous == "suspended" &&
+       ac.state == "running",
+       "AudioContext should switch to \"running\" when the audio hardware is" +
+       " ready.");
+
+    stateTracker.previous = ac.state;
+    ac.onstatechange = afterSuspend;
+    stateTracker.initial.handler = true;
+
+    ac.suspend().then(function() {
+      ok(!stateTracker.suspend.promise && !stateTracker.suspend.handler,
+        "Promise should be resolved before the callback, and only once.")
+      stateTracker.suspend.promise = true;
+    });
+  }
+
+  function afterSuspend() {
+    ok(stateTracker.previous == "running" &&
+       ac.state == "suspended",
+       "AudioContext should switch to \"suspend\" when the audio stream is" +
+       "suspended.");
+    ok(stateTracker.suspend.promise && !stateTracker.suspend.handler,
+        "Handler should be called after the callback, and only once");
+
+    stateTracker.suspend.handler = true;
+    stateTracker.previous = ac.state;
+    ac.onstatechange = afterResume;
+
+    ac.resume().then(function() {
+      ok(!stateTracker.resume.promise && !stateTracker.resume.handler,
+        "Promise should be called before the callback, and only once");
+      stateTracker.resume.promise = true;
+    });
+  }
+
+  function afterResume() {
+    ok(stateTracker.previous == "suspended" &&
+       ac.state == "running",
+   "AudioContext should switch to \"running\" when the audio stream resumes.");
+
+    ok(stateTracker.resume.promise && !stateTracker.resume.handler,
+       "Handler should be called after the callback, and only once");
+
+    stateTracker.resume.handler = true;
+    stateTracker.previous = ac.state;
+    ac.onstatechange = afterClose;
+
+    ac.close().then(function() {
+      ok(!stateTracker.close.promise && !stateTracker.close.handler,
+        "Promise should be called before the callback, and only once");
+      stateTracker.close.promise = true;
+      tryToToCreateNodeOnClosedContext(ac);
+      tryLegalOpeerationsOnClosedContext(ac);
+    });
+  }
+
+  function afterClose() {
+    ok(stateTracker.previous == "running" &&
+       ac.state == "closed",
+       "AudioContext should switch to \"closed\" when the audio stream is" +
+       " closed.");
+    ok(stateTracker.close.promise && !stateTracker.close.handler,
+       "Handler should be called after the callback, and only once");
+  }
+
+  ac.onstatechange = initialSuspendToRunning;
+}
+
+function testOfflineAudioContext() {
+  var o = new OfflineAudioContext(1, 44100, 44100);
+  is(o.state, "suspended", "OfflineAudioContext should start in suspended state.");
+
+  expectRejectedPromise(o, "suspend", "NotSupportedError");
+  expectRejectedPromise(o, "resume", "NotSupportedError");
+  expectRejectedPromise(o, "close", "NotSupportedError");
+
+  var previousState = o.state,
+      finishedRendering = false;
+  function beforeStartRendering() {
+    ok(previousState == "suspended" && o.state == "running", "onstatechanged" +
+        "handler is called on state changed, and the new state is running");
+    previousState = o.state;
+    o.onstatechange = onRenderingFinished;
+  }
+
+  function onRenderingFinished() {
+    ok(previousState == "running" && o.state == "closed",
+        "onstatechanged handler is called when rendering finishes, " +
+        "and the new state is closed");
+    ok(finishedRendering, "The Promise that is resolved when the rendering is" +
+                    "done should be resolved earlier than the state change.");
+    previousState = o.state;
+    o.onstatechange = afterRenderingFinished;
+
+    tryToToCreateNodeOnClosedContext(o);
+    tryLegalOpeerationsOnClosedContext(o);
+  }
+
+  function afterRenderingFinished() {
+    ok(false, "There should be no transition out of the closed state.");
+  }
+
+  o.onstatechange = beforeStartRendering;
+
+  o.startRendering().then(function(buffer) {
+    finishedRendering = true;
+  });
+}
+
+var remaining = 0;
+function finish() {
+  remaining--;
+  if (remaining == 0) {
+    SimpleTest.finish();
+  }
+}
+
+
+SimpleTest.waitForExplicitFinish();
+addLoadEvent(function() {
+  var tests = [
+    testAudioContext,
+    testOfflineAudioContext,
+    testScriptProcessNodeSuspended,
+    testMultiContextOutput,
+    testMultiContextInput
+  ];
+  remaining = tests.length;
+  tests.forEach(function(f) { f() });
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/dom/media/webaudio/test/webaudio.js
+++ b/dom/media/webaudio/test/webaudio.js
@@ -28,16 +28,28 @@ function expectTypeError(func) {
     func();
   } catch (ex) {
     threw = true;
     ok(ex instanceof TypeError, "Expect a TypeError");
   }
   ok(threw, "The exception was thrown");
 }
 
+function expectRejectedPromise(that, func, exceptionName) {
+  var promise = that[func]();
+
+  ok(promise instanceof Promise, "Expect a Promise");
+
+  promise.then(function(res) {
+    ok(false, "Promise resolved when it should have been rejected.");
+  }).catch(function(err) {
+    is(err.name, exceptionName, "Promise correctly reject with " + exceptionName);
+  });
+}
+
 function fuzzyCompare(a, b) {
   return Math.abs(a - b) < 9e-3;
 }
 
 function compareChannels(buf1, buf2,
                         /*optional*/ length,
                         /*optional*/ sourceOffset,
                         /*optional*/ destOffset,
--- a/dom/webidl/AudioContext.webidl
+++ b/dom/webidl/AudioContext.webidl
@@ -8,75 +8,89 @@
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 callback DecodeSuccessCallback = void (AudioBuffer decodedData);
 callback DecodeErrorCallback = void ();
 
+enum AudioContextState {
+    "suspended",
+    "running",
+    "closed"
+};
+
 [Constructor,
  Constructor(AudioChannel audioChannelType)]
 interface AudioContext : EventTarget {
 
     readonly attribute AudioDestinationNode destination;
     readonly attribute float sampleRate;
     readonly attribute double currentTime;
     readonly attribute AudioListener listener;
+    readonly attribute AudioContextState state;
+    [Throws]
+    Promise<void> suspend();
+    [Throws]
+    Promise<void> resume();
+    [Throws]
+    Promise<void> close();
+    attribute EventHandler onstatechange;
 
     [NewObject, Throws]
     AudioBuffer createBuffer(unsigned long numberOfChannels, unsigned long length, float sampleRate);
 
     [Throws]
     Promise<AudioBuffer> decodeAudioData(ArrayBuffer audioData,
                                          optional DecodeSuccessCallback successCallback,
                                          optional DecodeErrorCallback errorCallback);
 
     // AudioNode creation
-    [NewObject]
+    [NewObject, Throws]
     AudioBufferSourceNode createBufferSource();
 
     [NewObject, Throws]
     MediaStreamAudioDestinationNode createMediaStreamDestination();
 
     [NewObject, Throws]
     ScriptProcessorNode createScriptProcessor(optional unsigned long bufferSize = 0,
                                               optional unsigned long numberOfInputChannels = 2,
                                               optional unsigned long numberOfOutputChannels = 2);
 
-    [NewObject]
+    [NewObject, Throws]
     StereoPannerNode createStereoPanner();
-    [NewObject]
+    [NewObject, Throws]
     AnalyserNode createAnalyser();
     [NewObject, Throws, UnsafeInPrerendering]
     MediaElementAudioSourceNode createMediaElementSource(HTMLMediaElement mediaElement);
     [NewObject, Throws, UnsafeInPrerendering]
     MediaStreamAudioSourceNode createMediaStreamSource(MediaStream mediaStream);
-    [NewObject]
+    [NewObject, Throws]
     GainNode createGain();
     [NewObject, Throws]
     DelayNode createDelay(optional double maxDelayTime = 1);
-    [NewObject]
+    [NewObject, Throws]
     BiquadFilterNode createBiquadFilter();
-    [NewObject]
+    [NewObject, Throws]
     WaveShaperNode createWaveShaper();
-    [NewObject]
+    [NewObject, Throws]
     PannerNode createPanner();
-    [NewObject]
+    [NewObject, Throws]
     ConvolverNode createConvolver();
 
     [NewObject, Throws]
     ChannelSplitterNode createChannelSplitter(optional unsigned long numberOfOutputs = 6);
     [NewObject, Throws]
     ChannelMergerNode createChannelMerger(optional unsigned long numberOfInputs = 6);
 
-    [NewObject]
+    [NewObject, Throws]
     DynamicsCompressorNode createDynamicsCompressor();
 
-    [NewObject]
+    [NewObject, Throws]
     OscillatorNode createOscillator();
     [NewObject, Throws]
     PeriodicWave createPeriodicWave(Float32Array real, Float32Array imag);
 
 };
 
 // Mozilla extensions
 partial interface AudioContext {
--- a/dom/webidl/AudioListener.webidl
+++ b/dom/webidl/AudioListener.webidl
@@ -7,21 +7,24 @@
  * https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
 interface AudioListener {
 
-    // same as OpenAL (default 1) 
+    // same as OpenAL (default 1)
+    [Deprecated="PannerNodeDoppler"]
     attribute double dopplerFactor;
 
-    // in meters / second (default 343.3) 
+    // in meters / second (default 343.3)
+    [Deprecated="PannerNodeDoppler"]
     attribute double speedOfSound;
 
-    // Uses a 3D cartesian coordinate system 
+    // Uses a 3D cartesian coordinate system
     void setPosition(double x, double y, double z);
     void setOrientation(double x, double y, double z, double xUp, double yUp, double zUp);
+    [Deprecated="PannerNodeDoppler"]
     void setVelocity(double x, double y, double z);
 
 };
 
--- a/dom/webidl/PannerNode.webidl
+++ b/dom/webidl/PannerNode.webidl
@@ -24,16 +24,17 @@ enum DistanceModelType {
 interface PannerNode : AudioNode {
 
     // Default for stereo is equalpower
     attribute PanningModelType panningModel;
 
     // Uses a 3D cartesian coordinate system
     void setPosition(double x, double y, double z);
     void setOrientation(double x, double y, double z);
+    [Deprecated="PannerNodeDoppler"]
     void setVelocity(double x, double y, double z);
 
     // Distance model and attributes
     attribute DistanceModelType distanceModel;
     attribute double refDistance;
     attribute double maxDistance;
     attribute double rolloffFactor;
 
--- a/dom/workers/test/extensions/bootstrap/Makefile.in
+++ b/dom/workers/test/extensions/bootstrap/Makefile.in
@@ -1,18 +1,12 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DIST_FILES = \
-  bootstrap.js \
-  install.rdf \
-  worker.js \
-  $(NULL)
-
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
 
 GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
--- a/dom/workers/test/extensions/bootstrap/moz.build
+++ b/dom/workers/test/extensions/bootstrap/moz.build
@@ -1,7 +1,13 @@
 # -*- 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/.
 
 XPI_NAME = 'workerbootstrap'
+
+DIST_FILES += [
+    'bootstrap.js',
+    'install.rdf',
+    'worker.js',
+]
--- a/dom/workers/test/extensions/traditional/Makefile.in
+++ b/dom/workers/test/extensions/traditional/Makefile.in
@@ -1,17 +1,12 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DIST_FILES = \
-  install.rdf \
-  worker.js \
-  $(NULL)
-
 TEST_EXTENSIONS_DIR = $(DEPTH)/_tests/testing/mochitest/extensions
 
 GENERATED_DIRS = $(TEST_EXTENSIONS_DIR)
 
 include $(topsrcdir)/config/rules.mk
 
 libs::
 	@(cd $(DIST)/xpi-stage && tar $(TAR_CREATE_FLAGS) - $(XPI_NAME)) | (cd $(TEST_EXTENSIONS_DIR) && tar -xf -)
--- a/dom/workers/test/extensions/traditional/moz.build
+++ b/dom/workers/test/extensions/traditional/moz.build
@@ -11,8 +11,13 @@ XPIDL_SOURCES += [
 XPIDL_MODULE = 'WorkerTest'
 
 EXTRA_COMPONENTS += [
     'WorkerTest.js',
     'WorkerTest.manifest',
 ]
 
 XPI_NAME = 'worker'
+
+DIST_FILES += [
+    'install.rdf',
+    'worker.js',
+]
--- a/gfx/layers/LayersTypes.h
+++ b/gfx/layers/LayersTypes.h
@@ -208,16 +208,31 @@ struct EventRegions {
   }
 
   void Sub(const EventRegions& aMinuend, const nsIntRegion& aSubtrahend)
   {
     mHitRegion.Sub(aMinuend.mHitRegion, aSubtrahend);
     mDispatchToContentHitRegion.Sub(aMinuend.mDispatchToContentHitRegion, aSubtrahend);
   }
 
+  void ApplyTranslationAndScale(float aXTrans, float aYTrans, float aXScale, float aYScale)
+  {
+    mHitRegion.ScaleRoundOut(aXScale, aYScale);
+    mDispatchToContentHitRegion.ScaleRoundOut(aXScale, aYScale);
+    mNoActionRegion.ScaleRoundOut(aXScale, aYScale);
+    mHorizontalPanRegion.ScaleRoundOut(aXScale, aYScale);
+    mVerticalPanRegion.ScaleRoundOut(aXScale, aYScale);
+
+    mHitRegion.MoveBy(aXTrans, aYTrans);
+    mDispatchToContentHitRegion.MoveBy(aXTrans, aYTrans);
+    mNoActionRegion.MoveBy(aXTrans, aYTrans);
+    mHorizontalPanRegion.MoveBy(aXTrans, aYTrans);
+    mVerticalPanRegion.MoveBy(aXTrans, aYTrans);
+  }
+
   void Transform(const gfx3DMatrix& aTransform)
   {
     mHitRegion.Transform(aTransform);
     mDispatchToContentHitRegion.Transform(aTransform);
   }
 
   bool IsEmpty() const
   {
--- a/gfx/src/nsRegion.cpp
+++ b/gfx/src/nsRegion.cpp
@@ -560,16 +560,21 @@ uint64_t nsRegion::Area () const
   while ((r = iter.Next()) != nullptr) {
     area += uint64_t(r->width)*r->height;
   }
   return area;
 }
 
 nsRegion& nsRegion::ScaleRoundOut (float aXScale, float aYScale)
 {
+  if (mozilla::gfx::FuzzyEqual(aXScale, 1.0f) &&
+      mozilla::gfx::FuzzyEqual(aYScale, 1.0f)) {
+    return *this;
+  }
+
   int n;
   pixman_box32_t *boxes = pixman_region32_rectangles(&mImpl, &n);
   for (int i=0; i<n; i++) {
     nsRect rect = BoxToRect(boxes[i]);
     rect.ScaleRoundOut(aXScale, aYScale);
     boxes[i] = RectToBox(rect);
   }
 
--- a/gfx/thebes/gfxPlatform.cpp
+++ b/gfx/thebes/gfxPlatform.cpp
@@ -2310,22 +2310,16 @@ gfxPlatform::UsesOffMainThreadCompositin
       sPrefBrowserTabsRemoteAutostart ||
       gfxPrefs::LayersOffMainThreadCompositionEnabled() ||
       gfxPrefs::LayersOffMainThreadCompositionForceEnabled() ||
       gfxPrefs::LayersOffMainThreadCompositionTestingEnabled();
 #if defined(MOZ_WIDGET_GTK)
     // Linux users who chose OpenGL are being grandfathered in to OMTC
     result |= gfxPrefs::LayersAccelerationForceEnabled();
 
-#if !defined(NIGHTLY_BUILD)
-    // Yeah, these two env vars do the same thing.
-    // I'm told that one of them is enabled on some test slaves config,
-    // so be slightly careful if you think you can remove one of them.
-    result &= PR_GetEnv("MOZ_USE_OMTC") || PR_GetEnv("MOZ_OMTC_ENABLED");
-#endif
 #endif
     firstTime = false;
   }
 
   return result;
 }
 
 already_AddRefed<mozilla::gfx::VsyncSource>
--- a/js/src/builtin/Array.js
+++ b/js/src/builtin/Array.js
@@ -118,19 +118,19 @@ function ArrayEvery(callbackfn/*, thisAr
     /* Step 1. */
     var O = ToObject(this);
 
     /* Steps 2-3. */
     var len = TO_UINT32(O.length);
 
     /* Step 4. */
     if (arguments.length === 0)
-        ThrowError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every');
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, 'Array.prototype.every');
     if (!IsCallable(callbackfn))
-        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+        ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     /* Step 5. */
     var T = arguments.length > 1 ? arguments[1] : void 0;
 
     /* Steps 6-7. */
     /* Steps a (implicit), and d. */
     for (var k = 0; k < len; k++) {
         /* Step b */
--- a/js/src/builtin/Intl.js
+++ b/js/src/builtin/Intl.js
@@ -534,20 +534,20 @@ function CanonicalizeLocaleList(locales)
     var len = TO_UINT32(O.length);
     var k = 0;
     while (k < len) {
         // Don't call ToString(k) - SpiderMonkey is faster with integers.
         var kPresent = HasProperty(O, k);
         if (kPresent) {
             var kValue = O[k];
             if (!(typeof kValue === "string" || IsObject(kValue)))
-                ThrowError(JSMSG_INVALID_LOCALES_ELEMENT);
+                ThrowTypeError(JSMSG_INVALID_LOCALES_ELEMENT);
             var tag = ToString(kValue);
             if (!IsStructurallyValidLanguageTag(tag))
-                ThrowError(JSMSG_INVALID_LANGUAGE_TAG, tag);
+                ThrowRangeError(JSMSG_INVALID_LANGUAGE_TAG, tag);
             tag = CanonicalizeLanguageTag(tag);
             if (seen.indexOf(tag) === -1)
                 seen.push(tag);
         }
         k++;
     }
     return seen;
 }
--- a/js/src/builtin/TypedArray.js
+++ b/js/src/builtin/TypedArray.js
@@ -100,19 +100,19 @@ function TypedArrayEvery(callbackfn, thi
     // Steps 1-2.
     var O = this;
 
     // Steps 3-5.
     var len = TypedArrayLength(O);
 
     // Step 6.
     if (arguments.length === 0)
-        ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every");
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.every");
     if (!IsCallable(callbackfn))
-        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+        ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 7.
     var T = thisArg;
 
     // Steps 8-9.
     // Omit steps 9.a-9.c and the 'if' clause in step 9.d, since there are no holes in typed arrays.
     for (var k = 0; k < len; k++) {
         // Steps 9.d.i-9.d.ii.
@@ -181,19 +181,19 @@ function TypedArrayFilter(callbackfn, th
                            "TypedArrayFilter");
     }
 
     // Step 4.
     var len = TypedArrayLength(O);
 
     // Step 5.
     if (arguments.length === 0)
-        ThrowError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter");
+        ThrowTypeError(JSMSG_MISSING_FUN_ARG, 0, "%TypedArray%.prototype.filter");
     if (!IsCallable(callbackfn))
-        ThrowError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
+        ThrowTypeError(JSMSG_NOT_FUNCTION, DecompileArg(0, callbackfn));
 
     // Step 6.
     var T = thisArg;
 
     // Step 7.
     var defaultConstructor = _ConstructorForTypedArray(O);
 
     // Steps 8-9.
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -4802,49 +4802,21 @@ Parser<FullParseHandler>::forStatement()
          * After the following if-else, pn2 will point to the name or
          * destructuring pattern on in's left. pn1 will point to the decl, if
          * any, else nullptr. Note that the "declaration with initializer" case
          * rewrites the loop-head, moving the decl and setting pn1 to nullptr.
          */
         if (isForDecl) {
             pn2 = pn1->pn_head;
             if ((pn2->isKind(PNK_NAME) && pn2->maybeExpr()) || pn2->isKind(PNK_ASSIGN)) {
-                /*
-                 * Declaration with initializer.
-                 *
-                 * Rewrite 'for (<decl> x = i in o)' where <decl> is 'var' or
-                 * 'const' to hoist the initializer or the entire decl out of
-                 * the loop head.
-                 */
-                if (headKind == PNK_FOROF) {
-                    report(ParseError, false, pn2, JSMSG_INVALID_FOR_OF_INIT);
-                    return null();
-                }
-                if (blockObj) {
-                    report(ParseError, false, pn2, JSMSG_INVALID_FOR_IN_INIT);
-                    return null();
-                }
-
-                hoistedVar = pn1;
-
-                /*
-                 * All of 'var x = i' is hoisted above 'for (x in o)'.
-                 *
-                 * Request JSOP_POP here since the var is for a simple
-                 * name (it is not a destructuring binding's left-hand
-                 * side) and it has an initializer.
-                 */
-                pn1->pn_xflags |= PNX_POPVAR;
-                pn1 = nullptr;
-
-                if (pn2->isKind(PNK_ASSIGN)) {
-                    pn2 = pn2->pn_left;
-                    MOZ_ASSERT(pn2->isKind(PNK_ARRAY) || pn2->isKind(PNK_OBJECT) ||
-                               pn2->isKind(PNK_NAME));
-                }
+                // We have a bizarre |for (var/const/let x = ... in/of ...)|
+                // loop erroneously permitted by ES1-5 but removed in ES6.
+                report(ParseError, false, pn2, JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,
+                       headKind == PNK_FOROF ? "of" : "in");
+                return null();
             }
         } else {
             /* Not a declaration. */
             MOZ_ASSERT(!blockObj);
             pn2 = pn1;
             pn1 = nullptr;
 
             if (!checkAndMarkAsAssignmentLhs(pn2, PlainAssignment))
--- a/js/src/jit-test/tests/auto-regress/bug590772.js
+++ b/js/src/jit-test/tests/auto-regress/bug590772.js
@@ -1,4 +1,4 @@
 // Binary: cache/js-dbg-32-f561f17e6c27-linux
 // Flags:
 //
-Reflect.parse("for (var x = 3 in []) { }")
+Reflect.parse("for (var x in []) { }")
--- a/js/src/jit-test/tests/auto-regress/bug596817.js
+++ b/js/src/jit-test/tests/auto-regress/bug596817.js
@@ -1,6 +1,12 @@
 // Binary: cache/js-dbg-32-a409054e1395-linux
 // Flags: -m
 //
 load(libdir + 'asserts.js');
 // value is not iterable
-assertThrowsInstanceOf(function(){for(var[x]=x>>x in[[]<[]]){[]}}, TypeError);
+(function() {
+  for (var [x]  in [[] < []])
+  {
+    // Just a useless expression.
+    [];
+  }
+})();
deleted file mode 100644
--- a/js/src/jit-test/tests/closures/bug540136.js
+++ /dev/null
@@ -1,17 +0,0 @@
-// |jit-test| error: TypeError
-
-(eval("\
- (function () {\
-   for (var[x] = function(){} in \
-     (function m(a) {\
-       if (a < 1) {\
-         x;\
-         return\
-       }\
-       return m(a - 1) + m(a - 2)\
-     })(7)\
-     (eval(\"\"))\
-   )\
-   ([])\
- })\
-"))()
deleted file mode 100644
--- a/js/src/jit-test/tests/closures/bug540348.js
+++ /dev/null
@@ -1,3 +0,0 @@
-(function() {
- for (var [e] = [] in (eval("for (b = 0; b < 6; ++b) gc()"))) {}
-})()
--- a/js/src/jit-test/tests/ion/recover-lambdas-bug1118911.js
+++ b/js/src/jit-test/tests/ion/recover-lambdas-bug1118911.js
@@ -1,9 +1,9 @@
 
 function test() {
   function f()
     k.apply(this, arguments);
   if (undefined >> undefined !== 0) {}
-  for (var [ v , c ]  = 0 in this.tracemonkey) {  }
+  for (var [ v , c ] in this.tracemonkey) {  }
 }
 try { test(); } catch(exc1) {}
 try { test(); } catch(exc1) {}
deleted file mode 100644
--- a/js/src/jit-test/tests/jaeger/bug583684.js
+++ /dev/null
@@ -1,8 +0,0 @@
-// |jit-test| error: TypeError
-(function () {
-    var b = e
-    for (var [e] = b in w) c
-})()
-
-/* Don't assert. */
-
deleted file mode 100644
--- a/js/src/jit-test/tests/jaeger/bug719758.js
+++ /dev/null
@@ -1,10 +0,0 @@
-
-function test() {
-  try {
-    for (var i = 0 in this) throw p;
-  } catch (e) {
-    if (i !== 94)
-      return "what";
-  }
-}
-test();
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -248,18 +248,17 @@ MSG_DEF(JSMSG_EMPTY_CONSEQUENT,        0
 MSG_DEF(JSMSG_EQUAL_AS_ASSIGN,         0, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?")
 MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0, JSEXN_SYNTAXERR, "export declarations may only appear at top level")
 MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,     0, JSEXN_SYNTAXERR, "finally without try")
 MSG_DEF(JSMSG_FROM_AFTER_IMPORT_SPEC_SET, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import specifier set")
 MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT,     2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER,    0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
 MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0, JSEXN_SYNTAXERR, "illegal character")
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level")
-MSG_DEF(JSMSG_INVALID_FOR_IN_INIT,     0, JSEXN_SYNTAXERR, "for-in loop let declaration may not have an initializer")
-MSG_DEF(JSMSG_INVALID_FOR_OF_INIT,     0, JSEXN_SYNTAXERR, "for-of loop variable declaration may not have an initializer")
+MSG_DEF(JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,1,JSEXN_SYNTAXERR,"for-{0} loop head declarations may not have initializers")
 MSG_DEF(JSMSG_IN_AFTER_FOR_NAME,       0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,         0, JSEXN_SYNTAXERR, "label not found")
 MSG_DEF(JSMSG_LET_CLASS_BINDING,       0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class")
 MSG_DEF(JSMSG_LET_COMP_BINDING,        0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
 MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,   1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
 MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW,  0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,        1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
 MSG_DEF(JSMSG_MISSING_BINARY_DIGITS,   0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
@@ -451,18 +450,18 @@ MSG_DEF(JSMSG_TYPEDOBJECT_BINARYARRAY_BA
 MSG_DEF(JSMSG_TYPEDOBJECT_HANDLE_UNATTACHED, 0, JSEXN_TYPEERR, "handle unattached")
 MSG_DEF(JSMSG_TYPEDOBJECT_STRUCTTYPE_BAD_ARGS, 0, JSEXN_RANGEERR, "invalid field descriptor")
 MSG_DEF(JSMSG_TYPEDOBJECT_TOO_BIG,     0, JSEXN_ERR, "Type is too large to allocate")
 
 // Typed array
 MSG_DEF(JSMSG_BAD_INDEX,               0, JSEXN_RANGEERR, "invalid or out-of-range index")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_ARGS,    0, JSEXN_TYPEERR, "invalid arguments")
 MSG_DEF(JSMSG_TYPED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
-MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,   0, JSEXN_ERR, "invalid or out-of-range index")
-MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_ERR, "argument {0} must be >= 0")
+MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX,   0, JSEXN_RANGEERR, "invalid or out-of-range index")
+MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG,1, JSEXN_RANGEERR, "argument {0} must be >= 0")
 MSG_DEF(JSMSG_TYPED_ARRAY_DETACHED,    0, JSEXN_TYPEERR, "attempting to access detached ArrayBuffer")
 
 // Shared array buffer
 MSG_DEF(JSMSG_SHARED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
 MSG_DEF(JSMSG_SHARED_ARRAY_BAD_LENGTH,  0, JSEXN_RANGEERR, "length argument out of range")
 
 // Shared typed array
 MSG_DEF(JSMSG_SHARED_TYPED_ARRAY_BAD_OBJECT,  0, JSEXN_TYPEERR, "invalid object argument")
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -819,16 +819,18 @@ class AutoAssertNoException
 
 /* Exposed intrinsics so that Ion may inline them. */
 bool intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_IsObject(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_ToInteger(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_ToString(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_IsCallable(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_ThrowError(JSContext* cx, unsigned argc, Value* vp);
+bool intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp);
+bool intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_NewDenseArray(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_IsConstructing(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_SubstringKernel(JSContext* cx, unsigned argc, Value* vp);
 
 bool intrinsic_UnsafePutElements(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_DefineDataProperty(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_UnsafeSetReservedSlot(JSContext* cx, unsigned argc, Value* vp);
 bool intrinsic_UnsafeGetReservedSlot(JSContext* cx, unsigned argc, Value* vp);
new file mode 100644
--- /dev/null
+++ b/js/src/tests/ecma_6/TypedArray/set-negative-offset.js
@@ -0,0 +1,35 @@
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/publicdomain/zero/1.0/
+ */
+
+var gTestfile = "set-negative-offset.js";
+//-----------------------------------------------------------------------------
+var BUGNUMBER = 1140752;
+var summary =
+  "%TypedArray%.prototype.set must throw a RangeError when passed a negative " +
+  "offset";
+
+print(BUGNUMBER + ": " + summary);
+
+/**************
+ * BEGIN TEST *
+ **************/
+
+try
+{
+  new Uint8Array().set([], -1);
+  throw new Error("didn't throw at all");
+}
+catch (e)
+{
+  assertEq(e instanceof RangeError, true,
+           "expected RangeError, instead got: " + e);
+}
+
+/******************************************************************************/
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
+
+print("Tests complete");
--- a/js/src/tests/js1_5/Regress/regress-252892.js
+++ b/js/src/tests/js1_5/Regress/regress-252892.js
@@ -14,18 +14,18 @@ printStatus (summary);
 
 var status; 
 
 var dodis;
 
 function f1(o){for(var x in o)printStatus(o[x]); return x}
 function f2(o){with(this)for(var x in o)printStatus(o[x]); return x}
 function f2novar(o){with(this)for(x in o)printStatus(o[x]); return x}
-function f3(i,o){for(var x=i in o)printStatus(o[x]); return x}
-function f4(i,o){with(this)for(var x=i in o)printStatus(o[x]); return x}
+function f3(i,o){for(var x in o)printStatus(o[x]); return x}
+function f4(i,o){with(this)for(var x in o)printStatus(o[x]); return x}
 
 var t=0;
 function assert(c)
 {
   ++t;
 
   status = summary + ' ' + inSection(t);
   expect = true;
@@ -33,33 +33,36 @@ function assert(c)
 
   if (!c)
   {
     printStatus(t + " FAILED!");
   }
   reportCompare(expect, actual, summary);
 }
 
-assert(f1([]) == undefined);
+assertEq(f1([]), undefined);
 
-assert(f1(['first']) == 0);
+assertEq(f1(['first']), "0");
 
-assert(f2([]) == undefined);
+assertEq(f2([]), undefined);
 
-assert(f2(['first']) == 0);
+assertEq(f2(['first']), "0");
 
-assert(f3(42, []) == 42);
+assertEq(f3(42, []), undefined);
 
-assert(f3(42, ['first']) == 0);
+assertEq(f3(42, ['first']), "0");
 
-assert(f4(42, []) == 42);
+assertEq(f4(42, []), undefined);
 
-assert(f4(42, ['first']) == 0);
+assertEq(f4(42, ['first']), "0");
 
 this.x = 41;
 
-assert(f2([]) == undefined);
+assertEq(f2([]), undefined);
 
-assert(f2novar([]) == 41);
+assertEq(f2novar([]), 41);
 
-assert(f2(['first']) == undefined);
+assertEq(f2(['first']), undefined);
 
-assert(f2novar(['first']) == 0);
+assertEq(f2novar(['first']), "0");
+
+if (typeof reportCompare === "function")
+  reportCompare(true, true);
--- a/js/src/tests/js1_5/extensions/regress-226078.js
+++ b/js/src/tests/js1_5/extensions/regress-226078.js
@@ -10,17 +10,17 @@ var actual = 'No Crash';
 var expect = 'No Crash';
 
 printBugNumber(BUGNUMBER);
 printStatus (summary);
  
 
 function SetLangHead(l){
   with(p){
-    for(var i=0 in x)
+    for(var i in x)
       if(getElementById("TxtH"+i)!=undefined)
         printStatus('huh');
   }
 }
 x=[0,1,2,3];
 p={getElementById: function (id){printStatus(uneval(this), id); return undefined;}};
 SetLangHead(1);
 
--- a/js/src/tests/js1_8/regress/regress-459185.js
+++ b/js/src/tests/js1_8/regress/regress-459185.js
@@ -17,17 +17,17 @@ test();
 function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
   try
   {
-    for (var {a: []} = 2 in []) { }
+    for (var {a: []} in []) { }
   }
   catch(ex)
   {
   }
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
--- a/js/src/tests/js1_8/regress/regress-459186.js
+++ b/js/src/tests/js1_8/regress/regress-459186.js
@@ -17,17 +17,17 @@ test();
 function test()
 {
   enterFunc ('test');
   printBugNumber(BUGNUMBER);
   printStatus (summary);
  
   try
   {
-    for (var [,{y}] = 1 in []) {}
+    for (var [,{y}] in []) {}
   }
   catch(ex)
   {
   }
 
   reportCompare(expect, actual, summary);
 
   exitFunc ('test');
--- a/js/src/tests/js1_8_1/regress/regress-452498-052.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-052.js
@@ -22,17 +22,17 @@ function test()
   printBugNumber(BUGNUMBER);
   printStatus (summary);
 
 // ------- Comment #52 From Jason Orendorff
 
 // Crash in NoteLValue, called from BindDestructuringVar.
 // NoteLValue assumes pn->pn_lexdef is non-null, but here
 // pn is itself the definition of x.
-  for (var [x]=[] in null) ;
+  for (var [x] in null) ;
 
 // This one only crashes when executed from a file.
 // Assertion failure: pn != dn->dn_uses, at ../jsparse.cpp:1131
   for (var f in null)
     ;
   var f = 1;
   (f)
 
--- a/js/src/tests/js1_8_1/regress/regress-452498-053.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-053.js
@@ -47,17 +47,17 @@ function test()
   }
 // Calls LinkUseToDef with pn->pn_defn == 1.
 //
 // If you say "var x;" first, then run this case, it gets further,
 // crashing in NoteLValue like the first case in comment 52.
 //
   try
   {
-    for (var [x] = x in y) var x;
+    for (var [x] in y) var x;
   }
   catch(ex)
   {
   }
 // Assertion failure: !pn2->pn_defn, at ../jsparse.h:461
 // Another case where some optimization is going on.
   try
   {
--- a/js/src/tests/js1_8_1/regress/regress-452498-117.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-117.js
@@ -70,17 +70,17 @@ function test()
     eval("((x1) > [(x)(function() { x;}) for each (x in x)])()");
   }
   catch(ex)
   {
   }
 
 // Assertion failure: pnu->pn_lexdef == dn, at ../jsemit.cpp:1817
 // =====
-  uneval(function(){for(var [arguments] = ({ get y(){} }) in y ) (x);});
+  uneval(function(){arguments = ({ get y(){} }); for(var [arguments] in y ) (x);});
 
 // Assertion failure: n != 0, at ../jsfun.cpp:2689
 // =====
   try
   {
     eval('(function(){{for(c in (function (){ for(x in (x1))window} )()) {const x = undefined;} }})();');
   }
   catch(ex)
--- a/js/src/tests/js1_8_1/regress/regress-452498-123.js
+++ b/js/src/tests/js1_8_1/regress/regress-452498-123.js
@@ -30,17 +30,17 @@ function test()
     eval('y = (function (){y} for (x in []);');
   }
   catch(ex)
   {
   }
 
 // Assertion failure: !(pn->pn_dflags & flag), at ../jsparse.h:651
 // =====
-  (function(){for(var x = arguments in []){} function x(){}})();
+  (function(){for(var x in [arguments]){} function x(){}})();
 
 // Assertion failure: dn->pn_defn, at ../jsemit.cpp:1873
 // =====
 
 
 // Requires -j:
 // =====
   (function(){ eval("for (x in ['', {}, '', {}]) { this; }" )})();
--- a/js/src/tests/js1_8_5/reflect-parse/for-loop-destructuring.js
+++ b/js/src/tests/js1_8_5/reflect-parse/for-loop-destructuring.js
@@ -31,27 +31,11 @@ assertError("for (const {a:x,b:y,c:z} in
 assertError("for (const [x,y,z] in foo);", SyntaxError);
 assertError("for (const x of foo);", SyntaxError);
 assertError("for (const {a:x,b:y,c:z} of foo);", SyntaxError);
 assertError("for (const [x,y,z] of foo);", SyntaxError);
 assertError("for each (const x in foo);", SyntaxError);
 assertError("for each (const {a:x,b:y,c:z} in foo);", SyntaxError);
 assertError("for each (const [x,y,z] in foo);", SyntaxError);
 
-// destructuring in for-in and for-each-in loop heads with initializers
-
-assertStmt("for (var {a:x,b:y,c:z} = 22 in foo);", forInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
-assertStmt("for (var [x,y,z] = 22 in foo);", forInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
-assertStmt("for each (var {a:x,b:y,c:z} = 22 in foo);", forEachInStmt(varDecl([{ id: axbycz, init: lit(22) }]), ident("foo"), emptyStmt));
-assertStmt("for each (var [x,y,z] = 22 in foo);", forEachInStmt(varDecl([{ id: xyz, init: lit(22) }]), ident("foo"), emptyStmt));
-assertError("for (x = 22 in foo);", SyntaxError);
-assertError("for ({a:x,b:y,c:z} = 22 in foo);", SyntaxError);
-assertError("for ([x,y,z] = 22 in foo);", SyntaxError);
-assertError("for (const x = 22 in foo);", SyntaxError);
-assertError("for (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError);
-assertError("for (const [x,y,z] = 22 in foo);", SyntaxError);
-assertError("for each (const x = 22 in foo);", SyntaxError);
-assertError("for each (const {a:x,b:y,c:z} = 22 in foo);", SyntaxError);
-assertError("for each (const [x,y,z] = 22 in foo);", SyntaxError);
-
 }
 
 runtest(test);
--- a/js/src/tests/js1_8_5/regress/regress-600137.js
+++ b/js/src/tests/js1_8_5/regress/regress-600137.js
@@ -1,17 +1,17 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 4 -*- */
 /*
  * Any copyright is dedicated to the Public Domain.
  * http://creativecommons.org/licenses/publicdomain/
  */
 
 if (typeof evalcx == 'function') {
     var src = 'try {\n' +
-    '    for (var [e] = /x/ in d) {\n' +
+    '    for (var [e] in d) {\n' +
     '        (function () {});\n' +
     '    }\n' +
     '} catch (e) {}\n' +
     'try {\n' +
     '    let(x = Object.freeze(this, /x/))\n' +
     '    e = {}.toString\n' +
     '    function y() {}\n' +
     '} catch (e) {}';
--- a/js/src/tests/js1_8_5/regress/regress-672892.js
+++ b/js/src/tests/js1_8_5/regress/regress-672892.js
@@ -1,8 +1,8 @@
 // Any copyright is dedicated to the Public Domain.
 // http://creativecommons.org/licenses/publicdomain/
 
 with (0)
-    for (var b = 0 in 0)  // don't assert in parser
+    for (var b in 0)  // don't assert in parser
 	;
 
 reportCompare(0, 0, 'ok');
--- a/js/src/vm/SelfHosting.cpp
+++ b/js/src/vm/SelfHosting.cpp
@@ -37,16 +37,17 @@
 #include "vm/NativeObject-inl.h"
 #include "vm/NumberObject-inl.h"
 #include "vm/StringObject-inl.h"
 
 using namespace js;
 using namespace js::selfhosted;
 
 using JS::AutoCheckCannotGC;
+using mozilla::UniquePtr;
 
 static void
 selfHosting_ErrorReporter(JSContext* cx, const char* message, JSErrorReport* report)
 {
     PrintError(cx, stderr, message, report, true);
 }
 
 bool
@@ -145,47 +146,80 @@ js::intrinsic_SubstringKernel(JSContext*
 static bool
 intrinsic_OwnPropertyKeys(JSContext* cx, unsigned argc, Value* vp)
 {
     CallArgs args = CallArgsFromVp(argc, vp);
     return GetOwnPropertyKeys(cx, args,
                               JSITER_OWNONLY | JSITER_HIDDEN | JSITER_SYMBOLS);
 }
 
-bool
-js::intrinsic_ThrowError(JSContext* cx, unsigned argc, Value* vp)
+static void
+ThrowErrorWithType(JSContext* cx, JSExnType type, const CallArgs& args)
 {
-    CallArgs args = CallArgsFromVp(argc, vp);
-    MOZ_ASSERT(args.length() >= 1);
     uint32_t errorNumber = args[0].toInt32();
 
 #ifdef DEBUG
     const JSErrorFormatString* efs = GetErrorMessage(nullptr, errorNumber);
     MOZ_ASSERT(efs->argCount == args.length() - 1);
+    MOZ_ASSERT(efs->exnType == type, "error-throwing intrinsic and error number are inconsistent");
 #endif
 
     JSAutoByteString errorArgs[3];
     for (unsigned i = 1; i < 4 && i < args.length(); i++) {
         RootedValue val(cx, args[i]);
         if (val.isInt32()) {
             JSString* str = ToString<CanGC>(cx, val);
             if (!str)
-                return false;
+                return;
             errorArgs[i - 1].encodeLatin1(cx, str);
         } else if (val.isString()) {
             errorArgs[i - 1].encodeLatin1(cx, val.toString());
         } else {
-            errorArgs[i - 1].initBytes(DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr()).release());
+            UniquePtr<char[], JS::FreePolicy> bytes =
+                DecompileValueGenerator(cx, JSDVG_SEARCH_STACK, val, NullPtr());
+            if (!bytes)
+                return;
+            errorArgs[i - 1].initBytes(bytes.release());
         }
         if (!errorArgs[i - 1])
-            return false;
+            return;
     }
 
     JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber,
                          errorArgs[0].ptr(), errorArgs[1].ptr(), errorArgs[2].ptr());
+}
+
+bool
+js::intrinsic_ThrowError(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() >= 1);
+
+    uint32_t errorNumber = args[0].toInt32();
+    ThrowErrorWithType(cx, JSExnType(GetErrorMessage(nullptr, errorNumber)->exnType), args);
+    return false;
+}
+
+bool
+js::intrinsic_ThrowRangeError(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() >= 1);
+
+    ThrowErrorWithType(cx, JSEXN_RANGEERR, args);
+    return false;
+}
+
+bool
+js::intrinsic_ThrowTypeError(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    MOZ_ASSERT(args.length() >= 1);
+
+    ThrowErrorWithType(cx, JSEXN_TYPEERR, args);
     return false;
 }
 
 /**
  * Handles an assertion failure in self-hosted code just like an assertion
  * failure in C++ code. Information about the failure can be provided in args[0].
  */
 static bool
@@ -946,16 +980,18 @@ static const JSFunctionSpec intrinsic_fu
     JS_FN("IsObject",                intrinsic_IsObject,                1,0),
     JS_FN("ToInteger",               intrinsic_ToInteger,               1,0),
     JS_FN("ToString",                intrinsic_ToString,                1,0),
     JS_FN("ToPropertyKey",           intrinsic_ToPropertyKey,           1,0),
     JS_FN("IsCallable",              intrinsic_IsCallable,              1,0),
     JS_FN("IsConstructor",           intrinsic_IsConstructor,           1,0),
     JS_FN("OwnPropertyKeys",         intrinsic_OwnPropertyKeys,         1,0),
     JS_FN("ThrowError",              intrinsic_ThrowError,              4,0),
+    JS_FN("ThrowRangeError",         intrinsic_ThrowRangeError,         4,0),
+    JS_FN("ThrowTypeError",          intrinsic_ThrowTypeError,          4,0),
     JS_FN("AssertionFailed",         intrinsic_AssertionFailed,         1,0),
     JS_FN("MakeConstructible",       intrinsic_MakeConstructible,       2,0),
     JS_FN("_IsConstructing",         intrinsic_IsConstructing,          0,0),
     JS_FN("_ConstructorForTypedArray", intrinsic_ConstructorForTypedArray, 1,0),
     JS_FN("DecompileArg",            intrinsic_DecompileArg,            2,0),
     JS_FN("RuntimeDefaultLocale",    intrinsic_RuntimeDefaultLocale,    0,0),
     JS_FN("SubstringKernel",         intrinsic_SubstringKernel,         3,0),
 
--- a/js/src/vm/TypedArrayCommon.h
+++ b/js/src/vm/TypedArrayCommon.h
@@ -681,18 +681,17 @@ class TypedArrayMethods
 
         int32_t offset = 0;
         if (args.length() > 1) {
             if (!ToInt32(cx, args[1], &offset))
                 return false;
 
             if (offset < 0 || uint32_t(offset) > target->length()) {
                 // the given offset is bogus
-                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
-                                     JSMSG_TYPED_ARRAY_BAD_INDEX, "2");
+                JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
                 return false;
             }
         }
 
         RootedObject arg0(cx, &args[0].toObject());
         if (IsAnyTypedArray(arg0)) {
             if (AnyTypedArrayLength(arg0) > target->length() - offset) {
                 JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_ARRAY_LENGTH);
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,21 +24,21 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 276;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 277;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
-static_assert(JSErr_Limit == 395,
+static_assert(JSErr_Limit == 394,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");
 
 class XDRBuffer {
   public:
     explicit XDRBuffer(JSContext* cx)
--- a/layout/base/FrameLayerBuilder.cpp
+++ b/layout/base/FrameLayerBuilder.cpp
@@ -3029,22 +3029,19 @@ void ContainerState::FinishPaintedLayerD
     // then it's not a "maybe" any more, and doesn't go into the dispatch-to-
     // content region.
     nsIntRegion maybeHitRegion = ScaleRegionToOutsidePixels(data->mMaybeHitRegion);
     regions.mDispatchToContentHitRegion.Sub(maybeHitRegion, regions.mHitRegion);
     regions.mDispatchToContentHitRegion.OrWith(
         ScaleRegionToOutsidePixels(data->mDispatchToContentHitRegion));
     regions.mHitRegion.OrWith(maybeHitRegion);
 
-    nsIntPoint translation = -GetTranslationForPaintedLayer(data->mLayer);
-    regions.mHitRegion.MoveBy(translation);
-    regions.mDispatchToContentHitRegion.MoveBy(translation);
-    regions.mNoActionRegion.MoveBy(translation);
-    regions.mHorizontalPanRegion.MoveBy(translation);
-    regions.mVerticalPanRegion.MoveBy(translation);
+    Matrix mat = layer->GetBaseTransform().As2D();
+    mat.Invert();
+    regions.ApplyTranslationAndScale(mat._31, mat._32, mat._11, mat._22);
 
     layer->SetEventRegions(regions);
   }
 }
 
 static bool
 IsItemAreaInWindowOpaqueRegion(nsDisplayListBuilder* aBuilder,
                                nsDisplayItem* aItem,
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -137,22 +137,21 @@ nsContainerFrame::RemoveFrame(ChildListI
   // told not to (kNoReflowPrincipalList).
   bool generateReflowCommand = true;
   if (kNoReflowPrincipalList == aListID) {
     generateReflowCommand = false;
   }
   nsIPresShell* shell = PresContext()->PresShell();
   nsContainerFrame* lastParent = nullptr;
   while (aOldFrame) {
-    //XXXfr probably should use StealFrame here. I'm not sure if we need to
-    //      check the overflow lists atm, but we'll need a prescontext lookup
-    //      for overflow containers once we can split abspos elements with
-    //      inline containing blocks.
     nsIFrame* oldFrameNextContinuation = aOldFrame->GetNextContinuation();
     nsContainerFrame* parent = aOldFrame->GetParent();
+    // Please note that 'parent' may not actually be where 'aOldFrame' lives.
+    // We really MUST use StealFrame() and nothing else here.
+    // @see nsInlineFrame::StealFrame for details.
     parent->StealFrame(aOldFrame, true);
     aOldFrame->Destroy();
     aOldFrame = oldFrameNextContinuation;
     if (parent != lastParent && generateReflowCommand) {
       shell->FrameNeedsReflow(parent, nsIPresShell::eTreeChange,
                               NS_FRAME_HAS_DIRTY_CHILDREN);
       lastParent = parent;
     }
@@ -1616,17 +1615,16 @@ nsContainerFrame::MoveOverflowToChildLis
   return DrainSelfOverflowList() || result;
 }
 
 bool
 nsContainerFrame::DrainSelfOverflowList()
 {
   AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
   if (overflowFrames) {
-    NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
     mFrames.AppendFrames(nullptr, *overflowFrames);
     return true;
   }
   return false;
 }
 
 nsIFrame*
 nsContainerFrame::GetNextInFlowChild(ContinuationTraversingState& aState,
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -199,16 +199,55 @@ nsInlineFrame::DestroyFrom(nsIFrame* aDe
     // nsIFrame::DestroyFrom depends on that to find the sticky scroll
     // container (an ancestor).
     nsIFrame* lineContainer = nsLayoutUtils::FindNearestBlockAncestor(this);
     DrainSelfOverflowListInternal(eForDestroy, lineContainer);
   }
   nsContainerFrame::DestroyFrom(aDestructRoot);
 }
 
+nsresult
+nsInlineFrame::StealFrame(nsIFrame* aChild,
+                          bool      aForceNormal)
+{
+  if (aChild->HasAnyStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER) &&
+      !aForceNormal) {
+    return nsContainerFrame::StealFrame(aChild, aForceNormal);
+  }
+
+  nsInlineFrame* parent = this;
+  bool removed = false;
+  do {
+    removed = parent->mFrames.StartRemoveFrame(aChild);
+    if (removed) {
+      break;
+    }
+
+    // We didn't find the child in our principal child list.
+    // Maybe it's on the overflow list?
+    nsFrameList* frameList = parent->GetOverflowFrames();
+    if (frameList) {
+      removed = frameList->ContinueRemoveFrame(aChild);
+      if (frameList->IsEmpty()) {
+        parent->DestroyOverflowList();
+      }
+      if (removed) {
+        break;
+      }
+    }
+
+    // Due to our "lazy reparenting" optimization 'aChild' might not actually
+    // be on any of our child lists, but instead in one of our next-in-flows.
+    parent = static_cast<nsInlineFrame*>(parent->GetNextInFlow());
+  } while (parent);
+
+  MOZ_ASSERT(removed, "nsInlineFrame::StealFrame: can't find aChild");
+  return removed ? NS_OK : NS_ERROR_UNEXPECTED;
+}
+
 void
 nsInlineFrame::BuildDisplayList(nsDisplayListBuilder*   aBuilder,
                                 const nsRect&           aDirtyRect,
                                 const nsDisplayListSet& aLists)
 {
   BuildDisplayListForInline(aBuilder, aDirtyRect, aLists);
 
   // The sole purpose of this is to trigger display of the selection
@@ -464,17 +503,16 @@ nsInlineFrame::AttributeChanged(int32_t 
 }
 
 bool
 nsInlineFrame::DrainSelfOverflowListInternal(DrainFlags aFlags,
                                              nsIFrame* aLineContainer)
 {
   AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
   if (overflowFrames) {
-    NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
     // The frames on our own overflowlist may have been pushed by a
     // previous lazilySetParentPointer Reflow so we need to ensure the
     // correct parent pointer.  This is sometimes skipped by Reflow.
     if (!(aFlags & eDontReparentFrames)) {
       nsIFrame* firstChild = overflowFrames->FirstChild();
       if (aLineContainer && aLineContainer->GetPrevContinuation()) {
         ReparentFloatsForInlineChild(aLineContainer, firstChild, true);
       }
@@ -1156,18 +1194,16 @@ nsFirstLineFrame::PullOverflowsFromPrevI
   }
 }
 
 /* virtual */ bool
 nsFirstLineFrame::DrainSelfOverflowList()
 {
   AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
   if (overflowFrames) {
-    NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
-
     bool result = !overflowFrames->IsEmpty();
     const nsFrameList::Slice& newFrames =
       mFrames.AppendFrames(nullptr, *overflowFrames);
     ReparentChildListStyle(PresContext(), newFrames, this);
     return result;
   }
   return false;
 }
--- a/layout/generic/nsInlineFrame.h
+++ b/layout/generic/nsInlineFrame.h
@@ -59,16 +59,17 @@ public:
 
   virtual bool IsEmpty() override;
   virtual bool IsSelfEmpty() override;
 
   virtual FrameSearchResult PeekOffsetCharacter(bool aForward, int32_t* aOffset,
                                      bool aRespectClusters = true) override;
   
   virtual void DestroyFrom(nsIFrame* aDestructRoot) override;
+  virtual nsresult StealFrame(nsIFrame* aChild, bool aForceNormal) override;
 
   // nsIHTMLReflow overrides
   virtual void AddInlineMinISize(nsRenderingContext *aRenderingContext,
                                  InlineMinISizeData *aData) override;
   virtual void AddInlinePrefISize(nsRenderingContext *aRenderingContext,
                                   InlinePrefISizeData *aData) override;
   virtual mozilla::LogicalSize
   ComputeSize(nsRenderingContext *aRenderingContext,
--- a/layout/tools/reftest/Makefile.in
+++ b/layout/tools/reftest/Makefile.in
@@ -1,19 +1,13 @@
 # vim: set shiftwidth=8 tabstop=8 autoindent noexpandtab copyindent:
 # 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/.
 
-DIST_FILES = install.rdf
-
-ifeq ($(MOZ_BUILD_APP),mobile/android)
-DIST_FILES += bootstrap.js
-endif
-
 # Used in install.rdf
 USE_EXTENSION_MANIFEST=1
 
 _DEST_DIR = $(DEPTH)/_tests/reftest
 
 _HARNESS_FILES = \
   $(srcdir)/runreftest.py \
   $(srcdir)/remotereftest.py \
--- a/layout/tools/reftest/moz.build
+++ b/layout/tools/reftest/moz.build
@@ -15,8 +15,13 @@ else:
     EXTRA_PP_COMPONENTS += [
         'reftest-cmdline.js',
         'reftest-cmdline.manifest',
     ]
 
 JAR_MANIFESTS += ['jar.mn']
 
 XPI_NAME = 'reftest'
+
+DIST_FILES += ['install.rdf']
+
+if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':
+    DIST_FILES += ['bootstrap.js']
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -210,16 +210,18 @@ struct cubeb_stream
   /* Lifetime considerations:
    * - client, render_client, audio_clock and audio_stream_volume are interface
    *   pointer to the IAudioClient.
    * - The lifetime for device_enumerator and notification_client, resampler,
    *   mix_buffer are the same as the cubeb_stream instance. */
 
   /* Main handle on the WASAPI stream. */
   IAudioClient * client;
+  /* Interface pointer to the clock, to estimate latency. */
+  IAudioClock * audio_clock;
   /* Interface pointer to use the event-driven interface. */
   IAudioRenderClient * render_client;
   /* Interface pointer to use the volume facilities. */
   IAudioStreamVolume * audio_stream_volume;
   /* Device enumerator to be able to be notified when the default
    * device change. */
   IMMDeviceEnumerator * device_enumerator;
   /* Device notification client, to be able to be notified when the default
@@ -231,18 +233,26 @@ struct cubeb_stream
   HANDLE shutdown_event;
   /* Set by OnDefaultDeviceChanged when a stream reconfiguration is required.
      The reconfiguration is handled by the render loop thread. */
   HANDLE reconfigure_event;
   /* This is set by WASAPI when we should refill the stream. */
   HANDLE refill_event;
   /* Each cubeb_stream has its own thread. */
   HANDLE thread;
-  /* We synthesize our clock from the callbacks. */
-  LONG64 clock;
+  /* We synthesize our clock from the callbacks. This in fractional frames, in
+   * the stream samplerate. */
+  double clock;
+  UINT64 prev_position;
+  /* This is the clock value last time we reset the stream. This is in
+   * fractional frames, in the stream samplerate. */
+  double base_clock;
+  /* The latency in frames of the stream */
+  UINT32 latency_frames;
+  UINT64 device_frequency;
   owned_critical_section * stream_reset_lock;
   /* Maximum number of frames we can be requested in a callback. */
   uint32_t buffer_frame_count;
   /* Resampler instance. Resampling will only happen if necessary. */
   cubeb_resampler * resampler;
   /* Buffer used to downmix or upmix to the number of channels the mixer has.
    * its size is |frames_to_bytes_before_mix(buffer_frame_count)|. */
   float * mix_buffer;
@@ -337,24 +347,38 @@ public:
   }
 private:
   /* refcount for this instance, necessary to implement MSCOM semantics. */
   LONG ref_count;
   HANDLE reconfigure_event;
 };
 
 namespace {
-void clock_add(cubeb_stream * stm, LONG64 value)
+void clock_add(cubeb_stream * stm, double value)
 {
-  InterlockedExchangeAdd64(&stm->clock, value);
+  auto_lock lock(stm->stream_reset_lock);
+  stm->clock += value;
 }
 
-LONG64 clock_get(cubeb_stream * stm)
+UINT64 clock_get(cubeb_stream * stm)
+{
+  auto_lock lock(stm->stream_reset_lock);
+  return UINT64(stm->clock);
+}
+
+void latency_set(cubeb_stream * stm, UINT32 value)
 {
-  return InterlockedExchangeAdd64(&stm->clock, 0);
+  auto_lock lock(stm->stream_reset_lock);
+  stm->latency_frames = value;
+}
+
+UINT32 latency_get(cubeb_stream * stm)
+{
+  auto_lock lock(stm->stream_reset_lock);
+  return stm->latency_frames;
 }
 
 bool should_upmix(cubeb_stream * stream)
 {
   return stream->mix_params.channels > stream->stream_params.channels;
 }
 
 bool should_downmix(cubeb_stream * stream)
@@ -438,18 +462,16 @@ refill(cubeb_stream * stm, float * data,
   if (should_upmix(stm) || should_downmix(stm)) {
     dest = stm->mix_buffer;
   } else {
     dest = data;
   }
 
   long out_frames = cubeb_resampler_fill(stm->resampler, dest, frames_needed);
 
-  clock_add(stm, roundf(frames_needed * stream_to_mix_samplerate_ratio(stm)));
-
   /* XXX: Handle this error. */
   if (out_frames < 0) {
     XASSERT(false);
   }
 
   /* Go in draining mode if we got fewer frames than requested. */
   if (out_frames < frames_needed) {
     LOG("draining.\n");
@@ -511,26 +533,30 @@ wasapi_stream_render_loop(LPVOID stream)
       timeout_count = 0;
     }
     switch (waitResult) {
     case WAIT_OBJECT_0: { /* shutdown */
       is_playing = false;
       /* We don't check if the drain is actually finished here, we just want to
        * shutdown. */
       if (stm->draining) {
+        LOG("DRAINED");
         stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
       }
+
       continue;
     }
     case WAIT_OBJECT_0 + 1: { /* reconfigure */
       /* Close the stream */
       stm->client->Stop();
       {
         auto_lock lock(stm->stream_reset_lock);
         close_wasapi_stream(stm);
+        stm->base_clock = stm->clock;
+        stm->latency_frames = 0;
         /* Reopen a stream and start it immediately. This will automatically pick the
          * new default device for this role. */
         int r = setup_wasapi_stream(stm);
         if (r != CUBEB_OK) {
           /* Don't destroy the stream here, since we expect the caller to do
              so after the error has propagated via the state callback. */
           is_playing = false;
           hr = -1;
@@ -546,26 +572,36 @@ wasapi_stream_render_loop(LPVOID stream)
       hr = stm->client->GetCurrentPadding(&padding);
       if (FAILED(hr)) {
         LOG("Failed to get padding\n");
         is_playing = false;
         continue;
       }
       XASSERT(padding <= stm->buffer_frame_count);
 
+      long available = stm->buffer_frame_count - padding;
+
+      clock_add(stm, available * stream_to_mix_samplerate_ratio(stm));
+
+      UINT64 position = 0;
+      HRESULT hr = stm->audio_clock->GetPosition(&position, NULL);
+      if (SUCCEEDED(hr)) {
+        double playing_frame = stm->mix_params.rate * (double)position / stm->device_frequency;
+        double last_written_frame = stm->clock - stm->base_clock;
+        latency_set(stm, max(last_written_frame - playing_frame, 0));
+      }
+
       if (stm->draining) {
         if (padding == 0) {
           stm->state_callback(stm, stm->user_ptr, CUBEB_STATE_DRAINED);
           is_playing = false;
         }
         continue;
       }
 
-      long available = stm->buffer_frame_count - padding;
-
       if (available == 0) {
         continue;
       }
 
       BYTE * data;
       hr = stm->render_client->GetBuffer(available, &data);
       if (SUCCEEDED(hr)) {
         refill(stm, reinterpret_cast<float *>(data), available);
@@ -1040,16 +1076,29 @@ int setup_wasapi_stream(cubeb_stream * s
   }
 
   hr = stm->client->SetEventHandle(stm->refill_event);
   if (FAILED(hr)) {
     LOG("Could set the event handle for the client %x.\n", hr);
     return CUBEB_ERROR;
   }
 
+  hr = stm->client->GetService(__uuidof(IAudioClock),
+                               (void **)&stm->audio_clock);
+  if (FAILED(hr)) {
+    LOG("Could not get IAudioClock: %x.\n", hr);
+    return CUBEB_ERROR;
+  }
+
+  hr = stm->audio_clock->GetFrequency(&stm->device_frequency);
+  if (FAILED(hr)) {
+    LOG("Could not get the device frequency from IAudioClock: %x.\n", hr);
+    return CUBEB_ERROR;
+  }
+
   hr = stm->client->GetService(__uuidof(IAudioRenderClient),
                                (void **)&stm->render_client);
   if (FAILED(hr)) {
     LOG("Could not get the render client %x.\n", hr);
     return CUBEB_ERROR;
   }
 
   hr = stm->client->GetService(__uuidof(IAudioStreamVolume),
@@ -1098,21 +1147,24 @@ wasapi_stream_init(cubeb * context, cube
 
   stm->context = context;
   stm->data_callback = data_callback;
   stm->state_callback = state_callback;
   stm->user_ptr = user_ptr;
   stm->stream_params = stream_params;
   stm->draining = false;
   stm->latency = latency;
-  stm->clock = 0;
+  stm->clock = 0.0;
+  stm->base_clock = 0.0;
+  stm->latency_frames = 0;
 
   /* Null out WASAPI-specific state */
   stm->resampler = NULL;
   stm->client = NULL;
+  stm->audio_clock = NULL;
   stm->render_client = NULL;
   stm->audio_stream_volume = NULL;
   stm->device_enumerator = NULL;
   stm->notification_client = NULL;
 
   stm->stream_reset_lock = new owned_critical_section();
 
   stm->reconfigure_event = CreateEvent(NULL, 0, 0, NULL);
@@ -1160,16 +1212,19 @@ void close_wasapi_stream(cubeb_stream * 
   stm->stream_reset_lock->assert_current_thread_owns();
 
   SafeRelease(stm->client);
   stm->client = NULL;
 
   SafeRelease(stm->render_client);
   stm->render_client = NULL;
 
+  SafeRelease(stm->audio_clock);
+  stm->audio_clock = NULL;
+
   SafeRelease(stm->audio_stream_volume);
   stm->audio_stream_volume = NULL;
 
   if (stm->resampler) {
     cubeb_resampler_destroy(stm->resampler);
     stm->resampler = NULL;
   }
 
@@ -1269,17 +1324,30 @@ int wasapi_stream_stop(cubeb_stream * st
 
   return CUBEB_OK;
 }
 
 int wasapi_stream_get_position(cubeb_stream * stm, uint64_t * position)
 {
   XASSERT(stm && position);
 
-  *position = clock_get(stm);
+  UINT64 clock = clock_get(stm);
+  UINT32 latency = latency_get(stm);
+
+  *position = clock >= latency ? clock - latency : 0;
+
+  /* This can happen if the clock does not increase, for example, because the
+   * WASAPI endpoint buffer is full for now, and the latency naturally decreases
+   * because more samples have been played by the mixer process.
+   * We return the previous value to keep the clock monotonicaly increasing. */
+  if (*position < stm->prev_position) {
+    *position = stm->prev_position;
+  }
+
+  stm->prev_position = *position;
 
   return CUBEB_OK;
 }
 
 int wasapi_stream_get_latency(cubeb_stream * stm, uint32_t * latency)
 {
   XASSERT(stm && latency);
 
--- a/media/libstagefright/system/core/include/cutils/properties.h
+++ b/media/libstagefright/system/core/include/cutils/properties.h
@@ -12,17 +12,17 @@
  * 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.
  */
 
 #ifndef __CUTILS_PROPERTIES_H
 #define __CUTILS_PROPERTIES_H
 
-#include <sys/cdefs.h>
+#include <sys/types.h>
 #include <stddef.h>
 
 #ifdef __cplusplus
 extern "C" {
 #endif
 
 /* System properties are *small* name value pairs managed by the
 ** property service.  If your data doesn't fit in the provided
--- a/media/mtransport/nr_timer.cpp
+++ b/media/mtransport/nr_timer.cpp
@@ -62,93 +62,182 @@
 extern "C" {
 #include "nr_api.h"
 #include "async_timer.h"
 }
 
 
 namespace mozilla {
 
-class nrappkitTimerCallback : public nsITimerCallback
-{
-public:
-  // We're going to release ourself in the callback, so we need to be threadsafe
-  NS_DECL_THREADSAFE_ISUPPORTS
-  NS_DECL_NSITIMERCALLBACK
-
-  nrappkitTimerCallback(NR_async_cb cb, void *cb_arg,
-                        const char *function, int line)
+class nrappkitCallback  {
+ public:
+  nrappkitCallback(NR_async_cb cb, void *cb_arg,
+                   const char *function, int line)
     : cb_(cb), cb_arg_(cb_arg), function_(function), line_(line) {
   }
+  virtual ~nrappkitCallback() {}
 
-private:
-  virtual ~nrappkitTimerCallback() {}
+  virtual void Cancel() = 0;
 
 protected:
   /* additional members */
   NR_async_cb cb_;
   void *cb_arg_;
   std::string function_;
   int line_;
 };
 
+class nrappkitTimerCallback : public nrappkitCallback,
+                              public nsITimerCallback {
+ public:
+  // We're going to release ourself in the callback, so we need to be threadsafe
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSITIMERCALLBACK
+
+  nrappkitTimerCallback(NR_async_cb cb, void *cb_arg,
+                        const char *function, int line,
+                        nsITimer *timer)
+      : nrappkitCallback(cb, cb_arg, function, line),
+        timer_(timer) {}
+
+  virtual void Cancel() override {
+    AddRef();  // Cancelling the timer causes the callback it holds to
+               // be released. AddRef() keeps us alive.
+    timer_->Cancel();
+    timer_->Release();
+    Release(); // Will cause deletion of this object.
+  }
+
+ private:
+  nsITimer* timer_;
+  virtual ~nrappkitTimerCallback() {}
+};
+
 NS_IMPL_ISUPPORTS(nrappkitTimerCallback, nsITimerCallback)
 
 NS_IMETHODIMP nrappkitTimerCallback::Notify(nsITimer *timer) {
   r_log(LOG_GENERIC, LOG_DEBUG, "Timer callback fired (set in %s:%d)",
         function_.c_str(), line_);
-
+  MOZ_ASSERT(timer == timer_);
   cb_(0, 0, cb_arg_);
 
   // Allow the timer to go away.
   timer->Release();
   return NS_OK;
 }
+
+class nrappkitScheduledCallback : public nrappkitCallback {
+ public:
+
+  nrappkitScheduledCallback(NR_async_cb cb, void *cb_arg,
+                            const char *function, int line)
+      : nrappkitCallback(cb, cb_arg, function, line) {}
+
+  void Run() {
+    if (cb_) {
+      cb_(0, 0, cb_arg_);
+    }
+  }
+
+  virtual void Cancel() override {
+    cb_ = nullptr;
+  }
+
+  ~nrappkitScheduledCallback() {}
+};
+
 }  // close namespace
 
 
 using namespace mozilla;
 
-// These timers must only be used from the STS thread.
-// This function is a helper that enforces that.
-static void CheckSTSThread() {
+static nsCOMPtr<nsIEventTarget> GetSTSThread() {
   nsresult rv;
 
   nsCOMPtr<nsIEventTarget> sts_thread;
 
   sts_thread = do_GetService(NS_SOCKETTRANSPORTSERVICE_CONTRACTID, &rv);
+  MOZ_ASSERT(NS_SUCCEEDED(rv));
 
-  MOZ_ASSERT(NS_SUCCEEDED(rv));
+  return sts_thread;
+}
+
+// These timers must only be used from the STS thread.
+// This function is a helper that enforces that.
+static void CheckSTSThread() {
+  nsCOMPtr<nsIEventTarget> sts_thread = GetSTSThread();
+
   ASSERT_ON_THREAD(sts_thread);
 }
 
-int NR_async_timer_set(int timeout, NR_async_cb cb, void *arg, char *func,
-                       int l, void **handle) {
+static int nr_async_timer_set_zero(NR_async_cb cb, void *arg,
+                                   char *func, int l,
+                                   nrappkitCallback **handle) {
+  nrappkitScheduledCallback* callback(new nrappkitScheduledCallback(
+      cb, arg, func, l));
+
+  nsresult rv = GetSTSThread()->Dispatch(WrapRunnable(
+      nsAutoPtr<nrappkitScheduledCallback>(callback),
+      &nrappkitScheduledCallback::Run),
+                        NS_DISPATCH_NORMAL);
+  if (NS_FAILED(rv))
+    return R_FAILED;
+
+  *handle = callback;
+
+  // On exit to this function, the only strong reference to callback is in
+  // the Runnable. Because we are redispatching to the same thread,
+  // this is always safe.
+  return 0;
+}
+
+static int nr_async_timer_set_nonzero(int timeout, NR_async_cb cb, void *arg,
+                                      char *func, int l,
+                                      nrappkitCallback **handle) {
   nsresult rv;
   CheckSTSThread();
 
   nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID, &rv);
   if (NS_FAILED(rv)) {
     return(R_FAILED);
   }
 
-  rv = timer->InitWithCallback(new nrappkitTimerCallback(cb, arg, func, l),
-                               timeout, nsITimer::TYPE_ONE_SHOT);
+  nrappkitTimerCallback* callback =
+      new nrappkitTimerCallback(cb, arg, func, l, timer);
+  rv = timer->InitWithCallback(callback, timeout, nsITimer::TYPE_ONE_SHOT);
   if (NS_FAILED(rv)) {
     return R_FAILED;
   }
 
   // We need an AddRef here to keep the timer alive, per the spec.
   timer->AddRef();
 
+  *handle = callback;
+
+  return 0;
+}
+
+int NR_async_timer_set(int timeout, NR_async_cb cb, void *arg,
+                       char *func, int l, void **handle) {
+  CheckSTSThread();
+
+  nrappkitCallback *callback;
+  int r;
+
+  if (!timeout) {
+    r = nr_async_timer_set_zero(cb, arg, func, l, &callback);
+  } else {
+    r = nr_async_timer_set_nonzero(timeout, cb, arg, func, l, &callback);
+  }
+
+  if (r)
+    return r;
+
   if (handle)
-    *handle = timer.get();
-  // Bug 818806: if we have no handle to the timer, we have no way to avoid
-  // it leaking (though not the callback object) if it never fires (or if
-  // we exit before it fires).
+    *handle = callback;
 
   return 0;
 }
 
 int NR_async_schedule(NR_async_cb cb, void *arg, char *func, int l) {
   // No need to check the thread because we check it next in the
   // timer set.
   return NR_async_timer_set(0, cb, arg, func, l, nullptr);
@@ -158,17 +247,14 @@ int NR_async_timer_cancel(void *handle) 
   // Check for the handle being nonzero because sometimes we get
   // no-op cancels that aren't on the STS thread. This can be
   // non-racy as long as the upper-level code is careful.
   if (!handle)
     return 0;
 
   CheckSTSThread();
 
-  nsITimer *timer = static_cast<nsITimer *>(handle);
-
-  timer->Cancel();
-  // Allow the timer to go away.
-  timer->Release();
+  nrappkitCallback* callback = static_cast<nrappkitCallback *>(handle);
+  callback->Cancel();
 
   return 0;
 }
 
--- a/media/mtransport/test/nrappkit_unittest.cpp
+++ b/media/mtransport/test/nrappkit_unittest.cpp
@@ -39,20 +39,39 @@ class TimerTest : public ::testing::Test
 
     test_utils->sts_target()->Dispatch(
         WrapRunnableRet(this, &TimerTest::ArmTimer_w, timeout, &ret),
         NS_DISPATCH_SYNC);
 
     return ret;
   }
 
+  int ArmCancelTimer(int timeout) {
+    int ret;
+
+    test_utils->sts_target()->Dispatch(
+        WrapRunnableRet(this, &TimerTest::ArmCancelTimer_w, timeout, &ret),
+        NS_DISPATCH_SYNC);
+
+    return ret;
+  }
+
   int ArmTimer_w(int timeout) {
     return NR_ASYNC_TIMER_SET(timeout, cb, this, &handle_);
   }
 
+  int ArmCancelTimer_w(int timeout) {
+    int r;
+    r = ArmTimer_w(timeout);
+    if (r)
+      return r;
+
+    return CancelTimer_w();
+  }
+
   int CancelTimer() {
     int ret;
 
     test_utils->sts_target()->Dispatch(
         WrapRunnableRet(this, &TimerTest::CancelTimer_w, &ret),
         NS_DISPATCH_SYNC);
 
     return ret;
@@ -69,17 +88,17 @@ class TimerTest : public ::testing::Test
         WrapRunnableRet(this, &TimerTest::Schedule_w, &ret),
         NS_DISPATCH_SYNC);
 
     return ret;
   }
 
   int Schedule_w() {
     NR_ASYNC_SCHEDULE(cb, this);
-    
+
     return 0;
   }
 
 
   static void cb(NR_SOCKET r, int how, void *arg) {
     std::cerr << "Timer fired " << std::endl;
 
     TimerTest *t = static_cast<TimerTest *>(arg);
@@ -100,16 +119,22 @@ TEST_F(TimerTest, SimpleTimer) {
 
 TEST_F(TimerTest, CancelTimer) {
   ArmTimer(1000);
   CancelTimer();
   PR_Sleep(2000);
   ASSERT_FALSE(fired_);
 }
 
+TEST_F(TimerTest, CancelTimer0) {
+  ArmCancelTimer(0);
+  PR_Sleep(100);
+  ASSERT_FALSE(fired_);
+}
+
 TEST_F(TimerTest, ScheduleTest) {
   Schedule();
   ASSERT_TRUE_WAIT(fired_, 1000);
 }
 
 int main(int argc, char **argv) {
   test_utils = new MtransportTestUtils();
 
--- a/mobile/android/base/Makefile.in
+++ b/mobile/android/base/Makefile.in
@@ -1,16 +1,12 @@
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
-DIST_FILES := \
-  package-name.txt.in \
-  $(NULL)
-
 ifneq (,$(findstring -march=armv7,$(OS_CFLAGS)))
 MIN_CPU_VERSION=7
 else
 MIN_CPU_VERSION=5
 endif
 
 MOZ_APP_BUILDID=$(shell cat $(DEPTH)/config/buildid)
 
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -883,8 +883,10 @@ if CONFIG['MOZ_CRASHREPORTER']:
 
 if CONFIG['MOZ_ANDROID_MLS_STUMBLER']:
     main.included_projects += ['../FennecStumbler']
     main.referenced_projects += ['../FennecStumbler']
 
 if CONFIG['MOZ_ANDROID_SEARCH_ACTIVITY']:
     # The Search Activity code is built as part of Fennec, so we follow suit in Eclipse.
     main.add_classpathentry('search', TOPSRCDIR + '/mobile/android/search/java', dstdir='search')
+
+DIST_FILES += ['package-name.txt.in']
--- a/modules/libpref/init/all.js
+++ b/modules/libpref/init/all.js
@@ -3010,21 +3010,18 @@ pref("plugin.allow.asyncdrawing", false)
 // when a network address is unreachable.
 pref("network.autodial-helper.enabled", true);
 
 // Switch the keyboard layout per window
 pref("intl.keyboard.per_window_layout", false);
 
 #ifdef NS_ENABLE_TSF
 // Enable/Disable TSF support on Vista or later.
-#ifndef RELEASE_BUILD
 pref("intl.tsf.enable", true);
-#else
-pref("intl.tsf.enable", false);
-#endif
+
 // Force enable TSF even on WinXP or WinServer 2003.
 // Be aware, TSF framework on prior to Vista is not enough stable.
 pref("intl.tsf.force_enable", false);
 
 // Support IMEs implemented with IMM in TSF mode.
 pref("intl.tsf.support_imm", true);
 
 // Enables/Disables hack for specific TIP.
@@ -4011,47 +4008,33 @@ pref("layers.max-active", -1);
 // If this is set the tile size will only be treated as a suggestion.
 // On B2G we will round this to the stride of the underlying allocation.
 // On any platform we may later use the screen size and ignore
 // tile-width/tile-height entirely. Its recommended to turn this off
 // if you change the tile size.
 pref("layers.tiles.adjust", true);
 
 // Set the default values, and then override per-platform as needed
-pref("layers.offmainthreadcomposition.enabled", false);
+pref("layers.offmainthreadcomposition.enabled", true);
 // Compositor target frame rate. NOTE: If vsync is enabled the compositor
 // frame rate will still be capped.
 // -1 -> default (match layout.frame_rate or 60 FPS)
 // 0  -> full-tilt mode: Recomposite even if not transaction occured.
 pref("layers.offmainthreadcomposition.frame-rate", -1);
 
 // Asynchonous video compositing using the ImageBridge IPDL protocol.
 // requires off-main-thread compositing.
 pref("layers.async-video.enabled", true);
 pref("layers.async-video-oop.enabled",true);
 
-#ifdef XP_WIN
-pref("layers.offmainthreadcomposition.enabled", true);
-#endif
-
-#ifdef MOZ_WIDGET_QT
-pref("layers.offmainthreadcomposition.enabled", true);
-#endif
-
 #ifdef XP_MACOSX
-pref("layers.offmainthreadcomposition.enabled", true);
 pref("layers.enable-tiles", true);
 pref("layers.tiled-drawtarget.enabled", true);
 #endif
 
-// ANDROID covers android and b2g
-#ifdef ANDROID
-pref("layers.offmainthreadcomposition.enabled", true);
-#endif
-
 // same effect as layers.offmainthreadcomposition.enabled, but specifically for
 // use with tests.
 pref("layers.offmainthreadcomposition.testing.enabled", false);
 
 // whether to allow use of the basic compositor
 pref("layers.offmainthreadcomposition.force-basic", false);
 
 // Whether to animate simple opacity and transforms on the compositor
--- a/python/mozbuild/mozbuild/backend/recursivemake.py
+++ b/python/mozbuild/mozbuild/backend/recursivemake.py
@@ -27,16 +27,17 @@ import mozpack.path as mozpath
 
 from .common import CommonBackend
 from ..frontend.data import (
     AndroidEclipseProjectData,
     ConfigFileSubstitution,
     ContextDerived,
     ContextWrapped,
     Defines,
+    DistFiles,
     DirectoryTraversal,
     Exports,
     ExternalLibrary,
     FinalTargetFiles,
     GeneratedFile,
     GeneratedInclude,
     GeneratedSources,
     HostLibrary,
@@ -552,16 +553,25 @@ class RecursiveMakeBackend(CommonBackend
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, HostLibrary):
             self._process_host_library(obj, backend_file)
             self._process_linked_libraries(obj, backend_file)
 
         elif isinstance(obj, FinalTargetFiles):
             self._process_final_target_files(obj, obj.files, obj.target)
+
+        elif isinstance(obj, DistFiles):
+            # We'd like to install these via manifests as preprocessed files.
+            # But they currently depend on non-standard flags being added via
+            # some Makefiles, so for now we just pass them through to the
+            # underlying Makefile.in.
+            for f in obj.files:
+                backend_file.write('DIST_FILES += %s\n' % f)
+
         else:
             return
         obj.ack()
 
     def _fill_root_mk(self):
         """
         Create two files, root.mk and root-deps.mk, the first containing
         convenience variables, and the other dependency definitions for a
--- a/python/mozbuild/mozbuild/frontend/context.py
+++ b/python/mozbuild/mozbuild/frontend/context.py
@@ -725,16 +725,22 @@ VARIABLES = {
            FINAL_TARGET_FILES.images['do-not-use'] += ['bar.svg']
         """, None),
 
     'DISABLE_STL_WRAPPING': (bool, bool,
         """Disable the wrappers for STL which allow it to work with C++ exceptions
         disabled.
         """, None),
 
+    'DIST_FILES': (StrictOrderingOnAppendList, list,
+        """Additional files to place in ``FINAL_TARGET`` (typically ``dist/bin``).
+
+        Unlike ``FINAL_TARGET_FILES``, these files are preprocessed.
+        """, 'libs'),
+
     'EXTRA_COMPONENTS': (StrictOrderingOnAppendList, list,
         """Additional component files to distribute.
 
        This variable contains a list of files to copy into
        ``$(FINAL_TARGET)/components/``.
         """, 'misc'),
 
     'EXTRA_JS_MODULES': (HierarchicalStringList, list,
--- a/python/mozbuild/mozbuild/frontend/data.py
+++ b/python/mozbuild/mozbuild/frontend/data.py
@@ -848,16 +848,32 @@ class FinalTargetFiles(ContextDerived):
     __slots__ = ('files', 'target')
 
     def __init__(self, sandbox, files, target):
         ContextDerived.__init__(self, sandbox)
         self.files = files
         self.target = target
 
 
+class DistFiles(ContextDerived):
+    """Sandbox container object for FINAL_TARGET_FILES, which is a
+    HierarchicalStringList.
+
+    We need an object derived from ContextDerived for use in the backend, so
+    this object fills that role. It just has a reference to the underlying
+    HierarchicalStringList, which is created when parsing DIST_FILES.
+    """
+    __slots__ = ('files', 'target')
+
+    def __init__(self, sandbox, files, target):
+        ContextDerived.__init__(self, sandbox)
+        self.files = files
+        self.target = target
+
+
 class GeneratedFile(ContextDerived):
     """Represents a generated file."""
 
     __slots__ = (
         'script',
         'method',
         'output',
         'inputs',
--- a/python/mozbuild/mozbuild/frontend/emitter.py
+++ b/python/mozbuild/mozbuild/frontend/emitter.py
@@ -23,16 +23,17 @@ import mozpack.path as mozpath
 import manifestparser
 import reftest
 import mozinfo
 
 from .data import (
     ConfigFileSubstitution,
     ContextWrapped,
     Defines,
+    DistFiles,
     DirectoryTraversal,
     Exports,
     FinalTargetFiles,
     GeneratedEventWebIDLFile,
     GeneratedFile,
     GeneratedInclude,
     GeneratedSources,
     GeneratedWebIDLFile,
@@ -636,16 +637,26 @@ class TreeMetadataEmitter(LoggingMixin):
                 raise SandboxValidationError('Path specified in LOCAL_INCLUDES '
                     'does not exist: %s (resolved to %s)' % (local_include, actual_include), context)
             yield LocalInclude(context, local_include)
 
         final_target_files = context.get('FINAL_TARGET_FILES')
         if final_target_files:
             yield FinalTargetFiles(context, final_target_files, context['FINAL_TARGET'])
 
+        dist_files = context.get('DIST_FILES')
+        if dist_files:
+            for f in dist_files:
+                path = os.path.join(context.srcdir, f)
+                if not os.path.exists(path):
+                    raise SandboxValidationError('File listed in DIST_FILES '
+                        'does not exist: %s' % f, context)
+
+            yield DistFiles(context, dist_files, context['FINAL_TARGET'])
+
         self._handle_libraries(context)
 
         for obj in self._process_test_manifests(context):
             yield obj
 
         for obj in self._process_jar_manifests(context):
             yield obj
 
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/backend/data/dist-files/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+DIST_FILES += [
+    'install.rdf',
+    'main.js',
+]
--- a/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
+++ b/python/mozbuild/mozbuild/test/backend/test_recursivemake.py
@@ -622,16 +622,31 @@ class TestRecursiveMakeBackend(BackendTe
         for key, expected_rules in expected.iteritems():
             backend_path = mozpath.join(key, 'backend.mk')
             lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
             found = [str for str in lines if
                 str.startswith('FINAL_TARGET') or str.startswith('XPI_NAME') or
                 str.startswith('DIST_SUBDIR')]
             self.assertEqual(found, expected_rules)
 
+    def test_dist_files(self):
+        """Test that DIST_FILES is written to backend.mk correctly."""
+        env = self._consume('dist-files', RecursiveMakeBackend)
+
+        backend_path = mozpath.join(env.topobjdir, 'backend.mk')
+        lines = [l.strip() for l in open(backend_path, 'rt').readlines()[2:]]
+
+        expected = [
+            'DIST_FILES += install.rdf',
+            'DIST_FILES += main.js',
+        ]
+
+        found = [str for str in lines if str.startswith('DIST_FILES')]
+        self.assertEqual(found, expected)
+
     def test_config(self):
         """Test that CONFIGURE_SUBST_FILES and CONFIGURE_DEFINE_FILES are
         properly handled."""
         env = self._consume('test_config', RecursiveMakeBackend)
 
         self.assertEqual(
             open(os.path.join(env.topobjdir, 'file'), 'r').readlines(), [
                 '#ifdef foo\n',
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files-missing/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+DIST_FILES += [
+    'install.rdf',
+    'main.js',
+]
new file mode 100644
new file mode 100644
new file mode 100644
--- /dev/null
+++ b/python/mozbuild/mozbuild/test/frontend/data/dist-files/moz.build
@@ -0,0 +1,8 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# Any copyright is dedicated to the Public Domain.
+# http://creativecommons.org/publicdomain/zero/1.0/
+
+DIST_FILES += [
+    'install.rdf',
+    'main.js',
+]
--- a/python/mozbuild/mozbuild/test/frontend/test_emitter.py
+++ b/python/mozbuild/mozbuild/test/frontend/test_emitter.py
@@ -7,16 +7,17 @@ from __future__ import unicode_literals
 import os
 import unittest
 
 from mozunit import main
 
 from mozbuild.frontend.data import (
     ConfigFileSubstitution,
     Defines,
+    DistFiles,
     DirectoryTraversal,
     Exports,
     GeneratedFile,
     GeneratedInclude,
     GeneratedSources,
     HostSources,
     IPDLFile,
     JARManifest,
@@ -806,10 +807,31 @@ class TestEmitterBasic(unittest.TestCase
             '.mm': ['objc1.mm', 'objc2.mm'],
             '.c': ['c1.c', 'c2.c'],
         }
         for suffix, files in expected.items():
             sources = suffix_map[suffix]
             self.assertEqual(sources.files, files)
             self.assertFalse(sources.have_unified_mapping)
 
+    def test_dist_files(self):
+        """Test that DIST_FILES works properly."""
+        reader = self.reader('dist-files')
+        objs = self.read_topsrcdir(reader)
+
+        self.assertEqual(len(objs), 1)
+        self.assertIsInstance(objs[0], DistFiles)
+
+        self.assertEqual(len(objs[0].files), 2)
+
+        expected = {'install.rdf', 'main.js'}
+        for f in objs[0].files:
+            self.assertTrue(f in expected)
+
+    def test_missing_dist_files(self):
+        """Test that DIST_FILES with missing files throws errors."""
+        with self.assertRaisesRegexp(SandboxValidationError, 'File listed in '
+            'DIST_FILES does not exist'):
+            reader = self.reader('dist-files-missing')
+            self.read_topsrcdir(reader)
+
 if __name__ == '__main__':
     main()
--- a/testing/mochitest/Makefile.in
+++ b/testing/mochitest/Makefile.in
@@ -1,16 +1,15 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 NO_JS_MANIFEST = 1
 MOZ_CHROME_FILE_FORMAT = jar
-DIST_FILES = install.rdf
 
 # Used in install.rdf
 USE_EXTENSION_MANIFEST = 1
 
 _DEST_DIR = $(DEPTH)/_tests/$(relativesrcdir)
 
 include $(topsrcdir)/config/rules.mk
 # We're installing to _tests/testing/mochitest, so this is the depth
--- a/testing/mochitest/moz.build
+++ b/testing/mochitest/moz.build
@@ -13,16 +13,18 @@ DIRS += [
 
 if CONFIG['MOZ_BUILD_APP'] == 'mobile/android':
     DIRS += ['roboextender']
 
 XPI_NAME = 'mochijar'
 
 JAR_MANIFESTS += ['jar.mn']
 
+DIST_FILES += ['install.rdf']
+
 MOCHITEST_MANIFESTS += [
     'dynamic/mochitest.ini',
     'MochiKit/mochitest.ini',
     'static/mochitest.ini',
     'tests/MochiKit-1.4.2/MochiKit/mochitest.ini',
     'tests/MochiKit-1.4.2/tests/mochitest.ini',
 ]
 MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
--- a/testing/mozbase/mozprocess/mozprocess/processhandler.py
+++ b/testing/mozbase/mozprocess/mozprocess/processhandler.py
@@ -60,16 +60,17 @@ class ProcessHandlerMixin(object):
     class Process(subprocess.Popen):
         """
         Represents our view of a subprocess.
         It adds a kill() method which allows it to be stopped explicitly.
         """
 
         MAX_IOCOMPLETION_PORT_NOTIFICATION_DELAY = 180
         MAX_PROCESS_KILL_DELAY = 30
+        TIMEOUT_BEFORE_SIGKILL = 1.0
 
         def __init__(self,
                      args,
                      bufsize=0,
                      executable=None,
                      stdin=None,
                      stdout=None,
                      stderr=None,
@@ -139,26 +140,42 @@ class ProcessHandlerMixin(object):
                         winprocess.TerminateProcess(self._handle, winprocess.ERROR_CONTROL_C_EXIT)
                     except:
                         err = "Could not terminate process"
                     winprocess.GetExitCodeProcess(self._handle)
                     self._cleanup()
                     if err is not None:
                         raise OSError(err)
             else:
-                sig = sig or signal.SIGKILL
-                if not self._ignore_children:
-                    try:
-                        os.killpg(self.pid, sig)
-                    except BaseException, e:
-                        if getattr(e, "errno", None) != 3:
-                            # Error 3 is "no such process", which is ok
-                            print >> sys.stdout, "Could not kill process, could not find pid: %s, assuming it's already dead" % self.pid
+                def send_sig(sig):
+                    if not self._ignore_children:
+                        try:
+                            os.killpg(self.pid, sig)
+                        except BaseException, e:
+                            if getattr(e, "errno", None) != 3:
+                                # Error 3 is "no such process", which is ok
+                                print >> sys.stdout, "Could not kill process, could not find pid: %s, assuming it's already dead" % self.pid
+                    else:
+                        os.kill(self.pid, sig)
+
+                if sig is None and isPosix:
+                    # ask the process for termination and wait a bit
+                    send_sig(signal.SIGTERM)
+                    limit = time.time() + self.TIMEOUT_BEFORE_SIGKILL
+                    while time.time() <= limit:
+                        if self.poll() is not None:
+                            # process terminated nicely
+                            break
+                        time.sleep(0.02)
+                    else:
+                        # process did not terminate - send SIGKILL to force
+                        send_sig(signal.SIGKILL)
                 else:
-                    os.kill(self.pid, sig)
+                    # a signal was explicitly set or not posix
+                    send_sig(sig or signal.SIGKILL)
 
             self.returncode = self.wait()
             self._cleanup()
             return self.returncode
 
         def poll(self):
             """ Popen.poll
                 Check if child process has terminated. Set and return returncode attribute.
new file mode 100644
--- /dev/null
+++ b/testing/mozbase/mozprocess/tests/infinite_loop.py
@@ -0,0 +1,18 @@
+import threading
+import time
+import sys
+import signal
+
+if 'deadlock' in sys.argv:
+    lock = threading.Lock()
+
+    def trap(sig, frame):
+        lock.acquire()
+
+    # get the lock once
+    lock.acquire()
+    # and take it again on SIGTERM signal: deadlock.
+    signal.signal(signal.SIGTERM, trap)
+
+while 1:
+    time.sleep(1)
--- a/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py
+++ b/testing/mozbase/mozprocess/tests/test_mozprocess_kill.py
@@ -1,14 +1,15 @@
 #!/usr/bin/env python
 
 import os
 import time
 import unittest
 import proctest
+import signal
 from mozprocess import processhandler
 
 here = os.path.dirname(os.path.abspath(__file__))
 
 class ProcTestKill(proctest.ProcTest):
     """ Class to test various process tree killing scenatios """
 
     def test_kill_before_run(self):
@@ -75,10 +76,31 @@ class ProcTestKill(proctest.ProcTest):
 
         detected, output = proctest.check_for_process(self.proclaunch)
         self.determine_status(detected,
                               output,
                               p.proc.returncode,
                               p.didTimeout,
                               expectedfail=('returncode',))
 
+    @unittest.skipUnless(processhandler.isPosix, "posix only")
+    def test_process_kill_with_sigterm(self):
+        script = os.path.join(here, 'infinite_loop.py')
+        p = processhandler.ProcessHandler([self.python, script])
+
+        p.run()
+        p.kill()
+
+        self.assertEquals(p.proc.returncode, -signal.SIGTERM)
+
+    @unittest.skipUnless(processhandler.isPosix, "posix only")
+    def test_process_kill_with_sigint_if_needed(self):
+        script = os.path.join(here, 'infinite_loop.py')
+        p = processhandler.ProcessHandler([self.python, script, 'deadlock'])
+
+        p.run()
+        time.sleep(1)
+        p.kill()
+
+        self.assertEquals(p.proc.returncode, -signal.SIGKILL)
+
 if __name__ == '__main__':
     unittest.main()
--- a/testing/specialpowers/Makefile.in
+++ b/testing/specialpowers/Makefile.in
@@ -1,18 +1,15 @@
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 NO_JS_MANIFEST = 1
 MOZ_CHROME_FILE_FORMAT = flat
-DIST_FILES = \
-  install.rdf \
-  $(NULL)
 
 # Used in install.rdf
 USE_EXTENSION_MANIFEST=1
 
 TEST_EXTENSIONS_DIR = $(DEPTH)/testing/specialpowers
 
 include $(topsrcdir)/config/rules.mk
 
--- a/testing/specialpowers/moz.build
+++ b/testing/specialpowers/moz.build
@@ -5,9 +5,11 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_COMPONENTS += [
     'components/SpecialPowersObserver.js',
 ]
 
 XPI_NAME = 'specialpowers'
 
-JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
+JAR_MANIFESTS += ['jar.mn']
+
+DIST_FILES += ['install.rdf']
--- a/testing/web-platform/harness/requirements.txt
+++ b/testing/web-platform/harness/requirements.txt
@@ -1,5 +1,4 @@
 html5lib >= 0.99
 mozinfo >= 0.7
 mozlog >= 2.8
-# Unfortunately, just for gdb flags
-mozrunner >= 6.1
+mozdebug >= 0.1
--- a/testing/web-platform/harness/wptrunner/browsers/b2g.py
+++ b/testing/web-platform/harness/wptrunner/browsers/b2g.py
@@ -15,16 +15,17 @@ import mozrunner
 from marionette import expected
 from marionette.by import By
 from marionette.wait import Wait
 from mozprofile import FirefoxProfile, Preferences
 
 from .base import get_free_port, BrowserError, Browser, ExecutorBrowser
 from ..executors.executormarionette import MarionetteTestharnessExecutor
 from ..hosts import HostsFile, HostsLine
+from ..environment import hostnames
 
 here = os.path.split(__file__)[0]
 
 __wptrunner__ = {"product": "b2g",
                  "check_args": "check_args",
                  "browser": "B2GBrowser",
                  "executor": {"testharness": "B2GMarionetteTestharnessExecutor"},
                  "browser_kwargs": "browser_kwargs",
@@ -110,23 +111,16 @@ class B2GBrowser(Browser):
 
         self.logger.debug("Creating device runner")
         self.runner = mozrunner.B2GDeviceRunner(profile=profile)
         self.logger.debug("Starting device runner")
         self.runner.start()
         self.logger.debug("Device runner started")
 
     def setup_hosts(self):
-        hostnames = ["web-platform.test",
-                     "www.web-platform.test",
-                     "www1.web-platform.test",
-                     "www2.web-platform.test",
-                     "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
-                     "xn--lve-6lad.web-platform.test"]
-
         host_ip = moznetwork.get_ip()
 
         temp_dir = tempfile.mkdtemp()
         hosts_path = os.path.join(temp_dir, "hosts")
         remote_path = "/system/etc/hosts"
         try:
             self.device.getFile("/system/etc/hosts", hosts_path)
 
--- a/testing/web-platform/harness/wptrunner/browsers/base.py
+++ b/testing/web-platform/harness/wptrunner/browsers/base.py
@@ -36,16 +36,28 @@ def get_free_port(start_port, exclude=No
             s.bind(("127.0.0.1", port))
         except socket.error:
             port += 1
         else:
             return port
         finally:
             s.close()
 
+def browser_command(binary, args, debug_info):
+    if debug_info:
+        if debug_info.requiresEscapedArgs:
+            args = [item.replace("&", "\\&") for item in args]
+        debug_args = [debug_info.path] + debug_info.args
+    else:
+        debug_args = []
+
+    command = [binary] + args
+
+    return debug_args, command
+
 
 class BrowserError(Exception):
     pass
 
 
 class Browser(object):
     __metaclass__ = ABCMeta
 
--- a/testing/web-platform/harness/wptrunner/browsers/firefox.py
+++ b/testing/web-platform/harness/wptrunner/browsers/firefox.py
@@ -7,19 +7,20 @@ import subprocess
 
 import mozinfo
 from mozprocess import ProcessHandler
 from mozprofile import FirefoxProfile, Preferences
 from mozprofile.permissions import ServerLocations
 from mozrunner import FirefoxRunner
 from mozcrash import mozcrash
 
-from .base import get_free_port, Browser, ExecutorBrowser, require_arg, cmd_arg
+from .base import get_free_port, Browser, ExecutorBrowser, require_arg, cmd_arg, browser_command
 from ..executors import executor_kwargs as base_executor_kwargs
 from ..executors.executormarionette import MarionetteTestharnessExecutor, MarionetteRefTestExecutor
+from ..environment import hostnames
 
 here = os.path.join(os.path.split(__file__)[0])
 
 __wptrunner__ = {"product": "firefox",
                  "check_args": "check_args",
                  "browser": "FirefoxBrowser",
                  "executor": {"testharness": "MarionetteTestharnessExecutor",
                               "reftest": "MarionetteRefTestExecutor"},
@@ -32,18 +33,17 @@ def check_args(**kwargs):
     require_arg(kwargs, "binary")
     if kwargs["ssl_type"] != "none":
         require_arg(kwargs, "certutil_binary")
 
 
 def browser_kwargs(**kwargs):
     return {"binary": kwargs["binary"],
             "prefs_root": kwargs["prefs_root"],
-            "debug_args": kwargs["debug_args"],
-            "interactive": kwargs["interactive"],
+            "debug_info": kwargs["debug_info"],
             "symbols_path": kwargs["symbols_path"],
             "stackwalk_binary": kwargs["stackwalk_binary"],
             "certutil_binary": kwargs["certutil_binary"],
             "ca_certificate_path": kwargs["ssl_env"].ca_cert_path()}
 
 
 def executor_kwargs(test_type, server_config, cache_manager, **kwargs):
     executor_kwargs = base_executor_kwargs(test_type, server_config,
@@ -52,75 +52,71 @@ def executor_kwargs(test_type, server_co
     return executor_kwargs
 
 
 def env_options():
     return {"host": "127.0.0.1",
             "external_host": "web-platform.test",
             "bind_hostname": "false",
             "certificate_domain": "web-platform.test",
-            "encrypt_after_connect": True}
+            "supports_debugger": True}
 
 
 class FirefoxBrowser(Browser):
     used_ports = set()
 
-    def __init__(self, logger, binary, prefs_root, debug_args=None, interactive=None,
+    def __init__(self, logger, binary, prefs_root, debug_info=None,
                  symbols_path=None, stackwalk_binary=None, certutil_binary=None,
                  ca_certificate_path=None):
         Browser.__init__(self, logger)
         self.binary = binary
         self.prefs_root = prefs_root
         self.marionette_port = None
         self.used_ports.add(self.marionette_port)
         self.runner = None
-        self.debug_args = debug_args
-        self.interactive = interactive
+        self.debug_info = debug_info
         self.profile = None
         self.symbols_path = symbols_path
         self.stackwalk_binary = stackwalk_binary
         self.ca_certificate_path = ca_certificate_path
         self.certutil_binary = certutil_binary
 
     def start(self):
         self.marionette_port = get_free_port(2828, exclude=self.used_ports)
 
         env = os.environ.copy()
-        env["MOZ_CRASHREPORTER"] = "1"
-        env["MOZ_CRASHREPORTER_SHUTDOWN"] = "1"
-        env["MOZ_CRASHREPORTER_NO_REPORT"] = "1"
         env["MOZ_DISABLE_NONLOCAL_CONNECTIONS"] = "1"
 
         locations = ServerLocations(filename=os.path.join(here, "server-locations.txt"))
 
         preferences = self.load_prefs()
 
-        ports = {"http": "8000",
-                 "https": "8443",
-                 "ws": "8888"}
-
         self.profile = FirefoxProfile(locations=locations,
-                                      proxy=ports,
                                       preferences=preferences)
         self.profile.set_preferences({"marionette.defaultPrefs.enabled": True,
                                       "marionette.defaultPrefs.port": self.marionette_port,
-                                      "dom.disable_open_during_load": False})
+                                      "dom.disable_open_during_load": False,
+                                      "network.dns.localDomains": ",".join(hostnames)})
 
         if self.ca_certificate_path is not None:
             self.setup_ssl()
 
+        debug_args, cmd = browser_command(self.binary, [cmd_arg("marionette"), "about:blank"],
+                                          self.debug_info)
+
         self.runner = FirefoxRunner(profile=self.profile,
-                                    binary=self.binary,
-                                    cmdargs=[cmd_arg("marionette"), "about:blank"],
+                                    binary=cmd[0],
+                                    cmdargs=cmd[1:],
                                     env=env,
                                     process_class=ProcessHandler,
                                     process_args={"processOutputLine": [self.on_output]})
 
         self.logger.debug("Starting Firefox")
-        self.runner.start(debug_args=self.debug_args, interactive=self.interactive)
+
+        self.runner.start(debug_args=debug_args, interactive=self.debug_info and self.debug_info.interactive)
         self.logger.debug("Firefox Started")
 
     def load_prefs(self):
         prefs_path = os.path.join(self.prefs_root, "prefs_general.js")
         if os.path.exists(prefs_path):
             preferences = Preferences.read_prefs(prefs_path)
         else:
             self.logger.warning("Failed to find base prefs file in %s" % prefs_path)
--- a/testing/web-platform/harness/wptrunner/browsers/servo.py
+++ b/testing/web-platform/harness/wptrunner/browsers/servo.py
@@ -21,35 +21,36 @@ here = os.path.join(os.path.split(__file
 
 
 def check_args(**kwargs):
     require_arg(kwargs, "binary")
 
 
 def browser_kwargs(**kwargs):
     return {"binary": kwargs["binary"],
-            "debug_args": kwargs["debug_args"],
+            "debug_info": kwargs["debug_info"],
             "interactive": kwargs["interactive"]}
 
 
 def executor_kwargs(test_type, server_config, cache_manager, **kwargs):
     rv = base_executor_kwargs(test_type, server_config,
                               cache_manager, **kwargs)
     rv["pause_after_test"] = kwargs["pause_after_test"]
     return rv
 
 def env_options():
     return {"host": "localhost",
             "bind_hostname": "true",
-            "testharnessreport": "testharnessreport-servo.js"}
+            "testharnessreport": "testharnessreport-servo.js",
+            "supports_debugger": True}
 
 
 class ServoBrowser(NullBrowser):
-    def __init__(self, logger, binary, debug_args=None, interactive=False):
+    def __init__(self, logger, binary, debug_info=None, interactive=False):
         NullBrowser.__init__(self, logger)
         self.binary = binary
-        self.debug_args = debug_args
+        self.debug_info = debug_info
         self.interactive = interactive
 
     def executor_browser(self):
         return ExecutorBrowser, {"binary": self.binary,
-                                 "debug_args": self.debug_args,
+                                 "debug_info": self.debug_info,
                                  "interactive": self.interactive}
--- a/testing/web-platform/harness/wptrunner/environment.py
+++ b/testing/web-platform/harness/wptrunner/environment.py
@@ -1,28 +1,38 @@
 # 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 multiprocessing
+import signal
 import socket
 import sys
 import time
 
 from mozlog.structured import get_default_logger, handlers
 
 from wptlogging import LogLevelRewriter
 
 here = os.path.split(__file__)[0]
 
 serve = None
 sslutils = None
 
+
+hostnames = ["web-platform.test",
+             "www.web-platform.test",
+             "www1.web-platform.test",
+             "www2.web-platform.test",
+             "xn--n8j6ds53lwwkrqhv28a.web-platform.test",
+             "xn--lve-6lad.web-platform.test"]
+
+
 def do_delayed_imports(logger, test_paths):
     global serve, sslutils
 
     serve_root = serve_path(test_paths)
 
     sys.path.insert(0, serve_root)
 
     failed = []
@@ -85,49 +95,60 @@ class StaticHandler(object):
         return self.resp_headers, self.data
 
     def __call__(self, request, response):
         rv = self.handler(request, response)
         return rv
 
 
 class TestEnvironment(object):
-    def __init__(self, test_paths, ssl_env, pause_after_test, options):
+    def __init__(self, test_paths, ssl_env, pause_after_test, debug_info, options):
         """Context manager that owns the test environment i.e. the http and
         websockets servers"""
         self.test_paths = test_paths
         self.ssl_env = ssl_env
         self.server = None
         self.config = None
         self.external_config = None
         self.pause_after_test = pause_after_test
         self.test_server_port = options.pop("test_server_port", True)
+        self.debug_info = debug_info
         self.options = options if options is not None else {}
 
         self.cache_manager = multiprocessing.Manager()
         self.routes = self.get_routes()
 
+
     def __enter__(self):
         self.ssl_env.__enter__()
         self.cache_manager.__enter__()
         self.setup_server_logging()
         self.config = self.load_config()
         serve.set_computed_defaults(self.config)
         self.external_config, self.servers = serve.start(self.config, self.ssl_env,
                                                          self.routes)
+        if self.options.get("supports_debugger") and self.debug_info and self.debug_info.interactive:
+            self.ignore_interrupts()
         return self
 
     def __exit__(self, exc_type, exc_val, exc_tb):
+        self.process_interrupts()
         self.cache_manager.__exit__(exc_type, exc_val, exc_tb)
         self.ssl_env.__exit__(exc_type, exc_val, exc_tb)
 
         for scheme, servers in self.servers.iteritems():
             for port, server in servers:
                 server.kill()
 
+    def ignore_interrupts(self):
+        signal.signal(signal.SIGINT, signal.SIG_IGN)
+
+    def process_interrupts(self):
+        signal.signal(signal.SIGINT, signal.SIG_DFL)
+
     def load_config(self):
         default_config_path = os.path.join(serve_path(self.test_paths), "config.default.json")
         local_config_path = os.path.join(here, "config.json")
 
         with open(default_config_path) as f:
             default_config = json.load(f)
 
         with open(local_config_path) as f:
--- a/testing/web-platform/harness/wptrunner/executors/base.py
+++ b/testing/web-platform/harness/wptrunner/executors/base.py
@@ -16,17 +16,17 @@ here = os.path.split(__file__)[0]
 
 def executor_kwargs(test_type, server_config, cache_manager, **kwargs):
     timeout_multiplier = kwargs["timeout_multiplier"]
     if timeout_multiplier is None:
         timeout_multiplier = 1
 
     executor_kwargs = {"server_config": server_config,
                        "timeout_multiplier": timeout_multiplier,
-                       "debug_args": kwargs["debug_args"]}
+                       "debug_info": kwargs["debug_info"]}
 
     if test_type == "reftest":
         executor_kwargs["screenshot_cache"] = cache_manager.dict()
 
     return executor_kwargs
 
 
 def strip_server(url):
@@ -76,33 +76,33 @@ class ExecutorException(Exception):
 
 class TestExecutor(object):
     __metaclass__ = ABCMeta
 
     test_type = None
     convert_result = None
 
     def __init__(self, browser, server_config, timeout_multiplier=1,
-                 debug_args=None):
+                 debug_info=None):
         """Abstract Base class for object that actually executes the tests in a
         specific browser. Typically there will be a different TestExecutor
         subclass for each test type and method of executing tests.
 
         :param browser: ExecutorBrowser instance providing properties of the
                         browser that will be tested.
         :param server_config: Dictionary of wptserve server configuration of the
                               form stored in TestEnvironment.external_config
         :param timeout_multiplier: Multiplier relative to base timeout to use
                                    when setting test timeout.
         """
         self.runner = None
         self.browser = browser
         self.server_config = server_config
         self.timeout_multiplier = timeout_multiplier
-        self.debug_args = debug_args
+        self.debug_info = debug_info
         self.last_environment = {"protocol": "http",
                                  "prefs": []}
         self.protocol = None # This must be set in subclasses
 
     @property
     def logger(self):
         """StructuredLogger for this executor"""
         if self.runner is not None:
@@ -148,17 +148,17 @@ class TestExecutor(object):
                                self.server_config["host"],
                                self.server_config["ports"][protocol][0])
 
     def test_url(self, test):
         return urlparse.urljoin(self.server_url(test.environment["protocol"]), test.url)
 
     @abstractmethod
     def do_test(self, test):
-        """Test-type and protocol specific implmentation of running a
+        """Test-type and protocol specific implementation of running a
         specific test.
 
         :param test: The test to run."""
         pass
 
     def on_environment_change(self, new_environment):
         pass
 
@@ -177,20 +177,20 @@ class TestExecutor(object):
 class TestharnessExecutor(TestExecutor):
     convert_result = testharness_result_converter
 
 
 class RefTestExecutor(TestExecutor):
     convert_result = reftest_result_converter
 
     def __init__(self, browser, server_config, timeout_multiplier=1, screenshot_cache=None,
-                 debug_args=None):
+                 debug_info=None):
         TestExecutor.__init__(self, browser, server_config,
                               timeout_multiplier=timeout_multiplier,
-                              debug_args=debug_args)
+                              debug_info=debug_info)
 
         self.screenshot_cache = screenshot_cache
 
 class RefTestImplementation(object):
     def __init__(self, executor):
         self.timeout_multiplier = executor.timeout_multiplier
         self.executor = executor
         # Cache of url:(screenshot hash, screenshot). Typically the
--- a/testing/web-platform/harness/wptrunner/executors/executormarionette.py
+++ b/testing/web-platform/harness/wptrunner/executors/executormarionette.py
@@ -57,17 +57,17 @@ class MarionetteProtocol(Protocol):
         self.logger.debug("Connecting to marionette on port %i" % self.marionette_port)
         self.marionette = marionette.Marionette(host='localhost', port=self.marionette_port)
 
         # XXX Move this timeout somewhere
         self.logger.debug("Waiting for Marionette connection")
         while True:
             success = self.marionette.wait_for_port(60)
             #When running in a debugger wait indefinitely for firefox to start
-            if success or self.executor.debug_args is None:
+            if success or self.executor.debug_info is None:
                 break
 
         session_started = False
         if success:
             try:
                 self.logger.debug("Starting Marionette session")
                 self.marionette.start_session()
             except Exception as e:
@@ -266,21 +266,21 @@ class MarionetteRun(object):
             self.result = False, ("ERROR", e)
 
         finally:
             self.result_flag.set()
 
 
 class MarionetteTestharnessExecutor(TestharnessExecutor):
     def __init__(self, browser, server_config, timeout_multiplier=1, close_after_done=True,
-                 debug_args=None):
+                 debug_info=None):
         """Marionette-based executor for testharness.js tests"""
         TestharnessExecutor.__init__(self, browser, server_config,
                                      timeout_multiplier=timeout_multiplier,
-                                     debug_args=debug_args)
+                                     debug_info=debug_info)
 
         self.protocol = MarionetteProtocol(self, browser)
         self.script = open(os.path.join(here, "testharness_marionette.js")).read()
         self.close_after_done = close_after_done
         self.window_id = str(uuid.uuid4())
 
         self.original_pref_values = {}
 
@@ -292,17 +292,17 @@ class MarionetteTestharnessExecutor(Test
 
     def on_environment_change(self, new_environment):
         self.protocol.on_environment_change(self.last_environment, new_environment)
 
         if new_environment["protocol"] != self.last_environment["protocol"]:
             self.protocol.load_runner(new_environment["protocol"])
 
     def do_test(self, test):
-        timeout = (test.timeout * self.timeout_multiplier if self.debug_args is None
+        timeout = (test.timeout * self.timeout_multiplier if self.debug_info is None
                    else None)
 
         success, data = MarionetteRun(self.logger,
                                       self.do_testharness,
                                       self.protocol.marionette,
                                       self.test_url(test),
                                       timeout).run()
         if success:
@@ -326,24 +326,24 @@ class MarionetteTestharnessExecutor(Test
                                 "timeout": timeout_ms,
                                 "explicit_timeout": timeout is None}
 
         return marionette.execute_async_script(script, new_sandbox=False)
 
 
 class MarionetteRefTestExecutor(RefTestExecutor):
     def __init__(self, browser, server_config, timeout_multiplier=1,
-                 screenshot_cache=None, close_after_done=True, debug_args=None):
+                 screenshot_cache=None, close_after_done=True, debug_info=None):
         """Marionette-based executor for reftests"""
         RefTestExecutor.__init__(self,
                                  browser,
                                  server_config,
                                  screenshot_cache=screenshot_cache,
                                  timeout_multiplier=timeout_multiplier,
-                                 debug_args=debug_args)
+                                 debug_info=debug_info)
         self.protocol = MarionetteProtocol(self, browser)
         self.implementation = RefTestImplementation(self)
         self.close_after_done = close_after_done
         self.has_window = False
         self.original_pref_values = {}
 
         with open(os.path.join(here, "reftest.js")) as f:
             self.script = f.read()
@@ -368,17 +368,17 @@ class MarionetteRefTestExecutor(RefTestE
             self.protocol.marionette.switch_to_window(self.protocol.marionette.window_handles[-1])
             self.has_window = True
 
         result = self.implementation.run_test(test)
 
         return self.convert_result(test, result)
 
     def screenshot(self, test):
-        timeout = self.timeout_multiplier * test.timeout if self.debug_args is None else None
+        timeout =  self.timeout_multiplier * test.timeout if self.debug_info is None else None
 
         test_url = self.test_url(test)
 
         return MarionetteRun(self.logger,
                              self._screenshot,
                              self.protocol.marionette,
                              test_url,
                              timeout).run()
--- a/testing/web-platform/harness/wptrunner/executors/executorselenium.py
+++ b/testing/web-platform/harness/wptrunner/executors/executorselenium.py
@@ -160,21 +160,21 @@ class SeleniumRun(object):
             message += traceback.format_exc(e)
             self.result = False, ("ERROR", e)
         finally:
             self.result_flag.set()
 
 
 class SeleniumTestharnessExecutor(TestharnessExecutor):
     def __init__(self, browser, server_config, timeout_multiplier=1,
-                 close_after_done=True, capabilities=None, debug_args=None):
+                 close_after_done=True, capabilities=None, debug_info=None):
         """Selenium-based executor for testharness.js tests"""
         TestharnessExecutor.__init__(self, browser, server_config,
                                      timeout_multiplier=timeout_multiplier,
-                                     debug_args=debug_args)
+                                     debug_info=debug_info)
         self.protocol = SeleniumProtocol(self, browser, capabilities)
         with open(os.path.join(here, "testharness_webdriver.js")) as f:
             self.script = f.read()
         self.close_after_done = close_after_done
         self.window_id = str(uuid.uuid4())
 
     def is_alive(self):
         return self.protocol.is_alive()
@@ -201,24 +201,24 @@ class SeleniumTestharnessExecutor(Testha
                            "url": strip_server(url),
                            "window_id": self.window_id,
                            "timeout_multiplier": self.timeout_multiplier,
                            "timeout": timeout * 1000})
 
 class SeleniumRefTestExecutor(RefTestExecutor):
     def __init__(self, browser, server_config, timeout_multiplier=1,
                  screenshot_cache=None, close_after_done=True,
-                 debug_args=None, capabilities=None):
+                 debug_info=None, capabilities=None):
         """Selenium WebDriver-based executor for reftests"""
         RefTestExecutor.__init__(self,
                                  browser,
                                  server_config,
                                  screenshot_cache=screenshot_cache,
                                  timeout_multiplier=timeout_multiplier,
-                                 debug_args=debug_args)
+                                 debug_info=debug_info)
         self.protocol = SeleniumProtocol(self, browser,
                                          capabilities=capabilities)
         self.implementation = RefTestImplementation(self)
         self.close_after_done = close_after_done
         self.has_window = False
 
         with open(os.path.join(here, "reftest.js")) as f:
             self.script = f.read()
--- a/testing/web-platform/harness/wptrunner/executors/executorservo.py
+++ b/testing/web-platform/harness/wptrunner/executors/executorservo.py
@@ -16,16 +16,17 @@ from collections import defaultdict
 from mozprocess import ProcessHandler
 
 from .base import (ExecutorException,
                    Protocol,
                    RefTestImplementation,
                    testharness_result_converter,
                    reftest_result_converter)
 from .process import ProcessTestExecutor
+from ..executors.base import browser_command
 
 hosts_text = """127.0.0.1 web-platform.test
 127.0.0.1 www.web-platform.test
 127.0.0.1 www1.web-platform.test
 127.0.0.1 www2.web-platform.test
 127.0.0.1 xn--n8j6ds53lwwkrqhv28a.web-platform.test
 127.0.0.1 xn--lve-6lad.web-platform.test
 """
@@ -34,21 +35,21 @@ def make_hosts_file():
     hosts_fd, hosts_path = tempfile.mkstemp()
     with os.fdopen(hosts_fd, "w") as f:
         f.write(hosts_text)
     return hosts_path
 
 class ServoTestharnessExecutor(ProcessTestExecutor):
     convert_result = testharness_result_converter
 
-    def __init__(self, browser, server_config, timeout_multiplier=1, debug_args=None,
+    def __init__(self, browser, server_config, timeout_multiplier=1, debug_info=None,
                  pause_after_test=False):
         ProcessTestExecutor.__init__(self, browser, server_config,
                                      timeout_multiplier=timeout_multiplier,
-                                     debug_args=debug_args)
+                                     debug_info=debug_info)
         self.pause_after_test = pause_after_test
         self.result_data = None
         self.result_flag = None
         self.protocol = Protocol(self, browser)
         self.hosts_path = make_hosts_file()
 
     def teardown(self):
         try:
@@ -56,39 +57,41 @@ class ServoTestharnessExecutor(ProcessTe
         except OSError:
             pass
         ProcessTestExecutor.teardown(self)
 
     def do_test(self, test):
         self.result_data = None
         self.result_flag = threading.Event()
 
-        self.command = [self.binary, "--cpu", "--hard-fail", "-z", self.test_url(test)]
+        debug_args, command = browser_command(self.binary, ["--cpu", "--hard-fail", "-z", self.test_url(test)],
+                                              self.debug_info)
+
+        self.command = command
 
         if self.pause_after_test:
             self.command.remove("-z")
 
-        if self.debug_args:
-            self.command = list(self.debug_args) + self.command
+        self.command = debug_args + self.command
 
         env = os.environ.copy()
         env["HOST_FILE"] = self.hosts_path
 
         self.proc = ProcessHandler(self.command,
                                    processOutputLine=[self.on_output],
                                    onFinish=self.on_finish,
                                    env=env)
 
         try:
             self.proc.run()
 
             timeout = test.timeout * self.timeout_multiplier
 
             # Now wait to get the output we expect, or until we reach the timeout
-            if self.debug_args is None and not self.pause_after_test:
+            if self.debug_info is None and not self.pause_after_test:
                 wait_timeout = timeout + 5
             else:
                 wait_timeout = None
             self.result_flag.wait(wait_timeout)
 
             proc_is_running = True
             if self.result_flag.is_set() and self.result_data is not None:
                 self.result_data["test"] = test.url
@@ -145,23 +148,23 @@ class TempFilename(object):
         except OSError:
             pass
 
 
 class ServoRefTestExecutor(ProcessTestExecutor):
     convert_result = reftest_result_converter
 
     def __init__(self, browser, server_config, binary=None, timeout_multiplier=1,
-                 screenshot_cache=None, debug_args=None, pause_after_test=False):
+                 screenshot_cache=None, debug_info=None, pause_after_test=False):
 
         ProcessTestExecutor.__init__(self,
                                      browser,
                                      server_config,
                                      timeout_multiplier=timeout_multiplier,
-                                     debug_args=debug_args)
+                                     debug_info=debug_info)
 
         self.protocol = Protocol(self, browser)
         self.screenshot_cache = screenshot_cache
         self.implementation = RefTestImplementation(self)
         self.tempdir = tempfile.mkdtemp()
         self.hosts_path = make_hosts_file()
 
     def teardown(self):
--- a/testing/web-platform/harness/wptrunner/manifestinclude.py
+++ b/testing/web-platform/harness/wptrunner/manifestinclude.py
@@ -74,17 +74,17 @@ class IncludeManifest(ManifestItem):
         if url_parts.fragment:
             variant += "#" + url_parts.fragment
         if variant:
             rv.append(variant)
         rv.extend([item for item in reversed(url_parts.path.split("/")) if item])
         return rv
 
     def _add_rule(self, test_manifests, url, direction):
-        maybe_path = os.path.abspath(os.path.join(os.curdir, url)[1:])
+        maybe_path = os.path.join(os.path.abspath(os.curdir), url)
         rest, last = os.path.split(maybe_path)
         variant = ""
         if "#" in last:
             last, fragment = last.rsplit("#", 1)
             variant += "#" + fragment
         if "?" in last:
             last, query = last.rsplit("?", 1)
             variant += "?" + query
--- a/testing/web-platform/harness/wptrunner/testloader.py
+++ b/testing/web-platform/harness/wptrunner/testloader.py
@@ -185,18 +185,16 @@ class EqualTimeChunker(TestChunker):
         manifest = list(manifest_iter)
         tests = self._get_chunk(manifest)
         for item in tests:
             yield item
 
 
 class TestFilter(object):
     def __init__(self, test_manifests, include=None, exclude=None, manifest_path=None):
-        test_manifests = test_manifests
-
         if manifest_path is not None and include is None:
             self.manifest = manifestinclude.get_manifest(manifest_path)
         else:
             self.manifest = manifestinclude.IncludeManifest.create()
 
         if include:
             self.manifest.set("skip", "true")
             for item in include:
--- a/testing/web-platform/harness/wptrunner/testrunner.py
+++ b/testing/web-platform/harness/wptrunner/testrunner.py
@@ -163,17 +163,17 @@ def next_manager_number():
     return local
 
 
 class TestRunnerManager(threading.Thread):
     init_lock = threading.Lock()
 
     def __init__(self, suite_name, test_queue, test_source_cls, browser_cls, browser_kwargs,
                  executor_cls, executor_kwargs, stop_flag, pause_after_test=False,
-                 pause_on_unexpected=False, debug_args=None):
+                 pause_on_unexpected=False, debug_info=None):
         """Thread that owns a single TestRunner process and any processes required
         by the TestRunner (e.g. the Firefox binary).
 
         TestRunnerManagers are responsible for launching the browser process and the
         runner process, and for logging the test progress. The actual test running
         is done by the TestRunner. In particular they:
 
         * Start the binary of the program under test
@@ -201,17 +201,17 @@ class TestRunnerManager(threading.Thread
         self.browser_pid = None
 
         # Flags used to shut down this thread if we get a sigint
         self.parent_stop_flag = stop_flag
         self.child_stop_flag = multiprocessing.Event()
 
         self.pause_after_test = pause_after_test
         self.pause_on_unexpected = pause_on_unexpected
-        self.debug_args = debug_args
+        self.debug_info = debug_info
 
         self.manager_number = next_manager_number()
 
         self.command_queue = Queue()
         self.remote_queue = Queue()
 
         self.test_runner_proc = None
 
@@ -328,17 +328,17 @@ class TestRunnerManager(threading.Thread
                 self.command_queue.put(("init_failed", ()))
             else:
                 self.logger.debug("Setting child stop flag in init_failed")
                 self.child_stop_flag.set()
 
         with self.init_lock:
             # Guard against problems initialising the browser or the browser
             # remote control method
-            if self.debug_args is None:
+            if self.debug_info is None:
                 self.init_timer = threading.Timer(self.browser.init_timeout, init_failed)
 
             test_queue = self.test_source.get_queue()
             if test_queue is None:
                 self.logger.info("No more tests")
                 return Stop
 
             try:
@@ -555,17 +555,16 @@ class TestRunnerManager(threading.Thread
 
 class TestQueue(object):
     def __init__(self, test_source_cls, test_type, tests, **kwargs):
         self.queue = None
         self.test_source_cls = test_source_cls
         self.test_type = test_type
         self.tests = tests
         self.kwargs = kwargs
-        self.queue = None
 
     def __enter__(self):
         if not self.tests[self.test_type]:
             return None
 
         self.queue = Queue()
         has_tests = self.test_source_cls.queue_tests(self.queue,
                                                      self.test_type,
@@ -585,29 +584,29 @@ class TestQueue(object):
 
 
 class ManagerGroup(object):
     def __init__(self, suite_name, size, test_source_cls, test_source_kwargs,
                  browser_cls, browser_kwargs,
                  executor_cls, executor_kwargs,
                  pause_after_test=False,
                  pause_on_unexpected=False,
-                 debug_args=None):
+                 debug_info=None):
         """Main thread object that owns all the TestManager threads."""
         self.suite_name = suite_name
         self.size = size
         self.test_source_cls = test_source_cls
         self.test_source_kwargs = test_source_kwargs
         self.browser_cls = browser_cls
         self.browser_kwargs = browser_kwargs
         self.executor_cls = executor_cls
         self.executor_kwargs = executor_kwargs
         self.pause_after_test = pause_after_test
         self.pause_on_unexpected = pause_on_unexpected
-        self.debug_args = debug_args
+        self.debug_info = debug_info
 
         self.pool = set()
         # Event that is polled by threads so that they can gracefully exit in the face
         # of sigint
         self.stop_flag = threading.Event()
         self.logger = structuredlog.StructuredLogger(suite_name)
         self.test_queue = None
 
@@ -635,17 +634,17 @@ class ManagerGroup(object):
                                             self.test_source_cls,
                                             self.browser_cls,
                                             self.browser_kwargs,
                                             self.executor_cls,
                                             self.executor_kwargs,
                                             self.stop_flag,
                                             self.pause_after_test,
                                             self.pause_on_unexpected,
-                                            self.debug_args)
+                                            self.debug_info)
                 manager.start()
                 self.pool.add(manager)
             self.wait()
 
     def is_alive(self):
         """Boolean indicating whether any manager in the group is still alive"""
         return any(manager.is_alive() for manager in self.pool)
 
--- a/testing/web-platform/harness/wptrunner/wptcommandline.py
+++ b/testing/web-platform/harness/wptrunner/wptcommandline.py
@@ -20,22 +20,16 @@ def url_or_path(path):
     import urlparse
 
     parsed = urlparse.urlparse(path)
     if len(parsed.scheme) > 2:
         return path
     else:
         return abs_path(path)
 
-def slash_prefixed(url):
-    if not url.startswith("/"):
-        url = "/" + url
-    return url
-
-
 def require_arg(kwargs, name, value_func=None):
     if value_func is None:
         value_func = lambda x: x is not None
 
     if not name in kwargs or not value_func(kwargs[name]):
         print >> sys.stderr, "Missing required argument %s" % name
         sys.exit(1)
 
@@ -92,25 +86,25 @@ def create_parser(product_choices=None):
                         default=False,
                         help="List the tests that are disabled on the current platform")
 
     test_selection_group = parser.add_argument_group("Test Selection")
     test_selection_group.add_argument("--test-types", action="store",
                                       nargs="*", default=["testharness", "reftest"],
                                       choices=["testharness", "reftest"],
                                       help="Test types to run")
-    test_selection_group.add_argument("--include", action="append", type=slash_prefixed,
+    test_selection_group.add_argument("--include", action="append",
                                       help="URL prefix to include")
-    test_selection_group.add_argument("--exclude", action="append", type=slash_prefixed,
+    test_selection_group.add_argument("--exclude", action="append",
                                       help="URL prefix to exclude")
     test_selection_group.add_argument("--include-manifest", type=abs_path,
                                       help="Path to manifest listing tests to include")
 
     debugging_group = parser.add_argument_group("Debugging")
-    debugging_group.add_argument('--debugger',
+    debugging_group.add_argument('--debugger', const="__default__", nargs="?",
                                  help="run under a debugger, e.g. gdb or valgrind")
     debugging_group.add_argument('--debugger-args', help="arguments to the debugger")
 
     debugging_group.add_argument('--pause-after-test', action="store_true", default=None,
                                  help="Halt the test runner after each test (this happens by default if only a single test is run)")
     debugging_group.add_argument('--no-pause-after-test', dest="pause_after_test", action="store_false",
                                  help="Don't halt the test runner irrespective of the number of tests run")
 
@@ -228,18 +222,16 @@ def exe_path(name):
         return
 
     path = find_executable(name)
     if os.access(path, os.X_OK):
         return path
 
 
 def check_args(kwargs):
-    from mozrunner import debugger_arguments
-
     set_from_config(kwargs)
 
     for test_paths in kwargs["test_paths"].itervalues():
         if not ("tests_path" in test_paths and
                 "metadata_path" in test_paths):
             print "Fatal: must specify both a test path and metadata path"
             sys.exit(1)
         for key, path in test_paths.iteritems():
@@ -273,26 +265,27 @@ def check_args(kwargs):
             kwargs["chunk_type"] = "equal_time"
         else:
             kwargs["chunk_type"] = "none"
 
     if kwargs["processes"] is None:
         kwargs["processes"] = 1
 
     if kwargs["debugger"] is not None:
-        debug_args, interactive = debugger_arguments(kwargs["debugger"],
-                                                     kwargs["debugger_args"])
-        if interactive:
+        import mozdebug
+        if kwargs["debugger"] == "__default__":
+            kwargs["debugger"] = mozdebug.get_default_debugger_name()
+        debug_info = mozdebug.get_debugger_info(kwargs["debugger"],
+                                                kwargs["debugger_args"])
+        if debug_info.interactive:
             require_arg(kwargs, "processes", lambda x: x == 1)
             kwargs["no_capture_stdio"] = True
-        kwargs["interactive"] = interactive
-        kwargs["debug_args"] = debug_args
+        kwargs["debug_info"] = debug_info
     else:
-        kwargs["interactive"] = False
-        kwargs["debug_args"] = None
+        kwargs["debug_info"] = None
 
     if kwargs["binary"] is not None:
         if not os.path.exists(kwargs["binary"]):
             print >> sys.stderr, "Binary path %s does not exist" % kwargs["binary"]
             sys.exit(1)
 
     if kwargs["ssl_type"] is None:
         if None not in (kwargs["ca_cert_path"], kwargs["host_cert_path"], kwargs["host_key_path"]):
--- a/testing/web-platform/harness/wptrunner/wptrunner.py
+++ b/testing/web-platform/harness/wptrunner/wptrunner.py
@@ -129,16 +129,17 @@ def run_tests(config, test_paths, produc
 
         unexpected_total = 0
 
         kwargs["pause_after_test"] = get_pause_after_test(test_loader, **kwargs)
 
         with env.TestEnvironment(test_paths,
                                  ssl_env,
                                  kwargs["pause_after_test"],
+                                 kwargs["debug_info"],
                                  env_options) as test_environment:
             try:
                 test_environment.ensure_started()
             except env.TestEnvironmentError as e:
                 logger.critical("Error starting test environment: %s" % e.message)
                 raise
 
             browser_kwargs = get_browser_kwargs(ssl_env=ssl_env, **kwargs)
@@ -175,17 +176,17 @@ def run_tests(config, test_paths, produc
                                       test_source_cls,
                                       test_source_kwargs,
                                       browser_cls,
                                       browser_kwargs,
                                       executor_cls,
                                       executor_kwargs,
                                       kwargs["pause_after_test"],
                                       kwargs["pause_on_unexpected"],
-                                      kwargs["debug_args"]) as manager_group:
+                                      kwargs["debug_info"]) as manager_group:
                         try:
                             manager_group.run(test_type, test_loader.tests)
                         except KeyboardInterrupt:
                             logger.critical("Main thread got signal")
                             manager_group.stop()
                             raise
                     unexpected_count += manager_group.unexpected_count()
 
--- a/testing/web-platform/meta/MANIFEST.json
+++ b/testing/web-platform/meta/MANIFEST.json
@@ -9623,16 +9623,24 @@
         "path": "XMLHttpRequest/send-sync-timeout.htm",
         "url": "/XMLHttpRequest/send-sync-timeout.htm"
       },
       {
         "path": "XMLHttpRequest/send-timeout-events.htm",
         "url": "/XMLHttpRequest/send-timeout-events.htm"
       },
       {
+        "path": "XMLHttpRequest/send-usp.html",
+        "url": "/XMLHttpRequest/send-usp.html"
+      },
+      {
+        "path": "XMLHttpRequest/send-usp.worker.js",
+        "url": "/XMLHttpRequest/send-usp.worker"
+      },
+      {
         "path": "XMLHttpRequest/setrequestheader-after-send.htm",
         "url": "/XMLHttpRequest/setrequestheader-after-send.htm"
       },
       {
         "path": "XMLHttpRequest/setrequestheader-allow-empty-value.htm",
         "url": "/XMLHttpRequest/setrequestheader-allow-empty-value.htm"
       },
       {
@@ -16095,24 +16103,16 @@
         "path": "service-workers/cache-storage/serviceworker/cache-add.https.html",
         "url": "/service-workers/cache-storage/serviceworker/cache-add.https.html"
       },
       {
         "path": "service-workers/cache-storage/serviceworker/cache-delete.https.html",
         "url": "/service-workers/cache-storage/serviceworker/cache-delete.https.html"
       },
       {
-        "path": "service-workers/cache-storage/serviceworker/cache-match.https.html",
-        "url": "/service-workers/cache-storage/serviceworker/cache-match.https.html"
-      },
-      {
-        "path": "service-workers/cache-storage/serviceworker/cache-put.https.html",
-        "url": "/service-workers/cache-storage/serviceworker/cache-put.https.html"
-      },
-      {
         "path": "service-workers/cache-storage/serviceworker/cache-storage-keys.https.html",
         "url": "/service-workers/cache-storage/serviceworker/cache-storage-keys.https.html"
       },
       {
         "path": "service-workers/cache-storage/serviceworker/cache-storage-match.https.html",
         "url": "/service-workers/cache-storage/serviceworker/cache-storage-match.https.html"
       },
       {
@@ -16123,24 +16123,16 @@
         "path": "service-workers/cache-storage/window/cache-add.https.html",
         "url": "/service-workers/cache-storage/window/cache-add.https.html"
       },
       {
         "path": "service-workers/cache-storage/window/cache-delete.https.html",
         "url": "/service-workers/cache-storage/window/cache-delete.https.html"
       },
       {
-        "path": "service-workers/cache-storage/window/cache-match.https.html",
-        "url": "/service-workers/cache-storage/window/cache-match.https.html"
-      },
-      {
-        "path": "service-workers/cache-storage/window/cache-put.https.html",
-        "url": "/service-workers/cache-storage/window/cache-put.https.html"
-      },
-      {
         "path": "service-workers/cache-storage/window/cache-storage-keys.https.html",
         "url": "/service-workers/cache-storage/window/cache-storage-keys.https.html"
       },
       {
         "path": "service-workers/cache-storage/window/cache-storage-match.https.html",
         "url": "/service-workers/cache-storage/window/cache-storage-match.https.html"
       },
       {
@@ -16155,24 +16147,16 @@
         "path": "service-workers/cache-storage/worker/cache-add.https.html",
         "url": "/service-workers/cache-storage/worker/cache-add.https.html"
       },
       {
         "path": "service-workers/cache-storage/worker/cache-delete.https.html",
         "url": "/service-workers/cache-storage/worker/cache-delete.https.html"
       },
       {
-        "path": "service-workers/cache-storage/worker/cache-match.https.html",
-        "url": "/service-workers/cache-storage/worker/cache-match.https.html"
-      },
-      {
-        "path": "service-workers/cache-storage/worker/cache-put.https.html",
-        "url": "/service-workers/cache-storage/worker/cache-put.https.html"
-      },
-      {
         "path": "service-workers/cache-storage/worker/cache-storage-keys.https.html",
         "url": "/service-workers/cache-storage/worker/cache-storage-keys.https.html"
       },
       {
         "path": "service-workers/cache-storage/worker/cache-storage-match.https.html",
         "url": "/service-workers/cache-storage/worker/cache-storage-match.https.html"
       },
       {
@@ -19510,16 +19494,46 @@
         "url": "/html/syntax/parsing/html5lib_webkit02.html"
       },
       {
         "path": "media-source/mediasource-redundant-seek.html",
         "timeout": "long",
         "url": "/media-source/mediasource-redundant-seek.html"
       },
       {
+        "path": "service-workers/cache-storage/serviceworker/cache-match.https.html",
+        "timeout": "long",
+        "url": "/service-workers/cache-storage/serviceworker/cache-match.https.html"
+      },
+      {
+        "path": "service-workers/cache-storage/serviceworker/cache-put.https.html",
+        "timeout": "long",
+        "url": "/service-workers/cache-storage/serviceworker/cache-put.https.html"
+      },
+      {
+        "path": "service-workers/cache-storage/window/cache-match.https.html",
+        "timeout": "long",
+        "url": "/service-workers/cache-storage/window/cache-match.https.html"
+      },
+      {
+        "path": "service-workers/cache-storage/window/cache-put.https.html",
+        "timeout": "long",
+        "url": "/service-workers/cache-storage/window/cache-put.https.html"
+      },
+      {
+        "path": "service-workers/cache-storage/worker/cache-match.https.html",
+        "timeout": "long",
+        "url": "/service-workers/cache-storage/worker/cache-match.https.html"
+      },
+      {
+        "path": "service-workers/cache-storage/worker/cache-put.https.html",
+        "timeout": "long",
+        "url": "/service-workers/cache-storage/worker/cache-put.https.html"
+      },
+      {
         "path": "websockets/binary/002.html",
         "timeout": "long",
         "url": "/websockets/binary/002.html"
       },
       {
         "path": "websockets/binary/004.html",
         "timeout": "long",
         "url": "/websockets/binary/004.html"
@@ -19641,62 +19655,17 @@
       },
       {
         "path": "webdriver/user_input/clear_test.py"
       }
     ]
   },
   "local_changes": {
     "deleted": [],
-    "items": {
-      "testharness": {
-        "service-workers/cache-storage/serviceworker/cache-match.https.html": [
-          {
-            "path": "service-workers/cache-storage/serviceworker/cache-match.https.html",
-            "timeout": "long",
-            "url": "/service-workers/cache-storage/serviceworker/cache-match.https.html"
-          }
-        ],
-        "service-workers/cache-storage/serviceworker/cache-put.https.html": [
-          {
-            "path": "service-workers/cache-storage/serviceworker/cache-put.https.html",
-            "timeout": "long",
-            "url": "/service-workers/cache-storage/serviceworker/cache-put.https.html"
-          }
-        ],
-        "service-workers/cache-storage/window/cache-match.https.html": [
-          {
-            "path": "service-workers/cache-storage/window/cache-match.https.html",
-            "timeout": "long",
-            "url": "/service-workers/cache-storage/window/cache-match.https.html"
-          }
-        ],
-        "service-workers/cache-storage/window/cache-put.https.html": [
-          {
-            "path": "service-workers/cache-storage/window/cache-put.https.html",
-            "timeout": "long",
-            "url": "/service-workers/cache-storage/window/cache-put.https.html"
-          }
-        ],
-        "service-workers/cache-storage/worker/cache-match.https.html": [
-          {
-            "path": "service-workers/cache-storage/worker/cache-match.https.html",
-            "timeout": "long",
-            "url": "/service-workers/cache-storage/worker/cache-match.https.html"
-          }
-        ],
-        "service-workers/cache-storage/worker/cache-put.https.html": [
-          {
-            "path": "service-workers/cache-storage/worker/cache-put.https.html",
-            "timeout": "long",
-            "url": "/service-workers/cache-storage/worker/cache-put.https.html"
-          }
-        ]
-      }
-    },
+    "items": {},
     "reftest_nodes": {}
   },
   "reftest_nodes": {
     "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm": [
       {
         "path": "2dcontext/building-paths/canvas_complexshapes_arcto_001.htm",
         "references": [
           [
@@ -24755,12 +24724,12 @@
             "/webvtt/rendering/cues-with-video/processing-model/too_many_cues_wrapped-ref.html",
             "=="
           ]
         ],
         "url": "/webvtt/rendering/cues-with-video/processing-model/too_many_cues_wrapped.html"
       }
     ]
   },
-  "rev": "7311aa630534282885b9add15b1c30b2b59316dd",
+  "rev": "89b6e2bc460316c7f273712d22f0b2d3a3d0c5be",
   "url_base": "/",
   "version": 2
-}
+}
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/XMLHttpRequest/send-usp.html.ini
@@ -0,0 +1,5 @@
+[send-usp.html]
+  type: testharness
+  [XMLHttpRequest.send(URLSearchParams) (0)]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/XMLHttpRequest/send-usp.worker.js.ini
@@ -0,0 +1,3 @@
+[send-usp.worker]
+  type: testharness
+  expected: ERROR
--- a/testing/web-platform/meta/html/dom/reflection-misc.html.ini
+++ b/testing/web-platform/meta/html/dom/reflection-misc.html.ini
@@ -661,8 +661,122 @@
     expected: FAIL
 
   [undefinedelement.tabIndex: setAttribute() to object "3" followed by IDL get]
     expected: FAIL
 
   [undefinedelement.tabIndex: IDL set to -2147483648 followed by IDL get]
     expected: FAIL
 
+  [dialog.tabIndex: setAttribute() to -2147483648 followed by IDL get]
+    expected: FAIL
+
+  [dialog.tabIndex: IDL set to -2147483648 followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: typeof IDL attribute]
+    expected: FAIL
+
+  [dialog.open: IDL get with DOM attribute unset]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to "" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to " foo " followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to undefined followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to null followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to 7 followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to 1.5 followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to true followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to false followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to object "[object Object\]" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to NaN followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to Infinity followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to -Infinity followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to "\\0" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to object "test-toString" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to object "test-valueOf" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: setAttribute() to "open" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to "" followed by hasAttribute()]
+    expected: FAIL
+
+  [dialog.open: IDL set to "" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to " foo " followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to undefined followed by hasAttribute()]
+    expected: FAIL
+
+  [dialog.open: IDL set to undefined followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to null followed by hasAttribute()]
+    expected: FAIL
+
+  [dialog.open: IDL set to null followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to 7 followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to 1.5 followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to false followed by hasAttribute()]
+    expected: FAIL
+
+  [dialog.open: IDL set to object "[object Object\]" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to NaN followed by hasAttribute()]
+    expected: FAIL
+
+  [dialog.open: IDL set to NaN followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to Infinity followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to -Infinity followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to "\\0" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to object "test-toString" followed by IDL get]
+    expected: FAIL
+
+  [dialog.open: IDL set to object "test-valueOf" followed by IDL get]
+    expected: FAIL
+
--- a/testing/web-platform/meta/mozilla-sync
+++ b/testing/web-platform/meta/mozilla-sync
@@ -1,1 +1,1 @@
-cb3abe0063c59cf9273978c8db7700838bf835ad
\ No newline at end of file
+ec9e346b36e59c9a4fc6fc1cc5a3687417769191
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/navigation-timing/test_navigation_type_reload.html.ini
@@ -0,0 +1,4 @@
+[test_navigation_type_reload.html]
+  type: testharness
+  expected:
+    if not debug and (os == "mac") and (version == "OS X 10.8") and (processor == "x86_64") and (bits == 64): TIMEOUT
--- a/testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini
+++ b/testing/web-platform/meta/web-animations/animation-timeline/idlharness.html.ini
@@ -1,7 +1,8 @@
 [idlharness.html]
   type: testharness
   [AnimationTimeline must be primary interface of document.timeline]
     expected: FAIL
 
   [Stringification of document.timeline]
     expected: FAIL
+
--- a/testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/EventTarget.worker.js.ini
+++ b/testing/web-platform/meta/workers/interfaces/DedicatedWorkerGlobalScope/EventTarget.worker.js.ini
@@ -1,5 +1,6 @@
 [EventTarget.worker]
   type: testharness
+  expected: ERROR
   [removeEventListener]
     expected: FAIL
 
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/send-usp.html
@@ -0,0 +1,10 @@
+<!DOCTYPE html>
+<meta charset=utf-8>
+<title>XMLHttpRequest.send(URLSearchParams)</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="send-usp.js"></script>
+<div id="log"></div>
+<script>
+run_test();
+</script>
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/send-usp.js
@@ -0,0 +1,39 @@
+function encode(n) {
+  if (n === 0x20) {
+    return "\x2B";
+  }
+
+  if (n === 0x2A || n === 0x2D || n === 0x2E ||
+      (0x30 <= n && n <= 0x39) || (0x41 <= n && n <= 0x5A) ||
+      n === 0x5F || (0x61 <= n && n <= 0x7A)) {
+    return String.fromCharCode(n);
+  }
+
+  var s = n.toString(16).toUpperCase();
+  return "%" + (s.length === 2 ? s : '0' + s);
+}
+
+function do_test(n) {
+  async_test(function() {
+    var x = new XMLHttpRequest();
+    x.onload = this.step_func_done(function(e) {
+      assert_equals(x.response, "a=" + encode(n))
+    });
+    x.onerror = this.unreached_func();
+    x.open("POST", "resources/content.py");
+    var usp = new URLSearchParams();
+    usp.append("a", String.fromCharCode(n));
+    x.send(usp)
+  }, "XMLHttpRequest.send(URLSearchParams) (" + n + ")");
+}
+
+function run_test() {
+  var i = 0;
+  add_result_callback(function() {
+    if (++i === 128) {
+      return;
+    }
+    do_test(i);
+  });
+  do_test(i);
+}
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/tests/XMLHttpRequest/send-usp.worker.js
@@ -0,0 +1,4 @@
+importScripts("/resources/testharness.js");
+importScripts("/resources/testharnessreport.js");
+importScripts("send-usp.js");
+run_test();
--- a/testing/web-platform/tests/html/browsers/the-window-object/window-properties.html
+++ b/testing/web-platform/tests/html/browsers/the-window-object/window-properties.html
@@ -62,16 +62,17 @@ var replaceableAttributes = [
   "pageXOffset",
   "pageYOffset",
   "innerWidth",
   "innerHeight",
   "screenX",
   "screenY",
   "outerWidth",
   "outerHeight",
+  "devicePixelRatio",
 ];
 
 var methods = [
   "close",
   "stop",
   "focus",
   "blur",
   "open",
--- a/testing/web-platform/tests/html/dom/elements-misc.js
+++ b/testing/web-platform/tests/html/dom/elements-misc.js
@@ -45,14 +45,17 @@ var miscElements = {
     type: {type: "enum", keywords: ["command", "checkbox", "radio"], defaultVal: "command"},
     label: "string",
     icon: "url",
     disabled: "boolean",
     checked: "boolean",
     radiogroup: "string",
     "default": "boolean",
   },
+  dialog: {
+    open: "boolean",
+  },
 
   // Global attributes should exist even on unknown elements
   undefinedelement: {},
 };
 
 mergeElements(miscElements);
--- a/testing/web-platform/tests/tools/manifest/sourcefile.py
+++ b/testing/web-platform/tests/tools/manifest/sourcefile.py
@@ -44,21 +44,22 @@ class SourceFile(object):
         if "-" in self.name:
             self.type_flag = self.name.rsplit("-", 1)[1]
 
         self.meta_flags = self.name.split(".")[1:]
 
     def __getstate__(self):
         # Remove computed properties if we pickle this class
         rv = self.__dict__.copy()
-        cached_properties = rv.get("__cached_properties__", set())
-        for key in rv.keys():
-            if key in cached_properties:
-                del rv[key]
+
         if "__cached_properties__" in rv:
+            cached_properties = rv["__cached_properties__"]
+            for key in rv.keys():
+                if key in cached_properties:
+                    del rv[key]
             del rv["__cached_properties__"]
         return rv
 
     def name_prefix(self, prefix):
         """Check if the filename starts with a given prefix
 
         :param prefix: The prefix to check"""
         return self.name.startswith(prefix)
--- a/testing/web-platform/tests/tools/wptserve/wptserve/server.py
+++ b/testing/web-platform/tests/tools/wptserve/wptserve/server.py
@@ -280,17 +280,17 @@ class WebTestRequestHandler(BaseHTTPServ
             self.close_connection = True
             return
 
         except Exception as e:
             err = traceback.format_exc()
             if response:
                 response.set_error(500, err)
                 response.write()
-            logger.error(err)
+            self.logger.error(err)
 
     def get_request_line(self):
         try:
             self.raw_requestline = self.rfile.readline(65537)
         except socket.error:
             self.close_connection = True
             return False
         if len(self.raw_requestline) > 65536:
--- a/testing/web-platform/tests/workers/interfaces/DedicatedWorkerGlobalScope/EventTarget.worker.js
+++ b/testing/web-platform/tests/workers/interfaces/DedicatedWorkerGlobalScope/EventTarget.worker.js
@@ -1,16 +1,18 @@
 importScripts("/resources/testharness.js");
 
 test(function() {
     var i = 0;
-    addEventListener("message", this.step_func(function listener(evt) {
-        ++i;
-        removeEventListener("message", listener, true);
-    }), true);
+    addEventListener("message", function listener(evt) {
+        this.step(function() {
+            ++i;
+            removeEventListener("message", listener, true);
+        });
+    }, true);
     self.dispatchEvent(new Event("message"));
     self.dispatchEvent(new Event("message"));
     assert_equals(i, 1);
 }, "removeEventListener");
 
 test(function() {
     addEventListener("message", this.step_func(function(evt) {
         assert_equals(evt.target, self);
--- a/testing/web-platform/update/upstream.py
+++ b/testing/web-platform/update/upstream.py
@@ -128,41 +128,74 @@ class GetBaseCommit(Step):
         state.base_commit = state.sync_tree.get_remote_sha1(state.sync["remote_url"],
                                                             state.sync["branch"])
         self.logger.debug("New base commit is %s" % state.base_commit.sha1)
 
 
 class LoadCommits(Step):
     """Get a list of commits in the gecko tree that need to be upstreamed"""
 
-    provides = ["source_commits"]
+    provides = ["source_commits", "has_backouts"]
 
     def create(self, state):
         state.source_commits = state.local_tree.log(state.last_sync_commit,
                                                     state.tests_path)
 
         update_regexp = re.compile("Bug \d+ - Update web-platform-tests to revision [0-9a-f]{40}")
 
+        state.has_backouts = False
+
         for i, commit in enumerate(state.source_commits[:]):
             if update_regexp.match(commit.message.text):
                 # This is a previous update commit so ignore it
                 state.source_commits.remove(commit)
                 continue
 
-            if commit.message.backouts:
+            elif commit.message.backouts:
                 #TODO: Add support for collapsing backouts
-                raise NotImplementedError("Need to get the Git->Hg commits for backouts and remove the backed out patch")
+                state.has_backouts = True
 
-            if not commit.message.bug:
+            elif not commit.message.bug:
                 self.logger.error("Commit %i (%s) doesn't have an associated bug number." %
-                             (i + 1, commit.sha1))
+                                  (i + 1, commit.sha1))
                 return exit_unclean
 
         self.logger.debug("Source commits: %s" % state.source_commits)
 
+class SelectCommits(Step):
+    """Provide a UI to select which commits to upstream"""
+
+    def create(self, state):
+        while True:
+            commits = state.source_commits[:]
+            for i, commit in enumerate(commits):
+                print "%i:\t%s" % (i, commit.message.summary)
+
+            remove = raw_input("Provide a space-separated list of any commits numbers to remove from the list to upstream:\n").strip()
+            remove_idx = set()
+            for item in remove.split(" "):
+                try:
+                    item = int(item)
+                except:
+                    continue
+                if item < 0 or item >= len(commits):
+                    continue
+                remove_idx.add(item)
+
+            keep_commits = [(i,cmt) for i,cmt in enumerate(commits) if i not in remove_idx]
+            #TODO: consider printed removed commits
+            print "Selected the following commits to keep:"
+            for i, commit in keep_commits:
+                print "%i:\t%s" % (i, commit.message.summary)
+            confirm = raw_input("Keep the above commits? y/n\n").strip().lower()
+
+            if confirm == "y":
+                state.source_commits = [item[1] for item in keep_commits]
+                break
+
 class MovePatches(Step):
     """Convert gecko commits into patches against upstream and commit these to the sync tree."""
 
     provides = ["commits_loaded"]
 
     def create(self, state):
         state.commits_loaded = 0
 
@@ -184,19 +217,16 @@ class MovePatches(Step):
 
 class RebaseCommits(Step):
     """Rebase commits from the current branch on top of the upstream destination branch.
 
     This step is particularly likely to fail if the rebase generates merge conflicts.
     In that case the conflicts can be fixed up locally and the sync process restarted
     with --continue.
     """
-
-    provides = ["rebased_commits"]
-
     def create(self, state):
         self.logger.info("Rebasing local commits")
         continue_rebase = False
         # Check if there's a rebase in progress
         if (os.path.exists(os.path.join(state.sync_tree.root,
                                         ".git",
                                         "rebase-merge")) or
             os.path.exists(os.path.join(state.sync_tree.root,
@@ -204,23 +234,24 @@ class RebaseCommits(Step):
                                         "rebase-apply"))):
             continue_rebase = True
 
         try:
             state.sync_tree.rebase(state.base_commit, continue_rebase=continue_rebase)
         except subprocess.CalledProcessError:
             self.logger.info("Rebase failed, fix merge and run %s again with --continue" % sys.argv[0])
             raise
-        state.rebased_commits = state.sync_tree.log(state.base_commit)
         self.logger.info("Rebase successful")
 
 class CheckRebase(Step):
     """Check if there are any commits remaining after rebase"""
+    provides = ["rebased_commits"]
 
     def create(self, state):
+        state.rebased_commits = state.sync_tree.log(state.base_commit)
         if not state.rebased_commits:
             self.logger.info("Nothing to upstream, exiting")
             return exit_clean
 
 class MergeUpstream(Step):
     """Run steps to push local commits as seperate PRs and merge upstream."""
 
     provides = ["merge_index", "gh_repo"]
@@ -324,16 +355,17 @@ class PRDeleteBranch(Step):
 
 class SyncToUpstreamRunner(StepRunner):
     """Runner for syncing local changes to upstream"""
     steps = [LoadManifest,
              CheckoutBranch,
              GetLastSyncCommit,
              GetBaseCommit,
              LoadCommits,
+             SelectCommits,
              MovePatches,
              RebaseCommits,
              CheckRebase,
              MergeUpstream,
              UpdateLastSyncCommit]
 
 
 class PRMergeRunner(StepRunner):
--- a/toolkit/locales/en-US/chrome/global/aboutServiceWorkers.properties
+++ b/toolkit/locales/en-US/chrome/global/aboutServiceWorkers.properties
@@ -17,16 +17,17 @@ currentWorkerURL = Current Worker URL:
 activeCacheName = Active Cache Name:
 
 waitingCacheName = Waiting Cache Name:
 
 true = true
 
 false = false
 
+# LOCALIZATION NOTE this term is used as a button label (verb, not noun).
 update = Update
 
 unregister = Unregister
 
-waiting = Waiting...
+waiting = Waiting…
 
 # LOCALIZATION NODE the term "Service Worker" should not translated.
 unregisterError = Failed to unregister this Service Worker.
--- a/toolkit/xre/nsAppRunner.cpp
+++ b/toolkit/xre/nsAppRunner.cpp
@@ -3605,35 +3605,19 @@ XREMain::XRE_mainStartup(bool* aExitFlag
     display_name = PR_GetEnv("DISPLAY");
     if (!display_name) {
       PR_fprintf(PR_STDERR, "Error: no display specified\n");
       return 1;
     }
   }
 #endif /* MOZ_WIDGET_GTK */
 #ifdef MOZ_X11
-  // Init X11 in thread-safe mode. Must be called prior to the first call to XOpenDisplay 
+  // Init X11 in thread-safe mode. Must be called prior to the first call to XOpenDisplay
   // (called inside gdk_display_open). This is a requirement for off main tread compositing.
-  // This is done only on X11 platforms if the environment variable MOZ_USE_OMTC is set so 
-  // as to avoid overhead when omtc is not used. 
-  //
-  // On nightly builds, we call this by default to enable OMTC for Electrolysis testing. On
-  // aurora, beta, and release builds, there is a small tpaint regression from enabling this
-  // call, so it sits behind an environment variable.
-  //
-  // An environment variable is used instead of a pref on X11 platforms because we start having 
-  // access to prefs long after the first call to XOpenDisplay which is hard to change due to 
-  // interdependencies in the initialization.
-# ifndef NIGHTLY_BUILD
-  if (PR_GetEnv("MOZ_USE_OMTC") ||
-      PR_GetEnv("MOZ_OMTC_ENABLED"))
-# endif
-  {
-    XInitThreads();
-  }
+  XInitThreads();
 #endif
 #if defined(MOZ_WIDGET_GTK)
   {
     mGdkDisplay = gdk_display_open(display_name);
     if (!mGdkDisplay) {
       PR_fprintf(PR_STDERR, "Error: cannot open display: %s\n", display_name);
       return 1;
     }
--- a/tools/quitter/Makefile.in
+++ b/tools/quitter/Makefile.in
@@ -1,15 +1,10 @@
 #
 # 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/.
 
 XPI_PKGNAME = quitter@mozilla.org
 NO_JS_MANIFEST = 1
 
-DIST_FILES = \
-  install.rdf \
-  chrome.manifest \
-  $(NULL)
-
 # Used in install.rdf
 USE_EXTENSION_MANIFEST=1
--- a/tools/quitter/moz.build
+++ b/tools/quitter/moz.build
@@ -5,9 +5,14 @@
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 EXTRA_COMPONENTS += [
     'QuitterObserver.js',
 ]
 
 XPI_NAME = 'quitter'
 
-JAR_MANIFESTS += ['jar.mn']
\ No newline at end of file
+JAR_MANIFESTS += ['jar.mn']
+
+DIST_FILES += [
+    'chrome.manifest',
+    'install.rdf',
+]
--- a/widget/nsBaseWidget.cpp
+++ b/widget/nsBaseWidget.cpp
@@ -1132,27 +1132,16 @@ void nsBaseWidget::CreateCompositor(int 
     ConfigureAPZCTreeManager();
   }
 
   TextureFactoryIdentifier textureFactoryIdentifier;
   PLayerTransactionChild* shadowManager = nullptr;
   nsTArray<LayersBackend> backendHints;
   GetPreferredCompositorBackends(backendHints);
 
-#if !defined(MOZ_X11) && !defined(XP_WIN)
-  if (!mRequireOffMainThreadCompositing &&
-      !Preferences::GetBool("layers.offmainthreadcomposition.force-basic", false)) {
-    for (size_t i = 0; i < backendHints.Length(); ++i) {
-      if (backendHints[i] == LayersBackend::LAYERS_BASIC) {
-        backendHints[i] = LayersBackend::LAYERS_NONE;
-      }
-    }
-  }
-#endif
-
   bool success = false;
   if (!backendHints.IsEmpty()) {
     shadowManager = mCompositorChild->SendPLayerTransactionConstructor(
       backendHints, 0, &textureFactoryIdentifier, &success);
   }
 
   ShadowLayerForwarder* lf = lm->AsShadowForwarder();
 
--- a/xpcom/base/CycleCollectedJSRuntime.cpp
+++ b/xpcom/base/CycleCollectedJSRuntime.cpp
@@ -93,17 +93,17 @@ class IncrementalFinalizeRunnable : publ
   typedef nsAutoTArray<DeferredFinalizeFunctionHolder, 16> DeferredFinalizeArray;
   typedef CycleCollectedJSRuntime::DeferredFinalizerTable DeferredFinalizerTable;
 
   CycleCollectedJSRuntime* mRuntime;
   DeferredFinalizeArray mDeferredFinalizeFunctions;
   uint32_t mFinalizeFunctionToRun;
   bool mReleasing;
 
-  static const PRTime SliceMillis = 10; /* ms */
+  static const PRTime SliceMillis = 5; /* ms */
 
   static PLDHashOperator
   DeferredFinalizerEnumerator(DeferredFinalizeFunction& aFunction,
                               void*& aData,
                               void* aClosure);
 
 public:
   IncrementalFinalizeRunnable(CycleCollectedJSRuntime* aRt,
--- a/xulrunner/examples/simple/Makefile.in
+++ b/xulrunner/examples/simple/Makefile.in
@@ -1,15 +1,13 @@
 # vim:set ts=8 sw=8 sts=8 noet:
 # 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/.
 
-DIST_FILES	= application.ini
-
 ifneq (,$(filter windows,$(MOZ_WIDGET_TOOLKIT)))
 ICONS		= icons/simple.ico
 endif
 
 include $(topsrcdir)/config/rules.mk
 
 ifneq (,$(ICONS))
 libs:: $(ICONS)
--- a/xulrunner/examples/simple/moz.build
+++ b/xulrunner/examples/simple/moz.build
@@ -9,8 +9,11 @@ DIRS += ['components']
 XPI_NAME = 'simple'
 
 JAR_MANIFESTS += ['jar.mn']
 
 JS_PREFERENCE_FILES += [
     'simple-prefs.js',
 ]
 
+DIST_FILES += [
+    'application.ini',
+]