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

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: . 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 "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& message,
32 const std::string& extension_id) {
33 if (extension_id.empty())
34 return message;
35 return "(" + extension_id + ") " + message;
Jeffrey Yasskin 2013/06/14 21:42:59 This sort of expression on std::string is slow and
not at google - send to devlin 2013/06/14 22:00:32 Done.
36 }
37
38 void Fatal(const std::string& message, const std::string& extension_id) {
Jeffrey Yasskin 2013/06/14 21:42:59 nit: I would probably put the extension_id first s
not at google - send to devlin 2013/06/14 22:00:32 done.
39 // Only crash extension processes. Ideally we would crash on *any* (i.e. web
40 // too), but given we hardly have code coverage for JS we should be careful
41 // and not break the web.
42 const CommandLine* command_line = CommandLine::ForCurrentProcess();
43 bool is_extension_process =
44 command_line->HasSwitch(switches::kExtensionProcess) ||
45 command_line->HasSwitch(switches::kSingleProcess);
Jeffrey Yasskin 2013/06/14 21:42:59 You need a comment justifying crashing the whole b
not at google - send to devlin 2013/06/14 22:00:32 Done.
46 std::string with_extension_id = PrependExtensionID(message, extension_id);
47 if (is_extension_process)
Jeffrey Yasskin 2013/06/14 21:42:59 Maybe restrict this to the dev or even canary chan
not at google - send to devlin 2013/06/14 22:00:32 As in - you want it to crash in web pages, or you
Jeffrey Yasskin 2013/06/14 22:11:22 I think I'd want it to never crash at all on stabl
not at google - send to devlin 2013/06/14 22:27:44 True on the case of crashing in new situations; I
48 console::Fatal(v8::Context::GetCalling(), with_extension_id);
49 else
50 console::Error(v8::Context::GetCalling(), with_extension_id);
51 }
52
53 void Warn(const std::string& message, const std::string& extension_id) {
54 console::Warn(v8::Context::GetCalling(),
55 PrependExtensionID(message, extension_id));
56 }
57
28 // Default exception handler which logs the exception. 58 // Default exception handler which logs the exception.
29 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler { 59 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
30 public: 60 public:
61 explicit DefaultExceptionHandler(const std::string& extension_id)
62 : extension_id_(extension_id) {}
63
31 // Fatally dumps the debug info from |try_catch| to the console. 64 // Fatally dumps the debug info from |try_catch| to the console.
32 // Make sure this is never used for exceptions that originate in external 65 // Make sure this is never used for exceptions that originate in external
33 // code! 66 // code!
34 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE { 67 virtual void HandleUncaughtException(const v8::TryCatch& try_catch) OVERRIDE {
35 v8::HandleScope handle_scope; 68 v8::HandleScope handle_scope;
36 std::string stack_trace = "<stack trace unavailable>"; 69 std::string stack_trace = "<stack trace unavailable>";
37 if (!try_catch.StackTrace().IsEmpty()) { 70 if (!try_catch.StackTrace().IsEmpty()) {
38 v8::String::Utf8Value stack_value(try_catch.StackTrace()); 71 v8::String::Utf8Value stack_value(try_catch.StackTrace());
39 if (*stack_value) 72 if (*stack_value)
40 stack_trace.assign(*stack_value, stack_value.length()); 73 stack_trace.assign(*stack_value, stack_value.length());
41 else 74 else
42 stack_trace = "<could not convert stack trace to string>"; 75 stack_trace = "<could not convert stack trace to string>";
43 } 76 }
44 console::Fatal(v8::Context::GetCalling(), 77 Fatal(CreateExceptionString(try_catch) + "{" + stack_trace + "}",
45 CreateExceptionString(try_catch) + "{" + stack_trace + "}"); 78 extension_id_);
46 } 79 }
80
81 private:
82 std::string extension_id_;
47 }; 83 };
48 84
49 } // namespace 85 } // namespace
50 86
51 std::string ModuleSystem::ExceptionHandler::CreateExceptionString( 87 std::string ModuleSystem::ExceptionHandler::CreateExceptionString(
52 const v8::TryCatch& try_catch) { 88 const v8::TryCatch& try_catch) {
53 v8::Handle<v8::Message> message(try_catch.Message()); 89 v8::Handle<v8::Message> message(try_catch.Message());
54 if (message.IsEmpty()) { 90 if (message.IsEmpty()) {
55 return "try_catch has no message"; 91 return "try_catch has no message";
56 } 92 }
(...skipping 16 matching lines...) Expand all
73 message->GetLineNumber(), 109 message->GetLineNumber(),
74 error_message.c_str()); 110 error_message.c_str());
75 } 111 }
76 112
77 ModuleSystem::ModuleSystem(ChromeV8Context* context, 113 ModuleSystem::ModuleSystem(ChromeV8Context* context,
78 SourceMap* source_map) 114 SourceMap* source_map)
79 : ObjectBackedNativeHandler(context), 115 : ObjectBackedNativeHandler(context),
80 context_(context), 116 context_(context),
81 source_map_(source_map), 117 source_map_(source_map),
82 natives_enabled_(0), 118 natives_enabled_(0),
83 exception_handler_(new DefaultExceptionHandler()) { 119 exception_handler_(
120 new DefaultExceptionHandler(context->GetExtensionID())) {
84 RouteFunction("require", 121 RouteFunction("require",
85 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this))); 122 base::Bind(&ModuleSystem::RequireForJs, base::Unretained(this)));
86 RouteFunction("requireNative", 123 RouteFunction("requireNative",
87 base::Bind(&ModuleSystem::RequireNative, base::Unretained(this))); 124 base::Bind(&ModuleSystem::RequireNative, base::Unretained(this)));
88 125
89 v8::Handle<v8::Object> global(context->v8_context()->Global()); 126 v8::Handle<v8::Object> global(context->v8_context()->Global());
90 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New()); 127 global->SetHiddenValue(v8::String::New(kModulesField), v8::Object::New());
91 global->SetHiddenValue(v8::String::New(kModuleSystem), 128 global->SetHiddenValue(v8::String::New(kModuleSystem),
92 v8::External::New(this)); 129 v8::External::New(this));
93 } 130 }
(...skipping 57 matching lines...) Expand 10 before | Expand all | Expand 10 after
151 v8::Context::Scope context_scope(context()->v8_context()); 188 v8::Context::Scope context_scope(context()->v8_context());
152 189
153 v8::Handle<v8::Object> global(context()->v8_context()->Global()); 190 v8::Handle<v8::Object> global(context()->v8_context()->Global());
154 191
155 // The module system might have been deleted. This can happen if a different 192 // 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. 193 // 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). 194 // background page keeps reference to chrome object in a closed popup).
158 v8::Handle<v8::Value> modules_value = 195 v8::Handle<v8::Value> modules_value =
159 global->GetHiddenValue(v8::String::New(kModulesField)); 196 global->GetHiddenValue(v8::String::New(kModulesField));
160 if (modules_value.IsEmpty() || modules_value->IsUndefined()) { 197 if (modules_value.IsEmpty() || modules_value->IsUndefined()) {
161 console::Warn(v8::Context::GetCalling(), "Extension view no longer exists"); 198 Warn("Extension view no longer exists", context_->GetExtensionID());
162 return v8::Undefined(); 199 return v8::Undefined();
163 } 200 }
164 201
165 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value)); 202 v8::Handle<v8::Object> modules(v8::Handle<v8::Object>::Cast(modules_value));
166 v8::Handle<v8::Value> exports(modules->Get(module_name)); 203 v8::Handle<v8::Value> exports(modules->Get(module_name));
167 if (!exports->IsUndefined()) 204 if (!exports->IsUndefined())
168 return handle_scope.Close(exports); 205 return handle_scope.Close(exports);
169 206
170 std::string module_name_str = *v8::String::AsciiValue(module_name); 207 std::string module_name_str = *v8::String::AsciiValue(module_name);
171 v8::Handle<v8::Value> source(GetSource(module_name_str)); 208 v8::Handle<v8::Value> source(GetSource(module_name_str));
172 if (source.IsEmpty() || source->IsUndefined()) { 209 if (source.IsEmpty() || source->IsUndefined()) {
173 console::Error(v8::Context::GetCalling(), 210 Fatal("No source for require(" + module_name_str + ")",
174 "No source for require(" + module_name_str + ")"); 211 context_->GetExtensionID());
175 return v8::Undefined(); 212 return v8::Undefined();
176 } 213 }
177 v8::Handle<v8::String> wrapped_source(WrapSource( 214 v8::Handle<v8::String> wrapped_source(WrapSource(
178 v8::Handle<v8::String>::Cast(source))); 215 v8::Handle<v8::String>::Cast(source)));
179 // Modules are wrapped in (function(){...}) so they always return functions. 216 // Modules are wrapped in (function(){...}) so they always return functions.
180 v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name); 217 v8::Handle<v8::Value> func_as_value = RunString(wrapped_source, module_name);
181 if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) { 218 if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
182 console::Error(v8::Context::GetCalling(), 219 Fatal("Bad source for require(" + module_name_str + ")",
183 "Bad source for require(" + module_name_str + ")"); 220 context_->GetExtensionID());
184 return v8::Undefined(); 221 return v8::Undefined();
185 } 222 }
186 223
187 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value); 224 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(func_as_value);
188 225
189 exports = v8::Object::New(); 226 exports = v8::Object::New();
190 v8::Handle<v8::Object> natives(NewInstance()); 227 v8::Handle<v8::Object> natives(NewInstance());
191 v8::Handle<v8::Value> args[] = { 228 v8::Handle<v8::Value> args[] = {
192 natives->Get(v8::String::NewSymbol("require")), 229 natives->Get(v8::String::NewSymbol("require")),
193 natives->Get(v8::String::NewSymbol("requireNative")), 230 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()); 272 v8::Context::Scope context_scope(context()->v8_context());
236 273
237 v8::Local<v8::Value> module; 274 v8::Local<v8::Value> module;
238 { 275 {
239 NativesEnabledScope natives_enabled(this); 276 NativesEnabledScope natives_enabled(this);
240 module = v8::Local<v8::Value>::New( 277 module = v8::Local<v8::Value>::New(
241 RequireForJsInner(v8::String::New(module_name.c_str()))); 278 RequireForJsInner(v8::String::New(module_name.c_str())));
242 } 279 }
243 280
244 if (module.IsEmpty() || !module->IsObject()) { 281 if (module.IsEmpty() || !module->IsObject()) {
245 console::Error( 282 Fatal("Failed to get module " + module_name + " to call " + method_name,
246 v8::Context::GetCalling(), 283 context_->GetExtensionID());
247 "Failed to get module " + module_name + " to call " + method_name);
248 return handle_scope.Close(v8::Undefined()); 284 return handle_scope.Close(v8::Undefined());
249 } 285 }
250 286
251 v8::Local<v8::Value> value = 287 v8::Local<v8::Value> value =
252 v8::Handle<v8::Object>::Cast(module)->Get( 288 v8::Handle<v8::Object>::Cast(module)->Get(
253 v8::String::New(method_name.c_str())); 289 v8::String::New(method_name.c_str()));
254 if (value.IsEmpty() || !value->IsFunction()) { 290 if (value.IsEmpty() || !value->IsFunction()) {
255 console::Error(v8::Context::GetCalling(), 291 Fatal(module_name + "." + method_name + " is not a function",
256 module_name + "." + method_name + " is not a function"); 292 context_->GetExtensionID());
257 return handle_scope.Close(v8::Undefined()); 293 return handle_scope.Close(v8::Undefined());
258 } 294 }
259 295
260 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value); 296 v8::Handle<v8::Function> func = v8::Handle<v8::Function>::Cast(value);
261 v8::Local<v8::Value> result; 297 v8::Local<v8::Value> result;
262 { 298 {
263 v8::TryCatch try_catch; 299 v8::TryCatch try_catch;
264 try_catch.SetCaptureMessage(true); 300 try_catch.SetCaptureMessage(true);
265 result = context_->CallFunction(func, argc, argv); 301 result = context_->CallFunction(func, argc, argv);
266 if (try_catch.HasCaught()) 302 if (try_catch.HasCaught())
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 v8::HandleScope handle_scope; 346 v8::HandleScope handle_scope;
311 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data()); 347 v8::Handle<v8::Object> parameters = v8::Handle<v8::Object>::Cast(info.Data());
312 // This context should be the same as context()->v8_context(). 348 // This context should be the same as context()->v8_context().
313 v8::Handle<v8::Context> context = parameters->CreationContext(); 349 v8::Handle<v8::Context> context = parameters->CreationContext();
314 v8::Handle<v8::Object> global(context->Global()); 350 v8::Handle<v8::Object> global(context->Global());
315 v8::Handle<v8::Value> module_system_value = 351 v8::Handle<v8::Value> module_system_value =
316 global->GetHiddenValue(v8::String::New(kModuleSystem)); 352 global->GetHiddenValue(v8::String::New(kModuleSystem));
317 if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) { 353 if (module_system_value.IsEmpty() || !module_system_value->IsExternal()) {
318 // ModuleSystem has been deleted. 354 // ModuleSystem has been deleted.
319 // TODO(kalman): See comment in header file. 355 // TODO(kalman): See comment in header file.
320 console::Warn(v8::Context::GetCalling(), 356 Warn("Module system has been deleted, does extension view exist?", "");
321 "Module system has been deleted, does extension view exist?");
322 return; 357 return;
323 } 358 }
324 359
325 ModuleSystem* module_system = static_cast<ModuleSystem*>( 360 ModuleSystem* module_system = static_cast<ModuleSystem*>(
326 v8::Handle<v8::External>::Cast(module_system_value)->Value()); 361 v8::Handle<v8::External>::Cast(module_system_value)->Value());
327 362
328 std::string name = *v8::String::AsciiValue( 363 std::string name = *v8::String::AsciiValue(
329 parameters->Get(v8::String::New(kModuleName))->ToString()); 364 parameters->Get(v8::String::New(kModuleName))->ToString());
330 365
331 // Switch to our v8 context because we need functions created while running 366 // 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. 378 // require_function will have already logged this, we don't need to.
344 return; 379 return;
345 } 380 }
346 381
347 v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value); 382 v8::Handle<v8::Object> module = v8::Handle<v8::Object>::Cast(module_value);
348 v8::Handle<v8::String> field = 383 v8::Handle<v8::String> field =
349 parameters->Get(v8::String::New(kModuleField))->ToString(); 384 parameters->Get(v8::String::New(kModuleField))->ToString();
350 385
351 if (!module->Has(field)) { 386 if (!module->Has(field)) {
352 std::string field_str = *v8::String::AsciiValue(field); 387 std::string field_str = *v8::String::AsciiValue(field);
353 console::Fatal(v8::Context::GetCalling(), 388 Fatal("Lazy require of " + name + "." + field_str + " did not " +
354 "Lazy require of " + name + "." + field_str + " did not " + 389 "set the " + field_str + " field",
355 "set the " + field_str + " field"); 390 module_system->context_->GetExtensionID());
356 return; 391 return;
357 } 392 }
358 393
359 v8::Local<v8::Value> new_field = module->Get(field); 394 v8::Local<v8::Value> new_field = module->Get(field);
360 if (try_catch.HasCaught()) { 395 if (try_catch.HasCaught()) {
361 module_system->HandleException(try_catch); 396 module_system->HandleException(try_catch);
362 return; 397 return;
363 } 398 }
364 399
365 // Ok for it to be undefined, among other things it's how bindings signify 400 // 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 } 480 }
446 481
447 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString( 482 v8::Handle<v8::Value> ModuleSystem::RequireNativeFromString(
448 const std::string& native_name) { 483 const std::string& native_name) {
449 if (natives_enabled_ == 0) { 484 if (natives_enabled_ == 0) {
450 // HACK: if in test throw exception so that we can test the natives-disabled 485 // 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 486 // logic; however, under normal circumstances, this is programmer error so
452 // we could crash. 487 // we could crash.
453 if (exception_handler_) 488 if (exception_handler_)
454 return v8::ThrowException(v8::String::New("Natives disabled")); 489 return v8::ThrowException(v8::String::New("Natives disabled"));
455 console::Fatal(v8::Context::GetCalling(), 490 Fatal("Natives disabled for requireNative(" + native_name + ")",
456 "Natives disabled for requireNative(" + native_name + ")"); 491 context_->GetExtensionID());
457 return v8::Undefined(); 492 return v8::Undefined();
458 } 493 }
459 494
460 if (overridden_native_handlers_.count(native_name) > 0u) 495 if (overridden_native_handlers_.count(native_name) > 0u)
461 return RequireForJsInner(v8::String::New(native_name.c_str())); 496 return RequireForJsInner(v8::String::New(native_name.c_str()));
462 497
463 NativeHandlerMap::iterator i = native_handler_map_.find(native_name); 498 NativeHandlerMap::iterator i = native_handler_map_.find(native_name);
464 if (i == native_handler_map_.end()) { 499 if (i == native_handler_map_.end()) {
465 console::Fatal( 500 Fatal("Couldn't find native for requireNative(" + native_name + ")",
466 v8::Context::GetCalling(), 501 context_->GetExtensionID());
467 "Couldn't find native for requireNative(" + native_name + ")");
468 return v8::Undefined(); 502 return v8::Undefined();
469 } 503 }
470 return i->second->NewInstance(); 504 return i->second->NewInstance();
471 } 505 }
472 506
473 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) { 507 v8::Handle<v8::String> ModuleSystem::WrapSource(v8::Handle<v8::String> source) {
474 v8::HandleScope handle_scope; 508 v8::HandleScope handle_scope;
475 v8::Handle<v8::String> left = v8::String::New( 509 v8::Handle<v8::String> left = v8::String::New(
476 "(function(require, requireNative, exports) {'use strict';"); 510 "(function(require, requireNative, exports) {'use strict';");
477 v8::Handle<v8::String> right = v8::String::New("\n})"); 511 v8::Handle<v8::String> right = v8::String::New("\n})");
478 return handle_scope.Close( 512 return handle_scope.Close(
479 v8::String::Concat(left, v8::String::Concat(source, right))); 513 v8::String::Concat(left, v8::String::Concat(source, right)));
480 } 514 }
481 515
482 } // namespace extensions 516 } // namespace extensions
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698