Backed out 7 changesets (bug 1193394) for browser-chrome failures on browser_ext_popup_background.js. CLOSED TREE
authorCsoregi Natalia <ncsoregi@mozilla.com>
Thu, 01 Mar 2018 16:29:02 +0200
changeset 461053 a416b0a21b1395dfe7dc28577a31be57c075d9b4
parent 461052 9683f24ff8ec5ba768c4a0a124d8439c228b2c8b
child 461055 40ba247350dad945cccd29680ee53d578c006420
child 461062 7516a83ba950f147de219154f00cd4de39a5b01a
push id1683
push usersfraser@mozilla.com
push dateThu, 26 Apr 2018 16:43:40 +0000
treeherdermozilla-release@5af6cb21869d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1193394
milestone60.0a1
backs out9683f24ff8ec5ba768c4a0a124d8439c228b2c8b
0e7140a7c841c3b74b70d050636a01b9d4619b56
a0e26f6b2784e7946f166dbaed90861342fa6fb1
29e1fceaf48d9a09662a18789d034f3093c4b5e8
b8632bbbd2730a57af28ba0c356f20b01a6185d5
a54ef2d8f89626be6809676ff3534f077910052c
55c94c05c57f0bb34ca131c68c79e05a97aa4b6c
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Backed out 7 changesets (bug 1193394) for browser-chrome failures on browser_ext_popup_background.js. CLOSED TREE Backed out changeset 9683f24ff8ec (bug 1193394) Backed out changeset 0e7140a7c841 (bug 1193394) Backed out changeset a0e26f6b2784 (bug 1193394) Backed out changeset 29e1fceaf48d (bug 1193394) Backed out changeset b8632bbbd273 (bug 1193394) Backed out changeset a54ef2d8f896 (bug 1193394) Backed out changeset 55c94c05c57f (bug 1193394)
browser/base/content/test/permissions/browser.ini
browser/base/content/test/siteIdentity/browser.ini
browser/base/content/test/webrtc/browser.ini
browser/components/customizableui/test/browser.ini
browser/components/extensions/test/browser/browser-common.ini
browser/modules/test/browser/browser.ini
dom/animation/Animation.cpp
dom/animation/Animation.h
dom/animation/DocumentTimeline.cpp
dom/base/CustomElementRegistry.cpp
dom/base/nsContentUtils.cpp
dom/base/nsContentUtils.h
dom/base/nsDOMMutationObserver.cpp
dom/base/nsGlobalWindowInner.cpp
dom/bindings/CallbackObject.cpp
dom/events/EventListenerManager.cpp
dom/indexedDB/ActorsChild.cpp
dom/indexedDB/IDBFileHandle.cpp
dom/indexedDB/IDBTransaction.cpp
dom/promise/Promise.cpp
dom/promise/Promise.h
dom/promise/tests/test_promise.html
dom/script/ScriptSettings.cpp
dom/script/ScriptSettings.h
dom/serviceworkers/ServiceWorkerPrivate.cpp
dom/workers/RuntimeService.cpp
dom/workers/WorkerPrivate.cpp
js/xpconnect/src/XPCJSContext.cpp
testing/web-platform/meta/IndexedDB/event-dispatch-active-flag.html.ini
testing/web-platform/meta/IndexedDB/transaction-deactivation-timing.html.ini
testing/web-platform/meta/IndexedDB/upgrade-transaction-deactivation-timing.html.ini
testing/web-platform/meta/IndexedDB/upgrade-transaction-lifecycle-user-aborted.html.ini
testing/web-platform/meta/custom-elements/microtasks-and-constructors.html.ini
testing/web-platform/meta/html/webappapis/scripting/event-loops/task_microtask_ordering.html.ini
testing/web-platform/meta/service-workers/service-worker/extendable-event-async-waituntil.https.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/basic.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/bidi/bidi_ruby.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/bidi/u0041_first.html.ini
testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/bidi/u06E9_no_strong_dir.html.ini
xpcom/base/CycleCollectedJSContext.cpp
xpcom/base/CycleCollectedJSContext.h
--- a/browser/base/content/test/permissions/browser.ini
+++ b/browser/base/content/test/permissions/browser.ini
@@ -1,16 +1,15 @@
 [DEFAULT]
 support-files=
   head.js
   permissions.html
 
 [browser_canvas_fingerprinting_resistance.js]
 [browser_permissions.js]
-skip-if = true # temporarily disabled for bug 1193394
 [browser_reservedkey.js]
 [browser_temporary_permissions.js]
 support-files =
   temporary_permissions_subframe.html
   ../webrtc/get_user_media.html
 [browser_temporary_permissions_expiry.js]
 [browser_temporary_permissions_navigation.js]
 [browser_temporary_permissions_tabs.js]
--- a/browser/base/content/test/siteIdentity/browser.ini
+++ b/browser/base/content/test/siteIdentity/browser.ini
@@ -97,10 +97,8 @@ tags = mcb
 support-files =
   ../general/moz.png
   test_no_mcb_for_loopback.html
 [browser_no_mcb_for_onions.js]
 tags = mcb
 support-files =
   test_no_mcb_for_onions.html
 [browser_check_identity_state.js]
-skip-if = true # temporarily disabled for bug 1193394
-
--- a/browser/base/content/test/webrtc/browser.ini
+++ b/browser/base/content/test/webrtc/browser.ini
@@ -1,29 +1,25 @@
 [DEFAULT]
 support-files =
   get_user_media.html
   get_user_media_in_frame.html
   get_user_media_content_script.js
   head.js
 
 [browser_devices_get_user_media.js]
-#skip-if = (os == "linux" && debug) # linux: bug 976544
-skip-if = true # temporarily disabled for bug 1193394
+skip-if = (os == "linux" && debug) # linux: bug 976544
 [browser_devices_get_user_media_anim.js]
 [browser_devices_get_user_media_in_frame.js]
-#skip-if = debug # bug 1369731
-skip-if = true # temporarily disabled for bug 1193394
+skip-if = debug # bug 1369731
 [browser_devices_get_user_media_multi_process.js]
 skip-if = debug && (os == "win" || os == "mac") # bug 1393761
 [browser_devices_get_user_media_paused.js]
 [browser_devices_get_user_media_screen.js]
-#skip-if = (os == "win" && ccov) # bug 1421724
-skip-if = true # temporarily disabled for bug 1193394
+skip-if = (os == "win" && ccov) # bug 1421724
 [browser_devices_get_user_media_tear_off_tab.js]
-skip-if = true # temporarily disabled for bug 1193394
 [browser_devices_get_user_media_unprompted_access.js]
 [browser_devices_get_user_media_unprompted_access_in_frame.js]
 [browser_devices_get_user_media_unprompted_access_tear_off_tab.js]
 skip-if = (os == "win" && bits == 64) # win8: bug 1334752
 [browser_devices_get_user_media_unprompted_access_queue_request.js]
 [browser_webrtc_hooks.js]
 [browser_devices_get_user_media_queue_request.js]
--- a/browser/components/customizableui/test/browser.ini
+++ b/browser/components/customizableui/test/browser.ini
@@ -150,17 +150,16 @@ skip-if = os == "linux" # linux doesn't 
 tags = fullscreen
 skip-if = os == "mac"
 [browser_panelUINotifications_fullscreen_noAutoHideToolbar.js]
 tags = fullscreen
 [browser_panelUINotifications_multiWindow.js]
 [browser_remove_customized_specials.js]
 [browser_switch_to_customize_mode.js]
 [browser_synced_tabs_menu.js]
-skip-if = true # temporarily disabled for bug 1193394
 [browser_backfwd_enabled_post_customize.js]
 [browser_check_tooltips_in_navbar.js]
 [browser_editcontrols_update.js]
 subsuite = clipboard
 [browser_customization_context_menus.js]
 [browser_newtab_button_customizemode.js]
 [browser_open_from_popup.js]
 [browser_sidebar_toggle.js]
--- a/browser/components/extensions/test/browser/browser-common.ini
+++ b/browser/components/extensions/test/browser/browser-common.ini
@@ -43,34 +43,31 @@ support-files =
 skip-if = os == 'win' || os == 'mac' # Bug 1405453
 [browser_ext_browserAction_contextMenu.js]
 # bug 1369197
 skip-if = os == 'linux'
 [browser_ext_browserAction_disabled.js]
 [browser_ext_browserAction_pageAction_icon.js]
 [browser_ext_browserAction_pageAction_icon_permissions.js]
 [browser_ext_browserAction_popup.js]
-#skip-if = (debug && os == 'linux' && bits == 32) || (os == 'win' && !debug) # Bug 1313372, win: Bug 1285500
-skip-if = true # temporarily disabled for bug 1193394
+skip-if = (debug && os == 'linux' && bits == 32) || (os == 'win' && !debug) # Bug 1313372, win: Bug 1285500
 [browser_ext_browserAction_popup_preload.js]
 skip-if = (os == 'win' && !debug) # bug 1352668
 [browser_ext_browserAction_popup_resize.js]
-skip-if = true # temporarily disabled for bug 1193394
 [browser_ext_browserAction_simple.js]
 [browser_ext_browserAction_telemetry.js]
 [browser_ext_browserAction_theme_icons.js]
 [browser_ext_browsingData_formData.js]
 [browser_ext_browsingData_history.js]
 [browser_ext_browsingData_indexedDB.js]
 [browser_ext_browsingData_localStorage.js]
 [browser_ext_browsingData_pluginData.js]
 [browser_ext_browsingData_serviceWorkers.js]
 [browser_ext_chrome_settings_overrides_home.js]
 [browser_ext_commands_execute_browser_action.js]
-skip-if = true # temporarily disabled for bug 1193394
 [browser_ext_commands_execute_page_action.js]
 [browser_ext_commands_execute_sidebar_action.js]
 [browser_ext_commands_getAll.js]
 [browser_ext_commands_onCommand.js]
 [browser_ext_commands_update.js]
 [browser_ext_contentscript_connect.js]
 [browser_ext_contextMenus.js]
 [browser_ext_contextMenus_checkboxes.js]
