910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "content/renderer/media/inspector_media_event_handler.h"

#include <string>
#include <utility>

#include "base/json/json_writer.h"

namespace content {

namespace {

std::optional<blink::InspectorPlayerError> ErrorFromParams(
    const base::Value::Dict& param) {
  std::optional<int> code = param.FindInt(media::StatusConstants::kCodeKey);
  const std::string* group =
      param.FindString(media::StatusConstants::kGroupKey);
  const std::string* message =
      param.FindString(media::StatusConstants::kMsgKey);

  // message might be empty or not present, but group and code are required.
  CHECK(code.has_value() && group);

  blink::InspectorPlayerErrors caused_by;
  if (const auto* c = param.FindDict(media::StatusConstants::kCauseKey)) {
    auto parsed_cause = ErrorFromParams(*c);
    if (parsed_cause.has_value())
      caused_by.push_back(*parsed_cause);
  }

  std::vector<blink::InspectorPlayerError::SourceLocation> stack_vec;
  if (const auto* vec = param.FindList(media::StatusConstants::kStackKey)) {
    for (const auto& loc : *vec) {
      const auto& loc_dict = loc.GetDict();
      const std::string* file =
          loc_dict.FindString(media::StatusConstants::kFileKey);
      std::optional<int> line =
          loc_dict.FindInt(media::StatusConstants::kLineKey);
      if (!file || !line.has_value())
        continue;
      blink::InspectorPlayerError::SourceLocation entry = {
          blink::WebString::FromUTF8(*file), *line};
      stack_vec.push_back(std::move(entry));
    }
  }

  std::vector<blink::InspectorPlayerError::Data> data_vec;
  if (auto* data = param.FindDict(media::StatusConstants::kDataKey)) {
    for (const auto pair : *data) {
      std::string json = base::WriteJson(pair.second).value_or("");
      blink::InspectorPlayerError::Data entry = {
          blink::WebString::FromUTF8(pair.first),
          blink::WebString::FromUTF8(json)};
      data_vec.push_back(std::move(entry));
    }
  }

  blink::InspectorPlayerError result = {
      blink::WebString::FromUTF8(*group),
      *code,
      blink::WebString::FromUTF8(message ? *message : ""),
      std::move(stack_vec),
      std::move(caused_by),
      std::move(data_vec)};

  return std::move(result);
}

blink::WebString ToString(const base::Value& value) {
  if (value.is_string()) {
    return blink::WebString::FromUTF8(value.GetString());
  }
  std::string output_str = base::WriteJson(value).value_or("");
  return blink::WebString::FromUTF8(output_str);
}

blink::WebString ToString(const base::Value::Dict& value) {
  std::string output_str = base::WriteJson(value).value_or("");
  return blink::WebString::FromUTF8(output_str);
}

// TODO(tmathmeyer) stop using a string here eventually. This means rewriting
// the MediaLogRecord mojom interface.
blink::InspectorPlayerMessage::Level LevelFromString(const std::string& level) {
  if (level == "error")
    return blink::InspectorPlayerMessage::Level::kError;
  if (level == "warning")
    return blink::InspectorPlayerMessage::Level::kWarning;
  if (level == "info")
    return blink::InspectorPlayerMessage::Level::kInfo;
  CHECK_EQ(level, "debug");
  return blink::InspectorPlayerMessage::Level::kDebug;
}

}  // namespace

InspectorMediaEventHandler::InspectorMediaEventHandler(
    blink::MediaInspectorContext* inspector_context,
    int dom_node_id)
    : inspector_context_(inspector_context),
      player_id_(inspector_context_->CreatePlayer()) {
  inspector_context->SetDomNodeIdForPlayer(player_id_, dom_node_id);
}

// TODO(tmathmeyer) It would be wonderful if the definition for MediaLogRecord
// and InspectorPlayerEvent / InspectorPlayerProperty could be unified so that
// this method is no longer needed. Refactor MediaLogRecord at some point.
void InspectorMediaEventHandler::SendQueuedMediaEvents(
    std::vector<media::MediaLogRecord> events_to_send) {
  // If the video player is gone, the whole frame
  if (video_player_destroyed_)
    return;

  blink::InspectorPlayerProperties properties;
  blink::InspectorPlayerMessages messages;
  blink::InspectorPlayerEvents events;
  blink::InspectorPlayerErrors errors;

  for (media::MediaLogRecord event : events_to_send) {
    switch (event.type) {
      case media::MediaLogRecord::Type::kMessage: {
        for (auto&& itr : event.params) {
          blink::InspectorPlayerMessage msg = {
              LevelFromString(itr.first),
              blink::WebString::FromUTF8(itr.second.GetString())};
          messages.emplace_back(std::move(msg));
        }
        break;
      }
      case media::MediaLogRecord::Type::kMediaPropertyChange: {
        for (auto&& itr : event.params) {
          blink::InspectorPlayerProperty prop = {
              blink::WebString::FromUTF8(itr.first), ToString(itr.second)};
          properties.emplace_back(std::move(prop));
        }
        break;
      }
      case media::MediaLogRecord::Type::kMediaEventTriggered: {
        blink::InspectorPlayerEvent ev = {event.time, ToString(event.params)};
        events.emplace_back(std::move(ev));
        break;
      }
      case media::MediaLogRecord::Type::kMediaStatus: {
        std::optional<blink::InspectorPlayerError> error =
            ErrorFromParams(event.params);
        if (error.has_value())
          errors.emplace_back(std::move(*error));
      }
    }
  }

  if (!events.empty())
    inspector_context_->NotifyPlayerEvents(player_id_, std::move(events));

  if (!properties.empty())
    inspector_context_->SetPlayerProperties(player_id_, std::move(properties));

  if (!messages.empty())
    inspector_context_->NotifyPlayerMessages(player_id_, std::move(messages));

  if (!errors.empty())
    inspector_context_->NotifyPlayerErrors(player_id_, std::move(errors));
}

void InspectorMediaEventHandler::OnWebMediaPlayerDestroyed() {
  video_player_destroyed_ = true;
  inspector_context_->DestroyPlayer(player_id_);
}

}  // namespace content