[Release] livebox.web-provider-1.19
[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     // 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     cbs->webCrash = DPL::MakeDelegate(this, &AppBoxRenderView::crashWebProcessCallback);
157     wrt->SetUserDelegates(cbs);
158
159     // To support transparent background
160     evas_object_color_set(wrt->GetCurrentWebview(), 0, 0, 0, 0);
161     evas_object_layer_set(wrt->GetCurrentWebview(), EVAS_LAYER_MAX);
162
163     return wrt;
164 }
165
166 void AppBoxRenderView::destroyBoxWrtCore()
167 {
168     LogD("enter");
169
170     m_renderBuffer->stopCanvasUpdate();
171     deleteRenderTimer();
172     destroyWrtCore(m_boxWrt);
173     m_boxWrt.reset();
174 }
175
176 void AppBoxRenderView::destroyPdWrtCore()
177 {
178     LogD("enter");
179
180     destroyWrtCore(m_pdWrt);
181     m_pdWrt.reset();
182 }
183
184 void AppBoxRenderView::destroyWrtCore(WrtCorePtr wrt)
185 {
186     LogD("enter");
187
188     if (wrt) {
189         wrt->Hide();
190     }
191 }
192
193 void AppBoxRenderView::hideBox()
194 {
195     LogD("enter");
196     destroyBoxWrtCore();
197 }
198
199 void AppBoxRenderView::pauseBox()
200 {
201     LogD("enter");
202 }
203
204 void AppBoxRenderView::resumeBox()
205 {
206     LogD("enter");
207 }
208
209 void AppBoxRenderView::showPd(Evas_Object* pdWin, RenderInfoPtr renderInfo)
210 {
211     LogD("enter");
212
213     // create pd helper
214     std::string pdStartUrl = getStartUrl(URL_TYPE_PD, renderInfo->defaultUrlParams);
215     if (m_pdFastOpen) {
216         destroyPdWrtCore();
217         // if needed, last param regarding ewk context can be set to new one.
218         m_pdWrt = createWrtCore(pdStartUrl, pdWin, m_ewkContext);
219         if (!m_pdWrt) {
220             LogD("no wrt core instance");
221             return;
222         }
223         m_pdHelper = AppBoxPdHelper::create(pdWin);
224
225         // resize webview fitted to width, height of pd
226         evas_object_resize(
227                 m_pdWrt->GetCurrentWebview(), 
228                 renderInfo->width,
229                 renderInfo->height);
230         // show pd
231         m_pdWrt->Show();
232         m_pdHelper->finishOpen(m_pdWrt->GetCurrentWebview());
233     } else {
234         m_pdHelper = PdHelper::create(pdWin, pdStartUrl, renderInfo);
235     }
236
237     // show pd window
238     evas_object_show(pdWin);
239     showBox(m_renderInfo);
240 }
241
242 void AppBoxRenderView::hidePd()
243 {
244     LogD("enter");
245
246     if (m_pdFastOpen) {
247         destroyPdWrtCore();
248     }
249     m_pdHelper->close();
250     m_pdHelper.reset();
251
252     // stop box webview after render timer
253     addRenderTimer();
254 }
255
256 Evas_Object* AppBoxRenderView::getBoxWebView()
257 {
258     if (!m_pdHelper) {
259         return m_boxWrt->GetCurrentWebview();
260     } else {
261         // Here, we can't use GetCurrentWebView() of wrt-core to get Box' webview,
262         // because in the non fast-open, GetCurrentWebview() returns PD's webview.
263         return m_pdHelper->getBoxWebView();
264     }
265 }
266
267 Evas_Object* AppBoxRenderView::getPdWebView()
268 {
269     if (!m_pdHelper) {
270         return NULL;
271     }
272
273     return m_pdHelper->getPdWebView();
274 }
275
276 std::string AppBoxRenderView::getAppId(std::string& boxId)
277 {
278     LogD("enter");
279
280     const char* appId = web_provider_livebox_get_app_id(boxId.c_str());
281     if (!appId) {
282         LogD("no appid of %s", boxId.c_str());
283         return std::string();
284     }
285
286     return std::string(appId);
287 }
288
289 std::string AppBoxRenderView::getStartUrl(UrlType type, std::string& defaultParams)
290 {
291     std::string url;
292     switch (type) {
293     case URL_TYPE_BOX:
294         url = livebox_service_lb_script_path(m_boxId.c_str());
295         break;
296     case URL_TYPE_PD:
297         url = livebox_service_pd_script_path(m_boxId.c_str());
298         break;
299     default:
300         LogD("no available type");
301     }
302
303     // add default parameters to start url
304     url += defaultParams;
305
306     return url;
307 }
308
309 Evas_Object* AppBoxRenderView::getCurrentSnapShot() 
310 {
311     LogD("enter");
312     clearSnapShot();
313     m_snapshot = m_renderBuffer->getSnapshot();
314     //evas_object_layer_set(m_snapshot, EVAS_LAYER_MAX);
315
316     return m_snapshot;
317 }
318
319 void AppBoxRenderView::clearSnapShot() 
320 {
321     LogD("enter");
322     if (m_snapshot) {
323         evas_object_layer_set(m_snapshot, EVAS_LAYER_MIN);
324         //evas_object_hide(m_snapshot);
325         evas_object_del(m_snapshot);
326         m_snapshot = NULL;
327     }
328 }
329
330 void AppBoxRenderView::addRenderTimer()
331 {
332     LogD("enter");
333     if (m_fireRenderTimer) {
334         deleteRenderTimer();
335     }
336
337     m_fireRenderTimer = ecore_timer_add(
338                             RENDER_MAX_TIME, 
339                             fireRenderTimerCallback,
340                             this);
341 }
342
343 void AppBoxRenderView::deleteRenderTimer()
344 {
345     LogD("enter");
346     if (m_fireRenderTimer) {
347         ecore_timer_del(m_fireRenderTimer);
348         m_fireRenderTimer = NULL;
349     }
350 }
351
352 Eina_Bool AppBoxRenderView::fireRenderTimerCallback(void* data)
353 {
354     LogD("enter");
355
356     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
357     This->m_fireRenderTimer = NULL;
358
359     This->m_renderBuffer->stopCanvasUpdate();
360     if (web_provider_livebox_get_mouse_event(This->m_boxId.c_str())) {
361         // stop touch timer
362         This->m_renderBuffer->deleteTouchTimer();
363         This->m_boxWrt->Suspend();
364     } else {
365         // Before webview should be removed,
366         // new evas object with last render data should be created
367         // otherwise, after webview is removed, box is white screen.
368         evas_object_show(This->getCurrentSnapShot());
369         This->destroyBoxWrtCore();
370     }
371
372     return ECORE_CALLBACK_CANCEL;
373 }
374
375 Eina_Bool AppBoxRenderView::openPdIdlerCallback(void* data)
376 {
377     LogD("enter");
378     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
379     if (This && This->m_pdHelper) {
380         This->m_pdHelper->startOpen();
381     }
382     return ECORE_CALLBACK_CANCEL;
383 }
384
385 void AppBoxRenderView::executeScriptCallback(
386         Evas_Object* webview, const char* result, void* data)
387 {
388     LogD("enter");
389     std::string resultStr(result ? result : "null");
390     LogD("result: %s", resultStr.c_str());
391 }
392
393 void AppBoxRenderView::startLoadCallback(Evas_Object* webview)
394 {
395     LogD("enter");
396     m_renderBuffer->startCanvasUpdate();
397     // execute injection for creating js objects
398     std::ifstream jsFile(injectionFile);
399     std::string script((std::istreambuf_iterator<char>(jsFile)),
400                         std::istreambuf_iterator<char>());
401
402     LogD("injected js code: %s", script.c_str());
403     ewk_view_script_execute(webview, script.c_str(), executeScriptCallback, this);
404 }
405
406 void AppBoxRenderView::finishLoadCallback(Evas_Object* webview)
407 {
408     LogD("enter");
409     ewk_view_visibility_set(webview, EINA_TRUE);
410
411     if (!m_pdHelper) {
412         // start render timer
413         addRenderTimer();
414     } else {
415         if (!m_pdFastOpen) {
416             if (!(m_pdHelper->isPdOpened()) && 
417                     webview == m_pdHelper->getBoxWebView())
418             {
419                 // open pd
420                 ecore_idler_add(openPdIdlerCallback, this);
421             }
422         }
423     }
424 }
425
426 void AppBoxRenderView::createWindowBeforeCallback(Evas** canvas, Evas_Object* parent)
427 {
428     LogD("enter");
429
430     if (m_pdHelper) {
431         if (!(m_pdHelper->isPdOpened()) && 
432                 parent == m_pdHelper->getBoxWebView())
433         {
434             LogD("pd canvas is used");
435             *canvas = m_pdHelper->getPdCanvas();
436             return;
437         }
438     }
439     
440     LogD("canvas of this webview is used");
441     *canvas = evas_object_evas_get(parent);
442 }
443
444 void AppBoxRenderView::createWindowAfterCallback(Evas_Object* parent, Evas_Object* child)
445 {
446     LogD("enter");
447
448     // To support transparent background
449     evas_object_color_set(child, 0, 0, 0, 0);
450
451     if (m_pdHelper) {
452         Evas* parentCanvas = evas_object_evas_get(parent);
453         Evas* childCanvas = evas_object_evas_get(child);
454
455         if (parentCanvas != childCanvas) {
456            // wrt-core change visibility value to false internally
457            // So plugin should reset this value to true for painting parent webview
458            ewk_view_visibility_set(parent, EINA_TRUE);
459            evas_object_show(parent);
460            m_pdHelper->finishOpen(child); 
461         }
462     }
463
464     ewk_view_visibility_set(child, EINA_TRUE);
465     evas_object_show(child);
466 }
467
468 void AppBoxRenderView::setBufferCallback(Evas_Object* webview)
469 {
470     LogD("enter");
471     evas_object_show(webview);
472     evas_object_focus_set(webview, EINA_TRUE);
473 }
474
475 void AppBoxRenderView::unsetBufferCallback(Evas_Object* webview)
476 {
477     LogD("enter");
478     evas_object_hide(webview);
479 }
480
481 void AppBoxRenderView::decideNavigationCallback(Evas_Object* webview, std::string& uri)
482 {
483     LogD("enter");
484
485     // navigation of box scheme should be ignored
486     if(BoxSchemeHandler::Instance()->isBoxScheme(uri)) {
487         LogD("box scheme");
488         BoxSchemeHandler::Instance()->process(m_instanceId, uri);
489     }
490 }
491
492 void AppBoxRenderView::crashWebProcessCallback()
493 {
494     LogD("enter");
495     ewk_shutdown();
496     elm_exit();
497 }