Bug 1506014 - Refactor some of the common code in the implementations of nsIContentPermissionRequest into a common base class r=baku
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 14 Nov 2018 05:33:25 +0000
changeset 446149 eed2a2c3c3d25e10d7273b76d45d76fd3e3c94e3
parent 446148 7d3cd7ee5d67a7418f439de87b8183746b089146
child 446150 d8a262837cd3956ec6a6aa3082d0ff0e7352c79c
push id72933
push usereakhgari@mozilla.com
push dateWed, 14 Nov 2018 05:34:32 +0000
treeherderautoland@eed2a2c3c3d2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku
bugs1506014
milestone65.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 1506014 - Refactor some of the common code in the implementations of nsIContentPermissionRequest into a common base class r=baku Differential Revision: https://phabricator.services.mozilla.com/D11488
dom/base/nsContentPermissionHelper.cpp
dom/base/nsContentPermissionHelper.h
dom/geolocation/nsGeolocation.cpp
dom/html/AutoplayPermissionManager.cpp
dom/html/AutoplayPermissionRequest.cpp
dom/html/AutoplayPermissionRequest.h
dom/midi/MIDIPermissionRequest.cpp
dom/midi/MIDIPermissionRequest.h
dom/notification/Notification.cpp
dom/quota/StorageManager.cpp
--- a/dom/base/nsContentPermissionHelper.cpp
+++ b/dom/base/nsContentPermissionHelper.cpp
@@ -13,16 +13,17 @@
 #include "mozilla/dom/Event.h"
 #include "mozilla/dom/PContentPermission.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/dom/PContentPermissionRequestParent.h"
 #include "mozilla/dom/ScriptSettings.h"
 #include "mozilla/dom/TabChild.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/EventStateManager.h"
+#include "mozilla/Preferences.h"
 #include "mozilla/Unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsArrayUtils.h"
 #include "nsIMutableArray.h"
 #include "nsContentPermissionHelper.h"
 #include "nsJSUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
@@ -506,16 +507,187 @@ nsContentPermissionRequester::GetOnVisib
 {
   NS_ENSURE_ARG_POINTER(aCallback);
 
   nsCOMPtr<nsIContentPermissionRequestCallback> callback = mListener->GetCallback();
   callback.forget(aCallback);
   return NS_OK;
 }
 
