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.
4 
5 #include "extensions/renderer/module_system.h"
6 
7 #include "base/bind.h"
8 #include "base/command_line.h"
9 #include "base/logging.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/stl_util.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/stringprintf.h"
14 #include "base/timer/elapsed_timer.h"
15 #include "base/trace_event/trace_event.h"
16 #include "content/public/renderer/render_frame.h"
17 #include "content/public/renderer/render_view.h"
18 #include "extensions/common/extension.h"
19 #include "extensions/renderer/console.h"
20 #include "extensions/renderer/safe_builtins.h"
21 #include "extensions/renderer/script_context.h"
22 #include "extensions/renderer/script_context_set.h"
23 #include "extensions/renderer/source_map.h"
24 #include "extensions/renderer/v8_helpers.h"
25 #include "gin/converter.h"
26 #include "third_party/blink/public/web/web_context_features.h"
27 #include "third_party/blink/public/web/web_frame.h"
28 
29 namespace extensions {
30 
31 using v8_helpers::GetPrivateProperty;
32 using v8_helpers::SetPrivateProperty;
33 using v8_helpers::ToV8String;
34 using v8_helpers::ToV8StringUnsafe;
35 
36 namespace {
37 
38 const char kModuleSystem[] = "module_system";
39 const char kModuleName[] = "module_name";
40 const char kModuleField[] = "module_field";
41 const char kModulesField[] = "modules";
42 
43 // Determines if certain fatal extensions errors should be suppressed
44 // (i.e., only logged) or allowed (i.e., logged before crashing).
ShouldSuppressFatalErrors()45 bool ShouldSuppressFatalErrors() {
46   // Suppress fatal everywhere until the cause of bugs like http://crbug/471599
47   // are fixed. This would typically be:
48   // return GetCurrentChannel() > version_info::Channel::DEV;
49   return true;
50 }
51 
52 // Logs an error for the calling context in preparation for potentially
53 // crashing the renderer, with some added metadata about the context:
54 //  - Its type (blessed, unblessed, etc).
55 //  - Whether it's valid.
56 //  - The extension ID, if one exists.
57 // Crashing won't happen in stable/beta releases, but is encouraged to happen
58 // in the less stable released to catch errors early.
Fatal(ScriptContext * context,const std::string & message)59 void Fatal(ScriptContext* context, const std::string& message) {
60   // Prepend some context metadata.
61   std::string full_message = "(";
62   if (!context->is_valid())
63     full_message += "Invalid ";
64   full_message += context->GetContextTypeDescription();
65   full_message += " context";
66   if (context->extension()) {
67     full_message += " for ";
68     full_message += context->extension()->id();
69   }
70   full_message += ") ";
71   full_message += message;
72 
73   if (ShouldSuppressFatalErrors()) {
74     console::AddMessage(context, blink::mojom::ConsoleMessageLevel::kError,
75                         full_message);
76   } else {
77     console::Fatal(context, full_message);
78   }
79 }
80 
Warn(v8::Isolate * isolate,const std::string & message)81 void Warn(v8::Isolate* isolate, const std::string& message) {
82   ScriptContext* script_context =
83       ScriptContextSet::GetContextByV8Context(isolate->GetCurrentContext());
84   console::AddMessage(script_context,
85                       blink::mojom::ConsoleMessageLevel::kWarning, message);
86 }
87 
88 // Default exception handler which logs the exception.
89 class DefaultExceptionHandler : public ModuleSystem::ExceptionHandler {
90  public:
DefaultExceptionHandler(ScriptContext * context)91   explicit DefaultExceptionHandler(ScriptContext* context)
92       : ModuleSystem::ExceptionHandler(context) {}
93 
94   // Fatally dumps the debug info from |try_catch| to the console.
95   // Make sure this is never used for exceptions that originate in external
96   // code!
HandleUncaughtException(const v8::TryCatch & try_catch)97   void HandleUncaughtException(const v8::TryCatch& try_catch) override {
98     v8::HandleScope handle_scope(context_->isolate());
99     std::string stack_trace = "<stack trace unavailable>";
100     v8::Local<v8::Value> v8_stack_trace;
101     if (try_catch.StackTrace(context_->v8_context()).ToLocal(&v8_stack_trace)) {
102       v8::String::Utf8Value stack_value(context_->isolate(), v8_stack_trace);
103       if (*stack_value)
104         stack_trace.assign(*stack_value, stack_value.length());
105       else
106         stack_trace = "<could not convert stack trace to string>";
107     }
108     Fatal(context_, CreateExceptionString(try_catch) + "{" + stack_trace + "}");
109   }
110 };
111 
112 // Sets a property on the "exports" object for bindings. Called by JS with
113 // exports.$set(<key>, <value>).
SetExportsProperty(const v8::FunctionCallbackInfo<v8::Value> & args)114 void SetExportsProperty(
115     const v8::FunctionCallbackInfo<v8::Value>& args) {
116   v8::Local<v8::Object> obj = args.This();
117   CHECK_EQ(2, args.Length());
118   CHECK(args[0]->IsString());
119   v8::Maybe<bool> result =
120       obj->DefineOwnProperty(args.GetIsolate()->GetCurrentContext(),
121                              args[0].As<v8::String>(), args[1], v8::ReadOnly);
122   if (!result.FromMaybe(false))
123     LOG(ERROR) << "Failed to set private property on the export.";
124 }
125 
ContextNeedsMojoBindings(ScriptContext * context)126 bool ContextNeedsMojoBindings(ScriptContext* context) {
127   // Mojo is only used from JS by some APIs so a context only needs the mojo
128   // bindings if at least one is available.
129   //
130   // Prefer to use Mojo from C++ if possible rather than adding to this list.
131   static const char* const kApisRequiringMojo[] = {
132       "mediaPerceptionPrivate", "mimeHandlerPrivate", "mojoPrivate",
133   };
134 
135   for (const auto* api : kApisRequiringMojo) {
136     if (context->GetAvailability(api).is_available())
137       return true;
138   }
139   return false;
140 }
141 
142 }  // namespace
143 
CreateExceptionString(const v8::TryCatch & try_catch)144 std::string ModuleSystem::ExceptionHandler::CreateExceptionString(
145     const v8::TryCatch& try_catch) {
146   v8::Local<v8::Message> message(try_catch.Message());
147   if (message.IsEmpty()) {
148     return "try_catch has no message";
149   }
150 
151   std::string resource_name = "<unknown resource>";
152   if (!message->GetScriptOrigin().ResourceName().IsEmpty()) {
153     v8::String::Utf8Value resource_name_v8(
154         context_->isolate(), message->GetScriptOrigin().ResourceName());
155     resource_name.assign(*resource_name_v8, resource_name_v8.length());
156   }
157 
158   std::string error_message = "<no error message>";
159   if (!message->Get().IsEmpty()) {
160     v8::String::Utf8Value error_message_v8(context_->isolate(), message->Get());
161     error_message.assign(*error_message_v8, error_message_v8.length());
162   }
163 
164   int line_number = 0;
165   if (context_) {  // |context_| can be null in unittests.
166     auto maybe = message->GetLineNumber(context_->v8_context());
167     line_number = maybe.IsJust() ? maybe.FromJust() : 0;
168   }
169   return base::StringPrintf("%s:%d: %s",
170                             resource_name.c_str(),
171                             line_number,
172                             error_message.c_str());
173 }
174 
ModuleSystem(ScriptContext * context,const SourceMap * source_map)175 ModuleSystem::ModuleSystem(ScriptContext* context, const SourceMap* source_map)
176     : ObjectBackedNativeHandler(context),
177       context_(context),
178       source_map_(source_map),
179       natives_enabled_(0),
180       exception_handler_(new DefaultExceptionHandler(context)) {
181   v8::Local<v8::Object> global(context->v8_context()->Global());
182   v8::Isolate* isolate = context->isolate();
183   SetPrivate(global, kModulesField, v8::Object::New(isolate));
184   SetPrivate(global, kModuleSystem, v8::External::New(isolate, this));
185 
186   if (context_->GetRenderFrame() &&
187       context_->context_type() == Feature::BLESSED_EXTENSION_CONTEXT &&
188       !context_->IsForServiceWorker() && ContextNeedsMojoBindings(context_)) {
189     blink::WebContextFeatures::EnableMojoJS(context->v8_context(), true);
190   }
191 }
192 
~ModuleSystem()193 ModuleSystem::~ModuleSystem() {
194 }
195 
AddRoutes()196 void ModuleSystem::AddRoutes() {
197   RouteHandlerFunction(
198       "require",
199       base::BindRepeating(&ModuleSystem::RequireForJs, base::Unretained(this)));
200   RouteHandlerFunction("requireNative",
201                        base::BindRepeating(&ModuleSystem::RequireNative,
202                                            base::Unretained(this)));
203   RouteHandlerFunction(
204       "loadScript",
205       base::BindRepeating(&ModuleSystem::LoadScript, base::Unretained(this)));
206   RouteHandlerFunction("privates", base::BindRepeating(&ModuleSystem::Private,
207                                                        base::Unretained(this)));
208 }
209 
Invalidate()210 void ModuleSystem::Invalidate() {
211   // Clear the module system properties from the global context. It's polite,
212   // and we use this as a signal in lazy handlers that we no longer exist.
213   {
214     v8::HandleScope scope(GetIsolate());
215     v8::Local<v8::Object> global = context()->v8_context()->Global();
216     DeletePrivate(global, kModulesField);
217     DeletePrivate(global, kModuleSystem);
218   }
219 
220   // Invalidate all active and clobbered NativeHandlers we own.
221   for (const auto& handler : native_handler_map_)
222     handler.second->Invalidate();
223   for (const auto& clobbered_handler : clobbered_native_handlers_)
224     clobbered_handler->Invalidate();
225 
226   ObjectBackedNativeHandler::Invalidate();
227 }
228 
NativesEnabledScope(ModuleSystem * module_system)229 ModuleSystem::NativesEnabledScope::NativesEnabledScope(
230     ModuleSystem* module_system)
231     : module_system_(module_system) {
232   module_system_->natives_enabled_++;
233 }
234 
~NativesEnabledScope()235 ModuleSystem::NativesEnabledScope::~NativesEnabledScope() {
236   module_system_->natives_enabled_--;
237   CHECK_GE(module_system_->natives_enabled_, 0);
238 }
239 
HandleException(const v8::TryCatch & try_catch)240 void ModuleSystem::HandleException(const v8::TryCatch& try_catch) {
241   exception_handler_->HandleUncaughtException(try_catch);
242 }
243 
Require(const std::string & module_name)244 v8::MaybeLocal<v8::Object> ModuleSystem::Require(
245     const std::string& module_name) {
246   v8::Local<v8::String> v8_module_name;
247   if (!ToV8String(GetIsolate(), module_name, &v8_module_name))
248     return v8::MaybeLocal<v8::Object>();
249   v8::EscapableHandleScope handle_scope(GetIsolate());
250   v8::Local<v8::Value> value =
251       RequireForJsInner(v8_module_name, true /* create */);
252   if (value.IsEmpty() || !value->IsObject())
253     return v8::MaybeLocal<v8::Object>();
254   return handle_scope.Escape(value.As<v8::Object>());
255 }
256 
RequireForJs(const v8::FunctionCallbackInfo<v8::Value> & args)257 void ModuleSystem::RequireForJs(
258     const v8::FunctionCallbackInfo<v8::Value>& args) {
259   if (!args[0]->IsString()) {
260     NOTREACHED() << "require() called with a non-string argument";
261     return;
262   }
263   v8::Local<v8::String> module_name = args[0].As<v8::String>();
264   args.GetReturnValue().Set(RequireForJsInner(module_name, true /* create */));
265 }
266 
RequireForJsInner(v8::Local<v8::String> module_name,bool create)267 v8::Local<v8::Value> ModuleSystem::RequireForJsInner(
268     v8::Local<v8::String> module_name,
269     bool create) {
270   v8::EscapableHandleScope handle_scope(GetIsolate());
271   v8::Local<v8::Context> v8_context = context()->v8_context();
272   v8::Context::Scope context_scope(v8_context);
273 
274   v8::Local<v8::Object> global(context()->v8_context()->Global());
275 
276   // The module system might have been deleted. This can happen if a different
277   // context keeps a reference to us, but our frame is destroyed (e.g.
278   // background page keeps reference to chrome object in a closed popup).
279   v8::Local<v8::Value> modules_value;
280   if (!GetPrivate(global, kModulesField, &modules_value) ||
281       modules_value->IsUndefined()) {
282     Warn(GetIsolate(), "Extension view no longer exists");
283     return v8::Undefined(GetIsolate());
284   }
285 
286   v8::Local<v8::Object> modules(v8::Local<v8::Object>::Cast(modules_value));
287   v8::Local<v8::Value> exports;
288   if (!GetPrivateProperty(v8_context, modules, module_name, &exports) ||
289       !exports->IsUndefined())
290     return handle_scope.Escape(exports);
291 
292   if (!create)
293     return v8::Undefined(GetIsolate());
294 
295   exports = LoadModule(*v8::String::Utf8Value(GetIsolate(), module_name));
296   SetPrivateProperty(v8_context, modules, module_name, exports);
297   return handle_scope.Escape(exports);
298 }
299 
CallModuleMethodSafe(const std::string & module_name,const std::string & method_name)300 void ModuleSystem::CallModuleMethodSafe(const std::string& module_name,
301                                         const std::string& method_name) {
302   v8::HandleScope handle_scope(GetIsolate());
303   v8::Local<v8::Value> no_args;
304   CallModuleMethodSafe(module_name, method_name, 0, &no_args,
305                        ScriptInjectionCallback::CompleteCallback());
306 }
307 
CallModuleMethodSafe(const std::string & module_name,const std::string & method_name,std::vector<v8::Local<v8::Value>> * args)308 void ModuleSystem::CallModuleMethodSafe(
309     const std::string& module_name,
310     const std::string& method_name,
311     std::vector<v8::Local<v8::Value>>* args) {
312   CallModuleMethodSafe(module_name, method_name, args->size(), args->data(),
313                        ScriptInjectionCallback::CompleteCallback());
314 }
315 
CallModuleMethodSafe(const std::string & module_name,const std::string & method_name,int argc,v8::Local<v8::Value> argv[])316 void ModuleSystem::CallModuleMethodSafe(const std::string& module_name,
317                                         const std::string& method_name,
318                                         int argc,
319                                         v8::Local<v8::Value> argv[]) {
320   CallModuleMethodSafe(module_name, method_name, argc, argv,
321                        ScriptInjectionCallback::CompleteCallback());
322 }
323 
CallModuleMethodSafe(const std::string & module_name,const std::string & method_name,int argc,v8::Local<v8::Value> argv[],const ScriptInjectionCallback::CompleteCallback & callback)324 void ModuleSystem::CallModuleMethodSafe(
325     const std::string& module_name,
326     const std::string& method_name,
327     int argc,
328     v8::Local<v8::Value> argv[],
329     const ScriptInjectionCallback::CompleteCallback& callback) {
330   TRACE_EVENT2("v8", "v8.callModuleMethodSafe", "module_name", module_name,
331                "method_name", method_name);
332 
333   v8::HandleScope handle_scope(GetIsolate());
334   v8::Local<v8::Context> v8_context = context()->v8_context();
335   v8::Context::Scope context_scope(v8_context);
336 
337   v8::Local<v8::Function> function =
338       GetModuleFunction(module_name, method_name);
339   if (function.IsEmpty()) {
340     // This can legitimately happen when the module hasn't been loaded in the
341     // context (since GetModuleFunction() does not load an unloaded module).
342     // Typically, we won't do this, but we can in the case of, e.g., dispatching
343     // events (where we'll try to dispatch to each context in a process). In
344     // these cases, though, we can know that there are no listeners registered,
345     // since the event module hasn't been loaded.
346     return;
347   }
348 
349   {
350     v8::TryCatch try_catch(GetIsolate());
351     try_catch.SetCaptureMessage(true);
352     context_->SafeCallFunction(function, argc, argv, callback);
353     if (try_catch.HasCaught())
354       HandleException(try_catch);
355   }
356 }
357 
RegisterNativeHandler(const std::string & name,std::unique_ptr<NativeHandler> native_handler)358 void ModuleSystem::RegisterNativeHandler(
359     const std::string& name,
360     std::unique_ptr<NativeHandler> native_handler) {
361   ClobberExistingNativeHandler(name);
362   native_handler_map_[name] = std::move(native_handler);
363 }
364 
OverrideNativeHandlerForTest(const std::string & name)365 void ModuleSystem::OverrideNativeHandlerForTest(const std::string& name) {
366   ClobberExistingNativeHandler(name);
367   overridden_native_handlers_.insert(name);
368 }
369 
370 // static
NativeLazyFieldGetter(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Value> & info)371 void ModuleSystem::NativeLazyFieldGetter(
372     v8::Local<v8::Name> property,
373     const v8::PropertyCallbackInfo<v8::Value>& info) {
374   LazyFieldGetterInner(property.As<v8::String>(), info,
375                        &ModuleSystem::RequireNativeFromString);
376 }
377 
378 // static
LazyFieldGetter(v8::Local<v8::Name> property,const v8::PropertyCallbackInfo<v8::Value> & info)379 void ModuleSystem::LazyFieldGetter(
380     v8::Local<v8::Name> property,
381     const v8::PropertyCallbackInfo<v8::Value>& info) {
382   LazyFieldGetterInner(property.As<v8::String>(), info, &ModuleSystem::Require);
383 }
384 
385 // static
LazyFieldGetterInner(v8::Local<v8::String> property,const v8::PropertyCallbackInfo<v8::Value> & info,RequireFunction require_function)386 void ModuleSystem::LazyFieldGetterInner(
387     v8::Local<v8::String> property,
388     const v8::PropertyCallbackInfo<v8::Value>& info,
389     RequireFunction require_function) {
390   base::ElapsedTimer timer;
391   CHECK(!info.Data().IsEmpty());
392   CHECK(info.Data()->IsObject());
393   v8::Isolate* isolate = info.GetIsolate();
394   v8::HandleScope handle_scope(isolate);
395   v8::Local<v8::Object> parameters = v8::Local<v8::Object>::Cast(info.Data());
396   // This context should be the same as context()->v8_context().
397   v8::Local<v8::Context> context = parameters->CreationContext();
398   v8::Local<v8::Object> global(context->Global());
399   v8::Local<v8::Value> module_system_value;
400   if (!GetPrivate(context, global, kModuleSystem, &module_system_value) ||
401       !module_system_value->IsExternal()) {
402     // ModuleSystem has been deleted.
403     // TODO(kalman): See comment in header file.
404     Warn(isolate,
405          "Module system has been deleted, does extension view exist?");
406     return;
407   }
408 
409   ModuleSystem* module_system = static_cast<ModuleSystem*>(
410       v8::Local<v8::External>::Cast(module_system_value)->Value());
411 
412   v8::Local<v8::Value> v8_module_name;
413   if (!GetPrivateProperty(context, parameters, kModuleName, &v8_module_name)) {
414     Warn(isolate, "Cannot find module.");
415     return;
416   }
417   std::string name = *v8::String::Utf8Value(isolate, v8_module_name);
418 
419   // As part of instantiating a module, we delete the getter and replace it with
420   // the property directly. If we're trying to load the same module a second
421   // time, it means something went wrong. Bail out early rather than going
422   // through the initialization process again (since bindings may not expect to
423   // run multiple times).
424   if (!module_system->loaded_modules_.insert(name).second) {
425     Warn(isolate, "Previous API instantiation failed.");
426     return;
427   }
428 
429   // Switch to our v8 context because we need functions created while running
430   // the require()d module to belong to our context, not the current one.
431   v8::Context::Scope context_scope(context);
432   NativesEnabledScope natives_enabled_scope(module_system);
433 
434   v8::TryCatch try_catch(isolate);
435   v8::Local<v8::Value> module_value;
436   if (!(module_system->*require_function)(name).ToLocal(&module_value)) {
437     module_system->HandleException(try_catch);
438     return;
439   }
440 
441   v8::Local<v8::Object> module = v8::Local<v8::Object>::Cast(module_value);
442   v8::Local<v8::Value> field_value;
443   if (!GetPrivateProperty(context, parameters, kModuleField, &field_value)) {
444     module_system->HandleException(try_catch);
445     return;
446   }
447   v8::Local<v8::String> field;
448   if (!field_value->ToString(context).ToLocal(&field)) {
449     module_system->HandleException(try_catch);
450     return;
451   }
452 
453   if (!v8_helpers::IsTrue(module->Has(context, field))) {
454     std::string field_str = *v8::String::Utf8Value(isolate, field);
455     Fatal(module_system->context_,
456           "Lazy require of " + name + "." + field_str + " did not set the " +
457               field_str + " field");
458     return;
459   }
460 
461   v8::Local<v8::Value> new_field;
462   if (!v8_helpers::GetProperty(context, module, field, &new_field)) {
463     module_system->HandleException(try_catch);
464     return;
465   }
466 
467   // Ok for it to be undefined, among other things it's how bindings signify
468   // that the extension doesn't have permission to use them.
469   CHECK(!new_field.IsEmpty());
470 
471   // Delete the getter and set this field to |new_field| so the same object is
472   // returned every time a certain API is accessed.
473   v8::Local<v8::Value> val = info.This();
474   if (val->IsObject()) {
475     v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(val);
476     auto maybe_deleted = object->Delete(context, property);
477     if (!maybe_deleted.IsJust()) {
478       // In theory, deletion should never result in throwing an error. But
479       // crazier things have happened.
480       NOTREACHED();
481       return;
482     }
483     if (!maybe_deleted.FromJust()) {
484       // Deletion can *fail* in certain cases, such as when the script does
485       // Object.freeze(chrome).
486       return;
487     }
488     auto maybe_set = object->CreateDataProperty(context, property, new_field);
489     // Setting a new value can fail in multiple scenarios. Bail out if it does.
490     if (!maybe_set.IsJust() || !maybe_set.FromJust())
491       return;
492   } else {
493     NOTREACHED();
494   }
495   info.GetReturnValue().Set(new_field);
496 
497   UMA_HISTOGRAM_TIMES("Extensions.ApiBindingGenerationTime", timer.Elapsed());
498 }
499 
SetLazyField(v8::Local<v8::Object> object,const std::string & field,const std::string & module_name,const std::string & module_field)500 void ModuleSystem::SetLazyField(v8::Local<v8::Object> object,
501                                 const std::string& field,
502                                 const std::string& module_name,
503                                 const std::string& module_field) {
504   SetLazyField(
505       object, field, module_name, module_field, &ModuleSystem::LazyFieldGetter);
506 }
507 
SetLazyField(v8::Local<v8::Object> object,const std::string & field,const std::string & module_name,const std::string & module_field,v8::AccessorNameGetterCallback getter)508 void ModuleSystem::SetLazyField(v8::Local<v8::Object> object,
509                                 const std::string& field,
510                                 const std::string& module_name,
511                                 const std::string& module_field,
512                                 v8::AccessorNameGetterCallback getter) {
513   CHECK(field.size() < v8::String::kMaxLength);
514   CHECK(module_name.size() < v8::String::kMaxLength);
515   CHECK(module_field.size() < v8::String::kMaxLength);
516   v8::HandleScope handle_scope(GetIsolate());
517   v8::Local<v8::Object> parameters = v8::Object::New(GetIsolate());
518   v8::Local<v8::Context> context = context_->v8_context();
519   // Since we reset the accessor here, we remove the record of having loaded the
520   // module.
521   loaded_modules_.erase(module_name);
522   SetPrivateProperty(context, parameters, kModuleName,
523                      ToV8StringUnsafe(GetIsolate(), module_name.c_str()));
524   SetPrivateProperty(context, parameters, kModuleField,
525                      ToV8StringUnsafe(GetIsolate(), module_field.c_str()));
526   auto maybe = object->SetAccessor(
527       context, ToV8StringUnsafe(GetIsolate(), field.c_str()), getter, NULL,
528       parameters);
529   CHECK(v8_helpers::IsTrue(maybe));
530 }
531 
SetNativeLazyField(v8::Local<v8::Object> object,const std::string & field,const std::string & module_name,const std::string & module_field)532 void ModuleSystem::SetNativeLazyField(v8::Local<v8::Object> object,
533                                       const std::string& field,
534                                       const std::string& module_name,
535                                       const std::string& module_field) {
536   SetLazyField(object,
537                field,
538                module_name,
539                module_field,
540                &ModuleSystem::NativeLazyFieldGetter);
541 }
542 
OnNativeBindingCreated(const std::string & api_name,v8::Local<v8::Value> api_bridge_value)543 void ModuleSystem::OnNativeBindingCreated(
544     const std::string& api_name,
545     v8::Local<v8::Value> api_bridge_value) {
546   DCHECK(!get_internal_api_.IsEmpty());
547   v8::HandleScope scope(GetIsolate());
548   if (source_map_->Contains(api_name)) {
549     // We need to load the custom bindings and store them in our modules.
550     // Storing them is important so that calls through CallModuleMethod() route
551     // to the proper objects, if they share the same name as an API.
552     v8::Local<v8::Value> modules;
553     if (!GetPrivate(context()->v8_context()->Global(), kModulesField,
554                     &modules) ||
555         !modules->IsObject()) {
556       NOTREACHED();
557       return;
558     }
559 
560     NativesEnabledScope enabled(this);
561     v8::Local<v8::Value> exports =
562         LoadModuleWithNativeAPIBridge(api_name, api_bridge_value);
563     SetPrivateProperty(context()->v8_context(), modules.As<v8::Object>(),
564                        gin::StringToSymbol(GetIsolate(), api_name), exports);
565   }
566 }
567 
SetGetInternalAPIHook(v8::Local<v8::FunctionTemplate> get_internal_api)568 void ModuleSystem::SetGetInternalAPIHook(
569     v8::Local<v8::FunctionTemplate> get_internal_api) {
570   DCHECK(get_internal_api_.IsEmpty());
571   get_internal_api_.Set(GetIsolate(), get_internal_api);
572 }
573 
SetJSBindingUtilGetter(const JSBindingUtilGetter & getter)574 void ModuleSystem::SetJSBindingUtilGetter(const JSBindingUtilGetter& getter) {
575   DCHECK(js_binding_util_getter_.is_null());
576   js_binding_util_getter_ = getter;
577 }
578 
RunString(v8::Local<v8::String> code,v8::Local<v8::String> name)579 v8::Local<v8::Value> ModuleSystem::RunString(v8::Local<v8::String> code,
580                                              v8::Local<v8::String> name) {
581   return context_->RunScript(
582       name, code,
583       base::Bind(&ExceptionHandler::HandleUncaughtException,
584                  base::Unretained(exception_handler_.get())),
585       v8::ScriptCompiler::NoCacheReason::kNoCacheBecauseExtensionModule);
586 }
587 
RequireNative(const v8::FunctionCallbackInfo<v8::Value> & args)588 void ModuleSystem::RequireNative(
589     const v8::FunctionCallbackInfo<v8::Value>& args) {
590   CHECK_EQ(1, args.Length());
591   std::string native_name = *v8::String::Utf8Value(args.GetIsolate(), args[0]);
592   v8::Local<v8::Object> object;
593   if (RequireNativeFromString(native_name).ToLocal(&object))
594     args.GetReturnValue().Set(object);
595 }
596 
RequireNativeFromString(const std::string & native_name)597 v8::MaybeLocal<v8::Object> ModuleSystem::RequireNativeFromString(
598     const std::string& native_name) {
599   if (natives_enabled_ == 0) {
600     // HACK: if in test throw exception so that we can test the natives-disabled
601     // logic; however, under normal circumstances, this is programmer error so
602     // we could crash.
603     if (exception_handler_) {
604       GetIsolate()->ThrowException(
605           ToV8StringUnsafe(GetIsolate(), "Natives disabled"));
606       return v8::MaybeLocal<v8::Object>();
607     }
608     Fatal(context_, "Natives disabled for requireNative(" + native_name + ")");
609     return v8::MaybeLocal<v8::Object>();
610   }
611 
612   if (overridden_native_handlers_.count(native_name) > 0u) {
613     v8::Local<v8::Value> value = RequireForJsInner(
614         ToV8StringUnsafe(GetIsolate(), native_name.c_str()), true /* create */);
615     if (value.IsEmpty() || !value->IsObject())
616       return v8::MaybeLocal<v8::Object>();
617     return value.As<v8::Object>();
618   }
619 
620   auto i = native_handler_map_.find(native_name);
621   if (i == native_handler_map_.end()) {
622     Fatal(context_,
623           "Couldn't find native for requireNative(" + native_name + ")");
624     return v8::MaybeLocal<v8::Object>();
625   }
626 
627   if (!i->second->IsInitialized())
628     i->second->Initialize();
629 
630   return i->second->NewInstance();
631 }
632 
LoadScript(const v8::FunctionCallbackInfo<v8::Value> & args)633 void ModuleSystem::LoadScript(const v8::FunctionCallbackInfo<v8::Value>& args) {
634   CHECK_EQ(1, args.Length());
635   std::string module_name = *v8::String::Utf8Value(GetIsolate(), args[0]);
636 
637   v8::HandleScope handle_scope(GetIsolate());
638   v8::Local<v8::Context> v8_context = context()->v8_context();
639   v8::Context::Scope context_scope(v8_context);
640 
641   v8::Local<v8::String> source =
642       source_map_->GetSource(GetIsolate(), module_name);
643   if (source.IsEmpty())
644     Fatal(context_, "No source for loadScript(" + module_name + ")");
645 
646   v8::Local<v8::String> v8_module_name;
647   if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name))
648     Warn(GetIsolate(), "module_name is too long");
649 
650   RunString(source, v8_module_name);
651   args.GetReturnValue().Set(v8::Undefined(GetIsolate()));
652 }
653 
WrapSource(v8::Local<v8::String> source)654 v8::Local<v8::String> ModuleSystem::WrapSource(v8::Local<v8::String> source) {
655   v8::EscapableHandleScope handle_scope(GetIsolate());
656   // Keep in order with the arguments in RequireForJsInner.
657   v8::Local<v8::String> left = ToV8StringUnsafe(
658       GetIsolate(),
659       "(function(require, requireNative, loadScript, exports, console, "
660       "privates, apiBridge, bindingUtil, getInternalApi, $Array, $Function, "
661       "$JSON, $Object, $RegExp, $String, $Error, $Promise) {"
662       "'use strict';");
663   v8::Local<v8::String> right = ToV8StringUnsafe(GetIsolate(), "\n})");
664   return handle_scope.Escape(v8::Local<v8::String>(v8::String::Concat(
665       GetIsolate(), left, v8::String::Concat(GetIsolate(), source, right))));
666 }
667 
Private(const v8::FunctionCallbackInfo<v8::Value> & args)668 void ModuleSystem::Private(const v8::FunctionCallbackInfo<v8::Value>& args) {
669   CHECK_EQ(1, args.Length());
670   if (!args[0]->IsObject() || args[0]->IsNull()) {
671     GetIsolate()->ThrowException(v8::Exception::TypeError(ToV8StringUnsafe(
672         GetIsolate(), args[0]->IsUndefined()
673                           ? "Method called without a valid receiver (this). "
674                             "Did you forget to call .bind()?"
675                           : "Invalid invocation: receiver is not an object!")));
676     return;
677   }
678   v8::Local<v8::Object> obj = args[0].As<v8::Object>();
679   v8::Local<v8::Value> privates;
680   if (!GetPrivate(obj, "privates", &privates) || !privates->IsObject()) {
681     privates = v8::Object::New(args.GetIsolate());
682     if (privates.IsEmpty()) {
683       GetIsolate()->ThrowException(
684           ToV8StringUnsafe(GetIsolate(), "Failed to create privates"));
685       return;
686     }
687     v8::Maybe<bool> maybe =
688         privates.As<v8::Object>()->SetPrototype(context()->v8_context(),
689                                                 v8::Null(args.GetIsolate()));
690     CHECK(maybe.IsJust() && maybe.FromJust());
691     SetPrivate(obj, "privates", privates);
692   }
693   args.GetReturnValue().Set(privates);
694 }
695 
LoadModule(const std::string & module_name)696 v8::Local<v8::Value> ModuleSystem::LoadModule(const std::string& module_name) {
697   return LoadModuleWithNativeAPIBridge(module_name,
698                                        v8::Undefined(GetIsolate()));
699 }
700 
LoadModuleWithNativeAPIBridge(const std::string & module_name,v8::Local<v8::Value> api_bridge)701 v8::Local<v8::Value> ModuleSystem::LoadModuleWithNativeAPIBridge(
702     const std::string& module_name,
703     v8::Local<v8::Value> api_bridge) {
704   v8::EscapableHandleScope handle_scope(GetIsolate());
705   v8::Local<v8::Context> v8_context = context()->v8_context();
706   v8::Context::Scope context_scope(v8_context);
707 
708   v8::Local<v8::String> source =
709       source_map_->GetSource(GetIsolate(), module_name);
710   if (source.IsEmpty()) {
711     Fatal(context_, "No source for require(" + module_name + ")");
712     return v8::Undefined(GetIsolate());
713   }
714   v8::Local<v8::String> wrapped_source(WrapSource(source));
715   v8::Local<v8::String> v8_module_name;
716   if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name)) {
717     NOTREACHED() << "module_name is too long";
718     return v8::Undefined(GetIsolate());
719   }
720   // Modules are wrapped in (function(){...}) so they always return functions.
721   v8::Local<v8::Value> func_as_value =
722       RunString(wrapped_source, v8_module_name);
723   if (func_as_value.IsEmpty() || func_as_value->IsUndefined()) {
724     Fatal(context_, "Bad source for require(" + module_name + ")");
725     return v8::Undefined(GetIsolate());
726   }
727 
728   v8::Local<v8::Function> func = v8::Local<v8::Function>::Cast(func_as_value);
729 
730   v8::Local<v8::Object> exports = v8::Object::New(GetIsolate());
731 
732   v8::Local<v8::FunctionTemplate> tmpl = v8::FunctionTemplate::New(
733       GetIsolate(),
734       &SetExportsProperty);
735   tmpl->RemovePrototype();
736   v8::Local<v8::String> v8_key;
737   if (!ToV8String(GetIsolate(), "$set", &v8_key)) {
738     NOTREACHED();
739     return v8::Undefined(GetIsolate());
740   }
741 
742   v8::Local<v8::Function> function;
743   if (!tmpl->GetFunction(v8_context).ToLocal(&function)) {
744     NOTREACHED();
745     return v8::Undefined(GetIsolate());
746   }
747 
748   exports->DefineOwnProperty(v8_context, v8_key, function, v8::ReadOnly)
749       .FromJust();
750 
751   v8::Local<v8::Object> natives(NewInstance());
752   CHECK(!natives.IsEmpty());  // this can fail if v8 has issues
753 
754   v8::Local<v8::Value> get_internal_api;
755   if (get_internal_api_.IsEmpty()) {
756     get_internal_api = v8::Undefined(GetIsolate());
757   } else {
758     get_internal_api = get_internal_api_.Get(GetIsolate())
759                            ->GetFunction(v8_context)
760                            .ToLocalChecked();
761   }
762 
763   v8::Local<v8::Value> binding_util;
764   if (!js_binding_util_getter_.is_null()) {
765     js_binding_util_getter_.Run(v8_context, &binding_util);
766     if (binding_util.IsEmpty()) {
767       // The NativeExtensionBindingsSystem was destroyed. This shouldn't happen,
768       // but JS makes the impossible possible!
769       NOTREACHED();
770       return v8::Undefined(GetIsolate());
771     }
772   } else {
773     binding_util = v8::Undefined(GetIsolate());
774   }
775 
776   // These must match the argument order in WrapSource.
777   v8::Local<v8::Value> args[] = {
778       // CommonJS.
779       v8_helpers::GetPropertyUnsafe(v8_context, natives, "require",
780                                     v8::NewStringType::kInternalized),
781       v8_helpers::GetPropertyUnsafe(v8_context, natives, "requireNative",
782                                     v8::NewStringType::kInternalized),
783       v8_helpers::GetPropertyUnsafe(v8_context, natives, "loadScript",
784                                     v8::NewStringType::kInternalized),
785       exports,
786       // Libraries that we magically expose to every module.
787       console::AsV8Object(GetIsolate()),
788       v8_helpers::GetPropertyUnsafe(v8_context, natives, "privates",
789                                     v8::NewStringType::kInternalized),
790       api_bridge,        // exposed as apiBridge.
791       binding_util,      // exposed as bindingUtil.
792       get_internal_api,  // exposed as getInternalApi.
793       // Each safe builtin. Keep in order with the arguments in WrapSource.
794       context_->safe_builtins()->GetArray(),
795       context_->safe_builtins()->GetFunction(),
796       context_->safe_builtins()->GetJSON(),
797       context_->safe_builtins()->GetObjekt(),
798       context_->safe_builtins()->GetRegExp(),
799       context_->safe_builtins()->GetString(),
800       context_->safe_builtins()->GetError(),
801       context_->safe_builtins()->GetPromise(),
802   };
803   {
804     v8::TryCatch try_catch(GetIsolate());
805     try_catch.SetCaptureMessage(true);
806     context_->SafeCallFunction(func, base::size(args), args);
807     if (try_catch.HasCaught()) {
808       HandleException(try_catch);
809       return v8::Undefined(GetIsolate());
810     }
811   }
812   return handle_scope.Escape(exports);
813 }
814 
ClobberExistingNativeHandler(const std::string & name)815 void ModuleSystem::ClobberExistingNativeHandler(const std::string& name) {
816   auto existing_handler = native_handler_map_.find(name);
817   if (existing_handler != native_handler_map_.end()) {
818     clobbered_native_handlers_.push_back(std::move(existing_handler->second));
819     native_handler_map_.erase(existing_handler);
820   }
821 }
822 
GetModuleFunction(const std::string & module_name,const std::string & method_name)823 v8::Local<v8::Function> ModuleSystem::GetModuleFunction(
824     const std::string& module_name,
825     const std::string& method_name) {
826   v8::Local<v8::String> v8_module_name;
827   v8::Local<v8::String> v8_method_name;
828   if (!ToV8String(GetIsolate(), module_name.c_str(), &v8_module_name) ||
829       !ToV8String(GetIsolate(), method_name.c_str(), &v8_method_name)) {
830     return v8::Local<v8::Function>();
831   }
832 
833   v8::Local<v8::Value> module;
834   // Important: don't create the module if it doesn't exist. Doing so would
835   // force a call into JS, which is something we want to avoid in case it has
836   // been suspended. Additionally, we should only be calling module methods for
837   // modules that have been instantiated.
838   bool create = false;
839   module = RequireForJsInner(v8_module_name, create);
840 
841   // RequireForJsInner() returns Undefined in the case of a module not being
842   // loaded, since we don't create it here.
843   if (!module.IsEmpty() && module->IsUndefined())
844     return v8::Local<v8::Function>();
845 
846   if (module.IsEmpty() || !module->IsObject()) {
847     Fatal(context_,
848           "Failed to get module " + module_name + " to call " + method_name);
849     return v8::Local<v8::Function>();
850   }
851 
852   v8::Local<v8::Object> object = v8::Local<v8::Object>::Cast(module);
853   v8::Local<v8::Value> value;
854   if (!v8_helpers::GetProperty(context()->v8_context(), object, v8_method_name,
855                                &value) ||
856       !value->IsFunction()) {
857     Fatal(context_, module_name + "." + method_name + " is not a function");
858     return v8::Local<v8::Function>();
859   }
860 
861   return v8::Local<v8::Function>::Cast(value);
862 }
863 
864 }  // namespace extensions
865