gecko-dev/dom/html/AutoplayPermissionManager.h
Chris Pearce 848dee9a6f Bug 1472580 - Ensure we always get a allow/cancel response to an autoplay media permission request. r=smaug
The front end code can't always guarantee to give us an allow/cancel response
to a permission request. In particular in these cases:
* if we close a tab while showing a doorhanger, or
* if we navigate a tab while showing a doorhanger, or
* if the permission prompt requested in a background tab and never shown.

Handling all of these cases is problematic; we don't get events for all of
these where it's easy and cheap to determine that we should cancel the
permission request.

Canceling the permission request is important in the autoplay-media permission
request case as there's objects waiting on the resolution of the permission
request, and they leak in ASan builds while running chrome tests if the Gecko
size of the permission request doesn't get a notification telling it to stop
waiting.

But we can however rely on the doorhanger code to drop its reference to the
nsIContentPermissionRequest object that we pass to it when the doorhanger goes
away. So we can cancel the permission request in our
nsIContentPermissionRequest's implementation's destructor in order to easily
catch all the above cases.

In order to do that, we need to split AutoplayRequest into two; one part being
the implementation of nsIContentPermissionRequest (AutoplayPermissionRequest),
and the other part being the code to own the PromiseHolder and manage the
permission request (AutoplayPermissionManager).

AutoplayPermissionRequest keeps a weak reference to AutoplayPermissionManager,
so that it can tell the AutoplayPermissionManager to reject the request promise
when it's destroyed.

This fixes the ASan leak for which I got backed out from earlier in this bug,
and also fixes the cases above.

MozReview-Commit-ID: KoVkgIqDleW

--HG--
rename : dom/html/AutoplayRequest.cpp => dom/html/AutoplayPermissionManager.cpp
rename : dom/html/AutoplayRequest.h => dom/html/AutoplayPermissionManager.h
extra : rebase_source : dbca520a93d8c416f6d64c2da027630181bb5910
2018-07-06 21:15:20 +12:00

70 lines
2.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/. */
#ifndef __AutoplayPermissionRequestManager_h__
#define __AutoplayPermissionRequestManager_h__
#include "mozilla/MozPromise.h"
#include "mozilla/WeakPtr.h"
#include "nsIContentPermissionPrompt.h"
#include "nsISupports.h"
#include "nsIWeakReferenceUtils.h"
#include "nsWeakReference.h"
class nsGlobalWindowInner;
class nsIEventTarget;
namespace mozilla {
// Encapsulates requesting permission from the user to autoplay with a
// doorhanger. The AutoplayPermissionManager is stored on the top level window,
// and all documents in the tab use the top level window's
// AutoplayPermissionManager. The AutoplayPermissionManager ensures that
// multiple requests are merged into one, in order to avoid showing multiple
// doorhangers on one tab at once.
class AutoplayPermissionManager final
: public SupportsWeakPtr<AutoplayPermissionManager>
{
public:
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(AutoplayPermissionManager)
NS_INLINE_DECL_REFCOUNTING(AutoplayPermissionManager)
// Creates a new AutoplayPermissionManager. Don't call this directly, use
// AutoplayPolicy::RequestFor() to retrieve the appropriate
// AutoplayPermissionManager to use for a document/window.
explicit AutoplayPermissionManager(nsGlobalWindowInner* aWindow);
// Requests permission to autoplay via a user prompt. The returned MozPromise
// resolves/rejects when the user grants/denies permission to autoplay.
// If the request doorhanger is dismissed, i.e. tab closed, ESC pressed,
// etc, the MozPromise is rejected. If there is a stored permission for this
// window's origin, the parent process will approve/deny the
// autoplay request using the stored permission immediately (so the MozPromise
// rejects in the content process after an IPC round trip).
RefPtr<GenericPromise> RequestWithPrompt();
// Called by AutoplayPermissionRequest to approve the play request,
// and resolve the MozPromise returned by RequestWithPrompt().
void ApprovePlayRequest();
// Called by AutoplayPermissionRequest to deny the play request,
// and reject the MozPromise returned by RequestWithPrompt().
void DenyPlayRequest();
private:
~AutoplayPermissionManager();
nsWeakPtr mWindow;
MozPromiseHolder<GenericPromise> mPromiseHolder;
// Tracks whether we've dispatched a request to chrome in the parent process
// to prompt for user permission to autoplay. This flag ensures we don't
// request multiple times concurrently.
bool mRequestDispatched = false;
};
} // namespace mozilla
#endif // __AutoplayPermissionRequestManager_h__