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