Initialize Tizen 2.3
[framework/web/wrt-plugins-common.git] / src_wearable / 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     AssertMsg(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     AssertMsg(parentObject.instance && !parentObject.name.empty(),
265               "Wrong arguments");
266
267     if (!m_sessionStarted) {
268         _W("Session not started");
269         return false;
270     }
271     //    //TODO here may be a bug. if plugin contains feature rejected and
272     // accepted
273     //    LogDebug("Installing feature : " << widgetFeature.name);
274     //    if (widgetFeature.rejected) {
275     //        LogWarning("This api-feature was rejected");
276     //        return;
277     //    }
278     //
279     //    auto plugin = m_pluginsSupport->getPluginModelById(pluginHandle);
280     //    if (!plugin) {
281     //        LogError("Failed to load plugin. plugin handle: " <<
282     // pluginHandle);
283     //        return false;
284     //    }
285     m_pluginsSupport->registerPluginModel(pluginHandle);
286     return installPluginOnDemand(
287                m_pluginsSupport->getPluginModelById(pluginHandle),
288                parentObject,
289                context);
290 }
291
292 bool JSPageSession::Impl::installPluginOnDemand(PluginModelPtr plugin,
293                                                 JavaScriptObject& parentObject,
294                                                 JSGlobalContextRef context)
295 {
296     AssertMsg(plugin, "Plugin Model is NULL");
297     PluginPtr library = loadLibrary(plugin);
298
299     if (!library) {
300         _E("Loading library failed");
301         return false;
302     }
303
304     _D("Install Plugin %s", library->GetFileName().c_str());
305
306     if (!(parentObject.instance)) {
307         _E("NULL pointer value");
308         return false;
309     }
310
311     JSObjectPtr parent(new JSObject(parentObject.instance));
312
313     if (!parent->getObject()) {
314         _E("NULL pointer value");
315         assert(false);
316         return false;
317     }
318
319     FOREACH(it, *(library->GetClassList()))
320     {
321         bool installationStatus =
322             m_objectExplorer->registerObject(*it,
323                                              parentObject.name,
324                                              parent,
325                                              context);
326
327         if (!installationStatus) {
328             _E("Object Registration failed : %s; Parent object name: %s",
329                (*it)->getName().c_str(),
330                parentObject.name.c_str());
331             return false;
332         }
333     }
334
335     _D("Plugin on demand registration completed");
336     return true;
337 }
338
339 void JSPageSession::Impl::setCustomProperties(double scaleFactor,
340                                               const char* encodedBundle,
341                                               const char* theme)
342 {
343     m_objectExplorer->getWindowPropertySupport()
344         ->setScaleToNavigatorProperty(scaleFactor);
345     m_objectExplorer->getWindowPropertySupport()
346         ->setBundleToWindowProperty(encodedBundle);
347     m_objectExplorer->getWindowPropertySupport()
348         ->setThemeToNavigatorProperty(theme);
349 }
350
351 void JSPageSession::Impl::dispatchJavaScriptEvent(CustomEventType eventType,
352                                                   void* data)
353 {
354     // Check if session is already started
355     if (!m_sessionStarted) {
356         _W("Session not started!");
357         return;
358     }
359     m_objectExplorer->callEventListeners(eventType, data);
360 }
361
362 void JSPageSession::Impl::startSession(int widgetHandle,
363                                        JSGlobalContextRef context,
364                                        double scaleFactor,
365                                        const char* encodedBundle,
366                                        const char* theme)
367 {
368
369     // Check if corresponding session if not already created
370     if (m_sessionStarted) {
371         _W("Session already started!");
372         return;
373     }
374
375     // Create js object explorer object
376     m_objectExplorer = new Explorer(context);
377
378     m_sessionStarted = true;
379     m_widgetHandle = widgetHandle;
380     m_loadedPlugins.clear();
381     m_context = context;
382
383     // Register standard features
384     installStandardFunctions();
385
386     WidgetDAOReadOnly dao(m_widgetHandle);
387     WidgetType appType = dao.getWidgetType();
388     if (appType == WrtDB::APP_TYPE_TIZENWEBAPP) {
389         installRootPlugins();
390     }
391     // Register special features
392     installRequestedFeatures();
393
394     // set scale, bundle as window's property
395     setCustomProperties(scaleFactor, encodedBundle, theme);
396 }
397
398 void JSPageSession::Impl::stopSession()
399 {
400     if (!m_sessionStarted) {
401         _W("Session not started!");
402         return;
403     }
404
405     unloadPluginsFromSession();
406     m_sessionStarted = false;
407 }
408
409 void JSPageSession::Impl::unloadPluginsFromSession()
410 {
411     m_objectExplorer->removePluginsFromIframes();
412     m_objectExplorer->cleanIframesData();
413
414     // delete js object for overlayed js functions
415     FOREACH(it, JsFunctionManagerSingleton::Instance().getFunctions())
416     {
417         m_objectExplorer->deregisterObject(*it);
418     }
419
420     // delete js object for plugins
421     FOREACH(pluginIt, m_loadedPlugins)
422     {
423         _D("Unregistering plugin %s", (*pluginIt)->GetFileName().c_str());
424         (*pluginIt)->OnWidgetStop(m_widgetHandle);
425
426         FOREACH(it, *((*pluginIt)->GetClassList())) {
427             m_objectExplorer->deregisterObject(*it);
428         }
429     }
430
431     JavaScriptInterfaceSingleton::Instance().invokeGarbageCollector(m_context);
432
433     m_loadedPlugins.clear();
434
435     delete m_objectExplorer;
436     m_objectExplorer = NULL;
437 }
438
439 void JSPageSession::Impl::performLibrariesUnload()
440 {
441 #if 0
442     LogDebug("Perform library unload");
443
444     size_t unloadedLibraries = 0;
445
446     FOREACH(pluginIt, m_loadedPlugins)
447     {
448         LogDebug("Preparing library: " << (*pluginIt)->LibraryName.Get());
449
450         PluginPtr plugin = (*pluginIt)->LibraryInstance.Get();
451         if (!plugin) {
452             LogWarning("Library not loaded " << (*pluginIt)->LibraryName.Get());
453             continue;
454         }
455         unloadedLibraries++;
456         (*pluginIt)->LibraryInstance.Set(PluginPtr());
457     }
458
459     LogDebug("unloaded " << unloadedLibraries << " unreferenced libraries!");
460 #endif
461 }
462
463 PluginPtr JSPageSession::Impl::loadLibrary(PluginModelPtr& pluginModel)
464 {
465     PluginPtr pluginLib = pluginModel->LibraryInstance.Get();
466     if (!pluginLib) {
467         std::string path = pluginModel->LibraryPath.Get() +
468             std::string(LIBRARY_PATH_SEPARATOR) +
469             pluginModel->LibraryName.Get();
470
471         pluginLib = Plugin::LoadFromFile(path);
472
473         if (!pluginLib) {
474             _E("Loading library failed");
475         } else {
476             pluginModel->LibraryInstance.Set(pluginLib);
477
478             _D("On widget start");
479             // This is first time for this plugin, start widget Session
480             pluginLib->OnWidgetStart(
481                 m_widgetHandle);
482             m_loadedPlugins.insert(pluginLib);
483
484             FOREACH(context, m_loadedContexts)
485             {
486                 pluginLib->OnFrameLoad(*context);
487             }
488         }
489     } else {
490         _D("Get from LibraryInstance");
491         _D("On widget start");
492         // This is first time for this plugin, start widget Session
493         pluginLib->OnWidgetStart(
494             m_widgetHandle);
495         m_loadedPlugins.insert(pluginLib);
496
497         FOREACH(context, m_loadedContexts)
498         {
499             pluginLib->OnFrameLoad(*context);
500         }
501     }
502
503     return pluginLib;
504 }
505
506 void JSPageSession::Impl::loadFrame(JSGlobalContextRef context)
507 {
508     if (!m_sessionStarted) {
509         _W("Session NOT started!");
510         return;
511     }
512
513     m_loadedContexts.insert(context);
514
515     FOREACH(pluginIt, m_loadedPlugins)
516     {
517         _D("Try to call 'OnFrameLoad' callback : %s",
518            (*pluginIt)->GetFileName().c_str());
519         (*pluginIt)->OnFrameLoad(context);
520     }
521
522     m_objectExplorer->loadFrame(context);
523 }
524
525 void JSPageSession::Impl::unloadFrame(JSGlobalContextRef context)
526 {
527     if (!m_sessionStarted) {
528         _W("Session NOT started!");
529         return;
530     }
531
532     m_loadedContexts.erase(context);
533
534     FOREACH(pluginIt, m_loadedPlugins)
535     {
536         _D("Try to call 'OnFrameUnload' callback : %s",
537            (*pluginIt)->GetFileName().c_str());
538         (*pluginIt)->OnFrameUnload(context);
539     }
540
541     m_objectExplorer->unloadFrame(context);
542 }
543
544 void JSPageSession::startSession(int widgetHandle,
545                                  JSGlobalContextRef ctx,
546                                  double scaleFactor,
547                                  const char* encodedBundle,
548                                  const char* theme)
549 {
550     m_impl->startSession(widgetHandle, ctx, scaleFactor, encodedBundle, theme);
551 }
552
553 void JSPageSession::stopSession()
554 {
555     m_impl->stopSession();
556 }
557
558 void JSPageSession::performLibrariesUnload()
559 {
560     m_impl->performLibrariesUnload();
561 }
562
563 bool JSPageSession::loadPluginOnDemand(
564     const WrtDB::DbPluginHandle &pluginHandle,
565     JavaScriptObject& parentObject,
566     JSGlobalContextRef context)
567 {
568     return m_impl->loadPluginOnDemand(pluginHandle, parentObject, context);
569 }
570
571 void JSPageSession::setCustomProperties(double scaleFactor,
572                                         const char* encodedBundle,
573                                         const char* theme)
574 {
575     m_impl->setCustomProperties(scaleFactor, encodedBundle, theme);
576 }
577
578 void JSPageSession::dispatchJavaScriptEvent(CustomEventType eventType,
579                                             void* data)
580 {
581     m_impl->dispatchJavaScriptEvent(eventType, data);
582 }
583
584 void JSPageSession::loadFrame(JSGlobalContextRef context)
585 {
586     m_impl->loadFrame(context);
587 }
588
589 void JSPageSession::unloadFrame(JSGlobalContextRef context)
590 {
591     m_impl->unloadFrame(context);
592 }
593
594 JSPageSession::JSPageSession(const PluginContainerSupportPtr& containerSupport)
595     :
596     m_impl(new JSPageSession::Impl(containerSupport))
597 {}
598
599 JSPageSession::~JSPageSession()
600 {}