// 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/dom_activity_logger.h"

#include <memory>
#include <utility>

#include "content/public/renderer/render_thread.h"
#include "content/public/renderer/v8_value_converter.h"
#include "extensions/common/dom_action_types.h"
#include "extensions/renderer/activity_log_converter_strategy.h"
#include "ipc/ipc_sync_channel.h"
#include "third_party/blink/public/platform/web_string.h"
#include "third_party/blink/public/platform/web_url.h"
#include "v8/include/v8-isolate.h"

using blink::WebString;
using blink::WebURL;

namespace extensions {

namespace {

// Converts the given |v8_value| and appends it to the given |list|, if the
// conversion succeeds.
void AppendV8Value(const std::string& api_name,
                   const v8::Local<v8::Value>& v8_value,
                   base::Value::List& list) {
  std::unique_ptr<content::V8ValueConverter> converter =
      content::V8ValueConverter::Create();
  ActivityLogConverterStrategy strategy;
  converter->SetFunctionAllowed(true);
  converter->SetStrategy(&strategy);
  std::unique_ptr<base::Value> value(converter->FromV8Value(
      v8_value, v8::Isolate::GetCurrent()->GetCurrentContext()));

  if (value)
    list.Append(base::Value::FromUniquePtrValue(std::move(value)));
}

}  // namespace

DOMActivityLogger::DOMActivityLogger(const std::string& extension_id)
    : extension_id_(extension_id) {}

DOMActivityLogger::~DOMActivityLogger() = default;

void DOMActivityLogger::AttachToWorld(int32_t world_id,
                                      const std::string& extension_id) {
  // If there is no logger registered for world_id, construct a new logger
  // and register it with world_id.
  if (!blink::HasDOMActivityLogger(world_id,
                                   WebString::FromUTF8(extension_id))) {
    DOMActivityLogger* logger = new DOMActivityLogger(extension_id);
    blink::SetDOMActivityLogger(world_id, WebString::FromUTF8(extension_id),
                                logger);
  }
}

void DOMActivityLogger::LogGetter(const WebString& api_name,
                                  const WebURL& url,
                                  const WebString& title) {
  GetRendererHost()->AddDOMActionToActivityLog(
      extension_id_, api_name.Utf8(), base::Value::List(), url, title.Utf16(),
      DomActionType::GETTER);
}

void DOMActivityLogger::LogSetter(const WebString& api_name,
                                  const v8::Local<v8::Value>& new_value,
                                  const WebURL& url,
                                  const WebString& title) {
  logSetter(api_name, new_value, v8::Local<v8::Value>(), url, title);
}

void DOMActivityLogger::logSetter(const WebString& api_name,
                                  const v8::Local<v8::Value>& new_value,
                                  const v8::Local<v8::Value>& old_value,
                                  const WebURL& url,
                                  const WebString& title) {
  base::Value::List args;
  std::string api_name_utf8 = api_name.Utf8();
  AppendV8Value(api_name_utf8, new_value, args);
  if (!old_value.IsEmpty())
    AppendV8Value(api_name_utf8, old_value, args);
  GetRendererHost()->AddDOMActionToActivityLog(
      extension_id_, api_name_utf8, std::move(args), url, title.Utf16(),
      DomActionType::SETTER);
}

void DOMActivityLogger::LogMethod(const WebString& api_name,
                                  int argc,
                                  const v8::Local<v8::Value>* argv,
                                  const WebURL& url,
                                  const WebString& title) {
  base::Value::List args;
  std::string api_name_utf8 = api_name.Utf8();
  for (int i = 0; i < argc; ++i)
    AppendV8Value(api_name_utf8, argv[i], args);
  GetRendererHost()->AddDOMActionToActivityLog(
      extension_id_, api_name_utf8, std::move(args), url, title.Utf16(),
      DomActionType::METHOD);
}

void DOMActivityLogger::LogEvent(const WebString& event_name,
                                 int argc,
                                 const WebString* argv,
                                 const WebURL& url,
                                 const WebString& title) {
  base::Value::List args;
  std::string event_name_utf8 = event_name.Utf8();
  for (int i = 0; i < argc; ++i)
    args.Append(argv[i].Utf8());
  GetRendererHost()->AddDOMActionToActivityLog(
      extension_id_, event_name_utf8, std::move(args), url, title.Utf16(),
      DomActionType::METHOD);
}

mojom::RendererHost* DOMActivityLogger::GetRendererHost() {
  if (!renderer_host_.is_bound()) {
    content::RenderThread::Get()->GetChannel()->GetRemoteAssociatedInterface(
        &renderer_host_);
  }
  return renderer_host_.get();
}

}  // namespace extensions