1 // Copyright 2014 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 
5 #include "extensions/renderer/console.h"
6 
7 #include "base/compiler_specific.h"
8 #include "base/debug/alias.h"
9 #include "base/lazy_instance.h"
10 #include "base/macros.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "extensions/renderer/get_script_context.h"
15 #include "extensions/renderer/script_context.h"
16 #include "extensions/renderer/script_context_set.h"
17 #include "extensions/renderer/v8_helpers.h"
18 #include "extensions/renderer/worker_thread_dispatcher.h"
19 #include "gin/converter.h"
20 #include "gin/per_isolate_data.h"
21 #include "third_party/blink/public/web/web_console_message.h"
22 
23 namespace extensions {
24 namespace console {
25 
26 namespace {
27 
28 // Writes |message| to stack to show up in minidump, then crashes.
CheckWithMinidump(const std::string & message)29 void CheckWithMinidump(const std::string& message) {
30   DEBUG_ALIAS_FOR_CSTR(minidump, message.c_str(), 1024);
31   CHECK(false) << message;
32 }
33 
BoundLogMethodCallback(const v8::FunctionCallbackInfo<v8::Value> & info)34 void BoundLogMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
35   std::string message;
36   for (int i = 0; i < info.Length(); ++i) {
37     if (i > 0)
38       message += " ";
39     message += *v8::String::Utf8Value(info.GetIsolate(), info[i]);
40   }
41 
42   ScriptContext* script_context =
43       GetScriptContextFromV8Context(info.GetIsolate()->GetCurrentContext());
44 
45   // TODO(devlin): Consider (D)CHECK(script_context)
46   const auto level = static_cast<blink::mojom::ConsoleMessageLevel>(
47       info.Data().As<v8::Int32>()->Value());
48   AddMessage(script_context, level, message);
49 }
50 
51 gin::WrapperInfo kWrapperInfo = {gin::kEmbedderNativeGin};
52 
53 }  // namespace
54 
Fatal(ScriptContext * context,const std::string & message)55 void Fatal(ScriptContext* context, const std::string& message) {
56   AddMessage(context, blink::mojom::ConsoleMessageLevel::kError, message);
57   CheckWithMinidump(message);
58 }
59 
AddMessage(ScriptContext * script_context,blink::mojom::ConsoleMessageLevel level,const std::string & message)60 void AddMessage(ScriptContext* script_context,
61                 blink::mojom::ConsoleMessageLevel level,
62                 const std::string& message) {
63   if (!script_context) {
64     LOG(WARNING) << "Could not log \"" << message
65                  << "\": no ScriptContext found";
66     return;
67   }
68 
69   if (!script_context->is_valid()) {
70     LOG(WARNING) << "Could not log \"" << message
71                  << "\": ScriptContext invalidated.";
72     return;
73   }
74 
75   blink::WebConsoleMessage web_console_message(
76       level, blink::WebString::FromUTF8(message));
77   blink::WebConsoleMessage::LogWebConsoleMessage(script_context->v8_context(),
78                                                  web_console_message);
79 }
80 
AsV8Object(v8::Isolate * isolate)81 v8::Local<v8::Object> AsV8Object(v8::Isolate* isolate) {
82   v8::EscapableHandleScope handle_scope(isolate);
83   gin::PerIsolateData* data = gin::PerIsolateData::From(isolate);
84   v8::Local<v8::ObjectTemplate> templ = data->GetObjectTemplate(&kWrapperInfo);
85   if (templ.IsEmpty()) {
86     templ = v8::ObjectTemplate::New(isolate);
87     static const struct {
88       const char* name;
89       blink::mojom::ConsoleMessageLevel level;
90     } methods[] = {
91         {"debug", blink::mojom::ConsoleMessageLevel::kVerbose},
92         {"log", blink::mojom::ConsoleMessageLevel::kInfo},
93         {"warn", blink::mojom::ConsoleMessageLevel::kWarning},
94         {"error", blink::mojom::ConsoleMessageLevel::kError},
95     };
96     for (const auto& method : methods) {
97       v8::Local<v8::FunctionTemplate> function = v8::FunctionTemplate::New(
98           isolate, BoundLogMethodCallback,
99           v8::Integer::New(isolate, static_cast<int>(method.level)));
100       function->RemovePrototype();
101       templ->Set(gin::StringToSymbol(isolate, method.name), function);
102     }
103     data->SetObjectTemplate(&kWrapperInfo, templ);
104   }
105   return handle_scope.Escape(
106       templ->NewInstance(isolate->GetCurrentContext()).ToLocalChecked());
107 }
108 
109 }  // namespace console
110 }  // namespace extensions
111