exception_handler_unittest.cc 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "extensions/renderer/bindings/exception_handler.h"
  5. #include <string>
  6. #include <tuple>
  7. #include "base/bind.h"
  8. #include "base/strings/stringprintf.h"
  9. #include "extensions/renderer/bindings/api_binding_test.h"
  10. #include "extensions/renderer/bindings/api_binding_test_util.h"
  11. #include "gin/converter.h"
  12. #include "testing/gmock/include/gmock/gmock.h"
  13. #include "third_party/abseil-cpp/absl/types/optional.h"
  14. namespace extensions {
  15. namespace {
  16. void PopulateError(absl::optional<std::string>* error_out,
  17. v8::Local<v8::Context> context,
  18. const std::string& error) {
  19. *error_out = error;
  20. }
  21. void ThrowException(v8::Local<v8::Context> context,
  22. const std::string& to_throw,
  23. ExceptionHandler* handler) {
  24. v8::Isolate* isolate = context->GetIsolate();
  25. v8::TryCatch try_catch(isolate);
  26. v8::Local<v8::Function> function = FunctionFromString(
  27. context,
  28. base::StringPrintf("(function() { throw %s; })", to_throw.c_str()));
  29. std::ignore = function->Call(context, v8::Undefined(isolate), 0, nullptr);
  30. ASSERT_TRUE(try_catch.HasCaught());
  31. handler->HandleException(context, "handled", &try_catch);
  32. }
  33. } // namespace
  34. using ExceptionHandlerTest = APIBindingTest;
  35. TEST_F(ExceptionHandlerTest, TestBasicHandling) {
  36. v8::HandleScope handle_scope(isolate());
  37. v8::Local<v8::Context> context = MainContext();
  38. absl::optional<std::string> logged_error;
  39. ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
  40. ThrowException(context, "new Error('some error')", &handler);
  41. ASSERT_TRUE(logged_error);
  42. EXPECT_THAT(*logged_error, testing::StartsWith("handled: Error: some error"));
  43. }
  44. TEST_F(ExceptionHandlerTest, PerContextHandlers) {
  45. v8::HandleScope handle_scope(isolate());
  46. v8::Local<v8::Context> context_a = MainContext();
  47. v8::Local<v8::Context> context_b = AddContext();
  48. absl::optional<std::string> logged_error;
  49. ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
  50. v8::Local<v8::Function> custom_handler = FunctionFromString(
  51. context_a,
  52. "(function(message, exception) {\n"
  53. " this.loggedMessage = message;\n"
  54. " this.loggedExceptionMessage = exception && exception.message;\n"
  55. "})");
  56. handler.SetHandlerForContext(context_a, custom_handler);
  57. ThrowException(context_a, "new Error('context a error')", &handler);
  58. EXPECT_FALSE(logged_error);
  59. EXPECT_THAT(GetStringPropertyFromObject(context_a->Global(), context_a,
  60. "loggedMessage"),
  61. testing::StartsWith("\"handled: Error: context a error"));
  62. EXPECT_EQ("\"context a error\"",
  63. GetStringPropertyFromObject(context_a->Global(), context_a,
  64. "loggedExceptionMessage"));
  65. ASSERT_TRUE(context_a->Global()
  66. ->Set(context_a,
  67. gin::StringToSymbol(isolate(), "loggedMessage"),
  68. v8::Undefined(isolate()))
  69. .ToChecked());
  70. ASSERT_TRUE(
  71. context_a->Global()
  72. ->Set(context_a,
  73. gin::StringToSymbol(isolate(), "loggedExceptionMessage"),
  74. v8::Undefined(isolate()))
  75. .ToChecked());
  76. ThrowException(context_b, "new Error('context b error')", &handler);
  77. ASSERT_TRUE(logged_error);
  78. EXPECT_THAT(*logged_error,
  79. testing::StartsWith("handled: Error: context b error"));
  80. EXPECT_EQ("undefined", GetStringPropertyFromObject(
  81. context_a->Global(), context_a, "loggedMessage"));
  82. EXPECT_EQ("undefined",
  83. GetStringPropertyFromObject(context_a->Global(), context_a,
  84. "loggedExceptionMessage"));
  85. }
  86. TEST_F(ExceptionHandlerTest, ThrowingNonErrors) {
  87. v8::HandleScope handle_scope(isolate());
  88. v8::Local<v8::Context> context = MainContext();
  89. absl::optional<std::string> logged_error;
  90. ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
  91. ThrowException(context, "'hello'", &handler);
  92. ASSERT_TRUE(logged_error);
  93. EXPECT_EQ("handled: Uncaught hello", *logged_error);
  94. logged_error.reset();
  95. ThrowException(context, "{ message: 'hello' }", &handler);
  96. ASSERT_TRUE(logged_error);
  97. EXPECT_EQ("handled: Uncaught #<Object>", *logged_error);
  98. logged_error.reset();
  99. ThrowException(context, "{ toString: function() { throw 'goodbye' } }",
  100. &handler);
  101. ASSERT_TRUE(logged_error);
  102. EXPECT_EQ("handled: Uncaught [object Object]", *logged_error);
  103. v8::Local<v8::Function> custom_handler =
  104. FunctionFromString(context,
  105. "(function(message, exception) {\n"
  106. " this.loggedMessage = message;\n"
  107. " this.loggedException = exception;\n"
  108. "})");
  109. handler.SetHandlerForContext(context, custom_handler);
  110. ThrowException(context, "'hello'", &handler);
  111. EXPECT_EQ(
  112. "\"handled: Uncaught hello\"",
  113. GetStringPropertyFromObject(context->Global(), context, "loggedMessage"));
  114. EXPECT_EQ("\"hello\"", GetStringPropertyFromObject(context->Global(), context,
  115. "loggedException"));
  116. }
  117. TEST_F(ExceptionHandlerTest, StackTraces) {
  118. v8::HandleScope handle_scope(isolate());
  119. v8::Local<v8::Context> context = MainContext();
  120. absl::optional<std::string> logged_error;
  121. ExceptionHandler handler(base::BindRepeating(&PopulateError, &logged_error));
  122. {
  123. v8::TryCatch try_catch(isolate());
  124. v8::Local<v8::Script> script =
  125. v8::Script::Compile(context,
  126. gin::StringToV8(context->GetIsolate(),
  127. "throw new Error('simple');"))
  128. .ToLocalChecked();
  129. ASSERT_TRUE(script->Run(context).IsEmpty());
  130. ASSERT_TRUE(try_catch.HasCaught());
  131. handler.HandleException(context, "handled", &try_catch);
  132. ASSERT_TRUE(logged_error);
  133. EXPECT_EQ("handled: Error: simple\n at <anonymous>:1:7", *logged_error);
  134. }
  135. logged_error.reset();
  136. {
  137. v8::TryCatch try_catch(isolate());
  138. v8::Local<v8::Function> throw_error_function = FunctionFromString(
  139. context, "(function() { throw new Error('function'); })");
  140. std::ignore = throw_error_function->Call(context, v8::Undefined(isolate()),
  141. 0, nullptr);
  142. ASSERT_TRUE(try_catch.HasCaught());
  143. handler.HandleException(context, "handled", &try_catch);
  144. ASSERT_TRUE(logged_error);
  145. EXPECT_EQ("handled: Error: function\n at <anonymous>:1:21",
  146. *logged_error);
  147. }
  148. logged_error.reset();
  149. {
  150. v8::TryCatch try_catch(isolate());
  151. const char kNestedCall[] =
  152. "function throwError() { throw new Error('nested'); }\n"
  153. "function callThrowError() { throwError(); }\n"
  154. "callThrowError()\n";
  155. v8::Local<v8::Script> script =
  156. v8::Script::Compile(context,
  157. gin::StringToV8(context->GetIsolate(), kNestedCall))
  158. .ToLocalChecked();
  159. ASSERT_TRUE(script->Run(context).IsEmpty());
  160. ASSERT_TRUE(try_catch.HasCaught());
  161. handler.HandleException(context, "handled", &try_catch);
  162. ASSERT_TRUE(logged_error);
  163. const char kExpectedError[] =
  164. "handled: Error: nested\n"
  165. " at throwError (<anonymous>:1:31)\n"
  166. " at callThrowError (<anonymous>:2:29)\n"
  167. " at <anonymous>:3:1";
  168. EXPECT_EQ(kExpectedError, *logged_error);
  169. }
  170. }
  171. } // namespace extensions