--- a/browser/modules/test/browser/browser.ini
+++ b/browser/modules/test/browser/browser.ini
@@ -7,18 +7,17 @@ support-files =
   browser_BrowserErrorReporter.html
 [browser_BrowserUITelemetry_buckets.js]
 skip-if = !e10s # Bug 1373549
 [browser_BrowserUITelemetry_defaults.js]
 skip-if = !e10s # Bug 1373549
 [browser_BrowserUITelemetry_sidebar.js]
 skip-if = !e10s # Bug 1373549
 [browser_BrowserUITelemetry_syncedtabs.js]
-#skip-if = !e10s # Bug 1373549
-skip-if = true # temporarily disabled for bug 1193394
+skip-if = !e10s # Bug 1373549
 [browser_ContentSearch.js]
 support-files =
   contentSearch.js
   contentSearchBadImage.xml
   contentSearchSuggestions.sjs
   contentSearchSuggestions.xml
   !/browser/components/search/test/head.js
   !/browser/components/search/test/testEngine.xml
--- a/dom/animation/Animation.cpp
+++ b/dom/animation/Animation.cpp
@@ -1560,49 +1560,28 @@ Animation::GetRenderedDocument() const
 {
   if (!mEffect || !mEffect->AsKeyframeEffect()) {
     return nullptr;
   }
 
   return mEffect->AsKeyframeEffect()->GetRenderedDocument();
 }
 
