summaryrefslogtreecommitdiffstats
path: root/extensions/renderer/module_system.h
blob: 134d2baa05f2fb3d6da8fd1c079aae56c5c029a2 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef EXTENSIONS_RENDERER_MODULE_SYSTEM_H_
#define EXTENSIONS_RENDERER_MODULE_SYSTEM_H_

#include <map>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "base/compiler_specific.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
#include "extensions/renderer/native_handler.h"
#include "extensions/renderer/object_backed_native_handler.h"
#include "gin/modules/module_registry_observer.h"
#include "v8/include/v8.h"

namespace extensions {

class ScriptContext;

// A module system for JS similar to node.js' require() function.
// Each module has three variables in the global scope:
//   - exports, an object returned to dependencies who require() this
//     module.
//   - require, a function that takes a module name as an argument and returns
//     that module's exports object.
//   - requireNative, a function that takes the name of a registered
//     NativeHandler and returns an object that contains the functions the
//     NativeHandler defines.
//
// Each module in a ModuleSystem is executed at most once and its exports
// object cached.
//
// Note that a ModuleSystem must be used only in conjunction with a single
// v8::Context.
// TODO(koz): Rename this to JavaScriptModuleSystem.
class ModuleSystem : public ObjectBackedNativeHandler,
                     public gin::ModuleRegistryObserver {
 public:
  class SourceMap {
   public:
    virtual ~SourceMap() {}
    virtual v8::Local<v8::Value> GetSource(v8::Isolate* isolate,
                                           const std::string& name) = 0;
    virtual bool Contains(const std::string& name) = 0;
  };

  class ExceptionHandler {
   public:
    explicit ExceptionHandler(ScriptContext* context) : context_(context) {}
    virtual ~ExceptionHandler() {}
    virtual void HandleUncaughtException(const v8::TryCatch& try_catch) = 0;

   protected:
    // Formats |try_catch| as a nice string.
    std::string CreateExceptionString(const v8::TryCatch& try_catch);
    // A script context associated with this handler. Owned by the module
    // system.
    ScriptContext* context_;
  };

  // Enables native bindings for the duration of its lifetime.
  class NativesEnabledScope {
   public:
    explicit NativesEnabledScope(ModuleSystem* module_system);
    ~NativesEnabledScope();

   private:
    ModuleSystem* module_system_;
    DISALLOW_COPY_AND_ASSIGN(NativesEnabledScope);
  };

  // |source_map| is a weak pointer.
  ModuleSystem(ScriptContext* context, SourceMap* source_map);
  ~ModuleSystem() override;

  // Require the specified module. This is the equivalent of calling
  // require('module_name') from the loaded JS files.
  v8::MaybeLocal<v8::Object> Require(const std::string& module_name);
  void Require(const v8::FunctionCallbackInfo<v8::Value>& args);

  // Run |code| in the current context with the name |name| used for stack
  // traces.
  v8::Local<v8::Value> RunString(v8::Local<v8::String> code,
                                 v8::Local<v8::String> name);

  // Calls the specified method exported by the specified module. This is
  // equivalent to calling require('module_name').method_name() from JS.
  v8::Local<v8::Value> CallModuleMethod(const std::string& module_name,
                                        const std::string& method_name);
  v8::Local<v8::Value> CallModuleMethod(
      const std::string& module_name,
      const std::string& method_name,
      std::vector<v8::Local<v8::Value>>* args);
  v8::Local<v8::Value> CallModuleMethod(const std::string& module_name,
                                        const std::string& method_name,
                                        int argc,
                                        v8::Local<v8::Value> argv[]);

  // Register |native_handler| as a potential target for requireNative(), so
  // calls to requireNative(|name|) from JS will return a new object created by
  // |native_handler|.
  void RegisterNativeHandler(const std::string& name,
                             scoped_ptr<NativeHandler> native_handler);

  // Causes requireNative(|name|) to look for its module in |source_map_|
  // instead of using a registered native handler. This can be used in unit
  // tests to mock out native modules.
  void OverrideNativeHandlerForTest(const std::string& name);

  // Executes |code| in the current context with |name| as the filename.
  void RunString(const std::string& code, const std::string& name);

  // Make |object|.|field| lazily evaluate to the result of
  // require(|module_name|)[|module_field|].
  //
  // TODO(kalman): All targets for this method are ObjectBackedNativeHandlers,
  //               move this logic into those classes (in fact, the chrome
  //               object is the only client, only that needs to implement it).
  void SetLazyField(v8::Local<v8::Object> object,
                    const std::string& field,
                    const std::string& module_name,
                    const std::string& module_field);

  void SetLazyField(v8::Local<v8::Object> object,
                    const std::string& field,
                    const std::string& module_name,
                    const std::string& module_field,
                    v8::AccessorNameGetterCallback getter);

  // Make |object|.|field| lazily evaluate to the result of
  // requireNative(|module_name|)[|module_field|].
  // TODO(kalman): Same as above.
  void SetNativeLazyField(v8::Local<v8::Object> object,
                          const std::string& field,
                          const std::string& module_name,
                          const std::string& module_field);

  // Passes exceptions to |handler| rather than console::Fatal.
  void SetExceptionHandlerForTest(scoped_ptr<ExceptionHandler> handler) {
    exception_handler_ = std::move(handler);
  }

 protected:
  friend class ModuleSystemTestEnvironment;
  friend class ScriptContext;
  void Invalidate() override;

 private:
  typedef std::map<std::string, scoped_ptr<NativeHandler>> NativeHandlerMap;

  // Retrieves the lazily defined field specified by |property|.
  static void LazyFieldGetter(v8::Local<v8::Name> property,
                              const v8::PropertyCallbackInfo<v8::Value>& info);
  // Retrieves the lazily defined field specified by |property| on a native
  // object.
  static void NativeLazyFieldGetter(
      v8::Local<v8::Name> property,
      const v8::PropertyCallbackInfo<v8::Value>& info);

  // Called when an exception is thrown but not caught.
  void HandleException(const v8::TryCatch& try_catch);

  void RequireForJs(const v8::FunctionCallbackInfo<v8::Value>& args);
  v8::Local<v8::Value> RequireForJsInner(v8::Local<v8::String> module_name);

  typedef v8::MaybeLocal<v8::Object>(ModuleSystem::*RequireFunction)(
      const std::string&);
  // Base implementation of a LazyFieldGetter which uses |require_fn| to require
  // modules.
  static void LazyFieldGetterInner(
      v8::Local<v8::String> property,
      const v8::PropertyCallbackInfo<v8::Value>& info,
      RequireFunction require_function);

  // Return the named source file stored in the source map.
  // |args[0]| - the name of a source file in source_map_.
  v8::Local<v8::Value> GetSource(const std::string& module_name);

  // Return an object that contains the native methods defined by the named
  // NativeHandler.
  // |args[0]| - the name of a native handler object.
  v8::MaybeLocal<v8::Object> RequireNativeFromString(
      const std::string& native_name);
  void RequireNative(const v8::FunctionCallbackInfo<v8::Value>& args);

  // Return a promise for a requested module.
  // |args[0]| - the name of a module.
  void RequireAsync(const v8::FunctionCallbackInfo<v8::Value>& args);

  // Wraps |source| in a (function(define, require, requireNative, ...) {...}).
  v8::Local<v8::String> WrapSource(v8::Local<v8::String> source);

  // NativeHandler implementation which returns the private area of an Object.
  void Private(const v8::FunctionCallbackInfo<v8::Value>& args);

  // Loads and runs a Javascript module.
  v8::Local<v8::Value> LoadModule(const std::string& module_name);

  // Invoked when a module is loaded in response to a requireAsync call.
  // Resolves |resolver| with |value|.
  void OnModuleLoaded(scoped_ptr<v8::Global<v8::Promise::Resolver>> resolver,
                      v8::Local<v8::Value> value);

  // gin::ModuleRegistryObserver overrides.
  void OnDidAddPendingModule(
      const std::string& id,
      const std::vector<std::string>& dependencies) override;

  // Marks any existing NativeHandler named |name| as clobbered.
  // See |clobbered_native_handlers_|.
  void ClobberExistingNativeHandler(const std::string& name);

  ScriptContext* context_;

  // A map from module names to the JS source for that module. GetSource()
  // performs a lookup on this map.
  SourceMap* source_map_;

  // A map from native handler names to native handlers.
  NativeHandlerMap native_handler_map_;

  // When 0, natives are disabled, otherwise indicates how many callers have
  // pinned natives as enabled.
  int natives_enabled_;

  // Called when an exception is thrown but not caught in JS. Overridable by
  // tests.
  scoped_ptr<ExceptionHandler> exception_handler_;

  // A set of native handlers that should actually be require()d as non-native
  // handlers. This is used for tests to mock out native handlers in JS.
  std::set<std::string> overridden_native_handlers_;

  // A list of NativeHandlers that have been clobbered, either due to
  // registering a NativeHandler when one was already registered with the same
  // name, or due to OverrideNativeHandlerForTest. This is needed so that they
  // can be later Invalidated. It should only happen in tests.
  std::vector<scoped_ptr<NativeHandler>> clobbered_native_handlers_;

  base::WeakPtrFactory<ModuleSystem> weak_factory_;

  DISALLOW_COPY_AND_ASSIGN(ModuleSystem);
};

}  // namespace extensions

#endif  // EXTENSIONS_RENDERER_MODULE_SYSTEM_H_