1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Copyright (c) 2013 Intel Corporation. All rights reserved.
3 // Copyright (c) 2015 Samsung Electronics Co., Ltd. All rights reserved.
4 // Use of this source code is governed by a BSD-style license that can be
5 // found in the LICENSE file.
7 #include "extensions/renderer/xwalk_extension_module.h"
15 #include "common/arraysize.h"
16 #include "common/logger.h"
17 #include "common/profiler.h"
18 #include "extensions/renderer/runtime_ipc_client.h"
19 #include "extensions/renderer/xwalk_extension_client.h"
20 #include "extensions/renderer/xwalk_module_system.h"
22 namespace extensions {
26 // This is the key used in the data object passed to our callbacks to store a
27 // pointer back to kXWalkExtensionModule.
28 const char* kXWalkExtensionModule = "kXWalkExtensionModule";
32 XWalkExtensionModule::XWalkExtensionModule(XWalkExtensionClient* client,
33 XWalkModuleSystem* module_system,
34 const std::string& extension_name,
35 const std::string& extension_code)
36 : extension_name_(extension_name),
37 extension_code_(extension_code),
39 module_system_(module_system),
41 v8::Isolate* isolate = v8::Isolate::GetCurrent();
42 v8::HandleScope handle_scope(isolate);
43 v8::Handle<v8::Object> function_data = v8::Object::New(isolate);
44 function_data->Set(v8::String::NewFromUtf8(isolate, kXWalkExtensionModule),
45 v8::External::New(isolate, this));
47 v8::Handle<v8::ObjectTemplate> object_template =
48 v8::ObjectTemplate::New(isolate);
49 // TODO(cmarcelo): Use Template::Set() function that takes isolate, once we
50 // update the Chromium (and V8) version.
52 v8::String::NewFromUtf8(isolate, "postMessage"),
53 v8::FunctionTemplate::New(isolate, PostMessageCallback, function_data));
55 v8::String::NewFromUtf8(isolate, "sendSyncMessage"),
56 v8::FunctionTemplate::New(
57 isolate, SendSyncMessageCallback, function_data));
59 v8::String::NewFromUtf8(isolate, "setMessageListener"),
60 v8::FunctionTemplate::New(
61 isolate, SetMessageListenerCallback, function_data));
63 v8::String::NewFromUtf8(isolate, "sendRuntimeMessage"),
64 v8::FunctionTemplate::New(
65 isolate, SendRuntimeMessageCallback, function_data));
67 v8::String::NewFromUtf8(isolate, "sendRuntimeSyncMessage"),
68 v8::FunctionTemplate::New(
69 isolate, SendRuntimeSyncMessageCallback, function_data));
71 v8::String::NewFromUtf8(isolate, "sendRuntimeAsyncMessage"),
72 v8::FunctionTemplate::New(
73 isolate, SendRuntimeAsyncMessageCallback, function_data));
75 function_data_.Reset(isolate, function_data);
76 object_template_.Reset(isolate, object_template);
79 XWalkExtensionModule::~XWalkExtensionModule() {
80 v8::Isolate* isolate = v8::Isolate::GetCurrent();
81 v8::HandleScope handle_scope(isolate);
83 // Deleting the data will disable the functions, they'll return early. We do
84 // this because it might be the case that the JS objects we created outlive
85 // this object (getting references from inside an iframe and then destroying
86 // the iframe), even if we destroy the references we have.
87 v8::Handle<v8::Object> function_data =
88 v8::Local<v8::Object>::New(isolate, function_data_);
89 function_data.As<v8::Object>()->Delete(v8::String::NewFromUtf8(isolate,
90 kXWalkExtensionModule));
92 object_template_.Reset();
93 function_data_.Reset();
94 message_listener_.Reset();
96 if (!instance_id_.empty())
97 client_->DestroyInstance(module_system_->GetV8Context(), instance_id_);
102 std::string CodeToEnsureNamespace(const std::string& extension_name) {
106 pos = extension_name.find('.', pos);
107 if (pos == std::string::npos) {
108 result += extension_name + " = {};";
111 std::string ns = extension_name.substr(0, pos);
112 result += ns + " = " + ns + " || {}; ";
118 // Templatized backend for StringPrintF/StringAppendF. This does not finalize
119 // the va_list, the caller is expected to do that.
120 template <class StringType>
121 static void StringAppendVT(StringType* dst,
122 const typename StringType::value_type* format,
124 // First try with a small fixed size buffer.
125 // This buffer size should be kept in sync with StringUtilTest.GrowBoundary
126 // and StringUtilTest.StringPrintfBounds.
127 typename StringType::value_type stack_buf[1024];
130 va_copy(ap_copy, ap);
132 int result = vsnprintf(stack_buf, ARRAYSIZE(stack_buf), format, ap_copy);
135 if (result >= 0 && result < static_cast<int>(ARRAYSIZE(stack_buf))) {
137 dst->append(stack_buf, result);
141 // Repeatedly increase buffer size until it fits.
142 int mem_length = ARRAYSIZE(stack_buf);
145 if (errno != 0 && errno != EOVERFLOW)
147 // Try doubling the buffer size.
150 // We need exactly "result + 1" characters.
151 mem_length = result + 1;
154 if (mem_length > 32 * 1024 * 1024) {
155 // That should be plenty, don't try anything larger. This protects
156 // against huge allocations when using vsnprintfT implementations that
157 // return -1 for reasons other than overflow without setting errno.
158 LOGE("Unable to printf the requested string due to size.");
162 std::vector<typename StringType::value_type> mem_buf(mem_length);
164 // NOTE: You can only use a va_list once. Since we're in a while loop, we
165 // need to make a new copy each time so we don't use up the original.
166 va_copy(ap_copy, ap);
167 result = vsnprintf(&mem_buf[0], mem_length, format, ap_copy);
170 if ((result >= 0) && (result < mem_length)) {
172 dst->append(&mem_buf[0], result);
178 std::string StringPrintf(const char* format, ...) {
180 va_start(ap, format);
182 StringAppendVT(&result, format, ap);
187 // Wrap API code into a callable form that takes extension object as parameter.
188 std::string WrapAPICode(const std::string& extension_code,
189 const std::string& extension_name) {
190 // We take care here to make sure that line numbering for api_code after
191 // wrapping doesn't change, so that syntax errors point to the correct line.
194 "var %s; (function(extension, requireNative) { "
195 "extension.internal = {};"
196 "extension.internal.sendSyncMessage = extension.sendSyncMessage;"
197 "delete extension.sendSyncMessage;"
198 "var Object = requireNative('objecttools');"
199 "var exports = {}; (function() {'use strict'; %s\n})();"
201 CodeToEnsureNamespace(extension_name).c_str(),
202 extension_code.c_str(),
203 extension_name.c_str());
206 std::string ExceptionToString(const v8::TryCatch& try_catch) {
208 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
209 v8::String::Utf8Value exception(try_catch.Exception());
210 v8::Local<v8::Message> message(try_catch.Message());
211 if (message.IsEmpty()) {
212 str.append(StringPrintf("%s\n", *exception));
214 v8::String::Utf8Value filename(message->GetScriptResourceName());
215 int linenum = message->GetLineNumber();
216 int colnum = message->GetStartColumn();
217 str.append(StringPrintf(
218 "%s:%i:%i %s\n", *filename, linenum, colnum, *exception));
219 v8::String::Utf8Value sourceline(message->GetSourceLine());
220 str.append(StringPrintf("%s\n", *sourceline));
225 v8::Handle<v8::Value> RunString(const std::string& code,
226 std::string* exception) {
227 v8::Isolate* isolate = v8::Isolate::GetCurrent();
228 v8::EscapableHandleScope handle_scope(isolate);
229 v8::Handle<v8::String> v8_code(
230 v8::String::NewFromUtf8(isolate, code.c_str()));
232 v8::TryCatch try_catch;
233 try_catch.SetVerbose(true);
235 v8::Handle<v8::Script> script(v8::Script::Compile(v8_code));
236 if (try_catch.HasCaught()) {
237 *exception = ExceptionToString(try_catch);
238 return handle_scope.Escape(
239 v8::Local<v8::Primitive>(v8::Undefined(isolate)));
242 v8::Local<v8::Value> result = script->Run();
243 if (try_catch.HasCaught()) {
244 *exception = ExceptionToString(try_catch);
245 return handle_scope.Escape(
246 v8::Local<v8::Primitive>(v8::Undefined(isolate)));
249 return handle_scope.Escape(result);
254 void XWalkExtensionModule::LoadExtensionCode(
255 v8::Handle<v8::Context> context, v8::Handle<v8::Function> require_native) {
256 instance_id_ = client_->CreateInstance(context, extension_name_, this);
257 if (instance_id_.empty()) {
258 LOGGER(ERROR) << "Failed to create an instance of " << extension_name_;
262 if (extension_code_.empty()) {
263 extension_code_ = client_->GetAPIScript(context, extension_name_);
264 if (extension_code_.empty()) {
265 LOGGER(ERROR) << "Failed to get API script of " << extension_name_;
270 std::string wrapped_api_code = WrapAPICode(extension_code_, extension_name_);
272 std::string exception;
273 v8::Handle<v8::Value> result = RunString(wrapped_api_code, &exception);
275 if (!result->IsFunction()) {
276 LOGGER(ERROR) << "Couldn't load JS API code for "
277 << extension_name_ << " : " << exception;
280 v8::Handle<v8::Function> callable_api_code =
281 v8::Handle<v8::Function>::Cast(result);
282 v8::Handle<v8::ObjectTemplate> object_template =
283 v8::Local<v8::ObjectTemplate>::New(context->GetIsolate(),
287 v8::Handle<v8::Value> argv[argc] = {
288 object_template->NewInstance(),
292 v8::TryCatch try_catch;
293 try_catch.SetVerbose(true);
294 callable_api_code->Call(context->Global(), argc, argv);
295 if (try_catch.HasCaught()) {
296 LOGGER(ERROR) << "Exception while loading JS API code for "
297 << extension_name_ << " : " << ExceptionToString(try_catch);
301 void XWalkExtensionModule::HandleMessageFromNative(const std::string& msg) {
302 if (message_listener_.IsEmpty())
305 v8::Isolate* isolate = v8::Isolate::GetCurrent();
306 v8::HandleScope handle_scope(isolate);
307 v8::Handle<v8::Context> context = module_system_->GetV8Context();
308 v8::Context::Scope context_scope(context);
310 v8::Handle<v8::Value> args[] = {
311 v8::String::NewFromUtf8(isolate, msg.c_str()) };
313 v8::Handle<v8::Function> message_listener =
314 v8::Local<v8::Function>::New(isolate, message_listener_);
316 v8::TryCatch try_catch;
317 message_listener->Call(context->Global(), 1, args);
318 if (try_catch.HasCaught())
319 LOGGER(ERROR) << "Exception when running message listener: "
320 << ExceptionToString(try_catch);
324 void XWalkExtensionModule::PostMessageCallback(
325 const v8::FunctionCallbackInfo<v8::Value>& info) {
326 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
327 XWalkExtensionModule* module = GetExtensionModule(info);
328 if (!module || info.Length() != 1) {
333 v8::String::Utf8Value value(info[0]->ToString());
335 // CHECK(module->instance_id_);
336 module->client_->PostMessageToNative(module->module_system_->GetV8Context(),
337 module->instance_id_,
338 std::string(*value));
343 void XWalkExtensionModule::SendSyncMessageCallback(
344 const v8::FunctionCallbackInfo<v8::Value>& info) {
345 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
346 XWalkExtensionModule* module = GetExtensionModule(info);
347 if (!module || info.Length() != 1) {
352 v8::String::Utf8Value value(info[0]->ToString());
354 // CHECK(module->instance_id_);
356 module->client_->SendSyncMessageToNative(
357 module->module_system_->GetV8Context(),
358 module->instance_id_,
359 std::string(*value));
361 // If we tried to send a message to an instance that became invalid,
362 // then reply will be NULL.
363 if (!reply.empty()) {
364 result.Set(v8::String::NewFromUtf8(info.GetIsolate(), reply.c_str()));
369 void XWalkExtensionModule::SetMessageListenerCallback(
370 const v8::FunctionCallbackInfo<v8::Value>& info) {
371 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
372 XWalkExtensionModule* module = GetExtensionModule(info);
373 if (!module || info.Length() != 1) {
378 if (!info[0]->IsFunction() && !info[0]->IsUndefined()) {
379 LOGGER(ERROR) << "Trying to set message listener with invalid value.";
384 v8::Isolate* isolate = info.GetIsolate();
385 if (info[0]->IsUndefined())
386 module->message_listener_.Reset();
388 module->message_listener_.Reset(isolate, info[0].As<v8::Function>());
394 void XWalkExtensionModule::SendRuntimeMessageCallback(
395 const v8::FunctionCallbackInfo<v8::Value>& info) {
396 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
397 XWalkExtensionModule* module = GetExtensionModule(info);
398 if (!module || info.Length() < 1) {
403 v8::String::Utf8Value type(info[0]->ToString());
404 std::string data_str;
405 if (info.Length() > 1) {
406 v8::String::Utf8Value data(info[1]->ToString());
407 data_str = std::string(*data);
410 RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance();
411 rc->SendMessage(module->module_system_->GetV8Context(),
412 std::string(*type), data_str);
418 void XWalkExtensionModule::SendRuntimeSyncMessageCallback(
419 const v8::FunctionCallbackInfo<v8::Value>& info) {
420 v8::Isolate* isolate = info.GetIsolate();
422 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
423 XWalkExtensionModule* module = GetExtensionModule(info);
424 if (!module || info.Length() < 1) {
425 result.SetUndefined();
429 v8::String::Utf8Value type(info[0]->ToString());
430 std::string data_str;
431 if (info.Length() > 1) {
432 v8::String::Utf8Value data(info[1]->ToString());
433 data_str = std::string(*data);
436 RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance();
437 std::string reply = rc->SendSyncMessage(
438 module->module_system_->GetV8Context(),
439 std::string(*type), data_str);
441 result.Set(v8::String::NewFromUtf8(isolate, reply.c_str()));
445 void XWalkExtensionModule::SendRuntimeAsyncMessageCallback(
446 const v8::FunctionCallbackInfo<v8::Value>& info) {
447 v8::Isolate* isolate = info.GetIsolate();
448 v8::HandleScope handle_scope(isolate);
450 v8::ReturnValue<v8::Value> result(info.GetReturnValue());
451 XWalkExtensionModule* module = GetExtensionModule(info);
452 if (!module || info.Length() < 1) {
458 v8::String::Utf8Value type(info[0]->ToString());
461 std::string value_str;
462 if (info.Length() > 1) {
463 v8::String::Utf8Value value(info[1]->ToString());
464 value_str = std::string(*value);
468 RuntimeIPCClient::JSCallback* js_callback = NULL;
469 if (info.Length() > 2) {
470 if (info[2]->IsFunction()) {
471 v8::Handle<v8::Function> func = info[2].As<v8::Function>();
472 js_callback = new RuntimeIPCClient::JSCallback(isolate, func);
476 auto callback = [js_callback](const std::string& /*type*/,
477 const std::string& value) -> void {
479 LOGGER(ERROR) << "JsCallback is NULL.";
482 v8::Isolate* isolate = v8::Isolate::GetCurrent();
483 v8::HandleScope handle_scope(isolate);
484 v8::Handle<v8::Value> args[] = {
485 v8::String::NewFromUtf8(isolate, value.c_str()) };
486 js_callback->Call(isolate, args);
490 RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance();
491 rc->SendAsyncMessage(module->module_system_->GetV8Context(),
492 std::string(*type), value_str, callback);
498 XWalkExtensionModule* XWalkExtensionModule::GetExtensionModule(
499 const v8::FunctionCallbackInfo<v8::Value>& info) {
500 v8::Isolate* isolate = info.GetIsolate();
501 v8::HandleScope handle_scope(isolate);
503 v8::Local<v8::Object> data = info.Data().As<v8::Object>();
504 v8::Local<v8::Value> module =
505 data->Get(v8::String::NewFromUtf8(isolate, kXWalkExtensionModule));
506 if (module.IsEmpty() || module->IsUndefined()) {
507 LOGGER(ERROR) << "Trying to use extension from already destroyed context!";
510 // CHECK(module->IsExternal());
511 return static_cast<XWalkExtensionModule*>(module.As<v8::External>()->Value());
514 } // namespace extensions