Home | History | Annotate | Download | only in extensions
      1 // Copyright (c) 2012 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 #ifndef CHROME_RENDERER_EXTENSIONS_MODULE_SYSTEM_H_
      6 #define CHROME_RENDERER_EXTENSIONS_MODULE_SYSTEM_H_
      7 
      8 #include "base/compiler_specific.h"
      9 #include "base/memory/linked_ptr.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "chrome/renderer/extensions/native_handler.h"
     12 #include "chrome/renderer/extensions/object_backed_native_handler.h"
     13 #include "v8/include/v8.h"
     14 
     15 #include <map>
     16 #include <set>
     17 #include <string>
     18 #include <vector>
     19 
     20 namespace extensions {
     21 
     22 class ChromeV8Context;
     23 
     24 // A module system for JS similar to node.js' require() function.
     25 // Each module has three variables in the global scope:
     26 //   - exports, an object returned to dependencies who require() this
     27 //     module.
     28 //   - require, a function that takes a module name as an argument and returns
     29 //     that module's exports object.
     30 //   - requireNative, a function that takes the name of a registered
     31 //     NativeHandler and returns an object that contains the functions the
     32 //     NativeHandler defines.
     33 //
     34 // Each module in a ModuleSystem is executed at most once and its exports
     35 // object cached.
     36 //
     37 // Note that a ModuleSystem must be used only in conjunction with a single
     38 // v8::Context.
     39 // TODO(koz): Rename this to JavaScriptModuleSystem.
     40 class ModuleSystem : public ObjectBackedNativeHandler {
     41  public:
     42   class SourceMap {
     43    public:
     44     virtual ~SourceMap() {}
     45     virtual v8::Handle<v8::Value> GetSource(v8::Isolate* isolate,
     46                                             const std::string& name) = 0;
     47     virtual bool Contains(const std::string& name) = 0;
     48   };
     49 
     50   class ExceptionHandler {
     51    public:
     52     virtual ~ExceptionHandler() {}
     53     virtual void HandleUncaughtException(const v8::TryCatch& try_catch) = 0;
     54 
     55    protected:
     56     // Formats |try_catch| as a nice string.
     57     std::string CreateExceptionString(const v8::TryCatch& try_catch);
     58   };
     59 
     60   // Enables native bindings for the duration of its lifetime.
     61   class NativesEnabledScope {
     62    public:
     63     explicit NativesEnabledScope(ModuleSystem* module_system);
     64     ~NativesEnabledScope();
     65 
     66    private:
     67     ModuleSystem* module_system_;
     68     DISALLOW_COPY_AND_ASSIGN(NativesEnabledScope);
     69   };
     70 
     71   // |source_map| is a weak pointer.
     72   ModuleSystem(ChromeV8Context* context, SourceMap* source_map);
     73   virtual ~ModuleSystem();
     74 
     75   // Require the specified module. This is the equivalent of calling
     76   // require('module_name') from the loaded JS files.
     77   v8::Handle<v8::Value> Require(const std::string& module_name);
     78   void Require(const v8::FunctionCallbackInfo<v8::Value>& args);
     79 
     80   // Calls the specified method exported by the specified module. This is
     81   // equivalent to calling require('module_name').method_name() from JS.
     82   v8::Local<v8::Value> CallModuleMethod(const std::string& module_name,
     83                                         const std::string& method_name);
     84   v8::Local<v8::Value> CallModuleMethod(
     85       const std::string& module_name,
     86       const std::string& method_name,
     87       std::vector<v8::Handle<v8::Value> >* args);
     88   v8::Local<v8::Value> CallModuleMethod(
     89       const std::string& module_name,
     90       const std::string& method_name,
     91       int argc,
     92       v8::Handle<v8::Value> argv[]);
     93 
     94   // Register |native_handler| as a potential target for requireNative(), so
     95   // calls to requireNative(|name|) from JS will return a new object created by
     96   // |native_handler|.
     97   void RegisterNativeHandler(const std::string& name,
     98                              scoped_ptr<NativeHandler> native_handler);
     99 
    100   // Causes requireNative(|name|) to look for its module in |source_map_|
    101   // instead of using a registered native handler. This can be used in unit
    102   // tests to mock out native modules.
    103   void OverrideNativeHandlerForTest(const std::string& name);
    104 
    105   // Executes |code| in the current context with |name| as the filename.
    106   void RunString(const std::string& code, const std::string& name);
    107 
    108   // Make |object|.|field| lazily evaluate to the result of
    109   // require(|module_name|)[|module_field|].
    110   //
    111   // TODO(kalman): All targets for this method are ObjectBackedNativeHandlers,
    112   //               move this logic into those classes (in fact, the chrome
    113   //               object is the only client, only that needs to implement it).
    114   void SetLazyField(v8::Handle<v8::Object> object,
    115                     const std::string& field,
    116                     const std::string& module_name,
    117                     const std::string& module_field);
    118 
    119   void SetLazyField(v8::Handle<v8::Object> object,
    120                     const std::string& field,
    121                     const std::string& module_name,
    122                     const std::string& module_field,
    123                     v8::AccessorGetterCallback getter);
    124 
    125   // Make |object|.|field| lazily evaluate to the result of
    126   // requireNative(|module_name|)[|module_field|].
    127   // TODO(kalman): Same as above.
    128   void SetNativeLazyField(v8::Handle<v8::Object> object,
    129                           const std::string& field,
    130                           const std::string& module_name,
    131                           const std::string& module_field);
    132 
    133   // Passes exceptions to |handler| rather than console::Fatal.
    134   void SetExceptionHandlerForTest(scoped_ptr<ExceptionHandler> handler) {
    135     exception_handler_ = handler.Pass();
    136   }
    137 
    138   v8::Isolate* GetIsolate() const;
    139 
    140  protected:
    141   friend class ChromeV8Context;
    142   virtual void Invalidate() OVERRIDE;
    143 
    144  private:
    145   typedef std::map<std::string, linked_ptr<NativeHandler> > NativeHandlerMap;
    146 
    147   // Retrieves the lazily defined field specified by |property|.
    148   static void LazyFieldGetter(v8::Local<v8::String> property,
    149                               const v8::PropertyCallbackInfo<v8::Value>& info);
    150   // Retrieves the lazily defined field specified by |property| on a native
    151   // object.
    152   static void NativeLazyFieldGetter(
    153       v8::Local<v8::String> property,
    154       const v8::PropertyCallbackInfo<v8::Value>& info);
    155 
    156   // Called when an exception is thrown but not caught.
    157   void HandleException(const v8::TryCatch& try_catch);
    158 
    159   // Ensure that require_ has been evaluated from require.js.
    160   void EnsureRequireLoaded();
    161 
    162   // Run |code| in the current context with the name |name| used for stack
    163   // traces.
    164   v8::Handle<v8::Value> RunString(v8::Handle<v8::String> code,
    165                                   v8::Handle<v8::String> name);
    166 
    167   void RequireForJs(const v8::FunctionCallbackInfo<v8::Value>& args);
    168   v8::Local<v8::Value> RequireForJsInner(v8::Handle<v8::String> module_name);
    169 
    170   typedef v8::Handle<v8::Value> (ModuleSystem::*RequireFunction)(
    171       const std::string&);
    172   // Base implementation of a LazyFieldGetter which uses |require_fn| to require
    173   // modules.
    174   static void LazyFieldGetterInner(
    175       v8::Local<v8::String> property,
    176       const v8::PropertyCallbackInfo<v8::Value>& info,
    177       RequireFunction require_function);
    178 
    179   // Return the named source file stored in the source map.
    180   // |args[0]| - the name of a source file in source_map_.
    181   v8::Handle<v8::Value> GetSource(const std::string& module_name);
    182 
    183   // Return an object that contains the native methods defined by the named
    184   // NativeHandler.
    185   // |args[0]| - the name of a native handler object.
    186   v8::Handle<v8::Value> RequireNativeFromString(const std::string& native_name);
    187   void RequireNative(const v8::FunctionCallbackInfo<v8::Value>& args);
    188 
    189   // Wraps |source| in a (function(require, requireNative, exports) {...}).
    190   v8::Handle<v8::String> WrapSource(v8::Handle<v8::String> source);
    191 
    192   // NativeHandler implementation which returns the private area of an Object.
    193   void Private(const v8::FunctionCallbackInfo<v8::Value>& args);
    194 
    195   ChromeV8Context* context_;
    196 
    197   // A map from module names to the JS source for that module. GetSource()
    198   // performs a lookup on this map.
    199   SourceMap* source_map_;
    200 
    201   // A map from native handler names to native handlers.
    202   NativeHandlerMap native_handler_map_;
    203 
    204   // When 0, natives are disabled, otherwise indicates how many callers have
    205   // pinned natives as enabled.
    206   int natives_enabled_;
    207 
    208   // Called when an exception is thrown but not caught in JS. Overridable by
    209   // tests.
    210   scoped_ptr<ExceptionHandler> exception_handler_;
    211 
    212   std::set<std::string> overridden_native_handlers_;
    213 
    214   DISALLOW_COPY_AND_ASSIGN(ModuleSystem);
    215 };
    216 
    217 }  // namespace extensions
    218 
    219 #endif  // CHROME_RENDERER_EXTENSIONS_MODULE_SYSTEM_H_
    220