Bug 1345424 - Add an isHandlingUserInput attribute to permission requests. r=baku,smaug
authorJohann Hofmann <jhofmann@mozilla.com>
Mon, 20 Feb 2017 20:46:39 +0100
changeset 394004 6306f81cc2956ad0deddd0e9566dafd88a2157c3
parent 394003 5a352ef05045d2a2c9a228e11431a20cc24f0a51
child 394005 bde36d0fa2ebcdad6be225cedcf14d7c81d6d964
push id97798
push usertoros@mozilla.com
push dateWed, 29 Nov 2017 00:40:28 +0000
treeherdermozilla-inbound@27c06ad38066 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbaku, smaug
bugs1345424
milestone59.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 1345424 - Add an isHandlingUserInput attribute to permission requests. r=baku,smaug MozReview-Commit-ID: 80TXjnxLMuT
dom/base/nsContentPermissionHelper.cpp
dom/base/nsContentPermissionHelper.h
dom/base/test/chrome/chrome.ini
dom/base/test/chrome/test_permission_isHandlingUserInput.xul
dom/geolocation/nsGeolocation.cpp
dom/interfaces/base/nsIContentPermissionPrompt.idl
dom/ipc/ContentChild.cpp
dom/ipc/ContentChild.h
dom/ipc/ContentParent.cpp
dom/ipc/ContentParent.h
dom/ipc/PContent.ipdl
dom/notification/DesktopNotification.cpp
dom/notification/DesktopNotification.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/Element.h"
 #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/Unused.h"
 #include "nsComponentManagerUtils.h"
 #include "nsArrayUtils.h"
 #include "nsIMutableArray.h"
 #include "nsContentPermissionHelper.h"
 #include "nsJSUtils.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
@@ -123,43 +124,47 @@ VisibilityChangeListener::GetCallback()
 
 namespace mozilla {
 namespace dom {
 
 class ContentPermissionRequestParent : public PContentPermissionRequestParent
 {
  public:
   ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
-                                 Element* element,
-                                 const IPC::Principal& principal);
+                                 Element* aElement,
+                                 const IPC::Principal& aPrincipal,
+                                 const bool aIsHandlingUserInput);
   virtual ~ContentPermissionRequestParent();
 
   bool IsBeingDestroyed();
 
   nsCOMPtr<nsIPrincipal> mPrincipal;
   nsCOMPtr<Element> mElement;
+  bool mIsHandlingUserInput;
   RefPtr<nsContentPermissionRequestProxy> mProxy;
   nsTArray<PermissionRequest> mRequests;
 
  private:
   virtual mozilla::ipc::IPCResult Recvprompt();
   virtual mozilla::ipc::IPCResult RecvNotifyVisibility(const bool& aIsVisible);
   virtual mozilla::ipc::IPCResult RecvDestroy();
   virtual void ActorDestroy(ActorDestroyReason why);
 };
 
 ContentPermissionRequestParent::ContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
                                                                Element* aElement,
-                                                               const IPC::Principal& aPrincipal)
+                                                               const IPC::Principal& aPrincipal,
+                                                               const bool aIsHandlingUserInput)
 {
   MOZ_COUNT_CTOR(ContentPermissionRequestParent);
 
   mPrincipal = aPrincipal;
   mElement   = aElement;
   mRequests  = aRequests;
+  mIsHandlingUserInput = aIsHandlingUserInput;
 }
 
 ContentPermissionRequestParent::~ContentPermissionRequestParent()
 {
   MOZ_COUNT_DTOR(ContentPermissionRequestParent);
 }
 
 mozilla::ipc::IPCResult
@@ -344,22 +349,23 @@ nsContentPermissionUtils::CreatePermissi
   types->AppendElement(permType);
   types.forget(aTypesArray);
 
   return NS_OK;
 }
 
 /* static */ PContentPermissionRequestParent*
 nsContentPermissionUtils::CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