+NS_IMPL_CYCLE_COLLECTION(ContentPermissionRequestBase, mPrincipal, mWindow)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ContentPermissionRequestBase)
+  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsISupports)
+  NS_INTERFACE_MAP_ENTRY_CONCRETE(nsIContentPermissionRequest)
+NS_INTERFACE_MAP_END
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(ContentPermissionRequestBase)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(ContentPermissionRequestBase)
+
+ContentPermissionRequestBase::ContentPermissionRequestBase(nsIPrincipal* aPrincipal,
+                                                           bool aIsHandlingUserInput,
+                                                           nsPIDOMWindowInner* aWindow,
+                                                           const nsACString& aPrefName,
+                                                           const nsACString& aType)
+  : mPrincipal(aPrincipal)
+  , mWindow(aWindow)
+  , mRequester(aWindow ?
+                 new nsContentPermissionRequester(aWindow) :
+                 nullptr)
+  , mPrefName(aPrefName)
+  , mType(aType)
+  , mIsHandlingUserInput(aIsHandlingUserInput)
+{
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
+{
+  NS_ADDREF(*aRequestingPrincipal = mPrincipal);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetWindow(mozIDOMWindow** aRequestingWindow)
+{
+  NS_ADDREF(*aRequestingWindow = mWindow);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetElement(Element** aElement)
+{
+  NS_ENSURE_ARG_POINTER(aElement);
+  *aElement = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
+{
+  *aIsHandlingUserInput = mIsHandlingUserInput;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetRequester(nsIContentPermissionRequester** aRequester)
+{
+  NS_ENSURE_ARG_POINTER(aRequester);
+
+  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
+  requester.forget(aRequester);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+ContentPermissionRequestBase::GetTypes(nsIArray** aTypes)
+{
+  nsTArray<nsString> emptyOptions;
+  return nsContentPermissionUtils::CreatePermissionArray(mType,
+                                                         emptyOptions,
+                                                         aTypes);
+}
+
+ContentPermissionRequestBase::PromptResult
+ContentPermissionRequestBase::CheckPromptPrefs()
+{
+  MOZ_ASSERT(!mPrefName.IsEmpty(), "This derived class must support checking pref types");
+
+  nsAutoCString prefName(mPrefName);
+  prefName.AppendLiteral(".prompt.testing");
+  if (Preferences::GetBool(PromiseFlatCString(prefName).get(), false)) {
+    prefName.AppendLiteral(".allow");
+    if (Preferences::GetBool(PromiseFlatCString(prefName).get(), true)) {
+      return PromptResult::Granted;
+    }
+    return PromptResult::Denied;
+  }
+
+  return PromptResult::Pending;
+}
+
+nsresult
+ContentPermissionRequestBase::ShowPrompt(
+    ContentPermissionRequestBase::PromptResult& aResult)
+{
+  aResult = CheckPromptPrefs();
+
+  if (aResult != PromptResult::Pending) {
+    return NS_OK;
+  }
+
+  return nsContentPermissionUtils::AskPermission(this, mWindow);
+}
+
+class RequestPromptEvent : public Runnable
+{
+public:
+  RequestPromptEvent(ContentPermissionRequestBase* aRequest,
+                     nsPIDOMWindowInner* aWindow)
+    : mozilla::Runnable("RequestPromptEvent")
+    , mRequest(aRequest)
+    , mWindow(aWindow)
+  {
+  }
+
+  NS_IMETHOD Run() override
+  {
+    nsContentPermissionUtils::AskPermission(mRequest, mWindow);
+    return NS_OK;
+  }
+
+private:
+  RefPtr<ContentPermissionRequestBase> mRequest;
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+};
+
+class RequestAllowEvent : public Runnable
+{
+public:
+  RequestAllowEvent(bool allow, ContentPermissionRequestBase* request)
+    : mozilla::Runnable("RequestAllowEvent")
+    , mAllow(allow)
+    , mRequest(request)
+  {
+  }
+
+  NS_IMETHOD Run() override {
+    if (mAllow) {
+      mRequest->Allow(JS::UndefinedHandleValue);
+    } else {
+      mRequest->Cancel();
+    }
+    return NS_OK;
+  }
+
+private:
+  bool mAllow;
+  RefPtr<ContentPermissionRequestBase> mRequest;
+};
+
+void
+ContentPermissionRequestBase::RequestDelayedTask(nsIEventTarget* aTarget,
+    ContentPermissionRequestBase::DelayedTaskType aType)
+{
+  nsCOMPtr<nsIRunnable> r;
+  switch (aType) {
+  case DelayedTaskType::Allow:
+    r = new RequestAllowEvent(true, this);
+    break;
+  case DelayedTaskType::Deny:
+    r = new RequestAllowEvent(false, this);
+    break;
+  default:
+    r = new RequestPromptEvent(this, mWindow);
+    break;
+  }
+
+  aTarget->Dispatch(r.forget());
+}
+
 } // namespace dom
 } // namespace mozilla
 
 NS_IMPL_ISUPPORTS(nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy,
                   nsIContentPermissionRequester)
 
 NS_IMETHODIMP
 nsContentPermissionRequestProxy::nsContentPermissionRequesterProxy
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -107,16 +107,61 @@ public:
 
 private:
   virtual ~nsContentPermissionRequester();
 
   nsWeakPtr mWindow;
   RefPtr<VisibilityChangeListener> mListener;
 };
 
+class ContentPermissionRequestBase : public nsIContentPermissionRequest
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_CYCLE_COLLECTION_CLASS(ContentPermissionRequestBase)
+
+  NS_IMETHOD GetTypes(nsIArray **aTypes) override;
+  NS_IMETHOD GetPrincipal(nsIPrincipal **aPrincipal) override;
+  NS_IMETHOD GetWindow(mozIDOMWindow **aWindow) override;
+  NS_IMETHOD GetElement(mozilla::dom::Element **aElement) override;
+  NS_IMETHOD GetIsHandlingUserInput(bool *aIsHandlingUserInput) override;
+  NS_IMETHOD GetRequester(nsIContentPermissionRequester **aRequester) override;
+  // Overrides for Allow() and Cancel() aren't provided by this class.
+  // That is the responsibility of the subclasses.
+
+  enum class PromptResult {
+    Granted,
+    Denied,
+    Pending,
+  };
+  nsresult ShowPrompt(PromptResult& aResult);
+
+  PromptResult CheckPromptPrefs();
+
+  enum class DelayedTaskType {
+    Allow,
+    Deny,
+    Request,
+  };
+  void RequestDelayedTask(nsIEventTarget* aTarget, DelayedTaskType aType);
+
+protected:
+  ContentPermissionRequestBase(nsIPrincipal* aPrincipal, bool aIsHandlingUserInput,
+                               nsPIDOMWindowInner* aWindow, const nsACString& aPrefName,
+                               const nsACString& aType);
+  virtual ~ContentPermissionRequestBase() = default;
+
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsPIDOMWindowInner> mWindow;
+  nsCOMPtr<nsIContentPermissionRequester> mRequester;
+  nsCString mPrefName;
+  nsCString mType;
+  bool mIsHandlingUserInput;
+};
+
 } // namespace dom
 } // namespace mozilla
 
 using mozilla::dom::ContentPermissionRequestParent;
 
 class nsContentPermissionRequestProxy : public nsIContentPermissionRequest
 {
 public:
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -60,46 +60,50 @@ class nsIPrincipal;
 // default policy.
 #define PREF_GEO_SECURITY_ALLOWINSECURE "geo.security.allowinsecure"
 
 using mozilla::Unused;          // <snicker>
 using namespace mozilla;
 using namespace mozilla::dom;
 
 class nsGeolocationRequest final
- : public nsIContentPermissionRequest
+ : public ContentPermissionRequestBase
  , public nsIGeolocationUpdate
  , public SupportsWeakPtr<nsGeolocationRequest>
 {
  public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIGEOLOCATIONUPDATE
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(nsGeolocationRequest, ContentPermissionRequestBase)
 
   nsGeolocationRequest(Geolocation* aLocator,
                        GeoPositionCallback aCallback,
                        GeoPositionErrorCallback aErrorCallback,
                        UniquePtr<PositionOptions>&& aOptions,
                        uint8_t aProtocolType,
                        nsIEventTarget* aMainThreadTarget,
                        bool aWatchPositionRequest = false,
                        bool aIsHandlingUserInput = false,
                        int32_t aWatchId = 0);
 
+  // nsIContentPermissionRequest
+  NS_IMETHOD Cancel(void) override;
+  NS_IMETHOD Allow(JS::HandleValue choices) override;
+
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
 
   void Shutdown();
 
   void SendLocation(nsIDOMGeoPosition* aLocation);
   bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
   void SetTimeoutTimer();
   void StopTimeoutTimer();
   void NotifyErrorAndShutdown(uint16_t);
+  using ContentPermissionRequestBase::GetPrincipal;
   nsIPrincipal* GetPrincipal();
 
   bool IsWatch() { return mIsWatchPositionRequest; }
   int32_t WatchId() { return mWatchId; }
  private:
   virtual ~nsGeolocationRequest();
 
   class TimerCallbackHolder final : public nsITimerCallback
@@ -127,17 +131,16 @@ class nsGeolocationRequest final
   void Notify();
 
   bool mIsWatchPositionRequest;
 
   nsCOMPtr<nsITimer> mTimeoutTimer;
   GeoPositionCallback mCallback;
   GeoPositionErrorCallback mErrorCallback;
   UniquePtr<PositionOptions> mOptions;
-  bool mIsHandlingUserInput;
 
   RefPtr<Geolocation> mLocator;
 
   int32_t mWatchId;
   bool mShutdown;
   nsCOMPtr<nsIContentPermissionRequester> mRequester;
   uint8_t mProtocolType;
   nsCOMPtr<nsIEventTarget> mMainThreadTarget;
@@ -150,62 +153,16 @@ CreatePositionOptionsCopy(const Position
 
   geoOptions->mEnableHighAccuracy = aOptions.mEnableHighAccuracy;
   geoOptions->mMaximumAge = aOptions.mMaximumAge;
   geoOptions->mTimeout = aOptions.mTimeout;
 
   return geoOptions;
 }
 
-class RequestPromptEvent : public Runnable
-{
-public:
-  RequestPromptEvent(nsGeolocationRequest* aRequest, nsWeakPtr aWindow)
-    : mozilla::Runnable("RequestPromptEvent")
-    , mRequest(aRequest)
-    , mWindow(aWindow)
-  {
-  }
-
-  NS_IMETHOD Run() override
-  {
-    nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
-    nsContentPermissionUtils::AskPermission(mRequest, window);
-    return NS_OK;
-  }
-
-private:
-  RefPtr<nsGeolocationRequest> mRequest;
-  nsWeakPtr mWindow;
-};
-
-class RequestAllowEvent : public Runnable
-{
-public:
-  RequestAllowEvent(int allow, nsGeolocationRequest* request)
-    : mozilla::Runnable("RequestAllowEvent")
-    , mAllow(allow)
-    , mRequest(request)
-  {
-  }
-
-  NS_IMETHOD Run() override {
-    if (mAllow) {
-      mRequest->Allow(JS::UndefinedHandleValue);
-    } else {
-      mRequest->Cancel();
-    }
-    return NS_OK;
-  }
-
-private:
-  bool mAllow;
-  RefPtr<nsGeolocationRequest> mRequest;
-};
-
 class RequestSendLocationEvent : public Runnable
 {
 public:
   RequestSendLocationEvent(nsIDOMGeoPosition* aPosition,
                            nsGeolocationRequest* aRequest)
     : mozilla::Runnable("RequestSendLocationEvent")
     , mPosition(aPosition)
     , mRequest(aRequest)
@@ -222,56 +179,72 @@ private:
   RefPtr<nsGeolocationRequest> mRequest;
   RefPtr<Geolocation> mLocator;
 };
 
 ////////////////////////////////////////////////////
 // nsGeolocationRequest
 ////////////////////////////////////////////////////
 
+static
+nsPIDOMWindowInner*
+ConvertWeakReferenceToWindow(nsIWeakReference* aWeakPtr)
+{
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(aWeakPtr);
+  // This isn't usually safe, but here we're just extracting a raw pointer in order
+  // to pass it to a base class constructor which will in turn convert it into a
+  // strong pointer for us.
+  nsPIDOMWindowInner* raw = window.get();
+  return raw;
+}
+
 nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
                                            GeoPositionCallback aCallback,
                                            GeoPositionErrorCallback aErrorCallback,
                                            UniquePtr<PositionOptions>&& aOptions,
                                            uint8_t aProtocolType,
                                            nsIEventTarget* aMainThreadTarget,
                                            bool aWatchPositionRequest,
                                            bool aIsHandlingUserInput,
                                            int32_t aWatchId)
-  : mIsWatchPositionRequest(aWatchPositionRequest),
+  : ContentPermissionRequestBase(aLocator->GetPrincipal(),
+                                 aIsHandlingUserInput,
+                                 ConvertWeakReferenceToWindow(aLocator->GetOwner()),
+                                 NS_LITERAL_CSTRING("geo"),
+                                 NS_LITERAL_CSTRING("geolocation")),
+    mIsWatchPositionRequest(aWatchPositionRequest),
     mCallback(std::move(aCallback)),
     mErrorCallback(std::move(aErrorCallback)),
     mOptions(std::move(aOptions)),
-    mIsHandlingUserInput(aIsHandlingUserInput),
     mLocator(aLocator),
     mWatchId(aWatchId),
     mShutdown(false),
     mProtocolType(aProtocolType),
     mMainThreadTarget(aMainThreadTarget)
 {
   if (nsCOMPtr<nsPIDOMWindowInner> win =
       do_QueryReferent(mLocator->GetOwner())) {
-    mRequester = new nsContentPermissionRequester(win);
   }
 }
 
 nsGeolocationRequest::~nsGeolocationRequest()
 {
   StopTimeoutTimer();
 }
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsGeolocationRequest)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIGeolocationUpdate)
-NS_INTERFACE_MAP_END
+NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(nsGeolocationRequest,
+                                                   ContentPermissionRequestBase,
+                                                   nsIGeolocationUpdate)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(nsGeolocationRequest)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(nsGeolocationRequest)
-NS_IMPL_CYCLE_COLLECTION(nsGeolocationRequest, mCallback, mErrorCallback, mLocator)
+NS_IMPL_ADDREF_INHERITED(nsGeolocationRequest,
+                         ContentPermissionRequestBase)
+NS_IMPL_RELEASE_INHERITED(nsGeolocationRequest,
+                          ContentPermissionRequestBase)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(nsGeolocationRequest, ContentPermissionRequestBase,
+                                   mCallback, mErrorCallback, mLocator)
 
 void
 nsGeolocationRequest::Notify()
 {
   SetTimeoutTimer();
   NotifyErrorAndShutdown(PositionError_Binding::TIMEOUT);
 }
 
