1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
6 #include "extensions/renderer/xwalk_module_system.h"
12 #include "common/logger.h"
13 #include "common/profiler.h"
14 #include "extensions/renderer/xwalk_extension_module.h"
16 namespace extensions {
20 // Index used to set embedder data into v8::Context, so we can get from a
21 // context to its corresponding module. Index chosen to not conflict with
22 // WebCore::V8ContextEmbedderDataField in V8PerContextData.h.
23 const int kModuleSystemEmbedderDataIndex = 8;
25 // This is the key used in the data object passed to our callbacks to store a
26 // pointer back to XWalkExtensionModule.
27 const char* kXWalkModuleSystem = "kXWalkModuleSystem";
29 void RequireNativeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
30 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
32 v8::Isolate* isolate = info.GetIsolate();
33 v8::HandleScope handle_scope(isolate);
35 v8::Handle<v8::Object> data = info.Data().As<v8::Object>();
36 v8::Handle<v8::Value> module_system_value =
37 data->Get(v8::String::NewFromUtf8(isolate, kXWalkModuleSystem));
38 if (module_system_value.IsEmpty() || module_system_value->IsUndefined()) {
39 LOGGER(ERROR) << "Trying to use requireNative from already "
40 << "destroyed module system!";
41 result.SetUndefined();
45 XWalkModuleSystem* module_system = static_cast<XWalkModuleSystem*>(
46 module_system_value.As<v8::External>()->Value());
48 if (info.Length() < 1) {
49 // TODO(cmarcelo): Throw appropriate exception or warning.
50 result.SetUndefined();
54 result.SetUndefined();
57 v8::Handle<v8::Object> object =
58 module_system->RequireNative(*v8::String::Utf8Value(info[0]));
59 if (object.IsEmpty()) {
60 // TODO(cmarcelo): Throw appropriate exception or warning.
61 result.SetUndefined();
69 XWalkModuleSystem::XWalkModuleSystem(v8::Handle<v8::Context> context) {
70 v8::Isolate* isolate = context->GetIsolate();
71 v8_context_.Reset(isolate, context);
73 v8::HandleScope handle_scope(isolate);
74 v8::Handle<v8::Object> function_data = v8::Object::New(isolate);
75 function_data->Set(v8::String::NewFromUtf8(isolate, kXWalkModuleSystem),
76 v8::External::New(isolate, this));
77 v8::Handle<v8::FunctionTemplate> require_native_template =
78 v8::FunctionTemplate::New(isolate, RequireNativeCallback, function_data);
80 function_data_.Reset(isolate, function_data);
81 require_native_template_.Reset(isolate, require_native_template);
84 XWalkModuleSystem::~XWalkModuleSystem() {
85 DeleteExtensionModules();
86 auto it = native_modules_.begin();
87 for ( ; it != native_modules_.end(); ++it) {
90 native_modules_.clear();
92 v8::Isolate* isolate = v8::Isolate::GetCurrent();
93 v8::HandleScope handle_scope(isolate);
95 require_native_template_.Reset();
96 function_data_.Reset();
101 XWalkModuleSystem* XWalkModuleSystem::GetModuleSystemFromContext(
102 v8::Handle<v8::Context> context) {
103 return reinterpret_cast<XWalkModuleSystem*>(
104 context->GetAlignedPointerFromEmbedderData(
105 kModuleSystemEmbedderDataIndex));
109 void XWalkModuleSystem::SetModuleSystemInContext(
110 std::unique_ptr<XWalkModuleSystem> module_system,
111 v8::Handle<v8::Context> context) {
112 context->SetAlignedPointerInEmbedderData(kModuleSystemEmbedderDataIndex,
113 module_system.release());
117 void XWalkModuleSystem::ResetModuleSystemFromContext(
118 v8::Handle<v8::Context> context) {
119 XWalkModuleSystem* module_system = GetModuleSystemFromContext(context);
121 delete module_system;
122 SetModuleSystemInContext(std::unique_ptr<XWalkModuleSystem>(), context);
126 void XWalkModuleSystem::RegisterExtensionModule(
127 std::unique_ptr<XWalkExtensionModule> module,
128 const std::vector<std::string>& entry_points) {
129 const std::string& extension_name = module->extension_name();
130 if (ContainsEntryPoint(extension_name)) {
131 LOGGER(ERROR) << "Can't register Extension Module named for extension '"
132 << extension_name << "' in the Module System because name "
133 << " was already registered.";
137 std::vector<std::string>::const_iterator it = entry_points.begin();
138 for (; it != entry_points.end(); ++it) {
139 if (ContainsEntryPoint(*it)) {
140 LOGGER(ERROR) << "Can't register Extension Module named for extension '"
141 << extension_name << "' in the Module System because "
142 << "another extension has the entry point '"
148 extension_modules_.push_back(
149 ExtensionModuleEntry(extension_name, module.release(), entry_points));
152 void XWalkModuleSystem::RegisterNativeModule(
153 const std::string& name, std::unique_ptr<XWalkNativeModule> module) {
154 if (native_modules_.find(name) != native_modules_.end()) {
157 native_modules_[name] = module.release();
163 v8::Handle<v8::Value> EnsureTargetObjectForTrampoline(
164 v8::Handle<v8::Context> context, const std::vector<std::string>& path,
165 std::string* error) {
166 v8::Handle<v8::Object> object = context->Global();
167 v8::Isolate* isolate = context->GetIsolate();
169 std::vector<std::string>::const_iterator it = path.begin();
170 for (; it != path.end(); ++it) {
171 v8::Handle<v8::String> part =
172 v8::String::NewFromUtf8(isolate, it->c_str());
173 v8::Handle<v8::Value> value = object->Get(part);
175 if (value->IsUndefined()) {
176 v8::Handle<v8::Object> next_object = v8::Object::New(isolate);
177 object->Set(part, next_object);
178 object = next_object;
182 if (!value->IsObject()) {
183 *error = "the property '" + *it + "' in the path is undefined";
184 return v8::Undefined(isolate);
187 object = value.As<v8::Object>();
192 v8::Handle<v8::Value> GetObjectForPath(v8::Handle<v8::Context> context,
193 const std::vector<std::string>& path,
194 std::string* error) {
195 v8::Handle<v8::Object> object = context->Global();
196 v8::Isolate* isolate = context->GetIsolate();
198 std::vector<std::string>::const_iterator it = path.begin();
199 for (; it != path.end(); ++it) {
200 v8::Handle<v8::String> part =
201 v8::String::NewFromUtf8(isolate, it->c_str());
202 v8::Handle<v8::Value> value = object->Get(part);
204 if (!value->IsObject()) {
205 *error = "the property '" + *it + "' in the path is undefined";
206 return v8::Undefined(isolate);
209 object = value.As<v8::Object>();
216 template <typename STR>
217 void SplitString(const STR& str, const typename STR::value_type s,
218 std::vector<STR>* r) {
221 size_t c = str.size();
222 for (size_t i = 0; i <= c; ++i) {
223 if (i == c || str[i] == s) {
224 STR tmp(str, last, i - last);
225 if (i != c || !r->empty() || !tmp.empty())
232 bool XWalkModuleSystem::SetTrampolineAccessorForEntryPoint(
233 v8::Handle<v8::Context> context,
234 const std::string& entry_point,
235 v8::Local<v8::External> user_data) {
236 std::vector<std::string> path;
237 SplitString(entry_point, '.', &path);
238 std::string basename = path.back();
242 v8::Handle<v8::Value> value =
243 EnsureTargetObjectForTrampoline(context, path, &error);
244 if (value->IsUndefined()) {
245 LOGGER(ERROR) << "Error installing trampoline for " << entry_point
250 v8::Isolate* isolate = context->GetIsolate();
251 v8::Local<v8::Array> params = v8::Array::New(isolate);
252 v8::Local<v8::String> entry =
253 v8::String::NewFromUtf8(isolate, entry_point.c_str());
254 params->Set(v8::Integer::New(isolate, 0), user_data);
255 params->Set(v8::Integer::New(isolate, 1), entry);
257 // FIXME(cmarcelo): ensure that trampoline is readonly.
258 value.As<v8::Object>()->SetAccessor(context,
259 v8::String::NewFromUtf8(isolate, basename.c_str()),
260 TrampolineCallback, TrampolineSetterCallback, params);
265 bool XWalkModuleSystem::DeleteAccessorForEntryPoint(
266 v8::Handle<v8::Context> context,
267 const std::string& entry_point) {
268 std::vector<std::string> path;
269 SplitString(entry_point, '.', &path);
270 std::string basename = path.back();
274 v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
275 if (value->IsUndefined()) {
276 LOGGER(ERROR) << "Error retrieving object for " << entry_point
281 value.As<v8::Object>()->Delete(
282 v8::String::NewFromUtf8(context->GetIsolate(), basename.c_str()));
286 bool XWalkModuleSystem::InstallTrampoline(v8::Handle<v8::Context> context,
287 ExtensionModuleEntry* entry) {
288 v8::Local<v8::External> entry_ptr =
289 v8::External::New(context->GetIsolate(), entry);
290 bool ret = SetTrampolineAccessorForEntryPoint(context, entry->name,
293 LOGGER(ERROR) << "Error installing trampoline for " << entry->name;
297 std::vector<std::string>::const_iterator it = entry->entry_points.begin();
298 for (; it != entry->entry_points.end(); ++it) {
299 ret = SetTrampolineAccessorForEntryPoint(context, *it, entry_ptr);
301 // TODO(vcgomes): Remove already added trampolines when it fails.
302 LOGGER(ERROR) << "Error installing trampoline for " << entry->name;
309 v8::Handle<v8::Object> XWalkModuleSystem::RequireNative(
310 const std::string& name) {
311 NativeModuleMap::iterator it = native_modules_.find(name);
312 if (it == native_modules_.end())
313 return v8::Handle<v8::Object>();
314 return it->second->NewInstance();
317 void XWalkModuleSystem::Initialize() {
319 v8::Isolate* isolate = v8::Isolate::GetCurrent();
320 v8::HandleScope handle_scope(isolate);
321 v8::Handle<v8::Context> context = GetV8Context();
322 v8::Handle<v8::FunctionTemplate> require_native_template =
323 v8::Local<v8::FunctionTemplate>::New(isolate, require_native_template_);
324 v8::Handle<v8::Function> require_native =
325 require_native_template->GetFunction();
327 MarkModulesWithTrampoline();
329 ExtensionModules::iterator it = extension_modules_.begin();
330 for (; it != extension_modules_.end(); ++it) {
331 if (it->use_trampoline && InstallTrampoline(context, &*it))
333 it->module->LoadExtensionCode(context, require_native);
334 EnsureExtensionNamespaceIsReadOnly(context, it->name);
338 v8::Handle<v8::Context> XWalkModuleSystem::GetV8Context() {
339 return v8::Local<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
342 bool XWalkModuleSystem::ContainsEntryPoint(
343 const std::string& entry) {
344 auto it = extension_modules_.begin();
345 for (; it != extension_modules_.end(); ++it) {
346 if (it->name == entry)
349 auto entry_it = std::find(
350 it->entry_points.begin(), it->entry_points.end(), entry);
351 if (entry_it != it->entry_points.end()) {
358 void XWalkModuleSystem::DeleteExtensionModules() {
359 for (ExtensionModules::iterator it = extension_modules_.begin();
360 it != extension_modules_.end(); ++it) {
363 extension_modules_.clear();
367 void XWalkModuleSystem::LoadExtensionForTrampoline(
368 v8::Isolate* isolate,
369 v8::Local<v8::Value> data) {
370 v8::HandleScope handle_scope(isolate);
371 v8::Local<v8::Array> params = data.As<v8::Array>();
372 void* ptr = params->Get(
373 v8::Integer::New(isolate, 0)).As<v8::External>()->Value();
375 ExtensionModuleEntry* entry = static_cast<ExtensionModuleEntry*>(ptr);
380 v8::Handle<v8::Context> context = isolate->GetCurrentContext();
382 DeleteAccessorForEntryPoint(context, entry->name);
384 auto it = entry->entry_points.begin();
385 for (; it != entry->entry_points.end(); ++it) {
386 DeleteAccessorForEntryPoint(context, *it);
389 XWalkModuleSystem* module_system = GetModuleSystemFromContext(context);
390 v8::Handle<v8::FunctionTemplate> require_native_template =
391 v8::Local<v8::FunctionTemplate>::New(
393 module_system->require_native_template_);
395 XWalkExtensionModule* module = entry->module;
396 module->LoadExtensionCode(module_system->GetV8Context(),
397 require_native_template->GetFunction());
399 module_system->EnsureExtensionNamespaceIsReadOnly(context, entry->name);
403 v8::Handle<v8::Value> XWalkModuleSystem::RefetchHolder(
404 v8::Isolate* isolate,
405 v8::Local<v8::Value> data) {
406 v8::Local<v8::Array> params = data.As<v8::Array>();
407 const std::string entry_point = *v8::String::Utf8Value(
408 params->Get(v8::Integer::New(isolate, 1)).As<v8::String>());
410 std::vector<std::string> path;
411 SplitString(entry_point, '.', &path);
415 return GetObjectForPath(isolate->GetCurrentContext(), path, &error);
419 void XWalkModuleSystem::TrampolineCallback(
420 v8::Local<v8::Name> property,
421 const v8::PropertyCallbackInfo<v8::Value>& info) {
422 XWalkModuleSystem::LoadExtensionForTrampoline(info.GetIsolate(), info.Data());
423 v8::Handle<v8::Value> holder = RefetchHolder(info.GetIsolate(), info.Data());
424 if (holder->IsUndefined())
427 info.GetReturnValue().Set(holder.As<v8::Object>()->Get(property));
431 void XWalkModuleSystem::TrampolineSetterCallback(
432 v8::Local<v8::Name> property,
433 v8::Local<v8::Value> value,
434 const v8::PropertyCallbackInfo<void>& info) {
435 XWalkModuleSystem::LoadExtensionForTrampoline(info.GetIsolate(), info.Data());
436 v8::Handle<v8::Value> holder = RefetchHolder(info.GetIsolate(), info.Data());
437 if (holder->IsUndefined())
440 holder.As<v8::Object>()->Set(property, value);
443 XWalkModuleSystem::ExtensionModuleEntry::ExtensionModuleEntry(
444 const std::string& name,
445 XWalkExtensionModule* module,
446 const std::vector<std::string>& entry_points) :
447 name(name), module(module), use_trampoline(true),
448 entry_points(entry_points) {
451 XWalkModuleSystem::ExtensionModuleEntry::~ExtensionModuleEntry() {
454 // Returns whether the name of first is prefix of the second, considering "."
455 // character as a separator. So "a" is prefix of "a.b" but not of "ab".
456 bool XWalkModuleSystem::ExtensionModuleEntry::IsPrefix(
457 const ExtensionModuleEntry& first,
458 const ExtensionModuleEntry& second) {
459 const std::string& p = first.name;
460 const std::string& s = second.name;
461 return s.size() > p.size() && s[p.size()] == '.'
462 && std::mismatch(p.begin(), p.end(), s.begin()).first == p.end();
465 // Mark the extension modules that we want to setup "trampolines"
466 // instead of loading the code directly. The current algorithm is very
467 // simple: we only create trampolines for extensions that are leaves
468 // in the namespace tree.
470 // For example, if there are two extensions "tizen" and "tizen.time",
471 // the first one won't be marked with trampoline, but the second one
472 // will. So we'll only load code for "tizen" extension.
473 void XWalkModuleSystem::MarkModulesWithTrampoline() {
474 std::sort(extension_modules_.begin(), extension_modules_.end());
476 ExtensionModules::iterator it = extension_modules_.begin();
477 while (it != extension_modules_.end()) {
478 it = std::adjacent_find(it, extension_modules_.end(),
479 &ExtensionModuleEntry::IsPrefix);
480 if (it == extension_modules_.end())
482 it->use_trampoline = false;
487 // NOTE: Special Case for Security Reason
488 // xwalk module should not be trampolined even it does not have any children.
490 ExtensionModules::iterator it = extension_modules_.begin();
491 while (it != extension_modules_.end()) {
492 if ("xwalk" == (*it).name) {
493 it->use_trampoline = false;
501 void XWalkModuleSystem::EnsureExtensionNamespaceIsReadOnly(
502 v8::Handle<v8::Context> context,
503 const std::string& extension_name) {
504 std::vector<std::string> path;
505 SplitString(extension_name, '.', &path);
506 std::string basename = path.back();
510 v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
511 if (value->IsUndefined()) {
512 LOGGER(ERROR) << "Error retrieving object for " << extension_name << " : "
517 v8::Handle<v8::String> v8_extension_name(
518 v8::String::NewFromUtf8(context->GetIsolate(), basename.c_str()));
519 value.As<v8::Object>()->ForceSet(
520 v8_extension_name, value.As<v8::Object>()->Get(v8_extension_name),
524 } // namespace extensions