Bug 1463919 - Add AutoplayRequest to encapsulate asking for autoplay permission. r?smaug draft
authorChris Pearce <cpearce@mozilla.com>
Fri, 22 Jun 2018 11:57:24 +1200
changeset 811164 14558402124c3c2fdb0c67adab89b35f4d5ac3e4
parent 810527 d6b75732548cb1d73b9f82dce60a5e6e97d1da14
child 811165 12de3d9868f0920a25a45164485b906d38fa40d9
push id114216
push userbmo:cpearce@mozilla.com
push dateWed, 27 Jun 2018 03:52:23 +0000
reviewerssmaug
bugs1463919
milestone62.0a1
Bug 1463919 - Add AutoplayRequest to encapsulate asking for autoplay permission. r?smaug Add an implementation of nsIContentPermissionRequest to encapsulate requesting permission from the user to autoplay audible media. All documents in the tab request permission using the top level document's origin, so the AutoplayRequest instance for a tab is stored on the top level content window of the tab. AutoplayRequest ensures that there's only a single prompt shown at once. MozReview-Commit-ID: 2u3aLnEa21z
dom/base/nsGlobalWindowInner.cpp
dom/base/nsPIDOMWindow.h
dom/html/AutoplayRequest.cpp
dom/html/AutoplayRequest.h
dom/html/moz.build
dom/media/AutoplayPolicy.cpp
dom/media/AutoplayPolicy.h
--- a/dom/base/nsGlobalWindowInner.cpp
+++ b/dom/base/nsGlobalWindowInner.cpp
@@ -12,16 +12,17 @@
 
 // Local Includes
 #include "Navigator.h"
 #include "nsContentSecurityManager.h"
 #include "nsScreen.h"
 #include "nsHistory.h"
 #include "nsDOMNavigationTiming.h"
 #include "nsIDOMStorageManager.h"
+#include "mozilla/dom/AutoplayRequest.h"
 #include "mozilla/dom/DOMJSProxyHandler.h"
 #include "mozilla/dom/DOMPrefs.h"
 #include "mozilla/dom/EventTarget.h"
 #include "mozilla/dom/LocalStorage.h"
 #include "mozilla/dom/Storage.h"
 #include "mozilla/dom/IdleRequest.h"
 #include "mozilla/dom/Performance.h"
 #include "mozilla/dom/StorageEvent.h"
@@ -8119,16 +8120,46 @@ nsPIDOMWindowInner::AsGlobal()
 }
 
 const nsIGlobalObject*
 nsPIDOMWindowInner::AsGlobal() const
 {
   return nsGlobalWindowInner::Cast(this);
 }
 
+static nsPIDOMWindowInner*
+GetTopLevelInnerWindow(nsPIDOMWindowInner* aWindow)
+{
+  nsIDocShell* docShell = aWindow->GetDocShell();
+  if (!docShell) {
+    return nullptr;
+  }
+  nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
+  docShell->GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
+  if (!rootTreeItem || !rootTreeItem->GetDocument()) {
+    return nullptr;
+  }
+  return rootTreeItem->GetDocument()->GetInnerWindow();
+}
+
+already_AddRefed<mozilla::dom::AutoplayRequest>
+nsPIDOMWindowInner::GetAutoplayRequest()
+{
+  // The AutoplayRequest is stored on the top level window.
+  nsPIDOMWindowInner* window = GetTopLevelInnerWindow(this);
+  if (!window) {
+    return nullptr;
+  }
+  if (!window->mAutoplayRequest) {
+    window->mAutoplayRequest = AutoplayRequest::Create(window, false);
+  }
+  RefPtr<mozilla::dom::AutoplayRequest> request = window->mAutoplayRequest;
+  return request.forget();
+}
+
 // XXX: Can we define this in a header instead of here?
 namespace mozilla {
 namespace dom {
 extern uint64_t
 NextWindowID();
 } // namespace dom
 } // namespace mozilla
 
