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