OLD | NEW |
---|---|
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 Loading... | |
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 |
OLD | NEW |