--- a/dom/base/nsPIDOMWindow.h
+++ b/dom/base/nsPIDOMWindow.h
@@ -41,16 +41,17 @@ class nsPIDOMWindowOuter;
 class nsPIWindowRoot;
 class nsXBLPrototypeHandler;
 
 typedef uint32_t SuspendTypes;
 
 namespace mozilla {
 class ThrottledEventQueue;
 namespace dom {
+class AutoplayRequest;
 class AudioContext;
 class ClientInfo;
 class ClientState;
 class DocGroup;
 class TabGroup;
 class Element;
 class Navigator;
 class Performance;
@@ -606,16 +607,20 @@ public:
 
   virtual nsresult Focus() = 0;
   virtual nsresult Close() = 0;
 
   mozilla::dom::DocGroup* GetDocGroup() const;
   virtual nsISerialEventTarget*
   EventTargetFor(mozilla::TaskCategory aCategory) const = 0;
 
+  // Returns the AutoplayRequest that documents in this window should use
+  // to request permission to autoplay.
+  already_AddRefed<mozilla::dom::AutoplayRequest> GetAutoplayRequest();
+
 protected:
   void CreatePerformanceObjectIfNeeded();
 
   // Lazily instantiate an about:blank document if necessary, and if
   // we have what it takes to do so.
   void MaybeCreateDoc();
 
   void SetChromeEventHandlerInternal(mozilla::dom::EventTarget* aChromeEventHandler) {
@@ -688,16 +693,21 @@ protected:
   // could be null and we don't want it to be set multiple times.
   bool mHasTriedToCacheTopInnerWindow;
 
   // The number of active IndexedDB databases.
   uint32_t mNumOfIndexedDBDatabases;
 
   // The number of open WebSockets.
   uint32_t mNumOfOpenWebSockets;
+
+  // If we're in the process of requesting permission for this window to
+  // play audible media, or we've already been granted permission by the
+  // user, this is non-null, and encapsulates the request.
+  RefPtr<mozilla::dom::AutoplayRequest> mAutoplayRequest;
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsPIDOMWindowInner, NS_PIDOMWINDOWINNER_IID)
 
 class nsPIDOMWindowOuter : public mozIDOMWindowProxy
 {
 protected:
   explicit nsPIDOMWindowOuter();
new file mode 100644
--- /dev/null
+++ b/dom/html/AutoplayRequest.cpp
@@ -0,0 +1,172 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* 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/AutoplayRequest.h"
+
+#include "nsGlobalWindowInner.h"
+#include "nsISupportsImpl.h"
+#include "mozilla/Logging.h"
+#include "nsContentPermissionHelper.h"
+
+extern mozilla::LazyLogModule gMediaElementLog;
+
+#define PLAY_REQUEST_LOG(msg, ...)                                             \
+  MOZ_LOG(gMediaElementLog, LogLevel::Debug, (msg, ##__VA_ARGS__))
+
+namespace mozilla {
+namespace dom {
+
+NS_IMPL_ISUPPORTS(AutoplayRequest, nsIContentPermissionRequest)
+
+AutoplayRequest::AutoplayRequest(nsPIDOMWindowInner* aWindow,
+                                 nsIPrincipal* aNodePrincipal,
+                                 nsIEventTarget* aMainThreadTarget,
+                                 bool aIsHandlingUserInput)
+  : mWindow(do_GetWeakReference(aWindow))
+  , mNodePrincipal(aNodePrincipal)
+  , mMainThreadTarget(aMainThreadTarget)
+  , mRequester(new nsContentPermissionRequester(aWindow))
+  , mIsHandlingUserInput(aIsHandlingUserInput)
+{
+  MOZ_RELEASE_ASSERT(mNodePrincipal);
+}
+
+AutoplayRequest::~AutoplayRequest() {}
+
+already_AddRefed<AutoplayRequest>
+AutoplayRequest::Create(nsPIDOMWindowInner* aWindow, bool aIsHandlingUserInput)
+{
+  if (!aWindow || !aWindow->GetDoc() ||
+      !aWindow->EventTargetFor(TaskCategory::Other)) {
+    return nullptr;
+  }
+  RefPtr<AutoplayRequest> request =
+    new AutoplayRequest(aWindow,
+                        aWindow->GetDoc()->NodePrincipal(),
+                        aWindow->EventTargetFor(TaskCategory::Other),
+                        aIsHandlingUserInput);
+  PLAY_REQUEST_LOG("AutoplayRequest %p Create()", request.get());
+  return request.forget();
+}
+
+NS_IMETHODIMP
+AutoplayRequest::GetPrincipal(nsIPrincipal** aRequestingPrincipal)
+{
+  NS_ENSURE_ARG_POINTER(aRequestingPrincipal);
+
+  nsCOMPtr<nsIPrincipal> principal = mNodePrincipal;
+  principal.forget(aRequestingPrincipal);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AutoplayRequest::GetTypes(nsIArray** aTypes)
+{
+  NS_ENSURE_ARG_POINTER(aTypes);
+
+  nsTArray<nsString> emptyOptions;
+  return nsContentPermissionUtils::CreatePermissionArray(
+    NS_LITERAL_CSTRING("autoplay-media"),
+    NS_LITERAL_CSTRING("unused"),
+    emptyOptions,
+    aTypes);
+}
+
+NS_IMETHODIMP
+AutoplayRequest::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
+AutoplayRequest::GetElement(Element** aRequestingElement)
+{
+  NS_ENSURE_ARG_POINTER(aRequestingElement);
+  *aRequestingElement = nullptr;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AutoplayRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
+{
+  NS_ENSURE_ARG_POINTER(aIsHandlingUserInput);
+  *aIsHandlingUserInput = mIsHandlingUserInput;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AutoplayRequest::Cancel()
+{
+  MOZ_ASSERT(mRequestDispatched);
+  PLAY_REQUEST_LOG("AutoplayRequest %p Cancel()", this);
+  mRequestDispatched = false;
+  mPromiseHolder.RejectIfExists(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR, __func__);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AutoplayRequest::Allow(JS::HandleValue aChoices)
+{
+  MOZ_ASSERT(mRequestDispatched);
+  PLAY_REQUEST_LOG("AutoplayRequest %p Allow()", this);
+  mRequestDispatched = false;
+  mPromiseHolder.ResolveIfExists(true, __func__);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+AutoplayRequest::GetRequester(nsIContentPermissionRequester** aRequester)
+{
+  NS_ENSURE_ARG_POINTER(aRequester);
+
+  nsCOMPtr<nsIContentPermissionRequester> requester = mRequester;
+  requester.forget(aRequester);
+
+  return NS_OK;
+}
+
+RefPtr<GenericPromise>
+AutoplayRequest::RequestWithPrompt()
+{
+  // If we've already requested permission, we'll just return the promise,
+  // as we don't want to show multiple permission requests at once.
+  // The promise is non-exclusive, so if the request has already completed,
+  // the ThenValue will run immediately.
+  if (mRequestDispatched) {
+    PLAY_REQUEST_LOG(
+      "AutoplayRequest %p RequestWithPrompt() request already dispatched",
+      this);
+    return mPromiseHolder.Ensure(__func__);
+  }
+  mRequestDispatched = true;
+
+  nsCOMPtr<nsPIDOMWindowInner> window = do_QueryReferent(mWindow);
+  if (!window) {
+    return GenericPromise::CreateAndReject(NS_ERROR_DOM_MEDIA_NOT_ALLOWED_ERR,
+                                           __func__);
+  }
+  nsCOMPtr<nsIContentPermissionRequest> request = do_QueryInterface(this);
+  MOZ_RELEASE_ASSERT(request);
+  nsCOMPtr<nsIRunnable> f = NS_NewRunnableFunction(
+    "AutoplayRequest::RequestWithPrompt", [window, request]() {
+      nsContentPermissionUtils::AskPermission(request, window);
+    });
+  mMainThreadTarget->Dispatch(f, NS_DISPATCH_NORMAL);
+
+  return mPromiseHolder.Ensure(__func__);
+}
+
+} // namespace dom
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/html/AutoplayRequest.h
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __AutoplayRequest_h__
+#define __AutoplayRequest_h__
+
+#include "mozilla/MozPromise.h"
+#include "nsIContentPermissionPrompt.h"
+#include "nsIWeakReferenceUtils.h"
+
+class nsPIDOMWindowInner;
+class nsIEventTarget;
+
+namespace mozilla {
+namespace dom {
+
+// Encapsulates requesting permission from the user to autoplay with a
+// doorhanger. The AutoplayRequest is stored on the top level window,
+// and all documents in the tab use the top level window's AutoplayRequest.
+// The AutoplayRequest ensures that multiple requests are merged into one,
+// in order to avoid showing multiple doorhangers on one tab at once.
+class AutoplayRequest final : public nsIContentPermissionRequest
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSICONTENTPERMISSIONREQUEST
+
+  // Creates a new AutoplayRequest. Don't call this directly, use
+  // AutoplayPolicy::RequestFor() to retrieve the appropriate AutoplayRequest
+  // to use for a document/window.
+  static already_AddRefed<AutoplayRequest> Create(nsPIDOMWindowInner* aWindow,
+                                                  bool aIsHandlingInput);
+
+  // Requests permission to autoplay via a user prompt. Promise resolves when
+  // the user grants permission, or if permission has previously been granted.
+  // Rejects if permission is denied. Note: the promise may already have been
+  // fulfilled by a previous request to autoplay in this window, in which case
+  // a resolved or rejected promise is returned.
+  RefPtr<GenericPromise> RequestWithPrompt();
+
+private:
+  AutoplayRequest(nsPIDOMWindowInner* aWindow,
+                  nsIPrincipal* aNodePrincipal,
+                  nsIEventTarget* aMainThreadTarget,
+                  bool aIsHandlingInput);
+  ~AutoplayRequest();
+
+  nsWeakPtr mWindow;
+  nsCOMPtr<nsIPrincipal> mNodePrincipal;
+  nsCOMPtr<nsIEventTarget> mMainThreadTarget;
+  nsCOMPtr<nsIContentPermissionRequester> mRequester;
+  const bool mIsHandlingUserInput;
+  MozPromiseHolder<GenericPromise> mPromiseHolder;
+  // Tracks whether we've dispatched a request to chrome to prompt for user
+  // permission to autoplay. This flag ensures we don't request multiple times
+  // concurrently.
+  bool mRequestDispatched = false;
+};
+
+} // namespace dom
+} // namespace mozilla
+
+#endif // __AutoplayRequest_h__
--- a/dom/html/moz.build
+++ b/dom/html/moz.build
@@ -47,16 +47,17 @@ EXPORTS += [
     'nsTextEditorState.h',
 ]
 
 EXPORTS.mozilla += [
     'TextInputListener.h',
 ]
 
 EXPORTS.mozilla.dom += [
+    'AutoplayRequest.h',
     'HTMLAllCollection.h',
     'HTMLAnchorElement.h',
     'HTMLAreaElement.h',
     'HTMLAudioElement.h',
     'HTMLBodyElement.h',
     'HTMLBRElement.h',
     'HTMLButtonElement.h',
     'HTMLCanvasElement.h',
@@ -217,16 +218,17 @@ UNIFIED_SOURCES += [
     'RadioNodeList.cpp',
     'TextTrackManager.cpp',
     'TimeRanges.cpp',
     'ValidityState.cpp',
     'VideoDocument.cpp',
 ]
 
 SOURCES += [
+    'AutoplayRequest.cpp',
     # Includes npapi.h.
     'PluginDocument.cpp',
 ]
 
 EXTRA_COMPONENTS += [
     'htmlMenuBuilder.js',
     'htmlMenuBuilder.manifest'
 ]
--- a/dom/media/AutoplayPolicy.cpp
+++ b/dom/media/AutoplayPolicy.cpp
@@ -4,63 +4,116 @@
  * 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 "AutoplayPolicy.h"
 
 #include "mozilla/EventStateManager.h"
 #include "mozilla/Preferences.h"
 #include "mozilla/dom/AudioContext.h"
+#include "mozilla/dom/AutoplayRequest.h"
 #include "mozilla/dom/HTMLMediaElement.h"
 #include "mozilla/dom/HTMLMediaElementBinding.h"
 #include "nsContentUtils.h"
 #include "nsIDocument.h"
 #include "MediaManager.h"
+#include "nsIDocShell.h"
+#include "nsIDocShellTreeItem.h"
+#include "nsPIDOMWindow.h"
 
 namespace mozilla {
 namespace dom {
 
+static nsIDocument*
+ApproverDocOf(NotNull<nsIDocument*> aDoc)
+{
+  nsCOMPtr<nsIDocShell> ds = aDoc->GetDocShell();
+  if (!ds) {
+    return nullptr;
+  }
+
+  nsCOMPtr<nsIDocShellTreeItem> rootTreeItem;
+  ds->GetSameTypeRootTreeItem(getter_AddRefs(rootTreeItem));
+  if (!rootTreeItem) {
+    return nullptr;
+  }
+
+  return rootTreeItem->GetDocument();
+}
+
+static bool
+IsAllowedToPlay(nsPIDOMWindowInner* aWindow)
+{
+  if (!aWindow) {
+    return false;
+  }
+
+  // Pages which have been granted permission to capture WebRTC camera or
+  // microphone are assumed to be trusted, and are allowed to autoplay.
+  MediaManager* manager = MediaManager::GetIfExists();
+  if (manager &&
+      manager->IsActivelyCapturingOrHasAPermission(aWindow->WindowID())) {
+    return true;
+  }
+
+  if (!aWindow->GetExtantDoc()) {
+    return false;
+  }
+
+  nsIDocument* approver = ApproverDocOf(WrapNotNull(aWindow->GetExtantDoc()));
+  if (nsContentUtils::IsExactSitePermAllow(approver->NodePrincipal(),
+                                           "autoplay-media")) {
+    // Autoplay permission has been granted already.
+    return true;
+  }
+
+  if (approver->HasBeenUserGestureActivated()) {
+    // Document has been activated by user gesture.
+    return true;
+  }
+
+  return false;
+}
+
+/* static */
+already_AddRefed<AutoplayRequest>
+AutoplayPolicy::RequestFor(NotNull<nsIDocument*> aDocument)
+{
+  nsIDocument* document = ApproverDocOf(aDocument);
+  if (!document) {
+    return nullptr;
+  }
+  nsPIDOMWindowInner* window = document->GetInnerWindow();
+  if (!window) {
+    return nullptr;
+  }
+  return window->GetAutoplayRequest();
+}
+
 /* static */ bool
 AutoplayPolicy::IsMediaElementAllowedToPlay(NotNull<HTMLMediaElement*> aElement)
 {
   if (Preferences::GetBool("media.autoplay.enabled")) {
     return true;
   }
 
   // TODO : this old way would be removed when user-gestures-needed becomes
   // as a default option to block autoplay.
   if (!Preferences::GetBool("media.autoplay.enabled.user-gestures-needed", false)) {
     // If elelement is blessed, it would always be allowed to play().
     return aElement->IsBlessed() ||
            EventStateManager::IsHandlingUserInput();
   }
 
-  // Pages which have been granted permission to capture WebRTC camera or
-  // microphone are assumed to be trusted, and are allowed to autoplay.
-  MediaManager* manager = MediaManager::GetIfExists();
-  if (manager) {
-    nsCOMPtr<nsPIDOMWindowInner> window = aElement->OwnerDoc()->GetInnerWindow();
-    if (window && manager->IsActivelyCapturingOrHasAPermission(window->WindowID())) {
-      return true;
-    }
-  }
-
   // Muted content
   if (aElement->Volume() == 0.0 || aElement->Muted()) {
     return true;
   }
 
-  // Whitelisted.
-  if (nsContentUtils::IsExactSitePermAllow(
-        aElement->NodePrincipal(), "autoplay-media")) {
-    return true;
-  }
-
-  // Activated by user gesture.
-  if (aElement->OwnerDoc()->HasBeenUserGestureActivated()) {
+  if (IsAllowedToPlay(aElement->OwnerDoc()->GetInnerWindow())) {
     return true;
   }
 
   return false;
 }
 
 /* static */ bool
 AutoplayPolicy::IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext)
@@ -73,40 +126,17 @@ AutoplayPolicy::IsAudioContextAllowedToP
     return true;
   }
 
   // Offline context won't directly output sound to audio devices.
   if (aContext->IsOffline()) {
     return true;
   }
 
-  nsPIDOMWindowInner* window = aContext->GetOwner();
-  if (!window) {
-    return false;
-  }
-
-  // Pages which have been granted permission to capture WebRTC camera or
-  // microphone are assumed to be trusted, and are allowed to autoplay.
-  MediaManager* manager = MediaManager::GetIfExists();
-  if (manager) {
-    if (manager->IsActivelyCapturingOrHasAPermission(window->WindowID())) {
-      return true;
-    }
-  }
-
-  nsCOMPtr<nsIPrincipal> principal = aContext->GetParentObject()->AsGlobal()->PrincipalOrNull();
-
-  // Whitelisted.
-  if (principal &&
-      nsContentUtils::IsExactSitePermAllow(principal, "autoplay-media")) {
-    return true;
-  }
-
-  // Activated by user gesture.
-  if (window->GetExtantDoc()->HasBeenUserGestureActivated()) {
+  if (IsAllowedToPlay(aContext->GetOwner())) {
     return true;
   }
 
   return false;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/media/AutoplayPolicy.h
+++ b/dom/media/AutoplayPolicy.h
@@ -11,34 +11,41 @@
 
 class nsIDocument;
 
 namespace mozilla {
 namespace dom {
 
 class HTMLMediaElement;
 class AudioContext;
+class AutoplayRequest;
 
 /**
  * AutoplayPolicy is used to manage autoplay logic for all kinds of media,
  * including MediaElement, Web Audio and Web Speech.
  *
  * Autoplay could be disable by turn off the pref "media.autoplay.enabled".
  * Once user disable autoplay, media could only be played if one of following
  * conditions is true.
  * 1) Owner document is activated by user gestures
  *    We restrict user gestures to "mouse click", "keyboard press" and "touch".
  * 2) Muted media content or video without audio content.
  * 3) Document's origin has the "autoplay-media" permission.
  */
 class AutoplayPolicy
 {
 public:
+  // Returns whether a given media element is allowed to play.
   static bool IsMediaElementAllowedToPlay(NotNull<HTMLMediaElement*> aElement);
+
+  // Returns whether a given AudioContext is allowed to play.
   static bool IsAudioContextAllowedToPlay(NotNull<AudioContext*> aContext);
-private:
-  static bool IsDocumentAllowedToPlay(nsIDocument* aDoc);
+
+  // Returns the AutoplayRequest that a given document must request on
+  // for autoplay permission.
+  static already_AddRefed<AutoplayRequest> RequestFor(
+    NotNull<nsIDocument*> aDocument);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif