Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(111)

Side by Side Diff: chrome/renderer/extensions/module_system.cc

Issue 17104003: Improvements to fatal JavaScript extension errors: (1) include the extension (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: jeffrey Created 7 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « chrome/renderer/extensions/chrome_v8_context_set.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/renderer/extensions/module_system.h" 5 #include "chrome/renderer/extensions/module_system.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/command_line.h"
8 #include "base/debug/trace_event.h" 9 #include "base/debug/trace_event.h"
9 #include "base/stl_util.h" 10 #include "base/stl_util.h"
10 #include "base/strings/string_util.h" 11 #include "base/strings/string_util.h"
11 #include "base/strings/stringprintf.h" 12 #include "base/strings/stringprintf.h"
13 #include "chrome/common/chrome_switches.h"
12 #include "chrome/common/extensions/extension_messages.h" 14 #include "chrome/common/extensions/extension_messages.h"
13 #include "chrome/renderer/extensions/chrome_v8_context.h" 15 #include "chrome/renderer/extensions/chrome_v8_context.h"
14 #include "chrome/renderer/extensions/console.h" 16 #include "chrome/renderer/extensions/console.h"
15 #include "content/public/renderer/render_view.h" 17 #include "content/public/renderer/render_view.h"
16 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h" 18 #include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
17 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSup pression.h" 19 #include "third_party/WebKit/Source/WebKit/chromium/public/WebScopedMicrotaskSup pression.h"
18 20
19 namespace extensions { 21 namespace extensions {
20 22
21 namespace { 23 namespace {
22 24
23 const char* kModuleSystem = "module_system"; 25 const char* kModuleSystem = "module_system";
24 const char* kModuleName = "module_name"; 26 const char* kModuleName = "module_name";
25 const char* kModuleField = "module_field"; 27 const char* kModuleField = "module_field";
26 const char* kModulesField = "modules"; 28 const char* kModulesField = "modules";
27 29
30 // Prepends |extension_id| if it's non-empty to |message|.
31 std::string PrependExtensionID(const std::string& extension_id,
32 const std::string& message) {
33 std::string with_extension_id = message;
34 if (!extension_id.empty()) {
35 with_extension_id += "(";
Jeffrey Yasskin 2013/06/14 22:11:22 Heh, this will put the extension_id after the mess
not at google - send to devlin 2013/06/14 22:27:44 damnnnnnnn
36 with_extension_id += extension_id;
37 with_extension_id += ") ";
38 }
39 return with_extension_id;
40 }
41
42 void Fatal(const std::string& extension_id, const std::string& message) {
43 // Only crash extension processes. Ideally we would crash on *any* (i.e. web
44 // too), but given we hardly have code coverage for JS we should be careful
45 // and not break the web.
46 //
47 // NOTE: make single-process count as an extension process since it's for
48 // debugging, and we'll almost certainly want to use it to repro them.
Jeffrey Yasskin 2013/06/14 22:11:22 "them" lacks an antecedent.
not at google - send to devlin 2013/06/14 22:27:44 rephrased.
49 const CommandLine* command_line = CommandLine::ForCurrentProcess();
50 bool is_extension_process =
51 command_line->HasSwitch(switches::kExtensionProcess) ||
52 command_line->HasSwitch(switches::kSingleProcess);
53 std::string with_extension_id = PrependExtensionID(extension_id, message);
54 if (is_extension_process)
55 console::Fatal(v8::Context::GetCalling(), with_extension_id);
56 else
57 console::Error(v8::Context::GetCalling(), with_extension_id);
58 }
59
60 void Warn(const std::string& extension_id, const std::string& message) {
61 console::Warn(v8::Context::GetCalling(),
62 PrependExtensionID(extension_id, message));
63 }
64
28 // Default exception handler which logs the exception. 65 // Default exception handler which logs the exception.
29 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler { 66 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
30 public: 67 public:
68 explicit DefaultExceptionHandler(const std::string& extension_id)
69 : extension_id_(extension_id) {}
70
31 // Fatally dumps the debug info from |try_catch| to the console. 71 // Fatally dumps the debug info from |try_catch| to the console.
32 // Make sure this is never used for exceptions that originate in external 72 // Make sure this is never used for exceptions that originate in external
33 // code! 73 // code!
34 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE { 74 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE {
35 v8::HandleScope handle_scope; 75 v8::HandleScope handle_scope;
36 std::string stack_trace = "<stack trace unavailable>"; 76 std::string stack_trace = "<stack trace unavailable>";
37 if (!try_catch.StackTrace().IsEmpty()) { 77 if (!try_catch.StackTrace().IsEmpty()) {
38 v8::String::Utf8Value stack_value(try_catch.StackTrace()); 78 v8::String::Utf8Value stack_value(try_catch.StackTrace());
39 if (*stack_value) 79 if (*stack_value)
40 stack_trace.assign(*stack_value, stack_value.length()); 80 stack_trace.assign(*stack_value, stack_value.length());
41 else 81 else
42 stack_trace = "<could not convert stack trace to string>"; 82 stack_trace = "<could not convert stack trace to string>";
43 } 83 }
44 console::Fatal(v8::Context::GetCalling(), 84 Fatal(extension_id_,
45 CreateExceptionString(try_catch) + "{" + stack_trace + "}"); 85 CreateExceptionString(try_catch) + "{" + stack_trace + "}");
46 } 86 }
87
88 private:
89 std::string extension_id_;
47 }; 90 };
48 91
49 } // namespace 92 } // namespace
50 93
51 std::string ModuleSystem::ExceptionHandler::CreateExceptionString( 94 std::string ModuleSystem::ExceptionHandler::CreateExceptionString(
52 const v8::TryCatch& try_catch) { 95 const v8::TryCatch& try_catch) {
53 v8::Handle<v8::Message> message(try_catch.Message()); 96 v8::Handle<v8::Message> message(try_catch.Message());
54 if (message.IsEmpty()) { 97 if (message.IsEmpty()) {
55 return "try_catch has no message"; 98 return "try_catch has no message";
56 } 99 }
(...skipping 16 matching lines...) Expand all
73 message->GetLineNumber(), 116 message->GetLineNumber(),
74 error_message.c_str()); 117 error_message.c_str());
75 } 118 }
76 119
77 ModuleSystem::ModuleSystem(ChromeV8Context* context, 120 ModuleSystem::ModuleSystem(ChromeV8Context* context,
78 SourceMap* source_map) 121 SourceMap* source_map)
79 : ObjectBackedNativeHandler(context), 122 : ObjectBackedNativeHandler(context),
80 context_(context), 123 context_(context),
81 source_map_(source_map), 124 source_map_(source_map),
82 natives_enabled_(0), 125 natives_enabled_(0),
83 exception_handler_(new DefaultExceptionHandler()) { 126 exception_handler_(
127 new DefaultExceptionHandler(context->GetExtensionID())) {
84 RouteFunction("require", 128 RouteFunction("require",
85 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); 129 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
86 RouteFunction("requireNative", 130 RouteFunction("requireNative",
87 base::Bind(&ModuleSystem::RequireNative, base::Unretained(this))); 131 base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
88 132
89 v8::Handle<v8::Object> global(context->v8_context()->Global()); 133 v8::Handle<v8::Object> global(context->v8_context()->Global());
90 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New()); 134 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New());
91 global->SetHiddenValue(v8::String::New(kModuleSystem), 135 global->SetHiddenValue(v8::String::New(kModuleSystem),
92 v8::External::New(this)); 136 v8::External::New(this));
93 } 137 }
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 v8::Context::Scope context_scope(context()->v8_context()); 195 v8::Context::Scope context_scope(context()->v8_context());
152 196
153 v8::Handle<v8::Object> global(context()->v8_context()->Global()); 197 v8::Handle<v8::Object> global(context()->v8_context()->Global());
154 198
155 // The module system might have been deleted. This can happen if a different 199 // The module system might have been deleted. This can happen if a different
156 // context keeps a reference to us, but our frame is destroyed (e.g. 200 // context keeps a reference to us, but our frame is destroyed (e.g.
157 // background page keeps reference to chrome object in a closed popup). 201 // background page keeps reference to chrome object in a closed popup).
158 v8::Handle<v8::Value> modules_value = 202 v8::Handle<v8::Value> modules_value =
159 global->GetHiddenValue(v8::String::New(kModulesField)); 203 global->GetHiddenValue(v8::String::New(kModulesField));
160 if (modules_value.IsEmpty() || modules_value->IsUndefined()) { 204 if (modules_value.IsEmpty() || modules_value->IsUndefined()) {
161 console::Warn(v8::Context::GetCalling(), "Extension view no longer exists"); 205 Warn(context_->GetExtensionID(), "Extension view no longer exists");
162 return v8::Undefined(); 206 return v8::Undefined();
163 } 207 }
164 208
165 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value)); 209 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value));
166 v8::Handle<v8::Value> exports(modules->Get(module_name)); 210 v8::Handle<v8::Value> exports(modules->Get(module_name));
167 if (!exports->IsUndefined()) 211 if (!exports->IsUndefined())
168 return handle_scope.Close(exports); 212 return handle_scope.Close(exports);
169 213
170 std::string module_name_str = *v8::String::AsciiValue(module_name); 214 std::string module_name_str = *v8::String::AsciiValue(module_name);
171 v8::Handle<v8::Value> source(GetSource(module_name_str)); 215 v8::Handle<v8::Value> source(GetSource(module_name_str));
172 if (source.IsEmpty() || source->IsUndefined()) { 216 if (source.IsEmpty() || source->IsUndefined()) {
173 console::Error(v8::Context::GetCalling(), 217 Fatal(context_->GetExtensionID(),
174 "No source for require(" + module_name_str + ")"); 218 "No source for require(" + module_name_str + ")");
175 return v8::Undefined(); 219 return v8::Undefined();
176 } 220 }
177 v8::Handle<v8::String> wrapped_source(WrapSource( 221 v8::Handle<v8::String> wrapped_source(WrapSource(
178 v8::Handle<v8::String>::Cast(source))); 222 v8::Handle<v8::String>::Cast(source)));
179 // Modules are wrapped in (function(){...}) so they always return functions. 223 // Modules are wrapped in (function(){...}) so they always return functions.
180 v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name); 224 v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name);
181 if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) { 225 if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
182 console::Error(v8::Context::GetCalling(), 226 Fatal(context_->GetExtensionID(),
183 "Bad source for require(" + module_name_str + ")"); 227 "Bad source for require(" + module_name_str + ")");
184 return v8::Undefined(); 228 return v8::Undefined();
185 } 229 }
186 230
187 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value); 231 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value);
188 232
189 exports = v8::Object::New(); 233 exports = v8::Object::New();
190 v8::Handle<v8::Object> natives(NewInstance()); 234 v8::Handle<v8::Object> natives(NewInstance());
191 v8::Handle<v8::Value> args[] = { 235 v8::Handle<v8::Value> args[] = {
192 natives->Get(v8::String::NewSymbol("require")), 236 natives->Get(v8::String::NewSymbol("require")),
193 natives->Get(v8::String::NewSymbol("requireNative")), 237 natives->Get(v8::String::NewSymbol("requireNative")),
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
235 v8::Context::Scope context_scope(context()->v8_context()); 279 v8::Context::Scope context_scope(context()->v8_context());
236 280
237 v8::Local<v8::Value> module; 281 v8::Local<v8::Value> module;
238 { 282 {
239 NativesEnabledScope natives_enabled(this); 283 NativesEnabledScope natives_enabled(this);
240 module = v8::Local<v8::Value>::New( 284 module = v8::Local<v8::Value>::New(
241 RequireForJsInner(v8::String::New(module_name.c_str()))); 285 RequireForJsInner(v8::String::New(module_name.c_str())));
242 } 286 }
243 287
244 if (module.IsEmpty() || !module->IsObject()) { 288 if (module.IsEmpty() || !module->IsObject()) {
245 console::Error( 289 Fatal(context_->GetExtensionID(),
246 v8::Context::GetCalling(), 290 "Failed to get module " + module_name + " to call " + method_name);
247 "Failed to get module " + module_name + " to call " + method_name);
248 return handle_scope.Close(v8::Undefined()); 291 return handle_scope.Close(v8::Undefined());
249 } 292 }
250 293
251 v8::Local<v8::Value> value = 294 v8::Local<v8::Value> value =
252 v8::Handle<v8::Object>::Cast(module)->Get( 295 v8::Handle<v8::Object>::Cast(module)->Get(
253 v8::String::New(method_name.c_str())); 296 v8::String::New(method_name.c_str()));
254 if (value.IsEmpty() || !value->IsFunction()) { 297 if (value.IsEmpty() || !value->IsFunction()) {
255 console::Error(v8::Context::GetCalling(), 298 Fatal(context_->GetExtensionID(),
256 module_name + "." + method_name + " is not a function"); 299 module_name + "." + method_name + " is not a function");
257 return handle_scope.Close(v8::Undefined()); 300 return handle_scope.Close(v8::Undefined());
258 } 301 }
259 302
260 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value); 303 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
261 v8::Local<v8::Value> result; 304 v8::Local<v8::Value> result;
262 { 305 {
263 v8::TryCatch try_catch; 306 v8::TryCatch try_catch;
264 try_catch.SetCaptureMessage(true); 307 try_catch.SetCaptureMessage(true);
265 result = context_->CallFunction(func, argc, argv); 308 result = context_->CallFunction(func, argc, argv);
266 if (try_catch.HasCaught()) 309 if (try_catch.HasCaught())
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 v8::HandleScope handle_scope; 353 v8::HandleScope handle_scope;
311 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data()); 354 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data());
312 // This context should be the same as context()->v8_context(). 355 // This context should be the same as context()->v8_context().
313 v8::Handle<v8::Context> context = parameters->CreationContext(); 356 v8::Handle<v8::Context> context = parameters->CreationContext();
314 v8::Handle<v8::Object> global(context->Global()); 357 v8::Handle<v8::Object> global(context->Global());
315 v8::Handle<v8::Value> module_system_value = 358 v8::Handle<v8::Value> module_system_value =
316 global->GetHiddenValue(v8::String::New(kModuleSystem)); 359 global->GetHiddenValue(v8::String::New(kModuleSystem));
317 if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) { 360 if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) {
318 // ModuleSystem has been deleted. 361 // ModuleSystem has been deleted.
319 // TODO(kalman): See comment in header file. 362 // TODO(kalman): See comment in header file.
320 console::Warn(v8::Context::GetCalling(), 363 Warn("", "Module system has been deleted, does extension view exist?");
321 "Module system has been deleted, does extension view exist?");
322 return; 364 return;
323 } 365 }
324 366
325 ModuleSystem* module_system = static_cast<ModuleSystem*>( 367 ModuleSystem* module_system = static_cast<ModuleSystem*>(
326 v8::Handle<v8::External>::Cast(module_system_value)->Value()); 368 v8::Handle<v8::External>::Cast(module_system_value)->Value());
327 369
328 std::string name = *v8::String::AsciiValue( 370 std::string name = *v8::String::AsciiValue(
329 parameters->Get(v8::String::New(kModuleName))->ToString()); 371 parameters->Get(v8::String::New(kModuleName))->ToString());
330 372
331 // Switch to our v8 context because we need functions created while running 373 // Switch to our v8 context because we need functions created while running
(...skipping 11 matching lines...) Expand all
343 // require_function will have already logged this, we don't need to. 385 // require_function will have already logged this, we don't need to.
344 return; 386 return;
345 } 387 }
346 388
347 v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value); 389 v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value);
348 v8::Handle<v8::String> field = 390 v8::Handle<v8::String> field =
349 parameters->Get(v8::String::New(kModuleField))->ToString(); 391 parameters->Get(v8::String::New(kModuleField))->ToString();
350 392
351 if (!module->Has(field)) { 393 if (!module->Has(field)) {
352 std::string field_str = *v8::String::AsciiValue(field); 394 std::string field_str = *v8::String::AsciiValue(field);
353 console::Fatal(v8::Context::GetCalling(), 395 Fatal(module_system->context_->GetExtensionID(),
354 "Lazy require of " + name + "." + field_str + " did not " + 396 "Lazy require of " + name + "." + field_str + " did not " +
355 "set the " + field_str + " field"); 397 "set the " + field_str + " field");
356 return; 398 return;
357 } 399 }
358 400
359 v8::Local<v8::Value> new_field = module->Get(field); 401 v8::Local<v8::Value> new_field = module->Get(field);
360 if (try_catch.HasCaught()) { 402 if (try_catch.HasCaught()) {
361 module_system->HandleException(try_catch); 403 module_system->HandleException(try_catch);
362 return; 404 return;
363 } 405 }
364 406
365 // Ok for it to be undefined, among other things it's how bindings signify 407 // Ok for it to be undefined, among other things it's how bindings signify
(...skipping 79 matching lines...) Expand 10 before | Expand all | Expand 10 after
445 } 487 }
446 488
447 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( 489 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
448 const std::string& native_name) { 490 const std::string& native_name) {
449 if (natives_enabled_ == 0) { 491 if (natives_enabled_ == 0) {
450 // HACK: if in test throw exception so that we can test the natives-disabled 492 // HACK: if in test throw exception so that we can test the natives-disabled
451 // logic; however, under normal circumstances, this is programmer error so 493 // logic; however, under normal circumstances, this is programmer error so
452 // we could crash. 494 // we could crash.
453 if (exception_handler_) 495 if (exception_handler_)
454 return v8::ThrowException(v8::String::New("Natives disabled")); 496 return v8::ThrowException(v8::String::New("Natives disabled"));
455 console::Fatal(v8::Context::GetCalling(), 497 Fatal(context_->GetExtensionID(),
456 "Natives disabled for requireNative(" + native_name + ")"); 498 "Natives disabled for requireNative(" + native_name + ")");
457 return v8::Undefined(); 499 return v8::Undefined();
458 } 500 }
459 501
460 if (overridden_native_handlers_.count(native_name) > 0u) 502 if (overridden_native_handlers_.count(native_name) > 0u)
461 return RequireForJsInner(v8::String::New(native_name.c_str())); 503 return RequireForJsInner(v8::String::New(native_name.c_str()));
462 504
463 NativeHandlerMap::iterator i = native_handler_map_.find(native_name); 505 NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
464 if (i == native_handler_map_.end()) { 506 if (i == native_handler_map_.end()) {
465 console::Fatal( 507 Fatal(context_->GetExtensionID(),
466 v8::Context::GetCalling(), 508 "Couldn't find native for requireNative(" + native_name + ")");
467 "Couldn't find native for requireNative(" + native_name + ")");
468 return v8::Undefined(); 509 return v8::Undefined();
469 } 510 }
470 return i->second->NewInstance(); 511 return i->second->NewInstance();
471 } 512 }
472 513
473 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) { 514 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) {
474 v8::HandleScope handle_scope; 515 v8::HandleScope handle_scope;
475 v8::Handle<v8::String> left = v8::String::New( 516 v8::Handle<v8::String> left = v8::String::New(
476 "(function(require, requireNative, exports) {'use strict';"); 517 "(function(require, requireNative, exports) {'use strict';");
477 v8::Handle<v8::String> right = v8::String::New("\n})"); 518 v8::Handle<v8::String> right = v8::String::New("\n})");
478 return handle_scope.Close( 519 return handle_scope.Close(
479 v8::String::Concat(left, v8::String::Concat(source, right))); 520 v8::String::Concat(left, v8::String::Concat(source, right)));
480 } 521 }
481 522
482 } // namespace extensions 523 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/chrome_v8_context_set.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698