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