Files
tubestation/dom/html/AutoplayRequest.cpp
Chris Pearce 6d0c39faee 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
2018-06-22 11:57:24 +12:00

168 lines
4.8 KiB
C++

/* -*- 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 {
NS_IMPL_ISUPPORTS(AutoplayRequest, nsIContentPermissionRequest)
AutoplayRequest::AutoplayRequest(nsGlobalWindowInner* aWindow,
nsIPrincipal* aNodePrincipal,
nsIEventTarget* aMainThreadTarget)
: mWindow(do_GetWeakReference(aWindow))
, mNodePrincipal(aNodePrincipal)
, mMainThreadTarget(aMainThreadTarget)
, mRequester(new dom::nsContentPermissionRequester(aWindow))
{
MOZ_RELEASE_ASSERT(mNodePrincipal);
}
AutoplayRequest::~AutoplayRequest() {}
already_AddRefed<AutoplayRequest>
AutoplayRequest::Create(nsGlobalWindowInner* aWindow)
{
if (!aWindow || !aWindow->GetPrincipal() ||
!aWindow->EventTargetFor(TaskCategory::Other)) {
return nullptr;
}
RefPtr<AutoplayRequest> request =
new AutoplayRequest(aWindow,
aWindow->GetPrincipal(),
aWindow->EventTargetFor(TaskCategory::Other));
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 dom::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(dom::Element** aRequestingElement)
{
NS_ENSURE_ARG_POINTER(aRequestingElement);
*aRequestingElement = nullptr;
return NS_OK;
}
NS_IMETHODIMP
AutoplayRequest::GetIsHandlingUserInput(bool* aIsHandlingUserInput)
{
NS_ENSURE_ARG_POINTER(aIsHandlingUserInput);
*aIsHandlingUserInput = false;
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__);
}
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]() {
dom::nsContentPermissionUtils::AskPermission(request, window);
});
mMainThreadTarget->Dispatch(f, NS_DISPATCH_NORMAL);
mRequestDispatched = true;
return mPromiseHolder.Ensure(__func__);
}
} // namespace mozilla