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