Update wrt-plugins-common_0.3.53
[framework/web/wrt-plugins-common.git] / src / plugin-loading / javascript_interface.cpp
1 /*
2  * Copyright (c) 2011 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.0 (the "License");
5  *    you may not use this file except in compliance with the License.
6  *    You may obtain a copy of the License at
7  *
8  *        http://www.apache.org/licenses/LICENSE-2.0
9  *
10  *    Unless required by applicable law or agreed to in writing, software
11  *    distributed under the License is distributed on an "AS IS" BASIS,
12  *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *    See the License for the specific language governing permissions and
14  *    limitations under the License.
15  */
16 /**
17  * @file        plugin_webkit.h
18  * @author      Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
19  * @version     1.0
20  * @brief       This file is the implementation file of webkit js plugin
21  *              accessing routines in EFL
22  */
23 #include <javascript_interface.h>
24 #include <dpl/log/log.h>
25 #include <dpl/scoped_array.h>
26 #include <vector>
27 #include <dpl/singleton_impl.h>
28 #include <string>
29 #include <dpl/foreach.h>
30 #include <dpl/noncopyable.h>
31 #include <JavaScriptCore/JavaScript.h>
32 #include <JavaScriptCore/JSObjectRef.h>
33
34 IMPLEMENT_SINGLETON(JavaScriptInterface)
35
36 #define CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, object, ret) \
37     if (JSValueIsUndefined(context, object)) {\
38         LogError("Object " << #object << " is undefined");\
39         return ret;\
40     }
41
42 #define CHECK_JSOBJECT_IS_NULL_RETURN(object, ret) \
43     if (!object) {\
44         LogError("Object " << #object << " is NULL");\
45         return ret;\
46     }
47
48 namespace {
49 /**
50  * Converts JSStringRef to std::string
51  * */
52 std::string toString(const JSStringRef& arg)
53 {
54     Assert(arg);
55     std::string result;
56     size_t jsSize = JSStringGetMaximumUTF8CStringSize(arg);
57     if (jsSize > 0) {
58         ++jsSize;
59         DPL::ScopedArray<char> buffer(new char[jsSize]);
60         size_t written = JSStringGetUTF8CString(arg, buffer.Get(), jsSize);
61         if (written > jsSize) {
62             LogError("Conversion could not be fully performed.");
63             return std::string();
64         }
65         result = buffer.Get();
66     }
67
68     return result;
69 }
70
71 /**
72  * Converts JSValueRef to std::string
73  * */
74 std::string toString(JSContextRef ctx, JSValueRef value) {
75   Assert(ctx && value);
76   std::string result;
77   JSStringRef str = JSValueToStringCopy(ctx, value, NULL);
78   result = toString(str);
79   JSStringRelease(str);
80   return result;
81 }
82
83 JSValueRef getProperty(JSContextRef ctx, JSValueRef value, const char* name) {
84   Assert(ctx && value && name);
85   JSValueRef result = NULL;
86   JSObjectRef obj = JSValueToObject(ctx, value, NULL);
87   if (obj) {
88     JSStringRef str = JSStringCreateWithUTF8CString(name);
89     result = JSObjectGetProperty(ctx, obj, str, NULL);
90     JSStringRelease(str);
91   }
92   return result;
93 }
94
95 JSValueRef getPropertyObj(JSContextRef ctx, JSObjectRef obj, const char* name) {
96   Assert(ctx && obj && name);
97   JSStringRef str = JSStringCreateWithUTF8CString(name);
98   JSValueRef result = JSObjectGetProperty(ctx, obj, str, NULL);
99   JSStringRelease(str);
100   return result;
101 }
102
103 JSObjectRef getJSGlobalObject(JSGlobalContextRef context){
104     return JSContextGetGlobalObject(context);
105 }
106
107 }
108
109 typedef JSValueRef (*JSCFunction)(
110     JSContextRef context,
111     JSObjectRef object,
112     JSObjectRef thisObject,
113     size_t argumentCount,
114     const JSValueRef arguments[],
115     JSValueRef* exception);
116
117
118 void JavaScriptInterface::setObjectProperty(JSGlobalContextRef context,
119                                         const JSObjectPtr& parentObject,
120                                         const std::string &propertyName,
121                                         const JSObjectPtr& propertyObject)
122 {
123     LogInfo("Webkit:setting property --" << propertyName << "--");
124
125     //create name
126     JSStringRef name = JSStringCreateWithUTF8CString(propertyName.c_str());
127     //set property
128     JSObjectSetProperty(
129         static_cast<JSGlobalContextRef>(context),
130         static_cast<JSObjectRef>(parentObject->getObject()), name,
131         static_cast<JSObjectRef>(propertyObject->getObject()),
132         kJSPropertyAttributeReadOnly, 0);
133
134     JSStringRelease(name);
135 }
136
137 void JavaScriptInterface::removeObjectProperty(JSGlobalContextRef context,
138         const JSObjectPtr& parentObject,
139         const std::string &propertyName)
140 {
141     if (!context) {
142         //nothing to do -> no context
143         return;
144     }
145     LogDebug("Deleting property --" << propertyName << "--");
146
147     JSStringRef name = JSStringCreateWithUTF8CString(propertyName.c_str());
148     JSObjectDeleteProperty(
149         static_cast<JSGlobalContextRef>(context),
150         static_cast<JSObjectRef>(parentObject->getObject()), name, 0);
151
152     JSStringRelease(name);
153 }
154
155 JavaScriptInterface::PropertiesList JavaScriptInterface::getObjectPropertiesList(
156         JSGlobalContextRef context,
157         const JSObjectPtr& object) const
158 {
159     PropertiesList result;
160     JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(
161             static_cast<JSGlobalContextRef>(context),
162             static_cast<JSObjectRef>(object->getObject()));
163     std::size_t count = JSPropertyNameArrayGetCount(properties);
164     result.reserve(count);
165     LogDebug("propesties count " << count);
166     for (std::size_t i = 0; i < count; ++i) {
167         JSStringRef property = JSPropertyNameArrayGetNameAtIndex(properties, i);
168         result.push_back(toString(property));
169     }
170     JSPropertyNameArrayRelease(properties);
171     return result;
172 }
173
174 JSObjectPtr JavaScriptInterface::makeJSFunctionObject(
175         JSGlobalContextRef context,
176         const std::string &name,
177         js_function_impl functionImplementation) const
178 {
179     LogDebug("Create JS function");
180     JSStringRef jsFunName = JSStringCreateWithUTF8CString(name.c_str());
181
182     JSObjectRef object = JSObjectMakeFunctionWithCallback(
183         context,
184         jsFunName,
185         reinterpret_cast<JSObjectCallAsFunctionCallback>(
186                 functionImplementation));
187
188     JSStringRelease(jsFunName);
189     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
190 }
191
192 JSObjectPtr JavaScriptInterface::makeJSClassObject(
193         JSGlobalContextRef context,
194         JSObjectDeclaration::ConstClassTemplate classTemplate) const
195 {
196     LogDebug("Create JS object");
197     JSObjectRef object = JSObjectMake(
198             context,
199             static_cast<JSClassRef>(
200                 const_cast<JSObjectDeclaration::ClassTemplate>(classTemplate)),
201             NULL);
202     return JSObjectPtr(new JSObject(object));
203 }
204
205 JSObjectPtr JavaScriptInterface::makeJSObjectBasedOnInterface(
206         JSGlobalContextRef context,
207         const std::string &interfaceName) const
208 {
209     LogDebug("Create JS object base on interface: " << interfaceName);
210     LogDebug("Context: " << context);
211
212
213     JSObjectPtr jsInterfaceObj = getJSObjectProperty(context,
214             getGlobalObject(context), interfaceName);
215     JSObjectRef object = JSObjectCallAsConstructor(context,
216             static_cast<JSObjectRef>(jsInterfaceObj->getObject()), 0, NULL,
217             NULL);
218     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
219 }
220
221 JSObjectPtr JavaScriptInterface::makeJSInterface(
222         JSGlobalContextRef context,
223         JSObjectDeclaration::ConstClassTemplate classTemplate,
224         JSObjectDeclaration::ConstructorCallback constructorCallback) const
225 {
226     LogDebug("Create JS interface. Context: " << context);
227     JSObjectRef object = JSObjectMakeConstructor(context,
228             static_cast<JSClassRef>(
229                     const_cast<JSObjectDeclaration::ClassTemplate>(
230                             classTemplate)),
231             reinterpret_cast<JSObjectCallAsConstructorCallback>(
232                     constructorCallback)
233             );
234     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
235 }
236
237 JSObjectPtr JavaScriptInterface::createObject(JSGlobalContextRef context,
238         const JSObjectDeclarationPtr& declaration)
239 {
240     typedef JSObjectDeclaration::Options JO;
241
242     if( declaration->getOptions())
243     {
244         switch (declaration->getOptions()->getType())
245         {
246         case JO::ClassType::Function:
247             return makeJSFunctionObject(
248                     context,
249                     declaration->getName(),
250                     declaration->getOptions()->getFunctionImpl());
251
252         case JO::ClassType::Class:
253             if (declaration->getInterfaceName().empty()) {
254                 return makeJSClassObject(
255                         context,
256                         declaration->getClassTemplate());
257             } else {
258                 return makeJSObjectBasedOnInterface(
259                         context,
260                         declaration->getInterfaceName());
261             }
262
263         case JO::ClassType::Interface:
264             return makeJSInterface(
265                     context,
266                     /* product class template */
267                     declaration->getClassTemplate(),
268                     declaration->getConstructorCallback());
269
270         default:
271             LogError("Invalid class type. Making empty JS object.");
272             return JSObjectPtr(new JSObject(
273                     JSObjectMake(context, NULL, NULL)));
274         }
275     } else {
276         LogDebug("No declaration options");
277         return JSObjectPtr(new JSObject(
278                 JSObjectMake(
279                     context,
280                     static_cast<JSClassRef>(
281                             const_cast<JSObjectDeclaration::ClassTemplate>(
282                                     declaration->getClassTemplate())),
283                     NULL)));
284     }
285 }
286
287 JSObjectPtr JavaScriptInterface::getGlobalObject(JSGlobalContextRef context)
288     const
289 {
290     return JSObjectPtr(new JSObject(static_cast<JSObject::RealObject>(
291         JSContextGetGlobalObject(static_cast<JSGlobalContextRef>(context)))));
292 }
293
294 JSObjectPtr JavaScriptInterface::copyObjectToIframe(
295     JSGlobalContextRef context,
296     const JSObjectPtr& iframe,
297     const std::string& name)
298 {
299     LogError("Copy object to iframe: " << name);
300
301     JSGlobalContextRef jsGlobalContext =
302         static_cast<JSGlobalContextRef>(context);
303
304     JSObjectRef globalObject = JSContextGetGlobalObject(jsGlobalContext);
305
306     JSValueRef requestedObject = getPropertyObj(jsGlobalContext,
307                                                 globalObject,
308                                                 name.c_str());
309     CHECK_JSVALUE_IS_UNDEFINED_RETURN(jsGlobalContext,
310                                       requestedObject,
311                                       JSObjectPtr());
312
313     JSStringRef requestedObjectStr =
314         JSStringCreateWithUTF8CString(name.c_str());
315
316     JSObjectSetProperty(jsGlobalContext,
317                         static_cast<JSObjectRef>(iframe->getObject()),
318                         requestedObjectStr,
319                         requestedObject,
320                         kJSPropertyAttributeReadOnly,
321                         NULL);
322
323     JSStringRelease(requestedObjectStr);
324
325     return JSObjectPtr(
326         new JSObject(const_cast<OpaqueJSValue*>(requestedObject)));
327 }
328
329 JavaScriptInterface::ObjectsListPtr
330 JavaScriptInterface::getIframesList(JSGlobalContextRef ctx) const
331 {
332     JSGlobalContextRef context = static_cast<JSGlobalContextRef>(ctx);
333
334     LogDebug("get global object");
335     JSObjectRef globalObject = JSContextGetGlobalObject(context);
336
337     ObjectsListPtr retList = getIframesList(context, globalObject);
338
339     return retList;
340 }
341
342 JavaScriptInterface::ObjectsListPtr
343 JavaScriptInterface::getIframesList(JSContextRef context,
344                                     JSObjectRef globalObject) const
345 {
346     JSValueRef frames = getPropertyObj(context, globalObject, "frames");
347     CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, frames, ObjectsListPtr());
348
349     JSObjectRef frames_o = JSValueToObject(context, frames, NULL);
350     CHECK_JSOBJECT_IS_NULL_RETURN(frames_o, ObjectsListPtr());
351
352     JSValueRef len = getPropertyObj(context, frames_o, "length");
353     CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, len, ObjectsListPtr());
354
355     size_t count = JSValueToNumber(context, len, NULL);
356     LogDebug("frames_o.length = " << count);
357
358     ObjectsListPtr retList = ObjectsListPtr(new ObjectsList());
359
360     for (size_t i = 0; i < count; ++i) {
361         std::stringstream ss;
362         ss << i;
363         JSValueRef frame = getPropertyObj(context,
364                                           frames_o,
365                                           ss.str().c_str());
366         if (JSValueIsUndefined(context, frame)) {
367             LogError("Selected frame is null: frame[" << i << "]");
368             continue;
369         }
370         JSObjectRef frame_obj = JSValueToObject(context, frame, NULL);
371         if (!frame_obj) {
372             LogError("frame_obj is NULL.");
373             continue;
374         }
375         retList->push_back(JSObjectPtr(new JSObject(frame_obj)));
376         ObjectsListPtr childList = getIframesList(context, frame_obj);
377
378         retList->merge(*childList);
379         LogDebug("Frame Value Pointer: " << frame);
380         LogDebug("Frame Object Pointer: " << frame_obj);
381     }
382
383     return retList;
384 }
385
386 JSObjectPtr JavaScriptInterface::getJSObjectProperty(JSGlobalContextRef ctx,
387                                          const JSObjectPtr& frame,
388                                          const std::string& name) const
389 {
390     JSObjectRef frame_js = static_cast<JSObjectRef>(frame->getObject());
391
392     JSValueRef property = getPropertyObj(ctx, frame_js, name.c_str());
393
394     JSObjectRef objProp = JSValueToObject(ctx, property, NULL);
395
396     return JSObjectPtr(new JSObject(objProp));
397 }
398
399 void JavaScriptInterface::removeIframes(JSGlobalContextRef context)
400 {
401     const char* deleteIframesScript =
402         "frame_set = document.getElementsByTagName('iframe');"
403         "len = frame_set.length;"
404         "for(i=0; i< len; i++)"
405         "   frame_set[0].parentNode.removeChild(frame_set[0]); ";
406
407     JSGlobalContextRef ctx = static_cast<JSGlobalContextRef>(context);
408
409     JSStringRef script_src = JSStringCreateWithUTF8CString(deleteIframesScript);
410
411     JSEvaluateScript(ctx, script_src, 0, 0, 0, 0);
412
413     JSStringRelease(script_src);
414 }
415
416 void JavaScriptInterface::invokeGarbageCollector(JSGlobalContextRef context)
417 {
418     LogDebug("Garbage collection #1");
419     JSGarbageCollect(context);
420     LogDebug("Garbage collection #2");
421     JSGarbageCollect(context);
422     LogDebug("Garbage collection #3");
423     JSGarbageCollect(context);
424     LogDebug("Garbage collection #4");
425     JSGarbageCollect(context);
426 }