[Release] livebox.web-provider-1.41
[platform/framework/web/web-provider.git] / src / Plugin / AppBoxPlugin / AppBoxRenderView.cpp
1 /*
2  * Copyright (c) 2013 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Flora License, Version 1.1 (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://floralicense.org/license/
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    AppBoxRenderView.cpp
18  * @author  Yunchan Cho (yunchan.cho@samsung.com)
19  */
20 #include <string>
21 #include <fstream>
22 #include <streambuf>
23 #include <Eina.h>
24 #include <Evas.h>
25 #include <Ecore.h>
26 #include <ewk_view.h>
27 #include <ewk_context.h>
28 #include <ewk_settings.h>
29 #include <livebox-service.h>
30 #include <i_runnable_widget_object.h>
31 #include <core_module.h>
32 #include <dpl/fast_delegate.h>
33 #include <Core/BoxSchemeHandler.h>
34 #include <Core/View/IRenderView.h>
35 #include <Core/View/IPdHelper.h>
36 #include <Core/View/PdHelper.h>
37 #include <API/web_provider_livebox_info.h>
38 #include <Core/Util/Log.h>
39 #include "AppBoxObserver.h"
40 #include "AppBoxRenderBuffer.h"
41 #include "AppBoxPdHelper.h"
42 #include "AppBoxRenderView.h"
43
44 #define RENDER_MAX_TIME 30.0
45
46 // injection javascript file regarding creating js object used by box and pd
47 static const std::string injectionFile("/usr/share/web-provider/injection.js");
48
49 AppBoxRenderView::AppBoxRenderView(
50         std::string boxId, std::string instanceId,
51         EwkContextPtr ewkContext)
52     : m_appId()
53     , m_boxId(boxId)
54     , m_instanceId(instanceId)
55     , m_ewkContext(ewkContext)
56     , m_boxRenderInfo()
57     , m_boxWrt()
58     , m_pdWrt()
59     , m_snapshot()
60     , m_fireRenderTimer()
61     , m_pdHelper()
62     , m_pdFastOpen(false)
63     , m_boxRenderBuffer()
64     , m_boxWrt_isSuspended(false)
65 {
66     LogD("enter");
67     m_appId = getAppId(m_boxId);
68     if (m_appId.empty()) {
69         throw; //exception throw!
70     }
71
72     m_boxRenderBuffer = AppBoxObserver::Instance()->getRenderBuffer(m_instanceId);
73     m_pdFastOpen = web_provider_livebox_get_pd_fast_open(m_boxId.c_str()) ? true : false;
74     AppBoxObserver::Instance()->registerRenderView(m_instanceId, this);
75 }
76
77 AppBoxRenderView::~AppBoxRenderView()
78 {
79     LogD("enter");
80     AppBoxObserver::Instance()->unregisterRenderView(m_instanceId);
81 }
82
83 void AppBoxRenderView::showBox(RenderInfoPtr boxRenderInfo)
84 {
85     LogD("enter");
86
87     // stop updating render buffer
88     m_boxRenderBuffer->stopCanvasUpdate();
89     clearSnapShot();
90
91     // delete already running timer
92     deleteRenderTimer();
93
94     // stop touch timer
95     if (web_provider_livebox_get_mouse_event(m_boxId.c_str())) {
96         m_boxRenderBuffer->deleteTouchTimer();
97     }
98
99     // copy to url
100     std::string boxStartUrl = getStartUrl(URL_TYPE_BOX, boxRenderInfo->defaultUrlParams);
101     if (m_boxWrt) {
102         LogD("existing wrt core is removed");
103         destroyBoxWrtCore();
104     }
105
106     m_boxWrt = createWrtCore(boxStartUrl, boxRenderInfo->window, m_ewkContext);
107     m_boxWrt_isSuspended = false;
108
109     // in case of showing box by request of pd open
110     if (m_pdHelper) {
111         m_pdHelper->setBoxWebView(m_boxWrt->GetCurrentWebview());
112     }
113
114     // resize webview fitted to width, height of Box
115     evas_object_resize(
116             m_boxWrt->GetCurrentWebview(), 
117             boxRenderInfo->width,
118             boxRenderInfo->height);
119
120
121     evas_object_show(boxRenderInfo->window);
122     m_boxWrt->Show();
123     m_boxRenderInfo = boxRenderInfo;
124 }
125
126 AppBoxRenderView::WrtCorePtr AppBoxRenderView::createWrtCore(
127         std::string& startUrl, Evas_Object* win, EwkContextPtr ewkContext)
128 {
129     LogD("enter");
130     
131     WrtCorePtr wrt;
132 #ifdef MULTIPROCESS_SERVICE_SUPPORT
133     wrt = WRT::CoreModuleSingleton::
134                 Instance().getRunnableWidgetObject(m_appId, DPL::Optional<unsigned>());
135 #else
136     wrt = WRT::CoreModuleSingleton::
137                 Instance().getRunnableWidgetObject(m_appId);
138 #endif
139     // prepare webview
140     if (startUrl.empty()) {
141         LogD("no start url");
142         return WrtCorePtr();
143     }
144     wrt->PrepareView(startUrl, win, ewkContext.get());
145     wrt->CheckBeforeLaunch();
146
147     // set callback functions of RunnableWidgetObject
148     WRT::UserDelegatesPtr cbs(new WRT::UserDelegates);
149     cbs->loadStart = DPL::MakeDelegate(this, &AppBoxRenderView::startLoadCallback);
150     cbs->loadFinish = DPL::MakeDelegate(this, &AppBoxRenderView::finishLoadCallback);
151     cbs->bufferSet = DPL::MakeDelegate(this, &AppBoxRenderView::setBufferCallback);
152     cbs->bufferUnset = DPL::MakeDelegate(this, &AppBoxRenderView::unsetBufferCallback);
153     if (!m_pdFastOpen) {
154         cbs->windowCreateBefore =
155             DPL::MakeDelegate(this, &AppBoxRenderView::createWindowBeforeCallback);
156         cbs->windowCreateAfter =
157             DPL::MakeDelegate(this, &AppBoxRenderView::createWindowAfterCallback);
158     }
159
160     cbs->navigationDecide =
161         DPL::MakeDelegate(this, &AppBoxRenderView::decideNavigationCallback);
162     cbs->webCrash = DPL::MakeDelegate(this, &AppBoxRenderView::crashWebProcessCallback);
163     wrt->SetUserDelegates(cbs);
164
165     // set basic webview setting
166     setWebViewBasicSetting(wrt->GetCurrentWebview());
167     return wrt;
168 }
169
170 void AppBoxRenderView::destroyBoxWrtCore()
171 {
172     LogD("enter");
173
174     m_boxRenderBuffer->stopCanvasUpdate();
175     deleteRenderTimer();
176     destroyWrtCore(m_boxWrt);
177     m_boxWrt.reset();
178
179     // temp
180     m_boxWrt_isSuspended = false;
181 }
182
183 void AppBoxRenderView::destroyPdWrtCore()
184 {
185     LogD("enter");
186
187     destroyWrtCore(m_pdWrt);
188     m_pdWrt.reset();
189 }
190
191 void AppBoxRenderView::destroyWrtCore(WrtCorePtr wrt)
192 {
193     LogD("enter");
194
195     if (wrt) {
196         wrt->Hide();
197     }
198 }
199
200 void AppBoxRenderView::hideBox()
201 {
202     LogD("enter");
203     destroyBoxWrtCore();
204     if (m_boxRenderInfo->window) {
205         evas_object_hide(m_boxRenderInfo->window);
206     }
207 }
208
209 void AppBoxRenderView::pauseBox()
210 {
211     LogD("enter");
212 }
213
214 void AppBoxRenderView::resumeBox()
215 {
216     LogD("enter");
217 }
218
219 void AppBoxRenderView::showPd(RenderInfoPtr pdRenderInfo, RenderInfoPtr boxRenderInfo)
220 {
221     LogD("enter");
222
223     // create pd helper
224     std::string pdStartUrl = getStartUrl(URL_TYPE_PD, pdRenderInfo->defaultUrlParams);
225     if (m_pdFastOpen) {
226         destroyPdWrtCore();
227         // if needed, last param regarding ewk context can be set to new one.
228         m_pdWrt = createWrtCore(pdStartUrl, pdRenderInfo->window, m_ewkContext);
229         if (!m_pdWrt) {
230             LogD("no wrt core instance");
231             return;
232         }
233         m_pdHelper = AppBoxPdHelper::create(pdRenderInfo->window);
234
235         // resize webview fitted to width, height of pd
236         evas_object_resize(
237                 m_pdWrt->GetCurrentWebview(), 
238                 pdRenderInfo->width,
239                 pdRenderInfo->height);
240         // show pd
241         m_pdWrt->Show();
242         m_pdHelper->finishOpen(m_pdWrt->GetCurrentWebview());
243     } else {
244         m_pdHelper = PdHelper::create(pdRenderInfo, pdStartUrl);
245     }
246
247     // show pd window
248     evas_object_show(pdRenderInfo->window);
249     showBox(boxRenderInfo);
250 }
251
252 void AppBoxRenderView::hidePd()
253 {
254     LogD("enter");
255
256     if (m_pdFastOpen) {
257         destroyPdWrtCore();
258     }
259     m_pdHelper->close();
260     m_pdHelper.reset();
261
262     // stop box webview
263     stopRenderBox();
264 }
265
266 Evas_Object* AppBoxRenderView::getBoxWebView()
267 {
268     if (!m_pdHelper) {
269         return m_boxWrt->GetCurrentWebview();
270     } else {
271         // Here, we can't use GetCurrentWebView() of wrt-core to get Box' webview,
272         // because in the non fast-open, GetCurrentWebview() returns PD's webview.
273         return m_pdHelper->getBoxWebView();
274     }
275 }
276
277 Evas_Object* AppBoxRenderView::getPdWebView()
278 {
279     if (!m_pdHelper) {
280         return NULL;
281     }
282
283     return m_pdHelper->getPdWebView();
284 }
285
286 std::string AppBoxRenderView::getAppId(std::string& boxId)
287 {
288     LogD("enter");
289
290     const char* appId = web_provider_livebox_get_app_id(boxId.c_str());
291     if (!appId) {
292         LogD("no appid of %s", boxId.c_str());
293         return std::string();
294     }
295
296     return std::string(appId);
297 }
298
299 std::string AppBoxRenderView::getStartUrl(UrlType type, std::string& defaultParams)
300 {
301     std::string url;
302     switch (type) {
303     case URL_TYPE_BOX:
304         url = livebox_service_lb_script_path(m_boxId.c_str());
305         break;
306     case URL_TYPE_PD:
307         url = livebox_service_pd_script_path(m_boxId.c_str());
308         break;
309     default:
310         LogD("no available type");
311     }
312
313     // add default parameters to start url
314     url += defaultParams;
315
316     return url;
317 }
318
319 Evas_Object* AppBoxRenderView::getCurrentSnapShot() 
320 {
321     LogD("enter");
322     clearSnapShot();
323     m_snapshot = m_boxRenderBuffer->getSnapshot();
324
325     return m_snapshot;
326 }
327
328 void AppBoxRenderView::clearSnapShot() 
329 {
330     LogD("enter");
331     if (m_snapshot) {
332         evas_object_del(m_snapshot);
333         m_snapshot = NULL;
334     }
335 }
336
337 void AppBoxRenderView::addRenderTimer()
338 {
339     LogD("enter");
340     if (m_fireRenderTimer) {
341         deleteRenderTimer();
342     }
343
344     m_fireRenderTimer = ecore_timer_add(
345                             RENDER_MAX_TIME, 
346                             fireRenderTimerCallback,
347                             this);
348 }
349
350 void AppBoxRenderView::deleteRenderTimer()
351 {
352     LogD("enter");
353     if (m_fireRenderTimer) {
354         ecore_timer_del(m_fireRenderTimer);
355         m_fireRenderTimer = NULL;
356     }
357 }
358
359 void AppBoxRenderView::stopRenderBox()
360 {
361     deleteRenderTimer();
362     m_boxRenderBuffer->stopCanvasUpdate();
363     if (web_provider_livebox_get_mouse_event(m_boxId.c_str())) {
364         // stop touch timer
365         m_boxRenderBuffer->deleteTouchTimer();
366
367         // temp condition
368         if (m_boxWrt_isSuspended == false)
369         {
370             m_boxWrt_isSuspended = true;
371             m_boxWrt->Suspend();
372         }
373     } else {
374         // Before webview should be removed,
375         // new evas object with last render data should be created
376         // otherwise, after webview is removed, box is white screen.
377         evas_object_show(getCurrentSnapShot());
378         destroyBoxWrtCore();
379     }
380 }
381
382 void AppBoxRenderView::setWebViewBasicSetting(Evas_Object* webview)
383 {
384     LogD("enter");
385
386     if (!webview) {
387         return;
388     }
389     Ewk_Settings* setting = ewk_view_settings_get(webview);
390     // To support transparent background
391     evas_object_color_set(webview, 0, 0, 0, 1);
392     ewk_view_visibility_set(webview, EINA_TRUE);
393
394     // To know starting point for updating buffer
395     evas_object_smart_callback_add(
396             webview,
397             "load,nonemptylayout,finished",
398             loadNonEmptyLayoutFinishedCallback,
399             this);
400     evas_object_smart_callback_add(
401             webview,
402             "frame,rendered",
403             frameRenderedCallback,
404             this);
405 }
406
407 Eina_Bool AppBoxRenderView::fireRenderTimerCallback(void* data)
408 {
409     LogD("enter");
410
411     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
412     This->m_fireRenderTimer = NULL;
413     This->stopRenderBox();
414
415     return ECORE_CALLBACK_CANCEL;
416 }
417
418 Eina_Bool AppBoxRenderView::openPdIdlerCallback(void* data)
419 {
420     LogD("enter");
421     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
422     if (This && This->m_pdHelper) {
423         This->m_pdHelper->startOpen();
424     }
425     return ECORE_CALLBACK_CANCEL;
426 }
427
428 void AppBoxRenderView::executeScriptCallback(
429         Evas_Object* webview, const char* result, void* data)
430 {
431     LogD("enter");
432     std::string resultStr(result ? result : "null");
433     LogD("result: %s", resultStr.c_str());
434 }
435
436 void AppBoxRenderView::startLoadCallback(Evas_Object* webview)
437 {
438     LogD("enter");
439     // execute injection for creating js objects
440     std::ifstream jsFile(injectionFile);
441     std::string script((std::istreambuf_iterator<char>(jsFile)),
442                         std::istreambuf_iterator<char>());
443
444     LogD("injected js code: %s", script.c_str());
445     ewk_view_script_execute(webview, script.c_str(), executeScriptCallback, this);
446 }
447
448 void AppBoxRenderView::finishLoadCallback(Evas_Object* webview)
449 {
450     LogD("enter");
451     ewk_view_visibility_set(webview, EINA_TRUE);
452
453     if (!m_pdHelper) {
454         // start render timer
455         addRenderTimer();
456     } else {
457         if (!m_pdFastOpen) {
458             if (!(m_pdHelper->isPdOpened()) && 
459                     webview == m_pdHelper->getBoxWebView())
460             {
461                 // open pd
462                 ecore_idler_add(openPdIdlerCallback, this);
463             }
464         }
465     }
466 }
467
468 void AppBoxRenderView::createWindowBeforeCallback(Evas** canvas, Evas_Object* parent)
469 {
470     LogD("enter");
471
472     if (m_pdHelper) {
473         if (!(m_pdHelper->isPdOpened()) && 
474                 parent == m_pdHelper->getBoxWebView())
475         {
476             LogD("pd canvas is used");
477             *canvas = m_pdHelper->getPdCanvas();
478             return;
479         }
480     }
481     
482     LogD("canvas of this webview is used");
483     *canvas = evas_object_evas_get(parent);
484 }
485
486 void AppBoxRenderView::createWindowAfterCallback(Evas_Object* parent, Evas_Object* child)
487 {
488     LogD("enter");
489
490     if (m_pdHelper) {
491         Evas* parentCanvas = evas_object_evas_get(parent);
492         Evas* childCanvas = evas_object_evas_get(child);
493
494         if (parentCanvas != childCanvas) {
495            // wrt-core change visibility value to false internally
496            // So plugin should reset this value to true for painting parent webview
497            ewk_view_visibility_set(parent, EINA_TRUE);
498            evas_object_show(parent);
499            m_pdHelper->finishOpen(child); 
500         }
501     }
502
503     setWebViewBasicSetting(child);
504     evas_object_show(child);
505 }
506
507 void AppBoxRenderView::setBufferCallback(Evas_Object* webview)
508 {
509     LogD("enter");
510     evas_object_show(webview);
511     evas_object_focus_set(webview, EINA_TRUE);
512 }
513
514 void AppBoxRenderView::unsetBufferCallback(Evas_Object* webview)
515 {
516     LogD("enter");
517     evas_object_hide(webview);
518 }
519
520 void AppBoxRenderView::decideNavigationCallback(Evas_Object* webview, std::string& uri)
521 {
522     LogD("enter");
523
524     // navigation of box scheme should be ignored
525     if(BoxSchemeHandler::Instance()->isBoxScheme(uri)) {
526         LogD("box scheme");
527         BoxSchemeHandler::Instance()->process(m_instanceId, uri);
528     }
529 }
530
531 void AppBoxRenderView::crashWebProcessCallback()
532 {
533     LogD("enter");
534     ewk_shutdown();
535     elm_exit();
536 }
537
538 void AppBoxRenderView::loadNonEmptyLayoutFinishedCallback(
539         void* data, Evas_Object* webview, void* eventInfo)
540 {
541     LogD("enter");
542 }
543
544 void AppBoxRenderView::frameRenderedCallback(
545         void* data, Evas_Object* webview, void* eventInfo)
546 {
547     LogD("enter");
548
549     // start to update render buffer!
550     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
551     This->m_boxRenderBuffer->startCanvasUpdate();
552 }