1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013 Intel Corporation. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
9 #include <plugins-ipc-message/ipc_message_support.h>
11 #include "xwalk_extension_module.h"
12 #include "xwalk_extension_client.h"
13 #include "JSLifeManager.h"
14 #include "xwalk_module_system.h"
17 extern "C" JSGlobalContextRef JSContextGetGlobalContext(JSContextRef ctx);
19 // The arraysize(arr) macro returns the # of elements in an array arr.
20 // The expression is a compile-time constant, and therefore can be
21 // used in defining new arrays, for example. If you use arraysize on
22 // a pointer by mistake, you will get a compile-time error.
24 // One caveat is that arraysize() doesn't accept any array of an
25 // anonymous type or a type defined inside a function. In these rare
26 // cases, you have to use the unsafe ARRAYSIZE_UNSAFE() macro below. This is
27 // due to a limitation in C++'s template system. The limitation might
28 // eventually be removed, but it hasn't happened yet.
30 // This template function declaration is used in defining arraysize.
31 // Note that the function doesn't need an implementation, as we only
33 template <typename T, size_t N>
34 char (&ArraySizeHelper(T (&array)[N]))[N];
36 #define arraysize(array) (sizeof(ArraySizeHelper(array)))
42 std::string CodeToEnsureNamespace(const std::string& extension_name) {
46 pos = extension_name.find('.', pos);
47 if (pos == std::string::npos) {
48 result += extension_name + ";";
51 std::string ns = extension_name.substr(0, pos);
52 result += ns + " = " + ns + " || {}; ";
58 // Templatized backend for StringPrintF/StringAppendF. This does not finalize
59 // the va_list, the caller is expected to do that.
60 template <class StringType>
61 static void StringAppendVT(StringType* dst,
62 const typename StringType::value_type* format,
64 // First try with a small fixed size buffer.
65 // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
66 // and StringUtilTest.StringPrintfBounds.
67 typename StringType::value_type stack_buf[1024];
72 int result = vsnprintf(stack_buf, arraysize(stack_buf), format, ap_copy);
75 if (result >= 0 && result < static_cast<int>(arraysize(stack_buf))) {
77 dst->append(stack_buf, result);
81 // Repeatedly increase buffer size until it fits.
82 int mem_length = arraysize(stack_buf);
85 if (errno != 0 && errno != EOVERFLOW)
87 // Try doubling the buffer size.
90 // We need exactly "result + 1" characters.
91 mem_length = result + 1;
94 if (mem_length > 32 * 1024 * 1024) {
95 // That should be plenty, don't try anything larger. This protects
96 // against huge allocations when using vsnprintfT implementations that
97 // return -1 for reasons other than overflow without setting errno.
98 LOGE("Unable to printf the requested string due to size.");
102 std::vector<typename StringType::value_type> mem_buf(mem_length);
104 // NOTE: You can only use a va_list once. Since we're in a while loop, we
105 // need to make a new copy each time so we don't use up the original.
106 va_copy(ap_copy, ap);
107 result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy);
110 if ((result >= 0) && (result < mem_length)) {
112 dst->append(&mem_buf[0], result);
118 std::string StringPrintf(const char* format, ...) {
120 va_start(ap, format);
122 StringAppendVT(&result, format, ap);
127 // Wrap API code into a callable form that takes extension object as parameter.
128 std::string WrapAPICode(const std::string& extension_code,
129 const std::string& extension_name) {
130 // We take care here to make sure that line numbering for api_code after
131 // wrapping doesn't change, so that syntax errors point to the correct line.
134 "var %s; (function(extension, requireNative) { "
135 "extension.internal = {};"
136 "extension.internal.sendSyncMessage_ = extension.sendSyncMessage;"
137 "extension.internal.sendSyncMessage = function(){ return extension.internal.sendSyncMessage_.apply(extension, arguments); };"
138 "delete extension.sendSyncMessage;"
139 "var Object = requireNative('objecttools');"
140 "var exports = {}; (function() {'use strict'; %s\n})();"
141 "%s = %s || exports; });",
142 CodeToEnsureNamespace(extension_name).c_str(),
143 extension_code.c_str(),
144 extension_name.c_str(),
145 extension_name.c_str());
148 int GetNextChunkID() {
157 JSClassDefinition XWalkExtensionModule::class_definition_ = {
159 kJSClassAttributeNone,
163 XWalkExtensionModule::static_functions_,
169 NULL, //DeleteProperty,
170 NULL, //GetPropertyNames,
171 NULL, //CallAsFunction,
172 NULL, //CallAsConstructor,
178 JSStaticFunction XWalkExtensionModule::static_functions_[] = {
179 { "postMessage", PostMessageCallback, kJSPropertyAttributeNone },
180 { "postData", PostDataCallback, kJSPropertyAttributeNone },
181 { "sendSyncMessage", SendSyncMessageCallback, kJSPropertyAttributeNone },
182 { "sendSyncData", SendSyncDataCallback, kJSPropertyAttributeNone },
183 { "sendRuntimeMessage", SendRuntimeMessageCallback, kJSPropertyAttributeNone },
184 { "sendRuntimeAsyncMessage", SendRuntimeAsyncMessageCallback, kJSPropertyAttributeNone },
185 { "sendRuntimeSyncMessage", SendRuntimeSyncMessageCallback, kJSPropertyAttributeNone },
186 { "setMessageListener", SetMessageListenerCallback, kJSPropertyAttributeNone },
187 { "setDataListener", SetDataListenerCallback, kJSPropertyAttributeNone },
188 { "receiveChunkData", ReceiveChunkDataCallback, kJSPropertyAttributeNone },
192 JSClassRef XWalkExtensionModule::class_ref_ = NULL;
194 XWalkExtensionModule::ChunkData::ChunkData()
195 : data_(nullptr), length_(0) {
198 XWalkExtensionModule::ChunkData::~ChunkData() {
204 XWalkExtensionModule::XWalkExtensionModule(XWalkExtensionClient* client,
205 XWalkModuleSystem* module_system,
206 const std::string& extension_name,
207 std::function<std::string(void)> getter)
209 extension_name_(extension_name),
211 module_system_(module_system),
213 extension_code_getter_(getter) {
216 XWalkExtensionModule::~XWalkExtensionModule() {
217 JSContextRef ctx = module_system_->GetJSContext();
219 if( js_object_ != NULL ){
220 JSObjectSetPrivate(js_object_, NULL);
221 JSValueSafeUnprotect(ctx,js_object_);
224 if(message_listener_ != NULL){
225 JSValueSafeUnprotect(ctx,message_listener_);
228 client_->DestroyInstance(instance_id_);
231 void XWalkExtensionModule::LoadExtensionCode(JSContextRef context, JSObjectRef native_require) {
232 LOGD("========== << LoadExtensionCode >> ENTER ==========");
233 //CHECK(!instance_id_);
234 instance_id_ = client_->CreateInstance(extension_name_, this);
236 LOGD("Extension Module Name : [%s]", extension_name_.c_str());
238 std::string exception;
239 std::string extension_code = extension_code_getter_();
240 std::string wrapped_api_code = WrapAPICode(extension_code, extension_name_);
241 JSValueRef result = JsUtils::RunString(context, wrapped_api_code, &exception);
242 JSObjectRef callable_api_code = JSValueToObject(context, result, NULL);
244 if (!JSValueIsObject(context, result) || !JSObjectIsFunction(context, callable_api_code)) {
245 LOGE("Couldn't load JS API code for %s: %s", extension_name_.c_str(), exception.c_str());
250 if( js_object_ == NULL ){
251 if( XWalkExtensionModule::class_ref_ == NULL ){
252 XWalkExtensionModule::class_ref_ = JSClassCreate(&XWalkExtensionModule::class_definition_);
254 js_object_ = JSObjectMake( context, XWalkExtensionModule::class_ref_, this);
255 JSValueSafeProtect(context,js_object_);
257 JSValueRef argv[argc] = {
262 JSValueRef except_value = NULL;
263 JSObjectCallAsFunction(context, callable_api_code, NULL, argc, argv, &except_value);
264 if( except_value != NULL ){
265 LOGE("Couldn't load JS API code for %s: %s", extension_name_.c_str(), JsUtils::ExceptionToString(context, except_value).c_str());
270 void XWalkExtensionModule::HandleMessageFromNative(const std::string& msg) {
271 if (message_listener_ == NULL)
274 JSContextRef context = module_system_->GetJSContext();
275 JSValueRef args[] = { JsUtils::ToJSValueRef(context, msg) };
277 JSValueRef except_value = NULL;
278 JSObjectCallAsFunction(context, message_listener_, NULL, 1, args, &except_value);
279 if (except_value != NULL)
280 LOGE("Exception when running message listener: %s", JsUtils::ExceptionToString(context, except_value).c_str());
283 void XWalkExtensionModule::HandleMessageFromNative(const std::string& msg, unsigned char* buffer, std::size_t len) {
284 if (data_listener_ == NULL)
287 JSContextRef context = module_system_->GetJSContext();
288 JSValueRef args[] = {
289 JsUtils::ToJSValueRef(context, msg),
290 JSValueMakeNull(context)
293 int chunk_id = GetNextChunkID();
294 ChunkData& chunk_data = data_chunk_storage_[chunk_id];
295 chunk_data.set_data(buffer);
296 chunk_data.set_length(len);
297 args[1] = JSValueMakeNumber(context, chunk_id);
300 JSValueRef except_value = NULL;
301 JSObjectCallAsFunction(context, data_listener_, NULL, 2, args, &except_value);
302 if (except_value != NULL) {
303 LOGE("Exception when running message listener: %s", JsUtils::ExceptionToString(context, except_value).c_str());
309 JSValueRef XWalkExtensionModule::PostMessageCallback(JSContextRef context,
311 JSObjectRef thisObject,
312 size_t argumentCount,
313 const JSValueRef arguments[],
314 JSValueRef* exception) {
315 XWalkExtensionModule* module = GetExtensionModule(thisObject);
316 if (!module || argumentCount != 1) {
317 return JSValueMakeBoolean(context, false);
320 //CHECK(module->instance_id_);
321 module->client_->PostMessageToNative(module->instance_id_, JsUtils::JSValueToString(context, arguments[0]));
322 return JSValueMakeBoolean(context, true);
325 JSValueRef XWalkExtensionModule::PostDataCallback(JSContextRef context,
327 JSObjectRef thisObject,
328 size_t argumentCount,
329 const JSValueRef arguments[],
330 JSValueRef* exception) {
331 XWalkExtensionModule* module = GetExtensionModule(thisObject);
332 if (!module || argumentCount < 1) {
333 return JSValueMakeBoolean(context, false);
336 std::vector<unsigned char> chunk;
337 size_t chunk_size = 0;
338 if (argumentCount > 1) {
339 if (JSValueIsObject(context, arguments[1])) {
340 JSObjectRef arrayobj = JSValueToObject(context, arguments[1], NULL);
342 for (std::size_t i = 0; i < JSGetArrayLength(context, arrayobj); ++i) {
343 JSValueRef element = JSGetArrayElement(context, arrayobj, i);
344 unsigned char v = static_cast<unsigned char>(JSValueToNumber(context, element, NULL));
347 chunk_size = chunk.size();
349 } else if (JSValueIsString(context, arguments[1])) {
350 JSStringRef chunkStr = JSValueToStringCopy(context,
353 size_t len = JSStringGetLength(chunkStr) + 1;
355 JSStringGetUTF8CString(chunkStr,
356 reinterpret_cast<char*>(chunk.data()),
358 JSStringRelease(chunkStr);
359 chunk_size = len - 1;
363 module->client_->PostMessageToNative(module->instance_id_,
364 JsUtils::JSValueToString(context, arguments[0]),
367 return JSValueMakeBoolean(context, true);
372 JSValueRef XWalkExtensionModule::SendSyncMessageCallback(JSContextRef context,
374 JSObjectRef thisObject,
375 size_t argumentCount,
376 const JSValueRef arguments[],
377 JSValueRef* exception) {
379 XWalkExtensionModule* module = GetExtensionModule(thisObject);
380 if (!module || argumentCount != 1) {
381 return JSValueMakeBoolean(context, false);
384 //CHECK(module->instance_id_);
386 module->client_->SendSyncMessageToNative(module->instance_id_, JsUtils::JSValueToString(context, arguments[0]));
388 // If we tried to send a message to an instance that became invalid,
389 // then reply will be NULL.
390 if (!reply.empty()) {
391 return JsUtils::ToJSValueRef(context, reply);
393 return JSValueMakeNull(context);
397 JSValueRef XWalkExtensionModule::SendSyncDataCallback(JSContextRef context,
399 JSObjectRef thisObject,
400 size_t argumentCount,
401 const JSValueRef arguments[],
402 JSValueRef* exception) {
404 XWalkExtensionModule* module = GetExtensionModule(thisObject);
405 if (!module || argumentCount < 1) {
406 return JSValueMakeBoolean(context, false);
409 std::vector<unsigned char> chunk;
410 size_t chunk_size = 0;
411 if (argumentCount > 1) {
412 if (JSValueIsObject(context, arguments[1])) {
413 JSObjectRef arrayobj = JSValueToObject(context, arguments[1], NULL);
415 for (std::size_t i = 0; i < JSGetArrayLength(context, arrayobj); ++i) {
416 JSValueRef element = JSGetArrayElement(context, arrayobj, i);
417 unsigned char v = static_cast<unsigned char>(JSValueToNumber(context, element, NULL));
420 chunk_size = chunk.size();
422 } else if (JSValueIsString(context, arguments[1])) {
423 JSStringRef chunkStr = JSValueToStringCopy(context,
426 size_t len = JSStringGetLength(chunkStr) + 1;
428 JSStringGetUTF8CString(chunkStr,
429 reinterpret_cast<char*>(chunk.data()),
431 JSStringRelease(chunkStr);
432 chunk_size = len - 1;
436 unsigned char* received_buffer = NULL;
437 std::size_t received_size = 0;
438 //CHECK(module->instance_id_);
440 module->client_->SendSyncMessageToNative(
441 module->instance_id_,
442 JsUtils::JSValueToString(context, arguments[0]),
448 // If we tried to send a message to an instance that became invalid,
449 // then reply will be NULL.
450 if (!reply.empty()) {
451 JSObjectRef return_obj = JSObjectMake(context, NULL, NULL);
452 JsUtils::SetProperty(context,
455 JsUtils::ToJSValueRef(context, reply),
458 if (received_buffer || received_size > 0) {
459 int chunk_id = GetNextChunkID();
460 ChunkData& chunk_data = module->data_chunk_storage_[chunk_id];
461 chunk_data.set_data(received_buffer);
462 chunk_data.set_length(received_size);
463 JsUtils::SetProperty(context,
466 JSValueMakeNumber(context, chunk_id),
472 return JSValueMakeNull(context);
476 JSValueRef XWalkExtensionModule::ReceiveChunkDataCallback(JSContextRef context,
478 JSObjectRef thisObject,
479 size_t argumentCount,
480 const JSValueRef arguments[],
481 JSValueRef* exception) {
483 XWalkExtensionModule* module = GetExtensionModule(thisObject);
484 if (!module || argumentCount < 1) {
485 return JSValueMakeBoolean(context, false);
489 if (JSValueIsNumber(context, arguments[0])) {
490 chunk_id = JSValueToNumber(context, arguments[0], NULL);
493 std::string chunk_type("octet");
494 if (argumentCount > 1 && JSValueIsString(context, arguments[1])) {
495 chunk_type = JsUtils::JSValueToString(context, arguments[1]);
498 JSValueRef jschunk = JSValueMakeNull(context);
500 auto it = module->data_chunk_storage_.find(chunk_id);
501 if (it != module->data_chunk_storage_.end()) {
502 ChunkData& chunk_data = it->second;
503 if (chunk_type == "string") {
504 JSStringRef jsstr = JSStringCreateWithUTF8CString(reinterpret_cast<const char*>(chunk_data.data()));
505 jschunk = JSValueMakeString(context, jsstr);
506 JSStringRelease(jsstr);
508 std::unique_ptr<JSValueRef []> valueArray(new JSValueRef[chunk_data.length()]);
509 for (std::size_t i = 0; i < chunk_data.length(); ++i) {
510 valueArray[i] = JSValueMakeNumber(context, chunk_data.data()[i]);
512 jschunk = JSObjectMakeArray(context,
517 module->data_chunk_storage_.erase(it);
524 JSValueRef XWalkExtensionModule::SendRuntimeMessageCallback(JSContextRef context,
526 JSObjectRef thisObject,
527 size_t argumentCount,
528 const JSValueRef arguments[],
529 JSValueRef* exception) {
530 LOGD("SendRuntimeMessageCallback");
531 XWalkExtensionModule* module = GetExtensionModule(thisObject);
532 if (!module || argumentCount < 1) {
533 return JSValueMakeBoolean(context, false);
535 std::string message = JsUtils::JSValueToString(context, arguments[0]);
537 if (argumentCount > 1) {
538 body = JsUtils::JSValueToString(context, arguments[1]);
540 IPCMessageSupport::sendMessageToUiProcess(message.c_str(), body.c_str());
541 return JSValueMakeBoolean(context, true);
547 JSContextRef context;
548 JSObjectRef callback;
553 JSValueRef XWalkExtensionModule::SendRuntimeAsyncMessageCallback(JSContextRef context,
555 JSObjectRef thisObject,
556 size_t argumentCount,
557 const JSValueRef arguments[],
558 JSValueRef* exception) {
559 LOGD("SendRuntimeAsyncMessageCallback");
560 XWalkExtensionModule* module = GetExtensionModule(thisObject);
561 if (!module || argumentCount < 1) {
562 return JSValueMakeBoolean(context, false);
564 std::string message = JsUtils::JSValueToString(context, arguments[0]);
566 if (argumentCount > 1) {
567 body = JsUtils::JSValueToString(context, arguments[1]);
569 JSObjectRef js_callback = NULL;
570 if (argumentCount > 2) {
571 js_callback = JSValueToObject(context, arguments[2], NULL);
572 if (js_callback != NULL) {
573 JSValueSafeProtect(context,js_callback);
576 AsyncData* user_data = new AsyncData;
577 user_data->callback = js_callback;
578 user_data->context = JSContextGetGlobalContext(context);
580 auto callback = [](unsigned int id, void* user_data, const char* result) -> void {
581 LOGD("SendRuntimeAsyncMessageCallback async result callback - start");
582 AsyncData* data = static_cast<AsyncData*>(user_data);
583 using namespace WrtDeviceApis::CommonsJavaScript;
585 && data->context != NULL
586 && JSLifeManager::GetInstance().IsAvailableContext(data->context)
587 && data->callback != NULL
588 && JSObjectIsFunction(data->context, data->callback)) {
589 JSValueRef args[1] = { JsUtils::ToJSValueRef(data->context, result) };
590 JSObjectCallAsFunction(data->context, data->callback, NULL, 1, args, NULL);
591 JSValueSafeUnprotect(data->context,data->callback);
598 LOGD("SendRuntimeAsyncMessageCallback send async call");
599 IPCMessageSupport::sendAsyncMessageToUiProcess(
602 callback, user_data);
603 return JSValueMakeBoolean(context, true);
607 JSValueRef XWalkExtensionModule::SendRuntimeSyncMessageCallback(JSContextRef context,
609 JSObjectRef thisObject,
610 size_t argumentCount,
611 const JSValueRef arguments[],
612 JSValueRef* exception) {
613 LOGD("SendRuntimeSyncMessageCallback");
614 XWalkExtensionModule* module = GetExtensionModule(thisObject);
615 if (!module || argumentCount < 1) {
616 return JSValueMakeUndefined(context);
618 std::string message = JsUtils::JSValueToString(context, arguments[0]);
620 if (argumentCount > 1) {
621 body = JsUtils::JSValueToString(context, arguments[1]);
624 std::string retval = IPCMessageSupport::sendSyncMessageToUiProcess(message.c_str(), body.c_str());
625 return JsUtils::ToJSValueRef(context, retval);
631 JSValueRef XWalkExtensionModule::SetMessageListenerCallback(JSContextRef context,
633 JSObjectRef thisObject,
634 size_t argumentCount,
635 const JSValueRef arguments[],
636 JSValueRef* exception) {
637 XWalkExtensionModule* module = GetExtensionModule(thisObject);
638 if (!module || argumentCount != 1) {
639 return JSValueMakeBoolean(context, false);
642 JSObjectRef obj = JSValueToObject(context, arguments[0], NULL);
643 if ( !(obj != NULL && JSObjectIsFunction(context, obj)) && !JSValueIsUndefined(context, arguments[0])) {
644 LOGE("Trying to set message listener with invalid value.");
645 return JSValueMakeBoolean(context, false);
648 if( module->message_listener_ != NULL ){
649 JSValueSafeUnprotect(context,module->message_listener_);
651 module->message_listener_ = NULL;
653 if (!JSValueIsUndefined(context, arguments[0])){
654 module->message_listener_ = obj;
655 JSValueSafeProtect(context,module->message_listener_);
658 return JSValueMakeBoolean(context, true);
661 JSValueRef XWalkExtensionModule::SetDataListenerCallback(JSContextRef context,
663 JSObjectRef thisObject,
664 size_t argumentCount,
665 const JSValueRef arguments[],
666 JSValueRef* exception) {
667 XWalkExtensionModule* module = GetExtensionModule(thisObject);
668 if (!module || argumentCount != 1) {
669 return JSValueMakeBoolean(context, false);
672 JSObjectRef obj = JSValueToObject(context, arguments[0], NULL);
673 if ( !(obj != NULL && JSObjectIsFunction(context, obj)) && !JSValueIsUndefined(context, arguments[0])) {
674 LOGE("Trying to set message listener with invalid value.");
675 return JSValueMakeBoolean(context, false);
678 if( module->data_listener_ != NULL ){
679 JSValueSafeUnprotect(context,module->data_listener_);
681 module->data_listener_ = NULL;
683 if (!JSValueIsUndefined(context, arguments[0])){
684 module->data_listener_ = obj;
685 JSValueSafeProtect(context,module->data_listener_);
688 return JSValueMakeBoolean(context, true);
693 XWalkExtensionModule* XWalkExtensionModule::GetExtensionModule(JSObjectRef thisObject) {
694 return static_cast<XWalkExtensionModule*>(JSObjectGetPrivate(thisObject));