-                                                               Element* element,
-                                                               const IPC::Principal& principal,
+                                                               Element* aElement,
+                                                               const IPC::Principal& aPrincipal,
+                                                               const bool aIsHandlingUserInput,
                                                                const TabId& aTabId)
 {
   PContentPermissionRequestParent* parent =
-    new ContentPermissionRequestParent(aRequests, element, principal);
+    new ContentPermissionRequestParent(aRequests, aElement, aPrincipal, aIsHandlingUserInput);
   ContentPermissionRequestParentMap()[parent] = aTabId;
 
   return parent;
 }
 
 /* static */ nsresult
 nsContentPermissionUtils::AskPermission(nsIContentPermissionRequest* aRequest,
                                         nsPIDOMWindowInner* aWindow)
@@ -383,24 +389,29 @@ nsContentPermissionUtils::AskPermission(
 
     nsTArray<PermissionRequest> permArray;
     ConvertArrayToPermissionRequest(typeArray, permArray);
 
     nsCOMPtr<nsIPrincipal> principal;
     rv = aRequest->GetPrincipal(getter_AddRefs(principal));
     NS_ENSURE_SUCCESS(rv, rv);
 
+    bool isHandlingUserInput;
+    rv = aRequest->GetIsHandlingUserInput(&isHandlingUserInput);
+    NS_ENSURE_SUCCESS(rv, rv);
+
     ContentChild::GetSingleton()->SetEventTargetForActor(
       req, aWindow->EventTargetFor(TaskCategory::Other));
 
     req->IPDLAddRef();
     ContentChild::GetSingleton()->SendPContentPermissionRequestConstructor(
       req,
       permArray,
       IPC::Principal(principal),
+      isHandlingUserInput,
       child->GetTabId());
     ContentPermissionRequestChildMap()[req.get()] = child->GetTabId();
 
     req->Sendprompt();
     return NS_OK;
   }
 
   // for chrome process
@@ -645,16 +656,27 @@ nsContentPermissionRequestProxy::GetElem
   }
 
   nsCOMPtr<nsIDOMElement> elem = do_QueryInterface(mParent->mElement);
   elem.forget(aRequestingElement);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsContentPermissionRequestProxy::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
