Introduce JSNative project
authorWonYoung Choi <wy80.choi@samsung.com>
Thu, 12 Nov 2015 02:04:13 +0000 (11:04 +0900)
committerWonYoung Choi <wy80.choi@samsung.com>
Thu, 12 Nov 2015 02:34:47 +0000 (11:34 +0900)
JSNative consists of set of node.js modules to provide node.js environment
for Tizen platform.
Currently, JSNative has below node.js modules.
- appfw : Using tizen application frameworks
- gcontext: Using glib mainloop
- node-xwalk : Using crosswalk style extension
- jsn-cli : JSNative CLI tools

Change-Id: I8fcd5d8fa43150b06d3db3d53625ddfbaf3e731d

60 files changed:
CMakeLists.txt [new file with mode: 0644]
LICENSE [new file with mode: 0644]
LICENSE.BSD [new file with mode: 0644]
appfw/CMakeLists.txt [new file with mode: 0755]
appfw/lib/appfw.js [new file with mode: 0755]
appfw/package.json [new file with mode: 0644]
appfw/src/appfw.cc [new file with mode: 0755]
appfw/src/appfw.h [new file with mode: 0755]
appfw/src/appfw_native_node.cc [new file with mode: 0755]
gcontext/CMakeLists.txt [new file with mode: 0755]
gcontext/package.json [new file with mode: 0644]
gcontext/src/gcontext.cc [new file with mode: 0755]
gcontext/src/gcontext.h [new file with mode: 0755]
jsn-cli/bin/jsn-cli [new file with mode: 0755]
jsn-cli/lib/build.js [new file with mode: 0755]
jsn-cli/lib/cli.js [new file with mode: 0755]
jsn-cli/lib/create.js [new file with mode: 0755]
jsn-cli/lib/help.js [new file with mode: 0644]
jsn-cli/lib/jsn_error.js [new file with mode: 0644]
jsn-cli/lib/list.js [new file with mode: 0755]
jsn-cli/lib/parser.js [new file with mode: 0755]
jsn-cli/lib/remove.js [new file with mode: 0755]
jsn-cli/lib/shell_util.js [new file with mode: 0644]
jsn-cli/lib/sign.js [new file with mode: 0644]
jsn-cli/package.json [new file with mode: 0755]
jsn-cli/tizen-app-template/icon.png [new file with mode: 0755]
jsn-cli/tizen-app-template/index.js [new file with mode: 0755]
jsn-cli/tizen-app-template/pkgid.appid [new file with mode: 0755]
jsn-cli/tizen-app-template/tizen-manifest.xml [new file with mode: 0755]
node-xwalk/CMakeLists.txt [new file with mode: 0644]
node-xwalk/CPPLINT.cfg [new file with mode: 0644]
node-xwalk/README.md [new file with mode: 0644]
node-xwalk/binding.gyp [new file with mode: 0644]
node-xwalk/examples/echo/Makefile [new file with mode: 0644]
node-xwalk/examples/echo/echo.js [new file with mode: 0644]
node-xwalk/examples/echo/echo_extension.c [new file with mode: 0644]
node-xwalk/examples/echo/libecho.so [new file with mode: 0755]
node-xwalk/examples/echo_binary/Makefile [new file with mode: 0644]
node-xwalk/examples/echo_binary/echo2.js [new file with mode: 0644]
node-xwalk/examples/echo_binary/echo_extension_messaging_2.c [new file with mode: 0644]
node-xwalk/examples/echo_binary/libecho2.so [new file with mode: 0755]
node-xwalk/lib/loader.js [new file with mode: 0644]
node-xwalk/package.json [new file with mode: 0644]
node-xwalk/packaging/node-xwalk.manifest [new file with mode: 0644]
node-xwalk/packaging/node-xwalk.spec [new file with mode: 0644]
node-xwalk/src/extension.cc [new file with mode: 0644]
node-xwalk/src/extension.h [new file with mode: 0644]
node-xwalk/src/extension_adapter.cc [new file with mode: 0644]
node-xwalk/src/extension_adapter.h [new file with mode: 0644]
node-xwalk/src/log.h [new file with mode: 0644]
node-xwalk/src/native_binding.cc [new file with mode: 0644]
node-xwalk/src/native_binding.h [new file with mode: 0644]
node-xwalk/src/xwalk/extensions/public/XW_Extension.h [new file with mode: 0644]
node-xwalk/src/xwalk/extensions/public/XW_Extension_EntryPoints.h [new file with mode: 0644]
node-xwalk/src/xwalk/extensions/public/XW_Extension_Message_2.h [new file with mode: 0644]
node-xwalk/src/xwalk/extensions/public/XW_Extension_Permissions.h [new file with mode: 0644]
node-xwalk/src/xwalk/extensions/public/XW_Extension_Runtime.h [new file with mode: 0644]
node-xwalk/src/xwalk/extensions/public/XW_Extension_SyncMessage.h [new file with mode: 0644]
packaging/jsnative.manifest [new file with mode: 0644]
packaging/jsnative.spec [new file with mode: 0644]

