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/strings/string_util.h"
11#include "base/strings/stringprintf.h"
12#include "base/strings/utf_string_conversions.h"
13#include "content/public/renderer/render_view.h"
14#include "content/public/renderer/render_view_visitor.h"
15#include "extensions/renderer/dispatcher.h"
16#include "extensions/renderer/extension_helper.h"
17#include "third_party/WebKit/public/web/WebConsoleMessage.h"
18#include "third_party/WebKit/public/web/WebFrame.h"
19#include "third_party/WebKit/public/web/WebView.h"
20
21namespace extensions {
22namespace console {
23
24namespace {
25
26// Finds the RenderView associated with a context. Note: there will be multiple
27// contexts in each RenderView.
28class ByContextFinder : public content::RenderViewVisitor {
29 public:
30  static content::RenderView* Find(v8::Handle<v8::Context> context) {
31    ByContextFinder finder(context);
32    content::RenderView::ForEach(&finder);
33    return finder.found_;
34  }
35
36 private:
37  explicit ByContextFinder(v8::Handle<v8::Context> context)
38      : context_(context), found_(NULL) {}
39
40  virtual bool Visit(content::RenderView* render_view) OVERRIDE {
41    ExtensionHelper* helper = ExtensionHelper::Get(render_view);
42    if (helper &&
43        helper->dispatcher()->script_context_set().GetByV8Context(context_)) {
44      found_ = render_view;
45    }
46    return !found_;
47  }
48
49  v8::Handle<v8::Context> context_;
50  content::RenderView* found_;
51
52  DISALLOW_COPY_AND_ASSIGN(ByContextFinder);
53};
54
55// Writes |message| to stack to show up in minidump, then crashes.
56void CheckWithMinidump(const std::string& message) {
57  char minidump[1024];
58  base::debug::Alias(&minidump);
59  base::snprintf(
60      minidump, arraysize(minidump), "e::console: %s", message.c_str());
61  CHECK(false) << message;
62}
63
64typedef void (*LogMethod)(v8::Handle<v8::Context> context,
65                          const std::string& message);
66
67void BoundLogMethodCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
68  LogMethod log_method =
69      reinterpret_cast<LogMethod>(info.Data().As<v8::External>()->Value());
70  std::string message;
71  for (int i = 0; i < info.Length(); ++i) {
72    if (i > 0)
73      message += " ";
74    message += *v8::String::Utf8Value(info[i]);
75  }
76  (*log_method)(info.GetIsolate()->GetCallingContext(), message);
77}
78
79void BindLogMethod(v8::Isolate* isolate,
80                   v8::Local<v8::Object> target,
81                   const std::string& name,
82                   LogMethod log_method) {
83  v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
84      isolate,
85      &BoundLogMethodCallback,
86      v8::External::New(isolate, reinterpret_cast<void*>(log_method)));
87  target->Set(v8::String::NewFromUtf8(isolate, name.c_str()),
88              tmpl->GetFunction());
89}
90
91}  // namespace
92
93void Debug(content::RenderView* render_view, const std::string& message) {
94  AddMessage(render_view, content::CONSOLE_MESSAGE_LEVEL_DEBUG, message);
95}
96
97void Log(content::RenderView* render_view, const std::string& message) {
98  AddMessage(render_view, content::CONSOLE_MESSAGE_LEVEL_LOG, message);
99}
100
101void Warn(content::RenderView* render_view, const std::string& message) {
102  AddMessage(render_view, content::CONSOLE_MESSAGE_LEVEL_WARNING, message);
103}
104
105void Error(content::RenderView* render_view, const std::string& message) {
106  AddMessage(render_view, content::CONSOLE_MESSAGE_LEVEL_ERROR, message);
107}
108
109void Fatal(content::RenderView* render_view, const std::string& message) {
110  Error(render_view, message);
111  CheckWithMinidump(message);
112}
113
114void AddMessage(content::RenderView* render_view,
115                content::ConsoleMessageLevel level,
116                const std::string& message) {
117  blink::WebView* web_view = render_view->GetWebView();
118  if (!web_view || !web_view->mainFrame())
119    return;
120  blink::WebConsoleMessage::Level target_level =
121      blink::WebConsoleMessage::LevelLog;
122  switch (level) {
123    case content::CONSOLE_MESSAGE_LEVEL_DEBUG:
124      target_level = blink::WebConsoleMessage::LevelDebug;
125      break;
126    case content::CONSOLE_MESSAGE_LEVEL_LOG:
127      target_level = blink::WebConsoleMessage::LevelLog;
128      break;
129    case content::CONSOLE_MESSAGE_LEVEL_WARNING:
130      target_level = blink::WebConsoleMessage::LevelWarning;
131      break;
132    case content::CONSOLE_MESSAGE_LEVEL_ERROR:
133      target_level = blink::WebConsoleMessage::LevelError;
134      break;
135  }
136  web_view->mainFrame()->addMessageToConsole(
137      blink::WebConsoleMessage(target_level, base::UTF8ToUTF16(message)));
138}
139
140void Debug(v8::Handle<v8::Context> context, const std::string& message) {
141  AddMessage(context, content::CONSOLE_MESSAGE_LEVEL_DEBUG, message);
142}
143
144void Log(v8::Handle<v8::Context> context, const std::string& message) {
145  AddMessage(context, content::CONSOLE_MESSAGE_LEVEL_LOG, message);
146}
147
148void Warn(v8::Handle<v8::Context> context, const std::string& message) {
149  AddMessage(context, content::CONSOLE_MESSAGE_LEVEL_WARNING, message);
150}
151
152void Error(v8::Handle<v8::Context> context, const std::string& message) {
153  AddMessage(context, content::CONSOLE_MESSAGE_LEVEL_ERROR, message);
154}
155
156void Fatal(v8::Handle<v8::Context> context, const std::string& message) {
157  Error(context, message);
158  CheckWithMinidump(message);
159}
160
161void AddMessage(v8::Handle<v8::Context> context,
162                content::ConsoleMessageLevel level,
163                const std::string& message) {
164  if (context.IsEmpty()) {
165    LOG(WARNING) << "Could not log \"" << message << "\": no context given";
166    return;
167  }
168  content::RenderView* render_view = ByContextFinder::Find(context);
169  if (!render_view) {
170    LOG(WARNING) << "Could not log \"" << message << "\": no render view found";
171    return;
172  }
173  AddMessage(render_view, level, message);
174}
175
176v8::Local<v8::Object> AsV8Object() {
177  v8::Isolate* isolate = v8::Isolate::GetCurrent();
178  v8::EscapableHandleScope handle_scope(isolate);
179  v8::Local<v8::Object> console_object = v8::Object::New(isolate);
180  BindLogMethod(isolate, console_object, "debug", &Debug);
181  BindLogMethod(isolate, console_object, "log", &Log);
182  BindLogMethod(isolate, console_object, "warn", &Warn);
183  BindLogMethod(isolate, console_object, "error", &Error);
184  return handle_scope.Escape(console_object);
185}
186
187}  // namespace console
188}  // namespace extensions
189