Implement delegate callback for navigation decision on default livebox plugin
[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 <livebox-service.h>
28 #include <i_runnable_widget_object.h>
29 #include <core_module.h>
30 #include <dpl/fast_delegate.h>
31 #include <Core/BoxSchemeHandler.h>
32 #include <Core/View/IRenderView.h>
33 #include <Core/View/IPdHelper.h>
34 #include <Core/View/PdHelper.h>
35 #include <API/web_provider_livebox_info.h>
36 #include <Core/Util/Log.h>
37 #include "AppBoxObserver.h"
38 #include "AppBoxRenderBuffer.h"
39 #include "AppBoxRenderView.h"
40
41 #define RENDER_MAX_TIME 10.0
42
43 // injection javascript file regarding creating js object used by box and pd
44 static const std::string injectionFile("/usr/share/web-provider/injection.js");
45
46 AppBoxRenderView::AppBoxRenderView(
47         std::string boxId, std::string instanceId,
48         Evas_Object* boxWin, EwkContextPtr ewkContext)
49     : m_appId()
50     , m_boxId(boxId)
51     , m_instanceId(instanceId)
52     , m_boxWin(boxWin)
53     , m_baseWebView()
54     , m_snapshot()
55     , m_renderInfo()
56     , m_view()
57     , m_startUrl()
58     , m_fireRenderTimer()
59     , m_pdHelper()
60     , m_ewkContext(ewkContext)
61 {
62     LogD("enter");
63     m_appId = getAppId(m_boxId);
64     if (m_appId.empty()) {
65         throw; //exception throw!
66     }
67
68     evas_object_show(m_boxWin);
69     m_renderBuffer = AppBoxObserver::Instance()->getRenderBuffer(m_instanceId);
70     AppBoxObserver::Instance()->registerRenderView(m_instanceId, this);
71 }
72
73 AppBoxRenderView::~AppBoxRenderView()
74 {
75     LogD("enter");
76     evas_object_hide(m_boxWin);
77     AppBoxObserver::Instance()->unregisterRenderView(m_instanceId);
78 }
79
80 void AppBoxRenderView::showBox(RenderInfoPtr renderInfo)
81 {
82     LogD("enter");
83
84     // delete already running timer
85     deleteRenderTimer();
86
87     // stop touch timer
88     if (web_provider_livebox_get_mouse_event(m_boxId.c_str())) {
89         m_renderBuffer->deleteTouchTimer();
90     }
91
92     // copy to url
93     m_startUrl = getStartUrl(URL_TYPE_BOX, renderInfo->defaultUrlParams);
94
95     if (!createView()) {
96         LogD("can't create view instance");
97         return;
98     }
99
100     // in case of showing box by request of pd open
101     if (m_pdHelper) {
102         m_pdHelper->setBaseWebView(m_baseWebView);
103     }
104
105     // resize webview fitted to width, height of Box
106     evas_object_resize(
107             m_baseWebView, 
108             renderInfo->width,
109             renderInfo->height);
110
111     clearSnapShot();
112     m_renderBuffer->startCanvasUpdate();
113     m_view->Show();
114     m_renderInfo = renderInfo;
115 }
116
117 bool AppBoxRenderView::createView()
118 {
119     LogD("enter");
120     
121     if (m_view) {
122         m_view->Hide();
123         m_view.reset();
124         m_baseWebView = NULL;
125     }
126 #ifdef MULTIPROCESS_SERVICE_SUPPORT
127     m_view = WRT::CoreModuleSingleton::
128                 Instance().getRunnableWidgetObject(m_appId, DPL::Optional<unsigned>());
129 #else
130     m_view = WRT::CoreModuleSingleton::
131                 Instance().getRunnableWidgetObject(m_appId);
132 #endif
133     // prepare webview
134     if (m_startUrl.empty()) {
135         LogD("no start url");
136         return false;
137     }
138     m_view->PrepareView(m_startUrl, m_boxWin, m_ewkContext.get());
139     m_view->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     cbs->windowCreateBefore = DPL::MakeDelegate(this, &AppBoxRenderView::createWindowBeforeCallback);
148     cbs->windowCreateAfter = DPL::MakeDelegate(this, &AppBoxRenderView::createWindowAfterCallback);
149     cbs->navigationDecide = DPL::MakeDelegate(this, &AppBoxRenderView::decideNavigationCallback);
150     m_view->SetUserDelegates(cbs);
151
152     // set base webview
153     m_baseWebView = m_view->GetCurrentWebview();
154
155     // To support transparent background
156     evas_object_color_set(m_baseWebView, 0, 0, 0, 0);
157     evas_object_layer_set(m_baseWebView, EVAS_LAYER_MAX);
158     return true;
159 }
160
161 bool AppBoxRenderView::destroyView()
162 {
163     LogD("enter");
164
165     m_renderBuffer->stopCanvasUpdate();
166     deleteRenderTimer();
167     if (m_view) {
168         m_view->Hide();
169         m_view.reset();
170         m_baseWebView = NULL;
171     }
172
173     return true;
174 }
175
176 void AppBoxRenderView::hideBox()
177 {
178     LogD("enter");
179     destroyView();
180 }
181
182 void AppBoxRenderView::pauseBox()
183 {
184     LogD("enter");
185 }
186
187 void AppBoxRenderView::resumeBox()
188 {
189     LogD("enter");
190 }
191
192 void AppBoxRenderView::showPd(Evas_Object* pdWin, RenderInfoPtr renderInfo)
193 {
194     LogD("enter");
195
196     // create pd helper
197     std::string pdStartUrl = getStartUrl(URL_TYPE_PD, renderInfo->defaultUrlParams);
198     m_pdHelper = PdHelper::create(pdWin, pdStartUrl, renderInfo);
199
200     // show pd window
201     evas_object_show(pdWin);
202     showBox(m_renderInfo);
203 }
204
205 void AppBoxRenderView::hidePd()
206 {
207     LogD("enter");
208
209     m_pdHelper->close();
210     m_pdHelper.reset();
211
212     // stop box webview after render timer
213     addRenderTimer();
214 }
215
216 Evas_Object* AppBoxRenderView::getBoxWebView()
217 {
218     return m_baseWebView;
219 }
220
221 Evas_Object* AppBoxRenderView::getPdWebView()
222 {
223     if (!m_pdHelper) {
224         return NULL;
225     }
226
227     return m_pdHelper->getPdWebView();
228 }
229
230 std::string AppBoxRenderView::getAppId(std::string& boxId)
231 {
232     LogD("enter");
233
234     const char* appId = web_provider_livebox_get_app_id(boxId.c_str());
235     if (!appId) {
236         LogD("no appid of %s", boxId.c_str());
237         return std::string();
238     }
239
240     return std::string(appId);
241 }
242
243 std::string AppBoxRenderView::getStartUrl(UrlType type, std::string& defaultParams)
244 {
245     std::string url;
246     switch (type) {
247     case URL_TYPE_BOX:
248         url = livebox_service_lb_script_path(m_boxId.c_str());
249         break;
250     case URL_TYPE_PD:
251         url = livebox_service_pd_script_path(m_boxId.c_str());
252         break;
253     default:
254         LogD("no available type");
255     }
256
257     // add default parameters to start url
258     url += defaultParams;
259
260     return url;
261 }
262
263 Evas_Object* AppBoxRenderView::getCurrentSnapShot() 
264 {
265     LogD("enter");
266     clearSnapShot();
267     m_snapshot = m_renderBuffer->getSnapshot();
268     //evas_object_layer_set(m_snapshot, EVAS_LAYER_MAX);
269
270     return m_snapshot;
271 }
272
273 void AppBoxRenderView::clearSnapShot() 
274 {
275     LogD("enter");
276     if (m_snapshot) {
277         evas_object_layer_set(m_snapshot, EVAS_LAYER_MIN);
278         //evas_object_hide(m_snapshot);
279         evas_object_del(m_snapshot);
280         m_snapshot = NULL;
281     }
282 }
283
284 void AppBoxRenderView::addRenderTimer()
285 {
286     LogD("enter");
287     if (m_fireRenderTimer) {
288         deleteRenderTimer();
289     }
290
291     m_fireRenderTimer = ecore_timer_add(
292                             RENDER_MAX_TIME, 
293                             fireRenderTimerCallback,
294                             this);
295 }
296
297 void AppBoxRenderView::deleteRenderTimer()
298 {
299     LogD("enter");
300     if (m_fireRenderTimer) {
301         ecore_timer_del(m_fireRenderTimer);
302         m_fireRenderTimer = NULL;
303     }
304 }
305
306 Eina_Bool AppBoxRenderView::fireRenderTimerCallback(void* data)
307 {
308     LogD("enter");
309
310     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
311     This->m_fireRenderTimer = NULL;
312
313     This->m_renderBuffer->stopCanvasUpdate();
314     if (web_provider_livebox_get_mouse_event(This->m_boxId.c_str())) {
315         // stop touch timer
316         This->m_renderBuffer->deleteTouchTimer();
317         This->m_view->Suspend();
318     } else {
319         // Before webview should be removed,
320         // new evas object with last render data should be created
321         // otherwise, after webview is removed, box is white screen.
322         evas_object_show(This->getCurrentSnapShot());
323         This->destroyView();
324     }
325
326     return ECORE_CALLBACK_CANCEL;
327 }
328
329 Eina_Bool AppBoxRenderView::openPdIdlerCallback(void* data)
330 {
331     LogD("enter");
332     AppBoxRenderView* This = static_cast<AppBoxRenderView*>(data);
333     if (This && This->m_pdHelper) {
334         This->m_pdHelper->startOpen();
335     }
336     return ECORE_CALLBACK_CANCEL;
337 }
338
339 void AppBoxRenderView::executeScriptCallback(
340         Evas_Object* webview, const char* result, void* data)
341 {
342     LogD("enter");
343     std::string resultStr(result ? result : "null");
344     LogD("result: %s", resultStr.c_str());
345 }
346
347 void AppBoxRenderView::startLoadCallback(Evas_Object* webview)
348 {
349     LogD("enter");
350     // execute injection for creating js objects
351     std::ifstream jsFile(injectionFile);
352     std::string script((std::istreambuf_iterator<char>(jsFile)),
353                         std::istreambuf_iterator<char>());
354
355     LogD("injected js code: %s", script.c_str());
356     ewk_view_script_execute(webview, script.c_str(), executeScriptCallback, this);
357 }
358
359 void AppBoxRenderView::finishLoadCallback(Evas_Object* webview)
360 {
361     LogD("enter");
362     ewk_view_visibility_set(webview, EINA_TRUE);
363
364     if (!m_pdHelper) {
365         // start render timer
366         addRenderTimer();
367     } else {
368         if (!(m_pdHelper->isPdOpened()) && 
369                 webview == m_pdHelper->getBaseWebView())
370         {
371             // open pd
372             ecore_idler_add(openPdIdlerCallback, this);
373         }
374     }
375 }
376
377 void AppBoxRenderView::createWindowBeforeCallback(Evas** canvas, Evas_Object* parent)
378 {
379     LogD("enter");
380
381     if (m_pdHelper) {
382         if (!(m_pdHelper->isPdOpened()) && 
383                 parent == m_pdHelper->getBaseWebView())
384         {
385             LogD("pd canvas is used");
386             *canvas = m_pdHelper->getPdCanvas();
387             return;
388         }
389     } 
390     
391     LogD("canvas of this webview is used");
392     *canvas = evas_object_evas_get(parent);
393 }
394
395 void AppBoxRenderView::createWindowAfterCallback(Evas_Object* parent, Evas_Object* child)
396 {
397     LogD("enter");
398
399     // To support transparent background
400     evas_object_color_set(child, 0, 0, 0, 0);
401
402     if (m_pdHelper) {
403         Evas* parentCanvas = evas_object_evas_get(parent);
404         Evas* childCanvas = evas_object_evas_get(child);
405
406         if (parentCanvas != childCanvas) {
407            // wrt-core change visibility value to false internally
408            // So plugin should reset this value to true for painting parent webview
409            ewk_view_visibility_set(parent, EINA_TRUE);
410            evas_object_show(parent);
411            m_pdHelper->finishOpen(child); 
412         }
413     }
414
415     ewk_view_visibility_set(child, EINA_TRUE);
416     evas_object_show(child);
417 }
418
419 void AppBoxRenderView::setBufferCallback(Evas_Object* webview)
420 {
421     LogD("enter");
422     evas_object_show(webview);
423     evas_object_focus_set(webview, EINA_TRUE);
424 }
425
426 void AppBoxRenderView::unsetBufferCallback(Evas_Object* webview)
427 {
428     LogD("enter");
429     evas_object_hide(webview);
430 }
431
432 void AppBoxRenderView::decideNavigationCallback(Evas_Object* webview, std::string& uri)
433 {
434     LogD("enter");
435
436     // navigation of box scheme should be ignored
437     if(BoxSchemeHandler::Instance()->isBoxScheme(uri)) {
438         LogD("box scheme");
439         BoxSchemeHandler::Instance()->process(m_instanceId, uri);
440     }
441 }