[SVACE] Fix out of range exception error
[platform/framework/web/crosswalk-tizen.git] / extensions / renderer / xwalk_extension_module.cc
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.
6
7 #include "extensions/renderer/xwalk_extension_module.h"
8
9 #include <v8/v8.h>
10 #include <stdarg.h>
11 #include <stdio.h>
12
13 #include <vector>
14
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"
21
22 namespace extensions {
23
24 namespace {
25
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";
29
30 }  // namespace
31
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),
38       client_(client),
39       module_system_(module_system),
40       instance_id_("") {
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));
46
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.
51   object_template->Set(
52       v8::String::NewFromUtf8(isolate, "postMessage"),
53       v8::FunctionTemplate::New(isolate, PostMessageCallback, function_data));
54   object_template->Set(
55       v8::String::NewFromUtf8(isolate, "sendSyncMessage"),
56       v8::FunctionTemplate::New(
57           isolate, SendSyncMessageCallback, function_data));
58   object_template->Set(
59       v8::String::NewFromUtf8(isolate, "setMessageListener"),
60       v8::FunctionTemplate::New(
61           isolate, SetMessageListenerCallback, function_data));
62   object_template->Set(
63       v8::String::NewFromUtf8(isolate, "sendRuntimeMessage"),
64       v8::FunctionTemplate::New(
65           isolate, SendRuntimeMessageCallback, function_data));
66   object_template->Set(
67       v8::String::NewFromUtf8(isolate, "sendRuntimeSyncMessage"),
68       v8::FunctionTemplate::New(
69           isolate, SendRuntimeSyncMessageCallback, function_data));
70   object_template->Set(
71       v8::String::NewFromUtf8(isolate, "sendRuntimeAsyncMessage"),
72       v8::FunctionTemplate::New(
73           isolate, SendRuntimeAsyncMessageCallback, function_data));
74
75   function_data_.Reset(isolate, function_data);
76   object_template_.Reset(isolate, object_template);
77 }
78
79 XWalkExtensionModule::~XWalkExtensionModule() {
80   v8::Isolate* isolate = v8::Isolate::GetCurrent();
81   v8::HandleScope handle_scope(isolate);
82
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));
91
92   object_template_.Reset();
93   function_data_.Reset();
94   message_listener_.Reset();
95
96   if (!instance_id_.empty())
97     client_->DestroyInstance(module_system_->GetV8Context(), instance_id_);
98 }
99
100 namespace {
101
102 std::string CodeToEnsureNamespace(const std::string& extension_name) {
103   std::string result;
104   size_t pos = 0;
105   while (true) {
106     pos = extension_name.find('.', pos);
107     if (pos == std::string::npos) {
108       result += extension_name + " = {};";
109       break;
110     }
111     std::string ns = extension_name.substr(0, pos);
112     result += ns + " = " + ns + " || {}; ";
113     pos++;
114   }
115   return result;
116 }
117
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,
123                            va_list ap) {
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];
128
129   va_list ap_copy;
130   va_copy(ap_copy, ap);
131
132   int result = vsnprintf(stack_buf, ARRAYSIZE(stack_buf), format, ap_copy);
133   va_end(ap_copy);
134
135   if (result >= 0 && result < static_cast<int>(ARRAYSIZE(stack_buf))) {
136     // It fit.
137     dst->append(stack_buf, result);
138     return;
139   }
140
141   // Repeatedly increase buffer size until it fits.
142   int mem_length = ARRAYSIZE(stack_buf);
143   while (true) {
144     if (result < 0) {
145       if (errno != 0 && errno != EOVERFLOW)
146         return;
147       // Try doubling the buffer size.
148       mem_length *= 2;
149     } else {
150       // We need exactly "result + 1" characters.
151       mem_length = result + 1;
152     }
153
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.");
159       return;
160     }
161
162     std::vector<typename StringType::value_type> mem_buf(mem_length);
163
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);
168     va_end(ap_copy);
169
170     if ((result >= 0) && (result < mem_length)) {
171       // It fit.
172       dst->append(&mem_buf[0], result);
173       return;
174     }
175   }
176 }
177
178 std::string StringPrintf(const char* format, ...) {
179   va_list ap;
180   va_start(ap, format);
181   std::string result;
182   StringAppendVT(&result, format, ap);
183   va_end(ap);
184   return result;
185 }
186
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.
192
193   return StringPrintf(
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})();"
200       "%s = exports; });",
201       CodeToEnsureNamespace(extension_name).c_str(),
202       extension_code.c_str(),
203       extension_name.c_str());
204 }
205
206 std::string ExceptionToString(const v8::TryCatch& try_catch) {
207   std::string str;
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));
213   } else {
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));
221   }
222   return str;
223 }
224
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()));
231
232   v8::TryCatch try_catch;
233   try_catch.SetVerbose(true);
234
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)));
240   }
241
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)));
247   }
248
249   return handle_scope.Escape(result);
250 }
251
252 }  // namespace
253
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_;
259     return;
260   }
261
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_;
266       return;
267     }
268   }
269
270   std::string wrapped_api_code = WrapAPICode(extension_code_, extension_name_);
271
272   std::string exception;
273   v8::Handle<v8::Value> result = RunString(wrapped_api_code, &exception);
274
275   if (!result->IsFunction()) {
276     LOGGER(ERROR) << "Couldn't load JS API code for "
277                   << extension_name_ << " : " << exception;
278     return;
279   }
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(),
284                                           object_template_);
285
286   const int argc = 2;
287   v8::Handle<v8::Value> argv[argc] = {
288     object_template->NewInstance(),
289     require_native
290   };
291
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);
298   }
299 }
300
301 void XWalkExtensionModule::HandleMessageFromNative(const std::string& msg) {
302   if (message_listener_.IsEmpty())
303     return;
304
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);
309
310   v8::Handle<v8::Value> args[] = {
311       v8::String::NewFromUtf8(isolate, msg.c_str()) };
312
313   v8::Handle<v8::Function> message_listener =
314       v8::Local<v8::Function>::New(isolate, message_listener_);
315
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);
321 }
322
323 // static
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) {
329     result.Set(false);
330     return;
331   }
332
333   v8::String::Utf8Value value(info[0]->ToString());
334
335   // CHECK(module->instance_id_);
336   module->client_->PostMessageToNative(module->module_system_->GetV8Context(),
337                                        module->instance_id_,
338                                        std::string(*value));
339   result.Set(true);
340 }
341
342 // static
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) {
348     result.Set(false);
349     return;
350   }
351
352   v8::String::Utf8Value value(info[0]->ToString());
353
354   // CHECK(module->instance_id_);
355   std::string reply =
356       module->client_->SendSyncMessageToNative(
357           module->module_system_->GetV8Context(),
358           module->instance_id_,
359           std::string(*value));
360
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()));
365   }
366 }
367
368 // static
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) {
374     result.Set(false);
375     return;
376   }
377
378   if (!info[0]->IsFunction() && !info[0]->IsUndefined()) {
379     LOGGER(ERROR) << "Trying to set message listener with invalid value.";
380     result.Set(false);
381     return;
382   }
383
384   v8::Isolate* isolate = info.GetIsolate();
385   if (info[0]->IsUndefined())
386     module->message_listener_.Reset();
387   else
388     module->message_listener_.Reset(isolate, info[0].As<v8::Function>());
389
390   result.Set(true);
391 }
392
393 // static
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) {
399     result.Set(false);
400     return;
401   }
402
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);
408   }
409
410   RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance();
411   rc->SendMessage(module->module_system_->GetV8Context(),
412                   std::string(*type), data_str);
413
414   result.Set(true);
415 }
416
417 // static
418 void XWalkExtensionModule::SendRuntimeSyncMessageCallback(
419     const v8::FunctionCallbackInfo<v8::Value>& info) {
420   v8::Isolate* isolate = info.GetIsolate();
421
422   v8::ReturnValue<v8::Value> result(info.GetReturnValue());
423   XWalkExtensionModule* module = GetExtensionModule(info);
424   if (!module || info.Length() < 1) {
425     result.SetUndefined();
426     return;
427   }
428
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);
434   }
435
436   RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance();
437   std::string reply = rc->SendSyncMessage(
438       module->module_system_->GetV8Context(),
439       std::string(*type), data_str);
440
441   result.Set(v8::String::NewFromUtf8(isolate, reply.c_str()));
442 }
443
444 // static
445 void XWalkExtensionModule::SendRuntimeAsyncMessageCallback(
446     const v8::FunctionCallbackInfo<v8::Value>& info) {
447   v8::Isolate* isolate = info.GetIsolate();
448   v8::HandleScope handle_scope(isolate);
449
450   v8::ReturnValue<v8::Value> result(info.GetReturnValue());
451   XWalkExtensionModule* module = GetExtensionModule(info);
452   if (!module || info.Length() < 1) {
453     result.Set(false);
454     return;
455   }
456
457   // type
458   v8::String::Utf8Value type(info[0]->ToString());
459
460   // value
461   std::string value_str;
462   if (info.Length() > 1) {
463     v8::String::Utf8Value value(info[1]->ToString());
464     value_str = std::string(*value);
465   }
466
467   // callback
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);
473     }
474   }
475
476   auto callback = [js_callback](const std::string& /*type*/,
477                      const std::string& value) -> void {
478     if (!js_callback) {
479       LOGGER(ERROR) << "JsCallback is NULL.";
480       return;
481     }
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);
487     delete js_callback;
488   };
489
490   RuntimeIPCClient* rc = RuntimeIPCClient::GetInstance();
491   rc->SendAsyncMessage(module->module_system_->GetV8Context(),
492                        std::string(*type), value_str, callback);
493
494   result.Set(true);
495 }
496
497 // static
498 XWalkExtensionModule* XWalkExtensionModule::GetExtensionModule(
499     const v8::FunctionCallbackInfo<v8::Value>& info) {
500   v8::Isolate* isolate = info.GetIsolate();
501   v8::HandleScope handle_scope(isolate);
502
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!";
508     return NULL;
509   }
510   // CHECK(module->IsExternal());
511   return static_cast<XWalkExtensionModule*>(module.As<v8::External>()->Value());
512 }
513
514 }  // namespace extensions