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