Implements InjectedBundle and handlers of ExtensionServer
authorWonYoung Choi <wy80.choi@samsung.com>
Fri, 24 Apr 2015 10:34:54 +0000 (19:34 +0900)
committerWonYoung Choi <wy80.choi@samsung.com>
Fri, 24 Apr 2015 10:41:46 +0000 (19:41 +0900)
Change-Id: I0b002ed4eea90771019091bcd24130184ec4aa7d

28 files changed:
packaging/wrt.spec
src/bundle/CMakeLists.txt
src/bundle/extension_client.cc [new file with mode: 0755]
src/bundle/extension_client.h [new file with mode: 0755]
src/bundle/extension_client_test.cc [new file with mode: 0644]
src/bundle/extension_module.cc [new file with mode: 0644]
src/bundle/extension_module.h [new file with mode: 0644]
src/bundle/extension_renderer_controller.cc [new file with mode: 0755]
src/bundle/extension_renderer_controller.h [changed mode: 0644->0755]
src/bundle/injected_bundle.cc
src/bundle/module_system.cc [new file with mode: 0644]
src/bundle/module_system.h [new file with mode: 0644]
src/common/CMakeLists.txt
src/common/constants.cc
src/common/constants.h
src/common/dbus_client.cc
src/common/dbus_client.h
src/common/dbus_server.cc
src/common/dbus_server.h
src/common/profiler.cc [new file with mode: 0644]
src/common/profiler.h [new file with mode: 0644]
src/common/string_utils.cc
src/common/string_utils.h
src/extension/CMakeLists.txt
src/extension/extension_server.cc
src/extension/extension_server.h
src/runtime/runtime.cc
src/runtime/runtime.h

index 520efa9..7efc609 100755 (executable)
@@ -84,5 +84,6 @@ rm -fr %{buildroot}
 %files
 %attr(755,root,root) %{_bindir}/wrt
 %attr(755,root,root) %{_bindir}/wrt-extension
+%attr(755,root,root) %{_bindir}/wrt-extension-client-test
 %attr(644,root,root) %{_datadir}/edje/wrt/wrt.edj
 %attr(644,root,root) %{_libdir}/libwrt-injected-bundle.so
index 5545362..38f7e48 100644 (file)
@@ -5,6 +5,9 @@
 # Dependencies
 PKG_CHECK_MODULES(TARGET_INJECTED_BUNDLE_DEPS REQUIRED
   dlog
+  glib-2.0
+  gio-2.0
+  uuid
   elementary
   chromium-efl
 )
