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