1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/renderer/module_system.h"
8 #include "base/command_line.h"
9 #include "base/debug/trace_event.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "content/public/renderer/render_view.h"
14 #include "extensions/common/extension_messages.h"
15 #include "extensions/common/extensions_client.h"
16 #include "extensions/renderer/console.h"
17 #include "extensions/renderer/safe_builtins.h"
18 #include "extensions/renderer/script_context.h"
19 #include "third_party/WebKit/public/web/WebFrame.h"
20 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
22 namespace extensions
{
26 const char* kModuleSystem
= "module_system";
27 const char* kModuleName
= "module_name";
28 const char* kModuleField
= "module_field";
29 const char* kModulesField
= "modules";
31 // Logs a fatal error for the calling context, with some added metadata about
33 // - Its type (blessed, unblessed, etc).
34 // - Whether it's valid.
35 // - The extension ID, if one exists.
37 // This will only actual be fatal in in dev/canary, since in too many cases
38 // we're at the mercy of the extension or web page's environment. They can mess
39 // up our JS in unexpected ways. Hopefully dev/canary channel will pick up such
40 // problems, but given the wider variety on stable/beta it's impossible to know.
41 void Fatal(ScriptContext
* context
, const std::string
& message
) {
42 // Prepend some context metadata.
43 std::string full_message
= "(";
44 if (!context
->is_valid())
45 full_message
+= "Invalid ";
46 full_message
+= context
->GetContextTypeDescription();
47 full_message
+= " context";
48 if (context
->extension()) {
49 full_message
+= " for ";
50 full_message
+= context
->extension()->id();
53 full_message
+= message
;
55 if (ExtensionsClient::Get()->ShouldSuppressFatalErrors())
56 console::Error(context
->isolate()->GetCallingContext(), full_message
);
58 console::Fatal(context
->isolate()->GetCallingContext(), full_message
);
61 void Warn(v8::Isolate
* isolate
, const std::string
& message
) {
62 console::Warn(isolate
->GetCallingContext(), message
);
65 // Default exception handler which logs the exception.
66 class DefaultExceptionHandler
: public ModuleSystem::ExceptionHandler
{
68 explicit DefaultExceptionHandler(ScriptContext
* context
)
69 : context_(context
) {}
71 // Fatally dumps the debug info from |try_catch| to the console.
72 // Make sure this is never used for exceptions that originate in external
74 virtual void HandleUncaughtException(const v8::TryCatch
& try_catch
) OVERRIDE
{
75 v8::HandleScope
handle_scope(context_
->isolate());
76 std::string stack_trace
= "<stack trace unavailable>";
77 if (!try_catch
.StackTrace().IsEmpty()) {
78 v8::String::Utf8Value
stack_value(try_catch
.StackTrace());
80 stack_trace
.assign(*stack_value
, stack_value
.length());
82 stack_trace
= "<could not convert stack trace to string>";
84 Fatal(context_
, CreateExceptionString(try_catch
) + "{" + stack_trace
+ "}");
88 ScriptContext
* context_
;
93 std::string
ModuleSystem::ExceptionHandler::CreateExceptionString(
94 const v8::TryCatch
& try_catch
) {
95 v8::Handle
<v8::Message
> message(try_catch
.Message());
96 if (message
.IsEmpty()) {
97 return "try_catch has no message";
100 std::string resource_name
= "<unknown resource>";
101 if (!message
->GetScriptResourceName().IsEmpty()) {
102 v8::String::Utf8Value
resource_name_v8(
103 message
->GetScriptResourceName()->ToString());
104 resource_name
.assign(*resource_name_v8
, resource_name_v8
.length());
107 std::string error_message
= "<no error message>";
108 if (!message
->Get().IsEmpty()) {
109 v8::String::Utf8Value
error_message_v8(message
->Get());
110 error_message
.assign(*error_message_v8
, error_message_v8
.length());
113 return base::StringPrintf("%s:%d: %s",
114 resource_name
.c_str(),
115 message
->GetLineNumber(),
116 error_message
.c_str());
119 ModuleSystem::ModuleSystem(ScriptContext
* context
, SourceMap
* source_map
)
120 : ObjectBackedNativeHandler(context
),
122 source_map_(source_map
),
124 exception_handler_(new DefaultExceptionHandler(context
)) {
127 base::Bind(&ModuleSystem::RequireForJs
, base::Unretained(this)));
130 base::Bind(&ModuleSystem::RequireNative
, base::Unretained(this)));
131 RouteFunction("privates",
132 base::Bind(&ModuleSystem::Private
, base::Unretained(this)));
134 v8::Handle
<v8::Object
> global(context
->v8_context()->Global());
135 v8::Isolate
* isolate
= context
->isolate();
136 global
->SetHiddenValue(v8::String::NewFromUtf8(isolate
, kModulesField
),
137 v8::Object::New(isolate
));
138 global
->SetHiddenValue(v8::String::NewFromUtf8(isolate
, kModuleSystem
),
139 v8::External::New(isolate
, this));
142 ModuleSystem::~ModuleSystem() { Invalidate(); }
144 void ModuleSystem::Invalidate() {
148 // Clear the module system properties from the global context. It's polite,
149 // and we use this as a signal in lazy handlers that we no longer exist.
151 v8::HandleScope
scope(GetIsolate());
152 v8::Handle
<v8::Object
> global
= context()->v8_context()->Global();
153 global
->DeleteHiddenValue(
154 v8::String::NewFromUtf8(GetIsolate(), kModulesField
));
155 global
->DeleteHiddenValue(
156 v8::String::NewFromUtf8(GetIsolate(), kModuleSystem
));
159 // Invalidate all of the successfully required handlers we own.
160 for (NativeHandlerMap::iterator it
= native_handler_map_
.begin();
161 it
!= native_handler_map_
.end();
163 it
->second
->Invalidate();
166 ObjectBackedNativeHandler::Invalidate();
169 ModuleSystem::NativesEnabledScope::NativesEnabledScope(
170 ModuleSystem
* module_system
)
171 : module_system_(module_system
) {
172 module_system_
->natives_enabled_
++;
175 ModuleSystem::NativesEnabledScope::~NativesEnabledScope() {
176 module_system_
->natives_enabled_
--;
177 CHECK_GE(module_system_
->natives_enabled_
, 0);
180 void ModuleSystem::HandleException(const v8::TryCatch
& try_catch
) {
181 exception_handler_
->HandleUncaughtException(try_catch
);
184 v8::Handle
<v8::Value
> ModuleSystem::Require(const std::string
& module_name
) {
185 v8::EscapableHandleScope
handle_scope(GetIsolate());
186 return handle_scope
.Escape(RequireForJsInner(
187 v8::String::NewFromUtf8(GetIsolate(), module_name
.c_str())));
190 void ModuleSystem::RequireForJs(
191 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
192 v8::Handle
<v8::String
> module_name
= args
[0]->ToString();
193 args
.GetReturnValue().Set(RequireForJsInner(module_name
));
196 v8::Local
<v8::Value
> ModuleSystem::RequireForJsInner(
197 v8::Handle
<v8::String
> module_name
) {
198 v8::EscapableHandleScope
handle_scope(GetIsolate());
199 v8::Context::Scope
context_scope(context()->v8_context());
201 v8::Handle
<v8::Object
> global(context()->v8_context()->Global());
203 // The module system might have been deleted. This can happen if a different
204 // context keeps a reference to us, but our frame is destroyed (e.g.
205 // background page keeps reference to chrome object in a closed popup).
206 v8::Handle
<v8::Value
> modules_value
= global
->GetHiddenValue(
207 v8::String::NewFromUtf8(GetIsolate(), kModulesField
));
208 if (modules_value
.IsEmpty() || modules_value
->IsUndefined()) {
209 Warn(GetIsolate(), "Extension view no longer exists");
210 return v8::Undefined(GetIsolate());
213 v8::Handle
<v8::Object
> modules(v8::Handle
<v8::Object
>::Cast(modules_value
));
214 v8::Local
<v8::Value
> exports(modules
->Get(module_name
));
215 if (!exports
->IsUndefined())
216 return handle_scope
.Escape(exports
);
218 std::string module_name_str
= *v8::String::Utf8Value(module_name
);
219 v8::Handle
<v8::Value
> source(GetSource(module_name_str
));
220 if (source
.IsEmpty() || source
->IsUndefined()) {
221 Fatal(context_
, "No source for require(" + module_name_str
+ ")");
222 return v8::Undefined(GetIsolate());
224 v8::Handle
<v8::String
> wrapped_source(
225 WrapSource(v8::Handle
<v8::String
>::Cast(source
)));
226 // Modules are wrapped in (function(){...}) so they always return functions.
227 v8::Handle
<v8::Value
> func_as_value
= RunString(wrapped_source
, module_name
);
228 if (func_as_value
.IsEmpty() || func_as_value
->IsUndefined()) {
229 Fatal(context_
, "Bad source for require(" + module_name_str
+ ")");
230 return v8::Undefined(GetIsolate());
233 v8::Handle
<v8::Function
> func
= v8::Handle
<v8::Function
>::Cast(func_as_value
);
235 exports
= v8::Object::New(GetIsolate());
236 v8::Handle
<v8::Object
> natives(NewInstance());
237 CHECK(!natives
.IsEmpty()); // this can happen if v8 has issues
239 // These must match the argument order in WrapSource.
240 v8::Handle
<v8::Value
> args
[] = {
242 natives
->Get(v8::String::NewFromUtf8(
243 GetIsolate(), "require", v8::String::kInternalizedString
)),
244 natives
->Get(v8::String::NewFromUtf8(
245 GetIsolate(), "requireNative", v8::String::kInternalizedString
)),
247 // Libraries that we magically expose to every module.
248 console::AsV8Object(),
249 natives
->Get(v8::String::NewFromUtf8(
250 GetIsolate(), "privates", v8::String::kInternalizedString
)),
251 // Each safe builtin. Keep in order with the arguments in WrapSource.
252 context_
->safe_builtins()->GetArray(),
253 context_
->safe_builtins()->GetFunction(),
254 context_
->safe_builtins()->GetJSON(),
255 context_
->safe_builtins()->GetObjekt(),
256 context_
->safe_builtins()->GetRegExp(),
257 context_
->safe_builtins()->GetString(), };
259 v8::TryCatch try_catch
;
260 try_catch
.SetCaptureMessage(true);
261 context_
->CallFunction(func
, arraysize(args
), args
);
262 if (try_catch
.HasCaught()) {
263 HandleException(try_catch
);
264 return v8::Undefined(GetIsolate());
267 modules
->Set(module_name
, exports
);
268 return handle_scope
.Escape(exports
);
271 v8::Local
<v8::Value
> ModuleSystem::CallModuleMethod(
272 const std::string
& module_name
,
273 const std::string
& method_name
) {
274 v8::HandleScope
handle_scope(GetIsolate());
275 v8::Handle
<v8::Value
> no_args
;
276 return CallModuleMethod(module_name
, method_name
, 0, &no_args
);
279 v8::Local
<v8::Value
> ModuleSystem::CallModuleMethod(
280 const std::string
& module_name
,
281 const std::string
& method_name
,
282 std::vector
<v8::Handle
<v8::Value
> >* args
) {
283 return CallModuleMethod(
284 module_name
, method_name
, args
->size(), vector_as_array(args
));
287 v8::Local
<v8::Value
> ModuleSystem::CallModuleMethod(
288 const std::string
& module_name
,
289 const std::string
& method_name
,
291 v8::Handle
<v8::Value
> argv
[]) {
293 "v8.callModuleMethod",
299 v8::EscapableHandleScope
handle_scope(GetIsolate());
300 v8::Context::Scope
context_scope(context()->v8_context());
302 v8::Local
<v8::Value
> module
;
304 NativesEnabledScope
natives_enabled(this);
305 module
= RequireForJsInner(
306 v8::String::NewFromUtf8(GetIsolate(), module_name
.c_str()));
309 if (module
.IsEmpty() || !module
->IsObject()) {
311 "Failed to get module " + module_name
+ " to call " + method_name
);
312 return handle_scope
.Escape(
313 v8::Local
<v8::Primitive
>(v8::Undefined(GetIsolate())));
316 v8::Local
<v8::Value
> value
= v8::Handle
<v8::Object
>::Cast(module
)->Get(
317 v8::String::NewFromUtf8(GetIsolate(), method_name
.c_str()));
318 if (value
.IsEmpty() || !value
->IsFunction()) {
319 Fatal(context_
, module_name
+ "." + method_name
+ " is not a function");
320 return handle_scope
.Escape(
321 v8::Local
<v8::Primitive
>(v8::Undefined(GetIsolate())));
324 v8::Handle
<v8::Function
> func
= v8::Handle
<v8::Function
>::Cast(value
);
325 v8::Local
<v8::Value
> result
;
327 v8::TryCatch try_catch
;
328 try_catch
.SetCaptureMessage(true);
329 result
= context_
->CallFunction(func
, argc
, argv
);
330 if (try_catch
.HasCaught())
331 HandleException(try_catch
);
333 return handle_scope
.Escape(result
);
336 void ModuleSystem::RegisterNativeHandler(
337 const std::string
& name
,
338 scoped_ptr
<NativeHandler
> native_handler
) {
339 native_handler_map_
[name
] =
340 linked_ptr
<NativeHandler
>(native_handler
.release());
343 void ModuleSystem::OverrideNativeHandlerForTest(const std::string
& name
) {
344 overridden_native_handlers_
.insert(name
);
347 void ModuleSystem::RunString(const std::string
& code
, const std::string
& name
) {
348 v8::HandleScope
handle_scope(GetIsolate());
349 RunString(v8::String::NewFromUtf8(GetIsolate(), code
.c_str()),
350 v8::String::NewFromUtf8(GetIsolate(), name
.c_str()));
354 void ModuleSystem::NativeLazyFieldGetter(
355 v8::Local
<v8::String
> property
,
356 const v8::PropertyCallbackInfo
<v8::Value
>& info
) {
357 LazyFieldGetterInner(property
, info
, &ModuleSystem::RequireNativeFromString
);
361 void ModuleSystem::LazyFieldGetter(
362 v8::Local
<v8::String
> property
,
363 const v8::PropertyCallbackInfo
<v8::Value
>& info
) {
364 LazyFieldGetterInner(property
, info
, &ModuleSystem::Require
);
368 void ModuleSystem::LazyFieldGetterInner(
369 v8::Local
<v8::String
> property
,
370 const v8::PropertyCallbackInfo
<v8::Value
>& info
,
371 RequireFunction require_function
) {
372 CHECK(!info
.Data().IsEmpty());
373 CHECK(info
.Data()->IsObject());
374 v8::HandleScope
handle_scope(info
.GetIsolate());
375 v8::Handle
<v8::Object
> parameters
= v8::Handle
<v8::Object
>::Cast(info
.Data());
376 // This context should be the same as context()->v8_context().
377 v8::Handle
<v8::Context
> context
= parameters
->CreationContext();
378 v8::Handle
<v8::Object
> global(context
->Global());
379 v8::Handle
<v8::Value
> module_system_value
= global
->GetHiddenValue(
380 v8::String::NewFromUtf8(info
.GetIsolate(), kModuleSystem
));
381 if (module_system_value
.IsEmpty() || !module_system_value
->IsExternal()) {
382 // ModuleSystem has been deleted.
383 // TODO(kalman): See comment in header file.
384 Warn(info
.GetIsolate(),
385 "Module system has been deleted, does extension view exist?");
389 ModuleSystem
* module_system
= static_cast<ModuleSystem
*>(
390 v8::Handle
<v8::External
>::Cast(module_system_value
)->Value());
393 *v8::String::Utf8Value(
394 parameters
->Get(v8::String::NewFromUtf8(info
.GetIsolate(),
395 kModuleName
))->ToString());
397 // Switch to our v8 context because we need functions created while running
398 // the require()d module to belong to our context, not the current one.
399 v8::Context::Scope
context_scope(context
);
400 NativesEnabledScope
natives_enabled_scope(module_system
);
402 v8::TryCatch try_catch
;
403 v8::Handle
<v8::Value
> module_value
= (module_system
->*require_function
)(name
);
404 if (try_catch
.HasCaught()) {
405 module_system
->HandleException(try_catch
);
408 if (module_value
.IsEmpty() || !module_value
->IsObject()) {
409 // require_function will have already logged this, we don't need to.
413 v8::Handle
<v8::Object
> module
= v8::Handle
<v8::Object
>::Cast(module_value
);
414 v8::Handle
<v8::String
> field
=
415 parameters
->Get(v8::String::NewFromUtf8(info
.GetIsolate(), kModuleField
))
418 if (!module
->Has(field
)) {
419 std::string field_str
= *v8::String::Utf8Value(field
);
420 Fatal(module_system
->context_
,
421 "Lazy require of " + name
+ "." + field_str
+ " did not set the " +
422 field_str
+ " field");
426 v8::Local
<v8::Value
> new_field
= module
->Get(field
);
427 if (try_catch
.HasCaught()) {
428 module_system
->HandleException(try_catch
);
432 // Ok for it to be undefined, among other things it's how bindings signify
433 // that the extension doesn't have permission to use them.
434 CHECK(!new_field
.IsEmpty());
436 // Delete the getter and set this field to |new_field| so the same object is
437 // returned every time a certain API is accessed.
438 v8::Handle
<v8::Value
> val
= info
.This();
439 if (val
->IsObject()) {
440 v8::Handle
<v8::Object
> object
= v8::Handle
<v8::Object
>::Cast(val
);
441 object
->Delete(property
);
442 object
->Set(property
, new_field
);
446 info
.GetReturnValue().Set(new_field
);
449 void ModuleSystem::SetLazyField(v8::Handle
<v8::Object
> object
,
450 const std::string
& field
,
451 const std::string
& module_name
,
452 const std::string
& module_field
) {
454 object
, field
, module_name
, module_field
, &ModuleSystem::LazyFieldGetter
);
457 void ModuleSystem::SetLazyField(v8::Handle
<v8::Object
> object
,
458 const std::string
& field
,
459 const std::string
& module_name
,
460 const std::string
& module_field
,
461 v8::AccessorGetterCallback getter
) {
462 v8::HandleScope
handle_scope(GetIsolate());
463 v8::Handle
<v8::Object
> parameters
= v8::Object::New(GetIsolate());
464 parameters
->Set(v8::String::NewFromUtf8(GetIsolate(), kModuleName
),
465 v8::String::NewFromUtf8(GetIsolate(), module_name
.c_str()));
466 parameters
->Set(v8::String::NewFromUtf8(GetIsolate(), kModuleField
),
467 v8::String::NewFromUtf8(GetIsolate(), module_field
.c_str()));
468 object
->SetAccessor(v8::String::NewFromUtf8(GetIsolate(), field
.c_str()),
474 void ModuleSystem::SetNativeLazyField(v8::Handle
<v8::Object
> object
,
475 const std::string
& field
,
476 const std::string
& module_name
,
477 const std::string
& module_field
) {
482 &ModuleSystem::NativeLazyFieldGetter
);
485 v8::Handle
<v8::Value
> ModuleSystem::RunString(v8::Handle
<v8::String
> code
,
486 v8::Handle
<v8::String
> name
) {
487 v8::EscapableHandleScope
handle_scope(GetIsolate());
488 v8::Context::Scope
context_scope(context()->v8_context());
490 // Prepend extensions:: to |name| so that internal code can be differentiated
491 // from external code in stack traces. This has no effect on behaviour.
492 std::string internal_name
=
493 base::StringPrintf("extensions::%s", *v8::String::Utf8Value(name
));
495 blink::WebScopedMicrotaskSuppression suppression
;
496 v8::TryCatch try_catch
;
497 try_catch
.SetCaptureMessage(true);
498 v8::Handle
<v8::Script
> script(
499 v8::Script::Compile(code
,
500 v8::String::NewFromUtf8(GetIsolate(),
501 internal_name
.c_str(),
502 v8::String::kNormalString
,
503 internal_name
.size())));
504 if (try_catch
.HasCaught()) {
505 HandleException(try_catch
);
506 return v8::Undefined(GetIsolate());
509 v8::Local
<v8::Value
> result
= script
->Run();
510 if (try_catch
.HasCaught()) {
511 HandleException(try_catch
);
512 return v8::Undefined(GetIsolate());
515 return handle_scope
.Escape(result
);
518 v8::Handle
<v8::Value
> ModuleSystem::GetSource(const std::string
& module_name
) {
519 v8::EscapableHandleScope
handle_scope(GetIsolate());
520 if (!source_map_
->Contains(module_name
))
521 return v8::Undefined(GetIsolate());
522 return handle_scope
.Escape(
523 v8::Local
<v8::Value
>(source_map_
->GetSource(GetIsolate(), module_name
)));
526 void ModuleSystem::RequireNative(
527 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
528 CHECK_EQ(1, args
.Length());
529 std::string native_name
= *v8::String::Utf8Value(args
[0]->ToString());
530 args
.GetReturnValue().Set(RequireNativeFromString(native_name
));
533 v8::Handle
<v8::Value
> ModuleSystem::RequireNativeFromString(
534 const std::string
& native_name
) {
535 if (natives_enabled_
== 0) {
536 // HACK: if in test throw exception so that we can test the natives-disabled
537 // logic; however, under normal circumstances, this is programmer error so
539 if (exception_handler_
) {
540 return GetIsolate()->ThrowException(
541 v8::String::NewFromUtf8(GetIsolate(), "Natives disabled"));
543 Fatal(context_
, "Natives disabled for requireNative(" + native_name
+ ")");
544 return v8::Undefined(GetIsolate());
547 if (overridden_native_handlers_
.count(native_name
) > 0u) {
548 return RequireForJsInner(
549 v8::String::NewFromUtf8(GetIsolate(), native_name
.c_str()));
552 NativeHandlerMap::iterator i
= native_handler_map_
.find(native_name
);
553 if (i
== native_handler_map_
.end()) {
555 "Couldn't find native for requireNative(" + native_name
+ ")");
556 return v8::Undefined(GetIsolate());
558 return i
->second
->NewInstance();
561 v8::Handle
<v8::String
> ModuleSystem::WrapSource(v8::Handle
<v8::String
> source
) {
562 v8::EscapableHandleScope
handle_scope(GetIsolate());
563 // Keep in order with the arguments in RequireForJsInner.
564 v8::Handle
<v8::String
> left
= v8::String::NewFromUtf8(
566 "(function(require, requireNative, exports, "
568 "$Array, $Function, $JSON, $Object, $RegExp, $String) {"
570 v8::Handle
<v8::String
> right
= v8::String::NewFromUtf8(GetIsolate(), "\n})");
571 return handle_scope
.Escape(v8::Local
<v8::String
>(
572 v8::String::Concat(left
, v8::String::Concat(source
, right
))));
575 void ModuleSystem::Private(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
576 CHECK_EQ(1, args
.Length());
577 CHECK(args
[0]->IsObject());
578 v8::Local
<v8::Object
> obj
= args
[0].As
<v8::Object
>();
579 v8::Local
<v8::String
> privates_key
=
580 v8::String::NewFromUtf8(GetIsolate(), "privates");
581 v8::Local
<v8::Value
> privates
= obj
->GetHiddenValue(privates_key
);
582 if (privates
.IsEmpty()) {
583 privates
= v8::Object::New(args
.GetIsolate());
584 obj
->SetHiddenValue(privates_key
, privates
);
586 args
.GetReturnValue().Set(privates
);
589 } // namespace extensions