Bug 1529345 - Part 2: Add Gecko infrastructure for receiving notifications about debugger-related events. r=jimb,smaug
authorLogan Smyth <loganfsmyth@gmail.com>
Mon, 17 Jun 2019 04:36:29 +0000
changeset 479074 e074fcc399d1090a72a35383a8158b4ab8b4e155
parent 479073 2cd3642e09439146f04f8fa0df888a6c67151460
child 479075 05f0605563106e546d131074bcbd397d1fd9b494
push id36163
push usermalexandru@mozilla.com
push dateMon, 17 Jun 2019 09:57:50 +0000
treeherdermozilla-central@82daf65cbe5e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjimb, smaug
bugs1529345
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1529345 - Part 2: Add Gecko infrastructure for receiving notifications about debugger-related events. r=jimb,smaug Differential Revision: https://phabricator.services.mozilla.com/D30565
config/windows-h-constant.decls.h
dom/base/nsGlobalWindowInner.cpp
dom/base/nsGlobalWindowInner.h
dom/base/nsIGlobalObject.h
dom/base/nsINode.h
dom/bindings/Bindings.conf
dom/chrome-webidl/DebuggerNotification.webidl
dom/chrome-webidl/DebuggerNotificationObserver.webidl
dom/chrome-webidl/moz.build
dom/debugger/CallbackDebuggerNotification.cpp
dom/debugger/CallbackDebuggerNotification.h
dom/debugger/DebuggerNotification.cpp
dom/debugger/DebuggerNotification.h
dom/debugger/DebuggerNotificationManager.cpp
dom/debugger/DebuggerNotificationManager.h
dom/debugger/DebuggerNotificationObserver.cpp
dom/debugger/DebuggerNotificationObserver.h
dom/debugger/EventCallbackDebuggerNotification.cpp
dom/debugger/EventCallbackDebuggerNotification.h
dom/debugger/moz.build
dom/events/EventListenerManager.cpp
dom/events/EventTarget.h
dom/html/HTMLTrackElement.cpp
dom/moz.build
dom/workers/RegisterBindings.cpp
dom/workers/Worker.h
dom/workers/WorkerPrivate.cpp
dom/workers/WorkerScope.cpp
dom/workers/WorkerScope.h
dom/xhr/XMLHttpRequestEventTarget.h
layout/base/nsRefreshDriver.cpp
--- a/config/windows-h-constant.decls.h
+++ b/config/windows-h-constant.decls.h
@@ -39,16 +39,18 @@ auto MAXCHAR;
 auto MINSHORT;
 auto MAXSHORT;
 auto MINLONG;
 auto MAXLONG;
 auto MAXBYTE;
 auto MAXWORD;
 auto MAXDWORD;
 
+auto ERROR;
+
 auto DELETE;
 auto READ_CONTROL;
 auto WRITE_DAC;
 auto WRITE_OWNER;
 auto SYNCHRONIZE;
 
 auto MAXIMUM_ALLOWED;
 auto GENERIC_READ;
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -12,18 +12,20 @@
 
 // Local Includes
 #include "Navigator.h"
 #include "nsContentSecurityManager.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
+#include "mozilla/dom/CallbackDebuggerNotification.h"
 #include "mozilla/dom/ContentFrameMessageManager.h"
 #include "mozilla/dom/CSPEvalChecker.h"
+#include "mozilla/dom/DebuggerNotification.h"
 #include "mozilla/dom/DocumentInlines.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/LocalStorageCommon.h"
 #include "mozilla/dom/LSObject.h"
 #include "mozilla/dom/PartitionedLocalStorage.h"
 #include "mozilla/dom/Storage.h"
@@ -1365,16 +1367,18 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mClientSource)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mVRDisplays)
 
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
+
   // Traverse stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFocusedElement)
 
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocationbar)
@@ -1463,16 +1467,18 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mBrowserChild)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDoc)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mGamepads)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mVRDisplays)
 
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
+
   // Unlink stuff from nsPIDOMWindow
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mChromeEventHandler)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParentTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mFocusedElement)
 
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mMenubar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mToolbar)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocationbar)
@@ -3284,27 +3290,33 @@ int32_t nsGlobalWindowInner::RequestAnim
   if (!mDoc) {
     return 0;
   }
 
   if (GetWrapperPreserveColor()) {
     js::NotifyAnimationActivity(GetWrapperPreserveColor());
   }
 
+  DebuggerNotificationDispatch(this,
+                               DebuggerNotificationType::RequestAnimationFrame);
+
   int32_t handle;
   aError = mDoc->ScheduleFrameRequestCallback(aCallback, &handle);
   return handle;
 }
 
 void nsGlobalWindowInner::CancelAnimationFrame(int32_t aHandle,
                                                ErrorResult& aError) {
   if (!mDoc) {
     return;
   }
 
+  DebuggerNotificationDispatch(this,
+                               DebuggerNotificationType::CancelAnimationFrame);
+
   mDoc->CancelFrameRequestCallback(aHandle);
 }
 
 already_AddRefed<MediaQueryList> nsGlobalWindowInner::MatchMedia(
     const nsAString& aMediaQueryList, CallerType aCallerType,
     ErrorResult& aError) {
   // FIXME: This whole forward-to-outer and then get a pres
   // shell/context off the docshell dance is sort of silly; it'd make
@@ -3725,22 +3737,26 @@ void nsGlobalWindowInner::MozScrollSnap(
   FlushPendingNotifications(FlushType::Layout);
   nsIScrollableFrame* sf = GetScrollFrame();
   if (sf) {
     sf->ScrollSnap();
   }
 }
 
 void nsGlobalWindowInner::ClearTimeout(int32_t aHandle) {
+  DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
+
   if (aHandle > 0) {
     mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
   }
 }
 
 void nsGlobalWindowInner::ClearInterval(int32_t aHandle) {
+  DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
+
   if (aHandle > 0) {
     mTimeoutManager->ClearTimeout(aHandle, Timeout::Reason::eTimeoutOrInterval);
   }
 }
 
 void nsGlobalWindowInner::SetResizable(bool aResizable) const {
   // nop
 }
@@ -3998,16 +4014,30 @@ EventListenerManager* nsGlobalWindowInne
 
   return mListenerManager;
 }
 
 EventListenerManager* nsGlobalWindowInner::GetExistingListenerManager() const {
   return mListenerManager;
 }
 
+mozilla::dom::DebuggerNotificationManager*
+nsGlobalWindowInner::GetOrCreateDebuggerNotificationManager() {
+  if (!mDebuggerNotificationManager) {
+    mDebuggerNotificationManager = new DebuggerNotificationManager(this);
+  }
+
+  return mDebuggerNotificationManager;
+}
+
+mozilla::dom::DebuggerNotificationManager*
+nsGlobalWindowInner::GetExistingDebuggerNotificationManager() {
+  return mDebuggerNotificationManager;
+}
+
 //*****************************************************************************
 // nsGlobalWindowInner::nsPIDOMWindow
 //*****************************************************************************
 
 Location* nsGlobalWindowInner::Location() {
   if (!mLocation) {
     mLocation = new dom::Location(this, GetDocShell());
   }
@@ -5753,19 +5783,24 @@ int32_t nsGlobalWindowInner::SetTimeoutO
     const Sequence<JS::Value>& aArguments, bool aIsInterval,
     ErrorResult& aError) {
   nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
   if (!inner) {
     return -1;
   }
 
   if (inner != this) {
-    return inner->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
-                                       aIsInterval, aError);
-  }
+    RefPtr<nsGlobalWindowInner> innerRef(inner);
+    return innerRef->SetTimeoutOrInterval(aCx, aFunction, aTimeout, aArguments,
+                                          aIsInterval, aError);
+  }
+
+  DebuggerNotificationDispatch(
+      this, aIsInterval ? DebuggerNotificationType::SetInterval
+                        : DebuggerNotificationType::SetTimeout);
 
   if (!GetContextInternal() || !HasJSGlobal()) {
     // This window was already closed, or never properly initialized,
     // don't let a timer be scheduled on such a window.
     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     return 0;
   }
 
@@ -5791,19 +5826,24 @@ int32_t nsGlobalWindowInner::SetTimeoutO
                                                   bool aIsInterval,
                                                   ErrorResult& aError) {
   nsGlobalWindowInner* inner = InnerForSetTimeoutOrInterval(aError);
   if (!inner) {
     return -1;
   }
 
   if (inner != this) {
-    return inner->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
-                                       aError);
-  }
+    RefPtr<nsGlobalWindowInner> innerRef(inner);
+    return innerRef->SetTimeoutOrInterval(aCx, aHandler, aTimeout, aIsInterval,
+                                          aError);
+  }
+
+  DebuggerNotificationDispatch(
+      this, aIsInterval ? DebuggerNotificationType::SetInterval
+                        : DebuggerNotificationType::SetTimeout);
 
   if (!GetContextInternal() || !HasJSGlobal()) {
     // This window was already closed, or never properly initialized,
     // don't let a timer be scheduled on such a window.
     aError.Throw(NS_ERROR_NOT_INITIALIZED);
     return 0;
   }
 
