Add xwalk NativeModule and v8tools module for lifecycleTracker
authorSeungkeun Lee <sngn.lee@samsung.com>
Fri, 15 May 2015 04:06:04 +0000 (13:06 +0900)
committerSeungkeun Lee <sngn.lee@samsung.com>
Mon, 18 May 2015 07:13:50 +0000 (16:13 +0900)
Change-Id: I49e1f3704e0e18795ba3d0ae1ee54c2f89c9beda

src/bundle/CMakeLists.txt [changed mode: 0644->0755]
src/bundle/extension_module.cc [changed mode: 0644->0755]
src/bundle/extension_module.h [changed mode: 0644->0755]
src/bundle/extension_renderer_controller.cc
src/bundle/module_system.cc [changed mode: 0644->0755]
src/bundle/module_system.h [changed mode: 0644->0755]
src/bundle/xwalk_v8tools_module.cc [new file with mode: 0755]
src/bundle/xwalk_v8tools_module.h [new file with mode: 0755]

old mode 100644 (file)
new mode 100755 (executable)
index e46d1b8..81a8757
@@ -31,6 +31,7 @@ SET(TARGET_INJECTED_BUNDLE_SRCS
   ${BASE_SRCDIR}/bundle/extension_module.cc
   ${BASE_SRCDIR}/bundle/module_system.cc
   ${BASE_SRCDIR}/bundle/runtime_ipc_client.cc
+  ${BASE_SRCDIR}/bundle/xwalk_v8tools_module.cc
 )
 
 # Compiler Flags
old mode 100644 (file)
new mode 100755 (executable)
index f4020fb..c1e2b5b
@@ -266,7 +266,8 @@ v8::Handle<v8::Value> RunString(const std::string& code,
 
 }  // namespace
 
