exception_handler.cc 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153
  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 "base/check.h"
  6. #include "base/strings/stringprintf.h"
  7. #include "base/supports_user_data.h"
  8. #include "extensions/renderer/bindings/api_binding_util.h"
  9. #include "extensions/renderer/bindings/get_per_context_data.h"
  10. #include "extensions/renderer/bindings/js_runner.h"
  11. #include "gin/converter.h"
  12. #include "gin/handle.h"
  13. #include "gin/per_context_data.h"
  14. #include "gin/wrappable.h"
  15. namespace extensions {
  16. namespace {
  17. struct ExceptionHandlerPerContextData : public base::SupportsUserData::Data {
  18. static constexpr char kPerContextDataKey[] = "extension_exception_handler";
  19. v8::Global<v8::Function> custom_handler;
  20. };
  21. constexpr char ExceptionHandlerPerContextData::kPerContextDataKey[];
  22. // A helper class to wrap an ExceptionHandler WeakPtr in a v8::Value.
  23. class WrappedExceptionHandler : public gin::Wrappable<WrappedExceptionHandler> {
  24. public:
  25. static gin::WrapperInfo kWrapperInfo;
  26. base::WeakPtr<ExceptionHandler> exception_handler;
  27. };
  28. gin::WrapperInfo WrappedExceptionHandler::kWrapperInfo = {
  29. gin::kEmbedderNativeGin};
  30. } // namespace
  31. ExceptionHandler::ExceptionHandler(
  32. const binding::AddConsoleError& add_console_error)
  33. : add_console_error_(add_console_error) {}
  34. ExceptionHandler::~ExceptionHandler() {}
  35. v8::Local<v8::Value> ExceptionHandler::GetV8Wrapper(v8::Isolate* isolate) {
  36. auto handle = gin::CreateHandle(isolate, new WrappedExceptionHandler);
  37. handle->exception_handler = weak_factory_.GetWeakPtr();
  38. return handle.ToV8();
  39. }
  40. ExceptionHandler* ExceptionHandler::FromV8Wrapper(v8::Isolate* isolate,
  41. v8::Local<v8::Value> value) {
  42. WrappedExceptionHandler* handler;
  43. if (!gin::ConvertFromV8(isolate, value, &handler))
  44. return nullptr;
  45. return handler->exception_handler.get();
  46. }
  47. void ExceptionHandler::HandleException(v8::Local<v8::Context> context,
  48. const std::string& message,
  49. v8::TryCatch* try_catch) {
  50. DCHECK(try_catch->HasCaught());
  51. v8::Isolate* isolate = context->GetIsolate();
  52. v8::HandleScope handle_scope(isolate);
  53. v8::Local<v8::Value> message_value;
  54. {
  55. v8::TryCatch inner_try_catch(isolate);
  56. inner_try_catch.SetVerbose(true);
  57. v8::Local<v8::Value> stack_trace_value;
  58. if (try_catch->StackTrace(context).ToLocal(&stack_trace_value)) {
  59. message_value = stack_trace_value;
  60. } else if (!try_catch->Message().IsEmpty()) {
  61. message_value = try_catch->Message()->Get();
  62. }
  63. }
  64. std::string full_message =
  65. !message_value.IsEmpty()
  66. ? base::StringPrintf("%s: %s", message.c_str(),
  67. gin::V8ToString(isolate, message_value).c_str())
  68. : message;
  69. HandleException(context, full_message, try_catch->Exception());
  70. try_catch->Reset(); // Reset() to avoid handling the error more than once.
  71. }
  72. void ExceptionHandler::HandleException(v8::Local<v8::Context> context,
  73. const std::string& full_message,
  74. v8::Local<v8::Value> exception_value) {
  75. v8::Isolate* isolate = context->GetIsolate();
  76. v8::HandleScope handle_scope(isolate);
  77. v8::Local<v8::Function> handler = GetCustomHandler(context);
  78. if (!handler.IsEmpty()) {
  79. v8::Local<v8::Value> arguments[] = {
  80. gin::StringToV8(isolate, full_message), exception_value,
  81. };
  82. // Hopefully, handling an exception doesn't throw an exception - but it's
  83. // possible. Handle this gracefully, and log errors normally.
  84. v8::TryCatch handler_try_catch(isolate);
  85. handler_try_catch.SetVerbose(true);
  86. JSRunner::Get(context)->RunJSFunction(handler, context,
  87. std::size(arguments), arguments);
  88. } else {
  89. add_console_error_.Run(context, full_message);
  90. }
  91. }
  92. void ExceptionHandler::SetHandlerForContext(v8::Local<v8::Context> context,
  93. v8::Local<v8::Function> handler) {
  94. ExceptionHandlerPerContextData* data =
  95. GetPerContextData<ExceptionHandlerPerContextData>(context,
  96. kCreateIfMissing);
  97. DCHECK(data);
  98. data->custom_handler.Reset(context->GetIsolate(), handler);
  99. }
  100. void ExceptionHandler::RunExtensionCallback(
  101. v8::Local<v8::Context> context,
  102. v8::Local<v8::Function> extension_callback,
  103. std::vector<v8::Local<v8::Value>> callback_arguments,
  104. const std::string& message) {
  105. v8::TryCatch try_catch(context->GetIsolate());
  106. // TODO(devlin): JSRunner::RunJSFunction() isn't guaranteed to run
  107. // synchronously, so if JS is suspended at this moment, the `try_catch` here
  108. // is insufficient.
  109. JSRunner::Get(context)->RunJSFunction(extension_callback, context,
  110. callback_arguments.size(),
  111. callback_arguments.data());
  112. // Since arbitrary JS has ran, the context may have been invalidated. If it
  113. // was, bail.
  114. if (!binding::IsContextValid(context))
  115. return;
  116. if (try_catch.HasCaught())
  117. HandleException(context, message, &try_catch);
  118. }
  119. v8::Local<v8::Function> ExceptionHandler::GetCustomHandler(
  120. v8::Local<v8::Context> context) {
  121. ExceptionHandlerPerContextData* data =
  122. GetPerContextData<ExceptionHandlerPerContextData>(context,
  123. kDontCreateIfMissing);
  124. return data ? data->custom_handler.Get(context->GetIsolate())
  125. : v8::Local<v8::Function>();
  126. }
  127. } // namespace extensions