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

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: channel restriction 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
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 "chrome/renderer/extensions/safe_builtins.h" 17 #include "chrome/renderer/extensions/safe_builtins.h"
16 #include "content/public/renderer/render_view.h" 18 #include "content/public/renderer/render_view.h"
17 #include "third_party/WebKit/public/web/WebFrame.h" 19 #include "third_party/WebKit/public/web/WebFrame.h"
18 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h" 20 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
19 21
20 namespace extensions { 22 namespace extensions {
21 23
22 namespace { 24 namespace {
23 25
24 const char* kModuleSystem = "module_system"; 26 const char* kModuleSystem = "module_system";
25 const char* kModuleName = "module_name"; 27 const char* kModuleName = "module_name";
26 const char* kModuleField = "module_field"; 28 const char* kModuleField = "module_field";
27 const char* kModulesField = "modules"; 29 const char* kModulesField = "modules";
28 30
31 // Prepends |extension_id| if it's non-empty to |message|.
32 std::string PrependExtensionID(const std::string& extension_id,
33 const std::string& message) {
34 std::string with_extension_id;
35 if (!extension_id.empty()) {
36 with_extension_id += "(";
37 with_extension_id += extension_id;
38 with_extension_id += ") ";
39 }
40 with_extension_id += message;
41 return with_extension_id;
42 }
43
44 void Fatal(const std::string& extension_id, const std::string& message) {
45 // Only crash web pages in dev channel.
46 // Always crash extension processes, or when in single process mode (since
47 // typically it's used to debug renderer crashes).
48 bool is_fatal = false;
49 const CommandLine* command_line = CommandLine::ForCurrentProcess();
50 if (command_line->HasSwitch(switches::kExtensionProcess) ||
51 command_line->HasSwitch(switches::kSingleProcess)) {
52 is_fatal = true;
53 } else {
54 // <= dev means dev, canary, and trunk.
55 is_fatal = Feature::GetCurrentChannel() <= chrome::VersionInfo::CHANNEL_DEV;
56 }
57 std::string with_extension_id = PrependExtensionID(extension_id, message);
58 if (is_fatal)
59 console::Fatal(v8::Context::GetCalling(), with_extension_id);
60 else
61 console::Error(v8::Context::GetCalling(), with_extension_id);
62 }
63
64 void Warn(const std::string& extension_id, const std::string& message) {
65 console::Warn(v8::Context::GetCalling(),
66 PrependExtensionID(extension_id, message));
67 }
68
29 // Default exception handler which logs the exception. 69 // Default exception handler which logs the exception.
30 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler { 70 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
31 public: 71 public:
72 explicit DefaultExceptionHandler(const std::string& extension_id)
73 : extension_id_(extension_id) {}
74
32 // Fatally dumps the debug info from |try_catch| to the console. 75 // Fatally dumps the debug info from |try_catch| to the console.
33 // Make sure this is never used for exceptions that originate in external 76 // Make sure this is never used for exceptions that originate in external
34 // code! 77 // code!
35 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE { 78 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE {
36 v8::HandleScope handle_scope; 79 v8::HandleScope handle_scope;
37 std::string stack_trace = "<stack trace unavailable>"; 80 std::string stack_trace = "<stack trace unavailable>";
38 if (!try_catch.StackTrace().IsEmpty()) { 81 if (!try_catch.StackTrace().IsEmpty()) {
39 v8::String::Utf8Value stack_value(try_catch.StackTrace()); 82 v8::String::Utf8Value stack_value(try_catch.StackTrace());
40 if (*stack_value) 83 if (*stack_value)
41 stack_trace.assign(*stack_value, stack_value.length()); 84 stack_trace.assign(*stack_value, stack_value.length());
42 else 85 else
43 stack_trace = "<could not convert stack trace to string>"; 86 stack_trace = "<could not convert stack trace to string>";
44 } 87 }
45 console::Fatal(v8::Context::GetCalling(), 88 Fatal(extension_id_,
46 CreateExceptionString(try_catch) + "{" + stack_trace + "}"); 89 CreateExceptionString(try_catch) + "{" + stack_trace + "}");
47 } 90 }
91
92 private:
93 std::string extension_id_;
48 }; 94 };
49 95
50 } // namespace 96 } // namespace
51 97
52 std::string ModuleSystem::ExceptionHandler::CreateExceptionString( 98 std::string ModuleSystem::ExceptionHandler::CreateExceptionString(
53 const v8::TryCatch& try_catch) { 99 const v8::TryCatch& try_catch) {
54 v8::Handle<v8::Message> message(try_catch.Message()); 100 v8::Handle<v8::Message> message(try_catch.Message());
55 if (message.IsEmpty()) { 101 if (message.IsEmpty()) {
56 return "try_catch has no message"; 102 return "try_catch has no message";
57 } 103 }
(...skipping 16 matching lines...) Expand all
74 message->GetLineNumber(), 120 message->GetLineNumber(),
75 error_message.c_str()); 121 error_message.c_str());
76 } 122 }
77 123
78 ModuleSystem::ModuleSystem(ChromeV8Context* context, 124 ModuleSystem::ModuleSystem(ChromeV8Context* context,
79 SourceMap* source_map) 125 SourceMap* source_map)
80 : ObjectBackedNativeHandler(context), 126 : ObjectBackedNativeHandler(context),
81 context_(context), 127 context_(context),
82 source_map_(source_map), 128 source_map_(source_map),
83 natives_enabled_(0), 129 natives_enabled_(0),
84 exception_handler_(new DefaultExceptionHandler()) { 130 exception_handler_(
131 new DefaultExceptionHandler(context->GetExtensionID())) {
85 RouteFunction("require", 132 RouteFunction("require",
86 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); 133 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
87 RouteFunction("requireNative", 134 RouteFunction("requireNative",
88 base::Bind(&ModuleSystem::RequireNative, base::Unretained(this))); 135 base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
89 136
90 v8::Handle<v8::Object> global(context->v8_context()->Global()); 137 v8::Handle<v8::Object> global(context->v8_context()->Global());
91 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New()); 138 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New());
92 global->SetHiddenValue(v8::String::New(kModuleSystem), 139 global->SetHiddenValue(v8::String::New(kModuleSystem),
93 v8::External::New(this)); 140 v8::External::New(this));
94 } 141 }
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
152 v8::Context::Scope context_scope(context()->v8_context()); 199 v8::Context::Scope context_scope(context()->v8_context());
153 200
154 v8::Handle<v8::Object> global(context()->v8_context()->Global()); 201 v8::Handle<v8::Object> global(context()->v8_context()->Global());
155 202
156 // The module system might have been deleted. This can happen if a different 203 // The module system might have been deleted. This can happen if a different
157 // context keeps a reference to us, but our frame is destroyed (e.g. 204 // context keeps a reference to us, but our frame is destroyed (e.g.
158 // background page keeps reference to chrome object in a closed popup). 205 // background page keeps reference to chrome object in a closed popup).
159 v8::Handle<v8::Value> modules_value = 206 v8::Handle<v8::Value> modules_value =
160 global->GetHiddenValue(v8::String::New(kModulesField)); 207 global->GetHiddenValue(v8::String::New(kModulesField));
161 if (modules_value.IsEmpty() || modules_value->IsUndefined()) { 208 if (modules_value.IsEmpty() || modules_value->IsUndefined()) {
162 console::Warn(v8::Context::GetCalling(), "Extension view no longer exists"); 209 Warn(context_->GetExtensionID(), "Extension view no longer exists");
163 return v8::Undefined(); 210 return v8::Undefined();
164 } 211 }
165 212
166 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value)); 213 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value));
167 v8::Handle<v8::Value> exports(modules->Get(module_name)); 214 v8::Handle<v8::Value> exports(modules->Get(module_name));
168 if (!exports->IsUndefined()) 215 if (!exports->IsUndefined())
169 return handle_scope.Close(exports); 216 return handle_scope.Close(exports);
170 217
171 std::string module_name_str = *v8::String::AsciiValue(module_name); 218 std::string module_name_str = *v8::String::AsciiValue(module_name);
172 v8::Handle<v8::Value> source(GetSource(module_name_str)); 219 v8::Handle<v8::Value> source(GetSource(module_name_str));
173 if (source.IsEmpty() || source->IsUndefined()) { 220 if (source.IsEmpty() || source->IsUndefined()) {
174 console::Error(v8::Context::GetCalling(), 221 Fatal(context_->GetExtensionID(),
175 "No source for require(" + module_name_str + ")"); 222 "No source for require(" + module_name_str + ")");
176 return v8::Undefined(); 223 return v8::Undefined();
177 } 224 }
178 v8::Handle<v8::String> wrapped_source(WrapSource( 225 v8::Handle<v8::String> wrapped_source(WrapSource(
179 v8::Handle<v8::String>::Cast(source))); 226 v8::Handle<v8::String>::Cast(source)));
180 // Modules are wrapped in (function(){...}) so they always return functions. 227 // Modules are wrapped in (function(){...}) so they always return functions.
181 v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name); 228 v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name);
182 if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) { 229 if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
183 console::Error(v8::Context::GetCalling(), 230 Fatal(context_->GetExtensionID(),
184 "Bad source for require(" + module_name_str + ")"); 231 "Bad source for require(" + module_name_str + ")");
185 return v8::Undefined(); 232 return v8::Undefined();
186 } 233 }
187 234
188 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value); 235 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value);
189 236
190 exports = v8::Object::New(); 237 exports = v8::Object::New();
191 v8::Handle<v8::Object> natives(NewInstance()); 238 v8::Handle<v8::Object> natives(NewInstance());
192 CHECK(!natives.IsEmpty()); // this can happen if v8 has issues 239 CHECK(!natives.IsEmpty()); // this can happen if v8 has issues
193 240
194 // These must match the argument order in WrapSource. 241 // These must match the argument order in WrapSource.
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 v8::Context::Scope context_scope(context()->v8_context()); 294 v8::Context::Scope context_scope(context()->v8_context());
248 295
249 v8::Local<v8::Value> module; 296 v8::Local<v8::Value> module;
250 { 297 {
251 NativesEnabledScope natives_enabled(this); 298 NativesEnabledScope natives_enabled(this);
252 module = v8::Local<v8::Value>::New( 299 module = v8::Local<v8::Value>::New(
253 RequireForJsInner(v8::String::New(module_name.c_str()))); 300 RequireForJsInner(v8::String::New(module_name.c_str())));
254 } 301 }
255 302
256 if (module.IsEmpty() || !module->IsObject()) { 303 if (module.IsEmpty() || !module->IsObject()) {
257 console::Error( 304 Fatal(context_->GetExtensionID(),
258 v8::Context::GetCalling(), 305 "Failed to get module " + module_name + " to call " + method_name);
259 "Failed to get module " + module_name + " to call " + method_name);
260 return handle_scope.Close(v8::Undefined()); 306 return handle_scope.Close(v8::Undefined());
261 } 307 }
262 308
263 v8::Local<v8::Value> value = 309 v8::Local<v8::Value> value =
264 v8::Handle<v8::Object>::Cast(module)->Get( 310 v8::Handle<v8::Object>::Cast(module)->Get(
265 v8::String::New(method_name.c_str())); 311 v8::String::New(method_name.c_str()));
266 if (value.IsEmpty() || !value->IsFunction()) { 312 if (value.IsEmpty() || !value->IsFunction()) {
267 console::Error(v8::Context::GetCalling(), 313 Fatal(context_->GetExtensionID(),
268 module_name + "." + method_name + " is not a function"); 314 module_name + "." + method_name + " is not a function");
269 return handle_scope.Close(v8::Undefined()); 315 return handle_scope.Close(v8::Undefined());
270 } 316 }
271 317
272 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value); 318 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
273 v8::Local<v8::Value> result; 319 v8::Local<v8::Value> result;
274 { 320 {
275 v8::TryCatch try_catch; 321 v8::TryCatch try_catch;
276 try_catch.SetCaptureMessage(true); 322 try_catch.SetCaptureMessage(true);
277 result = context_->CallFunction(func, argc, argv); 323 result = context_->CallFunction(func, argc, argv);
278 if (try_catch.HasCaught()) 324 if (try_catch.HasCaught())
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
322 v8::HandleScope handle_scope; 368 v8::HandleScope handle_scope;
323 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data()); 369 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data());
324 // This context should be the same as context()->v8_context(). 370 // This context should be the same as context()->v8_context().
325 v8::Handle<v8::Context> context = parameters->CreationContext(); 371 v8::Handle<v8::Context> context = parameters->CreationContext();
326 v8::Handle<v8::Object> global(context->Global()); 372 v8::Handle<v8::Object> global(context->Global());
327 v8::Handle<v8::Value> module_system_value = 373 v8::Handle<v8::Value> module_system_value =
328 global->GetHiddenValue(v8::String::New(kModuleSystem)); 374 global->GetHiddenValue(v8::String::New(kModuleSystem));
329 if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) { 375 if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) {
330 // ModuleSystem has been deleted. 376 // ModuleSystem has been deleted.
331 // TODO(kalman): See comment in header file. 377 // TODO(kalman): See comment in header file.
332 console::Warn(v8::Context::GetCalling(), 378 Warn("", "Module system has been deleted, does extension view exist?");
333 "Module system has been deleted, does extension view exist?");
334 return; 379 return;
335 } 380 }
336 381
337 ModuleSystem* module_system = static_cast<ModuleSystem*>( 382 ModuleSystem* module_system = static_cast<ModuleSystem*>(
338 v8::Handle<v8::External>::Cast(module_system_value)->Value()); 383 v8::Handle<v8::External>::Cast(module_system_value)->Value());
339 384
340 std::string name = *v8::String::AsciiValue( 385 std::string name = *v8::String::AsciiValue(
341 parameters->Get(v8::String::New(kModuleName))->ToString()); 386 parameters->Get(v8::String::New(kModuleName))->ToString());
342 387
343 // Switch to our v8 context because we need functions created while running 388 // Switch to our v8 context because we need functions created while running
(...skipping 11 matching lines...) Expand all
355 // require_function will have already logged this, we don't need to. 400 // require_function will have already logged this, we don't need to.
356 return; 401 return;
357 } 402 }
358 403
359 v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value); 404 v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value);
360 v8::Handle<v8::String> field = 405 v8::Handle<v8::String> field =
361 parameters->Get(v8::String::New(kModuleField))->ToString(); 406 parameters->Get(v8::String::New(kModuleField))->ToString();
362 407
363 if (!module->Has(field)) { 408 if (!module->Has(field)) {
364 std::string field_str = *v8::String::AsciiValue(field); 409 std::string field_str = *v8::String::AsciiValue(field);
365 console::Fatal(v8::Context::GetCalling(), 410 Fatal(module_system->context_->GetExtensionID(),
366 "Lazy require of " + name + "." + field_str + " did not " + 411 "Lazy require of " + name + "." + field_str + " did not " +
367 "set the " + field_str + " field"); 412 "set the " + field_str + " field");
368 return; 413 return;
369 } 414 }
370 415
371 v8::Local<v8::Value> new_field = module->Get(field); 416 v8::Local<v8::Value> new_field = module->Get(field);
372 if (try_catch.HasCaught()) { 417 if (try_catch.HasCaught()) {
373 module_system->HandleException(try_catch); 418 module_system->HandleException(try_catch);
374 return; 419 return;
375 } 420 }
376 421
377 // Ok for it to be undefined, among other things it's how bindings signify 422 // 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
457 } 502 }
458 503
459 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( 504 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
460 const std::string& native_name) { 505 const std::string& native_name) {
461 if (natives_enabled_ == 0) { 506 if (natives_enabled_ == 0) {
462 // HACK: if in test throw exception so that we can test the natives-disabled 507 // HACK: if in test throw exception so that we can test the natives-disabled
463 // logic; however, under normal circumstances, this is programmer error so 508 // logic; however, under normal circumstances, this is programmer error so
464 // we could crash. 509 // we could crash.
465 if (exception_handler_) 510 if (exception_handler_)
466 return v8::ThrowException(v8::String::New("Natives disabled")); 511 return v8::ThrowException(v8::String::New("Natives disabled"));
467 console::Fatal(v8::Context::GetCalling(), 512 Fatal(context_->GetExtensionID(),
468 "Natives disabled for requireNative(" + native_name + ")"); 513 "Natives disabled for requireNative(" + native_name + ")");
469 return v8::Undefined(); 514 return v8::Undefined();
470 } 515 }
471 516
472 if (overridden_native_handlers_.count(native_name) > 0u) 517 if (overridden_native_handlers_.count(native_name) > 0u)
473 return RequireForJsInner(v8::String::New(native_name.c_str())); 518 return RequireForJsInner(v8::String::New(native_name.c_str()));
474 519
475 NativeHandlerMap::iterator i = native_handler_map_.find(native_name); 520 NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
476 if (i == native_handler_map_.end()) { 521 if (i == native_handler_map_.end()) {
477 console::Fatal( 522 Fatal(context_->GetExtensionID(),
478 v8::Context::GetCalling(), 523 "Couldn't find native for requireNative(" + native_name + ")");
479 "Couldn't find native for requireNative(" + native_name + ")");
480 return v8::Undefined(); 524 return v8::Undefined();
481 } 525 }
482 return i->second->NewInstance(); 526 return i->second->NewInstance();
483 } 527 }
484 528
485 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) { 529 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) {
486 v8::HandleScope handle_scope; 530 v8::HandleScope handle_scope;
487 // Keep in order with the arguments in RequireForJsInner. 531 // Keep in order with the arguments in RequireForJsInner.
488 v8::Handle<v8::String> left = v8::String::New( 532 v8::Handle<v8::String> left = v8::String::New(
489 "(function(require, requireNative, exports," 533 "(function(require, requireNative, exports,"
490 "$Array, $Function, $JSON, $Object, $RegExp, $String) {" 534 "$Array, $Function, $JSON, $Object, $RegExp, $String) {"
491 "'use strict';"); 535 "'use strict';");
492 v8::Handle<v8::String> right = v8::String::New("\n})"); 536 v8::Handle<v8::String> right = v8::String::New("\n})");
493 return handle_scope.Close( 537 return handle_scope.Close(
494 v8::String::Concat(left, v8::String::Concat(source, right))); 538 v8::String::Concat(left, v8::String::Concat(source, right)));
495 } 539 }
496 540
497 } // namespace extensions 541 } // namespace extensions
OLDNEW
« no previous file with comments | « chrome/renderer/extensions/dispatcher.cc ('k') | chrome/renderer/resources/extensions/ad_view.js » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698