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