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 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
|
// 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 <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/compiler_specific.h"
#include "base/macros.h"
#include "extensions/renderer/native_handler.h"
#include "extensions/renderer/object_backed_native_handler.h"
#include "extensions/renderer/script_injection_callback.h"
#include "gin/modules/module_registry_observer.h"
#include "v8/include/v8.h"
namespace extensions {
class ScriptContext;
class SourceMap;
// 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 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, const 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.
// DEPRECATED: see crbug.com/629431
// TODO(devlin): Remove these.
v8::Local<v8::Value> CallModuleMethod(const std::string& module_name,
const std::string& method_name,
int argc,
v8::Local<v8::Value> argv[]);
// Same as the above, but allows for blocking execution.
void CallModuleMethodSafe(const std::string& module_name,
const std::string& method_name);
void CallModuleMethodSafe(const std::string& module_name,
const std::string& method_name,
std::vector<v8::Local<v8::Value>>* args);
void CallModuleMethodSafe(const std::string& module_name,
const std::string& method_name,
int argc,
v8::Local<v8::Value> argv[]);
void CallModuleMethodSafe(
const std::string& module_name,
const std::string& method_name,
int argc,
v8::Local<v8::Value> argv[],
const ScriptInjectionCallback::CompleteCallback& callback);
// 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,
std::unique_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(std::unique_ptr<ExceptionHandler> handler) {
exception_handler_ = std::move(handler);
}
// Called when a native binding is created in order to run any custom binding
// code to set up various hooks.
// TODO(devlin): We can get rid of this once we convert all our custom
// bindings.
void OnNativeBindingCreated(const std::string& api_name,
v8::Local<v8::Value> api_bridge_value);
protected:
friend class ModuleSystemTestEnvironment;
friend class ScriptContext;
void Invalidate() override;
private:
typedef std::map<std::string, std::unique_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);
v8::Local<v8::Value> LoadModuleWithNativeAPIBridge(
const std::string& module_name,
v8::Local<v8::Value> api_object);
// Invoked when a module is loaded in response to a requireAsync call.
// Resolves |resolver| with |value|.
void OnModuleLoaded(
std::unique_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);
// Returns the v8::Function associated with the given module and method name.
v8::Local<v8::Function> GetModuleFunction(const std::string& module_name,
const std::string& method_name);
ScriptContext* context_;
// A map from module names to the JS source for that module. GetSource()
// performs a lookup on this map.
const SourceMap* const 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.
std::unique_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<std::unique_ptr<NativeHandler>> clobbered_native_handlers_;
base::WeakPtrFactory<ModuleSystem> weak_factory_;
DISALLOW_COPY_AND_ASSIGN(ModuleSystem);
};
} // namespace extensions
#endif // EXTENSIONS_RENDERER_MODULE_SYSTEM_H_
|