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/logging.h"
10 #include "base/stl_util.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/trace_event/trace_event.h"
14 #include "content/public/renderer/render_frame.h"
15 #include "content/public/renderer/render_view.h"
16 #include "extensions/common/extension.h"
17 #include "extensions/common/extensions_client.h"
18 #include "extensions/renderer/console.h"
19 #include "extensions/renderer/safe_builtins.h"
20 #include "extensions/renderer/script_context.h"
21 #include "extensions/renderer/script_context_set.h"
22 #include "extensions/renderer/v8_helpers.h"
23 #include "gin/modules/module_registry.h"
24 #include "third_party/WebKit/public/web/WebFrame.h"
25 #include "third_party/WebKit/public/web/WebScopedMicrotaskSuppression.h"
27 namespace extensions
{
29 using namespace v8_helpers
;
33 const char* kModuleSystem
= "module_system";
34 const char* kModuleName
= "module_name";
35 const char* kModuleField
= "module_field";
36 const char* kModulesField
= "modules";
38 // Logs an error for the calling context in preparation for potentially
39 // crashing the renderer, with some added metadata about the context:
40 // - Its type (blessed, unblessed, etc).
41 // - Whether it's valid.
42 // - The extension ID, if one exists.
43 // Crashing won't happen in stable/beta releases, but is encouraged to happen
44 // in the less stable released to catch errors early.
45 void Fatal(ScriptContext
* context
, const std::string
& message
) {
46 // Prepend some context metadata.
47 std::string full_message
= "(";
48 if (!context
->is_valid())
49 full_message
+= "Invalid ";
50 full_message
+= context
->GetContextTypeDescription();
51 full_message
+= " context";
52 if (context
->extension()) {
53 full_message
+= " for ";
54 full_message
+= context
->extension()->id();
57 full_message
+= message
;
59 ExtensionsClient
* client
= ExtensionsClient::Get();
60 if (client
->ShouldSuppressFatalErrors()) {
61 console::Error(context
->GetRenderFrame(), full_message
);
62 client
->RecordDidSuppressFatalError();
64 console::Fatal(context
->GetRenderFrame(), full_message
);
68 void Warn(v8::Isolate
* isolate
, const std::string
& message
) {
69 ScriptContext
* script_context
=
70 ScriptContextSet::GetContextByV8Context(isolate
->GetCallingContext());
71 console::Warn(script_context
? script_context
->GetRenderFrame() : nullptr,
75 // Default exception handler which logs the exception.
76 class DefaultExceptionHandler
: public ModuleSystem::ExceptionHandler
{
78 explicit DefaultExceptionHandler(ScriptContext
* context
)
79 : ModuleSystem::ExceptionHandler(context
) {}
81 // Fatally dumps the debug info from |try_catch| to the console.
82 // Make sure this is never used for exceptions that originate in external
84 void HandleUncaughtException(const v8::TryCatch
& try_catch
) override
{
85 v8::HandleScope
handle_scope(context_
->isolate());
86 std::string stack_trace
= "<stack trace unavailable>";
87 v8::Local
<v8::Value
> v8_stack_trace
;
88 if (try_catch
.StackTrace(context_
->v8_context()).ToLocal(&v8_stack_trace
)) {
89 v8::String::Utf8Value
stack_value(v8_stack_trace
);
91 stack_trace
.assign(*stack_value
, stack_value
.length());
93 stack_trace
= "<could not convert stack trace to string>";
95 Fatal(context_
, CreateExceptionString(try_catch
) + "{" + stack_trace
+ "}");
101 std::string
ModuleSystem::ExceptionHandler::CreateExceptionString(
102 const v8::TryCatch
& try_catch
) {
103 v8::Local
<v8::Message
> message(try_catch
.Message());
104 if (message
.IsEmpty()) {
105 return "try_catch has no message";
108 std::string resource_name
= "<unknown resource>";
109 if (!message
->GetScriptOrigin().ResourceName().IsEmpty()) {
110 v8::String::Utf8Value
resource_name_v8(
111 message
->GetScriptOrigin().ResourceName());
112 resource_name
.assign(*resource_name_v8
, resource_name_v8
.length());
115 std::string error_message
= "<no error message>";
116 if (!message
->Get().IsEmpty()) {
117 v8::String::Utf8Value
error_message_v8(message
->Get());
118 error_message
.assign(*error_message_v8
, error_message_v8
.length());
121 auto maybe
= message
->GetLineNumber(context_
->v8_context());
122 int line_number
= maybe
.IsJust() ? maybe
.FromJust() : 0;
123 return base::StringPrintf("%s:%d: %s",
124 resource_name
.c_str(),
126 error_message
.c_str());
129 ModuleSystem::ModuleSystem(ScriptContext
* context
, SourceMap
* source_map
)
130 : ObjectBackedNativeHandler(context
),
132 source_map_(source_map
),
134 exception_handler_(new DefaultExceptionHandler(context
)),
135 weak_factory_(this) {
138 base::Bind(&ModuleSystem::RequireForJs
, base::Unretained(this)));
141 base::Bind(&ModuleSystem::RequireNative
, base::Unretained(this)));
144 base::Bind(&ModuleSystem::RequireAsync
, base::Unretained(this)));
145 RouteFunction("privates",
146 base::Bind(&ModuleSystem::Private
, base::Unretained(this)));
148 v8::Local
<v8::Object
> global(context
->v8_context()->Global());
149 v8::Isolate
* isolate
= context
->isolate();
150 global
->SetHiddenValue(ToV8StringUnsafe(isolate
, kModulesField
),
151 v8::Object::New(isolate
));
152 global
->SetHiddenValue(ToV8StringUnsafe(isolate
, kModuleSystem
),
153 v8::External::New(isolate
, this));
155 gin::ModuleRegistry::From(context
->v8_context())->AddObserver(this);
156 if (context_
->GetRenderFrame()) {
157 context_
->GetRenderFrame()->EnsureMojoBuiltinsAreAvailable(
158 context
->isolate(), context
->v8_context());
162 ModuleSystem::~ModuleSystem() {
165 void ModuleSystem::Invalidate() {
166 // Clear the module system properties from the global context. It's polite,
167 // and we use this as a signal in lazy handlers that we no longer exist.
169 v8::HandleScope
scope(GetIsolate());
170 v8::Local
<v8::Object
> global
= context()->v8_context()->Global();
171 global
->DeleteHiddenValue(ToV8StringUnsafe(GetIsolate(), kModulesField
));
172 global
->DeleteHiddenValue(ToV8StringUnsafe(GetIsolate(), kModuleSystem
));
175 // Invalidate all active and clobbered NativeHandlers we own.
176 for (const auto& handler
: native_handler_map_
)
177 handler
.second
->Invalidate();
178 for (const auto& clobbered_handler
: clobbered_native_handlers_
)
179 clobbered_handler
->Invalidate();
181 ObjectBackedNativeHandler::Invalidate();
184 ModuleSystem::NativesEnabledScope::NativesEnabledScope(
185 ModuleSystem
* module_system
)
186 : module_system_(module_system
) {
187 module_system_
->natives_enabled_
++;
190 ModuleSystem::NativesEnabledScope::~NativesEnabledScope() {
191 module_system_
->natives_enabled_
--;
192 CHECK_GE(module_system_
->natives_enabled_
, 0);
195 void ModuleSystem::HandleException(const v8::TryCatch
& try_catch
) {
196 exception_handler_
->HandleUncaughtException(try_catch
);
199 v8::MaybeLocal
<v8::Object
> ModuleSystem::Require(
200 const std::string
& module_name
) {
201 v8::Local
<v8::String
> v8_module_name
;
202 if (!ToV8String(GetIsolate(), module_name
, &v8_module_name
))
203 return v8::MaybeLocal
<v8::Object
>();
204 v8::EscapableHandleScope
handle_scope(GetIsolate());
205 v8::Local
<v8::Value
> value
= RequireForJsInner(
207 if (value
.IsEmpty() || !value
->IsObject())
208 return v8::MaybeLocal
<v8::Object
>();
209 return handle_scope
.Escape(value
.As
<v8::Object
>());
212 void ModuleSystem::RequireForJs(
213 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
214 if (!args
[0]->IsString()) {
215 NOTREACHED() << "require() called with a non-string argument";
218 v8::Local
<v8::String
> module_name
= args
[0].As
<v8::String
>();
219 args
.GetReturnValue().Set(RequireForJsInner(module_name
));
222 v8::Local
<v8::Value
> ModuleSystem::RequireForJsInner(
223 v8::Local
<v8::String
> module_name
) {
224 v8::EscapableHandleScope
handle_scope(GetIsolate());
225 v8::Local
<v8::Context
> v8_context
= context()->v8_context();
226 v8::Context::Scope
context_scope(v8_context
);
228 v8::Local
<v8::Object
> global(context()->v8_context()->Global());
230 // The module system might have been deleted. This can happen if a different
231 // context keeps a reference to us, but our frame is destroyed (e.g.
232 // background page keeps reference to chrome object in a closed popup).
233 v8::Local
<v8::Value
> modules_value
= global
->GetHiddenValue(
234 ToV8StringUnsafe(GetIsolate(), kModulesField
));
235 if (modules_value
.IsEmpty() || modules_value
->IsUndefined()) {
236 Warn(GetIsolate(), "Extension view no longer exists");
237 return v8::Undefined(GetIsolate());
240 v8::Local
<v8::Object
> modules(v8::Local
<v8::Object
>::Cast(modules_value
));
241 v8::Local
<v8::Value
> exports
;
242 if (!GetProperty(v8_context
, modules
, module_name
, &exports
) ||
243 !exports
->IsUndefined())
244 return handle_scope
.Escape(exports
);
246 exports
= LoadModule(*v8::String::Utf8Value(module_name
));
247 SetProperty(v8_context
, modules
, module_name
, exports
);
248 return handle_scope
.Escape(exports
);
251 v8::Local
<v8::Value
> ModuleSystem::CallModuleMethod(
252 const std::string
& module_name
,
253 const std::string
& method_name
) {
254 v8::EscapableHandleScope
handle_scope(GetIsolate());
255 v8::Local
<v8::Value
> no_args
;
256 return handle_scope
.Escape(
257 CallModuleMethod(module_name
, method_name
, 0, &no_args
));
260 v8::Local
<v8::Value
> ModuleSystem::CallModuleMethod(
261 const std::string
& module_name
,
262 const std::string
& method_name
,
263 std::vector
<v8::Local
<v8::Value
>>* args
) {
264 return CallModuleMethod(
265 module_name
, method_name
, args
->size(), vector_as_array(args
));
268 v8::Local
<v8::Value
> ModuleSystem::CallModuleMethod(
269 const std::string
& module_name
,
270 const std::string
& method_name
,
272 v8::Local
<v8::Value
> argv
[]) {
274 "v8.callModuleMethod",
280 v8::EscapableHandleScope
handle_scope(GetIsolate());
281 v8::Local
<v8::Context
> v8_context
= context()->v8_context();
282 v8::Context::Scope
context_scope(v8_context
);
284 v8::Local
<v8::String
> v8_module_name
;
285 v8::Local
<v8::String
> v8_method_name
;
286 if (!ToV8String(GetIsolate(), module_name
.c_str(), &v8_module_name
) ||
287 !ToV8String(GetIsolate(), method_name
.c_str(), &v8_method_name
)) {
288 return handle_scope
.Escape(v8::Undefined(GetIsolate()));
291 v8::Local
<v8::Value
> module
;
293 NativesEnabledScope
natives_enabled(this);
294 module
= RequireForJsInner(v8_module_name
);
297 if (module
.IsEmpty() || !module
->IsObject()) {
299 "Failed to get module " + module_name
+ " to call " + method_name
);
300 return handle_scope
.Escape(v8::Undefined(GetIsolate()));
303 v8::Local
<v8::Object
> object
= v8::Local
<v8::Object
>::Cast(module
);
304 v8::Local
<v8::Value
> value
;
305 if (!GetProperty(v8_context
, object
, v8_method_name
, &value
) ||
306 !value
->IsFunction()) {
307 Fatal(context_
, module_name
+ "." + method_name
+ " is not a function");
308 return handle_scope
.Escape(v8::Undefined(GetIsolate()));
311 v8::Local
<v8::Function
> func
= v8::Local
<v8::Function
>::Cast(value
);
312 v8::Local
<v8::Value
> result
;
314 v8::TryCatch
try_catch(GetIsolate());
315 try_catch
.SetCaptureMessage(true);
316 result
= context_
->CallFunction(func
, argc
, argv
);
317 if (try_catch
.HasCaught()) {
318 HandleException(try_catch
);
319 result
= v8::Undefined(GetIsolate());
322 return handle_scope
.Escape(result
);
325 void ModuleSystem::RegisterNativeHandler(
326 const std::string
& name
,
327 scoped_ptr
<NativeHandler
> native_handler
) {
328 ClobberExistingNativeHandler(name
);
329 native_handler_map_
[name
] =
330 linked_ptr
<NativeHandler
>(native_handler
.release());
333 void ModuleSystem::OverrideNativeHandlerForTest(const std::string
& name
) {
334 ClobberExistingNativeHandler(name
);
335 overridden_native_handlers_
.insert(name
);
338 void ModuleSystem::RunString(const std::string
& code
, const std::string
& name
) {
339 v8::HandleScope
handle_scope(GetIsolate());
340 v8::Local
<v8::String
> v8_code
;
341 v8::Local
<v8::String
> v8_name
;
342 if (!ToV8String(GetIsolate(), code
.c_str(), &v8_code
) ||
343 !ToV8String(GetIsolate(), name
.c_str(), &v8_name
)) {
344 Warn(GetIsolate(), "Too long code or name.");
347 RunString(v8_code
, v8_name
);
351 void ModuleSystem::NativeLazyFieldGetter(
352 v8::Local
<v8::Name
> property
,
353 const v8::PropertyCallbackInfo
<v8::Value
>& info
) {
354 LazyFieldGetterInner(property
.As
<v8::String
>(), info
,
355 &ModuleSystem::RequireNativeFromString
);
359 void ModuleSystem::LazyFieldGetter(
360 v8::Local
<v8::Name
> property
,
361 const v8::PropertyCallbackInfo
<v8::Value
>& info
) {
362 LazyFieldGetterInner(property
.As
<v8::String
>(), info
, &ModuleSystem::Require
);
366 void ModuleSystem::LazyFieldGetterInner(
367 v8::Local
<v8::String
> property
,
368 const v8::PropertyCallbackInfo
<v8::Value
>& info
,
369 RequireFunction require_function
) {
370 CHECK(!info
.Data().IsEmpty());
371 CHECK(info
.Data()->IsObject());
372 v8::HandleScope
handle_scope(info
.GetIsolate());
373 v8::Local
<v8::Object
> parameters
= v8::Local
<v8::Object
>::Cast(info
.Data());
374 // This context should be the same as context()->v8_context().
375 v8::Local
<v8::Context
> context
= parameters
->CreationContext();
376 v8::Local
<v8::Object
> global(context
->Global());
377 v8::Local
<v8::Value
> module_system_value
= global
->GetHiddenValue(
378 ToV8StringUnsafe(info
.GetIsolate(), kModuleSystem
));
379 if (module_system_value
.IsEmpty() || !module_system_value
->IsExternal()) {
380 // ModuleSystem has been deleted.
381 // TODO(kalman): See comment in header file.
382 Warn(info
.GetIsolate(),
383 "Module system has been deleted, does extension view exist?");
387 ModuleSystem
* module_system
= static_cast<ModuleSystem
*>(
388 v8::Local
<v8::External
>::Cast(module_system_value
)->Value());
390 v8::Local
<v8::Value
> v8_module_name
;
391 if (!GetProperty(context
, parameters
, kModuleName
, &v8_module_name
)) {
392 Warn(info
.GetIsolate(), "Cannot find module.");
395 std::string name
= *v8::String::Utf8Value(v8_module_name
);
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(info
.GetIsolate());
403 v8::Local
<v8::Value
> module_value
;
404 if (!(module_system
->*require_function
)(name
).ToLocal(&module_value
)) {
405 module_system
->HandleException(try_catch
);
409 v8::Local
<v8::Object
> module
= v8::Local
<v8::Object
>::Cast(module_value
);
410 v8::Local
<v8::Value
> field_value
;
411 if (!GetProperty(context
, parameters
, kModuleField
, &field_value
)) {
412 module_system
->HandleException(try_catch
);
415 v8::Local
<v8::String
> field
;
416 if (!field_value
->ToString(context
).ToLocal(&field
)) {
417 module_system
->HandleException(try_catch
);
421 if (!IsTrue(module
->Has(context
, field
))) {
422 std::string field_str
= *v8::String::Utf8Value(field
);
423 Fatal(module_system
->context_
,
424 "Lazy require of " + name
+ "." + field_str
+ " did not set the " +
425 field_str
+ " field");
429 v8::Local
<v8::Value
> new_field
;
430 if (!GetProperty(context
, module
, field
, &new_field
)) {
431 module_system
->HandleException(try_catch
);
435 // Ok for it to be undefined, among other things it's how bindings signify
436 // that the extension doesn't have permission to use them.
437 CHECK(!new_field
.IsEmpty());
439 // Delete the getter and set this field to |new_field| so the same object is
440 // returned every time a certain API is accessed.
441 v8::Local
<v8::Value
> val
= info
.This();
442 if (val
->IsObject()) {
443 v8::Local
<v8::Object
> object
= v8::Local
<v8::Object
>::Cast(val
);
444 object
->Delete(context
, property
);
445 SetProperty(context
, object
, property
, new_field
);
449 info
.GetReturnValue().Set(new_field
);
452 void ModuleSystem::SetLazyField(v8::Local
<v8::Object
> object
,
453 const std::string
& field
,
454 const std::string
& module_name
,
455 const std::string
& module_field
) {
457 object
, field
, module_name
, module_field
, &ModuleSystem::LazyFieldGetter
);
460 void ModuleSystem::SetLazyField(v8::Local
<v8::Object
> object
,
461 const std::string
& field
,
462 const std::string
& module_name
,
463 const std::string
& module_field
,
464 v8::AccessorNameGetterCallback getter
) {
465 CHECK(field
.size() < v8::String::kMaxLength
);
466 CHECK(module_name
.size() < v8::String::kMaxLength
);
467 CHECK(module_field
.size() < v8::String::kMaxLength
);
468 v8::HandleScope
handle_scope(GetIsolate());
469 v8::Local
<v8::Object
> parameters
= v8::Object::New(GetIsolate());
470 v8::Local
<v8::Context
> context
= context_
->v8_context();
471 SetProperty(context
, parameters
, kModuleName
,
472 ToV8StringUnsafe(GetIsolate(), module_name
.c_str()));
473 SetProperty(context
, parameters
, kModuleField
,
474 ToV8StringUnsafe(GetIsolate(), module_field
.c_str()));
475 auto maybe
= object
->SetAccessor(
476 context
, ToV8StringUnsafe(GetIsolate(), field
.c_str()), getter
, NULL
,
478 CHECK(IsTrue(maybe
));
481 void ModuleSystem::SetNativeLazyField(v8::Local
<v8::Object
> object
,
482 const std::string
& field
,
483 const std::string
& module_name
,
484 const std::string
& module_field
) {
489 &ModuleSystem::NativeLazyFieldGetter
);
492 v8::Local
<v8::Value
> ModuleSystem::RunString(v8::Local
<v8::String
> code
,
493 v8::Local
<v8::String
> name
) {
494 v8::EscapableHandleScope
handle_scope(GetIsolate());
495 v8::Local
<v8::Context
> v8_context
= context()->v8_context();
496 v8::Context::Scope
context_scope(v8_context
);
498 // Prepend extensions:: to |name| so that internal code can be differentiated
499 // from external code in stack traces. This has no effect on behaviour.
500 std::string internal_name
=
501 base::StringPrintf("extensions::%s", *v8::String::Utf8Value(name
));
503 if (internal_name
.size() >= v8::String::kMaxLength
) {
504 NOTREACHED() << "internal_name is too long.";
505 return v8::Undefined(GetIsolate());
508 blink::WebScopedMicrotaskSuppression suppression
;
509 v8::TryCatch
try_catch(GetIsolate());
510 try_catch
.SetCaptureMessage(true);
511 v8::ScriptOrigin
origin(
512 ToV8StringUnsafe(GetIsolate(), internal_name
.c_str()));
513 v8::Local
<v8::Script
> script
;
514 if (!v8::Script::Compile(v8_context
, code
, &origin
).ToLocal(&script
)) {
515 HandleException(try_catch
);
516 return v8::Undefined(GetIsolate());
519 v8::Local
<v8::Value
> result
;
520 if (!script
->Run(v8_context
).ToLocal(&result
)) {
521 HandleException(try_catch
);
522 return v8::Undefined(GetIsolate());
525 return handle_scope
.Escape(result
);
528 v8::Local
<v8::Value
> ModuleSystem::GetSource(const std::string
& module_name
) {
529 v8::EscapableHandleScope
handle_scope(GetIsolate());
530 if (!source_map_
->Contains(module_name
))
531 return v8::Undefined(GetIsolate());
532 return handle_scope
.Escape(
533 v8::Local
<v8::Value
>(source_map_
->GetSource(GetIsolate(), module_name
)));
536 void ModuleSystem::RequireNative(
537 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
538 CHECK_EQ(1, args
.Length());
539 std::string native_name
= *v8::String::Utf8Value(args
[0]);
540 v8::Local
<v8::Object
> object
;
541 if (RequireNativeFromString(native_name
).ToLocal(&object
))
542 args
.GetReturnValue().Set(object
);
545 v8::MaybeLocal
<v8::Object
> ModuleSystem::RequireNativeFromString(
546 const std::string
& native_name
) {
547 if (natives_enabled_
== 0) {
548 // HACK: if in test throw exception so that we can test the natives-disabled
549 // logic; however, under normal circumstances, this is programmer error so
551 if (exception_handler_
) {
552 GetIsolate()->ThrowException(
553 ToV8StringUnsafe(GetIsolate(), "Natives disabled"));
554 return v8::MaybeLocal
<v8::Object
>();
556 Fatal(context_
, "Natives disabled for requireNative(" + native_name
+ ")");
557 return v8::MaybeLocal
<v8::Object
>();
560 if (overridden_native_handlers_
.count(native_name
) > 0u) {
561 v8::Local
<v8::Value
> value
= RequireForJsInner(
562 ToV8StringUnsafe(GetIsolate(), native_name
.c_str()));
563 if (value
.IsEmpty() || !value
->IsObject())
564 return v8::MaybeLocal
<v8::Object
>();
565 return value
.As
<v8::Object
>();
568 NativeHandlerMap::iterator i
= native_handler_map_
.find(native_name
);
569 if (i
== native_handler_map_
.end()) {
571 "Couldn't find native for requireNative(" + native_name
+ ")");
572 return v8::MaybeLocal
<v8::Object
>();
574 return i
->second
->NewInstance();
577 void ModuleSystem::RequireAsync(
578 const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
579 CHECK_EQ(1, args
.Length());
580 std::string module_name
= *v8::String::Utf8Value(args
[0]);
581 v8::Local
<v8::Context
> v8_context
= context_
->v8_context();
582 v8::Local
<v8::Promise::Resolver
> resolver(
583 v8::Promise::Resolver::New(v8_context
).ToLocalChecked());
584 args
.GetReturnValue().Set(resolver
->GetPromise());
585 scoped_ptr
<v8::Global
<v8::Promise::Resolver
>> global_resolver(
586 new v8::Global
<v8::Promise::Resolver
>(GetIsolate(), resolver
));
587 gin::ModuleRegistry
* module_registry
=
588 gin::ModuleRegistry::From(v8_context
);
589 if (!module_registry
) {
590 Warn(GetIsolate(), "Extension view no longer exists");
591 resolver
->Reject(v8_context
, v8::Exception::Error(ToV8StringUnsafe(
592 GetIsolate(), "Extension view no longer exists")));
595 module_registry
->LoadModule(
596 GetIsolate(), module_name
,
597 base::Bind(&ModuleSystem::OnModuleLoaded
, weak_factory_
.GetWeakPtr(),
598 base::Passed(&global_resolver
)));
599 if (module_registry
->available_modules().count(module_name
) == 0)
600 LoadModule(module_name
);
603 v8::Local
<v8::String
> ModuleSystem::WrapSource(v8::Local
<v8::String
> source
) {
604 v8::EscapableHandleScope
handle_scope(GetIsolate());
605 // Keep in order with the arguments in RequireForJsInner.
606 v8::Local
<v8::String
> left
= ToV8StringUnsafe(
608 "(function(define, require, requireNative, requireAsync, exports, "
610 "$Array, $Function, $JSON, $Object, $RegExp, $String, $Error) {"
612 v8::Local
<v8::String
> right
= ToV8StringUnsafe(GetIsolate(), "\n})");
613 return handle_scope
.Escape(v8::Local
<v8::String
>(
614 v8::String::Concat(left
, v8::String::Concat(source
, right
))));
617 void ModuleSystem::Private(const v8::FunctionCallbackInfo
<v8::Value
>& args
) {
618 CHECK_EQ(1, args
.Length());
619 if (!args
[0]->IsObject() || args
[0]->IsNull()) {
620 GetIsolate()->ThrowException(
621 v8::Exception::TypeError(ToV8StringUnsafe(GetIsolate(),
622 args
[0]->IsUndefined()
623 ? "Method called without a valid receiver (this). "
624 "Did you forget to call .bind()?"
625 : "Invalid invocation: receiver is not an object!")));
628 v8::Local
<v8::Object
> obj
= args
[0].As
<v8::Object
>();
629 v8::Local
<v8::String
> privates_key
=
630 ToV8StringUnsafe(GetIsolate(), "privates");
631 v8::Local
<v8::Value
> privates
= obj
->GetHiddenValue(privates_key
);
632 if (privates
.IsEmpty()) {
633 privates
= v8::Object::New(args
.GetIsolate());
634 if (privates
.IsEmpty()) {
635 GetIsolate()->ThrowException(
636 ToV8StringUnsafe(GetIsolate(), "Failed to create privates"));
639 obj
->SetHiddenValue(privates_key
, privates
);
641 args
.GetReturnValue().Set(privates
);
644 v8::Local
<v8::Value
> ModuleSystem::LoadModule(const std::string
& module_name
) {
645 v8::EscapableHandleScope
handle_scope(GetIsolate());
646 v8::Local
<v8::Context
> v8_context
= context()->v8_context();
647 v8::Context::Scope
context_scope(v8_context
);
649 v8::Local
<v8::Value
> source(GetSource(module_name
));
650 if (source
.IsEmpty() || source
->IsUndefined()) {
651 Fatal(context_
, "No source for require(" + module_name
+ ")");
652 return v8::Undefined(GetIsolate());
654 v8::Local
<v8::String
> wrapped_source(
655 WrapSource(v8::Local
<v8::String
>::Cast(source
)));
656 v8::Local
<v8::String
> v8_module_name
;
657 if (!ToV8String(GetIsolate(), module_name
.c_str(), &v8_module_name
)) {
658 NOTREACHED() << "module_name is too long";
659 return v8::Undefined(GetIsolate());
661 // Modules are wrapped in (function(){...}) so they always return functions.
662 v8::Local
<v8::Value
> func_as_value
=
663 RunString(wrapped_source
, v8_module_name
);
664 if (func_as_value
.IsEmpty() || func_as_value
->IsUndefined()) {
665 Fatal(context_
, "Bad source for require(" + module_name
+ ")");
666 return v8::Undefined(GetIsolate());
669 v8::Local
<v8::Function
> func
= v8::Local
<v8::Function
>::Cast(func_as_value
);
671 v8::Local
<v8::Object
> define_object
= v8::Object::New(GetIsolate());
672 gin::ModuleRegistry::InstallGlobals(GetIsolate(), define_object
);
674 v8::Local
<v8::Value
> exports
= v8::Object::New(GetIsolate());
675 v8::Local
<v8::Object
> natives(NewInstance());
676 CHECK(!natives
.IsEmpty()); // this can fail if v8 has issues
678 // These must match the argument order in WrapSource.
679 v8::Local
<v8::Value
> args
[] = {
681 GetPropertyUnsafe(v8_context
, define_object
, "define"),
683 GetPropertyUnsafe(v8_context
, natives
, "require",
684 v8::NewStringType::kInternalized
),
685 GetPropertyUnsafe(v8_context
, natives
, "requireNative",
686 v8::NewStringType::kInternalized
),
687 GetPropertyUnsafe(v8_context
, natives
, "requireAsync",
688 v8::NewStringType::kInternalized
),
690 // Libraries that we magically expose to every module.
691 console::AsV8Object(GetIsolate()),
692 GetPropertyUnsafe(v8_context
, natives
, "privates",
693 v8::NewStringType::kInternalized
),
694 // Each safe builtin. Keep in order with the arguments in WrapSource.
695 context_
->safe_builtins()->GetArray(),
696 context_
->safe_builtins()->GetFunction(),
697 context_
->safe_builtins()->GetJSON(),
698 context_
->safe_builtins()->GetObjekt(),
699 context_
->safe_builtins()->GetRegExp(),
700 context_
->safe_builtins()->GetString(),
701 context_
->safe_builtins()->GetError(),
704 v8::TryCatch
try_catch(GetIsolate());
705 try_catch
.SetCaptureMessage(true);
706 context_
->CallFunction(func
, arraysize(args
), args
);
707 if (try_catch
.HasCaught()) {
708 HandleException(try_catch
);
709 return v8::Undefined(GetIsolate());
712 return handle_scope
.Escape(exports
);
715 void ModuleSystem::OnDidAddPendingModule(
716 const std::string
& id
,
717 const std::vector
<std::string
>& dependencies
) {
718 bool module_system_managed
= source_map_
->Contains(id
);
720 gin::ModuleRegistry
* registry
=
721 gin::ModuleRegistry::From(context_
->v8_context());
723 for (const auto& dependency
: dependencies
) {
724 // If a dependency is not available, and either the module or this
725 // dependency is managed by ModuleSystem, attempt to load it. Other
726 // gin::ModuleRegistry users (WebUI and users of the mojoPrivate API) are
727 // responsible for loading their module dependencies when required.
728 if (registry
->available_modules().count(dependency
) == 0 &&
729 (module_system_managed
|| source_map_
->Contains(dependency
))) {
730 LoadModule(dependency
);
733 registry
->AttemptToLoadMoreModules(GetIsolate());
736 void ModuleSystem::OnModuleLoaded(
737 scoped_ptr
<v8::Global
<v8::Promise::Resolver
>> resolver
,
738 v8::Local
<v8::Value
> value
) {
741 v8::HandleScope
handle_scope(GetIsolate());
742 v8::Local
<v8::Promise::Resolver
> resolver_local(
743 v8::Local
<v8::Promise::Resolver
>::New(GetIsolate(), *resolver
));
744 resolver_local
->Resolve(context()->v8_context(), value
);
747 void ModuleSystem::ClobberExistingNativeHandler(const std::string
& name
) {
748 NativeHandlerMap::iterator existing_handler
= native_handler_map_
.find(name
);
749 if (existing_handler
!= native_handler_map_
.end()) {
750 clobbered_native_handlers_
.push_back(existing_handler
->second
);
751 native_handler_map_
.erase(existing_handler
);
755 } // namespace extensions