+{
+  NS_ENSURE_ARG_POINTER(aIsHandlingUserInput);
+  if (mParent == nullptr) {
+    return NS_ERROR_FAILURE;
+  }
+  *aIsHandlingUserInput = mParent->mIsHandlingUserInput;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsContentPermissionRequestProxy::Cancel()
 {
   if (mParent == nullptr) {
     return NS_ERROR_FAILURE;
   }
 
   // Don't send out the delete message when the managing protocol (PBrowser) is
   // being destroyed and PContentPermissionRequest will soon be.
--- a/dom/base/nsContentPermissionHelper.h
+++ b/dom/base/nsContentPermissionHelper.h
@@ -73,18 +73,19 @@ public:
   static nsresult
   CreatePermissionArray(const nsACString& aType,
                         const nsACString& aAccess,
                         const nsTArray<nsString>& aOptions,
                         nsIArray** aTypesArray);
 
   static PContentPermissionRequestParent*
   CreateContentPermissionRequestParent(const nsTArray<PermissionRequest>& aRequests,
-                                       Element* element,
-                                       const IPC::Principal& principal,
+                                       Element* aElement,
+                                       const IPC::Principal& aPrincipal,
+                                       const bool aIsHandlingUserInput,
                                        const TabId& aTabId);
 
   static nsresult
   AskPermission(nsIContentPermissionRequest* aRequest,
                 nsPIDOMWindowInner* aWindow);
 
   static nsTArray<PContentPermissionRequestParent*>
   GetContentPermissionRequestParentById(const TabId& aTabId);
--- a/dom/base/test/chrome/chrome.ini
+++ b/dom/base/test/chrome/chrome.ini
@@ -64,15 +64,17 @@ support-files = ../file_bug357450.js
 [test_bug1346936.html]
 [test_cpows.xul]
 [test_getElementsWithGrid.html]
 [test_registerElement_content.xul]
 [test_registerElement_ep.xul]
 [test_domparsing.xul]
 [test_fileconstructor.xul]
 [test_nsITextInputProcessor.xul]
+[test_permission_isHandlingUserInput.xul]
+support-files = ../dummy.html
 [test_range_getClientRectsAndTexts.html]
 [test_title.xul]
 support-files = file_title.xul
 [test_windowroot.xul]
 [test_swapFrameLoaders.xul]
 [test_groupedSHistory.xul]
 [test_bug1339722.html]
new file mode 100644
--- /dev/null
+++ b/dom/base/test/chrome/test_permission_isHandlingUserInput.xul
@@ -0,0 +1,90 @@
+<?xml version="1.0"?>
+<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
+<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
+<!--
+  Tests that the isHandlingUserInput attribute on permission requests is set correctly.
+-->
+<window title="isHandlingUserInput test" xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
+
+  <body xmlns="http://www.w3.org/1999/xhtml">
+    <iframe id="frame" src="https://example.com/chrome/dom/base/test/chrome/dummy.html" />
+  </body>
+
+  <script type="application/javascript">
+  <![CDATA[
+  let Cu = Components.utils;
+
+  Cu.import("resource://gre/modules/Integration.jsm");
+  Cu.import("resource:///modules/E10SUtils.jsm");
+
+  SimpleTest.waitForExplicitFinish();
+
+  let frame = document.getElementById("frame");
+
+  function checkPermissionRequest(permission, isHandlingUserInput) {
+    return new Promise(function(resolve) {
+      let TestIntegration = (base) => ({
+        __proto__: base,
+        createPermissionPrompt(type, request) {
+          is(type, permission, `Has correct permission type ${permission}.`);
+          is(request.isHandlingUserInput, isHandlingUserInput,
+             "The isHandlingUserInput attribute is set correctly.");
+          Integration.contentPermission.unregister(TestIntegration);
+          resolve();
+          return { prompt() {} };
+        },
+      });
+      Integration.contentPermission.register(TestIntegration);
+    });
+  }
+
+  async function runTest() {
+    // Test programmatic request for persistent storage.
+    let request = checkPermissionRequest("persistent-storage", false);
+    navigator.storage.persist();
+    await request;
+
+    // Test user-initiated request for persistent storage.
+    request = checkPermissionRequest("persistent-storage", true);
+    E10SUtils.wrapHandlingUserInput(content, true, function() {
+      navigator.storage.persist();
+    });
+    await request;
+
+    // Test programmatic request for geolocation.
+    request = checkPermissionRequest("geolocation", false);
+    navigator.geolocation.getCurrentPosition(() => {});
+    await request;
+
+    // Test user-initiated request for geolocation.
+    request = checkPermissionRequest("geolocation", true);
+    E10SUtils.wrapHandlingUserInput(content, true, function() {
+      navigator.geolocation.getCurrentPosition(() => {});
+    });
+    await request;
+
+    // Notifications need to be tested in an HTTPS frame, because
+    // chrome:// URLs are whitelisted.
+    let frameWin = frame.contentWindow;
+
+    // Test programmatic request for notifications.
+    request = checkPermissionRequest("desktop-notification", false);
+    frameWin.Notification.requestPermission();
+    await request;
+
+    // Test user-initiated request for notifications.
+    request = checkPermissionRequest("desktop-notification", true);
+    E10SUtils.wrapHandlingUserInput(content, true, function() {
+      frameWin.Notification.requestPermission();
+    });
+    await request;
+  }
+
+  frame.addEventListener("load", function() {
+    runTest().then(() => SimpleTest.finish());
+  });
+  ]]>
+  </script>
+</window>
--- a/dom/geolocation/nsGeolocation.cpp
+++ b/dom/geolocation/nsGeolocation.cpp
@@ -10,16 +10,17 @@
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/PermissionMessageUtils.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/UniquePtr.h"
 #include "mozilla/Unused.h"
 #include "mozilla/WeakPtr.h"
+#include "mozilla/EventStateManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsContentUtils.h"
 #include "nsDOMClassInfoID.h"
 #include "nsGlobalWindow.h"
 #include "nsIDocument.h"
 #include "nsINamed.h"
 #include "nsIObserverService.h"
@@ -73,16 +74,17 @@ class nsGeolocationRequest final
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsGeolocationRequest, nsIContentPermissionRequest)
 
   nsGeolocationRequest(Geolocation* aLocator,
                        GeoPositionCallback aCallback,
                        GeoPositionErrorCallback aErrorCallback,
                        UniquePtr<PositionOptions>&& aOptions,
                        uint8_t aProtocolType,
                        bool aWatchPositionRequest = false,
+                       bool aIsHandlingUserInput = false,
                        int32_t aWatchId = 0);
 
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsGeolocationRequest)
 
   void Shutdown();
 
   void SendLocation(nsIDOMGeoPosition* aLocation);
   bool WantsHighAccuracy() {return !mShutdown && mOptions && mOptions->mEnableHighAccuracy;}
