| // 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 |