-void ExtensionModule::LoadExtensionCode(v8::Handle<v8::Context> context) {
+void ExtensionModule::LoadExtensionCode(
+    v8::Handle<v8::Context> context, v8::Handle<v8::Function> require_native) {
   instance_id_ = client_->CreateInstance(extension_name_, this);
 
   std::string exception;
@@ -284,9 +285,10 @@ void ExtensionModule::LoadExtensionCode(v8::Handle<v8::Context> context) {
       v8::Local<v8::ObjectTemplate>::New(context->GetIsolate(),
                                           object_template_);
 
-  const int argc = 1;
+  const int argc = 2;
   v8::Handle<v8::Value> argv[argc] = {
-    object_template->NewInstance()
+    object_template->NewInstance(),
+    require_native
   };
 
   v8::TryCatch try_catch;
old mode 100644 (file)
new mode 100755 (executable)
index 6a4eb07..daeebbb
@@ -34,7 +34,8 @@ class ExtensionModule : public ExtensionClient::InstanceHandler {
 
   // TODO(cmarcelo): Make this return a v8::Handle<v8::Object>, and
   // let the module system set it to the appropriated object.
-  void LoadExtensionCode(v8::Handle<v8::Context> context);
+  void LoadExtensionCode(v8::Handle<v8::Context> context,
+                         v8::Handle<v8::Function> require_native);
 
   std::string extension_name() const { return extension_name_; }
 
index a04c4fa..76406a4 100755 (executable)
@@ -12,6 +12,7 @@
 #include "bundle/extension_client.h"
 #include "bundle/extension_module.h"
 #include "bundle/module_system.h"
+#include "bundle/xwalk_v8tools_module.h"
 
 namespace wrt {
 
@@ -54,6 +55,9 @@ void ExtensionRendererController::DidCreateScriptContext(
   ModuleSystem::SetModuleSystemInContext(
       std::unique_ptr<ModuleSystem>(module_system), context);
 
+  module_system->RegisterNativeModule(
+        "v8tools", std::unique_ptr<NativeModule>(new XWalkV8ToolsModule));
+
   CreateExtensionModules(extensions_client_.get(), module_system);
   module_system->Initialize();
 }
old mode 100644 (file)
new mode 100755 (executable)
index fe18b14..b5f39d4
@@ -25,6 +25,39 @@ const int kModuleSystemEmbedderDataIndex = 8;
 // pointer back to XWalkExtensionModule.
 const char* kWrtModuleSystem = "kWrtModuleSystem";
 
+void RequireNativeCallback(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::ReturnValue<v8::Value> result(info.GetReturnValue());
+
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Handle<v8::Object> data = info.Data().As<v8::Object>();
+  v8::Handle<v8::Value> module_system_value =
+      data->Get(v8::String::NewFromUtf8(isolate, kWrtModuleSystem));
+  if (module_system_value.IsEmpty() || module_system_value->IsUndefined()) {
+    LOGGER(ERROR) << "Trying to use requireNative from already "
+                  << "destroyed module system!";
+    return;
+  }
+
+  ModuleSystem* module_system = static_cast<ModuleSystem*>(
+      module_system_value.As<v8::External>()->Value());
+
+  if (info.Length() < 1) {
+    // TODO(cmarcelo): Throw appropriate exception or warning.
+    result.SetUndefined();
+    return;
+  }
+  v8::Handle<v8::Object> object =
+      module_system->RequireNative(*v8::String::Utf8Value(info[0]));
+  if (object.IsEmpty()) {
+    // TODO(cmarcelo): Throw appropriate exception or warning.
+    result.SetUndefined();
+    return;
+  }
+  result.Set(object);
+}
+
 }  // namespace
 
 ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context) {
@@ -35,16 +68,25 @@ ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context) {
   v8::Handle<v8::Object> function_data = v8::Object::New(isolate);
   function_data->Set(v8::String::NewFromUtf8(isolate, kWrtModuleSystem),
                      v8::External::New(isolate, this));
+  v8::Handle<v8::FunctionTemplate> require_native_template =
+      v8::FunctionTemplate::New(isolate, RequireNativeCallback, function_data);
 
   function_data_.Reset(isolate, function_data);
+  require_native_template_.Reset(isolate, require_native_template);
 }
 
 ModuleSystem::~ModuleSystem() {
   DeleteExtensionModules();
+  auto it = native_modules_.begin();
+  for ( ; it != native_modules_.end(); ++it) {
+    delete it->second;
+  }
+  native_modules_.clear();
 
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::HandleScope handle_scope(isolate);
 
+  require_native_template_.Reset();
   function_data_.Reset();
   v8_context_.Reset();
 }
@@ -98,6 +140,15 @@ void ModuleSystem::RegisterExtensionModule(
       ExtensionModuleEntry(extension_name, module.release(), entry_points));
 }
 
+void ModuleSystem::RegisterNativeModule(
+    const std::string& name, std::unique_ptr<NativeModule> module) {
+  if (native_modules_.find(name) != native_modules_.end()) {
+    return;
+  }
+  native_modules_[name] = module.release();
+}
+
+
 namespace {
 
 v8::Handle<v8::Value> EnsureTargetObjectForTrampoline(
@@ -246,10 +297,24 @@ bool ModuleSystem::InstallTrampoline(v8::Handle<v8::Context> context,
   return true;
 }
 
+v8::Handle<v8::Object> ModuleSystem::RequireNative(
+    const std::string& name) {
+  NativeModuleMap::iterator it = native_modules_.find(name);
+  if (it == native_modules_.end())
+    return v8::Handle<v8::Object>();
+  return it->second->NewInstance();
+}
+
 void ModuleSystem::Initialize() {
   v8::Isolate* isolate = v8::Isolate::GetCurrent();
   v8::HandleScope handle_scope(isolate);
   v8::Handle<v8::Context> context = GetV8Context();
+  v8::Handle<v8::FunctionTemplate> require_native_template =
+      v8::Local<v8::FunctionTemplate>::New(isolate, require_native_template_);
+  v8::Handle<v8::Function> require_native =
+      require_native_template->GetFunction();
+
+
 
   MarkModulesWithTrampoline();
 
@@ -257,7 +322,7 @@ void ModuleSystem::Initialize() {
   for (; it != extension_modules_.end(); ++it) {
     if (it->use_trampoline && InstallTrampoline(context, &*it))
       continue;
-    it->module->LoadExtensionCode(context);
+    it->module->LoadExtensionCode(context, require_native);
     EnsureExtensionNamespaceIsReadOnly(context, it->name);
   }
 }
@@ -313,9 +378,15 @@ void ModuleSystem::LoadExtensionForTrampoline(
   }
 
   ModuleSystem* module_system = GetModuleSystemFromContext(context);
+  v8::Handle<v8::FunctionTemplate> require_native_template =
+      v8::Local<v8::FunctionTemplate>::New(
+          isolate,
+          module_system->require_native_template_);
+
 
   ExtensionModule* module = entry->module;
-  module->LoadExtensionCode(module_system->GetV8Context());
+  module->LoadExtensionCode(module_system->GetV8Context(),
+                            require_native_template->GetFunction());
 
   module_system->EnsureExtensionNamespaceIsReadOnly(context, entry->name);
 }
old mode 100644 (file)
new mode 100755 (executable)
index c1a8cdd..173e612
 #include <vector>
 #include <string>
 #include <memory>
+#include <map>
 
 namespace wrt {
 
 class ExtensionModule;
 
+// Interface used to expose objects via the requireNative() function in JS API
+// code. Native modules should be registered with the module system.
+class NativeModule {
+ public:
+  virtual v8::Handle<v8::Object> NewInstance() = 0;
+  virtual ~NativeModule() {}
+};
+
+
 class ModuleSystem {
  public:
   explicit ModuleSystem(v8::Handle<v8::Context> context);
@@ -30,6 +40,9 @@ class ModuleSystem {
 
   void RegisterExtensionModule(std::unique_ptr<ExtensionModule> module,
                                const std::vector<std::string>& entry_points);
+  void RegisterNativeModule(const std::string& name,
+                            std::unique_ptr<NativeModule> module);
+  v8::Handle<v8::Object> RequireNative(const std::string& name);
 
   void Initialize();
 
@@ -86,7 +99,10 @@ class ModuleSystem {
 
   typedef std::vector<ExtensionModuleEntry> ExtensionModules;
   ExtensionModules extension_modules_;
+  typedef std::map<std::string, NativeModule*> NativeModuleMap;
+  NativeModuleMap native_modules_;
 
+  v8::Persistent<v8::FunctionTemplate> require_native_template_;
   v8::Persistent<v8::Object> function_data_;
 
   // Points back to the current context, used when native wants to callback
diff --git a/src/bundle/xwalk_v8tools_module.cc b/src/bundle/xwalk_v8tools_module.cc
new file mode 100755 (executable)
index 0000000..6e7626f
--- /dev/null
@@ -0,0 +1,92 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "bundle/xwalk_v8tools_module.h"
+
+#include <v8/v8.h>
+
+#include "common/logger.h"
+
+namespace wrt {
+
+namespace {
+
+void ForceSetPropertyCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  if (info.Length() != 3 || !info[0]->IsObject() || !info[1]->IsString()) {
+    return;
+  }
+  info[0].As<v8::Object>()->ForceSet(info[1], info[2]);
+}
+
+void LifecycleTrackerCleanup(
+    const v8::WeakCallbackData<v8::Object,
+                               v8::Persistent<v8::Object> >& data) {
+  v8::Isolate* isolate = data.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Object> tracker = data.GetValue();
+  v8::Handle<v8::Value> function =
+      tracker->Get(v8::String::NewFromUtf8(isolate, "destructor"));
+
+  if (function.IsEmpty() || !function->IsFunction()) {
+    LOGGER(WARN) << "Destructor function not set for LifecycleTracker.";
+    data.GetParameter()->Reset();
+    delete data.GetParameter();
+    return;
+  }
+
+  v8::Handle<v8::Context> context = v8::Context::New(isolate);
+
+  v8::TryCatch try_catch;
+  v8::Handle<v8::Function>::Cast(function)->Call(context->Global(), 0, NULL);
+  if (try_catch.HasCaught())
+    LOGGER(WARN) << "Exception when running LifecycleTracker destructor";
+
+  data.GetParameter()->Reset();
+  delete data.GetParameter();
+}
+
+void LifecycleTracker(const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Persistent<v8::Object>* tracker =
+      new v8::Persistent<v8::Object>(isolate, v8::Object::New(isolate));
+  tracker->SetWeak(tracker, &LifecycleTrackerCleanup);
+
+  info.GetReturnValue().Set(*tracker);
+}
+
+}  // namespace
+
+XWalkV8ToolsModule::XWalkV8ToolsModule() {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+  v8::Handle<v8::ObjectTemplate> object_template = v8::ObjectTemplate::New();
+
+  // TODO(cmarcelo): Use Template::Set() function that takes isolate, once we
+  // update the Chromium (and V8) version.
+  object_template->Set(v8::String::NewFromUtf8(isolate, "forceSetProperty"),
+                       v8::FunctionTemplate::New(
+                          isolate, ForceSetPropertyCallback));
+  object_template->Set(v8::String::NewFromUtf8(isolate, "lifecycleTracker"),
+                       v8::FunctionTemplate::New(isolate, LifecycleTracker));
+
+  object_template_.Reset(isolate, object_template);
+}
+
+XWalkV8ToolsModule::~XWalkV8ToolsModule() {
+  object_template_.Reset();
+}
+
+v8::Handle<v8::Object> XWalkV8ToolsModule::NewInstance() {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::EscapableHandleScope handle_scope(isolate);
+  v8::Handle<v8::ObjectTemplate> object_template =
+      v8::Local<v8::ObjectTemplate>::New(isolate, object_template_);
+  return handle_scope.Escape(object_template->NewInstance());
+}
+
+}  // namespace wrt
diff --git a/src/bundle/xwalk_v8tools_module.h b/src/bundle/xwalk_v8tools_module.h
new file mode 100755 (executable)
index 0000000..d330547
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WRT_BUNDLE_XWALK_V8TOOLS_MODULE_H_
+#define WRT_BUNDLE_XWALK_V8TOOLS_MODULE_H_
+
+#include "bundle/module_system.h"
+
+namespace wrt {
+
+// This module provides extra JS functions that help writing JS API code for
+// extensions, for example: allowing setting a read-only property of an object.
+class XWalkV8ToolsModule : public NativeModule {
+ public:
+  XWalkV8ToolsModule();
+  ~XWalkV8ToolsModule() override;
+
+ private:
+  v8::Handle<v8::Object> NewInstance() override;
+
+  v8::Persistent<v8::ObjectTemplate> object_template_;
+};
+
+}  // namespace wrt
+
+#endif  // WRT_BUNDLE_XWALK_V8TOOLS_MODULE_H_
\ No newline at end of file