@@ -121,16 +123,17 @@ 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;
 };
@@ -300,21 +303,23 @@ PositionError::NotifyCallback(const GeoP
 ////////////////////////////////////////////////////
 
 nsGeolocationRequest::nsGeolocationRequest(Geolocation* aLocator,
                                            GeoPositionCallback aCallback,
                                            GeoPositionErrorCallback aErrorCallback,
                                            UniquePtr<PositionOptions>&& aOptions,
                                            uint8_t aProtocolType,
                                            bool aWatchPositionRequest,
+                                           bool aIsHandlingUserInput,
                                            int32_t aWatchId)
   : mIsWatchPositionRequest(aWatchPositionRequest),
     mCallback(Move(aCallback)),
     mErrorCallback(Move(aErrorCallback)),
     mOptions(Move(aOptions)),
+    mIsHandlingUserInput(aIsHandlingUserInput),
     mLocator(aLocator),
     mWatchId(aWatchId),
     mShutdown(false),
     mProtocolType(aProtocolType)
 {
   if (nsCOMPtr<nsPIDOMWindowInner> win =
       do_QueryReferent(mLocator->GetOwner())) {
     mRequester = new nsContentPermissionRequester(win);
@@ -391,16 +396,23 @@ NS_IMETHODIMP
 nsGeolocationRequest::GetElement(nsIDOMElement * *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);
   }
@@ -551,23 +563,23 @@ nsGeolocationRequest::SendLocation(nsIDO
     const uint32_t maximumAge_ms = mOptions->mMaximumAge;
     const bool isTooOld =
         DOMTimeStamp(PR_Now() / PR_USEC_PER_MSEC - maximumAge_ms) > positionTime_ms;
     if (isTooOld) {
       return;
     }
   }
 
-  RefPtr<Position> wrapped;
+  RefPtr<mozilla::dom::Position> wrapped;
 
   if (aPosition) {
     nsCOMPtr<nsIDOMGeoPositionCoords> coords;
     aPosition->GetCoords(getter_AddRefs(coords));
     if (coords) {
-      wrapped = new Position(ToSupports(mLocator), aPosition);
+      wrapped = new mozilla::dom::Position(ToSupports(mLocator), aPosition);
     }
   }
 
   if (!wrapped) {
     NotifyError(nsIDOMGeoPositionError::POSITION_UNAVAILABLE);
     return;
   }
 
@@ -1249,17 +1261,17 @@ Geolocation::GetCurrentPosition(GeoPosit
 
   // Count the number of requests per protocol/scheme.
   Telemetry::Accumulate(Telemetry::GEOLOCATION_GETCURRENTPOSITION_SECURE_ORIGIN,
                         static_cast<uint8_t>(mProtocolType));
 
   RefPtr<nsGeolocationRequest> request =
     new nsGeolocationRequest(this, Move(callback), Move(errorCallback),
                              Move(options), static_cast<uint8_t>(mProtocolType),
-                             false);
+                             false, EventStateManager::IsHandlingUserInput());
 
   if (!sGeoEnabled || ShouldBlockInsecureRequests() ||
       nsContentUtils::ResistFingerprinting(aCallerType)) {
     nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
     NS_DispatchToMainThread(ev);
     return NS_OK;
   }
 
@@ -1336,17 +1348,18 @@ Geolocation::WatchPosition(GeoPositionCa
                         static_cast<uint8_t>(mProtocolType));
 
   // The watch ID:
   *aRv = mLastWatchId++;
 
   RefPtr<nsGeolocationRequest> request =
     new nsGeolocationRequest(this, Move(aCallback), Move(aErrorCallback),
                              Move(aOptions),
-                             static_cast<uint8_t>(mProtocolType), true, *aRv);
+                             static_cast<uint8_t>(mProtocolType), true,
+                             EventStateManager::IsHandlingUserInput(), *aRv);
 
   if (!sGeoEnabled || ShouldBlockInsecureRequests() ||
       nsContentUtils::ResistFingerprinting(aCallerType)) {
     nsCOMPtr<nsIRunnable> ev = new RequestAllowEvent(false, request);
     NS_DispatchToMainThread(ev);
     return NS_OK;
   }
 
--- a/dom/interfaces/base/nsIContentPermissionPrompt.idl
+++ b/dom/interfaces/base/nsIContentPermissionPrompt.idl
@@ -82,16 +82,18 @@ interface nsIContentPermissionRequest : 
    *  The window or element that the permission request was
    *  originated in.  Typically the element will be non-null
    *  in when using out of process content.  window or
    *  element can be null but not both.
    */
   readonly attribute mozIDOMWindow window;
   readonly attribute nsIDOMElement element;
 
+  readonly attribute boolean isHandlingUserInput;
+
   /**
    *  The requester to get the required information of
    *  the window.
    */
   readonly attribute nsIContentPermissionRequester requester;
 
   /**
    * allow or cancel the request
--- a/dom/ipc/ContentChild.cpp
+++ b/dom/ipc/ContentChild.cpp
@@ -3031,16 +3031,17 @@ ContentChild::RecvUpdateWindow(const uin
   MOZ_ASSERT(false, "ContentChild::RecvUpdateWindow calls unexpected on this platform.");
   return IPC_FAIL_NO_REASON(this);
 #endif
 }
 
 PContentPermissionRequestChild*
 ContentChild::AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
                                                   const IPC::Principal& aPrincipal,
+                                                  const bool& aIsHandlingUserInput,
                                                   const TabId& aTabId)
 {
   MOZ_CRASH("unused");
   return nullptr;
 }
 
 bool
 ContentChild::DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor)
--- a/dom/ipc/ContentChild.h
+++ b/dom/ipc/ContentChild.h
@@ -548,16 +548,17 @@ public:
 
   virtual PWebrtcGlobalChild* AllocPWebrtcGlobalChild() override;
 
   virtual bool DeallocPWebrtcGlobalChild(PWebrtcGlobalChild *aActor) override;
 
   virtual PContentPermissionRequestChild*
   AllocPContentPermissionRequestChild(const InfallibleTArray<PermissionRequest>& aRequests,
                                       const IPC::Principal& aPrincipal,
+                                      const bool& aIsHandlingUserInput,
                                       const TabId& aTabId) override;
   virtual bool
   DeallocPContentPermissionRequestChild(PContentPermissionRequestChild* actor) override;
 
   // Windows specific - set up audio session
   virtual mozilla::ipc::IPCResult
   RecvSetAudioSessionData(const nsID& aId,
                           const nsString& aDisplayName,
--- a/dom/ipc/ContentParent.cpp
+++ b/dom/ipc/ContentParent.cpp
@@ -4426,28 +4426,30 @@ ContentParent::RecvUpdateDropEffect(cons
     dragSession->UpdateDragEffect();
   }
   return IPC_OK();
 }
 
 PContentPermissionRequestParent*
 ContentParent::AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                                     const IPC::Principal& aPrincipal,
+                                                    const bool& aIsHandlingUserInput,
                                                     const TabId& aTabId)
 {
   ContentProcessManager* cpm = ContentProcessManager::GetSingleton();
   RefPtr<TabParent> tp =
     cpm->GetTopLevelTabParentByProcessAndTabId(this->ChildID(), aTabId);
   if (!tp) {
     return nullptr;
   }
 
   return nsContentPermissionUtils::CreateContentPermissionRequestParent(aRequests,
                                                                         tp->GetOwnerElement(),
                                                                         aPrincipal,
+                                                                        aIsHandlingUserInput,
                                                                         aTabId);
 }
 
 bool
 ContentParent::DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor)
 {
   nsContentPermissionUtils::NotifyRemoveContentPermissionRequestParent(actor);
   delete actor;
--- a/dom/ipc/ContentParent.h
+++ b/dom/ipc/ContentParent.h
@@ -512,16 +512,17 @@ public:
 
   virtual mozilla::ipc::IPCResult RecvFinishShutdown() override;
 
   void MaybeInvokeDragSession(TabParent* aParent);
 
   virtual PContentPermissionRequestParent*
   AllocPContentPermissionRequestParent(const InfallibleTArray<PermissionRequest>& aRequests,
                                        const IPC::Principal& aPrincipal,
+                                       const bool& aIsTrusted,
                                        const TabId& aTabId) override;
 
   virtual bool
   DeallocPContentPermissionRequestParent(PContentPermissionRequestParent* actor) override;
 
   virtual bool HandleWindowsMessages(const Message& aMsg) const override;
 
   void ForkNewProcess(bool aBlocking);
--- a/dom/ipc/PContent.ipdl
+++ b/dom/ipc/PContent.ipdl
@@ -987,17 +987,17 @@ parent:
      * @param tabId
      *   To identify which tab issues this request.
      *
      * NOTE: The principal is untrusted in the parent process. Only
      *       principals that can live in the content process should
      *       provided.
      */
     async PContentPermissionRequest(PermissionRequest[] aRequests, Principal aPrincipal,
-                                    TabId tabId);
+                                    bool aIsHandlingUserInput, TabId tabId);
 
     async ShutdownProfile(nsCString aProfile);
 
     /**
      * Request graphics initialization information from the parent.
      */
     sync GetGraphicsDeviceInitData()
         returns (ContentDeviceData aData);
