summaryrefslogtreecommitdiffstats
path: root/chrome/renderer/extensions/module_system.h
blob: 2521d678b13579c81d591711ac2211c75cb9e89c (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
// Copyright (c) 2012 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 CHROME_RENDERER_EXTENSIONS_MODULE_SYSTEM_H_
#define CHROME_RENDERER_EXTENSIONS_MODULE_SYSTEM_H_

#include "base/compiler_specific.h"
#include "base/memory/linked_ptr.h"
#include "base/memory/scoped_ptr.h"
#include "chrome/renderer/extensions/object_backed_native_handler.h"
#include "v8/include/v8.h"

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

namespace extensions {

// 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:
  class SourceMap {
   public:
    virtual ~SourceMap() {}
    virtual v8::Handle<v8::Value> GetSource(const std::string& name) = 0;
    virtual bool Contains(const std::string& name) = 0;
  };

  class ExceptionHandler {
   public:
    virtual ~ExceptionHandler() {}
    virtual void HandleUncaughtException() = 0;
  };

  // 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(v8::Handle<v8::Context> context, SourceMap* source_map);
  virtual ~ModuleSystem();

  // Returns true if the current context has a ModuleSystem installed in it.
  static bool IsPresentInCurrentContext();

  // Dumps the debug info from |try_catch| to LOG(ERROR).
  static void DumpException(const v8::TryCatch& try_catch);

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

  // 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);

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

  // 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);
  // Check if a native handler has been registered for this |name|.
  bool HasNativeHandler(const std::string& name);

  // 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 OverrideNativeHandler(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);

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

  // Make |object|.|field| lazily evaluate to the result of
  // require(|module_name|)[|module_field|].
  void SetLazyField(v8::Handle<v8::Object> object,
                    const std::string& field,
                    const std::string& module_name,
                    const std::string& module_field);

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

  void set_exception_handler(scoped_ptr<ExceptionHandler> handler) {
    exception_handler_ = handler.Pass();
  }

  virtual bool Invalidate() OVERRIDE;

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

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

  static std::string CreateExceptionString(const v8::TryCatch& try_catch);

  // Ensure that require_ has been evaluated from require.js.
  void EnsureRequireLoaded();

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

  v8::Handle<v8::Value> RequireForJs(const v8::Arguments& args);
  v8::Handle<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name);

  // Sets a lazy field using the specified |getter|.
  void SetLazyField(v8::Handle<v8::Object> object,
                    const std::string& field,
                    const std::string& module_name,
                    const std::string& module_field,
                    v8::AccessorGetter getter);

  typedef v8::Handle<v8::Value> (ModuleSystem::*GetModuleFunc)(
      const std::string&);
  // Base implementation of a LazyFieldGetter that can be customized by passing
  // in a |get_module| function.
  static v8::Handle<v8::Value> LazyFieldGetterInner(
      v8::Local<v8::String> property,
      const v8::AccessorInfo& info,
      GetModuleFunc get_module);

  // Return the named source file stored in the source map.
  // |args[0]| - the name of a source file in source_map_.
  v8::Handle<v8::Value> GetSource(v8::Handle<v8::String> source_name);

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

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

  // Throws an exception in the calling JS context.
  v8::Handle<v8::Value> ThrowException(const std::string& message);

  // 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_;

  // Set to false if |context_| has been deleted and this should not be used.
  bool is_valid_;

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

  std::set<std::string> overridden_native_handlers_;

  DISALLOW_COPY_AND_ASSIGN(ModuleSystem);
};

}  // extensions

#endif  // CHROME_RENDERER_EXTENSIONS_MODULE_SYSTEM_H_