// Copyright 2016 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 "extensions/renderer/api_binding_test_util.h"

#include "base/json/json_reader.h"
#include "base/json/json_writer.h"
#include "base/strings/string_util.h"
#include "base/values.h"
#include "content/public/child/v8_value_converter.h"
#include "gin/converter.h"
#include "testing/gtest/include/gtest/gtest.h"

namespace extensions {

namespace {

// Common call function implementation. Calls the given |function| with the
// specified |receiver| and arguments. If the call succeeds (doesn't throw an
// error), populates |out_value| with the returned result. If the call does
// throw, populates |out_error| with the thrown error.
// Returns true if the function runs without throwing an error.
bool RunFunctionImpl(v8::Local<v8::Function> function,
                     v8::Local<v8::Context> context,
                     v8::Local<v8::Value> receiver,
                     int argc,
                     v8::Local<v8::Value> argv[],
                     v8::Local<v8::Value>* out_value,
                     std::string* out_error) {
  v8::TryCatch try_catch(context->GetIsolate());
  v8::MaybeLocal<v8::Value> maybe_result =
      function->Call(context, receiver, argc, argv);
  if (try_catch.HasCaught()) {
    *out_error = gin::V8ToString(try_catch.Message()->Get());
    return false;
  }
  v8::Local<v8::Value> result;
  if (!maybe_result.ToLocal(&result)) {
    *out_error = "Could not convert result to v8::Local.";
    return false;
  }
  *out_value = result;
  return true;
}

}  // namespace

std::string ReplaceSingleQuotes(base::StringPiece str) {
  std::string result;
  base::ReplaceChars(str.as_string(), "'", "\"", &result);
  return result;
}

std::unique_ptr<base::Value> ValueFromString(base::StringPiece str) {
  std::unique_ptr<base::Value> value =
      base::JSONReader::Read(ReplaceSingleQuotes(str));
  EXPECT_TRUE(value) << str;
  return value;
}

std::unique_ptr<base::ListValue> ListValueFromString(base::StringPiece str) {
  return base::ListValue::From(ValueFromString(str));
}

std::unique_ptr<base::DictionaryValue> DictionaryValueFromString(
    base::StringPiece str) {
  return base::DictionaryValue::From(ValueFromString(str));
}

std::string ValueToString(const base::Value& value) {
  std::string json;
  EXPECT_TRUE(base::JSONWriter::Write(value, &json));
  return json;
}

v8::Local<v8::Value> V8ValueFromScriptSource(v8::Local<v8::Context> context,
                                             base::StringPiece source) {
  v8::MaybeLocal<v8::Script> maybe_script = v8::Script::Compile(
      context, gin::StringToV8(context->GetIsolate(), source));
  v8::Local<v8::Script> script;
  if (!maybe_script.ToLocal(&script))
    return v8::Local<v8::Value>();
  return script->Run();
}

v8::Local<v8::Function> FunctionFromString(v8::Local<v8::Context> context,
                                           base::StringPiece source) {
  v8::Local<v8::Value> value = V8ValueFromScriptSource(context, source);
  v8::Local<v8::Function> function;
  EXPECT_TRUE(gin::ConvertFromV8(context->GetIsolate(), value, &function));
  return function;
}

std::unique_ptr<base::Value> V8ToBaseValue(v8::Local<v8::Value> value,
                                           v8::Local<v8::Context> context) {
  std::unique_ptr<content::V8ValueConverter> converter(
      content::V8ValueConverter::create());
  return converter->FromV8Value(value, context);
}

v8::Local<v8::Value> RunFunction(v8::Local<v8::Function> function,
                                 v8::Local<v8::Context> context,
                                 v8::Local<v8::Value> receiver,
                                 int argc,
                                 v8::Local<v8::Value> argv[]) {
  std::string error;
  v8::Local<v8::Value> result;
  EXPECT_TRUE(
      RunFunctionImpl(function, context, receiver, argc, argv, &result, &error))
      << error;
  EXPECT_FALSE(result.IsEmpty());
  return result;
}

v8::Local<v8::Value> RunFunction(v8::Local<v8::Function> function,
                                 v8::Local<v8::Context> context,
                                 int argc,
                                 v8::Local<v8::Value> argv[]) {
  return RunFunction(function, context, v8::Undefined(context->GetIsolate()),
                     argc, argv);
}

v8::Local<v8::Value> RunFunctionOnGlobal(v8::Local<v8::Function> function,
                                         v8::Local<v8::Context> context,
                                         int argc,
                                         v8::Local<v8::Value> argv[]) {
  return RunFunction(function, context, context->Global(), argc, argv);
}

void RunFunctionOnGlobalAndIgnoreResult(v8::Local<v8::Function> function,
                                        v8::Local<v8::Context> context,
                                        int argc,
                                        v8::Local<v8::Value> argv[]) {
  RunFunction(function, context, context->Global(), argc, argv);
}

void RunFunctionAndExpectError(v8::Local<v8::Function> function,
                               v8::Local<v8::Context> context,
                               v8::Local<v8::Value> receiver,
                               int argc,
                               v8::Local<v8::Value> argv[],
                               const std::string& expected_error) {
  std::string error;
  v8::Local<v8::Value> result;
  EXPECT_FALSE(RunFunctionImpl(function, context, receiver, argc, argv, &result,
                               &error));
  EXPECT_TRUE(result.IsEmpty());
  EXPECT_EQ(expected_error, error);
}

void RunFunctionAndExpectError(v8::Local<v8::Function> function,
                               v8::Local<v8::Context> context,
                               int argc,
                               v8::Local<v8::Value> argv[],
                               const std::string& expected_error) {
  RunFunctionAndExpectError(function, context,
                            v8::Undefined(context->GetIsolate()), argc, argv,
                            expected_error);
}

v8::Local<v8::Value> GetPropertyFromObject(v8::Local<v8::Object> object,
                                           v8::Local<v8::Context> context,
                                           base::StringPiece key) {
  v8::Local<v8::Value> result;
  EXPECT_TRUE(object->Get(context, gin::StringToV8(context->GetIsolate(), key))
                  .ToLocal(&result));
  return result;
}

std::unique_ptr<base::Value> GetBaseValuePropertyFromObject(
    v8::Local<v8::Object> object,
    v8::Local<v8::Context> context,
    base::StringPiece key) {
  return V8ToBaseValue(GetPropertyFromObject(object, context, key), context);
}

}  // namespace extensions