@@ -283,62 +256,16 @@ nsGeolocationRequest::NotifyErrorAndShut
     Shutdown();
     mLocator->RemoveRequest(this);
   }
 
   NotifyError(aErrorCode);
 }
 
 NS_IMETHODIMP
-nsGeolocationRequest::GetPrincipal(nsIPrincipal * *aRequestingPrincipal)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
-
-  nsCOMPtr<nsIPrincipal> principal = mLocator->GetPrincipal();
-  principal.forget(aRequestingPrincipal);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsGeolocationRequest::GetTypes(nsIArray** aTypes)
-{
-  nsTArray<nsString> emptyOptions;
-  return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("geolocation"),
-                                                         emptyOptions,
-                                                         aTypes);
-}
-
-NS_IMETHODIMP
-nsGeolocationRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingWindow);
-
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mLocator->GetOwner());
-  window.forget(aRequestingWindow);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsGeolocationRequest::GetElement(Element** aRequestingElement)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingElement);
-  *aRequestingElement = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-nsGeolocationRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
-{
-  *aIsHandlingUserInput = mIsHandlingUserInput;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 nsGeolocationRequest::Cancel()
 {
   if (mRequester) {
     // Record the number of denied requests for regular web content.
     // This method is only called when the user explicitly denies the request,
     // and is not called when the page is simply unloaded, or similar.
     Telemetry::Accumulate(Telemetry::GEOLOCATION_REQUEST_GRANTED, mProtocolType);
   }
@@ -436,27 +363,16 @@ nsGeolocationRequest::Allow(JS::HandleVa
     mLocator->NotifyAllowedRequest(this);
   }
 
   SetTimeoutTimer();
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-nsGeolocationRequest::GetRequester(nsIContentPermissionRequester** aRequester)
-{
-  NS_ENSURE_ARG_POINTER(aRequester);
-
-  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
-  requester.forget(aRequester);
-
-  return NS_OK;
-}
-
 void
 nsGeolocationRequest::SetTimeoutTimer()
 {
   MOZ_ASSERT(!mShutdown, "set timeout after shutdown");
 
   StopTimeoutTimer();
 
   if (mOptions && mOptions->mTimeout != 0 && mOptions->mTimeout != 0x7fffffff) {
@@ -1235,18 +1151,17 @@ Geolocation::GetCurrentPosition(GeoPosit
   nsIEventTarget* target = MainThreadTarget(this);
   RefPtr<nsGeolocationRequest> request =
     new nsGeolocationRequest(this, std::move(callback), std::move(errorCallback),
                              std::move(options), static_cast<uint8_t>(mProtocolType), target,
                              false, EventStateManager::IsHandlingUserInput());
 
   if (!sGeoEnabled || ShouldBlockInsecureRequests() ||
       !FeaturePolicyBlocked()) {
-    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
-    target->Dispatch(ev.forget());
+    request->RequestDelayedTask(target, nsGeolocationRequest::DelayedTaskType::Deny);
     return NS_OK;
   }
 
   if (!mOwner && aCallerType != CallerType::System) {
     return NS_ERROR_FAILURE;
   }
 
   if (mOwner) {
@@ -1256,18 +1171,17 @@ Geolocation::GetCurrentPosition(GeoPosit
 
     return NS_OK;
   }
 
   if (aCallerType != CallerType::System) {
     return NS_ERROR_FAILURE;
   }
 
-  nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(true, request);
-  target->Dispatch(ev.forget());
+  request->RequestDelayedTask(target, nsGeolocationRequest::DelayedTaskType::Allow);
 
   return NS_OK;
 }
 
 int32_t
 Geolocation::WatchPosition(PositionCallback& aCallback,
                            PositionErrorCallback* aErrorCallback,
                            const PositionOptions& aOptions,
@@ -1319,18 +1233,17 @@ Geolocation::WatchPosition(GeoPositionCa
   RefPtr<nsGeolocationRequest> request =
     new nsGeolocationRequest(this, std::move(aCallback), std::move(aErrorCallback),
                              std::move(aOptions),
                              static_cast<uint8_t>(mProtocolType), target, true,
                              EventStateManager::IsHandlingUserInput(), watchId);
 
   if (!sGeoEnabled || ShouldBlockInsecureRequests() ||
       !FeaturePolicyBlocked()) {
-    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
-    target->Dispatch(ev.forget());
+    request->RequestDelayedTask(target, nsGeolocationRequest::DelayedTaskType::Deny);
     return watchId;
   }
 
   if (!mOwner && aCallerType != CallerType::System) {
     aRv.Throw(NS_ERROR_FAILURE);
     return -1;
   }
 
@@ -1416,25 +1329,27 @@ Geolocation::NotifyAllowedRequest(nsGeol
     mPendingCallbacks.AppendElement(aRequest);
   }
 }
 
 bool
 Geolocation::RegisterRequestWithPrompt(nsGeolocationRequest* request)
 {
   nsIEventTarget* target = MainThreadTarget(this);
-  if (Preferences::GetBool("geo.prompt.testing", false)) {
-    bool allow = Preferences::GetBool("geo.prompt.testing.allow", false);
-    nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(allow, request);
-    target->Dispatch(ev.forget());
+  ContentPermissionRequestBase::PromptResult pr = request->CheckPromptPrefs();
+  if (pr == ContentPermissionRequestBase::PromptResult::Granted) {
+    request->RequestDelayedTask(target, nsGeolocationRequest::DelayedTaskType::Allow);
+    return true;
+  }
+  if (pr == ContentPermissionRequestBase::PromptResult::Denied) {
+    request->RequestDelayedTask(target, nsGeolocationRequest::DelayedTaskType::Deny);
     return true;
   }
 
-  nsCOMPtr<nsIRunnable> ev  = new RequestPromptEvent(request, mOwner);
-  target->Dispatch(ev.forget());
+  request->RequestDelayedTask(target, nsGeolocationRequest::DelayedTaskType::Request);
   return true;
 }
 
 JSObject*
 Geolocation::WrapObject(JSContext *aCtx, JS::Handle<JSObject*> aGivenProto)
 {
   return Geolocation_Binding::Wrap(aCtx, this, aGivenProto);
 }
--- a/dom/html/AutoplayPermissionManager.cpp
+++ b/dom/html/AutoplayPermissionManager.cpp
@@ -34,29 +34,26 @@ AutoplayPermissionManager::RequestWithPr
   }
 
   nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
   if (!window) {
     return GenericPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR,
                                            __func__);
   }
 
-  nsCOMPtr<nsIContentPermissionRequest> request =
+  RefPtr<AutoplayPermissionRequest> request =
     AutoplayPermissionRequest::Create(nsGlobalWindowInner::Cast(window), this);
   if (!request) {
     return GenericPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR,
                                            __func__);
   }
 
   // Dispatch the request.
-  nsCOMPtr<nsIRunnable> f = NS_NewRunnableFunction(
-    "AutoplayPermissionManager::RequestWithPrompt", [window, request]() {
-      dom::nsContentPermissionUtils::AskPermission(request, window);
-    });
-  window->EventTargetFor(TaskCategory::Other)->Dispatch(f, NS_DISPATCH_NORMAL);
+  request->RequestDelayedTask(window->EventTargetFor(TaskCategory::Other),
+                              AutoplayPermissionRequest::DelayedTaskType::Request);
 
   mRequestDispatched = true;
   return mPromiseHolder.Ensure(__func__);
 }
 
 AutoplayPermissionManager::AutoplayPermissionManager(
   nsGlobalWindowInner* aWindow)
   : mWindow(do_GetWeakReference(aWindow))
--- a/dom/html/AutoplayPermissionRequest.cpp
+++ b/dom/html/AutoplayPermissionRequest.cpp
@@ -11,91 +11,41 @@
 
 extern mozilla::LazyLogModule gAutoplayPermissionLog;
 
 #define PLAY_REQUEST_LOG(msg, ...)                                             \
   MOZ_LOG(gAutoplayPermissionLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
 
 namespace mozilla {
 
-NS_IMPL_ISUPPORTS(AutoplayPermissionRequest, nsIContentPermissionRequest)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(AutoplayPermissionRequest,
+                                   ContentPermissionRequestBase)
+
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(AutoplayPermissionRequest,
+                                               ContentPermissionRequestBase)
 
 AutoplayPermissionRequest::AutoplayPermissionRequest(
   AutoplayPermissionManager* aManager,
   nsGlobalWindowInner* aWindow,
   nsIPrincipal* aNodePrincipal,
   nsIEventTarget* aMainThreadTarget)
-  : mManager(aManager)
-  , mWindow(do_GetWeakReference(aWindow))
-  , mNodePrincipal(aNodePrincipal)
+  : ContentPermissionRequestBase(aNodePrincipal, false, aWindow,
+                                 NS_LITERAL_CSTRING(""), // No testing pref used in this class
+                                 NS_LITERAL_CSTRING("autoplay-media"))
+  , mManager(aManager)
   , mMainThreadTarget(aMainThreadTarget)
-  , mRequester(new dom::nsContentPermissionRequester(aWindow))
 {
-  MOZ_ASSERT(mNodePrincipal);
 }
 
 AutoplayPermissionRequest::~AutoplayPermissionRequest()
 {
   Cancel();
 }
 
 NS_IMETHODIMP
-AutoplayPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
-
-  nsCOMPtr<nsIPrincipal> principal = mNodePrincipal;
-  principal.forget(aRequestingPrincipal);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AutoplayPermissionRequest::GetTypes(nsIArray** aTypes)
-{
-  NS_ENSURE_ARG_POINTER(aTypes);
-
-  nsTArray<nsString> emptyOptions;
-  return dom::nsContentPermissionUtils::CreatePermissionArray(
-    NS_LITERAL_CSTRING("autoplay-media"),
-    emptyOptions,
-    aTypes);
-}
-
-NS_IMETHODIMP
-AutoplayPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingWindow);
-
-  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
-  if (!window) {
-    return NS_ERROR_FAILURE;
-  }
-  window.forget(aRequestingWindow);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AutoplayPermissionRequest::GetElement(dom::Element** aRequestingElement)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingElement);
-  *aRequestingElement = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-AutoplayPermissionRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
-{
-  NS_ENSURE_ARG_POINTER(aIsHandlingUserInput);
-  *aIsHandlingUserInput = false;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 AutoplayPermissionRequest::Cancel()
 {
   if (mManager) {
     mManager->DenyPlayRequestIfExists();
     // Clear reference to manager, so we can't double report a result.
     // This could happen in particular if we call Cancel() in the destructor.
     mManager = nullptr;
   }
@@ -109,28 +59,16 @@ AutoplayPermissionRequest::Allow(JS::Han
     mManager->ApprovePlayRequestIfExists();
     // Clear reference to manager, so we can't double report a result.
     // This could happen in particular if we call Cancel() in the destructor.
     mManager = nullptr;
   }
   return NS_OK;
 }
 
-NS_IMETHODIMP
-AutoplayPermissionRequest::GetRequester(
-  nsIContentPermissionRequester** aRequester)
-{
-  NS_ENSURE_ARG_POINTER(aRequester);
-
-  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
-  requester.forget(aRequester);
-
-  return NS_OK;
-}
-
 already_AddRefed<AutoplayPermissionRequest>
 AutoplayPermissionRequest::Create(nsGlobalWindowInner* aWindow,
                                   AutoplayPermissionManager* aManager)
 {
   if (!aWindow || !aWindow->GetPrincipal() ||
       !aWindow->EventTargetFor(TaskCategory::Other)) {
     return nullptr;
   }
--- a/dom/html/AutoplayPermissionRequest.h
+++ b/dom/html/AutoplayPermissionRequest.h
@@ -22,36 +22,38 @@ class AutoplayPermissionManager;
 // reason (tab closed, user presses ESC, navigation, etc), the permission UI
 // code will drop its reference to the AutoplayPermissionRequest and it will
 // be destroyed. The AutoplayPermissionRequest keeps a weak reference to
 // the AutoplayPermissionManager. If the AutoplayPermissionManager is still
 // alive when the AutoplayPermissionRequest's destructor runs, the
 // AutoplayPermissionRequest's destructor calls the AutoplayPermissionManager
 // back with a cancel operation. Thus the AutoplayPermissionManager can
 // guarantee to always approve or cancel requests to play.
-class AutoplayPermissionRequest final : public nsIContentPermissionRequest
+class AutoplayPermissionRequest final : public dom::ContentPermissionRequestBase
 {
 public:
-  NS_DECL_ISUPPORTS
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(AutoplayPermissionRequest,
+                                           ContentPermissionRequestBase)
+
+  // nsIContentPermissionRequest
+  NS_IMETHOD Cancel(void) override;
+  NS_IMETHOD Allow(JS::HandleValue choices) override;
 
   static already_AddRefed<AutoplayPermissionRequest> Create(
     nsGlobalWindowInner* aWindow,
     AutoplayPermissionManager* aManager);
 
 private:
   AutoplayPermissionRequest(AutoplayPermissionManager* aManager,
                             nsGlobalWindowInner* aWindow,
                             nsIPrincipal* aNodePrincipal,
                             nsIEventTarget* aMainThreadTarget);
   ~AutoplayPermissionRequest();
 
   WeakPtr<AutoplayPermissionManager> mManager;
 
-  nsWeakPtr mWindow;
-  nsCOMPtr<nsIPrincipal> mNodePrincipal;
   nsCOMPtr<nsIEventTarget> mMainThreadTarget;
-  nsCOMPtr<nsIContentPermissionRequester> mRequester;
 };
 
 } // namespace mozilla
 
 #endif // AutoplayPermissionRequest_h_
--- a/dom/midi/MIDIPermissionRequest.cpp
+++ b/dom/midi/MIDIPermissionRequest.cpp
@@ -9,100 +9,60 @@
 #include "nsIGlobalObject.h"
 #include "mozilla/Preferences.h"
 #include "nsContentUtils.h"
 
 //-------------------------------------------------
 // MIDI Permission Requests
 //-------------------------------------------------
 
-NS_IMPL_CYCLE_COLLECTION(MIDIPermissionRequest, mWindow, mPromise)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(MIDIPermissionRequest,
+                                   ContentPermissionRequestBase,
+                                   mPromise)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(MIDIPermissionRequest)
-NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
-NS_INTERFACE_MAP_ENTRY(nsIRunnable)
-NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
-NS_INTERFACE_MAP_END
+NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(MIDIPermissionRequest,
+                                                   ContentPermissionRequestBase,
+                                                   nsIRunnable)
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(MIDIPermissionRequest)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(MIDIPermissionRequest)
+NS_IMPL_ADDREF_INHERITED(MIDIPermissionRequest,
+                         ContentPermissionRequestBase)
+NS_IMPL_RELEASE_INHERITED(MIDIPermissionRequest,
+                          ContentPermissionRequestBase)
 
 MIDIPermissionRequest::MIDIPermissionRequest(nsPIDOMWindowInner* aWindow,
                                              Promise* aPromise,
                                              const MIDIOptions& aOptions)
-: mWindow(aWindow),
-  mPromise(aPromise),
-  mNeedsSysex(aOptions.mSysex),
-  mRequester(new nsContentPermissionRequester(mWindow))
+  : ContentPermissionRequestBase(aWindow->GetDoc()->NodePrincipal(),
+                                 true, aWindow,
+                                 NS_LITERAL_CSTRING(""), // We check prefs in a custom way here
+                                 NS_LITERAL_CSTRING("midi")),
+    mPromise(aPromise),
+    mNeedsSysex(aOptions.mSysex)
 {
   MOZ_ASSERT(aWindow);
   MOZ_ASSERT(aPromise, "aPromise should not be null!");
   MOZ_ASSERT(aWindow->GetDoc());
   mPrincipal = aWindow->GetDoc()->NodePrincipal();
   MOZ_ASSERT(mPrincipal);
 }
 
-MIDIPermissionRequest::~MIDIPermissionRequest()
-{
-}
-
-NS_IMETHODIMP
-MIDIPermissionRequest::GetIsHandlingUserInput(bool* aHandlingInput)
-{
-  *aHandlingInput = true;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MIDIPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
-{
-  NS_ENSURE_ARG_POINTER(aRequester);
-  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
-  requester.forget(aRequester);
-  return NS_OK;
-}
-
 NS_IMETHODIMP
 MIDIPermissionRequest::GetTypes(nsIArray** aTypes)
 {
   NS_ENSURE_ARG_POINTER(aTypes);
   nsTArray<nsString> options;
   if (mNeedsSysex) {
     options.AppendElement(NS_LITERAL_STRING("sysex"));
   }
-  return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("midi"),
+  return nsContentPermissionUtils::CreatePermissionArray(mType,
                                                          options,
                                                          aTypes);
 }
 
 NS_IMETHODIMP
-MIDIPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
-  NS_IF_ADDREF(*aRequestingPrincipal = mPrincipal);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MIDIPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingWindow);
-  NS_IF_ADDREF(*aRequestingWindow = mWindow);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-MIDIPermissionRequest::GetElement(Element** aRequestingElement)
-{
-  NS_ENSURE_ARG_POINTER(aRequestingElement);
-  *aRequestingElement = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 MIDIPermissionRequest::Cancel()
 {
   mPromise->MaybeReject(NS_ERROR_DOM_SECURITY_ERR);
   return NS_OK;
 }
 
 NS_IMETHODIMP
 MIDIPermissionRequest::Allow(JS::HandleValue aChoices)
--- a/dom/midi/MIDIPermissionRequest.h
+++ b/dom/midi/MIDIPermissionRequest.h
@@ -13,41 +13,38 @@ namespace mozilla {
 namespace dom {
 
 struct MIDIOptions;
 
 /**
  * Handles permission dialog management when requesting MIDI permissions.
  */
 class MIDIPermissionRequest final
-  : public nsIContentPermissionRequest,
+  : public ContentPermissionRequestBase,
     public nsIRunnable
 {
 public:
   MIDIPermissionRequest(nsPIDOMWindowInner* aWindow,
                         Promise* aPromise,
                         const MIDIOptions& aOptions);
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
+  NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_NSIRUNNABLE
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(MIDIPermissionRequest,
-                                           nsIContentPermissionRequest)
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(MIDIPermissionRequest,
+                                           ContentPermissionRequestBase)
+  // nsIContentPermissionRequest
+  NS_IMETHOD Cancel(void) override;
+  NS_IMETHOD Allow(JS::HandleValue choices) override;
+  NS_IMETHOD GetTypes(nsIArray** aTypes) override;
 
 private:
-  ~MIDIPermissionRequest();
+  ~MIDIPermissionRequest() = default;
 
-  // Owning window for the permissions requests
-  nsCOMPtr<nsPIDOMWindowInner> mWindow;
-  // Principal for request
-  nsCOMPtr<nsIPrincipal> mPrincipal;
   // Promise for returning MIDIAccess on request success
   RefPtr<Promise> mPromise;
   // True if sysex permissions should be requested
   bool mNeedsSysex;
-  // Requester object
-  nsCOMPtr<nsIContentPermissionRequester> mRequester;
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif //mozilla_dom_MIDIPermissionRequest_h
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -220,58 +220,57 @@ public:
 
     rv = notificationStorage->Get(mOrigin, mTag, mCallback);
     //XXXnsm Is it guaranteed mCallback will be called in case of failure?
     Unused << NS_WARN_IF(NS_FAILED(rv));
     return rv;
   }
 };
 
-class NotificationPermissionRequest : public nsIContentPermissionRequest,
+class NotificationPermissionRequest : public ContentPermissionRequestBase,
                                       public nsIRunnable,
                                       public nsINamed
 {
 public:
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
   NS_DECL_NSIRUNNABLE
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
-                                           nsIContentPermissionRequest)
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(NotificationPermissionRequest,
+                                           ContentPermissionRequestBase)
+
+  // nsIContentPermissionRequest
+  NS_IMETHOD Cancel(void) override;
+  NS_IMETHOD Allow(JS::HandleValue choices) override;
 
   NotificationPermissionRequest(nsIPrincipal* aPrincipal, bool aIsHandlingUserInput,
                                 nsPIDOMWindowInner* aWindow, Promise* aPromise,
                                 NotificationPermissionCallback* aCallback)
-    : mPrincipal(aPrincipal), mWindow(aWindow),
+    : ContentPermissionRequestBase(aPrincipal, aIsHandlingUserInput,
+                                   aWindow, NS_LITERAL_CSTRING("notification"),
+                                   NS_LITERAL_CSTRING("desktop-notification")),
       mPermission(NotificationPermission::Default),
       mPromise(aPromise),
-      mCallback(aCallback),
-      mIsHandlingUserInput(aIsHandlingUserInput)
+      mCallback(aCallback)
   {
     MOZ_ASSERT(aPromise);
-    mRequester = new nsContentPermissionRequester(mWindow);
   }
 
   NS_IMETHOD GetName(nsACString& aName) override
   {
     aName.AssignLiteral("NotificationPermissionRequest");
     return NS_OK;
   }
 
 protected:
-  virtual ~NotificationPermissionRequest() {}
+  ~NotificationPermissionRequest() = default;
 
   nsresult ResolvePromise();
   nsresult DispatchResolvePromise();
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsPIDOMWindowInner> mWindow;
   NotificationPermission mPermission;
   RefPtr<Promise> mPromise;
   RefPtr<NotificationPermissionCallback> mCallback;
-  nsCOMPtr<nsIContentPermissionRequester> mRequester;
-  bool mIsHandlingUserInput;
 };
 
 namespace {
 class ReleaseNotificationControlRunnable final : public MainThreadWorkerControlRunnable
 {
   Notification* mNotification;
 
 public:
@@ -533,28 +532,27 @@ protected:
   virtual ~NotificationTask() {}
 
   UniquePtr<NotificationRef> mNotificationRef;
   NotificationAction mAction;
 };
 
 uint32_t Notification::sCount = 0;
 
-NS_IMPL_CYCLE_COLLECTION(NotificationPermissionRequest, mWindow, mPromise,
-                                                        mCallback)
+NS_IMPL_CYCLE_COLLECTION_INHERITED(NotificationPermissionRequest,
+                                   ContentPermissionRequestBase,
+                                   mCallback)
+NS_IMPL_ADDREF_INHERITED(NotificationPermissionRequest,
+                         ContentPermissionRequestBase)
+NS_IMPL_RELEASE_INHERITED(NotificationPermissionRequest,
+                          ContentPermissionRequestBase)
 
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(NotificationPermissionRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIRunnable)
-  NS_INTERFACE_MAP_ENTRY(nsINamed)
-  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIContentPermissionRequest)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTING_ADDREF(NotificationPermissionRequest)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(NotificationPermissionRequest)
+NS_IMPL_QUERY_INTERFACE_CYCLE_COLLECTION_INHERITED(NotificationPermissionRequest,
+                                                   ContentPermissionRequestBase,
+                                                   nsIRunnable, nsINamed)
 
 NS_IMETHODIMP
 NotificationPermissionRequest::Run()
 {
   if (nsContentUtils::IsSystemPrincipal(mPrincipal)) {
     mPermission = NotificationPermission::Granted;
   } else {
     // File are automatically granted permission.
@@ -565,62 +563,41 @@ NotificationPermissionRequest::Run()
       bool isFile;
       uri->SchemeIs("file", &isFile);
       if (isFile) {
         mPermission = NotificationPermission::Granted;
       }
     }
   }
 
-  // Grant permission if pref'ed on.
-  if (Preferences::GetBool("notification.prompt.testing", false)) {
-    if (Preferences::GetBool("notification.prompt.testing.allow", true)) {
-      mPermission = NotificationPermission::Granted;
-    } else {
-      mPermission = NotificationPermission::Denied;
-    }
+  // We can't call ShowPrompt() directly here since our logic for determining
+  // whether to display a prompt depends on the checks above as well as the
+  // result of CheckPromptPrefs().  So we have to manually check the prompt
+  // prefs and decide what to do based on that.
+  PromptResult pr = CheckPromptPrefs();
+  switch (pr) {
+  case PromptResult::Granted:
+    mPermission = NotificationPermission::Granted;
+    break;
+  case PromptResult::Denied:
+    mPermission = NotificationPermission::Denied;
+    break;
+  default:
+    // ignore
+    break;
   }
 
   if (mPermission != NotificationPermission::Default) {
     return DispatchResolvePromise();
   }
 
   return nsContentPermissionUtils::AskPermission(this, mWindow);
 }
 
 NS_IMETHODIMP
-NotificationPermissionRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
-{
-  NS_ADDREF(*aRequestingPrincipal = mPrincipal);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-NotificationPermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
-{
-  NS_ADDREF(*aRequestingWindow = mWindow);
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-NotificationPermissionRequest::GetElement(Element** aElement)
-{
-  NS_ENSURE_ARG_POINTER(aElement);
-  *aElement = nullptr;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-NotificationPermissionRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
-{
-  *aIsHandlingUserInput = mIsHandlingUserInput;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
 NotificationPermissionRequest::Cancel()
 {
   // `Cancel` is called if the user denied permission or dismissed the
   // permission request. To distinguish between the two, we set the
   // permission to "default" and query the permission manager in
   // `ResolvePromise`.
   mPermission = NotificationPermission::Default;
   return DispatchResolvePromise();
@@ -630,26 +607,16 @@ NS_IMETHODIMP
 NotificationPermissionRequest::Allow(JS::HandleValue aChoices)
 {
   MOZ_ASSERT(aChoices.isUndefined());
 
   mPermission = NotificationPermission::Granted;
   return DispatchResolvePromise();
 }
 
-NS_IMETHODIMP
-NotificationPermissionRequest::GetRequester(nsIContentPermissionRequester** aRequester)
-{
-  NS_ENSURE_ARG_POINTER(aRequester);
-
-  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
-  requester.forget(aRequester);
-  return NS_OK;
-}
-
 inline nsresult
 NotificationPermissionRequest::DispatchResolvePromise()
 {
   nsCOMPtr<nsIRunnable> resolver =
     NewRunnableMethod("NotificationPermissionRequest::DispatchResolvePromise",
                       this, &NotificationPermissionRequest::ResolvePromise);
   if (nsIEventTarget* target = mWindow->EventTargetFor(TaskCategory::Other)) {
     return target->Dispatch(resolver.forget(), nsIEventTarget::DISPATCH_NORMAL);
@@ -670,25 +637,16 @@ NotificationPermissionRequest::ResolvePr
     ErrorResult error;
     mCallback->Call(mPermission, error);
     rv = error.StealNSResult();
   }
   mPromise->MaybeResolve(mPermission);
   return rv;
 }
 
-NS_IMETHODIMP
-NotificationPermissionRequest::GetTypes(nsIArray** aTypes)
-{
-  nsTArray<nsString> emptyOptions;
-  return nsContentPermissionUtils::CreatePermissionArray(NS_LITERAL_CSTRING("desktop-notification"),
-                                                         emptyOptions,
-                                                         aTypes);
-}
-
 NS_IMPL_ISUPPORTS(NotificationTelemetryService, nsIObserver)
 
 NotificationTelemetryService::NotificationTelemetryService()
   : mDNDRecorded(false)
 {}
 
 NotificationTelemetryService::~NotificationTelemetryService()
 {}
--- a/dom/quota/StorageManager.cpp
+++ b/dom/quota/StorageManager.cpp
@@ -160,51 +160,47 @@ public:
   MainThreadRun() override;
 };
 
 /*******************************************************************************
  * PersistentStoragePermissionRequest
  ******************************************************************************/
 
 class PersistentStoragePermissionRequest final
-  : public nsIContentPermissionRequest
+  : public ContentPermissionRequestBase
 {
-  nsCOMPtr<nsIPrincipal> mPrincipal;
-  nsCOMPtr<nsPIDOMWindowInner> mWindow;
-  bool mIsHandlingUserInput;
   RefPtr<Promise> mPromise;
-  nsCOMPtr<nsIContentPermissionRequester> mRequester;
 
 public:
   PersistentStoragePermissionRequest(nsIPrincipal* aPrincipal,
                                      nsPIDOMWindowInner* aWindow,
                                      bool aIsHandlingUserInput,
                                      Promise* aPromise)
-    : mPrincipal(aPrincipal)
-    , mWindow(aWindow)
-    , mIsHandlingUserInput(aIsHandlingUserInput)
+    : ContentPermissionRequestBase(aPrincipal, aIsHandlingUserInput, aWindow,
+                                   NS_LITERAL_CSTRING("dom.storageManager"),
+                                   NS_LITERAL_CSTRING("persistent-storage"))
     , mPromise(aPromise)
   {
-    MOZ_ASSERT(aPrincipal);
     MOZ_ASSERT(aWindow);
     MOZ_ASSERT(aPromise);
-
-    mRequester = new nsContentPermissionRequester(mWindow);
   }
 
   nsresult
   Start();
 
-  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_NSICONTENTPERMISSIONREQUEST
-  NS_DECL_CYCLE_COLLECTION_CLASS(PersistentStoragePermissionRequest)
+  NS_DECL_ISUPPORTS_INHERITED
+  NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PersistentStoragePermissionRequest,
+                                           ContentPermissionRequestBase)
+
+  // nsIContentPermissionRequest
+  NS_IMETHOD Cancel(void) override;
+  NS_IMETHOD Allow(JS::HandleValue choices) override;
 
 private:
-  ~PersistentStoragePermissionRequest()
-  { }
+  ~PersistentStoragePermissionRequest() = default;
 };
 
 nsresult
 GetUsageForPrincipal(nsIPrincipal* aPrincipal,
                      nsIQuotaUsageCallback* aCallback,
                      nsIQuotaUsageRequest** aRequest)
 {
   MOZ_ASSERT(aPrincipal);
@@ -684,79 +680,36 @@ PersistedWorkerMainThreadRunnable::MainT
   return true;
 }
 
 nsresult
 PersistentStoragePermissionRequest::Start()
 {
   MOZ_ASSERT(NS_IsMainThread());
 
-  // Grant permission if pref'ed on.
-  if (Preferences::GetBool("dom.storageManager.prompt.testing", false)) {
-    if (Preferences::GetBool("dom.storageManager.prompt.testing.allow",
-                             false)) {
-      return Allow(JS::UndefinedHandleValue);
-    }
-
+  PromptResult pr;
+  nsresult rv = ShowPrompt(pr);
+  if (NS_WARN_IF(NS_FAILED(rv))) {
+    return rv;
+  }
+  if (pr == PromptResult::Granted) {
+    return Allow(JS::UndefinedHandleValue);
+  }
+  if (pr == PromptResult::Denied) {
     return Cancel();
   }
 
   return nsContentPermissionUtils::AskPermission(this, mWindow);
 }
 
-NS_IMPL_CYCLE_COLLECTING_ADDREF(PersistentStoragePermissionRequest)
-NS_IMPL_CYCLE_COLLECTING_RELEASE(PersistentStoragePermissionRequest)
-
-NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PersistentStoragePermissionRequest)
-  NS_INTERFACE_MAP_ENTRY(nsIContentPermissionRequest)
-  NS_INTERFACE_MAP_ENTRY(nsISupports)
-NS_INTERFACE_MAP_END
-
-NS_IMPL_CYCLE_COLLECTION(PersistentStoragePermissionRequest, mWindow, mPromise)
-
-NS_IMETHODIMP
-PersistentStoragePermissionRequest::GetPrincipal(nsIPrincipal** aPrincipal)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aPrincipal);
-  MOZ_ASSERT(mPrincipal);
-
-  NS_ADDREF(*aPrincipal = mPrincipal);
-
-  return NS_OK;
-}
+NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(PersistentStoragePermissionRequest,
+                                               ContentPermissionRequestBase)
 
-NS_IMETHODIMP
-PersistentStoragePermissionRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
-{
-  *aIsHandlingUserInput = mIsHandlingUserInput;
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PersistentStoragePermissionRequest::GetWindow(mozIDOMWindow** aRequestingWindow)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aRequestingWindow);
-  MOZ_ASSERT(mWindow);
-
-  NS_ADDREF(*aRequestingWindow = mWindow);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PersistentStoragePermissionRequest::GetElement(Element** aElement)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aElement);
-
-  *aElement = nullptr;
-  return NS_OK;
-}
+NS_IMPL_CYCLE_COLLECTION_INHERITED(PersistentStoragePermissionRequest, ContentPermissionRequestBase,
+                                   mPromise)
 
 NS_IMETHODIMP
 PersistentStoragePermissionRequest::Cancel()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mPromise);
 
   RefPtr<RequestResolver> resolver =
@@ -787,42 +740,16 @@ PersistentStoragePermissionRequest::Allo
     return rv;
   }
 
   MOZ_ALWAYS_SUCCEEDS(request->SetCallback(resolver));
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-PersistentStoragePermissionRequest::GetRequester(
-                                     nsIContentPermissionRequester** aRequester)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  MOZ_ASSERT(aRequester);
-
-  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
-  requester.forget(aRequester);
-
-  return NS_OK;
-}
-
-NS_IMETHODIMP
-PersistentStoragePermissionRequest::GetTypes(nsIArray** aTypes)
-{
-  MOZ_ASSERT(aTypes);
-
-  nsTArray<nsString> emptyOptions;
-
-  return nsContentPermissionUtils::CreatePermissionArray(
-                                       NS_LITERAL_CSTRING("persistent-storage"),
-                                       emptyOptions,
-                                       aTypes);
-}
-
 /*******************************************************************************
  * StorageManager
  ******************************************************************************/
 
 StorageManager::StorageManager(nsIGlobalObject* aGlobal)
   : mOwner(aGlobal)
 {
   MOZ_ASSERT(aGlobal);