-class AsyncFinishNotification : public MicroTaskRunnable
-{
-public:
-  explicit AsyncFinishNotification(Animation* aAnimation)
-  : MicroTaskRunnable()
-  , mAnimation(aAnimation)
-  {}
-
-  virtual void Run(AutoSlowOperation& aAso) override
-  {
-    mAnimation->DoFinishNotificationImmediately(this);
-    mAnimation = nullptr;
-  }
-
-  virtual bool Suppressed() override
-  {
-    nsIGlobalObject* global = mAnimation->GetOwnerGlobal();
-    return global && global->IsInSyncOperation();
-  }
-
-private:
-  RefPtr<Animation> mAnimation;
-};
-
 void
 Animation::DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag)
 {
   CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
 
   if (aSyncNotifyFlag == SyncNotifyFlag::Sync) {
     DoFinishNotificationImmediately();
-  } else if (!mFinishNotificationTask) {
-    RefPtr<MicroTaskRunnable> runnable = new AsyncFinishNotification(this);
+  } else if (!mFinishNotificationTask.IsPending()) {
+    RefPtr<nsRunnableMethod<Animation>> runnable =
+      NewRunnableMethod("dom::Animation::DoFinishNotificationImmediately",
+                        this,
+                        &Animation::DoFinishNotificationImmediately);
     context->DispatchToMicroTask(do_AddRef(runnable));
     mFinishNotificationTask = runnable.forget();
   }
 }
 
 void
 Animation::ResetFinishedPromise()
 {
@@ -1615,23 +1594,19 @@ Animation::MaybeResolveFinishedPromise()
 {
   if (mFinished) {
     mFinished->MaybeResolve(this);
   }
   mFinishedIsResolved = true;
 }
 
 void
-Animation::DoFinishNotificationImmediately(MicroTaskRunnable* aAsync)
+Animation::DoFinishNotificationImmediately()
 {
-  if (aAsync && aAsync != mFinishNotificationTask) {
-    return;
-  }
-
-  mFinishNotificationTask = nullptr;
+  mFinishNotificationTask.Revoke();
 
   if (PlayState() != AnimationPlayState::Finished) {
     return;
   }
 
   MaybeResolveFinishedPromise();
 
   DispatchPlaybackEvent(NS_LITERAL_STRING("finish"));
--- a/dom/animation/Animation.h
+++ b/dom/animation/Animation.h
@@ -6,17 +6,16 @@
 
 #ifndef mozilla_dom_Animation_h
 #define mozilla_dom_Animation_h
 
 #include "nsWrapperCache.h"
 #include "nsCycleCollectionParticipant.h"
 #include "mozilla/AnimationPerformanceWarning.h"
 #include "mozilla/Attributes.h"
-#include "mozilla/CycleCollectedJSContext.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/EffectCompositor.h" // For EffectCompositor::CascadeLevel
 #include "mozilla/LinkedList.h"
 #include "mozilla/TimeStamp.h" // for TimeStamp, TimeDuration
 #include "mozilla/dom/AnimationBinding.h" // for AnimationPlayState
 #include "mozilla/dom/AnimationEffectReadOnly.h"
 #include "mozilla/dom/AnimationTimeline.h"
 #include "mozilla/dom/Promise.h"
@@ -40,17 +39,16 @@ class nsIDocument;
 class nsIFrame;
 
 namespace mozilla {
 
 struct AnimationRule;
 
 namespace dom {
 
-class AsyncFinishNotification;
 class CSSAnimation;
 class CSSTransition;
 
 class Animation
   : public DOMEventTargetHelper
   , public LinkedListElement<Animation>
 {
 protected:
@@ -446,18 +444,17 @@ protected:
   void UpdateFinishedState(SeekFlag aSeekFlag,
                            SyncNotifyFlag aSyncNotifyFlag);
   void UpdateEffect();
   void FlushStyle() const;
   void PostUpdate();
   void ResetFinishedPromise();
   void MaybeResolveFinishedPromise();
   void DoFinishNotification(SyncNotifyFlag aSyncNotifyFlag);
-  friend class AsyncFinishNotification;
-  void DoFinishNotificationImmediately(MicroTaskRunnable* aAsync = nullptr);
+  void DoFinishNotificationImmediately();
   void DispatchPlaybackEvent(const nsAString& aName);
 
   /**
    * Remove this animation from the pending animation tracker and reset
    * mPendingState as necessary. The caller is responsible for resolving or
    * aborting the mReady promise as necessary.
    */
   void CancelPendingTasks();
@@ -540,17 +537,17 @@ protected:
   // possible for two different objects to have the same index.
   uint64_t mAnimationIndex;
 
   bool mFinishedAtLastComposeStyle;
   // Indicates that the animation should be exposed in an element's
   // getAnimations() list.
   bool mIsRelevant;
 
-  RefPtr<MicroTaskRunnable> mFinishNotificationTask;
+  nsRevocableEventPtr<nsRunnableMethod<Animation>> mFinishNotificationTask;
   // True if mFinished is resolved or would be resolved if mFinished has
   // yet to be created. This is not set when mFinished is rejected since
   // in that case mFinished is immediately reset to represent a new current
   // finished promise.
   bool mFinishedIsResolved;
 
   // True if this animation was triggered at the same time as one or more
   // geometric animations and hence we should run any transform animations on
--- a/dom/animation/DocumentTimeline.cpp
+++ b/dom/animation/DocumentTimeline.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "DocumentTimeline.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/dom/DocumentTimelineBinding.h"
+#include "mozilla/dom/Promise.h"
 #include "AnimationUtils.h"
 #include "nsContentUtils.h"
 #include "nsDOMMutationObserver.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsRefreshDriver.h"
 
@@ -154,21 +155,24 @@ DocumentTimeline::WillRefresh(mozilla::T
   MOZ_ASSERT(GetRefreshDriver(),
              "Should be able to reach refresh driver from within WillRefresh");
 
   bool needsTicks = false;
   nsTArray<Animation*> animationsToRemove(mAnimations.Count());
 
   // https://drafts.csswg.org/web-animations-1/#update-animations-and-send-events,
   // step2.
+  // FIXME: This needs to be replaced with nsAutoMicroTask.
   // Note that this should be done before nsAutoAnimationMutationBatch.  If
   // PerformMicroTaskCheckpoint was called before nsAutoAnimationMutationBatch
   // is destroyed, some mutation records might not be delivered in this
   // checkpoint.
-  nsAutoMicroTask mt;
+  auto autoPerformMicrotaskCheckpoint = MakeScopeExit([] {
+    Promise::PerformMicroTaskCheckpoint();
+  });
   nsAutoAnimationMutationBatch mb(mDocument);
 
   for (Animation* animation = mAnimationOrder.getFirst(); animation;
        animation = animation->getNext()) {
     // Skip any animations that are longer need associated with this timeline.
     if (animation->GetTimeline() != this) {
       // If animation has some other timeline, it better not be also in the
       // animation list of this timeline object!
--- a/dom/base/CustomElementRegistry.cpp
+++ b/dom/base/CustomElementRegistry.cpp
@@ -1062,17 +1062,17 @@ CustomElementReactionsStack::Enqueue(Ele
   elementData->mReactionQueue.AppendElement(aReaction);
 
   if (mIsBackupQueueProcessing) {
     return;
   }
 
   CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
   RefPtr<BackupQueueMicroTask> bqmt = new BackupQueueMicroTask(this);
-  context->DispatchToMicroTask(bqmt.forget());
+  context->DispatchMicroTaskRunnable(bqmt.forget());
 }
 
 void
 CustomElementReactionsStack::InvokeBackupQueue()
 {
   // Check backup queue size in order to reduce function call overhead.
   if (!mBackupQueue.IsEmpty()) {
     // Upgrade reactions won't be scheduled in backup queue and the exception of
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5866,20 +5866,20 @@ void
 nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
 {
   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
   CycleCollectedJSContext::Get()->RunInStableState(Move(aRunnable));
 }
 
 /* static */
 void
-nsContentUtils::AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction)
+nsContentUtils::RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable)
 {
   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
-  CycleCollectedJSContext::Get()->AddPendingIDBTransaction(Move(aTransaction));
+  CycleCollectedJSContext::Get()->RunInMetastableState(Move(aRunnable));
 }
 
 /* static */
 bool
 nsContentUtils::IsInStableOrMetaStableState()
 {
   MOZ_ASSERT(CycleCollectedJSContext::Get(), "Must be on a script thread!");
   return CycleCollectedJSContext::Get()->IsInStableOrMetaStableState();
@@ -6928,26 +6928,19 @@ nsContentUtils::IsSubDocumentTabbable(ns
   if (!contentViewer) {
     return false;
   }
 
   nsCOMPtr<nsIContentViewer> zombieViewer;
   contentViewer->GetPreviousViewer(getter_AddRefs(zombieViewer));
 
   // If there are 2 viewers for the current docshell, that
-  // means the current document may be a zombie document.
-  // While load and pageshow events are dispatched, zombie viewer is the old,
-  // to be hidden document.
-  if (zombieViewer) {
-    bool inOnLoad = false;
-    docShell->GetIsExecutingOnLoadHandler(&inOnLoad);
-    return inOnLoad;
-  }
-
-  return true;
+  // means the current document is a zombie document.
+  // Only navigate into the subdocument if it's not a zombie.
+  return !zombieViewer;
 }
 
 bool
 nsContentUtils::IsUserFocusIgnored(nsINode* aNode)
 {
   if (!nsGenericHTMLFrameElement::BrowserFramesEnabled()) {
     return false;
   }
--- a/dom/base/nsContentUtils.h
+++ b/dom/base/nsContentUtils.h
@@ -2026,22 +2026,27 @@ public:
    * finished, see
    * http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
    * In practice this runs aRunnable once the currently executing event
    * finishes. If called multiple times per task/event, all the runnables will
    * be executed, in the order in which RunInStableState() was called.
    */
   static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable);
 
-  /* Add a pending IDBTransaction to be cleaned up at the end of performing a
-   * microtask checkpoint.
-   * See the step of "Cleanup Indexed Database Transactions" in
-   * https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
+  /* Add a "synchronous section", in the form of an nsIRunnable run once the
+   * event loop has reached a "metastable state". |aRunnable| must not cause any
+   * queued events to be processed (i.e. must not spin the event loop).
+   * We've reached a metastable state when the currently executing task or
+   * microtask has finished.  This is not specced at this time.
+   * In practice this runs aRunnable once the currently executing task or
+   * microtask finishes.  If called multiple times per microtask, all the
+   * runnables will be executed, in the order in which RunInMetastableState()
+   * was called
    */
-  static void AddPendingIDBTransaction(already_AddRefed<nsIRunnable> aTransaction);
+  static void RunInMetastableState(already_AddRefed<nsIRunnable> aRunnable);
 
   /**
    * Returns true if we are doing StableState/MetastableState.
    */
   static bool IsInStableOrMetaStableState();
 
   /* Process viewport META data. This gives us information for the scale
    * and zoom of a page on mobile devices. We stick the information in
--- a/dom/base/nsDOMMutationObserver.cpp
+++ b/dom/base/nsDOMMutationObserver.cpp
@@ -615,17 +615,17 @@ nsDOMMutationObserver::QueueMutationObse
 {
   CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
   if (!ccjs) {
     return;
   }
 
   RefPtr<MutationObserverMicroTask> momt =
     new MutationObserverMicroTask();
-  ccjs->DispatchToMicroTask(momt.forget());
+  ccjs->DispatchMicroTaskRunnable(momt.forget());
 }
 
 void
 nsDOMMutationObserver::HandleMutations(mozilla::AutoSlowOperation& aAso)
 {
   if (sScheduledMutationObservers ||
       mozilla::dom::DocGroup::sPendingDocGroups) {
     HandleMutationsInternal(aAso);
@@ -638,17 +638,17 @@ nsDOMMutationObserver::RescheduleForRun(
   if (!sScheduledMutationObservers) {
     CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
     if (!ccjs) {
       return;
     }
 
     RefPtr<MutationObserverMicroTask> momt =
       new MutationObserverMicroTask();
-    ccjs->DispatchToMicroTask(momt.forget());
+    ccjs->DispatchMicroTaskRunnable(momt.forget());
     sScheduledMutationObservers = new AutoTArray<RefPtr<nsDOMMutationObserver>, 4>;
   }
 
   bool didInsert = false;
   for (uint32_t i = 0; i < sScheduledMutationObservers->Length(); ++i) {
     if (static_cast<nsDOMMutationObserver*>((*sScheduledMutationObservers)[i])
           ->mId > mId) {
       sScheduledMutationObservers->InsertElementAt(i, this);
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -6710,16 +6710,22 @@ nsGlobalWindowInner::RunTimeoutHandler(T
   // Call() on a Function here since we're in a loop
   // where we're likely to be running timeouts whose OS timers
   // didn't fire in time and we don't want to not fire those timers
   // now just because execution of one timer failed. We can't
   // propagate the error to anyone who cares about it from this
   // point anyway, and the script context should have already reported
   // the script error in the usual way - so we just drop it.
 
+  // Since we might be processing more timeouts, go ahead and flush the promise
+  // queue now before we do that.  We need to do that while we're still in our
+  // "running JS is safe" state (e.g. mRunningTimeout is set, timeout->mRunning
+  // is false).
+  Promise::PerformMicroTaskCheckpoint();
+
   if (trackNestingLevel) {
     TimeoutManager::SetNestingLevel(nestingLevel);
   }
 
   mTimeoutManager->EndRunningTimeout(last_running_timeout);
   timeout->mRunning = false;
 
   return timeout->mCleared;
--- a/dom/bindings/CallbackObject.cpp
+++ b/dom/bindings/CallbackObject.cpp
@@ -135,19 +135,21 @@ CallbackObject::CallSetup::CallSetup(Cal
                                      JSCompartment* aCompartment,
                                      bool aIsJSImplementedWebIDL)
   : mCx(nullptr)
   , mCompartment(aCompartment)
   , mErrorResult(aRv)
   , mExceptionHandling(aExceptionHandling)
   , mIsMainThread(NS_IsMainThread())
 {
-  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
-  if (ccjs) {
-    ccjs->EnterMicroTask();
+  if (mIsMainThread) {
+    CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+    if (ccjs) {
+      ccjs->EnterMicroTask();
+    }
   }
 
   // Compute the caller's subject principal (if necessary) early, before we
   // do anything that might perturb the relevant state.
   nsIPrincipal* webIDLCallerPrincipal = nullptr;
   if (aIsJSImplementedWebIDL) {
     webIDLCallerPrincipal = nsContentUtils::SubjectPrincipalOrSystemIfNativeCaller();
   }
@@ -344,19 +346,21 @@ CallbackObject::CallSetup::~CallSetup()
     }
   }
 
   mAutoIncumbentScript.reset();
   mAutoEntryScript.reset();
 
   // It is important that this is the last thing we do, after leaving the
   // compartment and undoing all our entry/incumbent script changes
-  CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
-  if (ccjs) {
-    ccjs->LeaveMicroTask();
+  if (mIsMainThread) {
+    CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+    if (ccjs) {
+      ccjs->LeaveMicroTask();
+    }
   }
 }
 
 already_AddRefed<nsISupports>
 CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
                                           const nsIID& aIID) const
 {
   MOZ_ASSERT(NS_IsMainThread());
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -1090,27 +1090,37 @@ EventListenerManager::HandleEventSubType
   // compiled the event handler itself
   if ((aListener->mListenerType == Listener::eJSEventListener) &&
       aListener->mHandlerIsString) {
     result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
     aListener = nullptr;
   }
 
   if (NS_SUCCEEDED(result)) {
-    nsAutoMicroTask mt;
-
+    if (mIsMainThreadELM) {
+      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+      if (ccjs) {
+        ccjs->EnterMicroTask();
+      }
+    }
     // nsIDOMEvent::currentTarget is set in EventDispatcher.
     if (listenerHolder.HasWebIDLCallback()) {
       ErrorResult rv;
       listenerHolder.GetWebIDLCallback()->
         HandleEvent(aCurrentTarget, *(aDOMEvent->InternalDOMEvent()), rv);
       result = rv.StealNSResult();
     } else {
       result = listenerHolder.GetXPCOMCallback()->HandleEvent(aDOMEvent);
     }
+    if (mIsMainThreadELM) {
+      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
+      if (ccjs) {
+        ccjs->LeaveMicroTask();
+      }
+    }
   }
 
   return result;
 }
 
 EventMessage
 EventListenerManager::GetLegacyEventMessage(EventMessage aEventMessage) const
 {
--- a/dom/indexedDB/ActorsChild.cpp
+++ b/dom/indexedDB/ActorsChild.cpp
@@ -844,36 +844,32 @@ DispatchSuccessEvent(ResultHelper* aResu
   } else {
     IDB_LOG_MARK("IndexedDB %s: Child  Request[%llu]: Firing %s event",
                  "IndexedDB %s: C R[%llu]: %s",
                  IDB_LOG_ID_STRING(),
                  request->LoggingSerialNumber(),
                  IDB_LOG_STRINGIFY(aEvent, kSuccessEventType));
   }
 
-  MOZ_ASSERT_IF(transaction,
-                transaction->IsOpen() && !transaction->IsAborted());
-
   bool dummy;
   nsresult rv = request->DispatchEvent(aEvent, &dummy);
   if (NS_WARN_IF(NS_FAILED(rv))) {
     return;
   }
 
+  MOZ_ASSERT_IF(transaction,
+                transaction->IsOpen() || transaction->IsAborted());
+
   WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
   MOZ_ASSERT(internalEvent);
 
   if (transaction &&
-      transaction->IsOpen()) {
-    if (internalEvent->mFlags.mExceptionWasRaised) {
-      transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
-    } else {
-      // To handle upgrade transaction.
-      transaction->Run();
-    }
+      transaction->IsOpen() &&
+      internalEvent->mFlags.mExceptionWasRaised) {
+    transaction->Abort(NS_ERROR_DOM_INDEXEDDB_ABORT_ERR);
   }
 }
 
 PRFileDesc*
 GetFileDescriptorFromStream(nsIInputStream* aStream)
 {
   MOZ_ASSERT(aStream);
 
--- a/dom/indexedDB/IDBFileHandle.cpp
+++ b/dom/indexedDB/IDBFileHandle.cpp
@@ -94,17 +94,17 @@ IDBFileHandle::Create(IDBMutableFile* aM
     new IDBFileHandle(aMutableFile, aMode);
 
   fileHandle->BindToOwner(aMutableFile);
 
   // XXX Fix!
   MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!");
 
   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle);
-  nsContentUtils::AddPendingIDBTransaction(runnable.forget());
+  nsContentUtils::RunInMetastableState(runnable.forget());
 
   fileHandle->mCreating = true;
 
   aMutableFile->RegisterFileHandle(fileHandle);
 
   return fileHandle.forget();
 }
 
--- a/dom/indexedDB/IDBTransaction.cpp
+++ b/dom/indexedDB/IDBTransaction.cpp
@@ -187,21 +187,25 @@ IDBTransaction::CreateVersionChange(
     new IDBTransaction(aDatabase,
                        emptyObjectStoreNames,
                        VERSION_CHANGE);
   aOpenRequest->GetCallerLocation(transaction->mFilename,
                                   &transaction->mLineNo, &transaction->mColumn);
 
   transaction->SetScriptOwner(aDatabase->GetScriptOwner());
 
+  nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
+  nsContentUtils::RunInMetastableState(runnable.forget());
+
   transaction->NoteActiveTransaction();
 
   transaction->mBackgroundActor.mVersionChangeBackgroundActor = aActor;
   transaction->mNextObjectStoreId = aNextObjectStoreId;
   transaction->mNextIndexId = aNextIndexId;
+  transaction->mCreating = true;
 
   aDatabase->RegisterTransaction(transaction);
   transaction->mRegistered = true;
 
   return transaction.forget();
 }
 
 // static
@@ -241,17 +245,17 @@ IDBTransaction::Create(JSContext* aCx, I
 #endif
       return nullptr;
     }
 
     transaction->mWorkerHolder = Move(workerHolder);
   }
 
   nsCOMPtr<nsIRunnable> runnable = do_QueryObject(transaction);
-  nsContentUtils::AddPendingIDBTransaction(runnable.forget());
+  nsContentUtils::RunInMetastableState(runnable.forget());
 
   transaction->mCreating = true;
 
   aDatabase->RegisterTransaction(transaction);
   transaction->mRegistered = true;
 
   return transaction.forget();
 }
--- a/dom/promise/Promise.cpp
+++ b/dom/promise/Promise.cpp
@@ -505,16 +505,136 @@ Promise::ReportRejectedPromise(JSContext
   RefPtr<nsIRunnable> event = new AsyncErrorReporter(xpcReport);
   if (win) {
     win->Dispatch(mozilla::TaskCategory::Other, event.forget());
   } else {
     NS_DispatchToMainThread(event);
   }
 }
 
+bool
+Promise::PerformMicroTaskCheckpoint()
+{
+  MOZ_ASSERT(NS_IsMainThread(), "Wrong thread!");
+
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+
+  // On the main thread, we always use the main promise micro task queue.
+  std::queue<nsCOMPtr<nsIRunnable>>& microtaskQueue =
+    context->GetPromiseMicroTaskQueue();
+
+  if (microtaskQueue.empty()) {
+    return false;
+  }
+
+  AutoSlowOperation aso;
+
+  do {
+    nsCOMPtr<nsIRunnable> runnable = microtaskQueue.front().forget();
+    MOZ_ASSERT(runnable);
+
+    // This function can re-enter, so we remove the element before calling.
+    microtaskQueue.pop();
+    nsresult rv = runnable->Run();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return false;
+    }
+    aso.CheckForInterrupt();
+    context->AfterProcessMicrotask();
+  } while (!microtaskQueue.empty());
+
+  return true;
+}
+
+bool
+Promise::IsWorkerDebuggerMicroTaskEmpty()
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+  if (!context) {
+    return true;
+  }
+
+  std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
+    &context->GetDebuggerPromiseMicroTaskQueue();
+
+  return microtaskQueue->empty();
+}
+
+void
+Promise::PerformWorkerMicroTaskCheckpoint()
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+  if (!context) {
+    return;
+  }
+
+  for (;;) {
+    // For a normal microtask checkpoint, we try to use the debugger microtask
+    // queue first. If the debugger queue is empty, we use the normal microtask
+    // queue instead.
+    std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
+      &context->GetDebuggerPromiseMicroTaskQueue();
+
+    if (microtaskQueue->empty()) {
+      microtaskQueue = &context->GetPromiseMicroTaskQueue();
+      if (microtaskQueue->empty()) {
+        break;
+      }
+    }
+
+    nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
+    MOZ_ASSERT(runnable);
+
+    // This function can re-enter, so we remove the element before calling.
+    microtaskQueue->pop();
+    nsresult rv = runnable->Run();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    context->AfterProcessMicrotask();
+  }
+}
+
+void
+Promise::PerformWorkerDebuggerMicroTaskCheckpoint()
+{
+  MOZ_ASSERT(!NS_IsMainThread(), "Wrong thread!");
+
+  CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
+  if (!context) {
+    return;
+  }
+
+  for (;;) {
+    // For a debugger microtask checkpoint, we always use the debugger microtask
+    // queue.
+    std::queue<nsCOMPtr<nsIRunnable>>* microtaskQueue =
+      &context->GetDebuggerPromiseMicroTaskQueue();
+
+    if (microtaskQueue->empty()) {
+      break;
+    }
+
+    nsCOMPtr<nsIRunnable> runnable = microtaskQueue->front().forget();
+    MOZ_ASSERT(runnable);
+
+    // This function can re-enter, so we remove the element before calling.
+    microtaskQueue->pop();
+    nsresult rv = runnable->Run();
+    if (NS_WARN_IF(NS_FAILED(rv))) {
+      return;
+    }
+    context->AfterProcessMicrotask();
+  }
+}
+
 JSObject*
 Promise::GlobalJSObject() const
 {
   return mGlobal->GetGlobalJSObject();
 }
 
 JSCompartment*
 Promise::Compartment() const
--- a/dom/promise/Promise.h
+++ b/dom/promise/Promise.h
@@ -99,16 +99,25 @@ public:
   // every translation unit that includes this header, because that would
   // require use to include DOMException.h either here or in all those
   // translation units.
   template<typename T>
   void MaybeRejectBrokenly(const T& aArg); // Not implemented by default; see
                                            // specializations in the .cpp for
                                            // the T values we support.
 
+  // Called by DOM to let us execute our callbacks.  May be called recursively.
+  // Returns true if at least one microtask was processed.
+  static bool PerformMicroTaskCheckpoint();
+
+  static void PerformWorkerMicroTaskCheckpoint();
+
+  static void PerformWorkerDebuggerMicroTaskCheckpoint();
+  static bool IsWorkerDebuggerMicroTaskEmpty();
+
   // WebIDL
 
   nsIGlobalObject* GetParentObject() const
   {
     return mGlobal;
   }
 
   // Do the equivalent of Promise.resolve in the current compartment of aCx.
--- a/dom/promise/tests/test_promise.html
+++ b/dom/promise/tests/test_promise.html
@@ -181,17 +181,17 @@ function promiseAsync_SyncXHR()
   });
 
   ok(!handlerExecuted, "Handlers are not called until the next microtask.");
 
   var xhr = new XMLHttpRequest();
   xhr.open("GET", "testXHR.txt", false);
   xhr.send(null);
 
-  ok(!handlerExecuted, "Sync XHR should not trigger microtask execution.");
+  todo(!handlerExecuted, "Sync XHR should not trigger microtask execution.");
 }
 
 function promiseDoubleThen() {
   var steps = 0;
   var promise = new Promise(function(r1, r2) {
     r1(42);
   });
 
--- a/dom/script/ScriptSettings.cpp
+++ b/dom/script/ScriptSettings.cpp
@@ -825,25 +825,24 @@ AutoSafeJSContext::AutoSafeJSContext(MOZ
              "This is quite odd.  We should have crashed in the "
              "xpc::NativeGlobal() call if xpc::UnprivilegedJunkScope() "
              "returned null, and inited correctly otherwise!");
 }
 
 AutoSlowOperation::AutoSlowOperation(MOZ_GUARD_OBJECT_NOTIFIER_ONLY_PARAM_IN_IMPL)
   : AutoJSAPI()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
   MOZ_GUARD_OBJECT_NOTIFIER_INIT;
 
   Init();
 }
 
 void
 AutoSlowOperation::CheckForInterrupt()
 {
-  // For now we support only main thread!
-  if (mIsMainThread) {
-    // JS_CheckForInterrupt expects us to be in a compartment.
-    JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
-    JS_CheckForInterrupt(cx());
-  }
+  // JS_CheckForInterrupt expects us to be in a compartment.
+  JSAutoCompartment ac(cx(), xpc::UnprivilegedJunkScope());
+  JS_CheckForInterrupt(cx());
 }
 
 } // namespace mozilla
--- a/dom/script/ScriptSettings.h
+++ b/dom/script/ScriptSettings.h
@@ -288,25 +288,25 @@ public:
     JS_ClearPendingException(cx());
   }
 
 protected:
   // Protected constructor for subclasses.  This constructor initialises the
   // AutoJSAPI, so Init must NOT be called on subclasses that use this.
   AutoJSAPI(nsIGlobalObject* aGlobalObject, bool aIsMainThread, Type aType);
 
+private:
   mozilla::Maybe<JSAutoRequest> mAutoRequest;
   mozilla::Maybe<JSAutoNullableCompartment> mAutoNullableCompartment;
   JSContext *mCx;
 
   // Whether we're mainthread or not; set when we're initialized.
   bool mIsMainThread;
   Maybe<JS::WarningReporter> mOldWarningReporter;
 
-private:
   void InitInternal(nsIGlobalObject* aGlobalObject, JSObject* aGlobal,
                     JSContext* aCx, bool aIsMainThread);
 
   AutoJSAPI(const AutoJSAPI&) = delete;
   AutoJSAPI& operator= (const AutoJSAPI&) = delete;
 };
 
 /*
--- a/dom/serviceworkers/ServiceWorkerPrivate.cpp
+++ b/dom/serviceworkers/ServiceWorkerPrivate.cpp
@@ -328,17 +328,17 @@ public:
   }
 
   void
   MaybeDone()
   {
     MOZ_ASSERT(mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
 
-    if (mPendingPromisesCount || !mKeepAliveToken) {
+    if (mPendingPromisesCount) {
       return;
     }
     if (mCallback) {
       mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
     }
 
     MaybeCleanup();
   }
@@ -360,28 +360,16 @@ private:
     if (mWorkerHolderAdded) {
       ReleaseWorker();
     }
 
     mKeepAliveToken = nullptr;
     mSelfRef = nullptr;
   }
 
-  class MaybeDoneRunner : public MicroTaskRunnable
-  {
-  public:
-    explicit MaybeDoneRunner(KeepAliveHandler* aHandler) : mHandler(aHandler) {}
-    virtual void Run(AutoSlowOperation& aAso) override
-    {
-      mHandler->MaybeDone();
-    }
-
-    RefPtr<KeepAliveHandler> mHandler;
-  };
-
   void
   RemovePromise(ExtendableEventResult aResult)
   {
     MOZ_ASSERT(mWorkerPrivate);
     mWorkerPrivate->AssertIsOnWorkerThread();
     MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
 
     // Note: mSelfRef and mKeepAliveToken can be nullptr here
@@ -395,17 +383,20 @@ private:
     --mPendingPromisesCount;
     if (mPendingPromisesCount) {
       return;
     }
 
     CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
     MOZ_ASSERT(cx);
 
-    RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
+    RefPtr<nsIRunnable> r =
+      NewRunnableMethod("dom::KeepAliveHandler::MaybeDone",
+                        this,
+                        &KeepAliveHandler::MaybeDone);
     cx->DispatchToMicroTask(r.forget());
   }
 };
 
 NS_IMPL_ISUPPORTS0(KeepAliveHandler)
 
 class RegistrationUpdateRunnable : public Runnable
 {
--- a/dom/workers/RuntimeService.cpp
+++ b/dom/workers/RuntimeService.cpp
@@ -1047,20 +1047,16 @@ class MOZ_STACK_CLASS WorkerJSContext fi
 public:
   // The heap size passed here doesn't matter, we will change it later in the
   // call to JS_SetGCParameter inside InitJSContextForWorker.
   explicit WorkerJSContext(WorkerPrivate* aWorkerPrivate)
     : mWorkerPrivate(aWorkerPrivate)
   {
     MOZ_COUNT_CTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
     MOZ_ASSERT(aWorkerPrivate);
-    // Magical number 2. Workers have the base recursion depth 1, and normal
-    // runnables run at level 2, and we don't want to process microtasks
-    // at any other level.
-    SetTargetedMicroTaskRecursionDepth(2);
   }
 
   ~WorkerJSContext()
   {
     MOZ_COUNT_DTOR_INHERITED(WorkerJSContext, CycleCollectedJSContext);
     JSContext* cx = MaybeContext();
     if (!cx) {
       return;   // Initialize() must have failed
@@ -1104,42 +1100,54 @@ public:
     JS_SetWrapObjectCallbacks(cx, &WrapObjectCallbacks);
     if (mWorkerPrivate->IsDedicatedWorker()) {
       JS_SetFutexCanWait(cx);
     }
 
     return NS_OK;
   }
 
-  virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable) override
+  virtual void AfterProcessTask(uint32_t aRecursionDepth) override
   {
-    RefPtr<MicroTaskRunnable> runnable(aRunnable);
+    // Only perform the Promise microtask checkpoint on the outermost event
+    // loop.  Don't run it, for example, during sync XHR or importScripts.
+    if (aRecursionDepth == 2) {
+      CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
+    } else if (aRecursionDepth > 2) {
+      AutoDisableMicroTaskCheckpoint disableMicroTaskCheckpoint;
+      CycleCollectedJSContext::AfterProcessTask(aRecursionDepth);
+    }
+  }
+
+  virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable) override
+  {
+    RefPtr<nsIRunnable> runnable(aRunnable);
 
     MOZ_ASSERT(!NS_IsMainThread());
     MOZ_ASSERT(runnable);
 
-    std::queue<RefPtr<MicroTaskRunnable>>* microTaskQueue = nullptr;
+    std::queue<nsCOMPtr<nsIRunnable>>* microTaskQueue = nullptr;
 
     JSContext* cx = GetCurrentWorkerThreadJSContext();
     NS_ASSERTION(cx, "This should never be null!");
 
     JS::Rooted<JSObject*> global(cx, JS::CurrentGlobalOrNull(cx));
     NS_ASSERTION(global, "This should never be null!");
 
     // On worker threads, if the current global is the worker global, we use the
-    // main micro task queue. Otherwise, the current global must be
+    // main promise micro task queue. Otherwise, the current global must be
     // either the debugger global or a debugger sandbox, and we use the debugger
-    // micro task queue instead.
+    // promise micro task queue instead.
     if (IsWorkerGlobal(global)) {
-      microTaskQueue = &GetMicroTaskQueue();
+      microTaskQueue = &mPromiseMicroTaskQueue;
     } else {
       MOZ_ASSERT(IsWorkerDebuggerGlobal(global) ||
                  IsWorkerDebuggerSandbox(global));
 
-      microTaskQueue = &GetDebuggerMicroTaskQueue();
+      microTaskQueue = &mDebuggerPromiseMicroTaskQueue;
     }
 
     microTaskQueue->push(runnable.forget());
   }
 
 private:
   WorkerPrivate* mWorkerPrivate;
 };
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -3254,20 +3254,18 @@ WorkerPrivate::DoRunLoop(JSContext* aCx)
         mDebuggerQueue.Pop(runnable);
         debuggerRunnablesPending = !mDebuggerQueue.IsEmpty();
       }
 
       MOZ_ASSERT(runnable);
       static_cast<nsIRunnable*>(runnable)->Run();
       runnable->Release();
 
-      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
-      if (ccjs) {
-        ccjs->PerformDebuggerMicroTaskCheckpoint();
-      }
+      // Flush the promise queue.
+      Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
 
       if (debuggerRunnablesPending) {
         WorkerDebuggerGlobalScope* globalScope = DebuggerGlobalScope();
         MOZ_ASSERT(globalScope);
 
         // Now *might* be a good time to GC. Let the JS engine make the decision.
         JSAutoCompartment ac(aCx, globalScope->GetGlobalJSObject());
         JS_MaybeGC(aCx);
@@ -4341,32 +4339,28 @@ WorkerPrivate::EnterDebuggerEventLoop()
     if (!debuggerRunnablesPending) {
       SetGCTimerMode(IdleTimer);
     }
 
     // Wait for something to do
     {
       MutexAutoLock lock(mMutex);
 
-      CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
-      std::queue<RefPtr<MicroTaskRunnable>>& debuggerMtQueue =
-        context->GetDebuggerMicroTaskQueue();
       while (mControlQueue.IsEmpty() &&
              !(debuggerRunnablesPending = !mDebuggerQueue.IsEmpty()) &&
-             debuggerMtQueue.empty()) {
+             Promise::IsWorkerDebuggerMicroTaskEmpty()) {
         WaitForWorkerEvents();
       }
 
       ProcessAllControlRunnablesLocked();
 
       // XXXkhuey should we abort JS on the stack here if we got Abort above?
     }
-    CycleCollectedJSContext* context = CycleCollectedJSContext::Get();
-    if (context) {
-      context->PerformDebuggerMicroTaskCheckpoint();
+    if (!Promise::IsWorkerDebuggerMicroTaskEmpty()) {
+      Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
     }
     if (debuggerRunnablesPending) {
       // Start the periodic GC timer if it is not already running.
       SetGCTimerMode(PeriodicTimer);
 
       WorkerRunnable* runnable = nullptr;
 
       {
@@ -4374,20 +4368,18 @@ WorkerPrivate::EnterDebuggerEventLoop()
 
         mDebuggerQueue.Pop(runnable);
       }
 
       MOZ_ASSERT(runnable);
       static_cast<nsIRunnable*>(runnable)->Run();
       runnable->Release();
 
-      CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
-      if (ccjs) {
-        ccjs->PerformDebuggerMicroTaskCheckpoint();
-      }
+      // Flush the promise queue.
+      Promise::PerformWorkerDebuggerMicroTaskCheckpoint();
 
       // Now *might* be a good time to GC. Let the JS engine make the decision.
       if (JS::CurrentGlobalOrNull(cx)) {
         JS_MaybeGC(cx);
       }
     }
   }
 }
@@ -4742,18 +4734,18 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
     if (info->mIsInterval) {
       reason = "setInterval handler";
     } else {
       reason = "setTimeout handler";
     }
 
     RefPtr<Function> callback = info->mHandler->GetCallback();
     if (!callback) {
-      nsAutoMicroTask mt;
-
+      // scope for the AutoEntryScript, so it comes off the stack before we do
+      // Promise::PerformMicroTaskCheckpoint.
       AutoEntryScript aes(global, reason, false);
 
       // Evaluate the timeout expression.
       const nsAString& script = info->mHandler->GetHandlerText();
 
       const char* filename = nullptr;
       uint32_t lineNo = 0, dummyColumn = 0;
       info->mHandler->GetLocation(&filename, &lineNo, &dummyColumn);
@@ -4778,16 +4770,20 @@ WorkerPrivate::RunExpiredTimeouts(JSCont
         rv.SuppressException();
         retval = false;
         break;
       }
 
       rv.SuppressException();
     }
 
+    // Since we might be processing more timeouts, go ahead and flush
+    // the promise queue now before we do that.
+    Promise::PerformWorkerMicroTaskCheckpoint();
+
     NS_ASSERTION(mRunningExpiredTimeouts, "Someone changed this!");
   }
 
   // No longer possible to be called recursively.
   mRunningExpiredTimeouts = false;
 
   // Now remove canceled and expired timeouts from the main list.
   // NB: The timeouts present in expiredTimeouts must have the same order
--- a/js/xpconnect/src/XPCJSContext.cpp
+++ b/js/xpconnect/src/XPCJSContext.cpp
@@ -1214,16 +1214,31 @@ XPCJSContext::NewXPCJSContext(XPCJSConte
     MOZ_CRASH("new XPCJSContext failed to initialize.");
 }
 
 void
 XPCJSContext::BeforeProcessTask(bool aMightBlock)
 {
     MOZ_ASSERT(NS_IsMainThread());
 
+    // If ProcessNextEvent was called during a Promise "then" callback, we
+    // must process any pending microtasks before blocking in the event loop,
+    // otherwise we may deadlock until an event enters the queue later.
+    if (aMightBlock) {
+        if (Promise::PerformMicroTaskCheckpoint()) {
+            // If any microtask was processed, we post a dummy event in order to
+            // force the ProcessNextEvent call not to block.  This is required
+            // to support nested event loops implemented using a pattern like
+            // "while (condition) thread.processNextEvent(true)", in case the
+            // condition is triggered here by a Promise "then" callback.
+
+            NS_DispatchToMainThread(new Runnable("Empty_microtask_runnable"));
+        }
+    }
+
     // Start the slow script timer.
     mSlowScriptCheckpoint = mozilla::TimeStamp::NowLoRes();
     mSlowScriptSecondHalf = false;
     mSlowScriptActualWait = mozilla::TimeDuration();
     mTimeoutAccumulated = false;
 
     // As we may be entering a nested event loop, we need to
     // cancel any ongoing performance measurement.
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/IndexedDB/event-dispatch-active-flag.html.ini
@@ -0,0 +1,13 @@
+[event-dispatch-active-flag.html]
+  [Transactions are active during success handlers]
+    expected: FAIL
+
+  [Transactions are active during success listeners]
+    expected: FAIL
+
+  [Transactions are active during error handlers]
+    expected: FAIL
+
+  [Transactions are active during error listeners]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/IndexedDB/transaction-deactivation-timing.html.ini
@@ -0,0 +1,10 @@
+[transaction-deactivation-timing.html]
+  [New transactions are not deactivated until after the microtask checkpoint]
+    expected: FAIL
+
+  [New transactions from microtask are still active through the microtask checkpoint]
+    expected: FAIL
+
+  [Deactivation of new transactions happens at end of invocation]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/IndexedDB/upgrade-transaction-deactivation-timing.html.ini
@@ -0,0 +1,4 @@
+[upgrade-transaction-deactivation-timing.html]
+  [Upgrade transactions are active in upgradeneeded callback and microtasks]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/IndexedDB/upgrade-transaction-lifecycle-user-aborted.html.ini
@@ -0,0 +1,4 @@
+[upgrade-transaction-lifecycle-user-aborted.html]
+  [in a promise microtask after abort() is called, before the transaction abort event is fired]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/custom-elements/microtasks-and-constructors.html.ini
@@ -0,0 +1,13 @@
+[microtasks-and-constructors.html]
+  [Microtasks evaluate immediately when the stack is empty inside the parser]
+    expected: FAIL
+
+  [Microtasks evaluate afterward when the stack is not empty using createElement()]
+    expected: FAIL
+
+  [Microtasks evaluate afterward when the stack is not empty using the constructor]
+    expected: FAIL
+
+  [Microtasks evaluate afterward when the stack is not empty due to upgrades]
+    expected: FAIL
+
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/html/webappapis/scripting/event-loops/task_microtask_ordering.html.ini
@@ -0,0 +1,4 @@
+[task_microtask_ordering.html]
+  [Level 1 bossfight (synthetic click)]
+    expected: FAIL
+
deleted file mode 100644
--- a/testing/web-platform/meta/service-workers/service-worker/extendable-event-async-waituntil.https.html.ini
+++ /dev/null
@@ -1,4 +0,0 @@
-[extendable-event-async-waituntil.https.html]
-  type: testharness
-  [Test calling waitUntil in a different microtask without an existing extension throws]
-    expected: FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/basic.html.ini
@@ -0,0 +1,3 @@
+[basic.html]
+  expected:
+    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/bidi/bidi_ruby.html.ini
@@ -0,0 +1,3 @@
+[bidi_ruby.html]
+  expected:
+    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/bidi/u0041_first.html.ini
@@ -0,0 +1,3 @@
+[u0041_first.html]
+  expected:
+    if e10s and (os == "mac") and (version == "OS X 10.10.5") and (processor == "x86_64") and (bits == 64): FAIL
new file mode 100644
--- /dev/null
+++ b/testing/web-platform/meta/webvtt/rendering/cues-with-video/processing-model/bidi/u06E9_no_strong_dir.html.ini
@@ -0,0 +1,3 @@
+[u06E9_no_strong_dir.html]
+  expected:
+    if os == "mac": FAIL
--- a/xpcom/base/CycleCollectedJSContext.cpp
+++ b/xpcom/base/CycleCollectedJSContext.cpp
@@ -47,17 +47,17 @@ using namespace mozilla::dom;
 
 namespace mozilla {
 
 CycleCollectedJSContext::CycleCollectedJSContext()
   : mIsPrimaryContext(true)
   , mRuntime(nullptr)
   , mJSContext(nullptr)
   , mDoingStableStates(false)
-  , mTargetedMicroTaskRecursionDepth(0)
+  , mDisableMicroTaskCheckpoint(false)
   , mMicroTaskLevel(0)
   , mMicroTaskRecursionDepth(0)
 {
   MOZ_COUNT_CTOR(CycleCollectedJSContext);
   nsCOMPtr<nsIThread> thread = do_GetCurrentThread();
   mOwningThread = thread.forget().downcast<nsThread>().take();
   MOZ_RELEASE_ASSERT(mOwningThread);
 }
@@ -72,27 +72,27 @@ CycleCollectedJSContext::~CycleCollected
 
   mRuntime->RemoveContext(this);
 
   if (mIsPrimaryContext) {
     mRuntime->Shutdown(mJSContext);
   }
 
   // Last chance to process any events.
-  CleanupIDBTransactions(mBaseRecursionDepth);
-  MOZ_ASSERT(mPendingIDBTransactions.IsEmpty());
+  ProcessMetastableStateQueue(mBaseRecursionDepth);
+  MOZ_ASSERT(mMetastableStateEvents.IsEmpty());
 
   ProcessStableStateQueue();
   MOZ_ASSERT(mStableStateEvents.IsEmpty());
 
   // Clear mPendingException first, since it might be cycle collected.
   mPendingException = nullptr;
 
-  MOZ_ASSERT(mDebuggerMicroTaskQueue.empty());
-  MOZ_ASSERT(mPendingMicroTaskRunnables.empty());
+  MOZ_ASSERT(mDebuggerPromiseMicroTaskQueue.empty());
+  MOZ_ASSERT(mPromiseMicroTaskQueue.empty());
 
   mUncaughtRejections.reset();
   mConsumedRejections.reset();
 
   JS_DestroyContext(mJSContext);
   mJSContext = nullptr;
 
   if (mIsPrimaryContext) {
@@ -176,47 +176,42 @@ CycleCollectedJSContext::InitializeNonPr
 }
 
 size_t
 CycleCollectedJSContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
 {
   return 0;
 }
 
-class PromiseJobRunnable final : public MicroTaskRunnable
+class PromiseJobRunnable final : public Runnable
 {
 public:
   PromiseJobRunnable(JS::HandleObject aCallback,
                      JS::HandleObject aAllocationSite,
                      nsIGlobalObject* aIncumbentGlobal)
-    :mCallback(
-       new PromiseJobCallback(aCallback, aAllocationSite, aIncumbentGlobal))
+    : Runnable("PromiseJobRunnable")
+    , mCallback(
+        new PromiseJobCallback(aCallback, aAllocationSite, aIncumbentGlobal))
   {
   }
 
   virtual ~PromiseJobRunnable()
   {
   }
 
 protected:
-  virtual void Run(AutoSlowOperation& aAso) override
+  NS_IMETHOD
+  Run() override
   {
     JSObject* callback = mCallback->CallbackPreserveColor();
     nsIGlobalObject* global = callback ? xpc::NativeGlobal(callback) : nullptr;
     if (global && !global->IsDying()) {
       mCallback->Call("promise callback");
-      aAso.CheckForInterrupt();
     }
-  }
-
-  virtual bool Suppressed() override
-  {
-    nsIGlobalObject* global =
-      xpc::NativeGlobal(mCallback->CallbackPreserveColor());
-    return global && global->IsInSyncOperation();
+    return NS_OK;
   }
 
 private:
   RefPtr<PromiseJobCallback> mCallback;
 };
 
 /* static */
 JSObject*
@@ -240,17 +235,17 @@ CycleCollectedJSContext::EnqueuePromiseJ
   CycleCollectedJSContext* self = static_cast<CycleCollectedJSContext*>(aData);
   MOZ_ASSERT(aCx == self->Context());
   MOZ_ASSERT(Get() == self);
 
   nsIGlobalObject* global = nullptr;
   if (aIncumbentGlobal) {
     global = xpc::NativeGlobal(aIncumbentGlobal);
   }
-  RefPtr<MicroTaskRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
+  nsCOMPtr<nsIRunnable> runnable = new PromiseJobRunnable(aJob, aAllocationSite, global);
   self->DispatchToMicroTask(runnable.forget());
   return true;
 }
 
 /* static */
 void
 CycleCollectedJSContext::PromiseRejectionTrackerCallback(JSContext* aCx,
                                                          JS::HandleObject aPromise,
@@ -281,28 +276,28 @@ CycleCollectedJSContext::GetPendingExcep
 
 void
 CycleCollectedJSContext::SetPendingException(Exception* aException)
 {
   MOZ_ASSERT(mJSContext);
   mPendingException = aException;
 }
 
-std::queue<RefPtr<MicroTaskRunnable>>&
-CycleCollectedJSContext::GetMicroTaskQueue()
+std::queue<nsCOMPtr<nsIRunnable>>&
+CycleCollectedJSContext::GetPromiseMicroTaskQueue()
 {
   MOZ_ASSERT(mJSContext);
-  return mPendingMicroTaskRunnables;
+  return mPromiseMicroTaskQueue;
 }
 
-std::queue<RefPtr<MicroTaskRunnable>>&
-CycleCollectedJSContext::GetDebuggerMicroTaskQueue()
+std::queue<nsCOMPtr<nsIRunnable>>&
+CycleCollectedJSContext::GetDebuggerPromiseMicroTaskQueue()
 {
   MOZ_ASSERT(mJSContext);
-  return mDebuggerMicroTaskQueue;
+  return mDebuggerPromiseMicroTaskQueue;
 }
 
 void
 CycleCollectedJSContext::ProcessStableStateQueue()
 {
   MOZ_ASSERT(mJSContext);
   MOZ_RELEASE_ASSERT(!mDoingStableStates);
   mDoingStableStates = true;
@@ -312,86 +307,79 @@ CycleCollectedJSContext::ProcessStableSt
     event->Run();
   }
 
   mStableStateEvents.Clear();
   mDoingStableStates = false;
 }
 
 void
-CycleCollectedJSContext::CleanupIDBTransactions(uint32_t aRecursionDepth)
+CycleCollectedJSContext::ProcessMetastableStateQueue(uint32_t aRecursionDepth)
 {
   MOZ_ASSERT(mJSContext);
   MOZ_RELEASE_ASSERT(!mDoingStableStates);
   mDoingStableStates = true;
 
-  nsTArray<PendingIDBTransactionData> localQueue = Move(mPendingIDBTransactions);
+  nsTArray<RunInMetastableStateData> localQueue = Move(mMetastableStateEvents);
 
   for (uint32_t i = 0; i < localQueue.Length(); ++i)
   {
-    PendingIDBTransactionData& data = localQueue[i];
+    RunInMetastableStateData& data = localQueue[i];
     if (data.mRecursionDepth != aRecursionDepth) {
       continue;
     }
 
     {
-      nsCOMPtr<nsIRunnable> transaction = data.mTransaction.forget();
-      transaction->Run();
+      nsCOMPtr<nsIRunnable> runnable = data.mRunnable.forget();
+      runnable->Run();
     }
 
     localQueue.RemoveElementAt(i--);
   }
 
   // If the queue has events in it now, they were added from something we called,
   // so they belong at the end of the queue.
-  localQueue.AppendElements(mPendingIDBTransactions);
-  localQueue.SwapElements(mPendingIDBTransactions);
+  localQueue.AppendElements(mMetastableStateEvents);
+  localQueue.SwapElements(mMetastableStateEvents);
   mDoingStableStates = false;
 }
 
 void
-CycleCollectedJSContext::BeforeProcessTask(bool aMightBlock)
-{
-  // If ProcessNextEvent was called during a microtask callback, we
-  // must process any pending microtasks before blocking in the event loop,
-  // otherwise we may deadlock until an event enters the queue later.
-  if (aMightBlock && PerformMicroTaskCheckPoint()) {
-    // If any microtask was processed, we post a dummy event in order to
-    // force the ProcessNextEvent call not to block.  This is required
-    // to support nested event loops implemented using a pattern like
-    // "while (condition) thread.processNextEvent(true)", in case the
-    // condition is triggered here by a Promise "then" callback.
-    NS_DispatchToMainThread(new Runnable("BeforeProcessTask"));
-  }
-}
-
-void
 CycleCollectedJSContext::AfterProcessTask(uint32_t aRecursionDepth)
 {
   MOZ_ASSERT(mJSContext);
 
   // See HTML 6.1.4.2 Processing model
 
+  // Execute any events that were waiting for a microtask to complete.
+  // This is not (yet) in the spec.
+  ProcessMetastableStateQueue(aRecursionDepth);
+
   // Step 4.1: Execute microtasks.
-  PerformMicroTaskCheckPoint();
+  if (!mDisableMicroTaskCheckpoint) {
+    PerformMicroTaskCheckPoint();
+    if (NS_IsMainThread()) {
+      Promise::PerformMicroTaskCheckpoint();
+    } else {
+      Promise::PerformWorkerMicroTaskCheckpoint();
+    }
+  }
 
   // Step 4.2 Execute any events that were waiting for a stable state.
   ProcessStableStateQueue();
 
   // This should be a fast test so that it won't affect the next task processing.
   IsIdleGCTaskNeeded();
 }
 
 void
-CycleCollectedJSContext::AfterProcessMicrotasks()
+CycleCollectedJSContext::AfterProcessMicrotask()
 {
   MOZ_ASSERT(mJSContext);
-  // Cleanup Indexed Database transactions:
-  // https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
-  CleanupIDBTransactions(RecursionDepth());
+  AfterProcessMicrotask(RecursionDepth());
 }
 
 void CycleCollectedJSContext::IsIdleGCTaskNeeded()
 {
   class IdleTimeGCTaskRunnable : public mozilla::IdleRunnable
   {
   public:
     using mozilla::IdleRunnable::IdleRunnable;
@@ -414,65 +402,74 @@ void CycleCollectedJSContext::IsIdleGCTa
 
   if (Runtime()->IsIdleGCTaskNeeded()) {
     nsCOMPtr<nsIRunnable> gc_task = new IdleTimeGCTaskRunnable();
     NS_IdleDispatchToCurrentThread(gc_task.forget());
     Runtime()->SetPendingIdleGCTask();
   }
 }
 
+void
+CycleCollectedJSContext::AfterProcessMicrotask(uint32_t aRecursionDepth)
+{
+  MOZ_ASSERT(mJSContext);
+
+  // Between microtasks, execute any events that were waiting for a microtask
+  // to complete.
+  ProcessMetastableStateQueue(aRecursionDepth);
+}
+
 uint32_t
 CycleCollectedJSContext::RecursionDepth()
 {
   return mOwningThread->RecursionDepth();
 }
 
 void
 CycleCollectedJSContext::RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable)
 {
   MOZ_ASSERT(mJSContext);
   mStableStateEvents.AppendElement(Move(aRunnable));
 }
 
 void
-CycleCollectedJSContext::AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction)
+CycleCollectedJSContext::RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable)
 {
   MOZ_ASSERT(mJSContext);
 
-  PendingIDBTransactionData data;
-  data.mTransaction = aTransaction;
+  RunInMetastableStateData data;
+  data.mRunnable = aRunnable;
 
   MOZ_ASSERT(mOwningThread);
   data.mRecursionDepth = RecursionDepth();
 
   // There must be an event running to get here.
 #ifndef MOZ_WIDGET_COCOA
   MOZ_ASSERT(data.mRecursionDepth > mBaseRecursionDepth);
 #else
   // XXX bug 1261143
   // Recursion depth should be greater than mBaseRecursionDepth,
   // or the runnable will stay in the queue forever.
   if (data.mRecursionDepth <= mBaseRecursionDepth) {
     data.mRecursionDepth = mBaseRecursionDepth + 1;
   }
 #endif
 
-  mPendingIDBTransactions.AppendElement(Move(data));
+  mMetastableStateEvents.AppendElement(Move(data));
 }
 
 void
-CycleCollectedJSContext::DispatchToMicroTask(
-    already_AddRefed<MicroTaskRunnable> aRunnable)
+CycleCollectedJSContext::DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable)
 {
-  RefPtr<MicroTaskRunnable> runnable(aRunnable);
+  RefPtr<nsIRunnable> runnable(aRunnable);
 
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(runnable);
 
-  mPendingMicroTaskRunnables.push(runnable.forget());
+  mPromiseMicroTaskQueue.push(runnable.forget());
 }
 
 class AsyncMutationHandler final : public mozilla::Runnable
 {
 public:
   AsyncMutationHandler() : mozilla::Runnable("AsyncMutationHandler") {}
 
   NS_IMETHOD Run() override
@@ -480,106 +477,62 @@ public:
     CycleCollectedJSContext* ccjs = CycleCollectedJSContext::Get();
     if (ccjs) {
       ccjs->PerformMicroTaskCheckPoint();
     }
     return NS_OK;
   }
 };
 
-bool
+void
 CycleCollectedJSContext::PerformMicroTaskCheckPoint()
 {
-  if (mPendingMicroTaskRunnables.empty() && mDebuggerMicroTaskQueue.empty()) {
-    AfterProcessMicrotasks();
+  if (mPendingMicroTaskRunnables.empty()) {
     // Nothing to do, return early.
-    return false;
+    return;
   }
 
   uint32_t currentDepth = RecursionDepth();
   if (mMicroTaskRecursionDepth >= currentDepth) {
     // We are already executing microtasks for the current recursion depth.
-    return false;
-  }
-
-  if (mTargetedMicroTaskRecursionDepth != 0 &&
-      mTargetedMicroTaskRecursionDepth != currentDepth) {
-    return false;
+    return;
   }
 
   if (NS_IsMainThread() && !nsContentUtils::IsSafeToRunScript()) {
     // Special case for main thread where DOM mutations may happen when
     // it is not safe to run scripts.
     nsContentUtils::AddScriptRunner(new AsyncMutationHandler());
-    return false;
+    return;
   }
 
   mozilla::AutoRestore<uint32_t> restore(mMicroTaskRecursionDepth);
   MOZ_ASSERT(currentDepth > 0);
   mMicroTaskRecursionDepth = currentDepth;
 
-  bool didProcess = false;
   AutoSlowOperation aso;
 
   std::queue<RefPtr<MicroTaskRunnable>> suppressed;
-  for (;;) {
-    RefPtr<MicroTaskRunnable> runnable;
-    if (!mDebuggerMicroTaskQueue.empty()) {
-      runnable = mDebuggerMicroTaskQueue.front().forget();
-      mDebuggerMicroTaskQueue.pop();
-    } else if (!mPendingMicroTaskRunnables.empty()) {
-      runnable = mPendingMicroTaskRunnables.front().forget();
-      mPendingMicroTaskRunnables.pop();
-    } else {
-      break;
-    }
-
+  while (!mPendingMicroTaskRunnables.empty()) {
+    RefPtr<MicroTaskRunnable> runnable =
+      mPendingMicroTaskRunnables.front().forget();
+    mPendingMicroTaskRunnables.pop();
     if (runnable->Suppressed()) {
-      // Microtasks in worker shall never be suppressed.
-      // Otherwise, mPendingMicroTaskRunnables will be replaced later with
-      // all suppressed tasks in mDebuggerMicroTaskQueue unexpectedly.
-      MOZ_ASSERT(NS_IsMainThread());
       suppressed.push(runnable);
     } else {
-      didProcess = true;
       runnable->Run(aso);
     }
   }
 
   // Put back the suppressed microtasks so that they will be run later.
   // Note, it is possible that we end up keeping these suppressed tasks around
   // for some time, but no longer than spinning the event loop nestedly
   // (sync XHR, alert, etc.)
   mPendingMicroTaskRunnables.swap(suppressed);
-
-  AfterProcessMicrotasks();
-
-  return didProcess;
 }
 
 void
-CycleCollectedJSContext::PerformDebuggerMicroTaskCheckpoint()
- {
-  // Don't do normal microtask handling checks here, since whoever is calling
-  // this method is supposed to know what they are doing.
-
-  AutoSlowOperation aso;
-  for (;;) {
-    // For a debugger microtask checkpoint, we always use the debugger microtask
-    // queue.
-    std::queue<RefPtr<MicroTaskRunnable>>* microtaskQueue =
-      &GetDebuggerMicroTaskQueue();
+CycleCollectedJSContext::DispatchMicroTaskRunnable(
+  already_AddRefed<MicroTaskRunnable> aRunnable)
+{
+  mPendingMicroTaskRunnables.push(aRunnable);
+}
 
-    if (microtaskQueue->empty()) {
-      break;
-    }
-
-    RefPtr<MicroTaskRunnable> runnable = microtaskQueue->front().forget();
-    MOZ_ASSERT(runnable);
-
-    // This function can re-enter, so we remove the element before calling.
-    microtaskQueue->pop();
-    runnable->Run(aso);
-  }
-
-  AfterProcessMicrotasks();
-}
 } // namespace mozilla
--- a/xpcom/base/CycleCollectedJSContext.h
+++ b/xpcom/base/CycleCollectedJSContext.h
@@ -98,96 +98,127 @@ protected:
   // See explanation in mIsPrimaryContext.
   MOZ_IS_CLASS_INIT
   nsresult InitializeNonPrimary(CycleCollectedJSContext* aPrimaryContext);
 
   virtual CycleCollectedJSRuntime* CreateRuntime(JSContext* aCx) = 0;
 
   size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
 
+  std::queue<nsCOMPtr<nsIRunnable>> mPromiseMicroTaskQueue;
+  std::queue<nsCOMPtr<nsIRunnable>> mDebuggerPromiseMicroTaskQueue;
+
 private:
   MOZ_IS_CLASS_INIT
   void InitializeCommon();
 
   static JSObject* GetIncumbentGlobalCallback(JSContext* aCx);
   static bool EnqueuePromiseJobCallback(JSContext* aCx,
                                         JS::HandleObject aJob,
                                         JS::HandleObject aAllocationSite,
                                         JS::HandleObject aIncumbentGlobal,
                                         void* aData);
   static void PromiseRejectionTrackerCallback(JSContext* aCx,
                                               JS::HandleObject aPromise,
                                               JS::PromiseRejectionHandlingState state,
                                               void* aData);
 
-  void AfterProcessMicrotasks();
+  void AfterProcessMicrotask(uint32_t aRecursionDepth);
 public:
   void ProcessStableStateQueue();
 private:
-  void CleanupIDBTransactions(uint32_t aRecursionDepth);
+  void ProcessMetastableStateQueue(uint32_t aRecursionDepth);
 
 public:
   enum DeferredFinalizeType {
     FinalizeIncrementally,
     FinalizeNow,
   };
 
   CycleCollectedJSRuntime* Runtime() const
   {
     MOZ_ASSERT(mRuntime);
     return mRuntime;
   }
 
   already_AddRefed<dom::Exception> GetPendingException() const;
   void SetPendingException(dom::Exception* aException);
 
-  std::queue<RefPtr<MicroTaskRunnable>>& GetMicroTaskQueue();
-  std::queue<RefPtr<MicroTaskRunnable>>& GetDebuggerMicroTaskQueue();
+  std::queue<nsCOMPtr<nsIRunnable>>& GetPromiseMicroTaskQueue();
+  std::queue<nsCOMPtr<nsIRunnable>>& GetDebuggerPromiseMicroTaskQueue();
 
   JSContext* Context() const
   {
     MOZ_ASSERT(mJSContext);
     return mJSContext;
   }
 
   JS::RootingContext* RootingCx() const
   {
     MOZ_ASSERT(mJSContext);
     return JS::RootingContext::get(mJSContext);
   }
 
-  void SetTargetedMicroTaskRecursionDepth(uint32_t aDepth)
+  bool MicroTaskCheckpointDisabled() const
+  {
+    return mDisableMicroTaskCheckpoint;
+  }
+
+  void DisableMicroTaskCheckpoint(bool aDisable)
+  {
+    mDisableMicroTaskCheckpoint = aDisable;
+  }
+
+  class MOZ_RAII AutoDisableMicroTaskCheckpoint
   {
-    mTargetedMicroTaskRecursionDepth = aDepth;
-  }
+    public:
+    AutoDisableMicroTaskCheckpoint()
+    : mCCJSCX(CycleCollectedJSContext::Get())
+    {
+      mOldValue = mCCJSCX->MicroTaskCheckpointDisabled();
+      mCCJSCX->DisableMicroTaskCheckpoint(true);
+    }
+
+    ~AutoDisableMicroTaskCheckpoint()
+    {
+      mCCJSCX->DisableMicroTaskCheckpoint(mOldValue);
+    }
+
+    CycleCollectedJSContext* mCCJSCX;
+    bool mOldValue;
+  };
 
 protected:
   JSContext* MaybeContext() const { return mJSContext; }
 
 public:
   // nsThread entrypoints
-  virtual void BeforeProcessTask(bool aMightBlock);
+  virtual void BeforeProcessTask(bool aMightBlock) { };
   virtual void AfterProcessTask(uint32_t aRecursionDepth);
 
+  // microtask processor entry point
+  void AfterProcessMicrotask();
+
   // Check whether we need an idle GC task.
   void IsIdleGCTaskNeeded();
 
   uint32_t RecursionDepth();
 
   // Run in stable state (call through nsContentUtils)
   void RunInStableState(already_AddRefed<nsIRunnable>&& aRunnable);
-
-  void AddPendingIDBTransaction(already_AddRefed<nsIRunnable>&& aTransaction);
+  // This isn't in the spec at all yet, but this gets the behavior we want for IDB.
+  // Runs after the current microtask completes.
+  void RunInMetastableState(already_AddRefed<nsIRunnable>&& aRunnable);
 
   // Get the current thread's CycleCollectedJSContext.  Returns null if there
   // isn't one.
   static CycleCollectedJSContext* Get();
 
   // Queue an async microtask to the current main or worker thread.
-  virtual void DispatchToMicroTask(already_AddRefed<MicroTaskRunnable> aRunnable);
+  virtual void DispatchToMicroTask(already_AddRefed<nsIRunnable> aRunnable);
 
   // Call EnterMicroTask when you're entering JS execution.
   // Usually the best way to do this is to use nsAutoMicroTask.
   void EnterMicroTask()
   {
     ++mMicroTaskLevel;
   }
 
@@ -208,19 +239,19 @@ public:
     return mMicroTaskLevel;
   }
 
   void SetMicroTaskLevel(uint32_t aLevel)
   {
     mMicroTaskLevel = aLevel;
   }
 
-  bool PerformMicroTaskCheckPoint();
+  void PerformMicroTaskCheckPoint();
 
-  void PerformDebuggerMicroTaskCheckpoint();
+  void DispatchMicroTaskRunnable(already_AddRefed<MicroTaskRunnable> aRunnable);
 
   bool IsInStableOrMetaStableState()
   {
     return mDoingStableStates;
   }
 
   // Storage for watching rejected promises waiting for some client to
   // consume their rejection.
@@ -245,35 +276,31 @@ private:
 
   CycleCollectedJSRuntime* mRuntime;
 
   JSContext* mJSContext;
 
   nsCOMPtr<dom::Exception> mPendingException;
   nsThread* mOwningThread; // Manual refcounting to avoid include hell.
 
-  struct PendingIDBTransactionData
+  struct RunInMetastableStateData
   {
-    nsCOMPtr<nsIRunnable> mTransaction;
+    nsCOMPtr<nsIRunnable> mRunnable;
     uint32_t mRecursionDepth;
   };
 
   nsTArray<nsCOMPtr<nsIRunnable>> mStableStateEvents;
-  nsTArray<PendingIDBTransactionData> mPendingIDBTransactions;
+  nsTArray<RunInMetastableStateData> mMetastableStateEvents;
   uint32_t mBaseRecursionDepth;
   bool mDoingStableStates;
 
-  // If set to none 0, microtasks will be processed only when recursion depth
-  // is the set value.
-  uint32_t mTargetedMicroTaskRecursionDepth;
+  bool mDisableMicroTaskCheckpoint;
 
   uint32_t mMicroTaskLevel;
-
   std::queue<RefPtr<MicroTaskRunnable>> mPendingMicroTaskRunnables;
-  std::queue<RefPtr<MicroTaskRunnable>> mDebuggerMicroTaskQueue;
 
   uint32_t mMicroTaskRecursionDepth;
 };
 
 class MOZ_STACK_CLASS nsAutoMicroTask
 {
 public:
   nsAutoMicroTask()