--- /dev/null
+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)
--- /dev/null
+
+ 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.
--- /dev/null
+ 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.
--- /dev/null
+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)
--- /dev/null
+/*
+ * 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();
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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);
--- /dev/null
+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)
--- /dev/null
+{
+ "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"
+ }
+}
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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_
--- /dev/null
+#!/usr/bin/env node
+
+const cli = require('../lib/cli');
+cli(process.argv);
--- /dev/null
+'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));
+ }
+}
--- /dev/null
+'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
+ };
+ }
+}
--- /dev/null
+'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
--- /dev/null
+'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;
+ }
+};
--- /dev/null
+'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;
--- /dev/null
+'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');
+ }
+ });
+};
--- /dev/null
+'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;
--- /dev/null
+'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));
+ });
+};
--- /dev/null
+'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
--- /dev/null
+'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);
+ });
+}
--- /dev/null
+{
+ "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" }
+ ]
+}
--- /dev/null
+#!/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}');
--- /dev/null
+#!/usr/bin/env node
+module.paths = ['/usr/lib/js-binding/'].concat(module.paths);
+require('../res/{appid}');
--- /dev/null
+<?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>
--- /dev/null
+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)
+
--- /dev/null
+set noparent
+filter=-build/header_guard,-build/include
+linelength=80
\ No newline at end of file
--- /dev/null
+## 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.
+
--- /dev/null
+{
+ "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
--- /dev/null
+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
--- /dev/null
+#!/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);
+
--- /dev/null
+// 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;
+}
--- /dev/null
+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
--- /dev/null
+#!/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);
+ });
+});
+
--- /dev/null
+// 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;
+}
--- /dev/null
+// 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;
--- /dev/null
+{
+ "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
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+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}/*
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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
--- /dev/null
+// 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_
--- /dev/null
+// 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_
--- /dev/null
+// 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);
+}
--- /dev/null
+// 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_
--- /dev/null
+// 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_
--- /dev/null
+// 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_
+
--- /dev/null
+// 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_
--- /dev/null
+// 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_
--- /dev/null
+// 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_
+
--- /dev/null
+// 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_
--- /dev/null
+<manifest>
+ <request>
+ <domain name="_"/>
+ </request>
+</manifest>
--- /dev/null
+%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/*