--- a/dom/notification/DesktopNotification.cpp
+++ b/dom/notification/DesktopNotification.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 #include "mozilla/dom/DesktopNotification.h"
 #include "mozilla/dom/DesktopNotificationBinding.h"
 #include "mozilla/dom/AppNotificationServiceOptionsBinding.h"
 #include "mozilla/dom/ToJSValue.h"
+#include "mozilla/EventStateManager.h"
 #include "nsComponentManagerUtils.h"
 #include "nsContentPermissionHelper.h"
 #include "nsXULAppAPI.h"
 #include "mozilla/dom/PBrowserChild.h"
 #include "mozilla/Preferences.h"
 #include "nsGlobalWindow.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsServiceManagerUtils.h"
@@ -107,22 +108,24 @@ DesktopNotification::PostDesktopNotifica
   NS_ENSURE_SUCCESS(rv, rv);
   return alerts->ShowAlert(alert, mObserver);
 }
 
 DesktopNotification::DesktopNotification(const nsAString & title,
                                          const nsAString & description,
                                          const nsAString & iconURL,
                                          nsPIDOMWindowInner* aWindow,
+                                         bool aIsHandlingUserInput,
                                          nsIPrincipal* principal)
   : DOMEventTargetHelper(aWindow)
   , mTitle(title)
   , mDescription(description)
   , mIconURL(iconURL)
   , mPrincipal(principal)
