Initialize Tizen 2.3
[framework/web/wrt-plugins-common.git] / src_mobile / plugin-loading / js_page_session.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_logic.cpp
18  * @author      Piotr Fatyga (p.fatyga@samsung.com)
19  * @author      Grzegorz Krawczyk (g.krawczyk@samsung.com)
20  * @author      Przemyslaw Dobrowolski (p.dobrowolsk@samsung.com)
21  * @version     1.0
22  * @brief       This file is the implementation file of plugin and
23  *              feature loading routines
24  * @brief       This code is intended to work behind view controller
25  */
26
27 #include "plugin_logic.h"
28
29 #include <dpl/assert.h>
30 #include <dpl/scoped_array.h>
31 #include <dpl/log/secure_log.h>
32 #include <dpl/foreach.h>
33 #include <dpl/singleton_impl.h>
34 #include <dpl/wrt-dao-ro/widget_dao_read_only.h>
35 #include <dpl/wrt-dao-ro/common_dao_types.h>
36 #include <dpl/wrt-dao-ro/global_config.h>
37
38 #include <JavaScriptCore/JavaScript.h>
39
40 #include <string>
41 #include <sys/types.h>
42 #include <sys/stat.h>
43 #include <dirent.h>
44 #include <errno.h>
45 #include <fstream>
46 #include <map>
47 #include <list>
48 #include <vector>
49 #include <algorithm>
50 #include <cstring>
51
52 #include <wrt_plugin_export.h>
53 #include <js_overlay_types.h>
54
55 #include "explorer.h"
56 #include "plugin.h"
57 #include "plugin_model.h"
58 #include "javascript_interface.h"
59 #include "js_function_manager.h"
60 #include "plugin_container_support.h"
61
62 #include "js_page_session.h"
63
64 using namespace std;
65 using namespace WrtDB;
66 using namespace WrtPlugins::W3C;
67
68 namespace {
69 const char *LIBRARY_PATH_SEPARATOR = "/";
70 }
71
72 class JSPageSession::Impl
73 {
74   private:
75     typedef std::set<PluginPtr> LoadedPlugins;
76     typedef std::set<JSGlobalContextRef> LoadedContexts;
77
78   private:
79     ///< Widget handle using this session
80     int m_widgetHandle;
81
82     //view for this session
83     JSGlobalContextRef m_context;
84
85     bool m_sessionStarted;
86
87     ///< JS object explorer for this session
88     Explorer* m_objectExplorer;
89
90     PluginContainerSupportPtr m_pluginsSupport;
91
92     ///< All loaded plugins. Each one must be unloaded. Plugins means
93     //set of features connected with such plugin (library)
94     LoadedPlugins m_loadedPlugins;
95
96     // Set of currently loaded web pages' contexts. These contexts are
97     // exactly matching with each frames' global context.
98     LoadedContexts m_loadedContexts;
99
100   private:
101     PluginPtr loadLibrary(PluginModelPtr& pluginModel);
102
103     void installStandardFunctions();
104     void installRootPlugins();
105     void installRequestedFeatures();
106
107     //returns true if success or false if failed
108     bool installPlugin(PluginModelPtr plugin);
109     bool installPluginOnDemand(PluginModelPtr plugin,
110                                JavaScriptObject& parentObject,
111                                JSGlobalContextRef context);
112
113     void unloadPluginsFromSession();
114
115   public:
116     Impl(const PluginContainerSupportPtr& containerSupport);
117     ~Impl();
118
119     // Widget session
120     void startSession(int widgetHandle,
121                       JSGlobalContextRef view,
122                       double scaleFactor,
123                       const char* encodedBundle,
124                       const char* theme);
125
126     void stopSession();
127
128     void performLibrariesUnload();
129
130     bool loadPluginOnDemand(const WrtDB::DbPluginHandle &pluginHandle,
131                             JavaScriptObject& parentObject,
132                             JSGlobalContextRef context);
133
134     void loadFrame(JSGlobalContextRef context);
135     void unloadFrame(JSGlobalContextRef context);
136
137     void setCustomProperties(double scaleFactor,
138                              const char* encodedBundle,
139                              const char* theme);
140
141     void dispatchJavaScriptEvent(CustomEventType eventType, void* data);
142 };
143
144 JSPageSession::Impl::Impl(const PluginContainerSupportPtr& support) :
145         m_widgetHandle(0),
146         m_context(NULL),
147         m_sessionStarted(false),
148         m_objectExplorer(NULL)
149 {
150     _D("Initializing Page Session");
151     m_pluginsSupport = support;
152 }
153
154 JSPageSession::Impl::~Impl()
155 {
156     if (m_sessionStarted) {
157         LogError("Must stop widget session before exit!");
158         stopSession();
159     }
160
161     _D("Deinitializing Page Session");
162 }
163
164  void JSPageSession::Impl::installStandardFunctions()
165  {
166     _D("========== install standard Functions START ==========");
167
168     //add standard functions
169     FOREACH(it, JsFunctionManagerSingleton::Instance().getFunctions()) {
170         m_objectExplorer->registerObject(*it, NULL);
171     }
172
173     _D("========== install standard Functions END ==========");
174  }
175
176 void JSPageSession::Impl::installRootPlugins()
177 {
178     _D("========== install root Functions START ==========");
179
180     PluginContainerSupport::PluginsList rootPlugins =
181                     m_pluginsSupport->getRootPlugins();
182     FOREACH(it, rootPlugins)
183     {
184         installPlugin(*it);
185     }
186
187     _D("========== install root Functions END ==========");
188 }
189
190 bool JSPageSession::Impl::installPlugin(PluginModelPtr plugin)
191 {
192     Assert(plugin && "Plugin Model is NULL");
193     PluginPtr library = loadLibrary(plugin);
194
195     if (!library) {
196         _E("Loading library failed");
197         return false;
198     }
199
200     _D("Install Plugin : %s", library->GetFileName().c_str());
201
202     // Register new class
203     FOREACH(it, *(library->GetClassList()))
204     {
205         if (!m_objectExplorer->registerObject(*it, NULL)) {
206             _E("Object Registration failed : %s", (*it)->getName().c_str());
207         }
208     }
209     return true;
210 }
211
212 void JSPageSession::Impl::installRequestedFeatures()
213 {
214     _D("========== install requested Features START ==========");
215
216     std::list<std::string> allowedFeatures =
217         m_pluginsSupport->getAllowedFeatures(m_widgetHandle);
218
219     PluginContainerSupport::PluginsList allowedPlugins;
220
221     FOREACH(feature, allowedFeatures)
222     {
223         _D("Processing feature: %s", (*feature).c_str());
224
225         PluginModelPtr plugin = m_pluginsSupport->getPluginForFeature(*feature);
226
227         if (!plugin) {
228             _W("It didn't have plugins! : %s", (*feature).c_str());
229             continue;
230         }
231
232         ImplementedObjectsList implObjs =
233             PluginDAOReadOnly::
234                 getImplementedObjectsForPluginHandle(plugin->Handle.Get());
235
236         FOREACH(obj, implObjs)
237         {
238             _D("Processing object: %s", (*obj).c_str());
239             /* This can be optimalized, but would need extra data in database.
240              * There should be a list of features that are allowed to be
241              * installed at widget start */
242             if (obj->find(".") == obj->rfind(".")) {
243                 allowedPlugins.push_back(plugin);
244                 _D("Plugin will be added: %s", plugin->LibraryName.Get().c_str());
245                 break;
246             }
247         }
248     }
249
250     FOREACH(plugin, allowedPlugins)
251     {
252         _D("Installation plugin: %s", (*plugin)->LibraryName.Get().c_str());
253         installPlugin(*plugin);
254     }
255
256     _D("========== install requested Features END ==========");
257 }
258
259 bool JSPageSession::Impl::loadPluginOnDemand(
260     const WrtDB::DbPluginHandle &pluginHandle,
261     JavaScriptObject& parentObject,
262     JSGlobalContextRef context)
263 {
264     Assert(parentObject.instance &&
265            !parentObject.name.empty()
266            && "Wrong arguments");
267
268     if (!m_sessionStarted) {
269         _W("Session not started");
270         return false;
271     }
272     //    //TODO here may be a bug. if plugin contains feature rejected and
273     // accepted
274     //    LogDebug("Installing feature : " << widgetFeature.name);
275     //    if (widgetFeature.rejected) {
276     //        LogWarning("This api-feature was rejected");
277     //        return;
278     //    }
279     //
280     //    auto plugin = m_pluginsSupport->getPluginModelById(pluginHandle);
281     //    if (!plugin) {
282     //        LogError("Failed to load plugin. plugin handle: " <<
283     // pluginHandle);
284     //        return false;
285     //    }
286     m_pluginsSupport->registerPluginModel(pluginHandle);
287     return installPluginOnDemand(
288                m_pluginsSupport->getPluginModelById(pluginHandle),
289                parentObject,
290                context);
291 }
292
293 bool JSPageSession::Impl::installPluginOnDemand(PluginModelPtr plugin,
294                                                 JavaScriptObject& parentObject,
295                                                 JSGlobalContextRef context)
296 {
297     Assert(plugin && "Plugin Model is NULL");
298     PluginPtr library = loadLibrary(plugin);
299
300     if (!library) {
301         _E("Loading library failed");
302         return false;
303     }
304
305     _D("Install Plugin %s", library->GetFileName().c_str());
306
307     if (!(parentObject.instance)) {
308         _E("NULL pointer value");
309         return false;
310     }
311
312     JSObjectPtr parent(new JSObject(parentObject.instance));
313
314     if (!parent->getObject()) {
315         _E("NULL pointer value");
316         assert(false);
317         return false;
318     }
319
320     FOREACH(it, *(library->GetClassList()))
321     {
322         bool installationStatus =
323             m_objectExplorer->registerObject(*it,
324                                              parentObject.name,
325                                              parent,
326                                              context);
327
328         if (!installationStatus) {
329             _E("Object Registration failed : %s; Parent object name: %s",
330                (*it)->getName().c_str(),
331                parentObject.name.c_str());
332             return false;
333         }
334     }
335
336     _D("Plugin on demand registration completed");
337     return true;
338 }
339
340 void JSPageSession::Impl::setCustomProperties(double scaleFactor,
341                                               const char* encodedBundle,
342                                               const char* theme)
343 {
344     m_objectExplorer->getWindowPropertySupport()
345         ->setScaleToNavigatorProperty(scaleFactor);
346     m_objectExplorer->getWindowPropertySupport()
347         ->setBundleToWindowProperty(encodedBundle);
348     m_objectExplorer->getWindowPropertySupport()
349         ->setThemeToNavigatorProperty(theme);
350 }
351
352 void JSPageSession::Impl::dispatchJavaScriptEvent(CustomEventType eventType,
353                                                   void* data)
354 {
355     // Check if session is already started
356     if (!m_sessionStarted) {
357         _W("Session not started!");
358         return;
359     }
360     m_objectExplorer->callEventListeners(eventType, data);
361 }
362
363 void JSPageSession::Impl::startSession(int widgetHandle,
364                                        JSGlobalContextRef context,
365                                        double scaleFactor,
366                                        const char* encodedBundle,
367                                        const char* theme)
368 {
369
370     // Check if corresponding session if not already created
371     if (m_sessionStarted) {
372         _W("Session already started!");
373         return;
374     }
375
376     // Create js object explorer object
377     m_objectExplorer = new Explorer(context);
378
379     m_sessionStarted = true;
380     m_widgetHandle = widgetHandle;
381     m_loadedPlugins.clear();
382     m_context = context;
383
384     // Register standard features
385     installStandardFunctions();
386
387     WidgetDAOReadOnly dao(m_widgetHandle);
388     WidgetType appType = dao.getWidgetType();
389     if (appType == WrtDB::APP_TYPE_TIZENWEBAPP) {
390         installRootPlugins();
391     }
392     // Register special features
393     installRequestedFeatures();
394
395     // set scale, bundle as window's property
396     setCustomProperties(scaleFactor, encodedBundle, theme);
397 }
398
399 void JSPageSession::Impl::stopSession()
400 {
401     if (!m_sessionStarted) {
402         _W("Session not started!");
403         return;
404     }
405
406     unloadPluginsFromSession();
407     m_sessionStarted = false;
408 }
409
410 void JSPageSession::Impl::unloadPluginsFromSession()
411 {
412     m_objectExplorer->removePluginsFromIframes();
413     m_objectExplorer->cleanIframesData();
414
415     // delete js object for overlayed js functions
416     FOREACH(it, JsFunctionManagerSingleton::Instance().getFunctions())
417     {
418         m_objectExplorer->deregisterObject(*it);
419     }
420
421     // delete js object for plugins
422     FOREACH(pluginIt, m_loadedPlugins)
423     {
424         _D("Unregistering plugin %s", (*pluginIt)->GetFileName().c_str());
425         (*pluginIt)->OnWidgetStop(m_widgetHandle);
426
427         FOREACH(it, *((*pluginIt)->GetClassList())) {
428             m_objectExplorer->deregisterObject(*it);
429         }
430     }
431
432     JavaScriptInterfaceSingleton::Instance().invokeGarbageCollector(m_context);
433
434     m_loadedPlugins.clear();
435
436     delete m_objectExplorer;
437     m_objectExplorer = NULL;
438 }
439
440 void JSPageSession::Impl::performLibrariesUnload()
441 {
442 #if 0
443     LogDebug("Perform library unload");
444
445     size_t unloadedLibraries = 0;
446
447     FOREACH(pluginIt, m_loadedPlugins)
448     {
449         LogDebug("Preparing library: " << (*pluginIt)->LibraryName.Get());
450
451         PluginPtr plugin = (*pluginIt)->LibraryInstance.Get();
452         if (!plugin) {
453             LogWarning("Library not loaded " << (*pluginIt)->LibraryName.Get());
454             continue;
455         }
456         unloadedLibraries++;
457         (*pluginIt)->LibraryInstance.Set(PluginPtr());
458     }
459
460     LogDebug("unloaded " << unloadedLibraries << " unreferenced libraries!");
461 #endif
462 }
463
464 PluginPtr JSPageSession::Impl::loadLibrary(PluginModelPtr& pluginModel)
465 {
466     PluginPtr pluginLib = pluginModel->LibraryInstance.Get();
467     if (!pluginLib) {
468         std::string path = pluginModel->LibraryPath.Get() +
469             std::string(LIBRARY_PATH_SEPARATOR) +
470             pluginModel->LibraryName.Get();
471
472         pluginLib = Plugin::LoadFromFile(path);
473
474         if (!pluginLib) {
475             _E("Loading library failed");
476         } else {
477             pluginModel->LibraryInstance.Set(pluginLib);
478
479             _D("On widget start");
480             // This is first time for this plugin, start widget Session
481             pluginLib->OnWidgetStart(
482                 m_widgetHandle);
483             m_loadedPlugins.insert(pluginLib);
484
485             FOREACH(context, m_loadedContexts)
486             {
487                 pluginLib->OnFrameLoad(*context);
488             }
489         }
490     } else {
491         _D("Get from LibraryInstance");
492         _D("On widget start");
493         // This is first time for this plugin, start widget Session
494         pluginLib->OnWidgetStart(
495             m_widgetHandle);
496         m_loadedPlugins.insert(pluginLib);
497
498         FOREACH(context, m_loadedContexts)
499         {
500             pluginLib->OnFrameLoad(*context);
501         }
502     }
503
504     return pluginLib;
505 }
506
507 void JSPageSession::Impl::loadFrame(JSGlobalContextRef context)
508 {
509     if (!m_sessionStarted) {
510         _W("Session NOT started!");
511         return;
512     }
513
514     m_loadedContexts.insert(context);
515
516     FOREACH(pluginIt, m_loadedPlugins)
517     {
518         _D("Try to call 'OnFrameLoad' callback : %s",
519            (*pluginIt)->GetFileName().c_str());
520         (*pluginIt)->OnFrameLoad(context);
521     }
522
523     m_objectExplorer->loadFrame(context);
524 }
525
526 void JSPageSession::Impl::unloadFrame(JSGlobalContextRef context)
527 {
528     if (!m_sessionStarted) {
529         _W("Session NOT started!");
530         return;
531     }
532
533     m_loadedContexts.erase(context);
534
535     FOREACH(pluginIt, m_loadedPlugins)
536     {
537         _D("Try to call 'OnFrameUnload' callback : %s",
538            (*pluginIt)->GetFileName().c_str());
539         (*pluginIt)->OnFrameUnload(context);
540     }
541
542     m_objectExplorer->unloadFrame(context);
543 }
544
545 void JSPageSession::startSession(int widgetHandle,
546                                  JSGlobalContextRef ctx,
547                                  double scaleFactor,
548                                  const char* encodedBundle,
549                                  const char* theme)
550 {
551     m_impl->startSession(widgetHandle, ctx, scaleFactor, encodedBundle, theme);
552 }
553
554 void JSPageSession::stopSession()
555 {
556     m_impl->stopSession();
557 }
558
559 void JSPageSession::performLibrariesUnload()
560 {
561     m_impl->performLibrariesUnload();
562 }
563
564 bool JSPageSession::loadPluginOnDemand(
565     const WrtDB::DbPluginHandle &pluginHandle,
566     JavaScriptObject& parentObject,
567     JSGlobalContextRef context)
568 {
569     return m_impl->loadPluginOnDemand(pluginHandle, parentObject, context);
570 }
571
572 void JSPageSession::setCustomProperties(double scaleFactor,
573                                         const char* encodedBundle,
574                                         const char* theme)
575 {
576     m_impl->setCustomProperties(scaleFactor, encodedBundle, theme);
577 }
578
579 void JSPageSession::dispatchJavaScriptEvent(CustomEventType eventType,
580                                             void* data)
581 {
582     m_impl->dispatchJavaScriptEvent(eventType, data);
583 }
584
585 void JSPageSession::loadFrame(JSGlobalContextRef context)
586 {
587     m_impl->loadFrame(context);
588 }
589
590 void JSPageSession::unloadFrame(JSGlobalContextRef context)
591 {
592     m_impl->unloadFrame(context);
593 }
594
595 JSPageSession::JSPageSession(const PluginContainerSupportPtr& containerSupport)
596     :
597     m_impl(new JSPageSession::Impl(containerSupport))
598 {}
599
600 JSPageSession::~JSPageSession()
601 {}