Bug 1345424 - Add an isHandlingUserInput attribute to permission requests. r=baku draft
authorJohann Hofmann <jhofmann@mozilla.com>
Mon, 20 Feb 2017 20:46:39 +0100
changeset 704219 fd8ce38d2ab7c0759856e67e62d586e61e68600e
parent 704194 5b33b070378ae0806bed0b5e5e34de429a29e7db
child 742029 03eb7f4e9a445dbd8b7d9014de103c009d9f44bc
push id91108
push userbmo:jhofmann@mozilla.com
push dateTue, 28 Nov 2017 11:04:12 +0000
reviewersbaku
bugs1345424
milestone59.0a1
Bug 1345424 - Add an isHandlingUserInput attribute to permission requests. r=baku 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);