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