@@ -5852,18 +5892,27 @@ bool nsGlobalWindowInner::RunTimeoutHand
 
   const char* reason;
   if (timeout->mIsInterval) {
     reason = "setInterval handler";
   } else {
     reason = "setTimeout handler";
   }
 
-  RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
-  bool abortIntervalHandler = !handler->Call(reason);
+  bool abortIntervalHandler;
+  {
+    RefPtr<TimeoutHandler> handler(timeout->mScriptHandler);
+
+    CallbackDebuggerNotificationGuard guard(
+        this, timeout->mIsInterval
+                  ? DebuggerNotificationType::SetIntervalCallback
+                  : DebuggerNotificationType::SetTimeoutCallback);
+    abortIntervalHandler = !handler->Call(reason);
+  }
+
   // If we received an uncatchable exception, do not schedule the timeout again.
   // This allows the slow script dialog to break easy DoS attacks like
   // setInterval(function() { while(1); }, 100);
   if (abortIntervalHandler) {
     // If it wasn't an interval timer to begin with, this does nothing.  If it
     // was, we'll treat it as a timeout that we just ran and discard it when
     // we return.
     timeout->mIsInterval = false;
--- a/dom/base/nsGlobalWindowInner.h
+++ b/dom/base/nsGlobalWindowInner.h
@@ -31,16 +31,17 @@
 #include "nsITimer.h"
 #include "mozilla/EventListenerManager.h"
 #include "nsIPrincipal.h"
 #include "nsSize.h"
 #include "mozilla/FlushType.h"
 #include "prclist.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/ChromeMessageBroadcaster.h"
+#include "mozilla/dom/DebuggerNotificationManager.h"
 #include "mozilla/dom/NavigatorBinding.h"
 #include "mozilla/dom/StorageEvent.h"
 #include "mozilla/dom/StorageEventBinding.h"
 #include "mozilla/dom/UnionTypes.h"
 #include "mozilla/ErrorResult.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GuardObjects.h"
 #include "mozilla/LinkedList.h"
@@ -279,16 +280,22 @@ class nsGlobalWindowInner final : public
   void Dump(const nsAString& aStr);
   void SetResizable(bool aResizable) const;
 
   virtual mozilla::EventListenerManager* GetExistingListenerManager()
       const override;
 
   virtual mozilla::EventListenerManager* GetOrCreateListenerManager() override;
 
+  mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
+  GetDebuggerNotificationType() const override {
+    return mozilla::Some(
+        mozilla::dom::EventCallbackDebuggerNotificationType::Global);
+  }
+
   bool ComputeDefaultWantsUntrusted(mozilla::ErrorResult& aRv) final;
 
   virtual nsPIDOMWindowOuter* GetOwnerGlobalForBindingsInternal() override;
 
   virtual nsIGlobalObject* GetOwnerGlobal() const override;
 
   EventTarget* GetTargetForDOMEvent() override;
 
@@ -312,16 +319,22 @@ class nsGlobalWindowInner final : public
   // bringing them to a complete stop.  A window can have Freeze() called
   // multiple times and will only thaw after a matching number of Thaw()
   // calls.
   void Freeze();
   void Thaw();
   virtual bool IsFrozen() const override;
   void SyncStateFromParentWindow();
 
+  mozilla::dom::DebuggerNotificationManager*
+  GetOrCreateDebuggerNotificationManager() override;
+
+  mozilla::dom::DebuggerNotificationManager*
+  GetExistingDebuggerNotificationManager() override;
+
   mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const override;
   mozilla::Maybe<mozilla::dom::ClientState> GetClientState() const;
   mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController()
       const override;
 
   void SetCsp(nsIContentSecurityPolicy* aCsp);
   void SetPreloadCsp(nsIContentSecurityPolicy* aPreloadCsp);
   nsIContentSecurityPolicy* GetCsp();
@@ -676,33 +689,45 @@ class nsGlobalWindowInner final : public
                       const nsAString& aTargetOrigin,
                       const mozilla::dom::Sequence<JSObject*>& aTransfer,
                       nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
   void PostMessageMoz(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                       const mozilla::dom::WindowPostMessageOptions& aOptions,
                       nsIPrincipal& aSubjectPrincipal,
                       mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeout(JSContext* aCx, mozilla::dom::Function& aFunction,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& aArguments,
                      mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      int32_t aTimeout,
                      const mozilla::dom::Sequence<JS::Value>& /* unused */,
                      mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   void ClearTimeout(int32_t aHandle);
+
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetInterval(JSContext* aCx, mozilla::dom::Function& aFunction,
                       const int32_t aTimeout,
                       const mozilla::dom::Sequence<JS::Value>& aArguments,
                       mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetInterval(JSContext* aCx, const nsAString& aHandler,
                       const int32_t aTimeout,
                       const mozilla::dom::Sequence<JS::Value>& /* unused */,
                       mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   void ClearInterval(int32_t aHandle);
   void GetOrigin(nsAString& aOrigin);
   void Atob(const nsAString& aAsciiBase64String, nsAString& aBinaryData,
             mozilla::ErrorResult& aError);
   void Btoa(const nsAString& aBinaryData, nsAString& aAsciiBase64String,
             mozilla::ErrorResult& aError);
   mozilla::dom::Storage* GetSessionStorage(mozilla::ErrorResult& aError);
   mozilla::dom::Storage* GetLocalStorage(mozilla::ErrorResult& aError);
@@ -789,18 +814,22 @@ class nsGlobalWindowInner final : public
                      mozilla::dom::CallerType aCallerType,
                      mozilla::ErrorResult& aError);
   void GetOuterHeight(JSContext* aCx, JS::MutableHandle<JS::Value> aValue,
                       mozilla::dom::CallerType aCallerType,
                       mozilla::ErrorResult& aError);
   void SetOuterHeight(JSContext* aCx, JS::Handle<JS::Value> aValue,
                       mozilla::dom::CallerType aCallerType,
                       mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   int32_t RequestAnimationFrame(mozilla::dom::FrameRequestCallback& aCallback,
                                 mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError);
 
   uint32_t RequestIdleCallback(JSContext* aCx,
                                mozilla::dom::IdleRequestCallback& aCallback,
                                const mozilla::dom::IdleRequestOptions& aOptions,
                                mozilla::ErrorResult& aError);
   void CancelIdleCallback(uint32_t aHandle);
 
@@ -1054,20 +1083,23 @@ class nsGlobalWindowInner final : public
   void ThawInternal();
 
   CallState ShouldReportForServiceWorkerScopeInternal(const nsACString& aScope,
                                                       bool* aResultOut);
 
  public:
   // Timeout Functions
   // |interval| is in milliseconds.
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeoutOrInterval(
       JSContext* aCx, mozilla::dom::Function& aFunction, int32_t aTimeout,
       const mozilla::dom::Sequence<JS::Value>& aArguments, bool aIsInterval,
       mozilla::ErrorResult& aError);
+
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                int32_t aTimeout, bool aIsInterval,
                                mozilla::ErrorResult& aError);
 
   // Return true if |aTimeout| was cleared while its handler ran.
   MOZ_CAN_RUN_SCRIPT
   bool RunTimeoutHandler(mozilla::dom::Timeout* aTimeout,
                          nsIScriptContext* aScx);
@@ -1312,16 +1344,19 @@ class nsGlobalWindowInner final : public
   RefPtr<mozilla::dom::VisualViewport> mVisualViewport;
 
   // The document's principals and CSP are only stored if
   // FreeInnerObjects has been called.
   nsCOMPtr<nsIPrincipal> mDocumentPrincipal;
   nsCOMPtr<nsIPrincipal> mDocumentStoragePrincipal;
   nsCOMPtr<nsIContentSecurityPolicy> mDocumentCsp;
 
+  RefPtr<mozilla::dom::DebuggerNotificationManager>
+      mDebuggerNotificationManager;
+
   // mBrowserChild is only ever populated in the content process.
   nsCOMPtr<nsIBrowserChild> mBrowserChild;
 
   uint32_t mSuspendDepth;
   uint32_t mFreezeDepth;
 
 #ifdef DEBUG
   uint32_t mSerial;
--- a/dom/base/nsIGlobalObject.h
+++ b/dom/base/nsIGlobalObject.h
@@ -30,16 +30,17 @@
 class nsCycleCollectionTraversalCallback;
 class nsIPrincipal;
 class nsPIDOMWindowInner;
 
 namespace mozilla {
 class DOMEventTargetHelper;
 namespace dom {
 class VoidFunction;
+class DebuggerNotificationManager;
 class ServiceWorker;
 class ServiceWorkerRegistration;
 class ServiceWorkerRegistrationDescriptor;
 }  // namespace dom
 }  // namespace mozilla
 
 class nsIGlobalObject : public nsISupports,
                         public mozilla::dom::DispatcherTrait {
@@ -126,16 +127,26 @@ class nsIGlobalObject : public nsISuppor
   // Iterate the registered DETH objects and call the given function
   // for each one.
   void ForEachEventTargetObject(
       const std::function<void(mozilla::DOMEventTargetHelper*, bool* aDoneOut)>&
           aFunc) const;
 
   virtual bool IsInSyncOperation() { return false; }
 
+  virtual mozilla::dom::DebuggerNotificationManager*
+  GetOrCreateDebuggerNotificationManager() {
+    return nullptr;
+  }
+
+  virtual mozilla::dom::DebuggerNotificationManager*
+  GetExistingDebuggerNotificationManager() {
+    return nullptr;
+  }
+
   virtual mozilla::Maybe<mozilla::dom::ClientInfo> GetClientInfo() const;
 
   virtual mozilla::Maybe<mozilla::dom::ServiceWorkerDescriptor> GetController()
       const;
 
   // Get the DOM object for the given descriptor or attempt to create one.
   // Creation can still fail and return nullptr during shutdown, etc.
   virtual RefPtr<mozilla::dom::ServiceWorker> GetOrCreateServiceWorker(
--- a/dom/base/nsINode.h
+++ b/dom/base/nsINode.h
@@ -935,16 +935,22 @@ class nsINode : public mozilla::dom::Eve
    * and context object's root otherwise.
    */
   nsINode* GetRootNode(const mozilla::dom::GetRootNodeOptions& aOptions);
 
   virtual mozilla::EventListenerManager* GetExistingListenerManager()
       const override;
   virtual mozilla::EventListenerManager* GetOrCreateListenerManager() override;
 
+  mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
+  GetDebuggerNotificationType() const override {
+    return mozilla::Some(
+        mozilla::dom::EventCallbackDebuggerNotificationType::Node);
+  }
+
   bool ComputeDefaultWantsUntrusted(mozilla::ErrorResult& aRv) final;
 
   virtual bool IsApzAware() const override;
 
   virtual nsPIDOMWindowOuter* GetOwnerGlobalForBindingsInternal() override;
   virtual nsIGlobalObject* GetOwnerGlobal() const override;
 
   using mozilla::dom::EventTarget::DispatchEvent;
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -196,16 +196,23 @@ DOMInterfaces = {
     'nativeType': 'mozilla::StyleSheet',
     'binaryNames': { 'ownerRule': 'DOMOwnerRule' },
 },
 
 'CustomElementRegistry': {
     'implicitJSContext': ['define'],
 },
 
+'DebuggerNotification': {
+    'concrete': True,
+},
+'CallbackDebuggerNotification': {
+    'concrete': True,
+},
+
 'DedicatedWorkerGlobalScope': {
     'headerFile': 'mozilla/dom/WorkerScope.h',
 },
 
 'DeviceAcceleration': {
     'headerFile': 'mozilla/dom/DeviceMotionEvent.h',
 },
 
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/DebuggerNotification.webidl
@@ -0,0 +1,57 @@
+/* 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/. */
+
+enum DebuggerNotificationType {
+  // DebuggerNotification
+  "setTimeout",
+  "clearTimeout",
+  "setInterval",
+  "clearInterval",
+  "requestAnimationFrame",
+  "cancelAnimationFrame",
+
+  // CallbackDebuggerNotification
+  "setTimeoutCallback",
+  "setIntervalCallback",
+  "requestAnimationFrameCallback",
+
+  // EventCallbackDebuggerNotification
+  "domEvent",
+};
+
+[ChromeOnly]
+interface DebuggerNotification {
+  readonly attribute DebuggerNotificationType type;
+
+  // The global object that has triggered the notification.
+  readonly attribute object global;
+};
+
+// For DOM events, we send notifications just before, and just after the
+// event handler has been dispatched so that listeners
+enum CallbackDebuggerNotificationPhase {
+  "pre",
+  "post",
+};
+
+// A base notification type for notifications that are dispatched as pairs with
+// a before and after notification.
+[ChromeOnly]
+interface CallbackDebuggerNotification : DebuggerNotification {
+  readonly attribute CallbackDebuggerNotificationPhase phase;
+};
+
+enum EventCallbackDebuggerNotificationType {
+  "global",
+  "node",
+  "xhr",
+  "worker",
+};
+
+// A notification that about the engine calling a DOM event handler.
+[ChromeOnly]
+interface EventCallbackDebuggerNotification : CallbackDebuggerNotification {
+  readonly attribute Event event;
+  readonly attribute EventCallbackDebuggerNotificationType targetType;
+};
new file mode 100644
--- /dev/null
+++ b/dom/chrome-webidl/DebuggerNotificationObserver.webidl
@@ -0,0 +1,28 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+callback DebuggerNotificationCallback = void (DebuggerNotification n);
+
+[ChromeOnly, Constructor, Exposed=(Window, Worker)]
+interface DebuggerNotificationObserver {
+  // Throws if the object is not a browser global or does not support
+  // debugger notifications.
+  // Returns false if already connected to this global.
+  [Throws]
+  boolean connect(object global);
+
+  // Throws if the object is not a browser global or does not support
+  // debugger notifications.
+  // Returns false if not connected to this global.
+  [Throws]
+  boolean disconnect(object global);
+
+  // Returns false if listener already added.
+  boolean addListener(DebuggerNotificationCallback handler);
+
+  // Returns false if listener was not found.
+  boolean removeListener(DebuggerNotificationCallback handler);
+};
--- a/dom/chrome-webidl/moz.build
+++ b/dom/chrome-webidl/moz.build
@@ -30,16 +30,18 @@ with Files("WebExtension*.webidl"):
 
 PREPROCESSED_WEBIDL_FILES = [
     'ChromeUtils.webidl',
 ]
 
 WEBIDL_FILES = [
     'BrowsingContext.webidl',
     'ChannelWrapper.webidl',
+    'DebuggerNotification.webidl',
+    'DebuggerNotificationObserver.webidl',
     'DocumentL10n.webidl',
     'DominatorTree.webidl',
     'DOMLocalization.webidl',
     'Flex.webidl',
     'HeapSnapshot.webidl',
     'InspectorUtils.webidl',
     'IteratorResult.webidl',
     'JSWindowActor.webidl',
new file mode 100644
--- /dev/null
+++ b/dom/debugger/CallbackDebuggerNotification.cpp
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "CallbackDebuggerNotification.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(CallbackDebuggerNotification,
+                                   DebuggerNotification)
+
+NS_IMPL_ADDREF_INHERITED(CallbackDebuggerNotification, DebuggerNotification)
+NS_IMPL_RELEASE_INHERITED(CallbackDebuggerNotification, DebuggerNotification)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(CallbackDebuggerNotification)
+NS_INTERFACE_MAP_END_INHERITING(DebuggerNotification)
+
+JSObject* CallbackDebuggerNotification::WrapObject(
+    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+  return CallbackDebuggerNotification_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<DebuggerNotification> CallbackDebuggerNotification::CloneInto(
+    nsIGlobalObject* aNewOwner) const {
+  RefPtr<CallbackDebuggerNotification> notification(
+      new CallbackDebuggerNotification(mDebuggeeGlobal, mType, mPhase,
+                                       aNewOwner));
+  return notification.forget();
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/debugger/CallbackDebuggerNotification.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_CallbackDebuggerNotification_h
+#define mozilla_dom_CallbackDebuggerNotification_h
+
+#include "DebuggerNotification.h"
+#include "DebuggerNotificationManager.h"
+
+namespace mozilla {
+namespace dom {
+
+class CallbackDebuggerNotification : public DebuggerNotification {
+ public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(CallbackDebuggerNotification,
+                                           DebuggerNotification)
+
+  CallbackDebuggerNotification(nsIGlobalObject* aDebuggeeGlobal,
+                               DebuggerNotificationType aType,
+                               CallbackDebuggerNotificationPhase aPhase,
+                               nsIGlobalObject* aOwnerGlobal = nullptr)
+      : DebuggerNotification(aDebuggeeGlobal, aType, aOwnerGlobal),
+        mPhase(aPhase) {}
+
+  // nsWrapperCache
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<DebuggerNotification> CloneInto(
+      nsIGlobalObject* aNewOwner) const override;
+
+  CallbackDebuggerNotificationPhase Phase() const { return mPhase; }
+
+ protected:
+  ~CallbackDebuggerNotification() = default;
+
+  CallbackDebuggerNotificationPhase mPhase;
+};
+
+class MOZ_RAII CallbackDebuggerNotificationGuard final {
+ public:
+  MOZ_CAN_RUN_SCRIPT CallbackDebuggerNotificationGuard(
+      nsIGlobalObject* aDebuggeeGlobal, DebuggerNotificationType aType)
+      : mDebuggeeGlobal(aDebuggeeGlobal), mType(aType) {
+    Dispatch(CallbackDebuggerNotificationPhase::Pre);
+  }
+  CallbackDebuggerNotificationGuard(const CallbackDebuggerNotificationGuard&) =
+      delete;
+  CallbackDebuggerNotificationGuard(CallbackDebuggerNotificationGuard&&) =
+      delete;
+  CallbackDebuggerNotificationGuard& operator=(
+      const CallbackDebuggerNotificationGuard&) = delete;
+  CallbackDebuggerNotificationGuard& operator=(
+      CallbackDebuggerNotificationGuard&&) = delete;
+
+  MOZ_CAN_RUN_SCRIPT ~CallbackDebuggerNotificationGuard() {
+    Dispatch(CallbackDebuggerNotificationPhase::Post);
+  }
+
+ private:
+  MOZ_CAN_RUN_SCRIPT void Dispatch(CallbackDebuggerNotificationPhase aPhase) {
+    auto manager = DebuggerNotificationManager::ForDispatch(mDebuggeeGlobal);
+    if (MOZ_UNLIKELY(manager)) {
+      manager->Dispatch<CallbackDebuggerNotification>(mType, aPhase);
+    }
+  }
+
+  nsIGlobalObject* mDebuggeeGlobal;
+  DebuggerNotificationType mType;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_CallbackDebuggerNotification_h
new file mode 100644
--- /dev/null
+++ b/dom/debugger/DebuggerNotification.cpp
@@ -0,0 +1,38 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DebuggerNotification.h"
+
+#include "DebuggerNotificationManager.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DebuggerNotification, mDebuggeeGlobal,
+                                      mOwnerGlobal)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotification)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotification)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotification)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+JSObject* DebuggerNotification::WrapObject(JSContext* aCx,
+                                           JS::Handle<JSObject*> aGivenProto) {
+  return DebuggerNotification_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+already_AddRefed<DebuggerNotification> DebuggerNotification::CloneInto(
+    nsIGlobalObject* aNewOwner) const {
+  RefPtr<DebuggerNotification> notification(
+      new DebuggerNotification(mDebuggeeGlobal, mType, aNewOwner));
+  return notification.forget();
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/debugger/DebuggerNotification.h
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DebuggerNotification_h
+#define mozilla_dom_DebuggerNotification_h
+
+#include "DebuggerNotificationManager.h"
+#include "mozilla/dom/DebuggerNotificationBinding.h"
+#include "nsIGlobalObject.h"
+#include "nsISupports.h"
+#include "nsWrapperCache.h"
+
+namespace mozilla {
+namespace dom {
+
+class DebuggerNotification : public nsISupports, public nsWrapperCache {
+ public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DebuggerNotification)
+
+  DebuggerNotification(nsIGlobalObject* aDebuggeeGlobal,
+                       DebuggerNotificationType aType,
+                       nsIGlobalObject* aOwnerGlobal = nullptr)
+      : mType(aType),
+        mDebuggeeGlobal(aDebuggeeGlobal),
+        mOwnerGlobal(aOwnerGlobal) {}
+
+  nsIGlobalObject* GetParentObject() const {
+    MOZ_ASSERT(mOwnerGlobal,
+               "Notification must be cloned into an observer global before "
+               "being wrapped");
+    return mOwnerGlobal;
+  }
+
+  DebuggerNotificationType Type() const { return mType; }
+
+  void GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aResult) {
+    aResult.set(mDebuggeeGlobal->GetGlobalJSObject());
+  }
+
+  virtual already_AddRefed<DebuggerNotification> CloneInto(
+      nsIGlobalObject* aNewOwner) const;
+
+  // nsWrapperCache
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+ protected:
+  virtual ~DebuggerNotification() = default;
+
+  DebuggerNotificationType mType;
+  nsCOMPtr<nsIGlobalObject> mDebuggeeGlobal;
+
+ private:
+  nsCOMPtr<nsIGlobalObject> mOwnerGlobal;
+};
+
+MOZ_CAN_RUN_SCRIPT inline void DebuggerNotificationDispatch(
+    nsIGlobalObject* aDebuggeeGlobal, DebuggerNotificationType aType) {
+  auto manager = DebuggerNotificationManager::ForDispatch(aDebuggeeGlobal);
+  if (MOZ_UNLIKELY(manager)) {
+    manager->Dispatch<DebuggerNotification>(aType);
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_DebuggerNotification_h
new file mode 100644
--- /dev/null
+++ b/dom/debugger/DebuggerNotificationManager.cpp
@@ -0,0 +1,72 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DebuggerNotificationManager.h"
+
+#include "mozilla/ScopeExit.h"
+#include "nsIGlobalObject.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION(DebuggerNotificationManager, mDebuggeeGlobal,
+                         mNotificationObservers)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotificationManager)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotificationManager)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotificationManager)
+  NS_INTERFACE_MAP_ENTRY(nsISupports)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(DebuggerNotificationManager)
+NS_IMPL_CYCLE_COLLECTION_TRACE_END
+
+bool DebuggerNotificationManager::Attach(
+    DebuggerNotificationObserver* aObserver) {
+  RefPtr<DebuggerNotificationObserver> ptr(aObserver);
+
+  if (mNotificationObservers.Contains(ptr)) {
+    return false;
+  }
+
+  mNotificationObservers.AppendElement(ptr);
+  return true;
+}
+bool DebuggerNotificationManager::Detach(
+    DebuggerNotificationObserver* aObserver) {
+  RefPtr<DebuggerNotificationObserver> ptr(aObserver);
+
+  return mNotificationObservers.RemoveElement(ptr);
+}
+
+bool DebuggerNotificationManager::HasListeners() {
+  nsTObserverArray<RefPtr<DebuggerNotificationObserver>>::ForwardIterator iter(
+      mNotificationObservers);
+
+  while (iter.HasMore()) {
+    RefPtr<DebuggerNotificationObserver> observer(iter.GetNext());
+    if (observer->HasListeners()) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
+void DebuggerNotificationManager::NotifyListeners(
+    DebuggerNotification* aNotification) {
+  nsTObserverArray<RefPtr<DebuggerNotificationObserver>>::ForwardIterator iter(
+      mNotificationObservers);
+
+  while (iter.HasMore()) {
+    RefPtr<DebuggerNotificationObserver> observer(iter.GetNext());
+    observer->NotifyListeners(aNotification);
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/debugger/DebuggerNotificationManager.h
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DebuggerNotificationManager_h
+#define mozilla_dom_DebuggerNotificationManager_h
+
+#include "DebuggerNotificationObserver.h"
+#include "nsCycleCollectionParticipant.h"
+#include "nsIGlobalObject.h"
+#include "nsISupports.h"
+#include "nsTObserverArray.h"
+
+namespace mozilla {
+namespace dom {
+
+class DebuggerNotificationManager final : public nsISupports {
+ public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DebuggerNotificationManager)
+
+  static RefPtr<DebuggerNotificationManager> ForDispatch(
+      nsIGlobalObject* aDebuggeeGlobal) {
+    if (MOZ_UNLIKELY(!aDebuggeeGlobal)) {
+      return nullptr;
+    }
+    auto managerPtr = aDebuggeeGlobal->GetExistingDebuggerNotificationManager();
+    if (MOZ_LIKELY(!managerPtr) || !managerPtr->HasListeners()) {
+      return nullptr;
+    }
+
+    return managerPtr;
+  }
+
+  explicit DebuggerNotificationManager(nsIGlobalObject* aDebuggeeGlobal)
+      : mDebuggeeGlobal(aDebuggeeGlobal), mNotificationObservers() {}
+
+  bool Attach(DebuggerNotificationObserver* aObserver);
+  bool Detach(DebuggerNotificationObserver* aObserver);
+
+  bool HasListeners();
+
+  template <typename T, typename... Args>
+  MOZ_CAN_RUN_SCRIPT void Dispatch(Args... aArgs) {
+    RefPtr<DebuggerNotification> notification(new T(mDebuggeeGlobal, aArgs...));
+    NotifyListeners(notification);
+  }
+
+ private:
+  ~DebuggerNotificationManager() = default;
+
+  MOZ_CAN_RUN_SCRIPT void NotifyListeners(DebuggerNotification* aNotification);
+
+  nsCOMPtr<nsIGlobalObject> mDebuggeeGlobal;
+  nsTObserverArray<RefPtr<DebuggerNotificationObserver>> mNotificationObservers;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_DebuggerNotificationManager_h
new file mode 100644
--- /dev/null
+++ b/dom/debugger/DebuggerNotificationObserver.cpp
@@ -0,0 +1,152 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DebuggerNotificationObserver.h"
+
+#include "DebuggerNotification.h"
+#include "nsIGlobalObject.h"
+#include "WrapperFactory.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DebuggerNotificationObserver,
+                                      mOwnerGlobal, mEventListenerCallbacks)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(DebuggerNotificationObserver)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(DebuggerNotificationObserver)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DebuggerNotificationObserver)
+  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
+NS_INTERFACE_MAP_END
+
+/* static */ already_AddRefed<DebuggerNotificationObserver>
+DebuggerNotificationObserver::Constructor(GlobalObject& aGlobal,
+                                          ErrorResult& aRv) {
+  nsCOMPtr<nsIGlobalObject> globalInterface(
+      do_QueryInterface(aGlobal.GetAsSupports()));
+  if (NS_WARN_IF(!globalInterface)) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  RefPtr<DebuggerNotificationObserver> observer(
+      new DebuggerNotificationObserver(globalInterface));
+  return observer.forget();
+}
+
+DebuggerNotificationObserver::DebuggerNotificationObserver(
+    nsIGlobalObject* aOwnerGlobal)
+    : mEventListenerCallbacks(), mOwnerGlobal(aOwnerGlobal) {}
+
+JSObject* DebuggerNotificationObserver::WrapObject(
+    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+  return DebuggerNotificationObserver_Binding::Wrap(aCx, this, aGivenProto);
+}
+
+static already_AddRefed<DebuggerNotificationManager> GetManager(
+    JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal) {
+  // The debuggee global here is likely a debugger-compartment cross-compartment
+  // wrapper for the debuggee global object, so we need to unwrap it to get
+  // the real debuggee-compartment global object.
+  JS::Rooted<JSObject*> debuggeeGlobalRooted(
+      aCx, js::UncheckedUnwrap(aDebuggeeGlobal, false));
+
+  if (!debuggeeGlobalRooted) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIGlobalObject> debuggeeGlobalObject(
+      xpc::NativeGlobal(debuggeeGlobalRooted));
+  if (!debuggeeGlobalObject) {
+    return nullptr;
+  }
+
+  RefPtr<DebuggerNotificationManager> manager(
+      debuggeeGlobalObject->GetOrCreateDebuggerNotificationManager());
+  return manager.forget();
+}
+
+bool DebuggerNotificationObserver::Connect(
+    JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) {
+  RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal));
+
+  if (!manager) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return false;
+  }
+
+  return manager->Attach(this);
+}
+
+bool DebuggerNotificationObserver::Disconnect(
+    JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal, ErrorResult& aRv) {
+  RefPtr<DebuggerNotificationManager> manager(GetManager(aCx, aDebuggeeGlobal));
+
+  if (!manager) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return false;
+  }
+
+  return manager->Detach(this);
+}
+
+bool DebuggerNotificationObserver::AddListener(
+    DebuggerNotificationCallback& aHandlerFn) {
+  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator iter(
+      mEventListenerCallbacks);
+  while (iter.HasMore()) {
+    if (*iter.GetNext().get() == aHandlerFn) {
+      return false;
+    }
+  }
+
+  RefPtr<DebuggerNotificationCallback> handlerFn(&aHandlerFn);
+  mEventListenerCallbacks.AppendElement(handlerFn);
+  return true;
+}
+
+bool DebuggerNotificationObserver::RemoveListener(
+    DebuggerNotificationCallback& aHandlerFn) {
+  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator iter(
+      mEventListenerCallbacks);
+  for (uint32_t i = 0; iter.HasMore(); i++) {
+    if (*iter.GetNext().get() == aHandlerFn) {
+      mEventListenerCallbacks.RemoveElementAt(i);
+      return true;
+    }
+  }
+
+  return false;
+}
+
+bool DebuggerNotificationObserver::HasListeners() {
+  return !mEventListenerCallbacks.IsEmpty();
+}
+
+void DebuggerNotificationObserver::NotifyListeners(
+    DebuggerNotification* aNotification) {
+  if (!HasListeners()) {
+    return;
+  }
+
+  // Since we want the notification objects to live in the same compartment
+  // as the observer, we create a new instance of the notification before
+  // an observer dispatches the event listeners.
+  RefPtr<DebuggerNotification> debuggerNotification(
+      aNotification->CloneInto(mOwnerGlobal));
+
+  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>::ForwardIterator iter(
+      mEventListenerCallbacks);
+
+  while (iter.HasMore()) {
+    RefPtr<DebuggerNotificationCallback> cb(iter.GetNext());
+    cb->Call(*debuggerNotification);
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/debugger/DebuggerNotificationObserver.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_DebuggerNotificationObserver_h
+#define mozilla_dom_DebuggerNotificationObserver_h
+
+#include "DebuggerNotificationManager.h"
+#include "mozilla/dom/DebuggerNotificationObserverBinding.h"
+#include "mozilla/RefPtr.h"
+#include "nsTObserverArray.h"
+#include "nsWrapperCache.h"
+
+class nsIGlobalObject;
+
+namespace mozilla {
+namespace dom {
+
+class DebuggerNotification;
+
+class DebuggerNotificationObserver final : public nsISupports,
+                                           public nsWrapperCache {
+ public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DebuggerNotificationObserver)
+
+  static already_AddRefed<DebuggerNotificationObserver> Constructor(
+      GlobalObject& aGlobal, ErrorResult& aRv);
+
+  nsIGlobalObject* GetParentObject() const { return mOwnerGlobal; }
+
+  // nsWrapperCache
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  bool Connect(JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal,
+               ErrorResult& aRv);
+  bool Disconnect(JSContext* aCx, JS::Handle<JSObject*> aDebuggeeGlobal,
+                  ErrorResult& aRv);
+
+  bool AddListener(DebuggerNotificationCallback& aHandlerFn);
+  bool RemoveListener(DebuggerNotificationCallback& aHandlerFn);
+
+  bool HasListeners();
+
+  MOZ_CAN_RUN_SCRIPT void NotifyListeners(DebuggerNotification* aNotification);
+
+ private:
+  explicit DebuggerNotificationObserver(nsIGlobalObject* aOwnerGlobal);
+  ~DebuggerNotificationObserver() = default;
+
+  nsTObserverArray<RefPtr<DebuggerNotificationCallback>>
+      mEventListenerCallbacks;
+  nsCOMPtr<nsIGlobalObject> mOwnerGlobal;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_DebuggerNotificationObserver_h
new file mode 100644
--- /dev/null
+++ b/dom/debugger/EventCallbackDebuggerNotification.cpp
@@ -0,0 +1,65 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "EventCallbackDebuggerNotification.h"
+
+#include "DebuggerNotificationManager.h"
+#include "mozilla/dom/EventTarget.h"
+#include "mozilla/dom/Worker.h"
+#include "mozilla/dom/XMLHttpRequestEventTarget.h"
+#include "nsIGlobalObject.h"
+#include "nsINode.h"
+#include "nsQueryObject.h"
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_CYCLE_COLLECTION_INHERITED(EventCallbackDebuggerNotification,
+                                   CallbackDebuggerNotification, mEvent)
+
+NS_IMPL_ADDREF_INHERITED(EventCallbackDebuggerNotification,
+                         CallbackDebuggerNotification)
+NS_IMPL_RELEASE_INHERITED(EventCallbackDebuggerNotification,
+                          CallbackDebuggerNotification)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventCallbackDebuggerNotification)
+NS_INTERFACE_MAP_END_INHERITING(CallbackDebuggerNotification)
+
+JSObject* EventCallbackDebuggerNotification::WrapObject(
+    JSContext* aCx, JS::Handle<JSObject*> aGivenProto) {
+  return EventCallbackDebuggerNotification_Binding::Wrap(aCx, this,
+                                                         aGivenProto);
+}
+
+already_AddRefed<DebuggerNotification>
+EventCallbackDebuggerNotification::CloneInto(nsIGlobalObject* aNewOwner) const {
+  RefPtr<EventCallbackDebuggerNotification> notification(
+      new EventCallbackDebuggerNotification(mDebuggeeGlobal, mType, mEvent,
+                                            mTargetType, mPhase, aNewOwner));
+  return notification.forget();
+}
+
+void EventCallbackDebuggerNotificationGuard::DispatchToManager(
+    const RefPtr<DebuggerNotificationManager>& aManager,
+    CallbackDebuggerNotificationPhase aPhase) {
+  if (!mEventTarget) {
+    MOZ_ASSERT(false, "target should exist");
+    return;
+  }
+
+  Maybe<EventCallbackDebuggerNotificationType> notificationType(
+      mEventTarget->GetDebuggerNotificationType());
+
+  if (notificationType) {
+    aManager->Dispatch<EventCallbackDebuggerNotification>(
+        DebuggerNotificationType::DomEvent,
+        // The DOM event will always be live during event dispatch.
+        MOZ_KnownLive(mEvent), *notificationType, aPhase);
+  }
+}
+
+}  // namespace dom
+}  // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/debugger/EventCallbackDebuggerNotification.h
@@ -0,0 +1,96 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_dom_EventCallbackDebuggerNotification_h
+#define mozilla_dom_EventCallbackDebuggerNotification_h
+
+#include "CallbackDebuggerNotification.h"
+#include "DebuggerNotificationManager.h"
+#include "mozilla/dom/Event.h"
+#include "mozilla/RefPtr.h"
+
+namespace mozilla {
+namespace dom {
+
+class EventCallbackDebuggerNotification : public CallbackDebuggerNotification {
+ public:
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(EventCallbackDebuggerNotification,
+                                           CallbackDebuggerNotification)
+
+  EventCallbackDebuggerNotification(
+      nsIGlobalObject* aDebuggeeGlobal, DebuggerNotificationType aType,
+      Event* aEvent, EventCallbackDebuggerNotificationType aTargetType,
+      CallbackDebuggerNotificationPhase aPhase,
+      nsIGlobalObject* aOwnerGlobal = nullptr)
+      : CallbackDebuggerNotification(aDebuggeeGlobal, aType, aPhase,
+                                     aOwnerGlobal),
+        mEvent(aEvent),
+        mTargetType(aTargetType) {}
+
+  // nsWrapperCache
+  virtual JSObject* WrapObject(JSContext* aCx,
+                               JS::Handle<JSObject*> aGivenProto) override;
+
+  already_AddRefed<DebuggerNotification> CloneInto(
+      nsIGlobalObject* aNewOwner) const override;
+
+  mozilla::dom::Event* Event() const { return mEvent; }
+  EventCallbackDebuggerNotificationType TargetType() const {
+    return mTargetType;
+  }
+
+ private:
+  ~EventCallbackDebuggerNotification() = default;
+
+  RefPtr<mozilla::dom::Event> mEvent;
+  EventCallbackDebuggerNotificationType mTargetType;
+};
+
+class MOZ_RAII EventCallbackDebuggerNotificationGuard final {
+ public:
+  MOZ_CAN_RUN_SCRIPT explicit EventCallbackDebuggerNotificationGuard(
+      mozilla::dom::EventTarget* aEventTarget, mozilla::dom::Event* aEvent)
+      : mDebuggeeGlobal(aEventTarget ? aEventTarget->GetOwnerGlobal()
+                                     : nullptr),
+        mEventTarget(aEventTarget),
+        mEvent(aEvent) {
+    Dispatch(CallbackDebuggerNotificationPhase::Pre);
+  }
+  EventCallbackDebuggerNotificationGuard(
+      const EventCallbackDebuggerNotificationGuard&) = delete;
+  EventCallbackDebuggerNotificationGuard(
+      EventCallbackDebuggerNotificationGuard&&) = delete;
+  EventCallbackDebuggerNotificationGuard& operator=(
+      const EventCallbackDebuggerNotificationGuard&) = delete;
+  EventCallbackDebuggerNotificationGuard& operator=(
+      EventCallbackDebuggerNotificationGuard&&) = delete;
+
+  MOZ_CAN_RUN_SCRIPT ~EventCallbackDebuggerNotificationGuard() {
+    Dispatch(CallbackDebuggerNotificationPhase::Post);
+  }
+
+ private:
+  MOZ_CAN_RUN_SCRIPT void Dispatch(CallbackDebuggerNotificationPhase aPhase) {
+    auto manager = DebuggerNotificationManager::ForDispatch(mDebuggeeGlobal);
+    if (MOZ_UNLIKELY(manager)) {
+      DispatchToManager(manager, aPhase);
+    }
+  }
+
+  MOZ_CAN_RUN_SCRIPT void DispatchToManager(
+      const RefPtr<DebuggerNotificationManager>& aManager,
+      CallbackDebuggerNotificationPhase aPhase);
+
+  nsIGlobalObject* mDebuggeeGlobal;
+  mozilla::dom::EventTarget* mEventTarget;
+  mozilla::dom::Event* mEvent;
+};
+
+}  // namespace dom
+}  // namespace mozilla
+
+#endif  // mozilla_dom_EventCallbackDebuggerNotification_h
new file mode 100644
--- /dev/null
+++ b/dom/debugger/moz.build
@@ -0,0 +1,26 @@
+# -*- Mode: python; 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/.
+
+with Files("**"):
+    BUG_COMPONENT = ("DevTools", "Debugger")
+
+EXPORTS.mozilla.dom += [
+    'CallbackDebuggerNotification.h',
+    'DebuggerNotification.h',
+    'DebuggerNotificationManager.h',
+    'DebuggerNotificationObserver.h',
+    'EventCallbackDebuggerNotification.h',
+]
+
+UNIFIED_SOURCES += [
+    'CallbackDebuggerNotification.cpp',
+    'DebuggerNotification.cpp',
+    'DebuggerNotificationManager.cpp',
+    'DebuggerNotificationObserver.cpp',
+    'EventCallbackDebuggerNotification.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
\ No newline at end of file
--- a/dom/events/EventListenerManager.cpp
+++ b/dom/events/EventListenerManager.cpp
@@ -15,16 +15,17 @@
 #include "mozilla/HalSensor.h"
 #include "mozilla/InternalMutationEvent.h"
 #include "mozilla/JSEventHandler.h"
 #include "mozilla/Maybe.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/PresShell.h"
 #include "mozilla/dom/BindingUtils.h"
+#include "mozilla/dom/EventCallbackDebuggerNotification.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/EventTargetBinding.h"
 #include "mozilla/dom/LoadedScript.h"
 #include "mozilla/dom/PopupBlocker.h"
 #include "mozilla/dom/ScriptLoader.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TouchEvent.h"
@@ -1009,16 +1010,17 @@ nsresult EventListenerManager::HandleEve
   // compiled the event handler itself
   if ((aListener->mListenerType == Listener::eJSEventListener) &&
       aListener->mHandlerIsString) {
     result = CompileEventHandlerInternal(aListener, nullptr, nullptr);
     aListener = nullptr;
   }
 
   if (NS_SUCCEEDED(result)) {
+    EventCallbackDebuggerNotificationGuard dbgGuard(aCurrentTarget, aDOMEvent);
     nsAutoMicroTask mt;
 
     // Event::currentTarget is set in EventDispatcher.
     if (listenerHolder.HasWebIDLCallback()) {
       ErrorResult rv;
       listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
                                                       *aDOMEvent, rv);
       result = rv.StealNSResult();
--- a/dom/events/EventTarget.h
+++ b/dom/events/EventTarget.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_EventTarget_h_
 #define mozilla_dom_EventTarget_h_
 
 #include "mozilla/dom/BindingDeclarations.h"
+#include "mozilla/dom/DebuggerNotificationBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "nsISupports.h"
 #include "nsWrapperCache.h"
 #include "nsAtom.h"
 
 class nsPIDOMWindowOuter;
 class nsIGlobalObject;
 class nsIDOMEventListener;
@@ -190,16 +191,21 @@ class EventTarget : public nsISupports, 
   virtual EventListenerManager* GetOrCreateListenerManager() = 0;
 
   /**
    * Get the event listener manager, returning null if it does not already
    * exist.
    */
   virtual EventListenerManager* GetExistingListenerManager() const = 0;
 
+  virtual Maybe<EventCallbackDebuggerNotificationType>
+  GetDebuggerNotificationType() const {
+    return Nothing();
+  }
+
   // Called from AsyncEventDispatcher to notify it is running.
   virtual void AsyncEventRunning(AsyncEventDispatcher* aEvent) {}
 
   // Used by APZ to determine whether this event target has non-chrome event
   // listeners for untrusted key events.
   bool HasNonSystemGroupListenersForUntrustedKeyEvents() const;
 
   // Used by APZ to determine whether this event target has non-chrome and
--- a/dom/html/HTMLTrackElement.cpp
+++ b/dom/html/HTMLTrackElement.cpp
@@ -2,20 +2,16 @@
 /* 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 "mozilla/dom/HTMLTrackElement.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/HTMLMediaElement.h"
-#ifdef XP_WIN
-// HTMLTrackElement.webidl defines ERROR, but so does windows.h:
-#  undef ERROR
-#endif
 #include "WebVTTListener.h"
 #include "mozilla/LoadInfo.h"
 #include "mozilla/dom/HTMLTrackElementBinding.h"
 #include "mozilla/dom/HTMLUnknownElement.h"
 #include "nsAttrValueInlines.h"
 #include "nsCOMPtr.h"
 #include "nsContentPolicyUtils.h"
 #include "nsContentUtils.h"
--- a/dom/moz.build
+++ b/dom/moz.build
@@ -39,16 +39,17 @@ DIRS += [
     'cache',
     'canvas',
     'webgpu',
     'chrome-webidl',
     'clients',
     'commandhandler',
     'credentialmanagement',
     'crypto',
+    'debugger',
     'encoding',
     'events',
     'fetch',
     'file',
     'filehandle',
     'filesystem',
     'flex',
     'gamepad',
--- a/dom/workers/RegisterBindings.cpp
+++ b/dom/workers/RegisterBindings.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this file,
  * You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WorkerPrivate.h"
 #include "ChromeWorkerScope.h"
 #include "RuntimeService.h"
 
 #include "jsapi.h"
+#include "mozilla/dom/DebuggerNotificationObserverBinding.h"
 #include "mozilla/dom/RegisterWorkerBindings.h"
 #include "mozilla/dom/RegisterWorkerDebuggerBindings.h"
 #include "mozilla/OSFileConstants.h"
 
 using namespace mozilla::dom;
 
 bool WorkerPrivate::RegisterBindings(JSContext* aCx,
                                      JS::Handle<JSObject*> aGlobal) {
@@ -47,14 +48,18 @@ bool WorkerPrivate::RegisterDebuggerBind
   if (!RegisterWorkerDebuggerBindings(aCx, aGlobal)) {
     return false;
   }
 
   if (!ChromeUtils_Binding::GetConstructorObject(aCx)) {
     return false;
   }
 
+  if (!DebuggerNotificationObserver_Binding::GetConstructorObject(aCx)) {
+    return false;
+  }
+
   if (!JS_DefineDebuggerObject(aCx, aGlobal)) {
     return false;
   }
 
   return true;
 }
--- a/dom/workers/Worker.h
+++ b/dom/workers/Worker.h
@@ -3,16 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_Worker_h
 #define mozilla_dom_Worker_h
 
 #include "mozilla/Attributes.h"
+#include "mozilla/dom/DebuggerNotificationBinding.h"
 #include "mozilla/DOMEventTargetHelper.h"
 #include "mozilla/RefPtr.h"
 #include "mozilla/WeakPtr.h"
 
 #ifdef XP_WIN
 #  undef PostMessage
 #endif
 
@@ -33,16 +34,21 @@ class Worker : public DOMEventTargetHelp
   static already_AddRefed<Worker> Constructor(const GlobalObject& aGlobal,
                                               const nsAString& aScriptURL,
                                               const WorkerOptions& aOptions,
                                               ErrorResult& aRv);
 
   JSObject* WrapObject(JSContext* aCx,
                        JS::Handle<JSObject*> aGivenProto) override;
 
+  Maybe<EventCallbackDebuggerNotificationType> GetDebuggerNotificationType()
+      const override {
+    return Some(EventCallbackDebuggerNotificationType::Worker);
+  }
+
   void PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                    const Sequence<JSObject*>& aTransferable, ErrorResult& aRv);
 
   void PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
                    const PostMessageOptions& aOptions, ErrorResult& aRv);
 
   void Terminate();
 
--- a/dom/workers/WorkerPrivate.cpp
+++ b/dom/workers/WorkerPrivate.cpp
@@ -12,16 +12,17 @@
 #include "js/ContextOptions.h"
 #include "js/LocaleSensitive.h"
 #include "js/MemoryMetrics.h"
 #include "js/SourceText.h"
 #include "MessageEventRunnable.h"
 #include "mozilla/ScopeExit.h"
 #include "mozilla/StaticPrefs.h"
 #include "mozilla/dom/BlobURLProtocolHandler.h"
+#include "mozilla/dom/CallbackDebuggerNotification.h"
 #include "mozilla/dom/ClientManager.h"
 #include "mozilla/dom/ClientSource.h"
 #include "mozilla/dom/ClientState.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/DOMTypes.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/IndexedDatabaseManager.h"
@@ -4304,16 +4305,22 @@ bool WorkerPrivate::RunExpiredTimeouts(J
     const char* reason;
     if (info->mIsInterval) {
       reason = "setInterval handler";
     } else {
       reason = "setTimeout handler";
     }
 
     RefPtr<TimeoutHandler> handler(info->mHandler);
+
+    RefPtr<WorkerGlobalScope> scope(this->GlobalScope());
+    CallbackDebuggerNotificationGuard guard(
+        scope, info->mIsInterval
+                   ? DebuggerNotificationType::SetIntervalCallback
+                   : DebuggerNotificationType::SetTimeoutCallback);
     if (!handler->Call(reason)) {
       retval = false;
       break;
     }
 
     NS_ASSERTION(data->mRunningExpiredTimeouts, "Someone changed this!");
   }
 
--- a/dom/workers/WorkerScope.cpp
+++ b/dom/workers/WorkerScope.cpp
@@ -9,16 +9,17 @@
 #include "jsapi.h"
 #include "jsfriendapi.h"
 #include "mozilla/EventListenerManager.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/Clients.h"
 #include "mozilla/dom/ClientState.h"
 #include "mozilla/dom/Console.h"
 #include "mozilla/dom/CSPEvalChecker.h"
+#include "mozilla/dom/DebuggerNotification.h"
 #include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
 #include "mozilla/dom/Fetch.h"
 #include "mozilla/dom/FunctionBinding.h"
 #include "mozilla/dom/IDBFactory.h"
 #include "mozilla/dom/ImageBitmap.h"
 #include "mozilla/dom/ImageBitmapBinding.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/Promise.h"
@@ -135,30 +136,32 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDebuggerNotificationManager)
   tmp->TraverseHostObjectURIs(cb);
   tmp->mWorkerPrivate->TraverseTimeouts(cb);
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope,
                                                 DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mDebuggerNotificationManager)
   tmp->UnlinkHostObjectURIs();
   tmp->mWorkerPrivate->UnlinkTimeouts();
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 
 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScope,
                                                DOMEventTargetHelper)
   tmp->mWorkerPrivate->AssertIsOnWorkerThread();
 NS_IMPL_CYCLE_COLLECTION_TRACE_END
@@ -298,16 +301,19 @@ int32_t WorkerGlobalScope::SetTimeout(JS
                                       const int32_t aTimeout,
                                       const Sequence<JS::Value>& /* unused */,
                                       ErrorResult& aRv) {
   return SetTimeoutOrInterval(aCx, aHandler, aTimeout, false, aRv);
 }
 
 void WorkerGlobalScope::ClearTimeout(int32_t aHandle) {
   mWorkerPrivate->AssertIsOnWorkerThread();
+
+  DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearTimeout);
+
   mWorkerPrivate->ClearTimeout(aHandle);
 }
 
 int32_t WorkerGlobalScope::SetInterval(JSContext* aCx, Function& aHandler,
                                        const int32_t aTimeout,
                                        const Sequence<JS::Value>& aArguments,
                                        ErrorResult& aRv) {
   return SetTimeoutOrInterval(aCx, aHandler, aTimeout, aArguments, true, aRv);
@@ -318,24 +324,31 @@ int32_t WorkerGlobalScope::SetInterval(J
                                        const int32_t aTimeout,
                                        const Sequence<JS::Value>& /* unused */,
                                        ErrorResult& aRv) {
   return SetTimeoutOrInterval(aCx, aHandler, aTimeout, true, aRv);
 }
 
 void WorkerGlobalScope::ClearInterval(int32_t aHandle) {
   mWorkerPrivate->AssertIsOnWorkerThread();
+
+  DebuggerNotificationDispatch(this, DebuggerNotificationType::ClearInterval);
+
   mWorkerPrivate->ClearTimeout(aHandle);
 }
 
 int32_t WorkerGlobalScope::SetTimeoutOrInterval(
     JSContext* aCx, Function& aHandler, const int32_t aTimeout,
     const Sequence<JS::Value>& aArguments, bool aIsInterval, ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
+  DebuggerNotificationDispatch(
+      this, aIsInterval ? DebuggerNotificationType::SetInterval
+                        : DebuggerNotificationType::SetTimeout);
+
   nsTArray<JS::Heap<JS::Value>> args;
   if (!args.AppendElements(aArguments, fallible)) {
     aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
     return 0;
   }
 
   RefPtr<TimeoutHandler> handler =
       new CallbackTimeoutHandler(aCx, this, &aHandler, std::move(args));
@@ -345,16 +358,20 @@ int32_t WorkerGlobalScope::SetTimeoutOrI
 
 int32_t WorkerGlobalScope::SetTimeoutOrInterval(JSContext* aCx,
                                                 const nsAString& aHandler,
                                                 const int32_t aTimeout,
                                                 bool aIsInterval,
                                                 ErrorResult& aRv) {
   mWorkerPrivate->AssertIsOnWorkerThread();
 
+  DebuggerNotificationDispatch(
+      this, aIsInterval ? DebuggerNotificationType::SetInterval
+                        : DebuggerNotificationType::SetTimeout);
+
   bool allowEval = false;
   aRv =
       CSPEvalChecker::CheckForWorker(aCx, mWorkerPrivate, aHandler, &allowEval);
   if (NS_WARN_IF(aRv.Failed()) || !allowEval) {
     return 0;
   }
 
   RefPtr<TimeoutHandler> handler =
@@ -495,16 +512,30 @@ nsISerialEventTarget* WorkerGlobalScope:
   return mSerialEventTarget;
 }
 
 AbstractThread* WorkerGlobalScope::AbstractMainThreadFor(
     TaskCategory aCategory) {
   MOZ_CRASH("AbstractMainThreadFor not supported for workers.");
 }
 
+mozilla::dom::DebuggerNotificationManager*
+WorkerGlobalScope::GetOrCreateDebuggerNotificationManager() {
+  if (!mDebuggerNotificationManager) {
+    mDebuggerNotificationManager = new DebuggerNotificationManager(this);
+  }
+
+  return mDebuggerNotificationManager;
+}
+
+mozilla::dom::DebuggerNotificationManager*
+WorkerGlobalScope::GetExistingDebuggerNotificationManager() {
+  return mDebuggerNotificationManager;
+}
+
 Maybe<ClientInfo> WorkerGlobalScope::GetClientInfo() const {
   return mWorkerPrivate->GetClientInfo();
 }
 
 Maybe<ClientState> WorkerGlobalScope::GetClientState() const {
   Maybe<ClientState> state;
   state.emplace(mWorkerPrivate->GetClientState());
   return state;
--- a/dom/workers/WorkerScope.h
+++ b/dom/workers/WorkerScope.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_dom_workerscope_h__
 #define mozilla_dom_workerscope_h__
 
 #include "mozilla/Attributes.h"
 #include "mozilla/dom/WorkerCommon.h"
 #include "mozilla/DOMEventTargetHelper.h"
+#include "mozilla/dom/DebuggerNotificationManager.h"
 #include "mozilla/dom/Headers.h"
 #include "mozilla/dom/RequestBinding.h"
 #include "nsWeakReference.h"
 #include "mozilla/dom/ImageBitmapSource.h"
 
 #ifdef XP_WIN
 #  undef PostMessage
 #endif
@@ -55,29 +56,33 @@ class WorkerGlobalScope : public DOMEven
   RefPtr<Console> mConsole;
   RefPtr<Crypto> mCrypto;
   RefPtr<WorkerLocation> mLocation;
   RefPtr<WorkerNavigator> mNavigator;
   RefPtr<Performance> mPerformance;
   RefPtr<IDBFactory> mIndexedDB;
   RefPtr<cache::CacheStorage> mCacheStorage;
   nsCOMPtr<nsISerialEventTarget> mSerialEventTarget;
+  RefPtr<mozilla::dom::DebuggerNotificationManager>
+      mDebuggerNotificationManager;
 
   uint32_t mWindowInteractionsAllowed;
 
  protected:
   WorkerPrivate* mWorkerPrivate;
 
   explicit WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
   virtual ~WorkerGlobalScope();
 
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeoutOrInterval(JSContext* aCx, Function& aHandler,
                                const int32_t aTimeout,
                                const Sequence<JS::Value>& aArguments,
                                bool aIsInterval, ErrorResult& aRv);
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeoutOrInterval(JSContext* aCx, const nsAString& aHandler,
                                const int32_t aTimeout, bool aIsInterval,
                                ErrorResult& aRv);
 
  public:
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aGivenProto) override;
 
@@ -110,29 +115,35 @@ class WorkerGlobalScope : public DOMEven
   already_AddRefed<WorkerNavigator> GetExistingNavigator() const;
 
   OnErrorEventHandlerNonNull* GetOnerror();
   void SetOnerror(OnErrorEventHandlerNonNull* aHandler);
 
   void ImportScripts(JSContext* aCx, const Sequence<nsString>& aScriptURLs,
                      ErrorResult& aRv);
 
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeout(JSContext* aCx, Function& aHandler, const int32_t aTimeout,
                      const Sequence<JS::Value>& aArguments, ErrorResult& aRv);
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetTimeout(JSContext* aCx, const nsAString& aHandler,
                      const int32_t aTimeout,
                      const Sequence<JS::Value>& /* unused */, ErrorResult& aRv);
+  MOZ_CAN_RUN_SCRIPT
   void ClearTimeout(int32_t aHandle);
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetInterval(JSContext* aCx, Function& aHandler,
                       const int32_t aTimeout,
                       const Sequence<JS::Value>& aArguments, ErrorResult& aRv);
+  MOZ_CAN_RUN_SCRIPT
   int32_t SetInterval(JSContext* aCx, const nsAString& aHandler,
                       const int32_t aTimeout,
                       const Sequence<JS::Value>& /* unused */,
                       ErrorResult& aRv);
+  MOZ_CAN_RUN_SCRIPT
   void ClearInterval(int32_t aHandle);
 
   void GetOrigin(nsAString& aOrigin) const;
 
   void Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const;
   void Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const;
 
   IMPL_EVENT_HANDLER(online)
@@ -186,16 +197,28 @@ class WorkerGlobalScope : public DOMEven
   // return failure if the worker thread is not alive.
   nsresult Dispatch(TaskCategory aCategory,
                     already_AddRefed<nsIRunnable>&& aRunnable) override;
 
   nsISerialEventTarget* EventTargetFor(TaskCategory aCategory) const override;
 
   AbstractThread* AbstractMainThreadFor(TaskCategory aCategory) override;
 
+  mozilla::dom::DebuggerNotificationManager*
+  GetOrCreateDebuggerNotificationManager() override;
+
+  mozilla::dom::DebuggerNotificationManager*
+  GetExistingDebuggerNotificationManager() override;
+
+  mozilla::Maybe<mozilla::dom::EventCallbackDebuggerNotificationType>
+  GetDebuggerNotificationType() const override {
+    return mozilla::Some(
+        mozilla::dom::EventCallbackDebuggerNotificationType::Global);
+  }
+
   Maybe<ClientInfo> GetClientInfo() const override;
 
   Maybe<ClientState> GetClientState() const;
 
   Maybe<ServiceWorkerDescriptor> GetController() const override;
 
   RefPtr<mozilla::dom::ServiceWorkerRegistration> GetServiceWorkerRegistration(
       const ServiceWorkerRegistrationDescriptor& aDescriptor) const override;
--- a/dom/xhr/XMLHttpRequestEventTarget.h
+++ b/dom/xhr/XMLHttpRequestEventTarget.h
@@ -21,16 +21,21 @@ class XMLHttpRequestEventTarget : public
 
   virtual ~XMLHttpRequestEventTarget() {}
 
  public:
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XMLHttpRequestEventTarget,
                                            DOMEventTargetHelper)
 
+  mozilla::Maybe<EventCallbackDebuggerNotificationType>
+  GetDebuggerNotificationType() const override {
+    return mozilla::Some(EventCallbackDebuggerNotificationType::Xhr);
+  }
+
   IMPL_EVENT_HANDLER(loadstart)
   IMPL_EVENT_HANDLER(progress)
   IMPL_EVENT_HANDLER(abort)
   IMPL_EVENT_HANDLER(error)
   IMPL_EVENT_HANDLER(load)
   IMPL_EVENT_HANDLER(timeout)
   IMPL_EVENT_HANDLER(loadend)
 
--- a/layout/base/nsRefreshDriver.cpp
+++ b/layout/base/nsRefreshDriver.cpp
@@ -47,16 +47,17 @@
 #include "nsContentUtils.h"
 #include "mozilla/PendingAnimationTracker.h"
 #include "mozilla/PendingFullscreenEvent.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/StaticPrefs.h"
 #include "nsViewManager.h"
 #include "GeckoProfiler.h"
 #include "nsNPAPIPluginInstance.h"
+#include "mozilla/dom/CallbackDebuggerNotification.h"
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/Selection.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/RestyleManager.h"
 #include "Layers.h"
 #include "imgIContainer.h"
 #include "mozilla/dom/ScriptSettings.h"
@@ -1690,16 +1691,22 @@ void nsRefreshDriver::RunFrameRequestCal
         }
         // else window is partially torn down already
       }
       for (auto& callback : docCallbacks.mCallbacks) {
         if (docCallbacks.mDocument->IsCanceledFrameRequestCallback(
                 callback.mHandle)) {
           continue;
         }
+
+        nsCOMPtr<nsIGlobalObject> global(innerWindow ? innerWindow->AsGlobal()
+                                                     : nullptr);
+        CallbackDebuggerNotificationGuard guard(
+            global, DebuggerNotificationType::RequestAnimationFrameCallback);
+
         // MOZ_KnownLive is OK, because the stack array frameRequestCallbacks
         // keeps callback alive and the mCallback strong reference can't be
         // mutated by the call.
         MOZ_KnownLive(callback.mCallback)->Call(timeStamp);
       }
     }
   }
 }