Bringup with updated chromium-efl(m56.0.2924)
[platform/framework/web/crosswalk-tizen.git] / extensions / renderer / xwalk_module_system.cc
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.
5
6 #include "extensions/renderer/xwalk_module_system.h"
7
8 #include <v8/v8.h>
9
10 #include <algorithm>
11
12 #include "common/logger.h"
13 #include "common/profiler.h"
14 #include "extensions/renderer/xwalk_extension_module.h"
15
16 namespace extensions {
17
18 namespace {
19
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;
24
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";
28
29 void RequireNativeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
30   v8::ReturnValue<v8::Value> result(info.GetReturnValue());
31
32   v8::Isolate* isolate = info.GetIsolate();
33   v8::HandleScope handle_scope(isolate);
34
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();
42     return;
43   }
44
45   XWalkModuleSystem* module_system = static_cast<XWalkModuleSystem*>(
46       module_system_value.As<v8::External>()->Value());
47
48   if (info.Length() < 1) {
49     // TODO(cmarcelo): Throw appropriate exception or warning.
50     result.SetUndefined();
51     return;
52   }
53   if (!module_system) {
54     result.SetUndefined();
55     return;
56   }
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();
62     return;
63   }
64   result.Set(object);
65 }
66
67 }  // namespace
68
69 XWalkModuleSystem::XWalkModuleSystem(v8::Handle<v8::Context> context) {
70   v8::Isolate* isolate = context->GetIsolate();
71   v8_context_.Reset(isolate, context);
72
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);
79
80   function_data_.Reset(isolate, function_data);
81   require_native_template_.Reset(isolate, require_native_template);
82 }
83
84 XWalkModuleSystem::~XWalkModuleSystem() {
85   DeleteExtensionModules();
86   auto it = native_modules_.begin();
87   for ( ; it != native_modules_.end(); ++it) {
88     delete it->second;
89   }
90   native_modules_.clear();
91
92   v8::Isolate* isolate = v8::Isolate::GetCurrent();
93   v8::HandleScope handle_scope(isolate);
94
95   require_native_template_.Reset();
96   function_data_.Reset();
97   v8_context_.Reset();
98 }
99
100 // static
101 XWalkModuleSystem* XWalkModuleSystem::GetModuleSystemFromContext(
102     v8::Handle<v8::Context> context) {
103   return reinterpret_cast<XWalkModuleSystem*>(
104       context->GetAlignedPointerFromEmbedderData(
105           kModuleSystemEmbedderDataIndex));
106 }
107
108 // static
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());
114 }
115
116 // static
117 void XWalkModuleSystem::ResetModuleSystemFromContext(
118     v8::Handle<v8::Context> context) {
119   XWalkModuleSystem* module_system = GetModuleSystemFromContext(context);
120   if (module_system) {
121     delete module_system;
122     SetModuleSystemInContext(std::unique_ptr<XWalkModuleSystem>(), context);
123   }
124 }
125
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.";
134     return;
135   }
136
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 '"
143                     << (*it) << "'.";
144       return;
145     }
146   }
147
148   extension_modules_.push_back(
149       ExtensionModuleEntry(extension_name, module.release(), entry_points));
150 }
151
152 void XWalkModuleSystem::RegisterNativeModule(
153     const std::string& name, std::unique_ptr<XWalkNativeModule> module) {
154   if (native_modules_.find(name) != native_modules_.end()) {
155     return;
156   }
157   native_modules_[name] = module.release();
158 }
159
160
161 namespace {
162
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();
168
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);
174
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;
179       continue;
180     }
181
182     if (!value->IsObject()) {
183       *error = "the property '" + *it + "' in the path is undefined";
184       return v8::Undefined(isolate);
185     }
186
187     object = value.As<v8::Object>();
188   }
189   return object;
190 }
191
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();
197
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);
203
204     if (!value->IsObject()) {
205       *error = "the property '" + *it + "' in the path is undefined";
206       return v8::Undefined(isolate);
207     }
208
209     object = value.As<v8::Object>();
210   }
211   return object;
212 }
213
214 }  // namespace
215
216 template <typename STR>
217 void SplitString(const STR& str, const typename STR::value_type s,
218                  std::vector<STR>* r) {
219   r->clear();
220   size_t last = 0;
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())
226         r->push_back(tmp);
227       last = i + 1;
228     }
229   }
230 }
231
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();
239   path.pop_back();
240
241   std::string error;
242   v8::Handle<v8::Value> value =
243       EnsureTargetObjectForTrampoline(context, path, &error);
244   if (value->IsUndefined()) {
245     LOGGER(ERROR) << "Error installing trampoline for " << entry_point
246                   << " : " << error;
247     return false;
248   }
249
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);
256
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);
261   return true;
262 }
263
264 // static
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();
271   path.pop_back();
272
273   std::string error;
274   v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
275   if (value->IsUndefined()) {
276     LOGGER(ERROR) << "Error retrieving object for " << entry_point
277                   << " : " << error;
278     return false;
279   }
280
281   value.As<v8::Object>()->Delete(
282       v8::String::NewFromUtf8(context->GetIsolate(), basename.c_str()));
283   return true;
284 }
285
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,
291                                                 entry_ptr);
292   if (!ret) {
293     LOGGER(ERROR) << "Error installing trampoline for " << entry->name;
294     return false;
295   }
296
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);
300     if (!ret) {
301       // TODO(vcgomes): Remove already added trampolines when it fails.
302       LOGGER(ERROR) << "Error installing trampoline for " << entry->name;
303       return false;
304     }
305   }
306   return true;
307 }
308
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();
315 }
316
317 void XWalkModuleSystem::Initialize() {
318   SCOPE_PROFILE();
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();
326
327   MarkModulesWithTrampoline();
328
329   ExtensionModules::iterator it = extension_modules_.begin();
330   for (; it != extension_modules_.end(); ++it) {
331     if (it->use_trampoline && InstallTrampoline(context, &*it))
332       continue;
333     it->module->LoadExtensionCode(context, require_native);
334     EnsureExtensionNamespaceIsReadOnly(context, it->name);
335   }
336 }
337
338 v8::Handle<v8::Context> XWalkModuleSystem::GetV8Context() {
339   return v8::Local<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
340 }
341
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)
347       return true;
348
349     auto entry_it = std::find(
350         it->entry_points.begin(), it->entry_points.end(), entry);
351     if (entry_it != it->entry_points.end()) {
352       return true;
353     }
354   }
355   return false;
356 }
357
358 void XWalkModuleSystem::DeleteExtensionModules() {
359   for (ExtensionModules::iterator it = extension_modules_.begin();
360        it != extension_modules_.end(); ++it) {
361     delete it->module;
362   }
363   extension_modules_.clear();
364 }
365
366 // static
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();
374
375   ExtensionModuleEntry* entry = static_cast<ExtensionModuleEntry*>(ptr);
376
377   if (!entry)
378     return;
379
380   v8::Handle<v8::Context> context = isolate->GetCurrentContext();
381
382   DeleteAccessorForEntryPoint(context, entry->name);
383
384   auto it = entry->entry_points.begin();
385   for (; it != entry->entry_points.end(); ++it) {
386     DeleteAccessorForEntryPoint(context, *it);
387   }
388
389   XWalkModuleSystem* module_system = GetModuleSystemFromContext(context);
390   v8::Handle<v8::FunctionTemplate> require_native_template =
391       v8::Local<v8::FunctionTemplate>::New(
392           isolate,
393           module_system->require_native_template_);
394
395   XWalkExtensionModule* module = entry->module;
396   module->LoadExtensionCode(module_system->GetV8Context(),
397                             require_native_template->GetFunction());
398
399   module_system->EnsureExtensionNamespaceIsReadOnly(context, entry->name);
400 }
401
402 // static
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>());
409
410   std::vector<std::string> path;
411   SplitString(entry_point, '.', &path);
412   path.pop_back();
413
414   std::string error;
415   return GetObjectForPath(isolate->GetCurrentContext(), path, &error);
416 }
417
418 // static
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())
425     return;
426
427   info.GetReturnValue().Set(holder.As<v8::Object>()->Get(property));
428 }
429
430 // static
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())
438     return;
439
440   holder.As<v8::Object>()->Set(property, value);
441 }
442
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) {
449 }
450
451 XWalkModuleSystem::ExtensionModuleEntry::~ExtensionModuleEntry() {
452 }
453
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();
463 }
464
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.
469 //
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());
475   {
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())
481         break;
482       it->use_trampoline = false;
483       ++it;
484     }
485   }
486
487   // NOTE: Special Case for Security Reason
488   // xwalk module should not be trampolined even it does not have any children.
489   {
490     ExtensionModules::iterator it = extension_modules_.begin();
491     while (it != extension_modules_.end()) {
492       if ("xwalk" == (*it).name) {
493         it->use_trampoline = false;
494         break;
495       }
496       ++it;
497     }
498   }
499 }
500
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();
507   path.pop_back();
508
509   std::string error;
510   v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
511   if (value->IsUndefined()) {
512     LOGGER(ERROR) << "Error retrieving object for " << extension_name << " : "
513                   << error;
514     return;
515   }
516
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),
521       v8::ReadOnly);
522 }
523
524 }  // namespace extensions