blob: a7ff21b43a6efb91796f870cfcac046b613eba06 [file] [log] [blame]
// Copyright 2019 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/core/inspector/inspector_media_context_impl.h"
#include <unordered_set>
#include <utility>
#include "base/unguessable_token.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
namespace blink {
const char MediaInspectorContextImpl::kSupplementName[] =
"MediaInspectorContextImpl";
// static
MediaInspectorContextImpl* MediaInspectorContextImpl::From(
ExecutionContext& execution_context) {
auto* context = Supplement<ExecutionContext>::From<MediaInspectorContextImpl>(
execution_context);
if (!context) {
context =
MakeGarbageCollected<MediaInspectorContextImpl>(execution_context);
Supplement<ExecutionContext>::ProvideTo(execution_context, context);
}
return context;
}
MediaInspectorContextImpl::MediaInspectorContextImpl(ExecutionContext& context)
: Supplement<ExecutionContext>(context) {
DCHECK(context.IsWindow() || context.IsWorkerGlobalScope());
}
// Local to cc file for converting
template <typename T, typename Iterable>
static Vector<T> Iter2Vector(const Iterable& iterable) {
Vector<T> result;
result.AppendRange(iterable.begin(), iterable.end());
return result;
}
// Garbage collection method.
void MediaInspectorContextImpl::Trace(Visitor* visitor) const {
Supplement<ExecutionContext>::Trace(visitor);
visitor->Trace(players_);
}
Vector<WebString> MediaInspectorContextImpl::AllPlayerIdsAndMarkSent() {
Vector<WebString> existing_players;
const auto& keys = players_.Keys();
existing_players.AppendRange(keys.begin(), keys.end());
unsent_players_.clear();
return existing_players;
}
const MediaPlayer& MediaInspectorContextImpl::MediaPlayerFromId(
const WebString& player_id) {
const auto& player = players_.find(player_id);
DCHECK_NE(player, players_.end());
return *player->value;
}
WebString MediaInspectorContextImpl::CreatePlayer() {
String next_player_id =
String::FromUTF8(base::UnguessableToken::Create().ToString());
players_.insert(next_player_id, MakeGarbageCollected<MediaPlayer>());
probe::PlayersCreated(GetSupplementable(), {next_player_id});
if (!GetSupplementable()->GetProbeSink()->HasInspectorMediaAgents())
unsent_players_.push_back(next_player_id);
return next_player_id;
}
void MediaInspectorContextImpl::RemovePlayer(const WebString& playerId) {
const auto& player = players_.Take(playerId);
if (player) {
total_event_count_ -=
player->errors.size() + player->events.size() + player->messages.size();
DCHECK_GE(total_event_count_, 0);
}
}
void MediaInspectorContextImpl::TrimPlayer(const WebString& playerId) {
MediaPlayer* player = players_.Take(playerId);
wtf_size_t overage = total_event_count_ - kMaxCachedPlayerEvents;
wtf_size_t excess = std::min<wtf_size_t>(overage, player->events.size());
player->events.EraseAt(0, excess);
total_event_count_ -= excess;
overage -= excess;
excess = std::min(overage, player->messages.size());
player->messages.EraseAt(0, excess);
total_event_count_ -= excess;
overage -= excess;
excess = std::min(overage, player->errors.size());
player->errors.EraseAt(0, excess);
total_event_count_ -= excess;
overage -= excess;
players_.insert(playerId, player);
}
void MediaInspectorContextImpl::CullPlayers(const WebString& prefer_keep) {
// Erase all the dead players, but only erase the required number of others.
for (const auto& playerId : dead_players_)
RemovePlayer(playerId);
dead_players_.clear();
while (!expendable_players_.IsEmpty()) {
if (total_event_count_ <= kMaxCachedPlayerEvents)
return;
RemovePlayer(expendable_players_.back());
expendable_players_.pop_back();
}
while (!unsent_players_.IsEmpty()) {
if (total_event_count_ <= kMaxCachedPlayerEvents)
return;
RemovePlayer(unsent_players_.back());
unsent_players_.pop_back();
}
// TODO(tmathmeyer) keep last event time stamps for players to remove the
// most stale one.
while (players_.size() > 1) {
if (total_event_count_ <= kMaxCachedPlayerEvents)
return;
auto iterator = players_.begin();
// Make sure not to delete the item that is preferred to keep.
if (WTF::String(prefer_keep) == iterator->key)
++iterator;
RemovePlayer(iterator->key);
}
// When there is only one player, selectively remove the oldest events.
if (players_.size() == 1 && total_event_count_ > kMaxCachedPlayerEvents)
TrimPlayer(players_.begin()->key);
}
void MediaInspectorContextImpl::DestroyPlayer(const WebString& playerId) {
if (unsent_players_.Contains(String(playerId))) {
// unsent players become dead when destroyed.
unsent_players_.EraseAt(unsent_players_.Find(String(playerId)));
dead_players_.push_back(playerId);
} else {
expendable_players_.push_back(playerId);
}
}
// Convert public version of event to protocol version, and send it.
void MediaInspectorContextImpl::NotifyPlayerErrors(
WebString playerId,
const InspectorPlayerErrors& errors) {
const auto& player = players_.find(playerId);
if (player != players_.end()) {
player->value->errors.AppendRange(errors.begin(), errors.end());
total_event_count_ += errors.size();
if (total_event_count_ > kMaxCachedPlayerEvents)
CullPlayers(playerId);
}
Vector<InspectorPlayerError> vector =
Iter2Vector<InspectorPlayerError>(errors);
probe::PlayerErrorsRaised(GetSupplementable(), playerId, vector);
}
void MediaInspectorContextImpl::NotifyPlayerEvents(
WebString playerId,
const InspectorPlayerEvents& events) {
const auto& player = players_.find(playerId);
if (player != players_.end()) {
player->value->events.AppendRange(events.begin(), events.end());
total_event_count_ += events.size();
if (total_event_count_ > kMaxCachedPlayerEvents)
CullPlayers(playerId);
}
Vector<InspectorPlayerEvent> vector =
Iter2Vector<InspectorPlayerEvent>(events);
probe::PlayerEventsAdded(GetSupplementable(), playerId, vector);
}
void MediaInspectorContextImpl::SetPlayerProperties(
WebString playerId,
const InspectorPlayerProperties& props) {
const auto& player = players_.find(playerId);
if (player != players_.end()) {
for (const auto& property : props)
player->value->properties.insert(property.name, property);
}
Vector<InspectorPlayerProperty> vector =
Iter2Vector<InspectorPlayerProperty>(props);
probe::PlayerPropertiesChanged(GetSupplementable(), playerId, vector);
}
void MediaInspectorContextImpl::NotifyPlayerMessages(
WebString playerId,
const InspectorPlayerMessages& messages) {
const auto& player = players_.find(playerId);
if (player != players_.end()) {
player->value->messages.AppendRange(messages.begin(), messages.end());
total_event_count_ += messages.size();
if (total_event_count_ > kMaxCachedPlayerEvents)
CullPlayers(playerId);
}
Vector<InspectorPlayerMessage> vector =
Iter2Vector<InspectorPlayerMessage>(messages);
probe::PlayerMessagesLogged(GetSupplementable(), playerId, vector);
}
HeapHashMap<String, Member<MediaPlayer>>*
MediaInspectorContextImpl::GetPlayersForTesting() {
return &players_;
}
} // namespace blink