blob: 6173ecfc1ae05f4c48ede64958ad53910b9d9d27 [file] [log] [blame]
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/modules/mediasession/media_session.h"
#include <memory>
#include "base/optional.h"
#include "services/service_manager/public/cpp/interface_provider.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_media_session_action_handler.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/user_gesture_indicator.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/local_frame.h"
#include "third_party/blink/renderer/modules/mediasession/media_metadata.h"
#include "third_party/blink/renderer/modules/mediasession/media_metadata_sanitizer.h"
namespace blink {
namespace {
using ::media_session::mojom::blink::MediaSessionAction;
const AtomicString& MojomActionToActionName(MediaSessionAction action) {
DEFINE_STATIC_LOCAL(const AtomicString, play_action_name, ("play"));
DEFINE_STATIC_LOCAL(const AtomicString, pause_action_name, ("pause"));
DEFINE_STATIC_LOCAL(const AtomicString, previous_track_action_name,
("previoustrack"));
DEFINE_STATIC_LOCAL(const AtomicString, next_track_action_name,
("nexttrack"));
DEFINE_STATIC_LOCAL(const AtomicString, seek_backward_action_name,
("seekbackward"));
DEFINE_STATIC_LOCAL(const AtomicString, seek_forward_action_name,
("seekforward"));
switch (action) {
case MediaSessionAction::kPlay:
return play_action_name;
case MediaSessionAction::kPause:
return pause_action_name;
case MediaSessionAction::kPreviousTrack:
return previous_track_action_name;
case MediaSessionAction::kNextTrack:
return next_track_action_name;
case MediaSessionAction::kSeekBackward:
return seek_backward_action_name;
case MediaSessionAction::kSeekForward:
return seek_forward_action_name;
default:
NOTREACHED();
}
return WTF::g_empty_atom;
}
base::Optional<MediaSessionAction> ActionNameToMojomAction(
const String& action_name) {
if ("play" == action_name)
return MediaSessionAction::kPlay;
if ("pause" == action_name)
return MediaSessionAction::kPause;
if ("previoustrack" == action_name)
return MediaSessionAction::kPreviousTrack;
if ("nexttrack" == action_name)
return MediaSessionAction::kNextTrack;
if ("seekbackward" == action_name)
return MediaSessionAction::kSeekBackward;
if ("seekforward" == action_name)
return MediaSessionAction::kSeekForward;
NOTREACHED();
return base::nullopt;
}
const AtomicString& MediaSessionPlaybackStateToString(
mojom::blink::MediaSessionPlaybackState state) {
DEFINE_STATIC_LOCAL(const AtomicString, none_value, ("none"));
DEFINE_STATIC_LOCAL(const AtomicString, paused_value, ("paused"));
DEFINE_STATIC_LOCAL(const AtomicString, playing_value, ("playing"));
switch (state) {
case mojom::blink::MediaSessionPlaybackState::NONE:
return none_value;
case mojom::blink::MediaSessionPlaybackState::PAUSED:
return paused_value;
case mojom::blink::MediaSessionPlaybackState::PLAYING:
return playing_value;
}
NOTREACHED();
return WTF::g_empty_atom;
}
mojom::blink::MediaSessionPlaybackState StringToMediaSessionPlaybackState(
const String& state_name) {
if (state_name == "none")
return mojom::blink::MediaSessionPlaybackState::NONE;
if (state_name == "paused")
return mojom::blink::MediaSessionPlaybackState::PAUSED;
DCHECK_EQ(state_name, "playing");
return mojom::blink::MediaSessionPlaybackState::PLAYING;
}
} // anonymous namespace
MediaSession::MediaSession(ExecutionContext* execution_context)
: ContextClient(execution_context),
playback_state_(mojom::blink::MediaSessionPlaybackState::NONE),
client_binding_(this) {}
MediaSession* MediaSession::Create(ExecutionContext* execution_context) {
return MakeGarbageCollected<MediaSession>(execution_context);
}
void MediaSession::Dispose() {
client_binding_.Close();
}
void MediaSession::setPlaybackState(const String& playback_state) {
playback_state_ = StringToMediaSessionPlaybackState(playback_state);
mojom::blink::MediaSessionService* service = GetService();
if (service)
service->SetPlaybackState(playback_state_);
}
String MediaSession::playbackState() {
return MediaSessionPlaybackStateToString(playback_state_);
}
void MediaSession::setMetadata(MediaMetadata* metadata) {
if (metadata)
metadata->SetSession(this);
if (metadata_)
metadata_->SetSession(nullptr);
metadata_ = metadata;
OnMetadataChanged();
}
MediaMetadata* MediaSession::metadata() const {
return metadata_;
}
void MediaSession::OnMetadataChanged() {
mojom::blink::MediaSessionService* service = GetService();
if (!service)
return;
service->SetMetadata(MediaMetadataSanitizer::SanitizeAndConvertToMojo(
metadata_, GetExecutionContext()));
}
void MediaSession::setActionHandler(const String& action,
V8MediaSessionActionHandler* handler) {
if (handler) {
auto add_result = action_handlers_.Set(action, handler);
if (!add_result.is_new_entry)
return;
NotifyActionChange(action, ActionChangeType::kActionEnabled);
} else {
if (action_handlers_.find(action) == action_handlers_.end())
return;
action_handlers_.erase(action);
NotifyActionChange(action, ActionChangeType::kActionDisabled);
}
}
void MediaSession::NotifyActionChange(const String& action,
ActionChangeType type) {
mojom::blink::MediaSessionService* service = GetService();
if (!service)
return;
auto mojom_action = ActionNameToMojomAction(action);
DCHECK(mojom_action.has_value());
switch (type) {
case ActionChangeType::kActionEnabled:
service->EnableAction(mojom_action.value());
break;
case ActionChangeType::kActionDisabled:
service->DisableAction(mojom_action.value());
break;
}
}
mojom::blink::MediaSessionService* MediaSession::GetService() {
if (service_)
return service_.get();
if (!GetExecutionContext())
return nullptr;
Document* document = To<Document>(GetExecutionContext());
LocalFrame* frame = document->GetFrame();
if (!frame)
return nullptr;
// See https://bit.ly/2S0zRAS for task types.
auto task_runner =
GetExecutionContext()->GetTaskRunner(TaskType::kMiscPlatformAPI);
frame->GetInterfaceProvider().GetInterface(
mojo::MakeRequest(&service_, task_runner));
if (service_.get()) {
// Record the eTLD+1 of the frame using the API.
Platform::Current()->RecordRapporURL("Media.Session.APIUsage.Origin",
document->Url());
blink::mojom::blink::MediaSessionClientPtr client;
client_binding_.Bind(mojo::MakeRequest(&client, task_runner), task_runner);
service_->SetClient(std::move(client));
}
return service_.get();
}
void MediaSession::DidReceiveAction(
media_session::mojom::blink::MediaSessionAction action) {
Document* document = To<Document>(GetExecutionContext());
std::unique_ptr<UserGestureIndicator> gesture_indicator =
LocalFrame::NotifyUserActivation(document ? document->GetFrame()
: nullptr);
auto iter = action_handlers_.find(MojomActionToActionName(action));
if (iter == action_handlers_.end())
return;
iter->value->InvokeAndReportException(this);
}
void MediaSession::Trace(blink::Visitor* visitor) {
visitor->Trace(metadata_);
visitor->Trace(action_handlers_);
ScriptWrappable::Trace(visitor);
ContextClient::Trace(visitor);
}
} // namespace blink