diff --git a/CMakeLists.txt b/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9d5f315
--- /dev/null
@@ -0,0 +1,30 @@
+CMAKE_MINIMUM_REQUIRED(VERSION 2.6)
+
+PROJECT("jsnative")
+
+INCLUDE(FindPkgConfig)
+
+IF(NOT CMAKE_BUILD_TYPE)
+    SET(CMAKE_BUILD_TYPE "Release")
+ENDIF(NOT CMAKE_BUILD_TYPE)
+
+SET(CMAKE_C_FLAGS_PROFILING    "-O2")
+SET(CMAKE_CXX_FLAGS_PROFILING  "-O2 -std=c++0x")
+SET(CMAKE_C_FLAGS_DEBUG        "-O0 -g")
+SET(CMAKE_CXX_FLAGS_DEBUG      "-O0 -std=c++0x -g")
+SET(CMAKE_C_FLAGS_RELEASE      "-O2 -g")
+SET(CMAKE_CXX_FLAGS_RELEASE    "-O2 -std=c++0x -g -fvisibility-inlines-hidden")
+SET(CMAKE_CXX_FLAGS_CCOV       "-O0 -std=c++0x -g --coverage")
+
+ADD_DEFINITIONS("-fPIC")
+ADD_DEFINITIONS("-Wall")                # Generate all warnings
+ADD_DEFINITIONS("-Wextra")              # Generate even more extra warnings
+ADD_DEFINITIONS("-Wno-variadic-macros") # Inhibit variadic macros warnings (needed for ORM)
+ADD_DEFINITIONS("-Wno-deprecated")      # No warnings about deprecated features
+ADD_DEFINITIONS("-std=c++0x")           # accept C++11x standard
+
+SET(GLOBAL_NODE_MODULE_PATH ${CMAKE_INSTALL_PREFIX}/lib/node)
+
+ADD_SUBDIRECTORY(appfw)
+ADD_SUBDIRECTORY(gcontext)
+ADD_SUBDIRECTORY(node-xwalk)
diff --git a/LICENSE b/LICENSE
new file mode 100644 (file)
index 0000000..d645695
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
diff --git a/LICENSE.BSD b/LICENSE.BSD
new file mode 100644 (file)
index 0000000..8c40c3e
--- /dev/null
@@ -0,0 +1,27 @@
+ Copyright (c) 2015 Samsung Electronics Co, Ltd. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are
+ met:
+
+    * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+    * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with the
+ distribution.
+    * Neither the name of Samsung Electronics nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/appfw/CMakeLists.txt b/appfw/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..965911c
--- /dev/null
@@ -0,0 +1,33 @@
+SET(TARGET_APPFW "appfw")
+SET(TARGET_APPFW_NATIVE "appfw-native")
+SET(TARGET_APPFW_SRC ${PROJECT_SOURCE_DIR}/appfw)
+
+# Copy Project
+INSTALL(FILES
+    ${TARGET_APPFW_SRC}/package.json
+    DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_APPFW}
+)
+
+INSTALL(FILES
+    ${TARGET_APPFW_SRC}/lib/appfw.js
+    DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_APPFW}/lib
+)
+
+# Native Binding Module
+PKG_CHECK_MODULES(TARGET_APPFW_NATIVE_DEPS REQUIRED
+    appcore-efl aul dlog nodejs
+)
+INCLUDE_DIRECTORIES (
+  ${TARGET_APPFW_SRC}/src
+  ${TARGET_APPFW_NATIVE_DEPS_INCLUDE_DIRS}
+)
+ADD_LIBRARY(${TARGET_APPFW_NATIVE} MODULE
+  ${TARGET_APPFW_SRC}/src/appfw.cc
+  ${TARGET_APPFW_SRC}/src/appfw_native_node.cc
+)
+SET_TARGET_PROPERTIES(${TARGET_APPFW_NATIVE} PROPERTIES PREFIX "" SUFFIX ".node")
+TARGET_LINK_LIBRARIES(${TARGET_APPFW_NATIVE}
+    ${TARGET_APPFW_NATIVE_DEPS_LIBRARIES}
+)
+INSTALL(TARGETS ${TARGET_APPFW_NATIVE} DESTINATION
+        ${GLOBAL_NODE_MODULE_PATH}/${TARGET_APPFW}/build/Release)
diff --git a/appfw/lib/appfw.js b/appfw/lib/appfw.js
new file mode 100755 (executable)
index 0000000..c41ba63
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+var util = require('util');
+var EventEmitter = require('events');
+
+var map = new WeakMap();
+var internal = function (object) {
+  if (!map.has(object))
+    map.set(object, {});
+  return map.get(object);
+}
+
+var Application = function() {
+  EventEmitter.call(this);
+  internal(this).initialized = false;
+};
+
+util.inherits(Application, EventEmitter);
+
+Application.prototype.init = function(name) {
+  if (internal(this).initialized)
+    return;
+  internal(this).initialized = true;
+  process.title = process.argv[1];
+
+  var appfw = require('../build/Release/appfw-native.node');
+  var EventEmitter = require('events');
+
+  // gcontext module should be installed as global module
+  require('gcontext').init();
+
+  process.on('exit', function(){
+    appfw.deinit();
+  });
+
+  appfw.onCreate = (function(self){
+    return self.emit.bind(self, ['create']);
+  })(this);
+
+  appfw.onService = (function(self){
+    return self.emit.bind(self, ['service']);
+  })(this);
+
+  appfw.onPause = (function(self){
+    return self.emit.bind(self, ['pause']);
+  })(this);
+
+  appfw.onResume = (function(self){
+    return self.emit.bind(self, ['resume']);
+  })(this);
+
+  appfw.onTerminate = (function(self){
+    return self.emit.bind(self, ['terminate']);
+  })(this);
+
+  appfw.init(process.argv.slice(1));
+}
+
+Application.prototype.exit = function(code) {
+  process.exit(code);
+}
+
+module.exports = new Application();
diff --git a/appfw/package.json b/appfw/package.json
new file mode 100644 (file)
index 0000000..7816c01
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "name": "appfw",
+  "version": "0.0.1",
+  "description": "Application framework module for Tizen",
+  "main": "./lib/appfw.js",
+  "author": {
+    "name": "Seungkeun Lee",
+    "email": "sngn.lee@samsung.com"
+  }
+}
diff --git a/appfw/src/appfw.cc b/appfw/src/appfw.cc
new file mode 100755 (executable)
index 0000000..81996fd
--- /dev/null
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "appfw.h"
+
+#include <dlog.h>
+#include <appcore-common.h>
+#include <appcore-efl.h>
+#include <aul.h>
+#include <uv.h>
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "JS_BINDING"
+
+namespace plugins {
+AppFW* AppFW::GetInstance() {
+  static AppFW instance;
+  return &instance;
+}
+
+AppFW::AppFW() : initialized_(false),
+    create_handler_(nullptr),
+    service_handler_(nullptr),
+    terminate_handler_(nullptr),
+    pause_handler_(nullptr),
+    resume_handler_(nullptr) {
+}
+
+AppFW::~AppFW() {
+}
+
+bool AppFW::Init(int argc, char* argv[]) {
+  if (initialized_)
+    return false;
+
+  initialized_ = true;
+  static struct appcore_ops ops;
+
+  ops.create = [](void* data) {
+    AppFW* appfw = static_cast<AppFW*>(data);
+    if (appfw->create_handler_)
+      appfw->create_handler_();
+    return 0;
+  };
+
+  ops.terminate = [](void* data) {
+    AppFW* appfw = static_cast<AppFW*>(data);
+    if (appfw->terminate_handler_)
+      appfw->terminate_handler_();
+    return 0;
+  };
+
+  ops.pause = [](void* data) {
+    AppFW* appfw = static_cast<AppFW*>(data);
+    if (appfw->pause_handler_)
+      appfw->pause_handler_();
+    return 0;
+  };
+
+  ops.resume = [](void* data) {
+    AppFW* appfw = static_cast<AppFW*>(data);
+    if (appfw->resume_handler_)
+      appfw->resume_handler_();
+    return 0;
+  };
+
+  ops.reset = [](bundle* b, void* data) {
+    AppFW* appfw = static_cast<AppFW*>(data);
+    if (appfw->service_handler_)
+      appfw->service_handler_(b);
+    return 0;
+  };
+
+  ops.data = this;
+
+  // TODO(sngn.lee) : Delete me, Temporary code to hack app terminate
+  auto status_cb = [](int status, void* data){
+    AppFW* appfw = static_cast<AppFW*>(data);
+    if (status == STATUS_DYING) {
+      uv_stop(uv_default_loop());
+      appfw->Deinit();
+    }
+    return 0;
+  };
+  aul_add_status_local_cb(status_cb, this);
+  // end of TODO
+
+  appcore_efl_init("js-binding", &argc, &argv, &ops);
+
+  return true;
+}
+
+void AppFW::Deinit() {
+  LOGE("AppFW Deinit was called..");
+  if (initialized_) {
+    initialized_ = false;
+    appcore_efl_fini();
+  }
+}
+
+void AppFW::set_create_handler(std::function<void(void)> handler) {
+  create_handler_ = handler;
+}
+void AppFW::set_service_handler(std::function<void(void*)> handler) {
+  service_handler_ = handler;
+}
+void AppFW::set_terminate_handler(std::function<void(void)> handler) {
+  terminate_handler_ = handler;
+}
+void AppFW::set_pause_handler(std::function<void(void)> handler) {
+  pause_handler_ = handler;
+}
+void AppFW::set_resume_handler(std::function<void(void)> handler) {
+  resume_handler_ = handler;
+}
+
+}  // namespace plugins
diff --git a/appfw/src/appfw.h b/appfw/src/appfw.h
new file mode 100755 (executable)
index 0000000..d6a1d98
--- /dev/null
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include <functional>
+
+namespace plugins {
+
+class AppFW {
+ public:
+  static AppFW* GetInstance();
+  bool Init(int argc, char* argv[]);
+  void Deinit();
+  void set_create_handler(std::function<void(void)> handler);
+  void set_service_handler(std::function<void(void*)> handler);
+  void set_terminate_handler(std::function<void(void)> handler);
+  void set_pause_handler(std::function<void(void)> handler);
+  void set_resume_handler(std::function<void(void)> handler);
+
+  // TODO(sngn.lee) : Add system event callback. eg) low battery, low memory...
+
+ private:
+  AppFW();
+  ~AppFW();
+  bool initialized_;
+  std::function<void(void)> create_handler_;
+  std::function<void(void*)> service_handler_;
+  std::function<void(void)> terminate_handler_;
+  std::function<void(void)> pause_handler_;
+  std::function<void(void)> resume_handler_;
+};
+
+}  // namespace plugins
diff --git a/appfw/src/appfw_native_node.cc b/appfw/src/appfw_native_node.cc
new file mode 100755 (executable)
index 0000000..27bfa59
--- /dev/null
@@ -0,0 +1,194 @@
+/*
+ * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+
+#include <v8.h>
+#include <node.h>
+#include <appfw.h>
+#include <dlog.h>
+
+#include <vector>
+
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "JS_BINDING"
+
+namespace {
+
+v8::Persistent<v8::Object> gThis_;
+const char* kCreateHandlerKey = "onCreate";
+const char* kTerminateHandlerKey = "onTerminate";
+const char* kServiceHandlerKey = "onService";
+const char* kPauseHandlerKey = "onPause";
+const char* kResumeHandlerKey = "onResume";
+
+v8::Handle<v8::Function> GetHandler(const std::string& name) {
+  if (gThis_.IsEmpty()) {
+    LOGD("this object is undefined");
+    return v8::Handle<v8::Function>();
+  }
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::EscapableHandleScope handle_scope(isolate);
+
+  v8::Handle<v8::Object> self = v8::Local<v8::Object>::New(isolate, gThis_);
+  v8::Local<v8::Value> handler_value =
+      self->Get(v8::String::NewFromUtf8(isolate, name.c_str()));
+
+  if (!handler_value->IsFunction()) {
+    LOGE("%s handler is undefined", name.c_str());
+    return v8::Handle<v8::Function>();
+  }
+  return handle_scope.Escape(v8::Handle<v8::Function>::Cast(handler_value));
+}
+
+void SetCreateHandler() {
+  plugins::AppFW* appfw = plugins::AppFW::GetInstance();
+  appfw->set_create_handler([](){
+    v8::Isolate* isolate = v8::Isolate::GetCurrent();
+    v8::HandleScope handle_scope(isolate);
+    v8::Handle<v8::Function> handler_function = GetHandler(kCreateHandlerKey);
+    if (handler_function.IsEmpty()) {
+      return;
+    }
+    v8::TryCatch try_catch;
+    handler_function->Call(v8::Local<v8::Object>::New(isolate, gThis_),
+        0, nullptr);
+    if (try_catch.HasCaught())
+      LOGE("Exception when running create handler");
+  });
+}
+
+void SetTerminateHandler() {
+  plugins::AppFW* appfw = plugins::AppFW::GetInstance();
+  appfw->set_terminate_handler([](){
+    v8::Isolate* isolate = v8::Isolate::GetCurrent();
+    v8::HandleScope handle_scope(isolate);
+    v8::Handle<v8::Function> handler_function =
+        GetHandler(kTerminateHandlerKey);
+    if (handler_function.IsEmpty()) {
+      return;
+    }
+    v8::TryCatch try_catch;
+    handler_function->Call(v8::Local<v8::Object>::New(isolate, gThis_),
+        0, nullptr);
+    if (try_catch.HasCaught())
+      LOGE("Exception when running terminate handler");
+  });
+}
+
+void SetPauseHandler() {
+  plugins::AppFW* appfw = plugins::AppFW::GetInstance();
+  appfw->set_pause_handler([](){
+    v8::Isolate* isolate = v8::Isolate::GetCurrent();
+    v8::HandleScope handle_scope(isolate);
+    v8::Handle<v8::Function> handler_function =
+        GetHandler(kPauseHandlerKey);
+    if (handler_function.IsEmpty()) {
+      return;
+    }
+    v8::TryCatch try_catch;
+    handler_function->Call(v8::Local<v8::Object>::New(isolate, gThis_),
+        0, nullptr);
+    if (try_catch.HasCaught())
+      LOGE("Exception when running pause handler");
+  });
+}
+
+void SetResumeHandler() {
+  plugins::AppFW* appfw = plugins::AppFW::GetInstance();
+  appfw->set_resume_handler([](){
+    v8::Isolate* isolate = v8::Isolate::GetCurrent();
+    v8::HandleScope handle_scope(isolate);
+    v8::Handle<v8::Function> handler_function =
+        GetHandler(kResumeHandlerKey);
+    if (handler_function.IsEmpty()) {
+      return;
+    }
+    v8::TryCatch try_catch;
+    handler_function->Call(v8::Local<v8::Object>::New(isolate, gThis_),
+        0, nullptr);
+    if (try_catch.HasCaught())
+      LOGE("Exception when running resume handler");
+  });
+}
+
+void SetServiceHandler() {
+  plugins::AppFW* appfw = plugins::AppFW::GetInstance();
+  appfw->set_service_handler([](void* bundle){
+    v8::Isolate* isolate = v8::Isolate::GetCurrent();
+    v8::HandleScope handle_scope(isolate);
+    v8::Handle<v8::Function> handler_function =
+        GetHandler(kServiceHandlerKey);
+    if (handler_function.IsEmpty()) {
+      return;
+    }
+    v8::TryCatch try_catch;
+    handler_function->Call(v8::Local<v8::Object>::New(isolate, gThis_),
+        0, nullptr);
+    if (try_catch.HasCaught())
+      LOGE("Exception when running service handler");
+  });
+}
+
+}  // namespace
+
+static void AppFWInit(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  if (args.Length() < 1 || !args[0]->IsArray())
+    return;
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope scope(isolate);
+
+  gThis_.Reset(v8::Isolate::GetCurrent(), args.This());
+
+  SetCreateHandler();
+  SetTerminateHandler();
+  SetPauseHandler();
+  SetResumeHandler();
+  SetServiceHandler();
+
+
+  v8::Local<v8::Array> array_args = v8::Local<v8::Array>::Cast(args[0]);
+  int argc = array_args->Length();
+  std::vector<char*> argv(argc);
+  for (int i = 0; i < argc; i++) {
+    v8::Local<v8::String> arg = array_args->Get(i)->ToString();
+    int nsize = arg->Utf8Length()+1;
+    argv[i] = new char[nsize];
+    arg->WriteUtf8(argv[i]);
+  }
+  plugins::AppFW::GetInstance()->Init(argc, argv.data());
+  for (int i = 0; i < argc; i++) {
+    delete[] argv[i];
+  }
+}
+
+static void AppFWDeinit(const v8::FunctionCallbackInfo<v8::Value>& args) {
+  plugins::AppFW::GetInstance()->Deinit();
+}
+
+static void init(v8::Handle<v8::Object> target) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope scope(isolate);
+  target->Set(v8::String::NewFromUtf8(isolate, "init"),
+      v8::FunctionTemplate::New(isolate, AppFWInit)->GetFunction());
+  target->Set(v8::String::NewFromUtf8(isolate, "deinit"),
+      v8::FunctionTemplate::New(isolate, AppFWDeinit)->GetFunction());
+}
+
+NODE_MODULE(appfw_native, init);
diff --git a/gcontext/CMakeLists.txt b/gcontext/CMakeLists.txt
new file mode 100755 (executable)
index 0000000..ac9003a
--- /dev/null
@@ -0,0 +1,28 @@
+SET(TARGET_GCONTEXT "gcontext")
+SET(TARGET_GCONTEXT_NATIVE "gcontext")
+SET(TARGET_GCONTEXT_SRC ${PROJECT_SOURCE_DIR}/gcontext)
+
+# Copy Project
+INSTALL(FILES
+    ${TARGET_GCONTEXT_SRC}/package.json
+    DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_GCONTEXT}
+)
+
+
+# Native Binding Module
+PKG_CHECK_MODULES(TARGET_GCONTEXT_NATIVE_DEPS REQUIRED
+    dlog glib-2.0 gobject-2.0 nodejs
+)
+INCLUDE_DIRECTORIES (
+  ${TARGET_GCONTEXT_SRC}/src
+  ${TARGET_GCONTEXT_NATIVE_DEPS_INCLUDE_DIRS}
+)
+ADD_LIBRARY(${TARGET_GCONTEXT_NATIVE} MODULE
+  ${TARGET_GCONTEXT_SRC}/src/gcontext.cc
+)
+SET_TARGET_PROPERTIES(${TARGET_GCONTEXT_NATIVE} PROPERTIES PREFIX "" SUFFIX ".node")
+TARGET_LINK_LIBRARIES(${TARGET_GCONTEXT_NATIVE}
+    ${TARGET_GCONTEXT_NATIVE_DEPS_LIBRARIES}
+)
+INSTALL(TARGETS ${TARGET_GCONTEXT_NATIVE} DESTINATION
+        ${GLOBAL_NODE_MODULE_PATH}/${TARGET_GCONTEXT}/build/Release)
diff --git a/gcontext/package.json b/gcontext/package.json
new file mode 100644 (file)
index 0000000..1102058
--- /dev/null
@@ -0,0 +1,10 @@
+{
+  "name": "gcontext",
+  "version": "0.0.1",
+  "description": "Utility module for integrating glib mainloop",
+  "main": "./build/Release/gcontext",
+  "author": {
+    "name": "Seungkeun Lee",
+    "email": "sngn.lee@samsung.com"
+  }
+}
diff --git a/gcontext/src/gcontext.cc b/gcontext/src/gcontext.cc
new file mode 100755 (executable)
index 0000000..3cbdb95
--- /dev/null
@@ -0,0 +1,327 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#include "gcontext.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <glib-object.h>
+#include <dlog.h>
+#include <v8.h>
+#include <node.h>
+
+#include <memory>
+
+
+#ifdef LOG_TAG
+#undef LOG_TAG
+#endif
+#define LOG_TAG "JS_BINDING"
+
+namespace wrt {
+namespace service {
+
+struct poll_handler {
+  int fd;
+  uv_poll_t *uv_poll;
+  int eventmask;
+  int ref;
+  std::list<GPollFD*> fd_list;
+};
+
+GContext::GContext():initialized_(false) {
+}
+
+GContext::~GContext() {
+}
+
+void GContext::Init() {
+  if (initialized_)
+    return;
+  initialized_ = true;
+  GMainContext *gc = g_main_context_default();
+
+#if !GLIB_CHECK_VERSION(2, 35, 0)
+  g_type_init();
+#endif
+
+  g_main_context_acquire(gc);
+  context_ = g_main_context_ref(gc);
+  fd_list_ = NULL;
+  fd_list_size_ = 0;
+  fd_count_ = 0;
+
+  // allocate memory to memory
+  prepare_handle_ = static_cast<uv_prepare_t*>(malloc(sizeof(uv_prepare_t)));
+  check_handle_ = static_cast<uv_check_t*>(malloc(sizeof(uv_check_t)));
+  timeout_handle_ = static_cast<uv_timer_t*>(malloc(sizeof(uv_timer_t)));
+
+  memset(prepare_handle_, 0, sizeof(uv_prepare_t));
+  memset(check_handle_, 0, sizeof(uv_check_t));
+  memset(timeout_handle_, 0, sizeof(uv_timer_t));
+
+  uv_prepare_init(uv_default_loop(), prepare_handle_);
+  uv_check_init(uv_default_loop(), check_handle_);
+  uv_timer_init(uv_default_loop(), timeout_handle_);
+
+  prepare_handle_->data = this;
+  check_handle_->data = this;
+  timeout_handle_->data = this;
+
+  uv_prepare_start(prepare_handle_, GContext::OnPrepare);
+  uv_check_start(check_handle_, GContext::OnCheck);
+}
+
+void GContext::Uninit() {
+  if (!initialized_)
+    return;
+
+  initialized_ = false;
+
+  /* Remove all handlers */
+  std::list<poll_handler*>::iterator itr = poll_handle_list_.begin();
+  while (itr != poll_handle_list_.end()) {
+    /* Stop polling handler */
+    uv_unref(reinterpret_cast<uv_handle_t*>((*itr)->uv_poll));
+    uv_poll_stop((*itr)->uv_poll);
+    uv_close(reinterpret_cast<uv_handle_t*>((*itr)->uv_poll),
+        reinterpret_cast<uv_close_cb>(::free));
+    delete *itr;
+    itr = poll_handle_list_.erase(itr);
+  }
+
+  uv_unref(reinterpret_cast<uv_handle_t*>(check_handle_));
+  uv_unref(reinterpret_cast<uv_handle_t*>(prepare_handle_));
+  uv_unref(reinterpret_cast<uv_handle_t*>(timeout_handle_));
+
+  uv_check_stop(check_handle_);
+  uv_prepare_stop(prepare_handle_);
+  uv_timer_stop(timeout_handle_);
+
+  uv_close(reinterpret_cast<uv_handle_t*>(check_handle_),
+      reinterpret_cast<uv_close_cb>(::free));
+  uv_close(reinterpret_cast<uv_handle_t*>(prepare_handle_),
+      reinterpret_cast<uv_close_cb>(::free));
+  uv_close(reinterpret_cast<uv_handle_t*>(timeout_handle_),
+      reinterpret_cast<uv_close_cb>(::free));
+
+  check_handle_ = NULL;
+  prepare_handle_ = NULL;
+  timeout_handle_ = NULL;
+
+  g_free(fd_list_);
+  fd_list_ = NULL;
+
+  /* Release GMainContext loop */
+  g_main_context_unref(context_);
+}
+
+static void poll_cb(uv_poll_t *uv_handle, int status, int events) {
+  poll_handler* handle = static_cast<poll_handler*>(uv_handle->data);
+  if (status == 0) {
+    std::list<GPollFD*>::iterator itr = handle->fd_list.begin();
+    while (itr != handle->fd_list.end()) {
+      GPollFD* pfd = *itr;
+      pfd->revents |= pfd->events & ((events & UV_READABLE ? G_IO_IN : 0)
+                                   | (events & UV_WRITABLE ? G_IO_OUT : 0));
+        ++itr;
+    }
+  } else {
+    uv_poll_stop(uv_handle);
+  }
+}
+
+void GContext::onPrepare() {
+  int i;
+  int timeout;
+
+  g_main_context_prepare(context_, &max_priority_);
+
+  // Getting all sources from GLib main context
+  while (fd_list_size_ < (fd_count_ = g_main_context_query(
+                                        context_,
+                                        max_priority_,
+                                        &timeout,
+                                        fd_list_,
+                                        fd_list_size_))) {
+    g_free(fd_list_);
+    fd_list_size_ = fd_count_;
+    fd_list_ = g_new(GPollFD, fd_list_size_);
+  }
+
+  // Poll
+  if (fd_count_ > 0) {
+    std::unique_ptr<char> flagsTable(new char[fd_count_]);
+    memset(flagsTable.get(), 0, fd_count_);
+    std::list<poll_handler*>::iterator itr = poll_handle_list_.begin();
+
+    // for each poll handler
+    while (itr != poll_handle_list_.end()) {
+      poll_handler* handle = *itr;
+      int origin_mask = handle->eventmask;
+      handle->ref = 0;
+      handle->eventmask = 0;
+      handle->fd_list.clear();
+
+      // check already initialized poll handles
+      for (i = 0; i < fd_count_; ++i) {
+        GPollFD *pfd = fd_list_ + i;
+        if (handle->fd == pfd->fd) {
+          flagsTable.get()[i] = 1;
+          handle->ref++;
+          pfd->revents = 0;
+          handle->eventmask |= (pfd->events & G_IO_IN ? UV_READABLE: 0)
+                               | (pfd->events & G_IO_OUT ? UV_WRITABLE: 0);
+          handle->fd_list.push_back(pfd);
+        }
+      }
+
+      // remasking events
+      if (handle->ref > 0) {
+        if (handle->eventmask != origin_mask) {
+          uv_poll_stop(handle->uv_poll);
+          uv_poll_start(handle->uv_poll, handle->eventmask, poll_cb);
+        }
+      }
+
+      // remove unused poll handles
+      if (handle->ref == 0) {
+        uv_unref(reinterpret_cast<uv_handle_t*>(handle->uv_poll));
+        uv_poll_stop(handle->uv_poll);
+        uv_close(reinterpret_cast<uv_handle_t*>(handle->uv_poll),
+                 reinterpret_cast<uv_close_cb>(::free));
+        itr = poll_handle_list_.erase(itr);
+        delete handle;
+      } else {
+        ++itr;
+      }
+    }
+
+    std::list<poll_handler*> new_poll_fds;
+    /* Process current file descriptors from GContext */
+    for (i = 0; i < fd_count_; ++i) {
+      GPollFD *pfd = &fd_list_[i];
+      int exists = flagsTable.get()[i];
+      if (exists)
+        continue;
+
+      pfd->revents = 0;
+      for (itr = new_poll_fds.begin(); itr != new_poll_fds.end(); ++itr) {
+        poll_handler *handle = *itr;
+        if (handle->fd == pfd->fd) {
+          int oldmask = handle->eventmask;
+          handle->eventmask |= (pfd->events & G_IO_IN ? UV_READABLE: 0)
+                               | (pfd->events & G_IO_OUT ? UV_WRITABLE: 0);
+          if (oldmask != handle->eventmask) {
+            uv_poll_stop(handle->uv_poll);
+            uv_poll_start(handle->uv_poll, handle->eventmask, poll_cb);
+          }
+          exists = 1;
+          break;
+        }
+      }
+
+      if (exists)
+        continue;
+
+      /* Preparing poll handler */
+      struct poll_handler* handle = new poll_handler();
+      handle->fd = pfd->fd;
+      handle->ref = 1;
+
+      /* Create uv poll handler, then append own poll handler on it */
+      uv_poll_t *pt = static_cast<uv_poll_t*>(malloc(sizeof(uv_poll_t)));
+      memset(pt, 0, sizeof(uv_poll_t));
+      pt->data = handle;
+      handle->uv_poll = pt;
+
+      uv_poll_init(uv_default_loop(), pt, pfd->fd);
+      int uv_events = (pfd->events & G_IO_IN ? UV_READABLE: 0)
+                      | (pfd->events & G_IO_OUT ? UV_WRITABLE: 0);
+      handle->eventmask = uv_events;
+      uv_poll_start(pt, uv_events, poll_cb);
+      new_poll_fds.push_back(handle);
+    }
+    if (!new_poll_fds.empty())
+      poll_handle_list_.merge(new_poll_fds);
+  }
+
+  if (timeout >= 0) {
+    uv_timer_start(timeout_handle_, OnTimeout, timeout, 0);
+  }
+}
+
+
+void GContext::OnPrepare(uv_prepare_t* handle) {
+  GContext *This = static_cast<GContext*>(handle->data);
+  This->onPrepare();
+}
+
+void GContext::OnCheck(uv_check_s* handle) {
+  GContext* This = static_cast<GContext*>(handle->data);
+  This->onCheck();
+}
+
+void GContext::onCheck() {
+  int ready = g_main_context_check(context_,
+                                   max_priority_,
+                                   fd_list_,
+                                   fd_count_);
+  if (ready) {
+      g_main_context_dispatch(context_);
+  }
+}
+
+void GContext::OnTimeout(uv_timer_s* handle) {
+  GContext* This = static_cast<GContext*>(handle->data);
+  This->onTimeout();
+}
+
+void GContext::onTimeout() {
+}
+
+
+static GContext *g_context = NULL;
+
+static void GContextInit(const v8::FunctionCallbackInfo<v8::Value>&) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope scope(isolate);
+
+  if (g_context == NULL) {
+    g_context = new GContext();
+  }
+  g_context->Init();
+}
+
+static void GContextUninit(const v8::FunctionCallbackInfo<v8::Value>&) {
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope scope(isolate);
+  if (g_context != NULL) {
+    g_context->Uninit();
+    delete g_context;
+    g_context = NULL;
+  }
+}
+
+static void init(v8::Handle<v8::Object> target) {
+  NODE_SET_METHOD(target, "init", GContextInit);
+  NODE_SET_METHOD(target, "uninit", GContextUninit);
+}
+
+NODE_MODULE(gcontext, init);
+
+}  // namespace service
+}  // namespace wrt
diff --git a/gcontext/src/gcontext.h b/gcontext/src/gcontext.h
new file mode 100755 (executable)
index 0000000..261ebf5
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2014 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ *    Licensed under the Apache License, Version 2.0 (the "License");
+ *    you may not use this file except in compliance with the License.
+ *    You may obtain a copy of the License at
+ *
+ *        http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *    Unless required by applicable law or agreed to in writing, software
+ *    distributed under the License is distributed on an "AS IS" BASIS,
+ *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *    See the License for the specific language governing permissions and
+ *    limitations under the License.
+ */
+
+#ifndef WRT_SERVICE_NODE_GCONTEXT_H_
+#define WRT_SERVICE_NODE_GCONTEXT_H_
+
+#include <glib.h>
+#include <uv.h>
+
+#include <list>
+
+namespace wrt {
+namespace service {
+
+class poll_handler;
+
+class GContext {
+ public:
+  GContext();
+  virtual ~GContext();
+  void Init();
+  void Uninit();
+
+ private:
+  void onPrepare();
+  void onCheck();
+  void onTimeout();
+
+  static void OnPrepare(uv_prepare_t* handle);
+  static void OnCheck(uv_check_s* handle);
+  static void OnTimeout(uv_timer_s* handle);
+
+  bool initialized_;
+  GMainContext *context_;
+  int max_priority_;
+
+  GPollFD *fd_list_;
+  int fd_list_size_;
+  int fd_count_;
+  std::list<poll_handler*> poll_handle_list_;
+
+  uv_prepare_t *prepare_handle_;
+  uv_check_t *check_handle_;
+  uv_timer_t *timeout_handle_;
+};
+
+}  // namespace service
+}  // namespace wrt
+
+#endif  // WRT_SERVICE_NODE_GCONTEXT_H_
diff --git a/jsn-cli/bin/jsn-cli b/jsn-cli/bin/jsn-cli
new file mode 100755 (executable)
index 0000000..6e383b5
--- /dev/null
@@ -0,0 +1,4 @@
+#!/usr/bin/env node
+
+const cli = require('../lib/cli');
+cli(process.argv);
diff --git a/jsn-cli/lib/build.js b/jsn-cli/lib/build.js
new file mode 100755 (executable)
index 0000000..c99403b
--- /dev/null
@@ -0,0 +1,71 @@
+'use strict'
+
+const fs = require('fs');
+const path = require('path');
+const Q = require('q');
+
+const JsnError = require('./jsn_error');
+const shellUtil = require('./shell_util');
+
+module.exports = build;
+
+// this module's build is meanging to package a tpk
+function build(argv, parser) {
+  const needToSign = argv.args_[0] || 'nosign';
+  switch(needToSign) {
+    case 'sign':
+    console.log('Build command will package with signing');
+
+    // current args_[0] is 'sign'. So remove it.
+    argv.args_.shift();
+    return require('./sign')(argv, parser)
+    .then(() => packageToTpk());
+
+    case 'nosign':
+    console.log('Build command will package without signing');
+    return parser.parse()
+    .catch(err => {
+      throw new JsnError('Parsing manifest file has a problem: ' +
+                         err.message);
+    })
+    .then(() => packageToTpk());
+
+    default:
+    throw new JsnError('Wrong parameters for build command: ' +
+                       needToSign);
+  }
+
+  function packageToTpk() {
+    const dotTpk = '.tpk';
+    const dirToGoBack = process.cwd();
+
+    console.log('Building the package');
+
+    // exist 'zip' command?
+    return shellUtil.shExec('which zip')
+
+    .then(() => {
+      // cd to project directory
+      process.chdir(parser.get('exec_path'));
+
+      const pkgIdTpk = parser.get('package').pkg_id + dotTpk;
+
+      // if *.tpk file exists, delete it
+      return Q.denodeify(fs.stat)(pkgIdTpk)
+        .then(stats => shellUtil.shExec('rm ' + pkgIdTpk),
+          () => {
+          // empty(if not, it is catrued by outer catch )
+        });
+    })
+
+    .then(() => {
+      const tpkFile = parser.get('package').pkg_id + dotTpk;
+      const output = '.' + path.sep + tpkFile;
+      const input = '.' + path.sep + '*';
+      return shellUtil.shExec('zip -r ' + output + ' ' + input);
+    })
+
+    // cd to previous cwd; actually, doesn't need this
+    .then(() => process.chdir(dirToGoBack));
+  }
+}
diff --git a/jsn-cli/lib/cli.js b/jsn-cli/lib/cli.js
new file mode 100755 (executable)
index 0000000..93196e1
--- /dev/null
@@ -0,0 +1,130 @@
+'use strict'
+
+const fs = require('fs');
+const path = require('path');
+const Q = require('q');
+
+const help = require('./help');
+
+module.exports = cli;
+
+/**
+  * This program uses 'Promises'.
+  * Reference below links for detailed contents.
+  * Promises: http://www.html5rocks.com/en/tutorials/es6/promises/
+  * q: https://github.com/kriskowal/q
+  */
+function cli(inputArgv) {
+  // verify inputArgv
+  const argv = getVerifiedArgv(inputArgv);
+  if (argv === null) {
+    help.printAll();
+    return;
+  }
+
+  // add exec path
+  const execPath = process.cwd();
+  argv.exec_path_ = execPath;
+
+  // setup manifest parser
+  const parser = new (require('./parser'))(execPath);
+
+  // get real path because it can be real bin file or symbolic link file
+  const binDir = path.dirname(fs.realpathSync(process.argv[1]));
+
+  // change cwd
+  process.chdir(path.join(binDir, '..', 'lib'));
+
+  // process command
+  switch (argv.cmd_) {
+    case 'create':
+    return require('./create')(argv, parser)
+    .then(
+      () => console.log('Ok'),
+      (err) => {
+        console.error(err.message);
+        help.printCreate();
+      });
+
+    case 'list':
+    return require('./list')(argv, parser)
+    .then(
+      () => console.log('Ok'),
+      (err) => {
+        console.error(err.message);
+        help.printList();
+      });
+
+    case 'remove':
+    return require('./remove')(argv, parser)
+    .then(
+      () => console.log('Ok'),
+      (err) => {
+        console.error(err.message);
+        help.printRemove();
+      });
+
+    case 'sign':
+    return require('./sign')(argv, parser)
+    .then(
+      () => console.log('Ok'),
+      (err) => {
+        console.error(err.message);
+        help.printSign();
+      });
+
+    case 'build':
+    return require('./build')(argv, parser)
+    .then(
+      () => console.log('Ok'),
+      (err) => {
+        console.error(err.message);
+        help.printBuild();
+      });
+
+    case 'help':
+    default:
+    help.print(argv.args_[0]);
+    break;
+  }
+
+  function getVerifiedArgv(inputArgv) {
+    if (inputArgv.length < 3)
+      return null;
+
+    const cmd = inputArgv[2];
+    const args = [];
+    switch (cmd) {
+      case 'create':
+      args.push(inputArgv[3]);
+      args.push(inputArgv[4]);
+      break;
+
+      case 'remove':
+      args.push(inputArgv[3]);
+      break;
+
+      case 'sign':
+      case 'build':
+      const len = inputArgv.length;
+      for (let i = 3; i < len; i+=1)
+        args[i-3] = inputArgv[i];
+      break;
+
+      case 'list':
+      break;
+
+      case 'help':
+      args.push(inputArgv[3]);
+      break;
+
+      default:
+      return null;
+    }
+
+    return {
+      cmd_: cmd,
+      args_: args
+    };
+  }
+}
diff --git a/jsn-cli/lib/create.js b/jsn-cli/lib/create.js
new file mode 100755 (executable)
index 0000000..bfb502f
--- /dev/null
@@ -0,0 +1,237 @@
+'use strict'
+
+const fs = require('fs');
+const path = require('path');
+const Q = require('q');
+const format = require('string-template');
+
+const JsnError = require('./jsn_error');
+const shellUtil = require('./shell_util');
+
+module.exports = create;
+
+function create(argv, parser) {
+  console.log('<create>');
+
+  const manifestFile = 'tizen-manifest.xml';
+  const iconFile = 'icon.png';
+
+  return Q.fcall(() => {
+    if (argv.args_[0] === undefined || argv.args_[1] === undefined) {
+      throw new JsnError('You should type proper parameters');
+    } else {
+      const createType = argv.args_[0];
+      console.log('Create Type: %s', createType);
+
+      switch (createType) {
+        case 'package':
+        const pkgId = argv.args_[1];
+        return createPackage(pkgId, parser);
+
+        case 'app':
+        const appId = argv.args_[1];
+        return createApp(appId, parser);
+
+        default:
+        throw new JsnError('You should type proper parameters');
+      }
+    }
+  })
+
+  /**
+    * creates directory to be below directory structure
+    * [pkgid]/shared(dir)
+    *              /res(dir)
+    *       /bin(dir)
+    *       /lib(dir)
+    *       /res(dir)
+    *           /icon.png(file)
+    *        /tizen-manifest.xml(file)
+    */
+  function createPackage(pkgId, parser) {
+    console.log('Creating Package');
+    console.log(parser.get('exec_path'));
+    const dirToCreate = path.join(parser.get('exec_path'), pkgId);
+
+    console.log('Checking directory to create package');
+
+    /**
+      * if the dir exists and the dir is not empty, exit program
+      * if the dir exists and the dir is empty, pass
+      * if the dir exists and the dir is file, exit program
+      * if there is no dir, create the dir
+      */
+    return Q.denodeify(fs.stat)(dirToCreate)
+    .then(
+      stats => {
+        if (stats.isDirectory()) {
+          return Q.denodeify(fs.readdir)(dirToCreate)
+            .then(files => {
+              if (files.length > 0)
+                throw new JsnError(pkgId + ' is not empty directory');
+            });
+        } else {
+          throw new JsnError(pkgId + ' file exists.');
+        }
+      },
+      () => {
+        console.log(dirToCreate + ' isn\'t existed');
+        console.log('Generating a new package dir : %s', dirToCreate);
+        return Q.denodeify(fs.mkdir)(dirToCreate);
+    })
+
+    /**
+      * creates directories like below directory structure
+      * [pkgid]/shared
+      *        /bin
+      *        /lib
+      *        /res
+      */
+    .then(() => {
+      console.log('Creating a new jsn project package');
+      console.log('Creating directories in project');
+      const shExecPromises = ['shared', 'bin', 'lib', 'res']
+        .map(dir => 'mkdir ' + path.join(dirToCreate, dir))
+        .map(shellUtil.shExec);
+      return Q.all(shExecPromises)
+        .catch(reason => { throw new JsnError(reason); });
+    })
+
+    /**
+      * creates a 'res' directory under 'shared' directory
+      * [pkgid]/shared
+      *               /res
+      */
+    .then(() => {
+      console.log('Creating directories in project/shared');
+      return Q.denodeify(fs.mkdir)(path.join(dirToCreate, 'shared', 'res'));
+    })
+
+    /**
+      * copy icon from 'tizen-app-template'
+      * [pkgid]/res
+      *            /icon.png
+      */
+    .then(() => {
+      console.log('Copying icon');
+      const defaultIconPath = path.join('..', 'tizen-app-template', iconFile);
+      const iconCmd = 'cp ' + defaultIconPath + ' ' +
+                      path.join(dirToCreate, 'shared', 'res', iconFile);
+      return shellUtil.shExec(iconCmd)
+        .catch(reason => { throw new JsnError(reason); });
+    })
+
+    /**
+      * creates manifest file from 'tizen-app-template'
+      * [pkgid]/tizen-manifest.xml
+      */
+    .then(() => {
+      console.log('Creating manifest');
+      const oriManifest = path.join('..', 'tizen-app-template', manifestFile);
+      return Q.denodeify(fs.readFile)(oriManifest);
+    })
+    .then(data => {
+      const manifestContent = format(data.toString(), { pkgid: pkgId });
+      const newManifest = path.join(parser.get('exec_path'),
+                                    pkgId, manifestFile);
+      return Q.denodeify(fs.writeFile)(newManifest, manifestContent)
+        .thenResolve(newManifest);
+    })
+
+    .catch(err => {
+        throw new JsnError('Creating package is failed: ' + err.message);
+      });
+  } // createPackage
+
+  /**
+    * creates directories to be below directory structure
+    * [pkgid]/shared(dir)
+    *               /res(dir)
+    *        /bin(dir)
+    *                 /[pkgid].[appid]
+    *        /lib(dir)
+    *        /res(dir)
+    *                 /[appid]
+    *                         /index.js
+    *            /icon.png(file)
+    *        /tizen-manifest.xml(file) - add 'ui-application' element
+    */
+  function createApp(appId, parser) {
+    console.log('createApp');
+
+    return parser.parse()
+    .catch(err => {
+        throw new JsnError('Parsing manifest file has a problem: ' +
+                           err.message);
+    })
+
+    .then(() => {
+      console.log('Creating a new appication.');
+      console.log('Checking whether the app(%s) exists', appId);
+      const app = parser.getAppSync(appId);
+      if (app) {
+        throw new JsnError(appId + " already existed");
+      }
+    })
+
+    // add 'ui-application' element in manifest file
+    .then(() => {
+      console.log('Addding <ui-application> element in manifest file');
+      return parser.addUiAppInManifest(appId)
+        .catch(err => { throw err; });
+    })
+
+    /**
+      * creates a bin file by copying and editing it
+      * [pkgid]/bin(dir)
+      *                 /[pkgid].[appid]
+      */
+    .then(() => {
+      console.log('Creating bin file');
+      const defaultBinPath =
+          path.join('..', 'tizen-app-template', 'pkgid.appid');
+      return Q.denodeify(fs.readFile)(defaultBinPath)
+        .then(data => {
+          // edit format string by 'string-format'
+          const binContent = format(data.toString(), { appid: appId });
+          const binPath = path.join(parser.get('exec_path'), 'bin',
+                                    parser.get('package').pkg_id +
+                                      '.' + appId);
+          return Q.denodeify(fs.writeFile)(binPath, binContent,
+                                           { mode: 0o775 });
+        });
+    })
+
+    /**
+      * creates directory and file
+      * [pkgid]/res(dir)
+      *                 /[appid]
+      *                         /index.js
+      */
+    .then(() => {
+      console.log('Creating app directory');
+      const appDir = path.join(parser.get('exec_path'), 'res', appId);
+      return Q.denodeify(fs.mkdir)(appDir);
+    })
+    .then(() => {
+      console.log('Creating index.js');
+      const defaultIndexPath =
+          path.join('..', 'tizen-app-template', 'index.js');
+      return Q.denodeify(fs.readFile)(defaultIndexPath)
+        .then(data => {
+          // edit format string by 'string-format'
+          const indexContent = format(data.toString(), { appid: appId });
+          const indexPath =
+              path.join(path.join(parser.get('exec_path'), 'res', appId),
+                        'index.js');
+          return Q.denodeify(fs.writeFile)(indexPath, indexContent,
+                                          { mode: 0o775 });
+        });
+    })
+
+    .catch(err => {
+      throw new JsnError('Creating app(' + appId + ') is failed: ' +
+                         err.message);
+    });
+  } // createApp
+} // create
diff --git a/jsn-cli/lib/help.js b/jsn-cli/lib/help.js
new file mode 100644 (file)
index 0000000..b2b9235
--- /dev/null
@@ -0,0 +1,156 @@
+'use strict'
+
+const CLI = 'jsn-cli';
+const CAUTION_EXECUTED = '%s command should be executed in project root ' +
+                       'where has tizen-manifest.xml';
+
+const help = module.exports;
+
+help.printAll = () => {
+  console.log('');
+  help.printSummary();
+  console.log('');
+  help.printCreate();
+  console.log('');
+  help.printList();
+  console.log('');
+  help.printRemove();
+  console.log('');
+  help.printSign();
+  console.log('');
+  help.printBuild();
+  console.log('');
+};
+
+help.printSummary = () => {
+  const summary = '$ ' + CLI + ' (command) (args0) (args1) [args_n] ...';
+  console.log('\n\t' + summary);
+  console.log('');
+};
+
+help.printCreate = () => {
+  const create = '$ ' + CLI + ' create ("package" or "app") (id)';
+  const createEx1 = 'ex) ' + CLI + ' create package pkgid';
+  const createEx2 = 'ex) ' + CLI + ' create app appid';
+  console.log('');
+  console.log('\t' + create);
+  console.log('\t\t' + createEx1);
+  console.log('\t\t' + createEx2);
+  console.log('\t\t\t !!! ' + CAUTION_EXECUTED, '"app"');
+  console.log('');
+};
+
+help.printList = () => {
+  const list = '$ ' + CLI + ' list';
+  console.log('');
+  console.log('\t' + list);
+  console.log('\t\t !!! ' + CAUTION_EXECUTED, '"list"');
+  console.log('');
+};
+
+help.printRemove = () => {
+  const remove = '$ ' + CLI + ' remove (appid)';
+  const removeEx = 'ex) ' + CLI + ' remove appa';
+  console.log('');
+  console.log('\t' + remove);
+  console.log('\t\t !!! ' + CAUTION_EXECUTED, '"remove"');
+  console.log('\t\t' + removeEx);
+  console.log('');
+};
+
+help.printSign = function() {
+  const sign = '$ ' + CLI + ' sign (signer) ' +
+             '(authorCAPath) (authorP12Path) (authorPwd) ' +
+             '(dist1P12Path) (dist1Pwd) (dist1CAPath) ' +
+             '[dist2P12Path, dist2Pwd, dist2CAPath, dist2RootPath]';
+  const signExplain = '(argv): must assign, [argv]: can be skipped, but ' +
+                    'if you not skip these, all of [4 parameters] should ' +
+                    'not be skipped';
+
+  const signer = '~/tizen-sdk/tools/ide/bin/native-signing';
+  const authorCAPath ='~/tizen-sdk/tools/certificate-generator/certificates/' +
+                    'developer/tizen-developer-ca.cer';
+  const authorP12Path = '~/tizen-sdk-data/keystore/author/yons.p12';
+  const authorPwd = '1234';
+  const dist1P12Path = '~/tizen-sdk/tools/certificate-generator/' +
+                     'certificates/distributor/tizen-distributor-signer.p12';
+  const dist1Pwd = 'tizenpkcs12passfordsigner';
+  const dist1CAPath = '~/tizen-sdk/tools/certificate-generator/certificates/' +
+                    'distributor/tizen-distributor-ca.cer';
+
+  const signEx = 'ex) ' + CLI + ' sign ' + signer + ' ' + authorCAPath +
+               ' ' + authorP12Path + ' ' + authorPwd + ' ' + dist1P12Path +
+               ' ' + dist1Pwd + ' ' + dist1CAPath;
+
+  console.log('');
+  console.log('\t' + sign);
+  console.log('\t' + signExplain);
+  console.log('\t\t !!! ' + CAUTION_EXECUTED, '"sign"');
+  console.log('\t\t' + signEx);
+  console.log('');
+}
+
+help.printBuild = () => {
+  const buildDefault = '$ ' + CLI + ' build';
+  const buildNoSign = '$ ' + CLI + ' build nosign';
+  const buildSign = '$ ' + CLI + ' build sign (signer) ' +
+                  '(authorCAPath) (authorP12Path) (authorPwd) ' +
+                  '(dist1P12Path) (dist1Pwd) (dist1CAPath) ' +
+                  '[dist2P12Path, dist2Pwd, dist2CAPath, dist2RootPath]';
+  const buildSignExplain = '(argv): must assign, [argv]: can be skipped, ' +
+                         'but if you not skip these, all of [4 parameters] ' +
+                         'should not be skipped';
+
+  const signer = '~/tizen-sdk/tools/ide/bin/native-signing';
+  const authorCAPath ='~/tizen-sdk/tools/certificate-generator/certificates/' +
+                    'developer/tizen-developer-ca.cer';
+  const authorP12Path = '~/tizen-sdk-data/keystore/author/yons.p12';
+  const authorPwd = '1234';
+  const dist1P12Path = '~/tizen-sdk/tools/certificate-generator/' +
+                     'certificates/distributor/tizen-distributor-signer.p12';
+  const dist1Pwd = 'tizenpkcs12passfordsigner';
+  const dist1CAPath = '~/tizen-sdk/tools/certificate-generator/certificates/' +
+                    'distributor/tizen-distributor-ca.cer';
+
+  const buildDefaultEx = 'ex) ' + CLI + ' build';
+  const buildNoSignEx = 'ex) ' + CLI + ' build nosign';
+  const buildSignEx = 'ex) ' + CLI + ' build sign ' + signer + ' ' +
+                    authorCAPath + ' ' + authorP12Path + ' ' +
+                    authorPwd + ' ' + dist1P12Path + ' ' +
+                    dist1Pwd + ' ' + dist1CAPath;
+
+  console.log('');
+  console.log('\t' + buildDefault);
+  console.log('\t' + buildNoSign);
+  console.log('\t\t' + 'build nosign == build (no parameters)')
+  console.log('\t' + buildSign);
+  console.log('\t' + buildSignExplain);
+  console.log('\t\t' + CAUTION_EXECUTED, '"build"');
+  console.log('\t\t' + buildDefaultEx);
+  console.log('\t\t' + buildNoSignEx);
+  console.log('\t\t' + buildSignEx);
+  console.log('');
+};
+
+help.print = cmd => {
+  switch(cmd) {
+    case 'create':
+    help.printCreate();
+    break;
+    case 'list':
+    help.printList();
+    break;
+    case 'remove':
+    help.printRemove();
+    break;
+    case 'sign':
+    help.printSign();
+    break;
+    case 'build':
+    help.printBuild();
+    break;
+    default:
+    help.printAll();
+    break;
+  }
+};
diff --git a/jsn-cli/lib/jsn_error.js b/jsn-cli/lib/jsn_error.js
new file mode 100644 (file)
index 0000000..647f88a
--- /dev/null
@@ -0,0 +1,12 @@
+'use strict'
+
+class JsnError extends Error {
+  constructor(message) {
+    super(message);
+    this.name = this.constructor.name;
+    this.message = message;
+    Error.captureStackTrace(this, this.constructor.name)
+  }
+}
+
+module.exports = JsnError;
diff --git a/jsn-cli/lib/list.js b/jsn-cli/lib/list.js
new file mode 100755 (executable)
index 0000000..96d4aaa
--- /dev/null
@@ -0,0 +1,27 @@
+'use strict'
+
+const path = require('path')
+const Q = require('q');
+
+const JsnError = require('./jsn_error');
+module.exports = function(argv, parser) {
+  return parser.parse()
+  .catch(err => {
+      throw new JsnError('Parsing manifest file has a problem: ' +
+                         err.message);
+    })
+  .then(() => {
+    console.log('Listing apps');
+    if (parser.get('parsed') && parser.get('has_app')) {
+      console.log('No.\tAppId\tAppType\tExec');
+      const len = parser.get('apps').length;
+      for (let i = 0; i < len; i++) {
+        const app = parser.get('apps')[i];
+        console.log((i+1) + '\t' + app.app_id + '\t' + app.type +
+                    '\t' + app.exec);
+      }
+    } else {
+      console.log('There is no app');
+    }
+  });
+};
diff --git a/jsn-cli/lib/parser.js b/jsn-cli/lib/parser.js
new file mode 100755 (executable)
index 0000000..3cfa479
--- /dev/null
@@ -0,0 +1,204 @@
+'use strict'
+
+const fs = require('fs');
+const path = require('path');
+const et = require('elementtree');
+const pd = require('pretty-data').pd;
+const Q = require('q');
+const format = require('string-template');
+
+const JsnError = require('./jsn_error');
+
+const MANIFEST_FILE = 'tizen-manifest.xml';
+
+class Parser {
+  // this constructor doesn't use any Promises
+  constructor(execPath) {
+    this.exec_path = execPath;
+    this.manifest_path = '';
+    this.has_package = false;
+    this.package = {};
+    this.has_app = false;
+    this.apps = [];
+
+    // parsed_ expresses done to parse
+    this.parsed = false;
+
+    // ready_ expresses ready for parsing
+    this.ready = (() => {
+      const maybeManifestPath = path.join(this.exec_path,
+                                          MANIFEST_FILE);
+      const existManifest = (path => {
+          try {
+            return (fs.statSync(path));
+          } catch(err) {
+            return false;
+          }
+      })(maybeManifestPath);
+      if (existManifest) {
+        this.manifest_path = maybeManifestPath;
+        return true;
+      } else {
+        return false;
+      }
+    })();
+  }
+
+  set(key, val) {
+    this[key] = val;
+  }
+
+  get(key) {
+    return this[key];
+  }
+
+  /**
+    * parses manifest file and its contents are parsed to Parser's
+    * members. this method returns a promise.
+    */
+  parse() {
+    if (this.ready === false) {
+      return Q.fcall(() => {
+        throw new JsnError('Parser is not ready. ' +
+                           'Checks whether the manifest file exists');
+      });
+    }
+
+    return Q.denodeify(fs.readFile)(this.manifest_path)
+    .then(data => {
+      const etree = et.parse(data.toString());
+
+      // parse package
+      const manifestElem = etree.getroot();
+      if (manifestElem) {
+        const pack = {
+          pkg_id: manifestElem.get('package'),
+          pkg_version: manifestElem.get('version'),
+          api_version: manifestElem.get('api-version')
+        };
+        this.package = pack;
+        this.has_package = true;
+      } else {
+        throw new JsnError('Can\'t get manifest element ' +
+                           'while parsing manifest file');
+      }
+
+      // parse apps
+      const uiAppElems = etree.findall('./ui-application');
+      if (uiAppElems) {
+        const len = uiAppElems.length;
+        this.has_app = (len > 0) ? true : false;
+        for (let i = 0; i < len; i++) {
+          const uiApp = uiAppElems[i];
+          const app = {
+            app_id: uiApp.get('appid'),
+            exec: uiApp.get('exec'),
+            type: uiApp.get('type')
+          };
+          this.apps[i] = app;
+        }
+      } else {
+        throw new JsnError('Can\'t get any ui app elements ' +
+                           'while parsing manifest file');
+      }
+      this.parsed = true;
+    })
+
+    .then(() => console.log('Parsing is successful'));
+  } // parse
+
+  addUiAppInManifest(appId) {
+    return Q.denodeify(fs.readFile)(this.manifest_path)
+    .then(data => {
+      const etree = et.parse(data.toString());
+      const subElement = et.SubElement;
+      let root = etree.getroot();
+      let uiAppElem = subElement(root, 'ui-application');
+      uiAppElem.set('appid', appId);
+      uiAppElem.set('exec', this.package.pkg_id + '.' + appId);
+      uiAppElem.set('type', 'jsapp');
+      uiAppElem.set('multiple', 'false');
+      uiAppElem.set('taskmanage', 'true');
+      uiAppElem.set('nodisplay', 'false');
+
+      let iconElem = subElement(uiAppElem, 'icon');
+      iconElem.text = 'icon.png';
+      let labelElem = subElement(uiAppElem, 'label');
+      labelElem.text = appId;
+
+      // print xml pretty
+      const xmlPretty = pd.xml(etree.write());
+      return xmlPretty;
+    })
+
+    .then(xmlPretty =>
+        Q.denodeify(fs.writeFile)(this.manifest_path, xmlPretty));
+  } // addUiAppInManifest
+
+  removeApp(appId) {
+    const app = this.getAppSync(appId);
+
+    return Q.fcall(() => {
+      if (app == null) {  // equals to ((=== null) or (=== undefined))
+        throw new JsnError(appId + ' doesn\'t exist');
+      }
+    })
+
+    .then(() => Q.denodeify(fs.readFile)(this.manifest_path))
+
+    .then(data => {
+      const etree = et.parse(data.toString());
+      let root = etree.getroot();
+      const uiAppElems = root.findall('./ui-application');
+      if (uiAppElems) {
+        const len = uiAppElems.length;
+        for (let i = 0; i < len; i++) {
+          const uiApp = uiAppElems[i];
+          if (uiApp.get('appid') == appId) {
+            root.remove(uiApp);
+            const xmlPretty = pd.xml(etree.write());
+            return xmlPretty;
+          }
+        }
+      }
+      throw new JsnError('Can\'t get valid ui-application element' +
+                         'while parsing mafniest file');
+    })
+
+    .then(xmlPretty =>
+        Q.denodeify(fs.writeFile)(this.manifest_path, xmlPretty))
+
+    .then(() => {
+      const len = this.apps.length;
+      for (let i = 0; i < len; i++) {
+        const app = this.apps[i];
+        if (app.app_id == appId) {
+          this.apps.splice(i, 1);
+          break;
+        }
+      }
+      this.has_app = (this.apps.length > 0) ? true : false;
+    });
+  } // removeApp
+
+  // it is not async, so adds 'sync' to the its name
+  getAppSync(appId) {
+    if (!this.has_app)
+      return null;
+    const len = this.apps.length;
+    for (let i = 0; i < len; i++) {
+      const app = this.apps[i];
+      if (app.app_id == appId)
+        return app;
+    }
+    return null;
+  } // getAppSync
+} // Parser
+
+/**
+  * This Parser module is a class. Parser contains actions such as parsing
+  * manifest file, and adding/removing 'ui-application' element.
+  * Parser does 'async' by 'Promises' with 'q'.
+  * (Reference to 'cli.js')
+  */
+module.exports = Parser;
diff --git a/jsn-cli/lib/remove.js b/jsn-cli/lib/remove.js
new file mode 100755 (executable)
index 0000000..070573b
--- /dev/null
@@ -0,0 +1,49 @@
+'use strict'
+
+const path = require('path');
+const Q = require('q');
+
+const shellUtil = require('./shell_util');
+const JsnError = require('./jsn_error');
+
+module.exports = function(argv, parser) {
+  const appId = argv.args_[0];
+
+  return Q.fcall(() => {
+    if (appId === undefined)
+      throw new JsnError('You should type appid');
+
+    return parser.parse()
+    .catch(err => {
+      throw new JsnError('Parsing manifest file has a problem: ' +
+                         err.message);
+    })
+  })
+
+  .then(() => {
+    console.log('Removing an app');
+    if (parser.get('parsed') && parser.get('has_app')) {
+      const app = parser.getAppSync(appId);
+      if (app === null) {
+        throw new JsnError('There is no app');
+      }
+
+      console.log('Deleting directories & files');
+      const execFileInBin = path.join(parser.get('exec_path'),
+                                      'bin', app.exec);
+      const appDirInRes = path.join(parser.get('exec_path'),
+                                    'res', app.app_id);
+      const shPromises = ['rm ' + execFileInBin, 'rm -rf ' + appDirInRes]
+                         .map(shellUtil.shExec);
+      return Q.all(shPromises);
+    } else {
+      throw new JsnError('There is no app');
+    }
+  })
+
+  .then(() => {
+    console.log('Deleting ui-application element in manifest file');
+    return parser.removeApp(appId)
+      .then(() => console.log('Done to remove ' + appId));
+  });
+};
diff --git a/jsn-cli/lib/shell_util.js b/jsn-cli/lib/shell_util.js
new file mode 100644 (file)
index 0000000..57af07e
--- /dev/null
@@ -0,0 +1,16 @@
+'use strict'
+
+const Q = require('q');
+const sh = require('shelljs');
+
+const shellUtil = module.exports;
+
+// This is utility function for easing usage of shell script command.
+shellUtil.shExec = cmd => {
+  const deferred = Q.defer();
+  sh.exec(cmd, {async:true, silent:true}, (code) => {
+    if (code != 0) deferred.reject(cmd + ' returns ' + code);
+    else deferred.resolve(code);
+  });
+  return deferred.promise;
+};
\ No newline at end of file
diff --git a/jsn-cli/lib/sign.js b/jsn-cli/lib/sign.js
new file mode 100644 (file)
index 0000000..a2554f1
--- /dev/null
@@ -0,0 +1,116 @@
+'use strict'
+
+const fs = require('fs');
+const path = require('path');
+const Q = require('q');
+
+const JsnError = require('./jsn_error');
+const shellUtil = require('./shell_util');
+
+module.exports = sign;
+
+/**
+  * Sign command should be executed before build command.
+  * If you execute sign command after build command, the sign command
+  * deletes the tpk file which created by build command.
+  */
+function sign(argv, parser) {
+  const argc = argv.args_.length;
+  const signCmdArray = [];    // it will be used as command
+
+  return Q.fcall(() => {
+    // if argc is not 11, argc's num is equal or greater than 7 minimally
+    if (argc !== 7 && argc !== 11) {
+      throw new JsnError(
+          'You should type sign command with proper arguments\n' +
+          'Command you typed: ' + argv.args_);
+    }
+  })
+
+  .then(() => {
+    /**
+      * argv.args_ are parameters for sign cmd
+      * sign cmd:
+      *     [signer] [targetDir] [authorCAPath] [authorP12Path] [authorPwd]
+      *     [dist1P12Path] [dist1Pwd] [dist1CAPath]
+      *     [dist2P12Path] [dist2Pwd] [dist2CAPath] [dist2RootPath]
+      * if argc is not 11,
+      *   fill them(11 - num of empty parameters) with ' ' (one space string)
+      */
+
+    // caution: targetDir is not from args_
+    const targetDir = parser.get('exec_path');
+    const signer = argv.args_[0];
+    const authorCAPath = argv.args_[1];
+    const authorP12Path = argv.args_[2];
+    const authorPwd = argv.args_[3];
+    const dist1P12Path = argv.args_[4];
+    const dist1Pwd = argv.args_[5];
+    const dist1CAPath = argv.args_[6];
+    const dist2P12Path = argv.args_[7] || '" "';
+    const dist2Pwd = argv.args_[8] || '" "';
+    const dist2CAPath = argv.args_[9] || '" "';
+    const dist2RootPath = argv.args_[10] || '" "';
+
+    // verify arguments for sign command by checking wheter files exist
+    console.log('Verifing arguments for signing');
+    let verifyArgv = [signer, targetDir, authorCAPath, authorP12Path,
+                      dist1P12Path, dist1CAPath];
+    if (argc === 11) {
+      verifyArgv =
+          verifyArgv.concat([dist2P12Path, dist2CAPath, dist2RootPath]);
+    }
+
+    const promToExistFiles = verifyArgv.map(path => {
+      const deferred = Q.defer();
+      fs.stat(path, (err, stats) => {
+        if (err) deferred.reject(err);
+        else deferred.resolve(stats);
+      });
+      return deferred.promise;
+    });
+
+    return Q.all(promToExistFiles)
+    .then(
+      () => {
+        let signArgv = [signer, targetDir, authorCAPath, authorP12Path,
+                        authorPwd, dist1P12Path, dist1Pwd, dist1CAPath];
+        if (argc === 7) {
+          signArgv = signArgv.concat([dist2P12Path, dist2Pwd,
+                                      dist2CAPath, dist2RootPath]);
+        }
+        Array.prototype.push.apply(signCmdArray, signArgv);
+      }, err => {
+      throw new JsnError('Can\'t verify arguments: ' + err.message);
+    })
+  })
+
+  .then(() => parser.parse())
+  .catch(err => {
+      throw new JsnError('Parsing manifest file has a problem: ' +
+                         err.message);
+  })
+
+  /**
+    * if [*.tpk, author-signature.xml, .manifest.tmp, signature1.xml]
+    * exists, remove all of them
+    */
+  .then(() => {
+    console.log('Signing package');
+    console.log('Checking previous signing');
+    const shExecPromises = ['*.tpk', 'author-signature.xml',
+                            '.manifest.tmp', 'signature1.xml']
+        .map(dir => ('rm ' + path.join(parser.get('exec_path'), dir)))
+        .map(shellUtil.shExec);
+    return Q.all(shExecPromises)
+      // If above file doesn't exist, it can fail. But it needs to pass.
+      .fail(() => {});
+  })
+
+  // try signing
+  .then(() => {
+    console.log('Trying signing from %s', signCmdArray[0]);
+    const signCmd = signCmdArray.join(' ');
+    return shellUtil.shExec(signCmd);
+  });
+}
diff --git a/jsn-cli/package.json b/jsn-cli/package.json
new file mode 100755 (executable)
index 0000000..f218641
--- /dev/null
@@ -0,0 +1,23 @@
+{
+  "name": "jsn-cli",
+  "version": "0.1.0",
+  "preferGlobal": "true",
+  "description": "command-line interface for js native runtime",
+  "main": "jsn-cli",
+  "bin": {
+    "jsn-cli": "./bin/jsn-cli"
+  },
+  "engines": { "node": ">= 4.2.1" },
+  "dependencies": {
+      "elementtree": "0.1.6",
+      "pretty-data": "0.40.0",
+      "q": "1.4.1",
+      "shelljs": "0.5.3",
+      "string-template": "0.2.1"
+    },
+  "author": { "name": "Yongseop Kim", "email": "yons.kim@samsung.com" },
+  "contributors": [
+    { "name": "Yongseop Kim", "email": "yons.kim@samsung.com" },
+    { "name": "Joonghyun Cho", "email": "jh5.cho@samsung.com" }
+  ]
+}
diff --git a/jsn-cli/tizen-app-template/icon.png b/jsn-cli/tizen-app-template/icon.png
new file mode 100755 (executable)
index 0000000..9765b1b
Binary files /dev/null and b/jsn-cli/tizen-app-template/icon.png differ
diff --git a/jsn-cli/tizen-app-template/index.js b/jsn-cli/tizen-app-template/index.js
new file mode 100755 (executable)
index 0000000..a0b992f
--- /dev/null
@@ -0,0 +1,25 @@
+#!/usr/bin/env node
+module.paths = ['/usr/lib/js-binding/'].concat(module.paths);
+var appfw = require('appfw');
+
+appfw.on('create' , function() {
+  console.log('!! created');
+});
+
+appfw.on('service' , function() {
+  console.log('!! service!!!');
+});
+
+appfw.on('pause' , function() {
+  console.log('!! pause!!!');
+});
+
+appfw.on('resume' , function() {
+  console.log('!! resume!!!');
+});
+
+appfw.on('terminate' , function() {
+  console.log('!! terminate!!!');
+});
+
+appfw.init('{appid}');
diff --git a/jsn-cli/tizen-app-template/pkgid.appid b/jsn-cli/tizen-app-template/pkgid.appid
new file mode 100755 (executable)
index 0000000..44d1da3
--- /dev/null
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+module.paths = ['/usr/lib/js-binding/'].concat(module.paths);
+require('../res/{appid}');
diff --git a/jsn-cli/tizen-app-template/tizen-manifest.xml b/jsn-cli/tizen-app-template/tizen-manifest.xml
new file mode 100755 (executable)
index 0000000..2662f25
--- /dev/null
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns="http://tizen.org/ns/packages" api-version="3.0" package="{pkgid}" version="1.0.0">
+</manifest>
diff --git a/node-xwalk/CMakeLists.txt b/node-xwalk/CMakeLists.txt
new file mode 100644 (file)
index 0000000..2c6ded1
--- /dev/null
@@ -0,0 +1,33 @@
+SET(TARGET_XWALK "node-xwalk")
+SET(TARGET_XWALK_NATIVE "native")
+SET(TARGET_XWALK_SRC ${PROJECT_SOURCE_DIR}/node-xwalk)
+
+# Copy Project
+INSTALL(FILES
+    ${TARGET_XWALK_SRC}/package.json
+    DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_XWALK}
+)
+
+INSTALL(FILES
+    ${TARGET_XWALK_SRC}/lib/loader.js
+    DESTINATION ${GLOBAL_NODE_MODULE_PATH}/${TARGET_XWALK}/lib
+)
+
+# Native Binding Module
+PKG_CHECK_MODULES(TARGET_XWALK_NATIVE_DEPS REQUIRED
+    nodejs
+)
+
+INCLUDE_DIRECTORIES (
+  ${TARGET_XWALK_SRC}/src
+  ${TARGET_XWALK_NATIVE_DEPS_INCLUDE_DIRS}
+)
+ADD_LIBRARY(${TARGET_XWALK_NATIVE} MODULE
+  ${TARGET_XWALK_SRC}/src/native_binding.cc
+  ${TARGET_XWALK_SRC}/src/extension.cc
+  ${TARGET_XWALK_SRC}/src/extension_adapter.cc
+)
+SET_TARGET_PROPERTIES(${TARGET_XWALK_NATIVE} PROPERTIES PREFIX "" SUFFIX ".node")
+INSTALL(TARGETS ${TARGET_XWALK_NATIVE} DESTINATION
+        ${GLOBAL_NODE_MODULE_PATH}/${TARGET_XWALK}/build/Release)
+
diff --git a/node-xwalk/CPPLINT.cfg b/node-xwalk/CPPLINT.cfg
new file mode 100644 (file)
index 0000000..51cbf64
--- /dev/null
@@ -0,0 +1,3 @@
+set noparent
+filter=-build/header_guard,-build/include
+linelength=80
\ No newline at end of file
diff --git a/node-xwalk/README.md b/node-xwalk/README.md
new file mode 100644 (file)
index 0000000..cca6cb4
--- /dev/null
@@ -0,0 +1,46 @@
+## node-xwalk
+Crosswalk Extension Loader for Node.js allows you to use crosswalk extensions in Node.js environment.
+
+* [Crosswalk Extension](https://github.com/crosswalk-project/crosswalk-website/wiki/Crosswalk-Extensions)
+
+  Crosswalk Extensions allow exposing new functionality to JavaScript environment of your application. 
+  This functionality can be implemented in native code.
+  * [Writing a Crosswalk Extension](https://github.com/crosswalk-project/crosswalk-website/wiki/Writing-a-Crosswalk-Extension)
+  * [Writing a GLib based Crosswalk Extension](https://github.com/crosswalk-project/crosswalk-website/wiki/Writing-a-glib-based-Crosswalk-Extension)
+
+Supported platforms: **Linux** | Other platforms will be supported soon.
+
+**Echo Sample**
+```javascript
+var xwalk = require('node-xwalk');
+var echo = xwalk.require('echo');
+echo.echo("Hello Async!!", function(msg) {
+  console.log(msg);
+});
+console.log(echo.syncEcho("Hello Sync!!"));
+```
+**Output**
+```
+Instance 1 created!
+Hello Async!!
+Hello Sync!!
+```
+
+## Getting started
+From your project directory, run
+```
+$ npm install https://github.com/WonyoungChoi/node-xwalk
+```
+This will download and build node-xwalk in ```./node_modules/```. 
+Then copy the example files from [examples/](https://github.com/WonyoungChoi/node-xwalk/tree/master/examples) directory in this project.
+
+Build and test a crosswalk extension *'echo example'* with Makefile.
+```
+$ make
+$ node echo.js
+```
+
+## License
+
+**node-xwalk** is available under the BSD licenses, see our `LICENSE` file.
+
diff --git a/node-xwalk/binding.gyp b/node-xwalk/binding.gyp
new file mode 100644 (file)
index 0000000..059f8e3
--- /dev/null
@@ -0,0 +1,15 @@
+{
+  "targets": [
+    {
+      "target_name": "native",
+      "sources": [
+        "src/native_binding.cc",
+        "src/extension.cc",
+        "src/extension_adapter.cc"
+      ],
+      "include_dirs": [
+        "src"
+      ]
+    }
+  ]
+}
\ No newline at end of file
diff --git a/node-xwalk/examples/echo/Makefile b/node-xwalk/examples/echo/Makefile
new file mode 100644 (file)
index 0000000..5e8f3a8
--- /dev/null
@@ -0,0 +1,12 @@
+CC=gcc
+CFLAGS=-fPIC -I../../src
+LDFLAGS=--shared
+
+TARGET=libecho.so
+SRCS=echo_extension.c
+
+$(TARGET): $(SRCS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
+
+clean:
+       rm -f $(TARGET)
\ No newline at end of file
diff --git a/node-xwalk/examples/echo/echo.js b/node-xwalk/examples/echo/echo.js
new file mode 100644 (file)
index 0000000..f3f2c5b
--- /dev/null
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+
+// Copyright (c) 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.
+
+var xwalk = require('node-xwalk');
+var echo = xwalk.require('echo');
+
+echo.echo("Hello Async!!", function(msg) {
+  console.log("Async callback: " + msg);
+});
+
+var ret = echo.syncEcho("Hello Sync!!");
+console.log("Sync return: " + ret);
+
diff --git a/node-xwalk/examples/echo/echo_extension.c b/node-xwalk/examples/echo/echo_extension.c
new file mode 100644 (file)
index 0000000..78b7986
--- /dev/null
@@ -0,0 +1,70 @@
+// 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.
+
+#if defined(__cplusplus)
+#error "This file is written in C to make sure the C API works as intended."
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "xwalk/extensions/public/XW_Extension.h"
+#include "xwalk/extensions/public/XW_Extension_SyncMessage.h"
+
+XW_Extension g_extension = 0;
+const XW_CoreInterface* g_core = NULL;
+const XW_MessagingInterface* g_messaging = NULL;
+const XW_Internal_SyncMessagingInterface* g_sync_messaging = NULL;
+
+void instance_created(XW_Instance instance) {
+  printf("Instance %d created!\n", instance);
+}
+
+void instance_destroyed(XW_Instance instance) {
+  printf("Instance %d destroyed!\n", instance);
+}
+
+void handle_message(XW_Instance instance, const char* message) {
+  g_messaging->PostMessage(instance, message);
+}
+
+void handle_sync_message(XW_Instance instance, const char* message) {
+  g_sync_messaging->SetSyncReply(instance, message);
+}
+
+void shutdown(XW_Extension extension) {
+  printf("Shutdown\n");
+}
+
+int32_t XW_Initialize(XW_Extension extension, XW_GetInterface get_interface) {
+  static const char* kAPI =
+      "var echoListener = null;"
+      "extension.setMessageListener(function(msg) {"
+      "  if (echoListener instanceof Function) {"
+      "    echoListener(msg);"
+      "  };"
+      "});"
+      "exports.echo = function(msg, callback) {"
+      "  echoListener = callback;"
+      "  extension.postMessage(msg);"
+      "};"
+      "exports.syncEcho = function(msg) {"
+      "  return extension.internal.sendSyncMessage(msg);"
+      "};";
+
+  g_extension = extension;
+  g_core = get_interface(XW_CORE_INTERFACE);
+  g_core->SetExtensionName(extension, "echo");
+  g_core->SetJavaScriptAPI(extension, kAPI);
+  g_core->RegisterInstanceCallbacks(
+      extension, instance_created, instance_destroyed);
+  g_core->RegisterShutdownCallback(extension, shutdown);
+
+  g_messaging = get_interface(XW_MESSAGING_INTERFACE);
+  g_messaging->Register(extension, handle_message);
+
+  g_sync_messaging = get_interface(XW_INTERNAL_SYNC_MESSAGING_INTERFACE);
+  g_sync_messaging->Register(extension, handle_sync_message);
+
+  return XW_OK;
+}
diff --git a/node-xwalk/examples/echo/libecho.so b/node-xwalk/examples/echo/libecho.so
new file mode 100755 (executable)
index 0000000..f0737f5
Binary files /dev/null and b/node-xwalk/examples/echo/libecho.so differ
diff --git a/node-xwalk/examples/echo_binary/Makefile b/node-xwalk/examples/echo_binary/Makefile
new file mode 100644 (file)
index 0000000..1f3fe17
--- /dev/null
@@ -0,0 +1,12 @@
+CC=gcc
+CFLAGS=-fPIC -I../../src
+LDFLAGS=--shared
+
+TARGET=libecho2.so
+SRCS=echo_extension_messaging_2.c
+
+$(TARGET): $(SRCS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $^
+
+clean:
+       rm -f $(TARGET)
\ No newline at end of file
diff --git a/node-xwalk/examples/echo_binary/echo2.js b/node-xwalk/examples/echo_binary/echo2.js
new file mode 100644 (file)
index 0000000..2eabd64
--- /dev/null
@@ -0,0 +1,33 @@
+#!/usr/bin/env node
+
+// Copyright (c) 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.
+
+var xwalk = require('node-xwalk');
+var echo2 = xwalk.require('echo2');
+
+// Test async messaging
+echo2.echo("Hello Async!!", function(msg) {
+  console.log(msg);
+  // Test async messaging with ArrayBuffer
+  var dataLength = 32;
+  var buffer = new ArrayBuffer(dataLength + 4);
+  var uint32View = new Uint32Array(buffer, 0, 1);
+  uint32View[0] = dataLength;
+  var uint8View = new Uint8Array(buffer, 4, dataLength);
+  for (var i=0; i < dataLength; i++) {
+    uint8View[i] = i;
+  }
+  echo2.echoBinary(buffer, function(msg) {
+    if (!(msg instanceof ArrayBuffer))
+      throw "message is not binary.";
+    var retUint32View = new Uint32Array(msg, 0, 1);
+    var retLength = retUint32View[0];
+    if (retLength != dataLength)
+      throw "message length doesn't match.";
+    var retUint8View = new Uint8Array(msg, 4, retLength);
+    console.log(retUint8View);
+  });
+});
+
diff --git a/node-xwalk/examples/echo_binary/echo_extension_messaging_2.c b/node-xwalk/examples/echo_binary/echo_extension_messaging_2.c
new file mode 100644 (file)
index 0000000..d19cb72
--- /dev/null
@@ -0,0 +1,79 @@
+// Copyright (c) 2015 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.
+
+#if defined(__cplusplus)
+#error "This file is written in C to make sure the C API works as intended."
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include "xwalk/extensions/public/XW_Extension.h"
+#include "xwalk/extensions/public/XW_Extension_Message_2.h"
+
+XW_Extension g_extension = 0;
+const XW_CoreInterface* g_core = NULL;
+const XW_MessagingInterface2* g_messaging_2 = NULL;
+
+void instance_created(XW_Instance instance) {
+  printf("Instance %d created!\n", instance);
+}
+
+void instance_destroyed(XW_Instance instance) {
+  printf("Instance %d destroyed!\n", instance);
+}
+
+void handle_message(XW_Instance instance, const char* message) {
+  g_messaging_2->PostMessage(instance, message);
+}
+
+void handle_binary_message(
+    XW_Instance instance, const char* message, const size_t size) {
+  g_messaging_2->PostBinaryMessage(instance, message, size);
+}
+
+void shutdown(XW_Extension extension) {
+  printf("Shutdown\n");
+}
+
+int32_t XW_Initialize(XW_Extension extension, XW_GetInterface get_interface) {
+  static const char* kAPI =
+      "var echoListener = null;"
+      "var echoBinaryListener = null;"
+      "extension.setMessageListener(function(msg) {"
+      "  if (echoListener instanceof Function) {"
+      "    if (msg instanceof ArrayBuffer) {"
+      "      echoBinaryListener(msg);"
+      "    } else {"
+      "      echoListener(msg);"
+      "    }"
+      "  }"
+      "});"
+      "exports.echo = function(msg, callback) {"
+      "  echoListener = callback;"
+      "  extension.postMessage(msg);"
+      "};"
+      "exports.echoBinary = function(msg, callback) {"
+      "  echoBinaryListener = callback;"
+      "  extension.postMessage(msg);"
+      "};";
+
+  g_extension = extension;
+  g_core = get_interface(XW_CORE_INTERFACE);
+  if (g_core == NULL)
+    return XW_ERROR;
+  g_core->SetExtensionName(extension, "echo2");
+  g_core->SetJavaScriptAPI(extension, kAPI);
+  g_core->RegisterInstanceCallbacks(
+      extension, instance_created, instance_destroyed);
+  g_core->RegisterShutdownCallback(extension, shutdown);
+
+  g_messaging_2 = get_interface(XW_MESSAGING_INTERFACE_2);
+  if (g_messaging_2 == NULL)
+    return XW_ERROR;
+  g_messaging_2->Register(extension, handle_message);
+  g_messaging_2->RegisterBinaryMesssageCallback(
+      extension, handle_binary_message);
+
+  return XW_OK;
+}
diff --git a/node-xwalk/examples/echo_binary/libecho2.so b/node-xwalk/examples/echo_binary/libecho2.so
new file mode 100755 (executable)
index 0000000..abe17dd
Binary files /dev/null and b/node-xwalk/examples/echo_binary/libecho2.so differ
diff --git a/node-xwalk/lib/loader.js b/node-xwalk/lib/loader.js
new file mode 100644 (file)
index 0000000..ff63d84
--- /dev/null
@@ -0,0 +1,123 @@
+// Copyright (c) 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.
+
+var fs = require('fs');
+var path = require('path');
+var process = require('process');
+
+var native_ = require("../build/Release/native.node");
+
+function error(msg) {
+  console.error("ERR: " + msg);
+}
+
+// Cached set of extension modules
+var extensions_ = {};
+
+var ExtensionModule = function(ext_name, ext_path) {
+  this.extension_name = ext_name;
+  this.extension_path = ext_path;
+};
+
+ExtensionModule.prototype.load = function() {
+  this.extension_info = native_.getExtensionInfo(this.extension_path);
+  if (!this.extension_info) {
+    error('Error during get information of extension "' +
+          this.extension_name + '"');
+    return false;
+  }
+
+  if (this.extension_info.name !== this.extension_name) {
+    error('Extension name does not match. ' +
+          'required = ' + this.extension_name +
+          ', loaded = ' + this.extension_info.name);
+    return false;
+  }
+
+  this.instance_id = native_.createInstance(this.extension_info.extension_id);
+  if (!this.instance_id) {
+    error('Error during creating instance of extension "' +
+          this.extension_name + '"');
+    return false;
+  }
+
+  var jscode =
+    '(function(extension) {' +
+    '  extension.internal = { instance_id: extension.instance_id };' +
+    '  extension.internal.sendSyncMessage = extension.sendSyncMessage;' +
+    '  delete extension.sendSyncMessage;' +
+    '  var exports = {}; ' +
+    '  (function() {\'use strict\'; ' + this.extension_info.jsapi + '})();' +
+    '  return exports;' +
+    '});';
+  try {
+    var func = eval(jscode);
+    this.instance = func({
+      instance_id : this.instance_id,
+      postMessage: function(msg) {
+        native_.postMessage(this.instance_id, msg);
+      },
+      sendSyncMessage: function(msg) {
+        return native_.sendSyncMessage(this.instance_id, msg);
+      },
+      setMessageListener: function(fn) {
+        native_.setMessageListener(this.instance_id, fn);
+      }
+    });
+    return true;
+  } catch (err) {
+    error('Error during loading extension "' +
+          this.extension_name + '"');
+    return false;
+  }
+
+  return false;
+};
+
+var ExtensionLoader = function() {
+  this.extension_paths = [
+    process.cwd(),
+    path.join(process.cwd(), 'xwalk_extensions')
+  ];
+};
+
+ExtensionLoader.prototype.findExtensionInPath = function(name) {
+  for (var i in this.extension_paths) {
+    var ext_path = path.join(this.extension_paths[i],
+                             'lib' + name + '.so');
+    try {
+      fs.accessSync(ext_path, fs.R_OK);
+      return ext_path;
+    } catch (err) {
+      continue;
+    }
+  }
+  return undefined;
+};
+
+ExtensionLoader.prototype.setRuntimeVariable = function(key, value) {
+  native_.setRuntimeVariable(key, value);
+};
+
+ExtensionLoader.prototype.require = function(name) {
+  if (extensions_.hasOwnProperty(name)) {
+    return extensions_[name].instance;
+  }
+
+  var ext_path = this.findExtensionInPath(name);
+  if (!ext_path) {
+    error('Can not find extension "' + name + '"');
+    return undefined;
+  }
+
+  var ext = new ExtensionModule(name, ext_path);
+  if (ext.load()) {
+    extensions_[name] = ext;
+    return extensions_[name].instance;
+  }
+
+  return undefined;
+};
+
+module.exports = new ExtensionLoader;
diff --git a/node-xwalk/package.json b/node-xwalk/package.json
new file mode 100644 (file)
index 0000000..54b2e17
--- /dev/null
@@ -0,0 +1,13 @@
+{
+  "name": "node-xwalk",
+  "version": "0.9.0",
+  "description": "Crosswalk extension loader for Node.JS",
+  "main": "./lib/loader.js",
+  "author": {
+    "name": "Wonyoung Choi",
+    "email": "wy80.choi@samsung.com"
+  },
+  "scripts": {
+    "install": "node-gyp rebuild"
+  }
+}
\ No newline at end of file
diff --git a/node-xwalk/packaging/node-xwalk.manifest b/node-xwalk/packaging/node-xwalk.manifest
new file mode 100644 (file)
index 0000000..75b0fa5
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
diff --git a/node-xwalk/packaging/node-xwalk.spec b/node-xwalk/packaging/node-xwalk.spec
new file mode 100644 (file)
index 0000000..14b2a78
--- /dev/null
@@ -0,0 +1,45 @@
+Name:       node-xwalk
+Summary:    Crosswalk extension loader for Node.JS
+Version:    0.9.0
+Release:    1
+Group:      Development/Libraries
+License:    BSD-3-Clause
+URL:        https:://github.com/WonyoungChoi/node-xwalk
+Source0:    %{name}-%{version}.tar.gz
+Source1:    %{name}.manifest
+
+BuildRequires: cmake
+BuildRequires: pkgconfig(nodejs)
+Requires: nodejs
+
+%description
+Crosswalk Extension Loader for Node.JS allows you
+to use crosswalk extensions in Node.JS environment.
+
+%prep
+%setup -q
+cp %{SOURCE1} .
+
+%build
+mkdir -p cmake_build_tmp
+cd cmake_build_tmp
+
+cmake .. \
+        -DCMAKE_INSTALL_PREFIX=%{prefix}
+
+make %{?jobs:-j%jobs}
+
+%install
+rm -rf %{buildroot}
+mkdir -p %{buildroot}/usr/share/license
+cp LICENSE %{buildroot}/usr/share/license/%{name}
+cd cmake_build_tmp
+%make_install
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%manifest %{name}.manifest
+%{_datadir}/license/%{name}
+/usr/local/lib/node_modules/%{name}/*
diff --git a/node-xwalk/src/extension.cc b/node-xwalk/src/extension.cc
new file mode 100644 (file)
index 0000000..69ca62d
--- /dev/null
@@ -0,0 +1,162 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 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 "extension.h"
+
+#include <dlfcn.h>
+#include <string.h>
+
+#include "extension_adapter.h"
+#include "log.h"
+
+namespace xwalk {
+namespace extensions {
+
+Extension::Extension(const std::string& path)
+  : initialized_(false),
+    library_path_(path),
+    xw_extension_(0),
+    created_instance_callback_(NULL),
+    destroyed_instance_callback_(NULL),
+    shutdown_callback_(NULL),
+    handle_msg_callback_(NULL),
+    handle_sync_msg_callback_(NULL),
+    handle_binary_msg_callback_(NULL) {
+}
+
+Extension::~Extension() {
+  if (!initialized_)
+    return;
+
+  if (shutdown_callback_)
+    shutdown_callback_(xw_extension_);
+  ExtensionAdapter::GetInstance()->UnregisterExtension(this);
+}
+
+bool Extension::Initialize() {
+  if (initialized_)
+    return true;
+
+  void* handle = dlopen(library_path_.c_str(), RTLD_LAZY);
+  if (!handle) {
+    LOGE("Error loading extension '%s' : %s",
+         library_path_.c_str(), dlerror());
+    return false;
+  }
+
+  XW_Initialize_Func initialize = reinterpret_cast<XW_Initialize_Func>(
+      dlsym(handle, "XW_Initialize"));
+  if (!initialize) {
+    LOGE("Error loading extension '%s' : "
+         "couldn't get XW_Initialize function",
+         library_path_.c_str());
+    dlclose(handle);
+    return false;
+  }
+
+  ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
+  xw_extension_ = adapter->GetNextXWExtension();
+  adapter->RegisterExtension(this);
+
+  int ret = initialize(xw_extension_, ExtensionAdapter::GetInterface);
+  if (ret != XW_OK) {
+    LOGE("Error loading extension '%s' : "
+         "XW_Initialize function returned error value.",
+         library_path_.c_str());
+    dlclose(handle);
+    return false;
+  }
+
+  initialized_ = true;
+  return true;
+}
+
+ExtensionInstance* Extension::CreateInstance() {
+  ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
+  XW_Instance xw_instance = adapter->GetNextXWInstance();
+  return new ExtensionInstance(this, xw_instance);
+}
+
+void Extension::GetRuntimeVariable(const char* key,
+                                   char* value,
+                                   size_t value_len) {
+  if (runtime_variable_callback_) {
+    std::string ret = runtime_variable_callback_(key);
+    strncpy(value, ret.data(), value_len);
+  }
+}
+
+int Extension::CheckAPIAccessControl(const char* /*api_name*/) {
+  // Not Supported
+  return XW_OK;
+}
+
+int Extension::RegisterPermissions(const char* /*perm_table*/) {
+  // Not Supported
+  return XW_OK;
+}
+
+ExtensionInstance::ExtensionInstance(Extension* extension,
+                                     XW_Instance xw_instance)
+  : extension_(extension),
+    xw_instance_(xw_instance),
+    instance_data_(NULL) {
+  ExtensionAdapter::GetInstance()->RegisterInstance(this);
+  XW_CreatedInstanceCallback callback =
+      extension_->created_instance_callback_;
+  if (callback)
+    callback(xw_instance_);
+}
+
+ExtensionInstance::~ExtensionInstance() {
+  XW_DestroyedInstanceCallback callback =
+      extension_->destroyed_instance_callback_;
+  if (callback)
+    callback(xw_instance_);
+  ExtensionAdapter::GetInstance()->UnregisterInstance(this);
+}
+
+void ExtensionInstance::HandleMessage(const std::string& msg) {
+  XW_HandleMessageCallback callback =
+      extension_->handle_msg_callback_;
+  if (callback)
+    callback(xw_instance_, msg.c_str());
+}
+
+void ExtensionInstance::HandleMessage(const char* msg, const size_t size) {
+  XW_HandleBinaryMessageCallback callback =
+      extension_->handle_binary_msg_callback_;
+  if (callback)
+    callback(xw_instance_, msg, size);
+}
+
+void ExtensionInstance::HandleSyncMessage(const std::string& msg) {
+  XW_HandleSyncMessageCallback callback =
+      extension_->handle_sync_msg_callback_;
+  if (callback) {
+    callback(xw_instance_, msg.c_str());
+  }
+}
+
+void ExtensionInstance::PostMessageToJS(const std::string& msg) {
+  if (post_message_callback_) {
+    post_message_callback_(msg.c_str(), 0, false);
+  }
+}
+
+void ExtensionInstance::PostMessageToJS(const char* msg, size_t size) {
+  if (post_message_callback_) {
+    post_message_callback_(msg, size, true);
+  }
+}
+
+void ExtensionInstance::SyncReplyToJS(const std::string& reply) {
+  if (send_sync_reply_callback_) {
+    send_sync_reply_callback_(reply.c_str(), 0, false);
+  }
+}
+
+}  // namespace extensions
+}  // namespace xwalk
diff --git a/node-xwalk/src/extension.h b/node-xwalk/src/extension.h
new file mode 100644 (file)
index 0000000..cb89c17
--- /dev/null
@@ -0,0 +1,132 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 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 NODE_XWALK_EXTENSIONS_EXTENSION_H_
+#define NODE_XWALK_EXTENSIONS_EXTENSION_H_
+
+#include <functional>
+#include <map>
+#include <string>
+#include <vector>
+
+#include "xwalk/extensions/public/XW_Extension.h"
+#include "xwalk/extensions/public/XW_Extension_Message_2.h"
+#include "xwalk/extensions/public/XW_Extension_SyncMessage.h"
+
+namespace xwalk {
+namespace extensions {
+
+class ExtensionAdapter;
+class ExtensionInstance;
+
+class Extension {
+ public:
+  typedef std::function<std::string(const std::string&)>
+      RuntimeVariableCallback;
+
+  explicit Extension(const std::string& path);
+  virtual ~Extension();
+
+  bool Initialize();
+  ExtensionInstance* CreateInstance();
+
+  XW_Extension xw_extension() const {
+    return xw_extension_;
+  }
+
+  std::string name() const {
+    return name_;
+  }
+
+  std::string javascript_api() const {
+    return javascript_api_;
+  }
+
+  std::vector<std::string>& entry_points() {
+    return entry_points_;
+  }
+
+  void set_name(const std::string& name) {
+    name_ = name;
+  }
+
+  void set_javascript_api(const std::string& javascript_api) {
+    javascript_api_ = javascript_api;
+  }
+
+  void set_runtime_variable_callback(RuntimeVariableCallback callback) {
+    runtime_variable_callback_ = callback;
+  }
+
+ private:
+  friend class ExtensionAdapter;
+  friend class ExtensionInstance;
+
+  void GetRuntimeVariable(const char* key, char* value, size_t value_len);
+  int CheckAPIAccessControl(const char* api_name);
+  int RegisterPermissions(const char* perm_table);
+
+  bool initialized_;
+  std::string library_path_;
+  XW_Extension xw_extension_;
+  std::string name_;
+  std::string javascript_api_;
+  std::vector<std::string> entry_points_;
+
+  RuntimeVariableCallback runtime_variable_callback_;
+
+  XW_CreatedInstanceCallback created_instance_callback_;
+  XW_DestroyedInstanceCallback destroyed_instance_callback_;
+  XW_ShutdownCallback shutdown_callback_;
+  XW_HandleMessageCallback handle_msg_callback_;
+  XW_HandleSyncMessageCallback handle_sync_msg_callback_;
+  XW_HandleBinaryMessageCallback handle_binary_msg_callback_;
+};
+
+class ExtensionInstance {
+ public:
+  typedef std::function<void(
+      const char* msg, const size_t size, const bool binary)>
+      MessageCallback;
+
+  ExtensionInstance(Extension* extension, XW_Instance xw_instance);
+  virtual ~ExtensionInstance();
+
+  void HandleMessage(const std::string& msg);
+  void HandleMessage(const char* msg, const size_t size);
+  void HandleSyncMessage(const std::string& msg);
+
+  XW_Instance xw_instance() const {
+    return xw_instance_;
+  }
+
+  void set_post_message_listener(MessageCallback callback) {
+    post_message_callback_ = callback;
+  }
+
+  void set_send_sync_message_listener(MessageCallback callback) {
+    send_sync_reply_callback_ = callback;
+  }
+
+ private:
+  friend class ExtensionAdapter;
+
+  void PostMessageToJS(const std::string& msg);
+  void PostMessageToJS(const char* msg, size_t size);
+  void SyncReplyToJS(const std::string& reply);
+
+  Extension* extension_;
+  XW_Instance xw_instance_;
+  void* instance_data_;
+
+  MessageCallback post_message_callback_;
+  MessageCallback send_sync_reply_callback_;
+};
+
+}  // namespace extensions
+}  // namespace xwalk
+
+
+#endif  // NODE_XWALK_EXTENSIONS_EXTENSION_H_
diff --git a/node-xwalk/src/extension_adapter.cc b/node-xwalk/src/extension_adapter.cc
new file mode 100644 (file)
index 0000000..a5559c8
--- /dev/null
@@ -0,0 +1,304 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 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 "extension_adapter.h"
+
+#include <string.h>
+
+#include <string>
+
+#include "log.h"
+
+namespace xwalk {
+namespace extensions {
+
+ExtensionAdapter::ExtensionAdapter()
+  : next_xw_extension_(1),
+    next_xw_instance_(1) {
+}
+
+ExtensionAdapter::~ExtensionAdapter() {
+}
+
+ExtensionAdapter* ExtensionAdapter::GetInstance() {
+  static ExtensionAdapter self;
+  return &self;
+}
+
+XW_Extension ExtensionAdapter::GetNextXWExtension() {
+  return next_xw_extension_++;
+}
+
+XW_Instance ExtensionAdapter::GetNextXWInstance() {
+  return next_xw_instance_++;
+}
+
+void ExtensionAdapter::RegisterExtension(Extension* extension) {
+  XW_Extension xw_extension = extension->xw_extension();
+  if (!(xw_extension > 0 && xw_extension < next_xw_extension_)) {
+    LOGW("xw_extension (%d) is invalid.", xw_extension);
+    return;
+  }
+  if (extension_map_.find(xw_extension) == extension_map_.end())
+    extension_map_[xw_extension] = extension;
+}
+
+void ExtensionAdapter::UnregisterExtension(Extension* extension) {
+  XW_Extension xw_extension = extension->xw_extension();
+  if (!(xw_extension > 0 && xw_extension < next_xw_extension_)) {
+    LOGW("xw_extension (%d) is invalid.", xw_extension);
+    return;
+  }
+  if (extension_map_.find(xw_extension) != extension_map_.end())
+    extension_map_.erase(xw_extension);
+}
+
+void ExtensionAdapter::RegisterInstance(ExtensionInstance* instance) {
+  XW_Instance xw_instance = instance->xw_instance();
+  if (!(xw_instance > 0 && xw_instance < next_xw_instance_)) {
+    LOGW("xw_instance (%d) is invalid.", xw_instance);
+    return;
+  }
+  if (instance_map_.find(xw_instance) == instance_map_.end())
+    instance_map_[xw_instance] = instance;
+}
+
+void ExtensionAdapter::UnregisterInstance(ExtensionInstance* instance) {
+  XW_Instance xw_instance = instance->xw_instance();
+  if (!(xw_instance > 0 && xw_instance < next_xw_instance_)) {
+    LOGW("xw_instance (%d) is invalid.", xw_instance);
+    return;
+  }
+  if (instance_map_.find(xw_instance) != instance_map_.end())
+    instance_map_.erase(xw_instance);
+}
+
+const void* ExtensionAdapter::GetInterface(const char* name) {
+  if (!strcmp(name, XW_CORE_INTERFACE_1)) {
+    static const XW_CoreInterface_1 coreInterface1 = {
+      CoreSetExtensionName,
+      CoreSetJavaScriptAPI,
+      CoreRegisterInstanceCallbacks,
+      CoreRegisterShutdownCallback,
+      CoreSetInstanceData,
+      CoreGetInstanceData
+    };
+    return &coreInterface1;
+  }
+
+  if (!strcmp(name, XW_MESSAGING_INTERFACE_1)) {
+    static const XW_MessagingInterface_1 messagingInterface1 = {
+      MessagingRegister,
+      MessagingPostMessage
+    };
+    return &messagingInterface1;
+  }
+
+  if (!strcmp(name, XW_MESSAGING_INTERFACE_2)) {
+    static const XW_MessagingInterface_2 messagingInterface2 = {
+      MessagingRegister,
+      MessagingPostMessage,
+      MessagingRegisterBinaryMessageCallback,
+      MessagingPostBinaryMessage
+    };
+    return &messagingInterface2;
+  }
+
+  if (!strcmp(name, XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1)) {
+    static const XW_Internal_SyncMessagingInterface_1
+        syncMessagingInterface1 = {
+      SyncMessagingRegister,
+      SyncMessagingSetSyncReply
+    };
+    return &syncMessagingInterface1;
+  }
+
+  if (!strcmp(name, XW_INTERNAL_ENTRY_POINTS_INTERFACE_1)) {
+    static const XW_Internal_EntryPointsInterface_1 entryPointsInterface1 = {
+      EntryPointsSetExtraJSEntryPoints
+    };
+    return &entryPointsInterface1;
+  }
+
+  if (!strcmp(name, XW_INTERNAL_RUNTIME_INTERFACE_1)) {
+    static const XW_Internal_RuntimeInterface_1 runtimeInterface1 = {
+      RuntimeGetStringVariable
+    };
+    return &runtimeInterface1;
+  }
+
+  if (!strcmp(name, XW_INTERNAL_PERMISSIONS_INTERFACE_1)) {
+    static const XW_Internal_PermissionsInterface_1 permissionsInterface1 = {
+      PermissionsCheckAPIAccessControl,
+      PermissionsRegisterPermissions
+    };
+    return &permissionsInterface1;
+  }
+
+  LOGW("Interface '%s' is not supported.", name);
+  return NULL;
+}
+
+Extension* ExtensionAdapter::GetExtension(XW_Extension xw_extension) {
+  ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
+  ExtensionMap::iterator it = adapter->extension_map_.find(xw_extension);
+  if (it == adapter->extension_map_.end())
+    return NULL;
+  return it->second;
+}
+
+ExtensionInstance* ExtensionAdapter::GetExtensionInstance(
+    XW_Instance xw_instance) {
+  ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
+  InstanceMap::iterator it = adapter->instance_map_.find(xw_instance);
+  if (it == adapter->instance_map_.end())
+    return NULL;
+  return it->second;
+}
+
+#define CHECK(x, xw) \
+  if (!x) { \
+    LOGW("Ignoring call. Invalid %s = %d", #xw, xw); \
+    return; \
+  }
+
+#define RETURN_IF_INITIALIZED(x) \
+  if (x->initialized_) \
+    return;
+
+void ExtensionAdapter::CoreSetExtensionName(XW_Extension xw_extension,
+    const char* name) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->name_ = name;
+}
+
+void ExtensionAdapter::CoreSetJavaScriptAPI(XW_Extension xw_extension,
+    const char* javascript_api) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->javascript_api_ = javascript_api;
+}
+
+void ExtensionAdapter::CoreRegisterInstanceCallbacks(XW_Extension xw_extension,
+    XW_CreatedInstanceCallback created,
+    XW_DestroyedInstanceCallback destroyed) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->created_instance_callback_ = created;
+  extension->destroyed_instance_callback_ = destroyed;
+}
+
+void ExtensionAdapter::CoreRegisterShutdownCallback(XW_Extension xw_extension,
+    XW_ShutdownCallback shutdown) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->shutdown_callback_ = shutdown;
+}
+
+void ExtensionAdapter::CoreSetInstanceData(XW_Instance xw_instance,
+    void* data) {
+  ExtensionInstance* instance = GetExtensionInstance(xw_instance);
+  CHECK(instance, xw_instance);
+  instance->instance_data_ = data;
+}
+
+void* ExtensionAdapter::CoreGetInstanceData(XW_Instance xw_instance) {
+  ExtensionInstance* instance = GetExtensionInstance(xw_instance);
+  if (instance)
+    return instance->instance_data_;
+  else
+    return NULL;
+}
+
+void ExtensionAdapter::MessagingRegister(XW_Extension xw_extension,
+    XW_HandleMessageCallback handle_message) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->handle_msg_callback_ = handle_message;
+}
+
+void ExtensionAdapter::MessagingPostMessage(XW_Instance xw_instance,
+    const char* message) {
+  ExtensionInstance* instance = GetExtensionInstance(xw_instance);
+  CHECK(instance, xw_instance);
+  instance->PostMessageToJS(message);
+}
+
+void ExtensionAdapter::MessagingRegisterBinaryMessageCallback(
+    XW_Extension xw_extension, XW_HandleBinaryMessageCallback handle_message) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->handle_binary_msg_callback_ = handle_message;
+}
+
+void ExtensionAdapter::MessagingPostBinaryMessage(
+    XW_Instance xw_instance, const char* message, size_t size) {
+  ExtensionInstance* instance = GetExtensionInstance(xw_instance);
+  CHECK(instance, xw_instance);
+  instance->PostMessageToJS(message, size);
+}
+
+void ExtensionAdapter::SyncMessagingRegister(XW_Extension xw_extension,
+    XW_HandleSyncMessageCallback handle_sync_message) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+  extension->handle_sync_msg_callback_ = handle_sync_message;
+}
+
+void ExtensionAdapter::SyncMessagingSetSyncReply(XW_Instance xw_instance,
+    const char* reply) {
+  ExtensionInstance* instance = GetExtensionInstance(xw_instance);
+  CHECK(instance, xw_instance);
+  instance->SyncReplyToJS(reply);
+}
+
+void ExtensionAdapter::EntryPointsSetExtraJSEntryPoints(
+    XW_Extension xw_extension, const char** entry_points) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  RETURN_IF_INITIALIZED(extension);
+
+  for (int i=0; entry_points[i]; ++i) {
+    extension->entry_points_.push_back(std::string(entry_points[i]));
+  }
+}
+
+void ExtensionAdapter::RuntimeGetStringVariable(XW_Extension xw_extension,
+    const char* key,
+    char* value,
+    unsigned int value_len) {
+  Extension* extension = GetExtension(xw_extension);
+  CHECK(extension, xw_extension);
+  extension->GetRuntimeVariable(key, value, value_len);
+}
+
+int ExtensionAdapter::PermissionsCheckAPIAccessControl(
+    XW_Extension xw_extension, const char* api_name) {
+  Extension* extension = GetExtension(xw_extension);
+  if (extension)
+    return extension->CheckAPIAccessControl(api_name);
+  else
+    return XW_ERROR;
+}
+
+int ExtensionAdapter::PermissionsRegisterPermissions(XW_Extension xw_extension,
+    const char* perm_table) {
+  Extension* extension = GetExtension(xw_extension);
+  if (extension)
+    return extension->RegisterPermissions(perm_table);
+  else
+    return XW_ERROR;
+}
+
+}  // namespace extensions
+}  // namespace xwalk
diff --git a/node-xwalk/src/extension_adapter.h b/node-xwalk/src/extension_adapter.h
new file mode 100644 (file)
index 0000000..9b894a0
--- /dev/null
@@ -0,0 +1,102 @@
+// Copyright (c) 2013 Intel Corporation. All rights reserved.
+// Copyright (c) 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 NODE_XWALK_EXTENSIONS_EXTENSION_ADAPTER_H_
+#define NODE_XWALK_EXTENSIONS_EXTENSION_ADAPTER_H_
+
+#include <map>
+
+#include "xwalk/extensions/public/XW_Extension.h"
+#include "xwalk/extensions/public/XW_Extension_Message_2.h"
+#include "xwalk/extensions/public/XW_Extension_SyncMessage.h"
+#include "xwalk/extensions/public/XW_Extension_EntryPoints.h"
+#include "xwalk/extensions/public/XW_Extension_Runtime.h"
+#include "xwalk/extensions/public/XW_Extension_Permissions.h"
+
+#include "extension.h"
+
+namespace xwalk {
+namespace extensions {
+
+class ExtensionAdapter {
+ public:
+  static ExtensionAdapter* GetInstance();
+
+  static Extension* GetExtension(XW_Extension xw_extension);
+  static ExtensionInstance* GetExtensionInstance(XW_Instance xw_instance);
+
+  XW_Extension GetNextXWExtension();
+  XW_Instance GetNextXWInstance();
+
+  void RegisterExtension(Extension* extension);
+  void UnregisterExtension(Extension* extension);
+
+  void RegisterInstance(ExtensionInstance* instance);
+  void UnregisterInstance(ExtensionInstance* instance);
+
+  // Returns the correct struct according to interface asked. This is
+  // passed to external extensions in XW_Initialize() call.
+  static const void* GetInterface(const char* name);
+
+ private:
+  ExtensionAdapter();
+  virtual ~ExtensionAdapter();
+
+  // Core Interface
+  static void CoreSetExtensionName(
+      XW_Extension xw_extension, const char* name);
+  static void CoreSetJavaScriptAPI(
+      XW_Extension xw_extension, const char* javascript_api);
+  static void CoreRegisterInstanceCallbacks(
+      XW_Extension xw_extension,
+      XW_CreatedInstanceCallback created,
+      XW_DestroyedInstanceCallback destroyed);
+  static void CoreRegisterShutdownCallback(
+      XW_Extension xw_extension, XW_ShutdownCallback shutdown);
+  static void CoreSetInstanceData(
+      XW_Instance xw_instance, void* data);
+  static void* CoreGetInstanceData(XW_Instance xw_instance);
+  // Messaging Interface
+  static void MessagingRegister(
+      XW_Extension xw_extension, XW_HandleMessageCallback handle_message);
+  static void MessagingPostMessage(
+      XW_Instance xw_instance, const char* message);
+  static void MessagingRegisterBinaryMessageCallback(
+      XW_Extension extension, XW_HandleBinaryMessageCallback handle_message);
+  static void MessagingPostBinaryMessage(
+      XW_Instance instance, const char* message, size_t size);
+  // SyncMessage Interface
+  static void SyncMessagingRegister(
+      XW_Extension xw_extension,
+      XW_HandleSyncMessageCallback handle_sync_message);
+  static void SyncMessagingSetSyncReply(
+      XW_Instance xw_instance, const char* reply);
+  // EntryPoints Interface
+  static void EntryPointsSetExtraJSEntryPoints(
+      XW_Extension xw_extension, const char** entry_points);
+  // Runtime Interface
+  static void RuntimeGetStringVariable(
+      XW_Extension xw_extension,
+      const char* key, char* value, unsigned int value_len);
+  // Permission Interface
+  static int PermissionsCheckAPIAccessControl(
+      XW_Extension xw_extension, const char* api_name);
+  static int PermissionsRegisterPermissions(
+      XW_Extension xw_extension, const char* perm_table);
+
+  typedef std::map<XW_Extension, Extension*> ExtensionMap;
+  ExtensionMap extension_map_;
+
+  typedef std::map<XW_Instance, ExtensionInstance*> InstanceMap;
+  InstanceMap instance_map_;
+
+  XW_Extension next_xw_extension_;
+  XW_Instance next_xw_instance_;
+};
+
+}  // namespace extensions
+}  // namespace xwalk
+
+#endif  // NODE_XWALK_EXTENSIONS_EXTENSION_ADAPTER_H_
diff --git a/node-xwalk/src/log.h b/node-xwalk/src/log.h
new file mode 100644 (file)
index 0000000..6b88f92
--- /dev/null
@@ -0,0 +1,15 @@
+// Copyright (c) 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 NODE_XWALK_EXTENSIONS_LOG_H_
+#define NODE_XWALK_EXTENSIONS_LOG_H_
+
+#include <stdio.h>
+
+#define LOGD(fmt, args...) printf(fmt "\n", ##args)
+#define LOGI(fmt, args...) printf(fmt "\n", ##args)
+#define LOGW(fmt, args...) printf(fmt "\n", ##args)
+#define LOGE(fmt, args...) printf(fmt "\n", ##args)
+
+#endif  // NODE_XWALK_EXTENSIONS_LOG_H_
diff --git a/node-xwalk/src/native_binding.cc b/node-xwalk/src/native_binding.cc
new file mode 100644 (file)
index 0000000..275f483
--- /dev/null
@@ -0,0 +1,264 @@
+// Copyright (c) 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 "native_binding.h"
+
+#include <map>
+#include <string>
+
+#include "extension.h"
+#include "extension_adapter.h"
+#include "log.h"
+
+namespace xwalk {
+namespace extensions {
+
+namespace {
+
+// Cached function callbackes for each instance id
+static std::map<int, v8::Persistent<v8::Function>> g_listeners;
+
+// Cached runtime variables
+static std::map<std::string, std::string> g_runtime_variables;
+
+}  // namespace
+
+// static
+void NativeBinding::Init(v8::Handle<v8::Object> target) {
+  NODE_SET_METHOD(target, "getExtensionInfo", GetExtensionInfo);
+  NODE_SET_METHOD(target, "createInstance", CreateInstance);
+  NODE_SET_METHOD(target, "destroyInstance", DestroyInstance);
+  NODE_SET_METHOD(target, "postMessage", PostMessage);
+  NODE_SET_METHOD(target, "sendSyncMessage", SendSyncMessage);
+  NODE_SET_METHOD(target, "setMessageListener", SetMessageListener);
+  NODE_SET_METHOD(target, "setRuntimeVariable", SetRuntimeVariable);
+}
+
+// static
+void NativeBinding::GetExtensionInfo(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+
+  if (args.Length() < 1 || !args[0]->IsString()) {
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  v8::String::Utf8Value ext_path(args[0]->ToString());
+
+  Extension* ext = new Extension(std::string(*ext_path));
+  if (ext && ext->Initialize()) {
+    auto rv_cb = [](const std::string& key) -> std::string {
+      auto it = g_runtime_variables.find(key);
+      if (it == g_runtime_variables.end()) {
+        return std::string("");
+      } else {
+        return it->second;
+      }
+    };
+    ext->set_runtime_variable_callback(rv_cb);
+    v8::Local<v8::Object> obj = v8::Object::New(isolate);
+    obj->Set(v8::String::NewFromUtf8(isolate, "extension_id"),
+             v8::Integer::New(isolate, ext->xw_extension()));
+    obj->Set(v8::String::NewFromUtf8(isolate, "name"),
+             v8::String::NewFromUtf8(isolate, ext->name().c_str()));
+    obj->Set(v8::String::NewFromUtf8(isolate, "jsapi"),
+             v8::String::NewFromUtf8(isolate, ext->javascript_api().c_str()));
+    result.Set(obj);
+  } else {
+    LOGE("Failed to create extension, path=%s", *ext_path);
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+}
+
+// static
+void NativeBinding::CreateInstance(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+
+  if (args.Length() < 1 || !args[0]->IsNumber()) {
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  int ext_id = args[0]->Int32Value();
+  Extension* ext = ExtensionAdapter::GetInstance()->GetExtension(ext_id);
+  if (!ext) {
+    LOGE("Can't find extension. extension_id=%d", ext_id);
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  ExtensionInstance* instance = ext->CreateInstance();
+  if (!instance) {
+    LOGE("Failed to create instance. extension_id=%d", ext_id);
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  using std::placeholders::_1;
+  using std::placeholders::_2;
+  using std::placeholders::_3;
+  instance->set_post_message_listener(
+      std::bind(NativeBinding::PostMessageToJSCallback,
+                _1, _2, _3, instance->xw_instance()));
+
+  result.Set(v8::Integer::New(isolate, instance->xw_instance()));
+}
+
+// static
+void NativeBinding::DestroyInstance(
+    const v8::FunctionCallbackInfo<v8::Value>& /*args*/) {
+  // Nothing to do.
+  // Destroy Instance callbacks will be called when each extension is freed.
+}
+
+// static
+void NativeBinding::PostMessage(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+
+  if (args.Length() < 2 || !args[0]->IsNumber()) {
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  int instance_id = args[0]->Int32Value();
+  ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
+  ExtensionInstance* instance = adapter->GetExtensionInstance(instance_id);
+  if (!instance) {
+    LOGE("Can't find instance. instance_id=%d", instance_id);
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  if (args[1]->IsString()) {
+    v8::String::Utf8Value msg(args[1]->ToString());
+    instance->HandleMessage(std::string(*msg));
+  } else if (args[1]->IsArrayBuffer()) {
+    v8::Handle<v8::ArrayBuffer> buffer =
+        v8::Handle<v8::ArrayBuffer>::Cast(args[1]);
+    v8::ArrayBuffer::Contents contents = buffer->Externalize();
+    void* ptr = contents.Data();
+    instance->HandleMessage(static_cast<char*>(ptr),
+                            contents.ByteLength());
+    std::free(ptr);
+  }
+}
+
+// static
+void NativeBinding::SendSyncMessage(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+
+  if (args.Length() < 2 || !args[0]->IsNumber()) {
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  int instance_id = args[0]->Int32Value();
+  ExtensionAdapter* adapter = ExtensionAdapter::GetInstance();
+  ExtensionInstance* instance = adapter->GetExtensionInstance(instance_id);
+  if (!instance) {
+    LOGE("Can't find instance. instance_id=%d", instance_id);
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  auto sync_cb = [](const char* reply, const size_t /*size*/,
+                    const bool /*binary*/,
+                    const v8::FunctionCallbackInfo<v8::Value>& args) {
+    v8::Isolate* isolate = args.GetIsolate();
+    v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+    result.Set(v8::String::NewFromUtf8(isolate, reply));
+  };
+  using std::placeholders::_1;
+  using std::placeholders::_2;
+  using std::placeholders::_3;
+  instance->set_send_sync_message_listener(
+      std::bind(sync_cb, _1, _2, _3, args));
+  v8::String::Utf8Value msg(args[1]->ToString());
+  instance->HandleSyncMessage(std::string(*msg));
+}
+
+// static
+void NativeBinding::SetMessageListener(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+
+  if (args.Length() < 2 || !args[0]->IsNumber()) {
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  int instance_id = args[0]->Int32Value();
+  g_listeners[instance_id].Reset(
+      isolate, v8::Handle<v8::Function>::Cast(args[1]));
+}
+
+// static
+void NativeBinding::SetRuntimeVariable(
+    const v8::FunctionCallbackInfo<v8::Value>& args) {
+  v8::Isolate* isolate = args.GetIsolate();
+  v8::HandleScope handle_scope(isolate);
+  v8::ReturnValue<v8::Value> result(args.GetReturnValue());
+
+  if (args.Length() < 2 || !args[0]->IsString() || !args[1]->IsString()) {
+    result.Set(v8::Undefined(isolate));
+    return;
+  }
+
+  v8::String::Utf8Value key(args[0]->ToString());
+  v8::String::Utf8Value value(args[1]->ToString());
+
+  g_runtime_variables[std::string(*key)] = std::string(*value);
+
+  result.Set(v8::Boolean::New(isolate, true));
+}
+
+// static
+// TODO(WonyoungChoi): Make this function thread-safety.
+void NativeBinding::PostMessageToJSCallback(
+    const char* msg, const size_t size, const bool binary, int instance_id) {
+  auto listener = g_listeners.find(instance_id);
+  if (listener == g_listeners.end()) {
+    LOGW("Can't find callback. instance_id=%d", instance_id);
+    return;
+  }
+
+  v8::Isolate* isolate = v8::Isolate::GetCurrent();
+  v8::HandleScope scope(isolate);
+  v8::Local<v8::Value> args[1];
+
+  if (binary) {
+    args[0] = v8::ArrayBuffer::New(isolate, (void*)(msg), size);
+  } else {
+    args[0] = v8::String::NewFromUtf8(isolate, msg);
+  }
+
+  v8::Local<v8::Function> func =
+      v8::Local<v8::Function>::New(isolate, listener->second);
+  func->Call(v8::Null(isolate), 1, args);
+}
+
+}  // namespace extensions
+}  // namespace xwalk
+
+extern "C" {
+  static void NodeInit(v8::Handle<v8::Object> target) {
+    xwalk::extensions::NativeBinding::Init(target);
+  }
+  NODE_MODULE(native, NodeInit);
+}
diff --git a/node-xwalk/src/native_binding.h b/node-xwalk/src/native_binding.h
new file mode 100644 (file)
index 0000000..66f9899
--- /dev/null
@@ -0,0 +1,42 @@
+// Copyright (c) 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 NODE_XWALK_EXTENSIONS_NATIVE_BINDING_H_
+#define NODE_XWALK_EXTENSIONS_NATIVE_BINDING_H_
+
+#include <node.h>
+#include <node_object_wrap.h>
+#include <v8.h>
+
+namespace xwalk {
+namespace extensions {
+
+class NativeBinding {
+ public:
+  static void Init(v8::Handle<v8::Object> target);
+
+  static void GetExtensionInfo(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void CreateInstance(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void DestroyInstance(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void PostMessage(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void SendSyncMessage(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void SetMessageListener(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+  static void SetRuntimeVariable(
+      const v8::FunctionCallbackInfo<v8::Value>& args);
+
+  static void PostMessageToJSCallback(
+      const char* msg, const size_t size, const bool binary,
+      int instance_id);
+};
+
+}  // namespace extensions
+}  // namespace xwalk
+
+#endif  // NODE_XWALK_EXTENSIONS_NATIVE_BINDING_H_
diff --git a/node-xwalk/src/xwalk/extensions/public/XW_Extension.h b/node-xwalk/src/xwalk/extensions/public/XW_Extension.h
new file mode 100644 (file)
index 0000000..174915a
--- /dev/null
@@ -0,0 +1,185 @@
+// 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 XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+
+// Crosswalk Extensions are modules of code loaded by Crosswalk runtime that
+// allow extending its capabilities. The extension is expected to define a
+// XW_Initialize() function as declared below, get the interfaces it need to
+// use and register to whatever callbacks it needs, then return XW_OK.
+//
+// The Extension is represented by the type XW_Extension. Each extension
+// loaded may be used multiple times for different pages, so to each execution
+// there will be an associated XW_Instance. A reasonable analogy is that the
+// XW_Extension represent a "class", and have concrete instances running.
+//
+// An interface is a struct with a set of functions, provided by Crosswalk,
+// that allow the extension code to interact with the web content. Certain
+// functions in an interface are used to register callbacks, so that Crosswalk
+// can call the extension at specific situations.
+//
+// Crosswalk won't call an extension's XW_Initialize() multiple times in the
+// same process.
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if __GNUC__ >= 4
+#define XW_EXPORT __attribute__ ((visibility("default")))
+#elif defined(_MSC_VER)
+#define XW_EXPORT __declspec(dllexport)
+#endif
+
+#include <stdint.h>
+
+
+// XW_Extension is used to identify your extension when calling functions from
+// the API. You should always use the XW_Extension received at XW_Initialize().
+//
+// XW_Instance is used to identify different web contents using your
+// extension. Each time a new web content is created you can be notified
+// registering the XW_CreatedInstanceCallback, that receives the new
+// XW_Instance. When interacting with an Instance (for example to post a
+// message), you should pass the corresponding XW_Instance.
+//
+// In both types the zero value is never used by Crosswalk, so can be used to
+// initialize variables.
+typedef int32_t XW_Extension;
+typedef int32_t XW_Instance;
+
+enum {
+  XW_OK = 0,
+  XW_ERROR = -1
+};
+
+// Returns a struct containing functions to be used by the extension. Those
+// structs can be stored statically and used until the extension is unloaded.
+// Extensions should use definitions like XW_CORE_INTERFACE, instead of using
+// the versioned definition or the literal string. Returns NULL if the
+// interface is not supported.
+typedef const void* (*XW_GetInterface)(const char* interface_name);
+
+
+typedef int32_t (*XW_Initialize_Func)(XW_Extension extension,
+                                      XW_GetInterface get_interface);
+
+// XW_Initialize is called after the extension code is loaded. The 'extension'
+// value should be used in further calls that expect XW_Extension argument.
+//
+// The 'get_interface' function should be used to get access to functions that
+// interact with the web content. It is only valid during the execution of the
+// XW_Initialize() function.
+//
+// This function should return XW_OK when the extension was succesfully
+// loaded, otherwise XW_ERROR.
+XW_EXPORT int32_t XW_Initialize(XW_Extension extension,
+                                XW_GetInterface get_interface);
+
+
+//
+// XW_CORE_INTERFACE: Basic functionality for Crosswalk Extensions. All
+// extensions should use this interface to set at least their name.
+//
+
+#define XW_CORE_INTERFACE_1 "XW_CoreInterface_1"
+#define XW_CORE_INTERFACE XW_CORE_INTERFACE_1
+
+typedef void (*XW_CreatedInstanceCallback)(XW_Instance instance);
+typedef void (*XW_DestroyedInstanceCallback)(XW_Instance instance);
+typedef void (*XW_ShutdownCallback)(XW_Extension extension);
+
+struct XW_CoreInterface_1 {
+  // Set the name of the extension. It is used as the namespace for the
+  // JavaScript code exposed by the extension. So extension named
+  // 'my_extension', will expose its JavaScript functionality inside
+  // the 'my_extension' namespace.
+  //
+  // This function should be called only during XW_Initialize().
+  void (*SetExtensionName)(XW_Extension extension, const char* name);
+
+  // Set the JavaScript code loaded in the web content when the extension is
+  // used. This can be used together with the messaging mechanism to implement
+  // a higher-level API that posts messages to extensions, see
+  // XW_MESSAGING_INTERFACE below.
+  //
+  // The code will be executed inside a JS function context with the following
+  // objects available:
+  //
+  // - exports: this object should be filled with properties and functions
+  //            that will be exposed in the namespace associated with this
+  //            extension.
+  //
+  // - extension.postMessage(): post a string message to the extension native
+  //                            code. See below for details.
+  // - extension.setMessageListener(): allow setting a callback that is called
+  //                                   when the native code sends a message
+  //                                   to JavaScript. Callback takes a string.
+  //
+  // This function should be called only during XW_Initialize().
+  void (*SetJavaScriptAPI)(XW_Extension extension, const char* api);
+
+  // Register callbacks that are called when an instance of this extension
+  // is created or destroyed. Everytime a new web content is loaded, it will
+  // get a new associated instance.
+  //
+  // This function should be called only during XW_Initialize().
+  void (*RegisterInstanceCallbacks)(XW_Extension extension,
+                                    XW_CreatedInstanceCallback created,
+                                    XW_DestroyedInstanceCallback destroyed);
+
+  // Register a callback to be executed when the extension will be unloaded.
+  //
+  // This function should be called only during XW_Initialize().
+  void (*RegisterShutdownCallback)(XW_Extension extension,
+                                   XW_ShutdownCallback shutdown_callback);
+
+  // These two functions are conveniences used to associated arbitrary data
+  // with a given XW_Instance. They can be used only with instances that were
+  // created but not yet completely destroyed. GetInstanceData() can be used
+  // during the destroyed instance callback. If not instance data was set,
+  // getting it returns NULL.
+  void (*SetInstanceData)(XW_Instance instance, void* data);
+  void* (*GetInstanceData)(XW_Instance instance);
+};
+
+typedef struct XW_CoreInterface_1 XW_CoreInterface;
+
+
+//
+// XW_MESSAGING_INTERFACE: Exchange asynchronous messages with JavaScript
+// code provided by extension.
+//
+
+#define XW_MESSAGING_INTERFACE_1 "XW_MessagingInterface_1"
+#define XW_MESSAGING_INTERFACE XW_MESSAGING_INTERFACE_1
+
+typedef void (*XW_HandleMessageCallback)(XW_Instance instance,
+                                         const char* message);
+
+struct XW_MessagingInterface_1 {
+  // Register a callback to be called when the JavaScript code associated
+  // with the extension posts a message. Note that the callback will be called
+  // with the XW_Instance that posted the message as well as the message
+  // contents.
+  void (*Register)(XW_Extension extension,
+                   XW_HandleMessageCallback handle_message);
+
+  // Post a message to the web content associated with the instance. To
+  // receive this message the extension's JavaScript code should set a
+  // listener using extension.setMessageListener() function.
+  //
+  // This function is thread-safe and can be called until the instance is
+  // destroyed.
+  void (*PostMessage)(XW_Instance instance, const char* message);
+};
+
+typedef struct XW_MessagingInterface_1 XW_MessagingInterface;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
diff --git a/node-xwalk/src/xwalk/extensions/public/XW_Extension_EntryPoints.h b/node-xwalk/src/xwalk/extensions/public/XW_Extension_EntryPoints.h
new file mode 100644 (file)
index 0000000..54532a9
--- /dev/null
@@ -0,0 +1,49 @@
+// 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 XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_
+#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_
+
+// NOTE: This file and interfaces marked as internal are not considered stable
+// and can be modified in incompatible ways between Crosswalk versions.
+
+#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+#error "You should include XW_Extension.h before this file"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XW_INTERNAL_ENTRY_POINTS_INTERFACE_1 \
+  "XW_Internal_EntryPointsInterface_1"
+#define XW_INTERNAL_ENTRY_POINTS_INTERFACE \
+  XW_INTERNAL_ENTRY_POINTS_INTERFACE_1
+
+//
+// XW_INTERNAL_ENTRY_POINTS_INTERFACE: provides a way for extensions to add
+// more information about its implementation. For now, allow extensions to
+// specify more objects that the access should cause the extension to be
+// loaded.
+//
+
+struct XW_Internal_EntryPointsInterface_1 {
+  // Register extra entry points for this extension. An "extra" entry points
+  // are objects outside the implicit namespace for which the extension should
+  // be loaded when they are touched.
+  //
+  // This function should be called only during XW_Initialize().
+  void (*SetExtraJSEntryPoints)(XW_Extension extension,
+                                const char** entry_points);
+};
+
+typedef struct XW_Internal_EntryPointsInterface_1
+    XW_Internal_EntryPointsInterface;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_ENTRYPOINTS_H_
+
diff --git a/node-xwalk/src/xwalk/extensions/public/XW_Extension_Message_2.h b/node-xwalk/src/xwalk/extensions/public/XW_Extension_Message_2.h
new file mode 100644 (file)
index 0000000..f417f88
--- /dev/null
@@ -0,0 +1,64 @@
+// Copyright (c) 2015 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 XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_MESSAGE_2_H_
+#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_MESSAGE_2_H_
+
+#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+#error "You should include XW_Extension.h before this file"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XW_MESSAGING_INTERFACE_2 "XW_MessagingInterface_2"
+
+typedef void (*XW_HandleBinaryMessageCallback)(XW_Instance instance,
+                                               const char* message,
+                                               const size_t size);
+
+struct XW_MessagingInterface_2 {
+  // Register a callback to be called when the JavaScript code associated
+  // with the extension posts a message. Note that the callback will be called
+  // with the XW_Instance that posted the message as well as the message
+  // contents.
+  void (*Register)(XW_Extension extension,
+                   XW_HandleMessageCallback handle_message);
+
+  // Post a message to the web content associated with the instance. To
+  // receive this message the extension's JavaScript code should set a
+  // listener using extension.setMessageListener() function.
+  //
+  // This function is thread-safe and can be called until the instance is
+  // destroyed.
+  void (*PostMessage)(XW_Instance instance, const char* message);
+
+  // Register a callback to be called when the JavaScript code associated
+  // with the extension posts a binary message (ArrayBuffer object).
+  // Note that the callback will be called with the XW_Instance that posted
+  // the message as well as the message contents.
+  void (*RegisterBinaryMesssageCallback)(
+      XW_Extension extension,
+      XW_HandleBinaryMessageCallback handle_message);
+
+  // Post a binary message to the web content associated with the instance. To
+  // receive this message the extension's JavaScript code should set a
+  // listener using extension.setMessageListener() function.
+  // The JavaScript message listener function would receive the binary message
+  // in an ArrayBuffer object.
+  //
+  // This function is thread-safe and can be called until the instance is
+  // destroyed.
+  void (*PostBinaryMessage)(XW_Instance instance,
+                            const char* message, size_t size);
+};
+
+typedef struct XW_MessagingInterface_2 XW_MessagingInterface2;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_MESSAGE_2_H_
diff --git a/node-xwalk/src/xwalk/extensions/public/XW_Extension_Permissions.h b/node-xwalk/src/xwalk/extensions/public/XW_Extension_Permissions.h
new file mode 100644 (file)
index 0000000..d25484e
--- /dev/null
@@ -0,0 +1,41 @@
+// Copyright (c) 2014 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 XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_
+#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_
+
+// NOTE: This file and interfaces marked as internal are not considered stable
+// and can be modified in incompatible ways between Crosswalk versions.
+
+#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+#error "You should include XW_Extension.h before this file"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XW_INTERNAL_PERMISSIONS_INTERFACE_1 \
+    "XW_Internal_PermissionsInterface_1"
+#define XW_INTERNAL_PERMISSIONS_INTERFACE \
+    XW_INTERNAL_PERMISSIONS_INTERFACE_1
+
+//
+// XW_INTERNAL_PERMISSIONS_INTERFACE: provides a way for extensions
+// check if they have the proper permissions for certain APIs.
+//
+
+struct XW_Internal_PermissionsInterface_1 {
+  int (*CheckAPIAccessControl)(XW_Extension extension, const char* api_name);
+  int (*RegisterPermissions)(XW_Extension extension, const char* perm_table);
+};
+
+typedef struct XW_Internal_PermissionsInterface_1
+    XW_Internal_PermissionsInterface;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_PERMISSIONS_H_
diff --git a/node-xwalk/src/xwalk/extensions/public/XW_Extension_Runtime.h b/node-xwalk/src/xwalk/extensions/public/XW_Extension_Runtime.h
new file mode 100644 (file)
index 0000000..11ad307
--- /dev/null
@@ -0,0 +1,44 @@
+// 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 XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_
+#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_
+
+// NOTE: This file and interfaces marked as internal are not considered stable
+// and can be modified in incompatible ways between Crosswalk versions.
+
+#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+#error "You should include XW_Extension.h before this file"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define XW_INTERNAL_RUNTIME_INTERFACE_1 \
+  "XW_Internal_RuntimeInterface_1"
+#define XW_INTERNAL_RUNTIME_INTERFACE \
+  XW_INTERNAL_RUNTIME_INTERFACE_1
+
+//
+// XW_INTERNAL_RUNTIME_INTERFACE: allow extensions to gather information
+// from the runtime.
+//
+
+struct XW_Internal_RuntimeInterface_1 {
+  void (*GetRuntimeVariableString)(XW_Extension extension,
+                                   const char* key,
+                                   char* value,
+                                   unsigned int value_len);
+};
+
+typedef struct XW_Internal_RuntimeInterface_1
+    XW_Internal_RuntimeInterface;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_RUNTIME_H_
+
diff --git a/node-xwalk/src/xwalk/extensions/public/XW_Extension_SyncMessage.h b/node-xwalk/src/xwalk/extensions/public/XW_Extension_SyncMessage.h
new file mode 100644 (file)
index 0000000..4eddbf9
--- /dev/null
@@ -0,0 +1,48 @@
+// 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 XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_
+#define XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_
+
+// NOTE: This file and interfaces marked as internal are not considered stable
+// and can be modified in incompatible ways between Crosswalk versions.
+
+#ifndef XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_H_
+#error "You should include XW_Extension.h before this file"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+//
+// XW_INTERNAL_SYNC_MESSAGING_INTERFACE: allow JavaScript code to send a
+// synchronous message to extension code and block until response is
+// available. The response is made available by calling the SetSyncReply
+// function, that can be done from outside the context of the SyncMessage
+// handler.
+//
+
+#define XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1 \
+  "XW_InternalSyncMessagingInterface_1"
+#define XW_INTERNAL_SYNC_MESSAGING_INTERFACE \
+  XW_INTERNAL_SYNC_MESSAGING_INTERFACE_1
+
+typedef void (*XW_HandleSyncMessageCallback)(XW_Instance instance,
+                                             const char* message);
+
+struct XW_Internal_SyncMessagingInterface_1 {
+  void (*Register)(XW_Extension extension,
+                   XW_HandleSyncMessageCallback handle_sync_message);
+  void (*SetSyncReply)(XW_Instance instance, const char* reply);
+};
+
+typedef struct XW_Internal_SyncMessagingInterface_1
+    XW_Internal_SyncMessagingInterface;
+
+#ifdef __cplusplus
+}  // extern "C"
+#endif
+
+#endif  // XWALK_EXTENSIONS_PUBLIC_XW_EXTENSION_SYNCMESSAGE_H_
diff --git a/packaging/jsnative.manifest b/packaging/jsnative.manifest
new file mode 100644 (file)
index 0000000..75b0fa5
--- /dev/null
@@ -0,0 +1,5 @@
+<manifest>
+    <request>
+        <domain name="_"/>
+    </request>
+</manifest>
diff --git a/packaging/jsnative.spec b/packaging/jsnative.spec
new file mode 100644 (file)
index 0000000..9ad276b
--- /dev/null
@@ -0,0 +1,54 @@
+%define cmake_tmp cmake_build_tmp
+
+Name:       jsnative
+Summary:    Node.js modules for JSNative apps
+Version:    0.9.0
+Release:    1
+Group:      Development/Libraries
+License:    Apache-2.0 and BSD-3-Clause
+URL:        https://www.tizen.org
+Source0:    %{name}-%{version}.tar.gz
+Source1:    %{name}.manifest
+
+BuildRequires: cmake
+BuildRequires: pkgconfig(appcore-efl)
+BuildRequires: pkgconfig(aul)
+BuildRequires: pkgconfig(dlog)
+BuildRequires: pkgconfig(nodejs)
+Requires: nodejs
+
+%description
+Node.js modules for JSNative apps
+
+%prep
+%setup -q
+cp %{SOURCE1} .
+
+%build
+
+# node-xwalk
+mkdir -p cmake_build_tmp
+cd cmake_build_tmp
+cmake .. \
+        -DCMAKE_INSTALL_PREFIX=%{_prefix}
+
+make %{?jobs:-j%jobs}
+
+
+%install
+rm -rf %{buildroot}
+
+mkdir -p %{buildroot}%{_datadir}/license
+cp LICENSE %{buildroot}%{_datadir}/license/%{name}
+cat LICENSE.BSD >> %{buildroot}%{_datadir}/license/%{name}
+
+cd cmake_build_tmp
+%make_install
+
+%clean
+rm -rf %{buildroot}
+
+%files
+%manifest %{name}.manifest
+%{_datadir}/license/%{name}
+%{_prefix}/lib/node/*