@@ -23,6 +26,10 @@ SET(TARGET_INJECTED_BUNDLE_LIBS
 # Source Files
 SET(TARGET_INJECTED_BUNDLE_SRCS
   ${BASE_SRCDIR}/bundle/injected_bundle.cc
+  ${BASE_SRCDIR}/bundle/extension_renderer_controller.cc
+  ${BASE_SRCDIR}/bundle/extension_client.cc
+  ${BASE_SRCDIR}/bundle/extension_module.cc
+  ${BASE_SRCDIR}/bundle/module_system.cc
 )
 
 INCLUDE_DIRECTORIES(${TARGET_INJECTED_BUNDLE_INCS})
@@ -49,3 +56,18 @@ INSTALL(TARGETS ${TARGET_INJECTED_BUNDLE}
 )
 
 
+# Build Executable for Test
+SET(TARGET_EXTENSION_TEST "wrt-extension-client-test")
+INCLUDE_DIRECTORIES(
+  ${BASE_SRCDIR}
+  ${TARGET_INJECTED_BUNDLE_INCS})
+ADD_EXECUTABLE(${TARGET_EXTENSION_TEST}
+  ${BASE_SRCDIR}/bundle/extension_client.cc
+  ${BASE_SRCDIR}/bundle/extension_client_test.cc)
+TARGET_LINK_LIBRARIES(${TARGET_EXTENSION_TEST}
+  ${TARGET_COMMON_STATIC}
+  ${TARGET_INJECTED_BUNDLE_LIBS}
+  "-ldl"
+)
+
+INSTALL(TARGETS ${TARGET_EXTENSION_TEST} DESTINATION bin)
\ No newline at end of file
diff --git a/src/bundle/extension_client.cc b/src/bundle/extension_client.cc
new file mode 100755 (executable)
index 0000000..726db8e
--- /dev/null
@@ -0,0 +1,153 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright 2015 Samsung Electronics Co, Ltd. 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/extension_client.h"
+
+#include <glib.h>
+#include <gio/gio.h>
+#include <string>
+
+#include "common/logger.h"
+#include "common/constants.h"
+#include "common/profiler.h"
+#include "common/string_utils.h"
+
+namespace wrt {
+
+ExtensionClient::ExtensionClient() {
+}
+
+ExtensionClient::~ExtensionClient() {
+  auto it = extension_apis_.begin();
+  for ( ; it != extension_apis_.end(); ++it) {
+    delete it->second;
+  }
+}
+
+std::string ExtensionClient::CreateInstance(
+    const std::string& extension_name, InstanceHandler* handler) {
+  GVariant* value = dbus_extension_client_.Call(
+      kDBusInterfaceNameForExtension, kMethodCreateInstance,
+      g_variant_new("(s)", extension_name.c_str()),
+      G_VARIANT_TYPE("(s)"));
+
+  if (!value) {
+    LoggerE("Failed to create instance for extension %s",
+            extension_name.c_str());
+    return std::string();
+  }
+
+  gchar* instance_id;
+  g_variant_get(value, "(&s)", &instance_id);
+
+  std::string ret(instance_id);
+  handlers_[ret] = handler;
+
+  return ret;
+}
+
+void ExtensionClient::DestroyInstance(const std::string& instance_id) {
+  GVariant* value = dbus_extension_client_.Call(
+      kDBusInterfaceNameForExtension, kMethodDestroyInstance,
+      g_variant_new("(s)", instance_id.c_str()),
+      G_VARIANT_TYPE("(s)"));
+
+  if (!value) {
+    LoggerE("Failed to destroy instance %s", instance_id.c_str());
+    return;
+  }
+
+  auto it = handlers_.find(instance_id);
+  if (it != handlers_.end()) {
+    handlers_.erase(it);
+  }
+}
+
+void ExtensionClient::PostMessageToNative(
+    const std::string& instance_id, const std::string& msg) {
+  GVariant* value = dbus_extension_client_.Call(
+      kDBusInterfaceNameForExtension, kMethodPostMessage,
+      g_variant_new("(ss)", instance_id.c_str(), msg.c_str()),
+      NULL);
+}
+
+std::string ExtensionClient::SendSyncMessageToNative(
+    const std::string& instance_id, const std::string& msg) {
+  GVariant* value = dbus_extension_client_.Call(
+      kDBusInterfaceNameForExtension, kMethodSendSyncMessage,
+      g_variant_new("(ss)", instance_id.c_str(), msg.c_str()),
+      G_VARIANT_TYPE("(s)"));
+
+  if (!value) {
+    LoggerE("Failed to send synchronous message to ExtensionServer.");
+    return std::string();
+  }
+
+  gchar* reply;
+  g_variant_get(value, "(&s)", &reply);
+
+  return std::string(reply);
+}
+
+void ExtensionClient::Initialize(const std::string& uuid) {
+  // Connect to DBusServer for ExtensionServer
+  if (dbus_extension_client_.ConnectByName(
+          uuid + "." + std::string(kDBusNameForExtension))) {
+    using std::placeholders::_1;
+    using std::placeholders::_2;
+    dbus_extension_client_.SetSignalCallback(
+        kDBusInterfaceNameForExtension,
+        std::bind(&ExtensionClient::HandleSignal, this, _1, _2));
+  } else {
+    LoggerE("Failed to connect to the dbus server for Extension.");
+    return;
+  }
+
+  // get extensions from ExtensionServer
+  GVariant* value = dbus_extension_client_.Call(
+      kDBusInterfaceNameForExtension, kMethodGetExtensions,
+      NULL,
+      G_VARIANT_TYPE("(a(ssas))"));
+
+  if (!value) {
+    LoggerE("Failed to get extension list from ExtensionServer.");
+    return;
+  }
+
+  gchar* name;
+  gchar* jsapi;
+  gchar* entry_point;
+  GVariantIter *it;
+  GVariantIter* entry_it;
+
+  g_variant_get(value, "(a(ssas))", &it);
+  while (g_variant_iter_loop(it, "(ssas)", &name, &jsapi, &entry_it)) {
+    ExtensionCodePoints* code = new ExtensionCodePoints;
+    code->api = std::string(jsapi);
+    while (g_variant_iter_loop(entry_it, "s", &entry_point)) {
+      code->entry_points.push_back(std::string(entry_point));
+    }
+    extension_apis_.insert(std::make_pair(std::string(name), code));
+  }
+}
+
+void ExtensionClient::HandleSignal(
+    const std::string& signal_name, GVariant* parameters) {
+  if (signal_name == kSignalOnMessageToJS) {
+    gchar* instance_id;
+    gchar* msg;
+    g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
+    auto it = handlers_.find(instance_id);
+    if (it != handlers_.end()) {
+      InstanceHandler* handler = it->second;
+      if (handler) {
+        handler->HandleMessageFromNative(msg);
+      }
+    }
+  }
+}
+
+}  // namespace wrt
diff --git a/src/bundle/extension_client.h b/src/bundle/extension_client.h
new file mode 100755 (executable)
index 0000000..887f13f
--- /dev/null
@@ -0,0 +1,59 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright 2015 Samsung Electronics Co, Ltd. 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_EXTENSION_CLIENT_H_
+#define WRT_BUNDLE_EXTENSION_CLIENT_H_
+
+#include <string>
+#include <memory>
+#include <map>
+#include <vector>
+
+#include "common/dbus_client.h"
+
+namespace wrt {
+
+class ExtensionClient {
+ public:
+  struct InstanceHandler {
+    virtual void HandleMessageFromNative(const std::string& msg) = 0;
+   protected:
+    ~InstanceHandler() {}
+  };
+
+  ExtensionClient();
+  virtual ~ExtensionClient();
+
+  std::string CreateInstance(const std::string& extension_name,
+                             InstanceHandler* handler);
+  void DestroyInstance(const std::string& instance_id);
+
+  void PostMessageToNative(const std::string& instance_id,
+                           const std::string& msg);
+  std::string SendSyncMessageToNative(const std::string& instance_id,
+                                      const std::string& msg);
+
+  void Initialize(const std::string& uuid);
+
+  struct ExtensionCodePoints {
+    std::string api;
+    std::vector<std::string> entry_points;
+  };
+
+  typedef std::map<std::string, ExtensionCodePoints*> ExtensionAPIMap;
+  const ExtensionAPIMap& extension_apis() const { return extension_apis_; }
+
+ private:
+  void HandleSignal(const std::string& signal_name, GVariant* parameters);
+
+  ExtensionAPIMap extension_apis_;
+  std::map<std::string, InstanceHandler*> handlers_;
+  DBusClient dbus_extension_client_;
+};
+
+}  // namespace wrt
+
+#endif  // WRT_BUNDLE_EXTENSION_CLIENT_H_
diff --git a/src/bundle/extension_client_test.cc b/src/bundle/extension_client_test.cc
new file mode 100644 (file)
index 0000000..fae82a0
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <stdio.h>
+#include <iostream>
+
+#include "bundle/extension_client.h"
+
+using namespace std;
+using namespace wrt;
+
+int main(int argc, char* argv[]) {
+
+  if (argc < 2) {
+    fprintf(stderr, "uuid is requried.\n");
+  }
+
+  ExtensionClient extension_client;
+
+  extension_client.Initialize(argv[1]);
+
+  string instance_id = extension_client.CreateInstance("tizen", NULL);
+  extension_client.DestroyInstance(instance_id);
+  cout << instance_id << endl;
+
+  return 0;
+}
diff --git a/src/bundle/extension_module.cc b/src/bundle/extension_module.cc
new file mode 100644 (file)
index 0000000..a00d383
--- /dev/null
@@ -0,0 +1,396 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright 2015 Samsung Electronics Co, Ltd. 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/extension_module.h"
+
+#include <stdio.h>
+#include <stdarg.h>
+#include <v8/v8.h>
+
+#include <vector>
+
+#include "common/logger.h"
+#include "bundle/extension_client.h"
+#include "bundle/module_system.h"
+
+// The arraysize(arr) macro returns the # of elements in an array arr.
+// The expression is a compile-time constant, and therefore can be
+// used in defining new arrays, for example.  If you use arraysize on
+// a pointer by mistake, you will get a compile-time error.
+//
+// One caveat is that arraysize() doesn't accept any array of an
+// anonymous type or a type defined inside a function.  In these rare
+// cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below.  This is
+// due to a limitation in C++'s template system.  The limitation might
+// eventually be removed, but it hasn't happened yet.
+
+// This template function declaration is used in defining arraysize.
+// Note that the function doesn't need an implementation, as we only
+// use its type.
+template <typename T, size_t N>
+char (&ArraySizeHelper(T (&array)[N]))[N];
+
+#define arraysize(array) (sizeof(ArraySizeHelper(array)))
+
+namespace wrt {
+
+namespace {
+
+// This is the key used in the data object passed to our callbacks to store a
+// pointer back to kWrtExtensionModule.
+const char* kWrtExtensionModule = "kWrtExtensionModule";
+
+}  // namespace
+
+ExtensionModule::ExtensionModule(ExtensionClient* client,
+                                 ModuleSystem* module_system,
+                                 const std::string& extension_name,
+                                 const std::string& extension_code)
+    : extension_name_(extension_name),
+      extension_code_(extension_code),
+      client_(client),
+      module_system_(module_system) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+  v8::Handle<v8::Object> function_data = v8::Object::New(isolate);
+  function_data->Set(v8::String::NewFromUtf8(isolate, kWrtExtensionModule),
+                     v8::External::New(isolate, this));
+
+  v8::Handle<v8::ObjectTemplate> object_template =
+      v8::ObjectTemplate::New(isolate);
+  // TODO(cmarcelo): Use Template::Set() function that takes isolate, once we
+  // update the Chromium (and V8) version.
+  object_template->Set(
+      v8::String::NewFromUtf8(isolate, "postMessage"),
+      v8::FunctionTemplate::New(isolate, PostMessageCallback, function_data));
+  object_template->Set(
+      v8::String::NewFromUtf8(isolate, "sendSyncMessage"),
+      v8::FunctionTemplate::New(
+          isolate, SendSyncMessageCallback, function_data));
+  object_template->Set(
+      v8::String::NewFromUtf8(isolate, "setMessageListener"),
+      v8::FunctionTemplate::New(
+          isolate, SetMessageListenerCallback, function_data));
+
+  function_data_.Reset(isolate, function_data);
+  object_template_.Reset(isolate, object_template);
+}
+
+ExtensionModule::~ExtensionModule() {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+
+  // Deleting the data will disable the functions, they'll return early. We do
+  // this because it might be the case that the JS objects we created outlive
+  // this object (getting references from inside an iframe and then destroying
+  // the iframe), even if we destroy the references we have.
+  v8::Handle<v8::Object> function_data =
+      v8::Local<v8::Object>::New(isolate, function_data_);
+  function_data->Delete(v8::String::NewFromUtf8(isolate,
+                                                kWrtExtensionModule));
+
+  object_template_.Reset();
+  function_data_.Reset();
+  message_listener_.Reset();
+
+  if (!instance_id_.empty())
+    client_->DestroyInstance(instance_id_);
+}
+
+namespace {
+
+std::string CodeToEnsureNamespace(const std::string& extension_name) {
+  std::string result;
+  size_t pos = 0;
+  while (true) {
+    pos = extension_name.find('.', pos);
+    if (pos == std::string::npos) {
+      result += extension_name + " = {};";
+      break;
+    }
+    std::string ns = extension_name.substr(0, pos);
+    result += ns + " = " + ns + " || {}; ";
+    pos++;
+  }
+  return result;
+}
+
+// Templatized backend for StringPrintF/StringAppendF. This does not finalize
+// the va_list, the caller is expected to do that.
+template <class StringType>
+static void StringAppendVT(StringType* dst,
+                           const typename StringType::value_type* format,
+                           va_list ap) {
+  // First try with a small fixed size buffer.
+  // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
+  // and StringUtilTest.StringPrintfBounds.
+  typename StringType::value_type stack_buf[1024];
+
+  va_list ap_copy;
+  va_copy(ap_copy, ap);
+
+  int result = vsnprintf(stack_buf, arraysize(stack_buf), format, ap_copy);
+  va_end(ap_copy);
+
+  if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) {
+    // It fit.
+    dst->append(stack_buf, result);
+    return;
+  }
+
+  // Repeatedly increase buffer size until it fits.
+  int mem_length = arraysize(stack_buf);
+  while (true) {
+    if (result < 0) {
+      if (errno != 0 && errno != EOVERFLOW)
+        return;
+      // Try doubling the buffer size.
+      mem_length *= 2;
+    } else {
+      // We need exactly "result + 1" characters.
+      mem_length = result + 1;
+    }
+
+    if (mem_length > 32 * 1024 * 1024) {
+      // That should be plenty, don't try anything larger.  This protects
+      // against huge allocations when using vsnprintfT implementations that
+      // return -1 for reasons other than overflow without setting errno.
+      LOGE("Unable to printf the requested string due to size.");
+      return;
+    }
+
+    std::vector<typename StringType::value_type> mem_buf(mem_length);
+
+    // NOTE: You can only use a va_list once.  Since we're in a while loop, we
+    // need to make a new copy each time so we don't use up the original.
+    va_copy(ap_copy, ap);
+    result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy);
+    va_end(ap_copy);
+
+    if ((result >= 0) && (result < mem_length)) {
+      // It fit.
+      dst->append(&mem_buf[0], result);
+      return;
+    }
+  }
+}
+
+std::string StringPrintf(const char* format, ...) {
+  va_list ap;
+  va_start(ap, format);
+  std::string result;
+  StringAppendVT(&result, format, ap);
+  va_end(ap);
+  return result;
+}
+
+// Wrap API code into a callable form that takes extension object as parameter.
+std::string WrapAPICode(const std::string& extension_code,
+                        const std::string& extension_name) {
+  // We take care here to make sure that line numbering for api_code after
+  // wrapping doesn't change, so that syntax errors point to the correct line.
+
+  return StringPrintf(
+      "var %s; (function(extension, requireNative) { "
+      "extension.internal = {};"
+      "extension.internal.sendSyncMessage = extension.sendSyncMessage;"
+      "delete extension.sendSyncMessage;"
+      "var exports = {}; (function() {'use strict'; %s\n})();"
+      "%s = exports; });",
+      CodeToEnsureNamespace(extension_name).c_str(),
+      extension_code.c_str(),
+      extension_name.c_str());
+}
+
+std::string ExceptionToString(const v8::TryCatch& try_catch) {
+  std::string str;
+  v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
+  v8::String::Utf8Value exception(try_catch.Exception());
+  v8::Local<v8::Message> message(try_catch.Message());
+  if (message.IsEmpty()) {
+    str.append(StringPrintf("%s\n", *exception));
+  } else {
+    v8::String::Utf8Value filename(message->GetScriptResourceName());
+    int linenum = message->GetLineNumber();
+    int colnum = message->GetStartColumn();
+    str.append(StringPrintf(
+        "%s:%i:%i %s\n", *filename, linenum, colnum, *exception));
+    v8::String::Utf8Value sourceline(message->GetSourceLine());
+    str.append(StringPrintf("%s\n", *sourceline));
+  }
+  return str;
+}
+
+v8::Handle<v8::Value> RunString(const std::string& code,
+                                std::string* exception) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::EscapableHandleScope handle_scope(isolate);
+  v8::Handle<v8::String> v8_code(
+      v8::String::NewFromUtf8(isolate, code.c_str()));
+
+  v8::TryCatch try_catch;
+  try_catch.SetVerbose(true);
+
+  v8::Handle<v8::Script> script(v8::Script::Compile(v8_code));
+  if (try_catch.HasCaught()) {
+    *exception = ExceptionToString(try_catch);
+    return handle_scope.Escape(
+        v8::Local<v8::Primitive>(v8::Undefined(isolate)));
+  }
+
+  v8::Local<v8::Value> result = script->Run();
+  if (try_catch.HasCaught()) {
+    *exception = ExceptionToString(try_catch);
+    return handle_scope.Escape(
+        v8::Local<v8::Primitive>(v8::Undefined(isolate)));
+  }
+
+  return handle_scope.Escape(result);
+}
+
+}  // namespace
+
+void ExtensionModule::LoadExtensionCode(v8::Handle<v8::Context> context) {
+  instance_id_ = client_->CreateInstance(extension_name_, this);
+
+  std::string exception;
+  std::string wrapped_api_code = WrapAPICode(extension_code_, extension_name_);
+  v8::Handle<v8::Value> result = RunString(wrapped_api_code, &exception);
+
+  if (!result->IsFunction()) {
+    LoggerE("Couldn't load JS API code for %s: %s",
+            extension_name_.c_str(), exception.c_str());
+    return;
+  }
+  v8::Handle<v8::Function> callable_api_code =
+      v8::Handle<v8::Function>::Cast(result);
+  v8::Handle<v8::ObjectTemplate> object_template =
+      v8::Local<v8::ObjectTemplate>::New(context->GetIsolate(),
+                                          object_template_);
+
+  const int argc = 1;
+  v8::Handle<v8::Value> argv[argc] = {
+    object_template->NewInstance()
+  };
+
+  v8::TryCatch try_catch;
+  try_catch.SetVerbose(true);
+  callable_api_code->Call(context->Global(), argc, argv);
+  if (try_catch.HasCaught()) {
+    LoggerE("Exception while loading JS API code for %s: %s",
+            extension_name_.c_str(), ExceptionToString(try_catch).c_str());
+  }
+}
+
+void ExtensionModule::HandleMessageFromNative(const std::string& msg) {
+  if (message_listener_.IsEmpty())
+    return;
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+  v8::Handle<v8::Context> context = module_system_->GetV8Context();
+  v8::Context::Scope context_scope(context);
+
+  v8::Handle<v8::Value> args[] = {
+      v8::String::NewFromUtf8(isolate, msg.c_str()) };
+
+  v8::Handle<v8::Function> message_listener =
+      v8::Local<v8::Function>::New(isolate, message_listener_);
+
+  v8::TryCatch try_catch;
+  message_listener->Call(context->Global(), 1, args);
+  if (try_catch.HasCaught())
+    LoggerE("Exception when running message listener: %s",
+            ExceptionToString(try_catch).c_str());
+}
+
+// static
+void ExtensionModule::PostMessageCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::ReturnValue<v8::Value> result(info.GetReturnValue());
+  ExtensionModule* module = GetExtensionModule(info);
+  if (!module || info.Length() != 1) {
+    result.Set(false);
+    return;
+  }
+
+  v8::Handle<v8::Context> context = info.GetIsolate()->GetCurrentContext();
+  v8::String::Utf8Value value(info[0]->ToString());
+
+  // CHECK(module->instance_id_);
+  module->client_->PostMessageToNative(module->instance_id_,
+                                       std::string(*value));
+  result.Set(true);
+}
+
+// static
+void ExtensionModule::SendSyncMessageCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::ReturnValue<v8::Value> result(info.GetReturnValue());
+  ExtensionModule* module = GetExtensionModule(info);
+  if (!module || info.Length() != 1) {
+    result.Set(false);
+    return;
+  }
+
+  v8::Handle<v8::Context> context = info.GetIsolate()->GetCurrentContext();
+  v8::String::Utf8Value value(info[0]->ToString());
+
+  // CHECK(module->instance_id_);
+  std::string reply =
+      module->client_->SendSyncMessageToNative(module->instance_id_,
+                                               std::string(*value));
+
+  // If we tried to send a message to an instance that became invalid,
+  // then reply will be NULL.
+  if (!reply.empty()) {
+    result.Set(v8::String::NewFromUtf8(info.GetIsolate(), reply.c_str()));
+  }
+}
+
+// static
+void ExtensionModule::SetMessageListenerCallback(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::ReturnValue<v8::Value> result(info.GetReturnValue());
+  ExtensionModule* module = GetExtensionModule(info);
+  if (!module || info.Length() != 1) {
+    result.Set(false);
+    return;
+  }
+
+  if (!info[0]->IsFunction() && !info[0]->IsUndefined()) {
+    LoggerE("Trying to set message listener with invalid value.");
+    result.Set(false);
+    return;
+  }
+
+  v8::Isolate* isolate = info.GetIsolate();
+  if (info[0]->IsUndefined())
+    module->message_listener_.Reset();
+  else
+    module->message_listener_.Reset(isolate, info[0].As<v8::Function>());
+
+  result.Set(true);
+}
+
+// static
+ExtensionModule* ExtensionModule::GetExtensionModule(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  v8::Isolate* isolate = info.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+
+  v8::Local<v8::Object> data = info.Data().As<v8::Object>();
+  v8::Local<v8::Value> module =
+      data->Get(v8::String::NewFromUtf8(isolate, kWrtExtensionModule));
+  if (module.IsEmpty() || module->IsUndefined()) {
+    LoggerE("Trying to use extension from already destroyed context!");
+    return NULL;
+  }
+  // CHECK(module->IsExternal());
+  return static_cast<ExtensionModule*>(module.As<v8::External>()->Value());
+}
+
+}  // namespace wrt
diff --git a/src/bundle/extension_module.h b/src/bundle/extension_module.h
new file mode 100644 (file)
index 0000000..dca58de
--- /dev/null
@@ -0,0 +1,77 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright 2015 Samsung Electronics Co, Ltd. 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_EXTENSION_MODULE_H_
+#define WRT_BUNDLE_EXTENSION_MODULE_H_
+
+#include <v8/v8.h>
+
+#include <memory>
+#include <string>
+
+#include "bundle/extension_client.h"
+
+namespace wrt {
+
+class ModuleSystem;
+
+// Responsible for running the JS code of a Extension. This includes
+// creating and exposing an 'extension' object for the execution context of
+// the extension JS code.
+//
+// We'll create one ExtensionModule per extension/frame pair, so
+// there'll be a set of different modules per v8::Context.
+class ExtensionModule : public ExtensionClient::InstanceHandler {
+ public:
+  ExtensionModule(ExtensionClient* client,
+                  ModuleSystem* module_system,
+                  const std::string& extension_name,
+                  const std::string& extension_code);
+  virtual ~ExtensionModule();
+
+  // 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);
+
+  std::string extension_name() const { return extension_name_; }
+
+ private:
+  // ExtensionClient::InstanceHandler implementation.
+  virtual void HandleMessageFromNative(const std::string& msg);
+
+  // Callbacks for JS functions available in 'extension' object.
+  static void PostMessageCallback(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void SendSyncMessageCallback(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+  static void SetMessageListenerCallback(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+
+  static ExtensionModule* GetExtensionModule(
+      const v8::FunctionCallbackInfo<v8::Value>& info);
+
+  // Template for the 'extension' object exposed to the extension JS code.
+  v8::Persistent<v8::ObjectTemplate> object_template_;
+
+  // This JS object contains a pointer back to the ExtensionModule, it is
+  // set as data for the function callbacks.
+  v8::Persistent<v8::Object> function_data_;
+
+  // Function to be called when the extension sends a message to its JS code.
+  // This value is registered by using 'extension.setMessageListener()'.
+  v8::Persistent<v8::Function> message_listener_;
+
+  std::string extension_name_;
+  std::string extension_code_;
+
+  ExtensionClient* client_;
+  ModuleSystem* module_system_;
+  std::string instance_id_;
+};
+
+}  // namespace wrt
+
+#endif  // WRT_BUNDLE_EXTENSION_MODULE_H_
diff --git a/src/bundle/extension_renderer_controller.cc b/src/bundle/extension_renderer_controller.cc
new file mode 100755 (executable)
index 0000000..73f746b
--- /dev/null
@@ -0,0 +1,72 @@
+// Copyright 2015 Samsung Electronics Co, Ltd. 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/extension_renderer_controller.h"
+
+#include <v8/v8.h>
+#include <string>
+#include <utility>
+
+#include "common/logger.h"
+#include "bundle/extension_client.h"
+#include "bundle/extension_module.h"
+#include "bundle/module_system.h"
+
+namespace wrt {
+
+namespace {
+
+void CreateExtensionModules(ExtensionClient* client,
+                            ModuleSystem* module_system) {
+  const ExtensionClient::ExtensionAPIMap& extensions =
+      client->extension_apis();
+  auto it = extensions.begin();
+  for (; it != extensions.end(); ++it) {
+    ExtensionClient::ExtensionCodePoints* codepoint = it->second;
+    if (codepoint->api.empty())
+      continue;
+
+    std::unique_ptr<ExtensionModule> module(
+        new ExtensionModule(client, module_system, it->first, codepoint->api));
+    module_system->RegisterExtensionModule(
+        std::move(module), codepoint->entry_points);
+  }
+}
+
+}  // namespace
+
+ExtensionRendererController& ExtensionRendererController::GetInstance() {
+  static ExtensionRendererController instance;
+  return instance;
+}
+
+ExtensionRendererController::ExtensionRendererController()
+    : extensions_client_(new ExtensionClient()) {
+}
+
+ExtensionRendererController::~ExtensionRendererController() {
+}
+
+void ExtensionRendererController::DidCreateScriptContext(
+    v8::Handle<v8::Context> context) {
+  ModuleSystem* module_system = new ModuleSystem(context);
+  ModuleSystem::SetModuleSystemInContext(
+      std::unique_ptr<ModuleSystem>(module_system), context);
+
+  CreateExtensionModules(extensions_client_.get(), module_system);
+  module_system->Initialize();
+}
+
+void ExtensionRendererController::WillReleaseScriptContext(
+    v8::Handle<v8::Context> context) {
+  v8::Context::Scope contextScope(context);
+  ModuleSystem::ResetModuleSystemFromContext(context);
+}
+
+void ExtensionRendererController::InitializeExtensions(
+    const std::string& uuid) {
+  extensions_client_->Initialize(uuid);
+}
+
+}  // namespace wrt
old mode 100644 (file)
new mode 100755 (executable)
index 1908ab9..7cfc23d
@@ -1,3 +1,35 @@
 // Copyright 2015 Samsung Electronics Co, Ltd. 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_EXTENSION_RENDERER_CONTROLLER_H_
+#define WRT_BUNDLE_EXTENSION_RENDERER_CONTROLLER_H_
+
+#include <v8/v8.h>
+#include <string>
+#include <memory>
+
+namespace wrt {
+
+class ExtensionClient;
+
+class ExtensionRendererController {
+ public:
+  static ExtensionRendererController& GetInstance();
+
+  void DidCreateScriptContext(v8::Handle<v8::Context> context);
+  void WillReleaseScriptContext(v8::Handle<v8::Context> context);
+
+  void InitializeExtensions(const std::string& uuid);
+
+ private:
+  ExtensionRendererController();
+  virtual ~ExtensionRendererController();
+
+ private:
+  std::unique_ptr<ExtensionClient> extensions_client_;
+};
+
+}  // namespace wrt
+
+#endif  // WRT_BUNDLE_EXTENSION_RENDERER_CONTROLLER_H_
index 1aa6305..95af893 100644 (file)
@@ -7,6 +7,7 @@
 #include <string>
 
 #include "common/logger.h"
+#include "bundle/extension_renderer_controller.h"
 
 extern "C" void DynamicSetWidgetInfo(int widget_id) {
   LoggerD("InjectedBundle::DynamicSetWidgetInfo !!");
@@ -20,11 +21,21 @@ extern "C" void DynamicPluginStartSession(int widget_id,
                                           const char* theme,
                                           const char* base_url) {
   LoggerD("InjectedBundle::DynamicPluginStartSession !!");
+
+  wrt::ExtensionRendererController& controller =
+      wrt::ExtensionRendererController::GetInstance();
+  // TODO(wy80.choi): Temporarily, uuid is passed as theme arguments.
+  controller.InitializeExtensions(theme);
+  controller.DidCreateScriptContext(context);
 }
 
 extern "C" void DynamicPluginStopSession(
     int widget_id, v8::Handle<v8::Context> context) {
   LoggerD("InjectedBundle::DynamicPluginStopSession !!");
+
+  wrt::ExtensionRendererController& controller =
+      wrt::ExtensionRendererController::GetInstance();
+  controller.WillReleaseScriptContext(context);
 }
 
 extern "C" void DynamicUrlParsing(
diff --git a/src/bundle/module_system.cc b/src/bundle/module_system.cc
new file mode 100644 (file)
index 0000000..0880870
--- /dev/null
@@ -0,0 +1,448 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright 2015 Samsung Electronics Co, Ltd. 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/module_system.h"
+
+#include <v8/v8.h>
+#include <algorithm>
+
+#include "common/logger.h"
+#include "bundle/extension_module.h"
+
+namespace wrt {
+
+namespace {
+
+// Index used to set embedder data into v8::Context, so we can get from a
+// context to its corresponding module. Index chosen to not conflict with
+// WebCore::V8ContextEmbedderDataField in V8PerContextData.h.
+const int kModuleSystemEmbedderDataIndex = 8;
+
+// This is the key used in the data object passed to our callbacks to store a
+// pointer back to XWalkExtensionModule.
+const char* kWrtModuleSystem = "kWrtModuleSystem";
+
+ModuleSystem* GetModuleSystem(
+    const v8::FunctionCallbackInfo<v8::Value>& info) {
+  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 =
+      data->Get(v8::String::NewFromUtf8(isolate, kWrtModuleSystem));
+  if (module_system.IsEmpty() || module_system->IsUndefined()) {
+    LoggerE("ModuleSystem is not defined.");
+    return NULL;
+  }
+
+  return static_cast<ModuleSystem*>(
+      module_system.As<v8::External>()->Value());
+}
+
+}  // namespace
+
+ModuleSystem::ModuleSystem(v8::Handle<v8::Context> context) {
+  v8::Isolate* isolate = context->GetIsolate();
+  v8_context_.Reset(isolate, context);
+
+  v8::HandleScope handle_scope(isolate);
+  v8::Handle<v8::Object> function_data = v8::Object::New(isolate);
+  function_data->Set(v8::String::NewFromUtf8(isolate, kWrtModuleSystem),
+                     v8::External::New(isolate, this));
+
+  function_data_.Reset(isolate, function_data);
+}
+
+ModuleSystem::~ModuleSystem() {
+  DeleteExtensionModules();
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+
+  function_data_.Reset();
+  v8_context_.Reset();
+}
+
+// static
+ModuleSystem* ModuleSystem::GetModuleSystemFromContext(
+    v8::Handle<v8::Context> context) {
+  return reinterpret_cast<ModuleSystem*>(
+      context->GetAlignedPointerFromEmbedderData(
+          kModuleSystemEmbedderDataIndex));
+}
+
+// static
+void ModuleSystem::SetModuleSystemInContext(
+    std::unique_ptr<ModuleSystem> module_system,
+    v8::Handle<v8::Context> context) {
+  context->SetAlignedPointerInEmbedderData(kModuleSystemEmbedderDataIndex,
+                                           module_system.release());
+}
+
+// static
+void ModuleSystem::ResetModuleSystemFromContext(
+    v8::Handle<v8::Context> context) {
+  delete GetModuleSystemFromContext(context);
+  SetModuleSystemInContext(std::unique_ptr<ModuleSystem>(), context);
+}
+
+void ModuleSystem::RegisterExtensionModule(
+    std::unique_ptr<ExtensionModule> module,
+    const std::vector<std::string>& entry_points) {
+  const std::string& extension_name = module->extension_name();
+  if (ContainsEntryPoint(extension_name)) {
+    LoggerE("Can't register Extension Module named for extension "
+            "'%s' in the Module System because name was already registered.",
+            extension_name.c_str());
+    return;
+  }
+
+  std::vector<std::string>::const_iterator it = entry_points.begin();
+  for (; it != entry_points.end(); ++it) {
+    if (ContainsEntryPoint(*it)) {
+      LoggerE("Can't register Extension Module named for extension "
+              "'%s' in the Module System because another extension "
+              "has the entry point '%s'.",
+              extension_name.c_str(), (*it).c_str());
+      return;
+    }
+  }
+
+  extension_modules_.push_back(
+      ExtensionModuleEntry(extension_name, module.release(), entry_points));
+}
+
+namespace {
+
+v8::Handle<v8::Value> EnsureTargetObjectForTrampoline(
+    v8::Handle<v8::Context> context, const std::vector<std::string>& path,
+    std::string* error) {
+  v8::Handle<v8::Object> object = context->Global();
+  v8::Isolate* isolate = context->GetIsolate();
+
+  std::vector<std::string>::const_iterator it = path.begin();
+  for (; it != path.end(); ++it) {
+    v8::Handle<v8::String> part =
+        v8::String::NewFromUtf8(isolate, it->c_str());
+    v8::Handle<v8::Value> value = object->Get(part);
+
+    if (value->IsUndefined()) {
+      v8::Handle<v8::Object> next_object = v8::Object::New(isolate);
+      object->Set(part, next_object);
+      object = next_object;
+      continue;
+    }
+
+    if (!value->IsObject()) {
+      *error = "the property '" + *it + "' in the path is undefined";
+      return v8::Undefined(isolate);
+    }
+
+    object = value.As<v8::Object>();
+  }
+  return object;
+}
+
+v8::Handle<v8::Value> GetObjectForPath(v8::Handle<v8::Context> context,
+                                       const std::vector<std::string>& path,
+                                       std::string* error) {
+  v8::Handle<v8::Object> object = context->Global();
+  v8::Isolate* isolate = context->GetIsolate();
+
+  std::vector<std::string>::const_iterator it = path.begin();
+  for (; it != path.end(); ++it) {
+    v8::Handle<v8::String> part =
+        v8::String::NewFromUtf8(isolate, it->c_str());
+    v8::Handle<v8::Value> value = object->Get(part);
+
+    if (!value->IsObject()) {
+      *error = "the property '" + *it + "' in the path is undefined";
+      return v8::Undefined(isolate);
+    }
+
+    object = value.As<v8::Object>();
+  }
+  return object;
+}
+
+}  // namespace
+
+template <typename STR>
+void SplitString(const STR& str, const typename STR::value_type s,
+                 std::vector<STR>* r) {
+  r->clear();
+  size_t last = 0;
+  size_t c = str.size();
+  for (size_t i = 0; i <= c; ++i) {
+    if (i == c || str[i] == s) {
+      STR tmp(str, last, i - last);
+      if (i != c || !r->empty() || !tmp.empty())
+        r->push_back(tmp);
+      last = i + 1;
+    }
+  }
+}
+
+bool ModuleSystem::SetTrampolineAccessorForEntryPoint(
+    v8::Handle<v8::Context> context,
+    const std::string& entry_point,
+    v8::Local<v8::External> user_data) {
+  std::vector<std::string> path;
+  SplitString(entry_point, '.', &path);
+  std::string basename = path.back();
+  path.pop_back();
+
+  std::string error;
+  v8::Handle<v8::Value> value =
+      EnsureTargetObjectForTrampoline(context, path, &error);
+  if (value->IsUndefined()) {
+    LoggerE("Error installing trampoline for %s: %s.",
+            entry_point.c_str(), error.c_str());
+    return false;
+  }
+
+  v8::Isolate* isolate = context->GetIsolate();
+  v8::Local<v8::Array> params = v8::Array::New(isolate);
+  v8::Local<v8::String> entry =
+      v8::String::NewFromUtf8(isolate, entry_point.c_str());
+  params->Set(v8::Integer::New(isolate, 0), user_data);
+  params->Set(v8::Integer::New(isolate, 1), entry);
+
+  // FIXME(cmarcelo): ensure that trampoline is readonly.
+  value.As<v8::Object>()->SetAccessor(
+      v8::String::NewFromUtf8(isolate, basename.c_str()),
+      TrampolineCallback, TrampolineSetterCallback, params);
+  return true;
+}
+
+// static
+bool ModuleSystem::DeleteAccessorForEntryPoint(
+    v8::Handle<v8::Context> context,
+    const std::string& entry_point) {
+  std::vector<std::string> path;
+  SplitString(entry_point, '.', &path);
+  std::string basename = path.back();
+  path.pop_back();
+
+  std::string error;
+  v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
+  if (value->IsUndefined()) {
+    LoggerE("Error retrieving object for %s: %s.",
+            entry_point.c_str(), error.c_str());
+    return false;
+  }
+
+  value.As<v8::Object>()->ForceDelete(
+      v8::String::NewFromUtf8(context->GetIsolate(), basename.c_str()));
+  return true;
+}
+
+bool ModuleSystem::InstallTrampoline(v8::Handle<v8::Context> context,
+                                     ExtensionModuleEntry* entry) {
+  v8::Local<v8::External> entry_ptr =
+      v8::External::New(context->GetIsolate(), entry);
+  bool ret = SetTrampolineAccessorForEntryPoint(context, entry->name,
+                                                entry_ptr);
+  if (!ret) {
+    LoggerE("Error installing trampoline for '%s'.", entry->name.c_str());
+    return false;
+  }
+
+  auto it = entry->entry_points.begin();
+  for (; it != entry->entry_points.end(); ++it) {
+    ret = SetTrampolineAccessorForEntryPoint(context, *it, entry_ptr);
+    if (!ret) {
+      // TODO(vcgomes): Remove already added trampolines when it fails.
+      LoggerE("Error installing trampoline for '%s'.", entry->name.c_str());
+      return false;
+    }
+  }
+  return true;
+}
+
+void ModuleSystem::Initialize() {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope handle_scope(isolate);
+  v8::Handle<v8::Context> context = GetV8Context();
+
+  MarkModulesWithTrampoline();
+
+  auto it = extension_modules_.begin();
+  for (; it != extension_modules_.end(); ++it) {
+    if (it->use_trampoline && InstallTrampoline(context, &*it))
+      continue;
+    it->module->LoadExtensionCode(context);
+    EnsureExtensionNamespaceIsReadOnly(context, it->name);
+  }
+}
+
+v8::Handle<v8::Context> ModuleSystem::GetV8Context() {
+  return v8::Local<v8::Context>::New(v8::Isolate::GetCurrent(), v8_context_);
+}
+
+bool ModuleSystem::ContainsEntryPoint(
+    const std::string& entry) {
+  auto it = extension_modules_.begin();
+  for (; it != extension_modules_.end(); ++it) {
+    if (it->name == entry)
+      return true;
+
+    auto entry_it = std::find(
+        it->entry_points.begin(), it->entry_points.end(), entry);
+    if (entry_it != it->entry_points.end()) {
+      return true;
+    }
+  }
+  return false;
+}
+
+void ModuleSystem::DeleteExtensionModules() {
+  for (ExtensionModules::iterator it = extension_modules_.begin();
+       it != extension_modules_.end(); ++it) {
+    delete it->module;
+  }
+  extension_modules_.clear();
+}
+
+// static
+void ModuleSystem::LoadExtensionForTrampoline(
+    v8::Isolate* isolate,
+    v8::Local<v8::Value> data) {
+  v8::Local<v8::Array> params = data.As<v8::Array>();
+  void* ptr = params->Get(
+      v8::Integer::New(isolate, 0)).As<v8::External>()->Value();
+
+  ExtensionModuleEntry* entry = static_cast<ExtensionModuleEntry*>(ptr);
+
+  if (!entry)
+    return;
+
+  v8::Handle<v8::Context> context = isolate->GetCurrentContext();
+
+  DeleteAccessorForEntryPoint(context, entry->name);
+
+  auto it = entry->entry_points.begin();
+  for (; it != entry->entry_points.end(); ++it) {
+    DeleteAccessorForEntryPoint(context, *it);
+  }
+
+  ModuleSystem* module_system = GetModuleSystemFromContext(context);
+
+  ExtensionModule* module = entry->module;
+  module->LoadExtensionCode(module_system->GetV8Context());
+
+  module_system->EnsureExtensionNamespaceIsReadOnly(context, entry->name);
+}
+
+// static
+v8::Handle<v8::Value> ModuleSystem::RefetchHolder(
+    v8::Isolate* isolate,
+    v8::Local<v8::Value> data) {
+  v8::Local<v8::Array> params = data.As<v8::Array>();
+  const std::string entry_point = *v8::String::Utf8Value(
+      params->Get(v8::Integer::New(isolate, 1)).As<v8::String>());
+
+  std::vector<std::string> path;
+  SplitString(entry_point, '.', &path);
+  path.pop_back();
+
+  std::string error;
+  return GetObjectForPath(isolate->GetCurrentContext(), path, &error);
+}
+
+// static
+void ModuleSystem::TrampolineCallback(
+    v8::Local<v8::String> property,
+    const v8::PropertyCallbackInfo<v8::Value>& info) {
+  ModuleSystem::LoadExtensionForTrampoline(info.GetIsolate(), info.Data());
+  v8::Handle<v8::Value> holder = RefetchHolder(info.GetIsolate(), info.Data());
+  if (holder->IsUndefined())
+    return;
+
+  info.GetReturnValue().Set(holder.As<v8::Object>()->Get(property));
+}
+
+// static
+void ModuleSystem::TrampolineSetterCallback(
+    v8::Local<v8::String> property,
+    v8::Local<v8::Value> value,
+    const v8::PropertyCallbackInfo<void>& info) {
+  ModuleSystem::LoadExtensionForTrampoline(info.GetIsolate(), info.Data());
+  v8::Handle<v8::Value> holder = RefetchHolder(info.GetIsolate(), info.Data());
+  if (holder->IsUndefined())
+    return;
+
+  holder.As<v8::Object>()->Set(property, value);
+}
+
+ModuleSystem::ExtensionModuleEntry::ExtensionModuleEntry(
+    const std::string& name,
+    ExtensionModule* module,
+    const std::vector<std::string>& entry_points) :
+    name(name), module(module), use_trampoline(true),
+    entry_points(entry_points) {
+}
+
+ModuleSystem::ExtensionModuleEntry::~ExtensionModuleEntry() {
+}
+
+// Returns whether the name of first is prefix of the second, considering "."
+// character as a separator. So "a" is prefix of "a.b" but not of "ab".
+bool ModuleSystem::ExtensionModuleEntry::IsPrefix(
+    const ExtensionModuleEntry& first,
+    const ExtensionModuleEntry& second) {
+  const std::string& p = first.name;
+  const std::string& s = second.name;
+  return s.size() > p.size() && s[p.size()] == '.'
+      && std::mismatch(p.begin(), p.end(), s.begin()).first == p.end();
+}
+
+// Mark the extension modules that we want to setup "trampolines"
+// instead of loading the code directly. The current algorithm is very
+// simple: we only create trampolines for extensions that are leaves
+// in the namespace tree.
+//
+// For example, if there are two extensions "tizen" and "tizen.time",
+// the first one won't be marked with trampoline, but the second one
+// will. So we'll only load code for "tizen" extension.
+void ModuleSystem::MarkModulesWithTrampoline() {
+  std::sort(extension_modules_.begin(), extension_modules_.end());
+
+  ExtensionModules::iterator it = extension_modules_.begin();
+  while (it != extension_modules_.end()) {
+    it = std::adjacent_find(it, extension_modules_.end(),
+                            &ExtensionModuleEntry::IsPrefix);
+    if (it == extension_modules_.end())
+      break;
+    it->use_trampoline = false;
+    ++it;
+  }
+}
+
+void ModuleSystem::EnsureExtensionNamespaceIsReadOnly(
+    v8::Handle<v8::Context> context,
+    const std::string& extension_name) {
+  std::vector<std::string> path;
+  SplitString(extension_name, '.', &path);
+  std::string basename = path.back();
+  path.pop_back();
+
+  std::string error;
+  v8::Handle<v8::Value> value = GetObjectForPath(context, path, &error);
+  if (value->IsUndefined()) {
+    LoggerE("Error retrieving object for %s: %s.",
+            extension_name.c_str(), error.c_str());
+    return;
+  }
+
+  v8::Handle<v8::String> v8_extension_name(
+      v8::String::NewFromUtf8(context->GetIsolate(), basename.c_str()));
+  value.As<v8::Object>()->ForceSet(
+      v8_extension_name, value.As<v8::Object>()->Get(v8_extension_name),
+      v8::ReadOnly);
+}
+
+}  // namespace wrt
diff --git a/src/bundle/module_system.h b/src/bundle/module_system.h
new file mode 100644 (file)
index 0000000..c1a8cdd
--- /dev/null
@@ -0,0 +1,100 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright 2015 Samsung Electronics Co, Ltd. 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_MODULE_SYSTEM_H_
+#define WRT_BUNDLE_MODULE_SYSTEM_H_
+
+#include <v8/v8.h>
+#include <vector>
+#include <string>
+#include <memory>
+
+namespace wrt {
+
+class ExtensionModule;
+
+class ModuleSystem {
+ public:
+  explicit ModuleSystem(v8::Handle<v8::Context> context);
+  ~ModuleSystem();
+
+  static ModuleSystem* GetModuleSystemFromContext(
+      v8::Handle<v8::Context> context);
+  static void SetModuleSystemInContext(
+      std::unique_ptr<ModuleSystem> module_system,
+      v8::Handle<v8::Context> context);
+  static void ResetModuleSystemFromContext(v8::Handle<v8::Context> context);
+
+  void RegisterExtensionModule(std::unique_ptr<ExtensionModule> module,
+                               const std::vector<std::string>& entry_points);
+
+  void Initialize();
+
+  v8::Handle<v8::Context> GetV8Context();
+
+ private:
+  struct ExtensionModuleEntry {
+    ExtensionModuleEntry(const std::string& name, ExtensionModule* module,
+                         const std::vector<std::string>& entry_points);
+    ~ExtensionModuleEntry();
+    std::string name;
+    ExtensionModule* module;
+    bool use_trampoline;
+    std::vector<std::string> entry_points;
+    bool operator<(const ExtensionModuleEntry& other) const {
+      return name < other.name;
+    }
+
+    static bool IsPrefix(const ExtensionModuleEntry& first,
+                         const ExtensionModuleEntry& second);
+  };
+
+  bool SetTrampolineAccessorForEntryPoint(
+      v8::Handle<v8::Context> context,
+      const std::string& entry_point,
+      v8::Local<v8::External> user_data);
+
+  static bool DeleteAccessorForEntryPoint(v8::Handle<v8::Context> context,
+                                          const std::string& entry_point);
+
+  bool InstallTrampoline(v8::Handle<v8::Context> context,
+                         ExtensionModuleEntry* entry);
+
+  static void TrampolineCallback(
+      v8::Local<v8::String> property,
+      const v8::PropertyCallbackInfo<v8::Value>& info);
+  static void TrampolineSetterCallback(
+      v8::Local<v8::String> property,
+      v8::Local<v8::Value> value,
+      const v8::PropertyCallbackInfo<void>& info);
+  static void LoadExtensionForTrampoline(
+      v8::Isolate* isolate,
+      v8::Local<v8::Value> data);
+  static v8::Handle<v8::Value> RefetchHolder(
+    v8::Isolate* isolate,
+    v8::Local<v8::Value> data);
+
+  bool ContainsEntryPoint(const std::string& entry_point);
+  void MarkModulesWithTrampoline();
+  void DeleteExtensionModules();
+
+  void EnsureExtensionNamespaceIsReadOnly(v8::Handle<v8::Context> context,
+                                          const std::string& extension_name);
+
+  typedef std::vector<ExtensionModuleEntry> ExtensionModules;
+  ExtensionModules extension_modules_;
+
+  v8::Persistent<v8::Object> function_data_;
+
+  // Points back to the current context, used when native wants to callback
+  // JavaScript. When WillReleaseScriptContext() is called, we dispose this
+  // persistent.
+  v8::Persistent<v8::Context> v8_context_;
+};
+
+}  // namespace wrt
+
+#endif  // WRT_BUNDLE_MODULE_SYSTEM_H_
index 0c5faa1..376f7b8 100644 (file)
@@ -21,6 +21,7 @@ SET(TARGET_COMMON_STATIC_SRCS
   ${BASE_SRCDIR}/common/command_line.cc
   ${BASE_SRCDIR}/common/dbus_server.cc
   ${BASE_SRCDIR}/common/dbus_client.cc
+  ${BASE_SRCDIR}/common/profiler.cc
 )
 
 INCLUDE_DIRECTORIES(${TARGET_COMMON_STATIC_INCS})
index fe49580..90402fb 100644 (file)
@@ -17,5 +17,11 @@ const char kMethodGetRuntimeVariable[] = "GetRuntimeVariable";
 
 const char kDBusNameForExtension[] = "Extension";
 const char kDBusInterfaceNameForExtension[] = "org.tizen.wrt.Extension";
+const char kMethodGetExtensions[] = "GetExtensions";
+const char kMethodCreateInstance[] = "CreateInstance";
+const char kMethodDestroyInstance[] = "DestroyInstance";
+const char kMethodSendSyncMessage[] = "SendSyncMessage";
+const char kMethodPostMessage[] = "PostMessage";
+const char kSignalOnMessageToJS[] = "OnMessageToJS";
 
 }  // namespace wrt
index 7895c73..c7856b2 100644 (file)
@@ -18,6 +18,12 @@ extern const char kMethodGetRuntimeVariable[];
 
 extern const char kDBusNameForExtension[];
 extern const char kDBusInterfaceNameForExtension[];
+extern const char kMethodGetExtensions[];
+extern const char kMethodCreateInstance[];
+extern const char kMethodDestroyInstance[];
+extern const char kMethodSendSyncMessage[];
+extern const char kMethodPostMessage[];
+extern const char kSignalOnMessageToJS[];
 
 }  // namespace wrt
 
index de5ff18..9d9f734 100644 (file)
@@ -8,12 +8,32 @@
 
 namespace wrt {
 
+namespace {
+
+void OnSignalReceived(GDBusConnection* /*connection*/,
+                      const gchar* /*sender_name*/,
+                      const gchar* /*object_path*/,
+                      const gchar* interface_name,
+                      const gchar* signal_name,
+                      GVariant* parameters,
+                      gpointer user_data) {
+  DBusClient* self = reinterpret_cast<DBusClient*>(user_data);
+  auto callback = self->GetSignalCallback(interface_name);
+  if (callback) {
+    callback(signal_name, parameters);
+  }
+}
+
+}  // namespace
+
 DBusClient::DBusClient()
-    : connection_(NULL) {
+    : connection_(NULL),
+      signal_subscription_id_(0) {
 }
 
 DBusClient::~DBusClient() {
   if (connection_) {
+    g_dbus_connection_signal_unsubscribe(connection_, signal_subscription_id_);
     g_dbus_connection_close_sync(connection_, NULL, NULL);
   }
 }
@@ -38,6 +58,11 @@ bool DBusClient::Connect(const std::string& address) {
     g_error_free(err);
     return false;
   }
+
+  signal_subscription_id_ = g_dbus_connection_signal_subscribe(
+      connection_, NULL, NULL, NULL, NULL, NULL, G_DBUS_SIGNAL_FLAGS_NONE,
+      OnSignalReceived, this, NULL);
+
   return true;
 }
 
@@ -69,4 +94,14 @@ GVariant* DBusClient::Call(const std::string& iface,
   return reply;
 }
 
+void DBusClient::SetSignalCallback(const std::string& iface,
+                                   SignalCallback func) {
+  signal_callbacks_[iface] = func;
+}
+
+DBusClient::SignalCallback
+DBusClient::GetSignalCallback(const std::string& iface) {
+  return signal_callbacks_[iface];
+}
+
 }  // namespace wrt
index c171ca7..a44d611 100644 (file)
@@ -9,11 +9,16 @@
 #include <gio/gio.h>
 
 #include <string>
+#include <functional>
+#include <map>
 
 namespace wrt {
 
 class DBusClient {
  public:
+  typedef std::function<void(const std::string& signal,
+                             GVariant* parameters)> SignalCallback;
+
   DBusClient();
   virtual ~DBusClient();
 
@@ -22,8 +27,14 @@ class DBusClient {
 
   GVariant* Call(const std::string& iface, const std::string& method,
                  GVariant* parameters, const GVariantType* reply_type);
+
+  void SetSignalCallback(const std::string& iface, SignalCallback func);
+  SignalCallback GetSignalCallback(const std::string& iface);
+
  private:
   GDBusConnection* connection_;
+  guint signal_subscription_id_;
+  std::map<std::string, SignalCallback> signal_callbacks_;
 };
 
 }  // namespace wrt
index c2569eb..50bc712 100644 (file)
@@ -10,7 +10,7 @@ namespace wrt {
 
 namespace {
 
-static void OnMethodCall(GDBusConnection* /*connection*/,
+static void OnMethodCall(GDBusConnection* connection,
                          const gchar* /*sender*/,
                          const gchar* /*object_path*/,
                          const gchar* interface_name,
@@ -25,11 +25,11 @@ static void OnMethodCall(GDBusConnection* /*connection*/,
   }
   auto callback = self->GetMethodCallback(interface_name);
   if (callback) {
-    callback(method_name, parameters, invocation);
+    callback(connection, method_name, parameters, invocation);
   }
 }
 
-static GVariant* OnGetProperty(GDBusConnection* /*connection*/,
+static GVariant* OnGetProperty(GDBusConnection* connection,
                                const gchar* /*sender*/,
                                const gchar* /*object_path*/,
                                const gchar* interface_name,
@@ -47,13 +47,13 @@ static GVariant* OnGetProperty(GDBusConnection* /*connection*/,
 
   GVariant* ret = NULL;
   if (callback) {
-    ret = callback(property_name);
+    ret = callback(connection, property_name);
   }
 
   return ret;
 }
 
-static gboolean OnSetProperty(GDBusConnection* /*connection*/,
+static gboolean OnSetProperty(GDBusConnection* connection,
                               const gchar* /*sender*/,
                               const gchar* /*object_path*/,
                               const gchar* interface_name,
@@ -72,7 +72,7 @@ static gboolean OnSetProperty(GDBusConnection* /*connection*/,
 
   gboolean ret = FALSE;
   if (callback) {
-    if (callback(property_name, value)) {
+    if (callback(connection, property_name, value)) {
       ret = TRUE;
     }
   }
@@ -204,6 +204,22 @@ void DBusServer::SetIntrospectionXML(const std::string& xml) {
   }
 }
 
+void DBusServer::SendSignal(GDBusConnection* connection,
+                            const std::string& iface,
+                            const std::string& signal_name,
+                            GVariant* parameters) {
+  GError* err = NULL;
+  gboolean ret = g_dbus_connection_emit_signal(
+      connection, NULL, "/",
+      iface.c_str(), signal_name.c_str(),
+      parameters, &err);
+  if (!ret) {
+    LoggerE("Failed to emit signal : '%s.%s'",
+            iface.c_str(), signal_name.c_str());
+    g_error_free(err);
+  }
+}
+
 void DBusServer::SetPeerCredentialsCallback(PeerCredentialsCallback func) {
   peer_credentials_callback_ = func;
 }
@@ -223,19 +239,23 @@ void DBusServer::SetPropertySetter(
   property_setters_[iface] = func;
 }
 
-DBusServer::PeerCredentialsCallback DBusServer::GetPeerCredentialsCallback() const {
+DBusServer::PeerCredentialsCallback
+DBusServer::GetPeerCredentialsCallback() const {
   return peer_credentials_callback_;
 }
 
-DBusServer::MethodCallback DBusServer::GetMethodCallback(const std::string& iface) {
+DBusServer::MethodCallback
+DBusServer::GetMethodCallback(const std::string& iface) {
   return method_callbacks_[iface];
 }
 
-DBusServer::PropertySetter DBusServer::GetPropertySetter(const std::string& iface) {
+DBusServer::PropertySetter
+DBusServer::GetPropertySetter(const std::string& iface) {
   return property_setters_[iface];
 }
 
-DBusServer::PropertyGetter DBusServer::GetPropertyGetter(const std::string& iface) {
+DBusServer::PropertyGetter
+DBusServer::GetPropertyGetter(const std::string& iface) {
   return property_getters_[iface];
 }
 
index 41451fe..5e0cf57 100644 (file)
@@ -16,13 +16,17 @@ namespace wrt {
 class DBusServer {
  public:
   typedef std::function<bool(GCredentials* creds)> PeerCredentialsCallback;
-  typedef std::function<void(const std::string& method_name,
+  typedef std::function<void(GDBusConnection* connection,
+                             const std::string& method_name,
                              GVariant* parameters,
                              GDBusMethodInvocation* invocation)> MethodCallback;
-  typedef std::function<GVariant*(const gchar*)> PropertyGetter;
-  typedef std::function<bool(const gchar*, GVariant*)> PropertySetter;
+  typedef std::function<GVariant*(GDBusConnection* connection,
+                                  const gchar* property)> PropertyGetter;
+  typedef std::function<bool(GDBusConnection* connection,
+                             const gchar* property,
+                             GVariant* value)> PropertySetter;
 
-  explicit DBusServer();
+  DBusServer();
   virtual ~DBusServer();
 
   void Start(const std::string& name);
@@ -32,6 +36,10 @@ class DBusServer {
   void SetIntrospectionXML(const std::string& xml);
   GDBusNodeInfo* GetIntrospectionNodeInfo() const { return node_info_; }
 
+  void SendSignal(GDBusConnection* connection,
+                  const std::string& iface, const std::string& signal_name,
+                  GVariant* parameters);
+
   void SetPeerCredentialsCallback(PeerCredentialsCallback func);
   void SetMethodCallback(const std::string& iface, MethodCallback func);
   void SetPropertyGetter(const std::string& iface, PropertyGetter func);
@@ -40,6 +48,7 @@ class DBusServer {
   MethodCallback GetMethodCallback(const std::string& iface);
   PropertySetter GetPropertySetter(const std::string& iface);
   PropertyGetter GetPropertyGetter(const std::string& iface);
+
  private:
   GDBusServer* server_;
   GDBusNodeInfo* node_info_;
diff --git a/src/common/profiler.cc b/src/common/profiler.cc
new file mode 100644 (file)
index 0000000..d9a2d13
--- /dev/null
@@ -0,0 +1,17 @@
+// Copyright 2015 Samsung Electronics Co, Ltd. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "common/profiler.h"
+
+#include "common/logger.h"
+#include "common/string_utils.h"
+
+namespace wrt {
+
+void PrintProfileLog(const char* func, const char* tag) {
+  LoggerD("[PROF] [%s] %s:%s", utils::GetCurrentMilliSeconds().c_str(),
+                               func, tag);
+}
+
+}  // namespace wrt
diff --git a/src/common/profiler.h b/src/common/profiler.h
new file mode 100644 (file)
index 0000000..2ee4713
--- /dev/null
@@ -0,0 +1,18 @@
+// Copyright 2015 Samsung Electronics Co, Ltd. 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_COMMON_PROFILER_H_
+#define WRT_COMMON_PROFILER_H_
+
+namespace wrt {
+
+#define PROFILE_START() PrintProfileLog(__FUNCTION__, "START");
+#define PROFILE_END() PrintProfileLog(__FUNCTION__, "END");
+#define PROFILE(x) PrintProfileLog(__FUNCTION__, x);
+
+void PrintProfileLog(const char* func, const char* tag);
+
+}  // namespace wrt
+
+#endif  // WRT_COMMON_PROFILER_H_
index df38552..3a2ba3b 100644 (file)
@@ -4,8 +4,12 @@
 
 #include "common/string_utils.h"
 
+#include <time.h>
+#include <math.h>
 #include <uuid/uuid.h>
 #include <string>
+#include <sstream>
+#include <iomanip>
 #include <algorithm>
 
 namespace wrt {
@@ -40,5 +44,14 @@ std::string ReplaceAll(const std::string& replace,
   return str;
 }
 
+std::string GetCurrentMilliSeconds() {
+  std::ostringstream ss;
+  struct timespec spec;
+  clock_gettime(CLOCK_REALTIME, &spec);
+  ss << spec.tv_sec << "." <<
+     std::setw(3) << std::setfill('0') << (round(spec.tv_nsec / 1.0e6));
+  return ss.str();
+}
+
 }  // namespace utils
 }  // namespace wrt
index d47f7d9..ea4d626 100644 (file)
@@ -15,6 +15,7 @@ bool StartsWith(const std::string& str, const std::string& sub);
 bool EndsWith(const std::string& str, const std::string& sub);
 std::string ReplaceAll(const std::string& replace,
                        const std::string& from, const std::string& to);
+std::string GetCurrentMilliSeconds();
 
 }  // namespace utils
 }  // namespace wrt
index ab4ff78..ce4d344 100644 (file)
@@ -4,6 +4,7 @@
 
 # Dependencies
 PKG_CHECK_MODULES(TARGET_EXTENSION_DEPS
+  uuid
   dlog
   glib-2.0
   gio-2.0
index fb7bab2..2a277c2 100644 (file)
@@ -11,6 +11,7 @@
 #include "common/logger.h"
 #include "common/constants.h"
 #include "common/file_utils.h"
+#include "common/string_utils.h"
 #include "extension/extension.h"
 
 namespace wrt {
@@ -25,23 +26,24 @@ const char kDBusIntrospectionXML[] =
   "    </method>"
   "    <method name='CreateInstance'>"
   "      <arg name='extension_name' type='s' direction='in' />"
-  "      <arg name='instance_id' type='s' direction='in' />"
+  "      <arg name='instance_id' type='s' direction='out' />"
   "    </method>"
   "    <method name='DestroyInstance'>"
   "      <arg name='instance_id' type='s' direction='in' />"
+  "      <arg name='instance_id' type='s' direction='out' />"
   "    </method>"
-  "    <method name='CallASync'>"
+  "    <method name='PostMessage'>"
   "      <arg name='instance_id' type='s' direction='in' />"
-  "      <arg name='body' type='s' direction='in' />"
+  "      <arg name='msg' type='s' direction='in' />"
   "    </method>"
-  "    <method name='CallSync'>"
+  "    <method name='SendSyncMessage'>"
   "      <arg name='instance_id' type='s' direction='in' />"
-  "      <arg name='body' type='s' direction='in' />"
+  "      <arg name='msg' type='s' direction='in' />"
   "      <arg name='reply' type='s' direction='out' />"
   "    </method>"
   "    <signal name='OnMessageToJS'>"
   "      <arg name='instance_id' type='s' />"
-  "      <arg name='body' type='s' />"
+  "      <arg name='msg' type='s' />"
   "    </signal>"
   "  </interface>"
   "</node>";
@@ -81,10 +83,11 @@ bool ExtensionServer::Start(const StringVector& paths) {
   using std::placeholders::_1;
   using std::placeholders::_2;
   using std::placeholders::_3;
+  using std::placeholders::_4;
   dbus_server_.SetIntrospectionXML(kDBusIntrospectionXML);
   dbus_server_.SetMethodCallback(
       kDBusInterfaceNameForExtension,
-      std::bind(&ExtensionServer::HandleDBusMethod, this, _1, _2, _3));
+      std::bind(&ExtensionServer::HandleDBusMethod, this, _1, _2, _3, _4));
   dbus_server_.Start(app_uuid_ + "." + std::string(kDBusNameForExtension));
 
   // Send 'ready' signal to Injected Bundle.
@@ -97,6 +100,7 @@ void ExtensionServer::RegisterExtension(const std::string& path) {
   Extension* ext = new Extension(path, this);
   if (!ext->Initialize() || !RegisterSymbols(ext)) {
     delete ext;
+    return;
   }
   extensions_[ext->name()] = ext;
 }
@@ -142,21 +146,9 @@ bool ExtensionServer::RegisterSymbols(Extension* extension) {
   return true;
 }
 
-void ExtensionServer::AddRuntimeVariable(const std::string& key,
-    const std::string& value) {
-  runtime_variables_.insert(std::make_pair(key, value));
-}
-
-void ExtensionServer::ClearRuntimeVariables() {
-  runtime_variables_.clear();
-}
-
 void ExtensionServer::GetRuntimeVariable(const char* key, char* value,
     size_t value_len) {
-  auto it = runtime_variables_.find(key);
-  if (it != runtime_variables_.end()) {
-    strncpy(value, it->second.c_str(), value_len);
-  }
+  // TODO(wy80.choi): DBus Call to Runtime to get runtime variable
 }
 
 void ExtensionServer::NotifyEPCreatedToRuntime() {
@@ -166,11 +158,176 @@ void ExtensionServer::NotifyEPCreatedToRuntime() {
       NULL);
 }
 
-void ExtensionServer::HandleDBusMethod(const std::string& method_name,
+void ExtensionServer::HandleDBusMethod(GDBusConnection* connection,
+                                       const std::string& method_name,
                                        GVariant* parameters,
                                        GDBusMethodInvocation* invocation) {
-  // TODO(wy80.choi): Handle DBus Method Calls from InjectedBundle
+  LoggerD("HandleDBusMethod (%s)", method_name.c_str());
+
+  if (method_name == kMethodGetExtensions) {
+    OnGetExtensions(invocation);
+  } else if (method_name == kMethodCreateInstance) {
+    gchar* extension_name;
+    g_variant_get(parameters, "(&s)", &extension_name);
+    OnCreateInstance(connection, extension_name, invocation);
+  } else if (method_name == kMethodDestroyInstance) {
+    gchar* instance_id;
+    g_variant_get(parameters, "(&s)", &instance_id);
+    OnDestroyInstance(instance_id, invocation);
+  } else if (method_name == kMethodSendSyncMessage) {
+    gchar* instance_id;
+    gchar* msg;
+    g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
+    OnSendSyncMessage(instance_id, msg, invocation);
+  } else if (method_name == kMethodPostMessage) {
+    gchar* instance_id;
+    gchar* msg;
+    g_variant_get(parameters, "(&s&s)", &instance_id, &msg);
+    OnPostMessage(instance_id, msg);
+  }
+}
+
+void ExtensionServer::OnGetExtensions(GDBusMethodInvocation* invocation) {
+  GVariantBuilder builder;
+  g_variant_builder_init(&builder, G_VARIANT_TYPE_ARRAY);
+
+  // build an array of extensions
+  auto it = extensions_.begin();
+  for ( ; it != extensions_.end(); ++it) {
+    Extension* ext = it->second;
+    // open container for extension
+    g_variant_builder_open(&builder, G_VARIANT_TYPE("(ssas)"));
+    g_variant_builder_add(&builder, "s", ext->name().c_str());
+    g_variant_builder_add(&builder, "s", ext->javascript_api().c_str());
+    // open container for entry_point
+    g_variant_builder_open(&builder, G_VARIANT_TYPE("as"));
+    auto it_entry = ext->entry_points().begin();
+    for ( ; it_entry != ext->entry_points().end(); ++it_entry) {
+      g_variant_builder_add(&builder, "s", (*it_entry).c_str());
+    }
+    // close container('as') for entry_point
+    g_variant_builder_close(&builder);
+    // close container('(ssas)') for extension
+    g_variant_builder_close(&builder);
+  }
+  GVariant* reply = g_variant_builder_end(&builder);
+  g_dbus_method_invocation_return_value(
+      invocation, g_variant_new_tuple(&reply, 1));
+}
+
+void ExtensionServer::OnCreateInstance(
+    GDBusConnection* connection, const std::string& extension_name,
+    GDBusMethodInvocation* invocation) {
+  std::string instance_id = utils::GenerateUUID();
+
+  // find extension with given the extension name
+  auto it = extensions_.find(extension_name);
+  if (it == extensions_.end()) {
+    LoggerE("Failed to find extension [%s]", extension_name.c_str());
+    g_dbus_method_invocation_return_error(
+        invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+        "Not found extension %s", extension_name.c_str());
+    return;
+  }
+
+  // create instance
+  ExtensionInstance* instance = it->second->CreateInstance();
+  if (!instance) {
+    LoggerE("Failed to create instance of extension [%s]",
+            extension_name.c_str());
+    g_dbus_method_invocation_return_error(
+        invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+        "Failed to create instance of extension %s", extension_name.c_str());
+    return;
+  }
+
+  // set callbacks
+  using std::placeholders::_1;
+  instance->SetSendSyncReplyCallback(
+      std::bind(&ExtensionServer::SyncReplyCallback, this, _1, invocation));
+  instance->SetPostMessageCallback(
+      std::bind(&ExtensionServer::PostMessageToJSCallback,
+                this, connection, instance_id, _1));
+
+  instances_[instance_id] = instance;
+  g_dbus_method_invocation_return_value(
+      invocation, g_variant_new("(s)", instance_id.c_str()));
+}
+
+void ExtensionServer::OnDestroyInstance(
+    const std::string& instance_id, GDBusMethodInvocation* invocation) {
+  // find instance with the given instance id
+  auto it = instances_.find(instance_id);
+  if (it == instances_.end()) {
+    LoggerE("Failed to find instance [%s]", instance_id.c_str());
+    g_dbus_method_invocation_return_error(
+        invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+        "Not found instance %s", instance_id.c_str());
+    return;
+  }
+
+  // destroy the instance
+  ExtensionInstance* instance = it->second;
+  delete instance;
+
+  instances_.erase(it);
+
+  g_dbus_method_invocation_return_value(
+      invocation, g_variant_new("(s)", instance_id.c_str()));
+}
+
+void ExtensionServer::OnSendSyncMessage(
+    const std::string& instance_id, const std::string& msg,
+    GDBusMethodInvocation* invocation) {
+  // find instance with the given instance id
+  auto it = instances_.find(instance_id);
+  if (it == instances_.end()) {
+    LoggerE("Failed to find instance [%s]", instance_id.c_str());
+    g_dbus_method_invocation_return_error(
+        invocation, G_DBUS_ERROR, G_DBUS_ERROR_FAILED,
+        "Not found instance %s", instance_id.c_str());
+    return;
+  }
+
+  ExtensionInstance* instance = it->second;
+  instance->HandleSyncMessage(msg);
+
+  // reponse will be sent by SyncReplyCallback()
 }
 
+// async
+void ExtensionServer::OnPostMessage(
+    const std::string& instance_id, const std::string& msg) {
+  auto it = instances_.find(instance_id);
+  if (it == instances_.end()) {
+    LoggerE("Failed to find instance [%s]", instance_id.c_str());
+    return;
+  }
+
+  ExtensionInstance* instance = it->second;
+  instance->HandleMessage(msg);
+}
+
+void ExtensionServer::SyncReplyCallback(
+    const std::string& reply, GDBusMethodInvocation* invocation) {
+  g_dbus_method_invocation_return_value(
+      invocation, g_variant_new("(s)", reply.c_str()));
+}
+
+void ExtensionServer::PostMessageToJSCallback(
+    GDBusConnection* connection, const std::string& instance_id,
+    const std::string& msg) {
+  if (!connection || g_dbus_connection_is_closed(connection)) {
+    LoggerE("Client connection is closed already.");
+    return;
+  }
+
+  dbus_server_.SendSignal(connection,
+                          kDBusInterfaceNameForExtension,
+                          kSignalOnMessageToJS,
+                          g_variant_new("(ss)",
+                                        instance_id.c_str(),
+                                        msg.c_str()));
+}
 
 }  // namespace wrt
index 23cf20a..a2edb83 100644 (file)
@@ -33,15 +33,34 @@ class ExtensionServer : public Extension::ExtensionDelegate {
   void RegisterSystemExtensions();
   bool RegisterSymbols(Extension* extension);
 
-  void AddRuntimeVariable(const std::string& key, const std::string& value);
   void GetRuntimeVariable(const char* key, char* value, size_t value_len);
-  void ClearRuntimeVariables();
 
   void NotifyEPCreatedToRuntime();
-  void HandleDBusMethod(const std::string& method_name,
+
+  void HandleDBusMethod(GDBusConnection* connection,
+                        const std::string& method_name,
                         GVariant* parameters,
                         GDBusMethodInvocation* invocation);
 
+  void OnGetExtensions(GDBusMethodInvocation* invocation);
+  void OnCreateInstance(GDBusConnection* connection,
+                        const std::string& extension_name,
+                        GDBusMethodInvocation* invocation);
+  void OnDestroyInstance(const std::string& instance_id,
+                         GDBusMethodInvocation* invocation);
+  void OnSendSyncMessage(const std::string& instance_id,
+                         const std::string& msg,
+                         GDBusMethodInvocation* invocation);
+  void OnPostMessage(const std::string& instance_id,
+                     const std::string& msg);
+
+  void SyncReplyCallback(const std::string& reply,
+                         GDBusMethodInvocation* invocation);
+
+  void PostMessageToJSCallback(GDBusConnection* connection,
+                               const std::string& instance_id,
+                               const std::string& msg);
+
   std::string app_uuid_;
   DBusServer dbus_server_;
   DBusClient dbus_runtime_client_;
@@ -49,9 +68,6 @@ class ExtensionServer : public Extension::ExtensionDelegate {
   typedef std::set<std::string> StringSet;
   StringSet extension_symbols_;
 
-  typedef std::map<std::string, std::string> StringMap;
-  StringMap runtime_variables_;
-
   typedef std::map<std::string, Extension*> ExtensionMap;
   ExtensionMap extensions_;
 
index 197c104..885ce1e 100755 (executable)
@@ -81,9 +81,10 @@ bool Runtime::OnCreate() {
   using std::placeholders::_1;
   using std::placeholders::_2;
   using std::placeholders::_3;
+  using std::placeholders::_4;
   dbus_server_.SetIntrospectionXML(kDBusIntrospectionXML);
   dbus_server_.SetMethodCallback(kDBusInterfaceNameForRuntime,
-    std::bind(&Runtime::HandleDBusMethod, this, _1, _2, _3));
+    std::bind(&Runtime::HandleDBusMethod, this, _1, _2, _3, _4));
   dbus_server_.Start(application_->uuid() +
                      "." + std::string(kDBusNameForRuntime));
 
@@ -131,7 +132,8 @@ void Runtime::OnLowMemory() {
   }
 }
 
-void Runtime::HandleDBusMethod(const std::string& method_name,
+void Runtime::HandleDBusMethod(GDBusConnection* connection,
+                               const std::string& method_name,
                                GVariant* parameters,
                                GDBusMethodInvocation* invocation) {
   if (method_name == kMethodNotifyEPCreated) {
index 2d5a7c5..c8ead3d 100755 (executable)
@@ -31,7 +31,8 @@ class Runtime {
   virtual void OnLowMemory();
 
  private:
-  void HandleDBusMethod(const std::string& method_name,
+  void HandleDBusMethod(GDBusConnection* connection,
+                        const std::string& method_name,
                         GVariant* parameters,
                         GDBusMethodInvocation* invocation);