Revert "Performance improvement of PluginManager with clean up some plugin code."
[platform/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 {
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     LogInfo("Webkit:setting property --" << propertyName << "--");
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     LogDebug("Deleting property --" << propertyName << "--");
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     LogDebug("propesties count " << 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     LogDebug("Create JS function");
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     LogDebug("Create JS object");
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     LogDebug("Create JS object base on interface: " << interfaceName);
213     LogDebug("Context: " << context);
214
215     JSObjectPtr jsInterfaceObj = getJSObjectProperty(context,
216                                                      getGlobalObject(
217                                                          context),
218                                                      interfaceName);
219     JSObjectRef object = JSObjectCallAsConstructor(context,
220                                                    static_cast<JSObjectRef>(
221                                                        jsInterfaceObj->
222                                                            getObject()), 0,
223                                                    NULL,
224                                                    NULL);
225     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
226 }
227
228 JSObjectPtr JavaScriptInterface::makeJSInterface(
229     JSGlobalContextRef context,
230     JSObjectDeclaration::ConstClassTemplate classTemplate,
231     JSObjectDeclaration::ConstructorCallback constructorCallback) const
232 {
233     LogDebug("Create JS interface. Context: " << context);
234     JSObjectRef object = JSObjectMakeConstructor(context,
235                                                  static_cast<JSClassRef>(
236                                                      const_cast<
237                                                          JSObjectDeclaration::
238                                                              ClassTemplate>(
239                                                          classTemplate)),
240                                                  reinterpret_cast<
241                                                      JSObjectCallAsConstructorCallback>(
242                                                      constructorCallback)
243                                                  );
244     return JSObjectPtr(new JSObject(static_cast<void*>(object)));
245 }
246
247 JSObjectPtr JavaScriptInterface::createObject(
248     JSGlobalContextRef context,
249     const JSObjectDeclarationPtr&
250     declaration)
251 {
252     typedef JSObjectDeclaration::Options JO;
253
254     if (declaration->getOptions()) {
255         switch (declaration->getOptions()->getType()) {
256         case JO::ClassType::Function:
257             return makeJSFunctionObject(
258                        context,
259                        declaration->getName(),
260                        declaration->getOptions()->getFunctionImpl());
261
262         case JO::ClassType::Class:
263             if (declaration->getInterfaceName().empty()) {
264                 return makeJSClassObject(
265                            context,
266                            declaration->getClassTemplate());
267             } else {
268                 return makeJSObjectBasedOnInterface(
269                            context,
270                            declaration->getInterfaceName());
271             }
272
273         case JO::ClassType::Interface:
274             return makeJSInterface(
275                        context,
276                        /* product class template */
277                        declaration->getClassTemplate(),
278                        declaration->getConstructorCallback());
279
280         default:
281             LogError("Invalid class type. Making empty JS object.");
282             return JSObjectPtr(new JSObject(
283                                    JSObjectMake(context, NULL, NULL)));
284         }
285     } else {
286         LogDebug("No declaration options");
287         return JSObjectPtr(new JSObject(
288                                JSObjectMake(
289                                    context,
290                                    static_cast<JSClassRef>(
291                                        const_cast<JSObjectDeclaration::
292                                                       ClassTemplate>(
293                                            declaration->getClassTemplate())),
294                                    NULL)));
295     }
296 }
297
298 JSObjectPtr JavaScriptInterface::getGlobalObject(JSGlobalContextRef context)
299 const
300 {
301     return JSObjectPtr(new JSObject(static_cast<JSObject::RealObject>(
302                                         JSContextGetGlobalObject(static_cast<
303                                                                      JSGlobalContextRef>(
304                                                                      context)))));
305 }
306
307 JSObjectPtr JavaScriptInterface::copyObjectToIframe(
308     JSGlobalContextRef context,
309     const JSObjectPtr& iframe,
310     const std::string& name)
311 {
312     LogError("Copy object to iframe: " << name);
313
314     JSGlobalContextRef jsGlobalContext =
315         static_cast<JSGlobalContextRef>(context);
316
317     JSObjectRef globalObject = JSContextGetGlobalObject(jsGlobalContext);
318
319     JSValueRef requestedObject = getPropertyObj(jsGlobalContext,
320                                                 globalObject,
321                                                 name.c_str());
322     CHECK_JSVALUE_IS_UNDEFINED_RETURN(jsGlobalContext,
323                                       requestedObject,
324                                       JSObjectPtr());
325
326     JSStringRef requestedObjectStr =
327         JSStringCreateWithUTF8CString(name.c_str());
328
329     JSObjectSetProperty(jsGlobalContext,
330                         static_cast<JSObjectRef>(iframe->getObject()),
331                         requestedObjectStr,
332                         requestedObject,
333                         kJSPropertyAttributeReadOnly,
334                         NULL);
335
336     JSStringRelease(requestedObjectStr);
337
338     return JSObjectPtr(
339                new JSObject(const_cast<OpaqueJSValue*>(requestedObject)));
340 }
341
342 JavaScriptInterface::ObjectsListPtr
343 JavaScriptInterface::getIframesList(JSGlobalContextRef ctx) const
344 {
345     JSGlobalContextRef context = static_cast<JSGlobalContextRef>(ctx);
346
347     LogDebug("get global object");
348     JSObjectRef globalObject = JSContextGetGlobalObject(context);
349
350     ObjectsListPtr retList = getIframesList(context, globalObject);
351
352     return retList;
353 }
354
355 JavaScriptInterface::ObjectsListPtr
356 JavaScriptInterface::getIframesList(JSContextRef context,
357                                     JSObjectRef globalObject) const
358 {
359     JSValueRef frames = getPropertyObj(context, globalObject, "frames");
360     CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, frames, ObjectsListPtr());
361
362     JSObjectRef frames_o = JSValueToObject(context, frames, NULL);
363     CHECK_JSOBJECT_IS_NULL_RETURN(frames_o, ObjectsListPtr());
364
365     JSValueRef len = getPropertyObj(context, frames_o, "length");
366     CHECK_JSVALUE_IS_UNDEFINED_RETURN(context, len, ObjectsListPtr());
367
368     size_t count = JSValueToNumber(context, len, NULL);
369     LogDebug("frames_o.length = " << count);
370
371     ObjectsListPtr retList = ObjectsListPtr(new ObjectsList());
372
373     for (size_t i = 0; i < count; ++i) {
374         std::stringstream ss;
375         ss << i;
376         JSValueRef frame = getPropertyObj(context,
377                                           frames_o,
378                                           ss.str().c_str());
379         if (JSValueIsUndefined(context, frame)) {
380             LogError("Selected frame is null: frame[" << i << "]");
381             continue;
382         }
383         JSObjectRef frame_obj = JSValueToObject(context, frame, NULL);
384         if (!frame_obj) {
385             LogError("frame_obj is NULL.");
386             continue;
387         }
388         retList->push_back(JSObjectPtr(new JSObject(frame_obj)));
389         ObjectsListPtr childList = getIframesList(context, frame_obj);
390
391         retList->merge(*childList);
392         LogDebug("Frame Value Pointer: " << frame);
393         LogDebug("Frame Object Pointer: " << frame_obj);
394     }
395
396     return retList;
397 }
398
399 JSObjectPtr JavaScriptInterface::getJSObjectProperty(JSGlobalContextRef ctx,
400                                                      const JSObjectPtr& frame,
401                                                      const std::string& name)
402 const
403 {
404     JSObjectRef frame_js = static_cast<JSObjectRef>(frame->getObject());
405
406     JSValueRef property = getPropertyObj(ctx, frame_js, name.c_str());
407
408     JSObjectRef objProp = JSValueToObject(ctx, property, NULL);
409
410     return JSObjectPtr(new JSObject(objProp));
411 }
412
413 void JavaScriptInterface::removeIframes(JSGlobalContextRef context)
414 {
415     const char* deleteIframesScript =
416         "frame_set = document.getElementsByTagName('iframe');"
417         "len = frame_set.length;"
418         "for(i=0; i< len; i++)"
419         "   frame_set[0].parentNode.removeChild(frame_set[0]); ";
420
421     JSGlobalContextRef ctx = static_cast<JSGlobalContextRef>(context);
422
423     JSStringRef script_src = JSStringCreateWithUTF8CString(deleteIframesScript);
424
425     JSEvaluateScript(ctx, script_src, 0, 0, 0, 0);
426
427     JSStringRelease(script_src);
428 }
429
430 void JavaScriptInterface::invokeGarbageCollector(JSGlobalContextRef context)
431 {
432     LogDebug("Garbage collection #1");
433     JSGarbageCollect(context);
434     LogDebug("Garbage collection #2");
435     JSGarbageCollect(context);
436     LogDebug("Garbage collection #3");
437     JSGarbageCollect(context);
438     LogDebug("Garbage collection #4");
439     JSGarbageCollect(context);
440 }