123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206 |
- // Copyright 2017 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/bindings/exception_handler.h"
- #include <string>
- #include <tuple>
- #include "base/bind.h"
- #include "base/strings/stringprintf.h"
- #include "extensions/renderer/bindings/api_binding_test.h"
- #include "extensions/renderer/bindings/api_binding_test_util.h"
- #include "gin/converter.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "third_party/abseil-cpp/absl/types/optional.h"
- namespace extensions {
- namespace {
- void PopulateError(absl::optional<std::string>* error_out,
- v8::Local<v8::Context> context,
- const std::string& error) {
- *error_out = error;
- }
- void ThrowException(v8::Local<v8::Context> context,
- const std::string& to_throw,
- ExceptionHandler* handler) {
- v8::Isolate* isolate = context->GetIsolate();
- v8::TryCatch try_catch(isolate);
- v8::Local<v8::Function> function = FunctionFromString(
- context,
- base::StringPrintf("(function() { throw %s; })", to_throw.c_str()));
- std::ignore = function->Call(context, v8::Undefined(isolate), 0, nullptr);
- ASSERT_TRUE(try_catch.HasCaught());
- handler->HandleException(context, "handled", &try_catch);
- }
- } // namespace
- using ExceptionHandlerTest = APIBindingTest;
- TEST_F(ExceptionHandlerTest, TestBasicHandling) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- absl::optional<std::string> logged_error;
- ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
- ThrowException(context, "new Error('some error')", &handler);
- ASSERT_TRUE(logged_error);
- EXPECT_THAT(*logged_error, testing::StartsWith("handled: Error: some error"));
- }
- TEST_F(ExceptionHandlerTest, PerContextHandlers) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context_a = MainContext();
- v8::Local<v8::Context> context_b = AddContext();
- absl::optional<std::string> logged_error;
- ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
- v8::Local<v8::Function> custom_handler = FunctionFromString(
- context_a,
- "(function(message, exception) {\n"
- " this.loggedMessage = message;\n"
- " this.loggedExceptionMessage = exception && exception.message;\n"
- "})");
- handler.SetHandlerForContext(context_a, custom_handler);
- ThrowException(context_a, "new Error('context a error')", &handler);
- EXPECT_FALSE(logged_error);
- EXPECT_THAT(GetStringPropertyFromObject(context_a->Global(), context_a,
- "loggedMessage"),
- testing::StartsWith("\"handled: Error: context a error"));
- EXPECT_EQ("\"context a error\"",
- GetStringPropertyFromObject(context_a->Global(), context_a,
- "loggedExceptionMessage"));
- ASSERT_TRUE(context_a->Global()
- ->Set(context_a,
- gin::StringToSymbol(isolate(), "loggedMessage"),
- v8::Undefined(isolate()))
- .ToChecked());
- ASSERT_TRUE(
- context_a->Global()
- ->Set(context_a,
- gin::StringToSymbol(isolate(), "loggedExceptionMessage"),
- v8::Undefined(isolate()))
- .ToChecked());
- ThrowException(context_b, "new Error('context b error')", &handler);
- ASSERT_TRUE(logged_error);
- EXPECT_THAT(*logged_error,
- testing::StartsWith("handled: Error: context b error"));
- EXPECT_EQ("undefined", GetStringPropertyFromObject(
- context_a->Global(), context_a, "loggedMessage"));
- EXPECT_EQ("undefined",
- GetStringPropertyFromObject(context_a->Global(), context_a,
- "loggedExceptionMessage"));
- }
- TEST_F(ExceptionHandlerTest, ThrowingNonErrors) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- absl::optional<std::string> logged_error;
- ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
- ThrowException(context, "'hello'", &handler);
- ASSERT_TRUE(logged_error);
- EXPECT_EQ("handled: Uncaught hello", *logged_error);
- logged_error.reset();
- ThrowException(context, "{ message: 'hello' }", &handler);
- ASSERT_TRUE(logged_error);
- EXPECT_EQ("handled: Uncaught #<Object>", *logged_error);
- logged_error.reset();
- ThrowException(context, "{ toString: function() { throw 'goodbye' } }",
- &handler);
- ASSERT_TRUE(logged_error);
- EXPECT_EQ("handled: Uncaught [object Object]", *logged_error);
- v8::Local<v8::Function> custom_handler =
- FunctionFromString(context,
- "(function(message, exception) {\n"
- " this.loggedMessage = message;\n"
- " this.loggedException = exception;\n"
- "})");
- handler.SetHandlerForContext(context, custom_handler);
- ThrowException(context, "'hello'", &handler);
- EXPECT_EQ(
- "\"handled: Uncaught hello\"",
- GetStringPropertyFromObject(context->Global(), context, "loggedMessage"));
- EXPECT_EQ("\"hello\"", GetStringPropertyFromObject(context->Global(), context,
- "loggedException"));
- }
- TEST_F(ExceptionHandlerTest, StackTraces) {
- v8::HandleScope handle_scope(isolate());
- v8::Local<v8::Context> context = MainContext();
- absl::optional<std::string> logged_error;
- ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
- {
- v8::TryCatch try_catch(isolate());
- v8::Local<v8::Script> script =
- v8::Script::Compile(context,
- gin::StringToV8(context->GetIsolate(),
- "throw new Error('simple');"))
- .ToLocalChecked();
- ASSERT_TRUE(script->Run(context).IsEmpty());
- ASSERT_TRUE(try_catch.HasCaught());
- handler.HandleException(context, "handled", &try_catch);
- ASSERT_TRUE(logged_error);
- EXPECT_EQ("handled: Error: simple\n at <anonymous>:1:7", *logged_error);
- }
- logged_error.reset();
- {
- v8::TryCatch try_catch(isolate());
- v8::Local<v8::Function> throw_error_function = FunctionFromString(
- context, "(function() { throw new Error('function'); })");
- std::ignore = throw_error_function->Call(context, v8::Undefined(isolate()),
- 0, nullptr);
- ASSERT_TRUE(try_catch.HasCaught());
- handler.HandleException(context, "handled", &try_catch);
- ASSERT_TRUE(logged_error);
- EXPECT_EQ("handled: Error: function\n at <anonymous>:1:21",
- *logged_error);
- }
- logged_error.reset();
- {
- v8::TryCatch try_catch(isolate());
- const char kNestedCall[] =
- "function throwError() { throw new Error('nested'); }\n"
- "function callThrowError() { throwError(); }\n"
- "callThrowError()\n";
- v8::Local<v8::Script> script =
- v8::Script::Compile(context,
- gin::StringToV8(context->GetIsolate(), kNestedCall))
- .ToLocalChecked();
- ASSERT_TRUE(script->Run(context).IsEmpty());
- ASSERT_TRUE(try_catch.HasCaught());
- handler.HandleException(context, "handled", &try_catch);
- ASSERT_TRUE(logged_error);
- const char kExpectedError[] =
- "handled: Error: nested\n"
- " at throwError (<anonymous>:1:31)\n"
- " at callThrowError (<anonymous>:2:29)\n"
- " at <anonymous>:3:1";
- EXPECT_EQ(kExpectedError, *logged_error);
- }
- }
- } // namespace extensions
|