Initialize Tizen 2.3
[framework/web/wrt-plugins-common.git] / src_mobile / 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/secure_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         _E("Object %s is undefined", #object); \
39         return ret; \
40     }
41
42 #define CHECK_JSOBJECT_IS_NULL_RETURN(object, ret) \
43     if (!object) { \
44         _E("Object %s is NULL", #object); \
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             _E("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 {
76     Assert(ctx && value);
77     std::string result;
78     JSStringRef str = JSValueToStringCopy(ctx, value, NULL);
79     result = toString(str);
80     JSStringRelease(str);
81     return result;
82 }
83
84 JSValueRef getProperty(JSContextRef ctx, JSValueRef value, const char* name)
85 {
86     Assert(ctx && value && name);
87     JSValueRef result = NULL;
88     JSObjectRef obj = JSValueToObject(ctx, value, NULL);
89     if (obj) {
90         JSStringRef str = JSStringCreateWithUTF8CString(name);
91         result = JSObjectGetProperty(ctx, obj, str, NULL);
92         JSStringRelease(str);
93     }
94     return result;
95 }
96
97 JSValueRef getPropertyObj(JSContextRef ctx, JSObjectRef obj, const char* name)
98 {
99     Assert(ctx && obj && name);
100     JSStringRef str = JSStringCreateWithUTF8CString(name);
101     JSValueRef result = JSObjectGetProperty(ctx, obj, str, NULL);
102     JSStringRelease(str);
103     return result;
104 }
105
106 JSObjectRef getJSGlobalObject(JSGlobalContextRef context)
107 {
108     return JSContextGetGlobalObject(context);
109 }
110 }
111
112 typedef JSValueRef (*JSCFunction)(
113     JSContextRef context,
114     JSObjectRef object,
115     JSObjectRef thisObject,
116     size_t argumentCount,
117     const JSValueRef arguments[],
118     JSValueRef* exception);
119
120 void JavaScriptInterface::setObjectProperty(JSGlobalContextRef context,
121                                             const JSObjectPtr& parentObject,
122                                             const std::string &propertyName,
123                                             const JSObjectPtr& propertyObject)
124 {
125     _D("JSObjectSetProperty(%p, \"%s\")", context, propertyName.c_str());
126
127     //create name
128     JSStringRef name = JSStringCreateWithUTF8CString(propertyName.c_str());
129     //set property
130     JSObjectSetProperty(
131         static_cast<JSGlobalContextRef>(context),
132         static_cast<JSObjectRef>(parentObject->getObject()), name,
133         static_cast<JSObjectRef>(propertyObject->getObject()),
134         kJSPropertyAttributeReadOnly, 0);
135
136     JSStringRelease(name);
137 }
138
139 void JavaScriptInterface::removeObjectProperty(JSGlobalContextRef context,
140                                                const JSObjectPtr& parentObject,
141                                                const std::string &propertyName)
142 {
143     if (!context) {
144         //nothing to do -> no context
145         return;
146     }
147     _D("JSObjectDeleteProperty(%p, \"%s\")", context, propertyName.c_str());
148
149     JSStringRef name = JSStringCreateWithUTF8CString(propertyName.c_str());
150     JSObjectDeleteProperty(
151         static_cast<JSGlobalContextRef>(context),
152         static_cast<JSObjectRef>(parentObject->getObject()), name, 0);
153
154     JSStringRelease(name);
155 }
156
157 JavaScriptInterface::PropertiesList JavaScriptInterface::
158     getObjectPropertiesList(
159     JSGlobalContextRef context,
160     const JSObjectPtr& object) const
161 {
162     PropertiesList result;
163     JSPropertyNameArrayRef properties = JSObjectCopyPropertyNames(
164             static_cast<JSGlobalContextRef>(context),
165             static_cast<JSObjectRef>(object->getObject()));
166     std::size_t count = JSPropertyNameArrayGetCount(properties);
167     result.reserve(count);
168     _D("propesties count %d", count);
169     for (std::size_t i = 0; i < count; ++i) {
170         JSStringRef property = JSPropertyNameArrayGetNameAtIndex(properties, i);
171         result.push_back(toString(property));
172     }
173     JSPropertyNameArrayRelease(properties);
174     return result;
175 }
176
177 JSObjectPtr JavaScriptInterface::makeJSFunctionObject(
178     JSGlobalContextRef context,
179     const std::string &name,
180     js_function_impl functionImplementation) const
181 {
182     _D("JSObjectMakeFunctionWithCallback(%p, \"%s\")", context, name.c_str());
183     JSStringRef jsFunName = JSStringCreateWithUTF8CString(name.c_str());
184
185     JSObjectRef object = JSObjectMakeFunctionWithCallback(
186             context,
187             jsFunName,
188             reinterpret_cast<JSObjectCallAsFunctionCallback>(
189                 functionImplementation));
190
191     JSStringRelease(jsFunName);
192     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
193 }
194
195 JSObjectPtr JavaScriptInterface::makeJSClassObject(
196     JSGlobalContextRef context,
197     JSObjectDeclaration::ConstClassTemplate classTemplate) const
198 {
199     _D("JSObjectMake(%p)", context);
200     JSObjectRef object = JSObjectMake(
201             context,
202             static_cast<JSClassRef>(
203                 const_cast<JSObjectDeclaration::ClassTemplate>(classTemplate)),
204             NULL);
205     return JSObjectPtr(new JSObject(object));
206 }
207
208 JSObjectPtr JavaScriptInterface::makeJSObjectBasedOnInterface(
209     JSGlobalContextRef context,
210     const std::string &interfaceName) const
211 {
212     _D("makeJSObjectBasedOnInterface(%p, \"%s\")", context, interfaceName.c_str());
213     JSObjectPtr jsInterfaceObj = getJSObjectProperty(context,
214                                                      getGlobalObject(
215                                                          context),
216                                                      interfaceName);
217     JSObjectRef object = JSObjectCallAsConstructor(context,
218                                                    static_cast<JSObjectRef>(
219                                                        jsInterfaceObj->
220                                                            getObject()), 0,
221                                                    NULL,
222                                                    NULL);
223     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
224 }
225
226 JSObjectPtr JavaScriptInterface::makeJSInterface(
227     JSGlobalContextRef context,
228     JSObjectDeclaration::ConstClassTemplate classTemplate,
229     JSObjectDeclaration::ConstructorCallback constructorCallback) const
230 {
231     _D("makeJSInterface(%p)", context);
232     JSObjectRef object = JSObjectMakeConstructor(context,
233                                                  static_cast<JSClassRef>(
234                                                      const_cast<
235                                                          JSObjectDeclaration::
236                                                              ClassTemplate>(
237                                                          classTemplate)),
238                                                  reinterpret_cast<
239                                                      JSObjectCallAsConstructorCallback>(
240                                                      constructorCallback)
241                                                  );
242     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
243 }
244
245 JSObjectPtr JavaScriptInterface::createObject(
246     JSGlobalContextRef context,
247     const JSObjectDeclarationPtr&
248     declaration)
249 {
250     typedef JSObjectDeclaration::Options JO;
251
252     if (declaration->getOptions()) {
253         switch (declaration->getOptions()->getType()) {
254         case JO::ClassType::Function:
255             return makeJSFunctionObject(
256                        context,
257                        declaration->getName(),
258                        declaration->getOptions()->getFunctionImpl());
259
260         case JO::ClassType::Class:
261             if (declaration->getInterfaceName().empty()) {
262                 return makeJSClassObject(
263                            context,
264                            declaration->getClassTemplate());
265             } else {
266                 return makeJSObjectBasedOnInterface(
267                            context,
268                            declaration->getInterfaceName());
269             }
270
271         case JO::ClassType::Interface:
272             return makeJSInterface(
273                        context,
274                        /* product class template */
275                        declaration->getClassTemplate(),
276                        declaration->getConstructorCallback());
277
278         default:
279             _E("Invalid class type. Making empty JS object.");
280             return JSObjectPtr(new JSObject(
281                                    JSObjectMake(context, NULL, NULL)));
282         }
283     } else {
284         _D("No declaration options");
285         return JSObjectPtr(new JSObject(
286                                JSObjectMake(
287                                    context,
288                                    static_cast<JSClassRef>(
289                                        const_cast<JSObjectDeclaration::
290                                                       ClassTemplate>(
291                                            declaration->getClassTemplate())),
292                                    NULL)));
293     }
294 }
295
296 JSObjectPtr JavaScriptInterface::getGlobalObject(JSGlobalContextRef context)
297 const
298 {
299     return JSObjectPtr(new JSObject(static_cast<JSObject::RealObject>(
300                                         JSContextGetGlobalObject(static_cast<
301                                                                      JSGlobalContextRef>(
302                                                                      context)))));
303 }
304
305 JSObjectPtr JavaScriptInterface::copyObjectToIframe(
306     JSGlobalContextRef context,
307     const JSObjectPtr& iframe,
308     const std::string& name)
309 {
310     _D("copyObjectToIframe(%s)", name.c_str());
311
312     JSGlobalContextRef jsGlobalContext =
313         static_cast<JSGlobalContextRef>(context);
314
315     JSObjectRef globalObject = JSContextGetGlobalObject(jsGlobalContext);
316
317     JSValueRef requestedObject = getPropertyObj(jsGlobalContext,
318                                                 globalObject,
319                                                 name.c_str());
320     CHECK_JSVALUE_IS_UNDEFINED_RETURN(jsGlobalContext,
321                                       requestedObject,
322                                       JSObjectPtr());
323
324     JSStringRef requestedObjectStr =
325         JSStringCreateWithUTF8CString(name.c_str());
326
327     JSObjectSetProperty(jsGlobalContext,
328                         static_cast<JSObjectRef>(iframe->getObject()),
329                         requestedObjectStr,
330                         requestedObject,
331                         kJSPropertyAttributeReadOnly,
332                         NULL);
333
334     JSStringRelease(requestedObjectStr);
335
336     return JSObjectPtr(
337                new JSObject(const_cast<OpaqueJSValue*>(requestedObject)));
338 }
339
340 JavaScriptInterface::ObjectsListPtr
341 JavaScriptInterface::getIframesList(JSGlobalContextRef ctx) const
342 {
343     JSGlobalContextRef context = static_cast<JSGlobalContextRef>(ctx);
344     JSObjectRef globalObject = JSContextGetGlobalObject(context);
345     ObjectsListPtr retList = getIframesList(context, globalObject);
346
347     return retList;
348 }
349
350 JavaScriptInterface::ObjectsListPtr
351 JavaScriptInterface::getIframesList(JSContextRef context,
352                                     JSObjectRef globalObject) const
353 {
354     JSValueRef frames = getPropertyObj(context, globalObject, "frames");
355     CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, frames, ObjectsListPtr());
356
357     JSObjectRef frames_o = JSValueToObject(context, frames, NULL);
358     CHECK_JSOBJECT_IS_NULL_RETURN(frames_o, ObjectsListPtr());
359
360     JSValueRef len = getPropertyObj(context, frames_o, "length");
361     CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, len, ObjectsListPtr());
362
363     size_t count = JSValueToNumber(context, len, NULL);
364     _D("frames_o.length = %%d", count);
365
366     ObjectsListPtr retList = ObjectsListPtr(new ObjectsList());
367
368     for (size_t i = 0; i < count; ++i) {
369         std::stringstream ss;
370         ss << i;
371         JSValueRef frame = getPropertyObj(context,
372                                           frames_o,
373                                           ss.str().c_str());
374         if (JSValueIsUndefined(context, frame)) {
375             _E("Selected frame is null: frame[%d]", i);
376             continue;
377         }
378         JSObjectRef frame_obj = JSValueToObject(context, frame, NULL);
379         if (!frame_obj) {
380             _E("frame_obj is NULL.");
381             continue;
382         }
383         retList->push_back(JSObjectPtr(new JSObject(frame_obj)));
384         ObjectsListPtr childList = getIframesList(context, frame_obj);
385
386         retList->merge(*childList);
387         _D("Frame Value Pointer: %p", frame);
388         _D("Frame Object Pointer: %p", frame_obj);
389     }
390
391     return retList;
392 }
393
394 JSObjectPtr JavaScriptInterface::getJSObjectProperty(JSGlobalContextRef ctx,
395                                                      const JSObjectPtr& frame,
396                                                      const std::string& name)
397 const
398 {
399     _D("makeJSObjectBasedOnInterface(%p, \"%s\")", ctx, name.c_str());
400     JSObjectRef frame_js = static_cast<JSObjectRef>(frame->getObject());
401     JSValueRef property = getPropertyObj(ctx, frame_js, name.c_str());
402     JSObjectRef objProp = JSValueToObject(ctx, property, NULL);
403
404     return JSObjectPtr(new JSObject(objProp));
405 }
406
407 void JavaScriptInterface::removeIframes(JSGlobalContextRef context)
408 {
409     const char* deleteIframesScript =
410         "frame_set = document.getElementsByTagName('iframe');"
411         "len = frame_set.length;"
412         "for(i=0; i< len; i++)"
413         "   frame_set[0].parentNode.removeChild(frame_set[0]); ";
414
415     JSGlobalContextRef ctx = static_cast<JSGlobalContextRef>(context);
416
417     JSStringRef script_src = JSStringCreateWithUTF8CString(deleteIframesScript);
418
419     JSEvaluateScript(ctx, script_src, 0, 0, 0, 0);
420
421     JSStringRelease(script_src);
422 }
423
424 void JavaScriptInterface::invokeGarbageCollector(JSGlobalContextRef context)
425 {
426     LogDebug("Garbage collection");
427     JSGarbageCollect(context);
428     JSGarbageCollect(context);
429     JSGarbageCollect(context);
430     JSGarbageCollect(context);
431 }