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

#include "extensions/renderer/api_activity_logger.h"

#include <stddef.h>

#include <string>
#include <utility>

#include "base/functional/bind.h"
#include "content/public/renderer/v8_value_converter.h"
#include "extensions/renderer/activity_log_converter_strategy.h"
#include "extensions/renderer/dispatcher.h"
#include "extensions/renderer/extensions_renderer_client.h"
#include "extensions/renderer/ipc_message_sender.h"
#include "extensions/renderer/script_context.h"
#include "extensions/renderer/worker_script_context_set.h"
#include "extensions/renderer/worker_thread_util.h"
#include "v8/include/v8-container.h"
#include "v8/include/v8-function-callback.h"
#include "v8/include/v8-isolate.h"
#include "v8/include/v8-primitive.h"

namespace extensions {

namespace {

bool g_log_for_testing = false;

ScriptContext* GetContextByV8Context(v8::Local<v8::Context> context) {
  if (worker_thread_util::IsWorkerThread()) {
    return Dispatcher::GetWorkerScriptContextSet()->GetContextByV8Context(
        context);
  }

  return ScriptContextSet::GetContextByV8Context(context);
}

}  // namespace

APIActivityLogger::APIActivityLogger(IPCMessageSender* ipc_sender,
                                     ScriptContext* context)
    : ObjectBackedNativeHandler(context), ipc_sender_(ipc_sender) {}

APIActivityLogger::~APIActivityLogger() = default;

void APIActivityLogger::AddRoutes() {
  RouteHandlerFunction(
      "LogEvent",
      base::BindRepeating(&APIActivityLogger::LogForJS, base::Unretained(this),
                          IPCMessageSender::ActivityLogCallType::EVENT));
  RouteHandlerFunction(
      "LogAPICall",
      base::BindRepeating(&APIActivityLogger::LogForJS, base::Unretained(this),
                          IPCMessageSender::ActivityLogCallType::APICALL));
}

// static
bool APIActivityLogger::IsLoggingEnabled() {
  const Dispatcher* dispatcher = ExtensionsRendererClient::Get()->dispatcher();
  return (dispatcher &&  // dispatcher can be null in unittests.
          dispatcher->activity_logging_enabled()) ||
         g_log_for_testing;
}

// static
void APIActivityLogger::LogAPICall(
    IPCMessageSender* ipc_sender,
    v8::Local<v8::Context> context,
    const std::string& call_name,
    const v8::LocalVector<v8::Value>& arguments) {
  if (!IsLoggingEnabled())
    return;

  ScriptContext* script_context = GetContextByV8Context(context);
  if (!script_context)
    return;

  std::unique_ptr<content::V8ValueConverter> converter =
      content::V8ValueConverter::Create();
  ActivityLogConverterStrategy strategy;
  converter->SetFunctionAllowed(true);
  converter->SetStrategy(&strategy);

  base::Value::List value_args;
  value_args.reserve(arguments.size());
  // TODO(devlin): This doesn't protect against custom properties, so it might
  // not perfectly reflect the passed arguments.
  for (const auto& arg : arguments) {
    std::unique_ptr<base::Value> converted_arg =
        converter->FromV8Value(arg, context);
    if (!converted_arg)
      converted_arg = std::make_unique<base::Value>();
    value_args.Append(
        base::Value::FromUniquePtrValue(std::move(converted_arg)));
  }

  ipc_sender->SendActivityLogIPC(script_context,
                                 script_context->GetExtensionID(),
                                 IPCMessageSender::ActivityLogCallType::APICALL,
                                 call_name, std::move(value_args),
                                 /*extra=*/std::string());
}

void APIActivityLogger::LogEvent(IPCMessageSender* ipc_sender,
                                 ScriptContext* script_context,
                                 const std::string& event_name,
                                 base::Value::List arguments) {
  if (!IsLoggingEnabled())
    return;

  ipc_sender->SendActivityLogIPC(script_context,
                                 script_context->GetExtensionID(),
                                 IPCMessageSender::ActivityLogCallType::EVENT,
                                 event_name, std::move(arguments),
                                 /*extra=*/std::string());
}

void APIActivityLogger::set_log_for_testing(bool log) {
  g_log_for_testing = log;
}

void APIActivityLogger::LogForJS(
    const IPCMessageSender::ActivityLogCallType call_type,
    const v8::FunctionCallbackInfo<v8::Value>& args) {
  CHECK_GT(args.Length(), 2);
  CHECK(args[0]->IsString());
  CHECK(args[1]->IsString());
  CHECK(args[2]->IsArray());

  if (!IsLoggingEnabled())
    return;

  v8::Isolate* isolate = args.GetIsolate();
  v8::HandleScope handle_scope(isolate);
  v8::Local<v8::Context> context = isolate->GetCurrentContext();

  ScriptContext* script_context = GetContextByV8Context(context);
  if (!script_context) {
    return;
  }

  std::string extension_id = *v8::String::Utf8Value(isolate, args[0]);
  std::string call_name = *v8::String::Utf8Value(isolate, args[1]);
  std::string extra;
  if (args.Length() == 4) {  // Extras are optional.
    CHECK(args[3]->IsString());
    extra = *v8::String::Utf8Value(isolate, args[3]);
  }

  // Get the array of call arguments.
  base::Value::List arguments;
  v8::Local<v8::Array> arg_array = v8::Local<v8::Array>::Cast(args[2]);
  if (arg_array->Length() > 0) {
    arguments.reserve(arg_array->Length());
    std::unique_ptr<content::V8ValueConverter> converter =
        content::V8ValueConverter::Create();
    ActivityLogConverterStrategy strategy;
    converter->SetFunctionAllowed(true);
    converter->SetStrategy(&strategy);
    for (size_t i = 0; i < arg_array->Length(); ++i) {
      // TODO(crbug.com/40605992): Possibly replace ToLocalChecked here with
      // actual error handling.
      std::unique_ptr<base::Value> converted_arg = converter->FromV8Value(
          arg_array->Get(context, i).ToLocalChecked(), context);
      if (!converted_arg)
        converted_arg = std::make_unique<base::Value>();
      arguments.Append(
          base::Value::FromUniquePtrValue(std::move(converted_arg)));
    }
  }

  ipc_sender_->SendActivityLogIPC(script_context, extension_id, call_type,
                                  call_name, std::move(arguments), extra);
}

}  // namespace extensions