+  , mIsHandlingUserInput(aIsHandlingUserInput)
   , mAllow(false)
   , mShowHasBeenCalled(false)
 {
   if (Preferences::GetBool("notification.disabled", false)) {
     return;
   }
 
   // If we are in testing mode (running mochitests, for example)
@@ -227,16 +230,17 @@ DesktopNotificationCenter::CreateNotific
 {
   MOZ_ASSERT(mOwner);
 
   RefPtr<DesktopNotification> notification =
     new DesktopNotification(aTitle,
                             aDescription,
                             aIconURL,
                             mOwner,
+                            EventStateManager::IsHandlingUserInput(),
                             mPrincipal);
   notification->Init();
   return notification.forget();
 }
 
 JSObject*
 DesktopNotificationCenter::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
 {
@@ -276,16 +280,23 @@ NS_IMETHODIMP
 DesktopNotificationRequest::GetElement(nsIDOMElement * *aElement)
 {
   NS_ENSURE_ARG_POINTER(aElement);
   *aElement = nullptr;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+DesktopNotificationRequest::GetIsHandlingUserInput(bool *aIsHandlingUserInput)
+{
+  *aIsHandlingUserInput = mDesktopNotification->mIsHandlingUserInput;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 DesktopNotificationRequest::Cancel()
 {
   nsresult rv = mDesktopNotification->SetAllow(false);
   mDesktopNotification = nullptr;
   return rv;
 }
 
 NS_IMETHODIMP
--- a/dom/notification/DesktopNotification.h
+++ b/dom/notification/DesktopNotification.h
@@ -87,16 +87,17 @@ class DesktopNotification final : public
   friend class DesktopNotificationRequest;
 
 public:
 
   DesktopNotification(const nsAString& aTitle,
                       const nsAString& aDescription,
                       const nsAString& aIconURL,
                       nsPIDOMWindowInner* aWindow,
+                      bool aIsHandlingUserInput,
                       nsIPrincipal* principal);
 
   virtual ~DesktopNotification();
 
   void Init();
 
   /*
    * PostDesktopNotification
@@ -130,16 +131,17 @@ public:
 protected:
 
   nsString mTitle;
   nsString mDescription;
   nsString mIconURL;
 
   RefPtr<AlertServiceObserver> mObserver;
   nsCOMPtr<nsIPrincipal> mPrincipal;
+  bool mIsHandlingUserInput;
   bool mAllow;
   bool mShowHasBeenCalled;
 
   static uint32_t sCount;
 };
 
 class AlertServiceObserver: public nsIObserver
 {
--- a/dom/notification/Notification.cpp
+++ b/dom/notification/Notification.cpp
@@ -2,16 +2,17 @@
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/dom/Notification.h"
 
 #include "mozilla/Encoding.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/JSONWriter.h"
 #include "mozilla/Move.h"
 #include "mozilla/OwningNonNull.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/Services.h"
 #include "mozilla/Telemetry.h"
 #include "mozilla/Unused.h"
 
@@ -231,23 +232,24 @@ class NotificationPermissionRequest : pu
 {
 public:
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSICONTENTPERMISSIONREQUEST
   NS_DECL_NSIRUNNABLE
   NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(NotificationPermissionRequest,
                                            nsIContentPermissionRequest)
 
-  NotificationPermissionRequest(nsIPrincipal* aPrincipal,
+  NotificationPermissionRequest(nsIPrincipal* aPrincipal, bool aIsHandlingUserInput,
                                 nsPIDOMWindowInner* aWindow, Promise* aPromise,
                                 NotificationPermissionCallback* aCallback)
     : mPrincipal(aPrincipal), mWindow(aWindow),
       mPermission(NotificationPermission::Default),
       mPromise(aPromise),
-      mCallback(aCallback)
+      mCallback(aCallback),
+      mIsHandlingUserInput(aIsHandlingUserInput)
   {
     MOZ_ASSERT(aPromise);
     mRequester = new nsContentPermissionRequester(mWindow);
   }
 
   NS_IMETHOD GetName(nsACString& aName) override
   {
     aName.AssignLiteral("NotificationPermissionRequest");
@@ -260,16 +262,17 @@ protected:
   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:
@@ -595,16 +598,23 @@ NS_IMETHODIMP
 NotificationPermissionRequest::GetElement(nsIDOMElement** 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();
@@ -1806,18 +1816,19 @@ Notification::RequestPermission(const Gl
   RefPtr<Promise> promise = Promise::Create(global, aRv);
   if (aRv.Failed()) {
     return nullptr;
   }
   NotificationPermissionCallback* permissionCallback = nullptr;
   if (aCallback.WasPassed()) {
     permissionCallback = &aCallback.Value();
   }
-  nsCOMPtr<nsIRunnable> request =
-    new NotificationPermissionRequest(principal, window, promise, permissionCallback);
+  bool isHandlingUserInput = EventStateManager::IsHandlingUserInput();
+  nsCOMPtr<nsIRunnable> request = new NotificationPermissionRequest(
+    principal, isHandlingUserInput, window, promise, permissionCallback);
 
   global->Dispatch(TaskCategory::Other, request.forget());
 
   return promise.forget();
 }
 
 // static
 NotificationPermission
--- a/dom/quota/StorageManager.cpp
+++ b/dom/quota/StorageManager.cpp
@@ -6,16 +6,17 @@
 
 #include "StorageManager.h"
 
 #include "mozilla/dom/PromiseWorkerProxy.h"
 #include "mozilla/dom/quota/QuotaManagerService.h"
 #include "mozilla/dom/StorageManagerBinding.h"
 #include "mozilla/dom/WorkerPrivate.h"
 #include "mozilla/ErrorResult.h"
+#include "mozilla/EventStateManager.h"
 #include "mozilla/Telemetry.h"
 #include "nsContentPermissionHelper.h"
 #include "nsIQuotaCallbacks.h"
 #include "nsIQuotaRequests.h"
 #include "nsPIDOMWindow.h"
 
 using namespace mozilla::dom::workers;
 
@@ -162,25 +163,28 @@ public:
  * PersistentStoragePermissionRequest
  ******************************************************************************/
 
 class PersistentStoragePermissionRequest final
   : public nsIContentPermissionRequest
 {
   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)
     , mPromise(aPromise)
   {
     MOZ_ASSERT(aPrincipal);
     MOZ_ASSERT(aWindow);
     MOZ_ASSERT(aPromise);
 
     mRequester = new nsContentPermissionRequester(mWindow);
   }
@@ -298,17 +302,20 @@ ExecuteOpOnMainOrWorkerThread(nsIGlobalO
         RefPtr<nsIQuotaRequest> request;
         aRv = Persisted(principal, resolver, getter_AddRefs(request));
 
         break;
       }
 
       case RequestResolver::Type::Persist: {
         RefPtr<PersistentStoragePermissionRequest> request =
-          new PersistentStoragePermissionRequest(principal, window, promise);
+          new PersistentStoragePermissionRequest(principal,
+                                                 window,
+                                                 EventStateManager::IsHandlingUserInput(),
+                                                 promise);
 
         // In private browsing mode, no permission prompt.
         if (nsContentUtils::IsInPrivateBrowsing(doc)) {
           aRv = request->Cancel();
         } else {
           aRv = request->Start();
         }
 
@@ -712,16 +719,23 @@ PersistentStoragePermissionRequest::GetP
   MOZ_ASSERT(mPrincipal);
 
   NS_ADDREF(*aPrincipal = mPrincipal);
 
   return NS_OK;
 }
 
 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);