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