Bug 1640998 - part7 : create `MediaController` webidl. r=chunmin,smaug

This patch will
- create a chrome-only webdil interface `MediaController`
- expose supported keys via `MediaController` webidl interface

The advantage of doing so are
- to have a dedicated interface that is only used for MediaController that can be used for testing and our future plan (media hub)

More Details :
Currently, we access media controller's from `ChromeUtils` [1], but it causes a problem of creating a duplicated enum of the enum which we want to expose into Chrome JS.

Instead, we should create a media controller interface to access all its attibutes, which is more easier and clean.

In addition, we're planning to have a something like Chrome's media hub [2]. In order to do that, we have to expose some JS methods to allow us to control playback directly from Chrome JS.

[1] https://searchfox.org/mozilla-central/rev/559b25eb41c1cbffcb90a34e008b8288312fcd25/dom/chrome-webidl/ChromeUtils.webidl#485-493
[2] https://blog.google/products/chrome/manage-audio-and-video-in-chrome/

Differential Revision: https://phabricator.services.mozilla.com/D77757
This commit is contained in:
alwu 2020-06-08 18:51:12 +00:00
Родитель 5aa603709c
Коммит 0d200dff8a
9 изменённых файлов: 132 добавлений и 11 удалений

Просмотреть файл

@ -148,6 +148,8 @@ interface CanonicalBrowsingContext : BrowsingContext {
void loadURI(DOMString aURI, optional LoadURIOptions aOptions = {});
readonly attribute nsISHistory? sessionHistory;
readonly attribute MediaController? mediaController;
};
[Exposed=Window, ChromeOnly]

Просмотреть файл

@ -0,0 +1,36 @@
/* 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/.
*/
/**
* This enum lists all supported behaviors on the media controller.
*/
enum MediaControlKey {
"focus",
"play",
"pause",
"playpause",
"previoustrack",
"nexttrack",
"seekbackward",
"seekforward",
"stop",
};
/**
* MediaController is used to control media playback for a tab, and each tab
* would only have one media controller, which can be accessed from the
* canonical browsing context.
*/
[Exposed=Window, ChromeOnly]
interface MediaController : EventTarget {
[Frozen, Cached, Pure]
readonly attribute sequence<MediaControlKey> supportedKeys;
attribute EventHandler onsupportedkeyschange;
// TODO : expose other media controller methods to webidl in order to support
// the plan of controlling media directly from the chrome JS.
// eg. play(), pause().
};

Просмотреть файл

@ -56,6 +56,7 @@ WEBIDL_FILES = [
'Localization.webidl',
'MatchGlob.webidl',
'MatchPattern.webidl',
'MediaController.webidl',
'MessageManager.webidl',
'MozDocumentObserver.webidl',
'MozSharedMap.webidl',

Просмотреть файл

@ -243,7 +243,7 @@ bool MediaControlService::ControllerManager::RemoveController(
}
// This is LinkedListElement's method which will remove controller from
// `mController`.
aController->remove();
static_cast<LinkedListControllerPtr>(aController)->remove();
// If main controller is removed from the list, the last controller in the
// list would become the main controller. Or reset the main controller when
// the list is already empty.
@ -291,7 +291,7 @@ void MediaControlService::ControllerManager::ReorderGivenController(
// controller would be B. But if we don't maintain the controller order when
// main controller changes, we would pick C as the main controller because
// the list is still [A, B, C].
aController->remove();
static_cast<LinkedListControllerPtr>(aController)->remove();
return mControllers.insertBack(aController);
}
@ -303,8 +303,9 @@ void MediaControlService::ControllerManager::ReorderGivenController(
// a list [A, B, C, D, E] and E is the main controller. If we want to
// reorder B to the front of E, then the list would become [A, C, D, B, E].
MOZ_ASSERT(GetMainController() != aController);
aController->remove();
return GetMainController()->setPrevious(aController);
static_cast<LinkedListControllerPtr>(aController)->remove();
return static_cast<LinkedListControllerPtr>(GetMainController())
->setPrevious(aController);
}
}
@ -380,7 +381,14 @@ MediaController* MediaControlService::ControllerManager::GetMainController()
}
uint64_t MediaControlService::ControllerManager::GetControllersNum() const {
return mControllers.length();
size_t length = 0;
const auto* element =
static_cast<ConstLinkedListControllerPtr>(mControllers.getFirst());
while (element) {
length++;
element = element->getNext();
}
return length;
}
bool MediaControlService::ControllerManager::Contains(

Просмотреть файл

@ -101,6 +101,9 @@ class MediaControlService final : public nsIObserver {
~ControllerManager() = default;
using MediaKeysArray = nsTArray<MediaControlKeysEvent>;
using LinkedListControllerPtr = LinkedListElement<RefPtr<MediaController>>*;
using ConstLinkedListControllerPtr =
const LinkedListElement<RefPtr<MediaController>>*;
bool AddController(MediaController* aController);
bool RemoveController(MediaController* aController);

Просмотреть файл

@ -10,6 +10,7 @@
#include "MediaController.h"
#include "MediaControlKeysEvent.h"
#include "mozilla/dom/ChromeUtilsBinding.h"
#include "mozilla/dom/MediaControllerBinding.h"
#include "mozilla/Logging.h"
extern mozilla::LazyLogModule gMediaControlLog;
@ -128,6 +129,32 @@ inline MediaSessionAction ConvertToMediaSessionAction(uint8_t aActionValue) {
return static_cast<MediaSessionAction>(aActionValue);
}
// TODO : merge `MediaControlKeysEvent` and `MediaControlKey`
inline MediaControlKey ConvertToMediaControlKey(
MediaControlKeysEvent aKeyEvent) {
switch (aKeyEvent) {
case MediaControlKeysEvent::eFocus:
return MediaControlKey::Focus;
case MediaControlKeysEvent::ePause:
return MediaControlKey::Pause;
case MediaControlKeysEvent::ePlay:
return MediaControlKey::Play;
case MediaControlKeysEvent::ePlayPause:
return MediaControlKey::Playpause;
case MediaControlKeysEvent::ePrevTrack:
return MediaControlKey::Previoustrack;
case MediaControlKeysEvent::eNextTrack:
return MediaControlKey::Nexttrack;
case MediaControlKeysEvent::eSeekBackward:
return MediaControlKey::Seekbackward;
case MediaControlKeysEvent::eSeekForward:
return MediaControlKey::Seekforward;
default:
MOZ_ASSERT(aKeyEvent == MediaControlKeysEvent::eStop);
return MediaControlKey::Stop;
}
}
inline const char* ToMediaPlaybackStateStr(MediaPlaybackState aState) {
switch (aState) {
case MediaPlaybackState::eStarted:

Просмотреть файл

@ -8,6 +8,7 @@
#include "MediaControlService.h"
#include "MediaControlUtils.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
@ -21,6 +22,32 @@
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_INHERITED(MediaController, DOMEventTargetHelper)
NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(MediaController,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(MediaController,
DOMEventTargetHelper)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
nsISupports* MediaController::GetParentObject() const {
RefPtr<BrowsingContext> bc = BrowsingContext::Get(Id());
return bc;
}
JSObject* MediaController::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return MediaController_Binding::Wrap(aCx, this, aGivenProto);
}
void MediaController::GetSupportedKeys(
nsTArray<MediaControlKey>& aRetVal) const {
aRetVal.Clear();
for (const auto& key : mSupportedKeys) {
// TODO : merge `MediaControlKey` and `MediaControlKeysEvent`.
aRetVal.AppendElement(ConvertToMediaControlKey(key));
}
}
static const MediaControlKeysEvent sDefaultSupportedKeys[] = {
MediaControlKeysEvent::eFocus, MediaControlKeysEvent::ePlay,
MediaControlKeysEvent::ePause, MediaControlKeysEvent::ePlayPause,
@ -260,6 +287,10 @@ void MediaController::HandleSupportedMediaSessionActionsChanged(
LOG("Supported keys changes");
mSupportedKeys = newSupportedKeys;
mSupportedKeysChangedEvent.Notify(mSupportedKeys);
RefPtr<AsyncEventDispatcher> asyncDispatcher = new AsyncEventDispatcher(
this, NS_LITERAL_STRING("supportedkeyschange"), CanBubble::eYes);
asyncDispatcher->PostDOMEvent();
MediaController_Binding::ClearCachedSupportedKeysValue(this);
}
CopyableTArray<MediaControlKeysEvent> MediaController::GetSupportedMediaKeys()

Просмотреть файл

@ -10,6 +10,8 @@
#include "MediaEventSource.h"
#include "MediaPlaybackStatus.h"
#include "MediaStatusManager.h"
#include "mozilla/DOMEventTargetHelper.h"
#include "mozilla/dom/MediaControllerBinding.h"
#include "mozilla/LinkedList.h"
#include "nsDataHashtable.h"
#include "nsISupportsImpl.h"
@ -66,15 +68,23 @@ class IMediaController {
* tabs playing media at the same time, we can use the ID to query the specific
* controller from `MediaControlService`.
*/
class MediaController final
: public IMediaController,
public MediaStatusManager,
public LinkedListElement<RefPtr<MediaController>> {
class MediaController final : public DOMEventTargetHelper,
public IMediaController,
public LinkedListElement<RefPtr<MediaController>>,
public MediaStatusManager {
public:
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaController, override);
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(MediaController,
DOMEventTargetHelper)
explicit MediaController(uint64_t aBrowsingContextId);
// WebIDL methods
nsISupports* GetParentObject() const;
JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
void GetSupportedKeys(nsTArray<MediaControlKey>& aRetVal) const;
IMPL_EVENT_HANDLER(supportedkeyschange);
// IMediaController's methods
void Focus() override;
void Play() override;

Просмотреть файл

@ -2380,6 +2380,9 @@ STATIC_ATOMS = [
Atom("onmark", "onmark"),
Atom("onboundary", "onboundary"),
# Media Controller
Atom("onsupportedkeyschange", "onsupportedkeyschange"),
# Contextual Identity / Containers
Atom("usercontextid", "usercontextid"),
Atom("geckoViewSessionContextId", "geckoViewSessionContextId"),