Handle back-key behavior when viewmode is 'windowed'
[platform/framework/web/crosswalk-tizen.git] / runtime / browser / web_application.cc
1 /*
2  * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
3  *
4  *    Licensed under the Apache License, Version 2.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://www.apache.org/licenses/LICENSE-2.0
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 #include "runtime/browser/web_application.h"
18
19 #include <app.h>
20 #include <Ecore.h>
21 #include <aul.h>
22
23 #include <algorithm>
24 #include <map>
25 #include <memory>
26 #include <sstream>
27 #include <vector>
28
29 #include "common/application_data.h"
30 #include "common/app_db.h"
31 #include "common/app_control.h"
32 #include "common/command_line.h"
33 #include "common/locale_manager.h"
34 #include "common/logger.h"
35 #include "common/profiler.h"
36 #include "common/resource_manager.h"
37 #include "common/string_utils.h"
38 #include "runtime/browser/native_window.h"
39 #include "runtime/browser/notification_manager.h"
40 #include "runtime/browser/popup.h"
41 #include "runtime/browser/popup_string.h"
42 #include "runtime/browser/vibration_manager.h"
43 #include "runtime/browser/web_view.h"
44 #include "runtime/browser/splash_screen.h"
45 #include "extensions/common/xwalk_extension_server.h"
46
47 #ifndef INJECTED_BUNDLE_PATH
48 #error INJECTED_BUNDLE_PATH is not set.
49 #endif
50
51 namespace runtime {
52
53 namespace {
54 const char* kKeyNameBack = "back";
55 const char* kKeyNameMenu = "menu";
56
57 const char* kConsoleLogEnableKey = "WRT_CONSOLE_LOG_ENABLE";
58 const char* kConsoleMessageLogTag = "ConsoleMessage";
59
60 const char* kVerboseKey = "verbose";
61 const char* kPortKey = "port";
62
63 const char* kAppControlEventScript =
64     "(function(){"
65     "var __event = document.createEvent(\"CustomEvent\");\n"
66     "__event.initCustomEvent(\"appcontrol\", true, true, null);\n"
67     "document.dispatchEvent(__event);\n"
68     "\n"
69     "for (var i=0; i < window.frames.length; i++)\n"
70     "{ window.frames[i].document.dispatchEvent(__event); }"
71     "})()";
72 const char* kBackKeyEventScript =
73     "(function(){"
74     "var __event = document.createEvent(\"CustomEvent\");\n"
75     "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
76     "__event.keyName = \"back\";\n"
77     "document.dispatchEvent(__event);\n"
78     "\n"
79     "for (var i=0; i < window.frames.length; i++)\n"
80     "{ window.frames[i].document.dispatchEvent(__event); }"
81     "})()";
82 const char* kMenuKeyEventScript =
83     "(function(){"
84     "var __event = document.createEvent(\"CustomEvent\");\n"
85     "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
86     "__event.keyName = \"menu\";\n"
87     "document.dispatchEvent(__event);\n"
88     "\n"
89     "for (var i=0; i < window.frames.length; i++)\n"
90     "{ window.frames[i].document.dispatchEvent(__event); }"
91     "})()";
92 const char* kAmbientTickEventScript =
93     "(function(){"
94     "var __event = document.createEvent(\"CustomEvent\");\n"
95     "__event.initCustomEvent(\"timetick\", true, true);\n"
96     "document.dispatchEvent(__event);\n"
97     "\n"
98     "for (var i=0; i < window.frames.length; i++)\n"
99     "{ window.frames[i].document.dispatchEvent(__event); }"
100     "})()";
101 const char* kFullscreenPrivilege = "http://tizen.org/privilege/fullscreen";
102 const char* kFullscreenFeature = "fullscreen";
103 const char* kNotificationPrivilege = "http://tizen.org/privilege/notification";
104 const char* kLocationPrivilege = "http://tizen.org/privilege/location";
105 const char* kStoragePrivilege = "http://tizen.org/privilege/unlimitedstorage";
106 const char* kUsermediaPrivilege = "http://tizen.org/privilege/mediacapture";
107 const char* kNotiIconFile = "noti_icon.png";
108 const char* kFileScheme = "file://";
109
110 const char* kVisibilitySuspendFeature = "visibility,suspend";
111 const char* kMediastreamRecordFeature = "mediastream,record";
112 const char* kEncryptedDatabaseFeature = "encrypted,database";
113 const char* kRotationLockFeature = "rotation,lock";
114 const char* kBackgroundMusicFeature = "background,music";
115 const char* kSoundModeFeature = "sound,mode";
116 const char* kBackgroundVibrationFeature = "background,vibration";
117 const char* kCSPFeature = "csp";
118
119 const char* kGeolocationPermissionPrefix = "__WRT_GEOPERM_";
120 const char* kNotificationPermissionPrefix = "__WRT_NOTIPERM_";
121 const char* kQuotaPermissionPrefix = "__WRT_QUOTAPERM_";
122 const char* kCertificateAllowPrefix = "__WRT_CERTIPERM_";
123 const char* kUsermediaPermissionPrefix = "__WRT_USERMEDIAPERM_";
124 const char* kDBPrivateSection = "private";
125
126 const char* kDefaultCSPRule =
127     "default-src *; script-src 'self'; style-src 'self'; object-src 'none';";
128 const char* kResWgtPath = "res/wgt/";
129 const char* kAppControlMain = "http://tizen.org/appcontrol/operation/main";
130
131 bool FindPrivilege(common::ApplicationData* app_data,
132                    const std::string& privilege) {
133   if (app_data->permissions_info().get() == NULL) return false;
134   auto it = app_data->permissions_info()->GetAPIPermissions().begin();
135   auto end = app_data->permissions_info()->GetAPIPermissions().end();
136   for (; it != end; ++it) {
137     if (*it == privilege) return true;
138   }
139   return false;
140 }
141
142 static void SendDownloadRequest(const std::string& url) {
143   common::AppControl request;
144   request.set_operation(APP_CONTROL_OPERATION_DOWNLOAD);
145   request.set_uri(url);
146   request.LaunchRequest();
147 }
148
149 static void InitializeNotificationCallback(Ewk_Context* ewk_context,
150                                            WebApplication* app) {
151   auto show = [](Ewk_Context*, Ewk_Notification* noti, void* user_data) {
152     WebApplication* self = static_cast<WebApplication*>(user_data);
153     if (self == NULL) return;
154     uint64_t id = ewk_notification_id_get(noti);
155     std::string title(ewk_notification_title_get(noti)
156                           ? ewk_notification_title_get(noti)
157                           : "");
158     std::string body(
159         ewk_notification_body_get(noti) ? ewk_notification_body_get(noti) : "");
160     std::string icon_path = self->data_path() + "/" + kNotiIconFile;
161     if (!ewk_notification_icon_save_as_png(noti, icon_path.c_str())) {
162       icon_path = "";
163     }
164     if (NotificationManager::GetInstance()->Show(id, title, body, icon_path))
165       ewk_notification_showed(id);
166   };
167   auto hide = [](Ewk_Context*, uint64_t noti_id, void*) {
168     NotificationManager::GetInstance()->Hide(noti_id);
169     ewk_notification_closed(noti_id, EINA_FALSE);
170   };
171   ewk_context_notification_callbacks_set(ewk_context, show, hide, app);
172 }
173
174 static Eina_Bool ExitAppIdlerCallback(void* data) {
175   WebApplication* app = static_cast<WebApplication*>(data);
176   if (app)
177     app->Terminate();
178   return ECORE_CALLBACK_CANCEL;
179 }
180
181 static bool ClearCookie(Ewk_Context* ewk_context) {
182   Ewk_Cookie_Manager* cookie_manager =
183       ewk_context_cookie_manager_get(ewk_context);
184   if (!cookie_manager) {
185     LOGGER(ERROR) << "Fail to get cookie manager";
186     return false;
187   }
188   ewk_cookie_manager_cookies_clear(cookie_manager);
189   return true;
190 }
191
192 static bool ProcessWellKnownScheme(const std::string& url) {
193   if (common::utils::StartsWith(url, "file:") ||
194       common::utils::StartsWith(url, "app:") ||
195       common::utils::StartsWith(url, "data:") ||
196       common::utils::StartsWith(url, "http:") ||
197       common::utils::StartsWith(url, "https:") ||
198       common::utils::StartsWith(url, "widget:") ||
199       common::utils::StartsWith(url, "about:") ||
200       common::utils::StartsWith(url, "blob:")) {
201     return false;
202   }
203
204   std::unique_ptr<common::AppControl> request(
205       common::AppControl::MakeAppcontrolFromURL(url));
206   if (request.get() == NULL || !request->LaunchRequest()) {
207     LOGGER(ERROR) << "Fail to send appcontrol request";
208     SLoggerE("Fail to send appcontrol request [%s]", url.c_str());
209   }
210
211   // Should return true, to stop the WebEngine progress step about this URL
212   return true;
213 }
214
215 }  // namespace
216
217 WebApplication::WebApplication(
218     NativeWindow* window,
219     common::ApplicationData* app_data)
220     : launched_(false),
221       debug_mode_(false),
222       verbose_mode_(false),
223       lang_changed_mode_(false),
224       ewk_context_(
225           ewk_context_new_with_injected_bundle_path(INJECTED_BUNDLE_PATH)),
226       has_ownership_of_ewk_context_(true),
227       window_(window),
228       appid_(app_data->app_id()),
229       app_data_(app_data),
230       locale_manager_(new common::LocaleManager()),
231       terminator_(NULL) {
232   Initialize();
233 }
234
235 WebApplication::WebApplication(
236     NativeWindow* window,
237     common::ApplicationData* app_data,
238     Ewk_Context* context)
239     : launched_(false),
240       debug_mode_(false),
241       verbose_mode_(false),
242       ewk_context_(context),
243       has_ownership_of_ewk_context_(false),
244       window_(window),
245       appid_(app_data->app_id()),
246       app_data_(app_data),
247       locale_manager_(new common::LocaleManager()),
248       terminator_(NULL) {
249   Initialize();
250 }
251
252 WebApplication::~WebApplication() {
253   window_->SetContent(NULL);
254   auto it = view_stack_.begin();
255   for (; it != view_stack_.end(); ++it) {
256     delete *it;
257   }
258   view_stack_.clear();
259   if (ewk_context_ && has_ownership_of_ewk_context_)
260     ewk_context_delete(ewk_context_);
261 }
262
263 bool WebApplication::Initialize() {
264   SCOPE_PROFILE();
265   std::unique_ptr<char, decltype(std::free)*> path{app_get_data_path(),
266                                                    std::free};
267   app_data_path_ = path.get();
268
269   if (app_data_->setting_info() != NULL &&
270       app_data_->setting_info()->screen_orientation() ==
271           wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
272     ewk_context_tizen_extensible_api_string_set(ewk_context_,
273                                                 kRotationLockFeature, true);
274     window_->SetAutoRotation();
275   } else if (app_data_->setting_info() != NULL &&
276              app_data_->setting_info()->screen_orientation() ==
277                  wgt::parse::SettingInfo::ScreenOrientation::PORTRAIT) {
278     window_->SetRotationLock(NativeWindow::ScreenOrientation::PORTRAIT_PRIMARY);
279   } else if (app_data_->setting_info() != NULL &&
280              app_data_->setting_info()->screen_orientation() ==
281                  wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
282     window_->SetRotationLock(
283         NativeWindow::ScreenOrientation::LANDSCAPE_PRIMARY);
284   }
285
286   splash_screen_.reset(new SplashScreen(
287       window_, app_data_->splash_screen_info(), app_data_->application_path()));
288   resource_manager_.reset(
289       new common::ResourceManager(app_data_, locale_manager_.get()));
290   resource_manager_->set_base_resource_path(app_data_->application_path());
291
292   auto extension_server = extensions::XWalkExtensionServer::GetInstance();
293   extension_server->SetupIPC(ewk_context_);
294
295   // ewk setting
296   ewk_context_cache_model_set(ewk_context_, EWK_CACHE_MODEL_DOCUMENT_BROWSER);
297
298   // cookie
299   auto cookie_manager = ewk_context_cookie_manager_get(ewk_context_);
300   ewk_cookie_manager_accept_policy_set(cookie_manager,
301                                        EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
302
303   // set persistent storage path
304   std::string cookie_path = data_path() + ".cookie";
305   ewk_cookie_manager_persistent_storage_set(
306       cookie_manager, cookie_path.c_str(),
307       EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
308
309   // vibration callback
310   auto vibration_start_callback = [](uint64_t ms, void*) {
311     platform::VibrationManager::GetInstance()->Start(static_cast<int>(ms));
312   };
313   auto vibration_stop_callback = [](void* /*user_data*/) {
314     platform::VibrationManager::GetInstance()->Stop();
315   };
316   ewk_context_vibration_client_callbacks_set(
317       ewk_context_, vibration_start_callback, vibration_stop_callback, NULL);
318
319   auto download_callback = [](const char* downloadUrl, void* /*data*/) {
320     SendDownloadRequest(downloadUrl);
321   };
322   ewk_context_did_start_download_callback_set(ewk_context_, download_callback,
323                                               this);
324   InitializeNotificationCallback(ewk_context_, this);
325
326   if (FindPrivilege(app_data_, kFullscreenPrivilege)) {
327     ewk_context_tizen_extensible_api_string_set(ewk_context_,
328                                                 kFullscreenFeature, true);
329   }
330
331   if (app_data_->setting_info() != NULL &&
332       app_data_->setting_info()->background_support_enabled()) {
333     ewk_context_tizen_extensible_api_string_set(
334                                 ewk_context_, kVisibilitySuspendFeature, true);
335     ewk_context_tizen_extensible_api_string_set(ewk_context_,
336                                                 kBackgroundMusicFeature, true);
337   } else {
338     ewk_context_tizen_extensible_api_string_set(
339                                ewk_context_, kVisibilitySuspendFeature, false);
340     ewk_context_tizen_extensible_api_string_set(ewk_context_,
341                                                kBackgroundMusicFeature, false);
342   }
343   ewk_context_tizen_extensible_api_string_set(ewk_context_,
344                                               kMediastreamRecordFeature, true);
345   ewk_context_tizen_extensible_api_string_set(ewk_context_,
346                                               kEncryptedDatabaseFeature, true);
347
348   if (app_data_->setting_info() != NULL &&
349       app_data_->setting_info()->sound_mode() ==
350           wgt::parse::SettingInfo::SoundMode::EXCLUSIVE) {
351     ewk_context_tizen_extensible_api_string_set(ewk_context_, kSoundModeFeature,
352                                                 true);
353   }
354
355   if (app_data_->setting_info() != NULL &&
356       app_data_->setting_info()->background_vibration()) {
357     ewk_context_tizen_extensible_api_string_set(
358         ewk_context_, kBackgroundVibrationFeature, true);
359   }
360
361   if (app_data_->widget_info() != NULL &&
362       !app_data_->widget_info()->default_locale().empty()) {
363     locale_manager_->SetDefaultLocale(
364         app_data_->widget_info()->default_locale());
365   }
366
367   if (app_data_->widget_info() != NULL &&
368         app_data_->widget_info()->view_modes() == "fullscreen") {
369       window_->FullScreen(true);
370   }
371
372   if (app_data_->csp_info() != NULL || app_data_->csp_report_info() != NULL ||
373       app_data_->allowed_navigation_info() != NULL) {
374     security_model_version_ = 2;
375     if (app_data_->csp_info() == NULL ||
376         app_data_->csp_info()->security_rules().empty()) {
377       csp_rule_ = kDefaultCSPRule;
378     } else {
379       csp_rule_ = app_data_->csp_info()->security_rules();
380     }
381     if (app_data_->csp_report_info() != NULL &&
382         !app_data_->csp_report_info()->security_rules().empty()) {
383       csp_report_rule_ = app_data_->csp_report_info()->security_rules();
384     }
385     ewk_context_tizen_extensible_api_string_set(ewk_context_, kCSPFeature,
386                                                 EINA_TRUE);
387   } else {
388     security_model_version_ = 1;
389   }
390
391 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
392   // Set manual rotation
393   window_->EnableManualRotation(true);
394 #endif  // MANUAL_ROTATE_FEATURE_SUPPORT
395
396   return true;
397 }
398
399 void WebApplication::Launch(std::unique_ptr<common::AppControl> appcontrol) {
400   // send widget info to injected bundle
401   ewk_context_tizen_app_id_set(ewk_context_, appid_.c_str());
402
403   // Setup View
404   WebView* view = new WebView(window_, ewk_context_);
405   SetupWebView(view);
406
407   std::unique_ptr<common::ResourceManager::Resource> res =
408       resource_manager_->GetStartResource(appcontrol.get());
409   view->SetDefaultEncoding(res->encoding());
410
411   STEP_PROFILE_END("OnCreate -> URL Set");
412   STEP_PROFILE_START("URL Set -> Rendered");
413
414   window_->SetContent(view->evas_object());
415
416 #ifdef PROFILE_MOBILE
417   // rotate and resize window forcibily for landscape mode.
418   // window rotate event is generated after window show. so
419   // when app get width and height from viewport, wrong value can be returned.
420   if (app_data_->setting_info()->screen_orientation() ==
421              wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
422     LOGGER(DEBUG) << "rotate and resize window for landscape mode";
423     elm_win_rotation_with_resize_set(window_->evas_object(), 270);
424     evas_norender(evas_object_evas_get(window_->evas_object()));
425   }
426 #endif  // PROFILE_MOBILE
427
428   view->LoadUrl(res->uri(), res->mime());
429   view_stack_.push_front(view);
430
431 #ifdef PROFILE_WEARABLE
432   // ewk_view_bg_color_set is not working at webview initialization.
433   if (app_data_->app_type() == common::ApplicationData::WATCH) {
434     view->SetBGColor(0, 0, 0, 255);
435   }
436 #endif  // PROFILE_WEARABLE
437
438   if (appcontrol->data(AUL_K_DEBUG) == "1") {
439     debug_mode_ = true;
440     LaunchInspector(appcontrol.get());
441   }
442   if (appcontrol->data(kVerboseKey) == "true") {
443     verbose_mode_ = true;
444   }
445
446   launched_ = true;
447
448 #ifdef PROFILE_MOBILE
449   if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
450     LOGGER(DEBUG) << "Show window after launch for remote URL";
451     window_->Show();
452     window_->Active();
453   }
454 #endif  // PROFILE_MOBILE
455 }
456
457 void WebApplication::AppControl(
458     std::unique_ptr<common::AppControl> appcontrol) {
459   std::unique_ptr<common::ResourceManager::Resource> res =
460       resource_manager_->GetStartResource(appcontrol.get());
461
462   bool do_reset = res->should_reset();
463
464   if (!do_reset) {
465     std::string current_page = view_stack_.front()->GetUrl();
466     std::string localized_page = res->uri();
467
468     if (current_page != localized_page) {
469       do_reset = true;
470     } else {
471       SendAppControlEvent();
472     }
473   }
474
475   // handle http://tizen.org/appcontrol/operation/main operation specially.
476   // only menu-screen app can send launch request with main operation.
477   // in this case, web app should have to resume web app not reset.
478   if (do_reset && (appcontrol->operation() == kAppControlMain)){
479     LOGGER(DEBUG) << "resume app for main operation";
480     do_reset = false;
481     SendAppControlEvent();
482   }
483
484   if (do_reset) {
485     // Reset to context
486     ClearViewStack();
487     WebView* view = view_stack_.front();
488     SetupWebView(view);
489     view->SetDefaultEncoding(res->encoding());
490     view->LoadUrl(res->uri(), res->mime());
491     window_->SetContent(view->evas_object());
492   }
493
494   if (!debug_mode_ && appcontrol->data(AUL_K_DEBUG) == "1") {
495     debug_mode_ = true;
496     LaunchInspector(appcontrol.get());
497   }
498   if (!verbose_mode_ && appcontrol->data(kVerboseKey) == "true") {
499     verbose_mode_ = true;
500   }
501   window_->Active();
502 }
503
504 void WebApplication::SendAppControlEvent() {
505   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
506     view_stack_.front()->EvalJavascript(kAppControlEventScript);
507 }
508
509 void WebApplication::ClearViewStack() {
510   window_->SetContent(NULL);
511   WebView* front = view_stack_.front();
512   auto it = view_stack_.begin();
513   for (; it != view_stack_.end(); ++it) {
514     if (*it != front) {
515       (*it)->Suspend();
516       delete *it;
517     }
518   }
519   view_stack_.clear();
520   view_stack_.push_front(front);
521 }
522
523 void WebApplication::Resume() {
524   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
525     view_stack_.front()->SetVisibility(true);
526
527   if (app_data_->setting_info() != NULL &&
528       app_data_->setting_info()->background_support_enabled()) {
529     return;
530   }
531
532   auto it = view_stack_.begin();
533   for (; it != view_stack_.end(); ++it) {
534     (*it)->Resume();
535   }
536 }
537
538 void WebApplication::Suspend() {
539   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
540     view_stack_.front()->SetVisibility(false);
541
542   if (app_data_->setting_info() != NULL &&
543       app_data_->setting_info()->background_support_enabled()) {
544     LOGGER(DEBUG) << "gone background (backgroud support enabed)";
545     return;
546   }
547
548   auto it = view_stack_.begin();
549   for (; it != view_stack_.end(); ++it) {
550     (*it)->Suspend();
551   }
552 }
553
554 void WebApplication::Terminate() {
555   if (terminator_) {
556     terminator_();
557   } else {
558     elm_exit();
559   }
560   auto extension_server = extensions::XWalkExtensionServer::GetInstance();
561   extension_server->Shutdown();
562 }
563
564 void WebApplication::OnCreatedNewWebView(WebView* /*view*/, WebView* new_view) {
565   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
566     view_stack_.front()->SetVisibility(false);
567
568   SetupWebView(new_view);
569   view_stack_.push_front(new_view);
570   window_->SetContent(new_view->evas_object());
571 }
572
573 void WebApplication::RemoveWebViewFromStack(WebView* view) {
574   if (view_stack_.size() == 0) return;
575
576   WebView* current = view_stack_.front();
577   if (current == view) {
578     // In order to prevent the crash issue due to the callback
579     // which occur after destroying WebApplication class,
580     // we have to set the 'SetEventListener' to NULL.
581     view->SetEventListener(NULL);
582     view_stack_.pop_front();
583   } else {
584     auto found = std::find(view_stack_.begin(), view_stack_.end(), view);
585     if (found != view_stack_.end()) {
586       // In order to prevent the crash issue due to the callback
587       // which occur after destroying WebApplication class,
588       // we have to set the 'SetEventListener' to NULL.
589       view->SetEventListener(NULL);
590       view_stack_.erase(found);
591     }
592   }
593
594   if (view_stack_.size() == 0) {
595     Terminate();
596   } else if (current != view_stack_.front()) {
597     view_stack_.front()->SetVisibility(true);
598     window_->SetContent(view_stack_.front()->evas_object());
599   }
600
601   // Delete after the callback context(for ewk view) was not used
602   ecore_idler_add([](void* view) {
603                     WebView* obj = static_cast<WebView*>(view);
604                     delete obj;
605                     return EINA_FALSE;
606                   },
607                   view);
608 }
609
610 void WebApplication::OnClosedWebView(WebView* view) {
611     RemoveWebViewFromStack(view);
612 }
613
614 void WebApplication::OnReceivedWrtMessage(WebView* /*view*/,
615                                           Ewk_IPC_Wrt_Message_Data* msg) {
616   Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg);
617
618 #define TYPE_BEGIN(x) (!strncmp(msg_type, x, strlen(x)))
619 #define TYPE_IS(x) (!strcmp(msg_type, x))
620
621   if (TYPE_BEGIN("xwalk://")) {
622     auto extension_server = extensions::XWalkExtensionServer::GetInstance();
623     extension_server->HandleIPCMessage(msg);
624   } else {
625     Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg);
626     Eina_Stringshare* msg_ref_id =
627         ewk_ipc_wrt_message_data_reference_id_get(msg);
628     Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg);
629
630     if (TYPE_IS("tizen://hide")) {
631       // One Way Message
632       window_->InActive();
633     } else if (TYPE_IS("tizen://exit")) {
634       // One Way Message
635       ecore_idler_add(ExitAppIdlerCallback, this);
636     } else if (TYPE_IS("tizen://changeUA")) {
637       // Async Message
638       // Change UserAgent of current WebView
639       bool ret = false;
640       if (view_stack_.size() > 0 && view_stack_.front() != NULL) {
641         ret = view_stack_.front()->SetUserAgent(std::string(msg_value));
642       }
643       // Send response
644       Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
645       ewk_ipc_wrt_message_data_type_set(ans, msg_type);
646       ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
647       if (ret)
648         ewk_ipc_wrt_message_data_value_set(ans, "success");
649       else
650         ewk_ipc_wrt_message_data_value_set(ans, "failed");
651       if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
652         LOGGER(ERROR) << "Failed to send response";
653       }
654       ewk_ipc_wrt_message_data_del(ans);
655     } else if (TYPE_IS("tizen://deleteAllCookies")) {
656       Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
657       ewk_ipc_wrt_message_data_type_set(ans, msg_type);
658       ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
659       if (ClearCookie(ewk_context_))
660         ewk_ipc_wrt_message_data_value_set(ans, "success");
661       else
662         ewk_ipc_wrt_message_data_value_set(ans, "failed");
663       if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
664         LOGGER(ERROR) << "Failed to send response";
665       }
666       ewk_ipc_wrt_message_data_del(ans);
667     } else if (TYPE_IS("tizen://hide_splash_screen")) {
668       splash_screen_->HideSplashScreen(SplashScreen::HideReason::CUSTOM);
669     }
670
671     eina_stringshare_del(msg_ref_id);
672     eina_stringshare_del(msg_id);
673     eina_stringshare_del(msg_value);
674   }
675
676 #undef TYPE_IS
677 #undef TYPE_BEGIN
678
679   eina_stringshare_del(msg_type);
680 }
681
682 void WebApplication::OnOrientationLock(
683     WebView* view, bool lock,
684     NativeWindow::ScreenOrientation preferred_rotation) {
685   if (view_stack_.size() == 0) return;
686
687   // Only top-most view can set the orientation relate operation
688   if (view_stack_.front() != view) return;
689
690   auto orientaion_setting =
691       app_data_->setting_info() != NULL
692           ? app_data_->setting_info()->screen_orientation()
693           :
694           wgt::parse::SettingInfo::ScreenOrientation::AUTO;
695   if (orientaion_setting != wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
696     return;
697   }
698
699   if (lock) {
700     window_->SetRotationLock(preferred_rotation);
701   } else {
702     window_->SetAutoRotation();
703   }
704 }
705
706 void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
707   // NOTE: This code is added to enable back-key on remote URL
708   bool enabled = app_data_->setting_info() != NULL
709                      ? app_data_->setting_info()->hwkey_enabled()
710                      : true;
711
712   if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
713     if (kKeyNameBack == keyname) {
714       LOGGER(DEBUG) << "Back to previous page for remote URL";
715       if (!view->Backward()) {
716         if(enabled)
717           view->EvalJavascript(kBackKeyEventScript);
718         RemoveWebViewFromStack(view_stack_.front());
719       }
720     }
721     return;
722   }
723
724   if (enabled && kKeyNameBack == keyname) {
725     view->EvalJavascript(kBackKeyEventScript);
726     // NOTE: This code is added for backward compatibility.
727     // If the 'backbutton_presence' is true, WebView should be navigated back.
728     if ((app_data_->setting_info() != NULL &&
729          app_data_->setting_info()->backbutton_presence()) ||
730         (app_data_->widget_info() != NULL &&
731          app_data_->widget_info()->view_modes() == "windowed")) {
732       if (!view->Backward()) {
733         RemoveWebViewFromStack(view_stack_.front());
734       }
735     }
736   } else if (enabled && kKeyNameMenu == keyname) {
737     view->EvalJavascript(kMenuKeyEventScript);
738   }
739 }
740
741 void WebApplication::OnLanguageChanged() {
742   lang_changed_mode_ = true;
743   locale_manager_->UpdateSystemLocale();
744   ewk_context_cache_clear(ewk_context_);
745   auto it = view_stack_.begin();
746   for (; it != view_stack_.end(); ++it) {
747     (*it)->Reload();
748   }
749 }
750
751 void WebApplication::OnConsoleMessage(const std::string& msg, int level) {
752   static bool enabled = (getenv(kConsoleLogEnableKey) != NULL);
753   enabled = true;
754
755   std::string split_msg = msg;
756   std::size_t pos = msg.find(kResWgtPath);
757   if (pos != std::string::npos) {
758     split_msg = msg.substr(pos + strlen(kResWgtPath));
759   }
760
761   if (debug_mode_ || verbose_mode_ || enabled) {
762     int dlog_level = DLOG_DEBUG;
763     switch (level) {
764       case EWK_CONSOLE_MESSAGE_LEVEL_WARNING:
765         dlog_level = DLOG_WARN;
766         break;
767       case EWK_CONSOLE_MESSAGE_LEVEL_ERROR:
768         dlog_level = DLOG_ERROR;
769         break;
770       default:
771         dlog_level = DLOG_DEBUG;
772         break;
773     }
774     LOGGER_RAW(dlog_level, kConsoleMessageLogTag)
775       << "[" << app_data_->pkg_id() << "] " << split_msg;
776   }
777 }
778
779 void WebApplication::OnLowMemory() {
780   ewk_context_cache_clear(ewk_context_);
781   ewk_context_notify_low_memory(ewk_context_);
782 }
783
784 void WebApplication::OnSoftKeyboardChangeEvent(WebView* /*view*/,
785                                SoftKeyboardChangeEventValue softkeyboard_value) {
786   LOGGER(DEBUG) << "OnSoftKeyboardChangeEvent";
787   std::stringstream script;
788   script
789     << "(function(){"
790     << "var __event = document.createEvent(\"CustomEvent\");\n"
791     << "var __detail = {};\n"
792     << "__event.initCustomEvent(\"softkeyboardchange\",true,true,__detail);\n"
793     << "__event.state = \"" << softkeyboard_value.state << "\";\n"
794     << "__event.width = " << softkeyboard_value.width << ";\n"
795     << "__event.height = " << softkeyboard_value.height << ";\n"
796     << "document.dispatchEvent(__event);\n"
797     << "\n"
798     << "for (var i=0; i < window.frames.length; i++)\n"
799     << "{ window.frames[i].document.dispatchEvent(__event); }"
800     << "})()";
801   std::string kSoftKeyboardScript = script.str();
802   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
803     view_stack_.front()->EvalJavascript(kSoftKeyboardScript.c_str());
804 }
805
806 #ifdef ROTARY_EVENT_FEATURE_SUPPORT
807 void WebApplication::OnRotaryEvent(WebView* /*view*/,
808                                    RotaryEventType type) {
809   LOGGER(DEBUG) << "OnRotaryEvent";
810   std::stringstream script;
811   script
812     << "(function(){"
813     << "var __event = document.createEvent(\"CustomEvent\");\n"
814     << "var __detail = {};\n"
815     << "__event.initCustomEvent(\"rotarydetent\", true, true, __detail);\n"
816     << "__event.detail.direction = \""
817     << (type == RotaryEventType::CLOCKWISE ? "CW" : "CCW")
818     << "\";\n"
819     << "document.dispatchEvent(__event);\n"
820     << "\n"
821     << "for (var i=0; i < window.frames.length; i++)\n"
822     << "{ window.frames[i].document.dispatchEvent(__event); }"
823     << "})()";
824   std::string kRotaryEventScript = script.str();
825   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
826     view_stack_.front()->EvalJavascript(kRotaryEventScript.c_str());
827 }
828 #endif  // ROTARY_EVENT_FEATURE_SUPPORT
829
830 void WebApplication::OnTimeTick(long time) {
831 #if 0
832   LOGGER(DEBUG) << "TimeTick";
833   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
834     view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
835 #endif
836 }
837
838 void WebApplication::OnAmbientTick(long time) {
839   LOGGER(DEBUG) << "AmbientTick";
840   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
841     view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
842 }
843
844 void WebApplication::OnAmbientChanged(bool ambient_mode) {
845   LOGGER(DEBUG) << "AmbientChanged";
846   std::stringstream script;
847   script
848     << "(function(){"
849     << "var __event = document.createEvent(\"CustomEvent\");\n"
850     << "var __detail = {};\n"
851     << "__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);\n"
852     << "__event.detail.ambientMode = "
853     << (ambient_mode ? "true" : "false") << ";\n"
854     << "document.dispatchEvent(__event);\n"
855     << "\n"
856     << "for (var i=0; i < window.frames.length; i++)\n"
857     << "{ window.frames[i].document.dispatchEvent(__event); }"
858     << "})()";
859   std::string kAmbientChangedEventScript = script.str();
860   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
861     view_stack_.front()->EvalJavascript(kAmbientChangedEventScript.c_str());
862 }
863
864 bool WebApplication::OnContextMenuDisabled(WebView* /*view*/) {
865   return !(app_data_->setting_info() != NULL
866                ? app_data_->setting_info()->context_menu_enabled()
867                : true);
868 }
869
870 void WebApplication::OnLoadStart(WebView* /*view*/) {
871   LOGGER(DEBUG) << "LoadStart";
872 }
873
874 void WebApplication::OnLoadFinished(WebView* /*view*/) {
875   LOGGER(DEBUG) << "LoadFinished";
876   splash_screen_->HideSplashScreen(SplashScreen::HideReason::LOADFINISHED);
877 }
878
879 void WebApplication::OnRendered(WebView* view) {
880   STEP_PROFILE_END("URL Set -> Rendered");
881   STEP_PROFILE_END("Start -> Launch Completed");
882   LOGGER(DEBUG) << "Rendered";
883   splash_screen_->HideSplashScreen(SplashScreen::HideReason::RENDERED);
884
885   // Do not show(), active() for language change
886   if(lang_changed_mode_ == false){
887       // Show window after frame rendered.
888 #ifdef PROFILE_MOBILE
889       if (common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
890         window_->Show();
891         window_->Active();
892       }
893 #else  // PROFILE_MOBILE
894       window_->Show();
895       window_->Active();
896 #endif
897   }
898   else{
899       lang_changed_mode_ = false;
900   }
901 }
902
903 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
904 void WebApplication::OnRotatePrepared(WebView* /*view*/) {
905   window_->ManualRotationDone();
906 }
907 #endif  // MANUAL_ROTATE_FEATURE_SUPPORT
908
909 void WebApplication::LaunchInspector(common::AppControl* appcontrol) {
910   unsigned int port = ewk_context_inspector_server_start(ewk_context_, 0);
911   std::stringstream ss;
912   ss << port;
913   std::map<std::string, std::vector<std::string>> data;
914   data[kPortKey] = {ss.str()};
915   appcontrol->Reply(data);
916 }
917
918 void WebApplication::SetupWebView(WebView* view) {
919   view->SetEventListener(this);
920
921   // Setup CSP Rule
922   if (security_model_version_ == 2) {
923     view->SetCSPRule(csp_rule_, false);
924     if (!csp_report_rule_.empty()) {
925       view->SetCSPRule(csp_report_rule_, true);
926     }
927   }
928
929 // Setup longpolling value
930   if (app_data_->setting_info() != NULL &&
931      app_data_->setting_info()->long_polling()) {
932     boost::optional <unsigned int> polling_val(app_data_->setting_info()->long_polling());
933     unsigned long *ptr =  reinterpret_cast <unsigned long *> (&polling_val.get());
934     view->SetLongPolling(*ptr);
935   }
936 }
937
938 bool WebApplication::OnDidNavigation(WebView* /*view*/,
939                                      const std::string& url) {
940   // scheme handling
941   // except(file , http, https, app) pass to appcontrol and return false
942   if (ProcessWellKnownScheme(url)) {
943     return false;
944   }
945
946   // send launch request for blocked URL to guarrenty backward-compatibility.
947   if (resource_manager_->AllowNavigation(url)) {
948     return true;
949   } else {
950     LOGGER(DEBUG) << "URL is blocked. send launch request for URL : " << url;
951     std::unique_ptr<common::AppControl> request(
952       common::AppControl::MakeAppcontrolFromURL(url));
953     if (request.get() == NULL || !request->LaunchRequest()) {
954       LOGGER(ERROR) << "Fail to send appcontrol request";
955     }
956     return false;
957   }
958 }
959
960 void WebApplication::OnNotificationPermissionRequest(
961     WebView*, const std::string& url,
962     std::function<void(bool)> result_handler) {
963   auto db = common::AppDB::GetInstance();
964   std::string reminder =
965       db->Get(kDBPrivateSection, kNotificationPermissionPrefix + url);
966   if (reminder == "allowed") {
967     result_handler(true);
968     return;
969   } else if (reminder == "denied") {
970     result_handler(false);
971     return;
972   }
973
974   // Local Domain: Grant permission if defined, otherwise Popup user prompt.
975   // Remote Domain: Popup user prompt.
976   if (common::utils::StartsWith(url, "file://") &&
977       FindPrivilege(app_data_, kNotificationPrivilege)) {
978     result_handler(true);
979     return;
980   }
981
982   Popup* popup = Popup::CreatePopup(window_);
983   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
984   popup->SetTitle(popup_string::kPopupTitleWebNotification);
985   popup->SetBody(popup_string::kPopupBodyWebNotification);
986   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
987   popup->SetResultHandler(
988       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
989         bool result = popup->GetButtonResult();
990         bool remember = popup->GetCheckBoxResult();
991         if (remember) {
992           db->Set(kDBPrivateSection, kNotificationPermissionPrefix + url,
993                   result ? "allowed" : "denied");
994         }
995         result_handler(result);
996       },
997       this);
998   popup->Show();
999 }
1000
1001 void WebApplication::OnGeolocationPermissionRequest(
1002     WebView*, const std::string& url,
1003     std::function<void(bool)> result_handler) {
1004   auto db = common::AppDB::GetInstance();
1005   std::string reminder =
1006       db->Get(kDBPrivateSection, kGeolocationPermissionPrefix + url);
1007   if (reminder == "allowed") {
1008     result_handler(true);
1009     return;
1010   } else if (reminder == "denied") {
1011     result_handler(false);
1012     return;
1013   }
1014
1015   // Local Domain: Grant permission if defined, otherwise block execution.
1016   // Remote Domain: Popup user prompt if defined, otherwise block execution.
1017   if (!FindPrivilege(app_data_, kLocationPrivilege)) {
1018     result_handler(false);
1019     return;
1020   }
1021
1022   if (common::utils::StartsWith(url, "file://")) {
1023     result_handler(true);
1024     return;
1025   }
1026
1027   Popup* popup = Popup::CreatePopup(window_);
1028   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1029   popup->SetTitle(popup_string::kPopupTitleGeoLocation);
1030   popup->SetBody(popup_string::kPopupBodyGeoLocation);
1031   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1032   popup->SetResultHandler(
1033       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1034         bool result = popup->GetButtonResult();
1035         bool remember = popup->GetCheckBoxResult();
1036         if (remember) {
1037           db->Set(kDBPrivateSection, kGeolocationPermissionPrefix + url,
1038                   result ? "allowed" : "denied");
1039         }
1040         result_handler(result);
1041       },
1042       this);
1043   popup->Show();
1044 }
1045
1046 void WebApplication::OnQuotaExceed(WebView*, const std::string& url,
1047                                    std::function<void(bool)> result_handler) {
1048   auto db = common::AppDB::GetInstance();
1049   std::string reminder =
1050       db->Get(kDBPrivateSection, kQuotaPermissionPrefix + url);
1051   if (reminder == "allowed") {
1052     result_handler(true);
1053     return;
1054   } else if (reminder == "denied") {
1055     result_handler(false);
1056     return;
1057   }
1058
1059   // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1060   // Remote Domain: Popup user prompt.
1061   if (common::utils::StartsWith(url, "file://") &&
1062       FindPrivilege(app_data_, kStoragePrivilege)) {
1063     result_handler(true);
1064     return;
1065   }
1066
1067   Popup* popup = Popup::CreatePopup(window_);
1068   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1069   popup->SetTitle(popup_string::kPopupTitleWebStorage);
1070   popup->SetBody(popup_string::kPopupBodyWebStorage);
1071   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1072   popup->SetResultHandler(
1073       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1074         bool result = popup->GetButtonResult();
1075         bool remember = popup->GetCheckBoxResult();
1076         if (remember) {
1077           db->Set(kDBPrivateSection, kQuotaPermissionPrefix + url,
1078                   result ? "allowed" : "denied");
1079         }
1080         result_handler(result);
1081       },
1082       this);
1083   popup->Show();
1084 }
1085
1086 void WebApplication::OnAuthenticationRequest(
1087     WebView*, const std::string& /*url*/, const std::string& /*message*/,
1088     std::function<void(bool submit, const std::string& id,
1089                        const std::string& password)> result_handler) {
1090   Popup* popup = Popup::CreatePopup(window_);
1091   popup->SetButtonType(Popup::ButtonType::LoginCancelButton);
1092   popup->SetFirstEntry(popup_string::kPopupLabelAuthusername,
1093                        Popup::EntryType::Edit);
1094   popup->SetSecondEntry(popup_string::kPopupLabelPassword,
1095                         Popup::EntryType::PwEdit);
1096   popup->SetTitle(popup_string::kPopupTitleAuthRequest);
1097   popup->SetBody(popup_string::kPopupBodyAuthRequest);
1098   popup->SetResultHandler([result_handler](Popup* popup, void* /*user_data*/) {
1099                             bool result = popup->GetButtonResult();
1100                             std::string id = popup->GetFirstEntryResult();
1101                             std::string passwd = popup->GetSecondEntryResult();
1102                             result_handler(result, id, passwd);
1103                           },
1104                           this);
1105   popup->Show();
1106 }
1107
1108 void WebApplication::OnCertificateAllowRequest(
1109     WebView*, const std::string& url, const std::string& pem,
1110     std::function<void(bool allow)> result_handler) {
1111   auto db = common::AppDB::GetInstance();
1112   std::string reminder =
1113       db->Get(kDBPrivateSection, kCertificateAllowPrefix + pem);
1114   if (reminder == "allowed") {
1115     result_handler(true);
1116     return;
1117   } else if (reminder == "denied") {
1118     result_handler(false);
1119     return;
1120   }
1121
1122   Popup* popup = Popup::CreatePopup(window_);
1123   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1124   popup->SetTitle(popup_string::kPopupTitleCert);
1125   popup->SetBody(popup_string::GetText(
1126                  popup_string::kPopupBodyCert) + "\n\n" + url);
1127   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1128   popup->SetResultHandler(
1129       [db, result_handler, pem](Popup* popup, void* /*user_data*/) {
1130         bool result = popup->GetButtonResult();
1131         bool remember = popup->GetCheckBoxResult();
1132         if (remember) {
1133           db->Set(kDBPrivateSection, kCertificateAllowPrefix + pem,
1134                   result ? "allowed" : "denied");
1135         }
1136         result_handler(result);
1137       },
1138       this);
1139   popup->Show();
1140 }
1141
1142 void WebApplication::OnUsermediaPermissionRequest(
1143     WebView*, const std::string& url,
1144     std::function<void(bool)> result_handler) {
1145   auto db = common::AppDB::GetInstance();
1146   std::string reminder =
1147       db->Get(kDBPrivateSection, kUsermediaPermissionPrefix + url);
1148   if (reminder == "allowed") {
1149     result_handler(true);
1150     return;
1151   } else if (reminder == "denied") {
1152     result_handler(false);
1153     return;
1154   }
1155
1156   // Local Domain: Grant permission if defined, otherwise block execution.
1157   // Remote Domain: Popup user prompt if defined, otherwise block execution.
1158   if (!FindPrivilege(app_data_, kUsermediaPrivilege)) {
1159     result_handler(false);
1160     return;
1161   }
1162
1163   if (common::utils::StartsWith(url, "file://")) {
1164     result_handler(true);
1165     return;
1166   }
1167
1168   Popup* popup = Popup::CreatePopup(window_);
1169   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1170   popup->SetTitle(popup_string::kPopupTitleUserMedia);
1171   popup->SetBody(popup_string::kPopupBodyUserMedia);
1172   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1173   popup->SetResultHandler(
1174       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1175         bool result = popup->GetButtonResult();
1176         bool remember = popup->GetCheckBoxResult();
1177         if (remember) {
1178           db->Set(kDBPrivateSection, kUsermediaPermissionPrefix + url,
1179                   result ? "allowed" : "denied");
1180         }
1181         result_handler(result);
1182       },
1183       this);
1184   popup->Show();
1185 }
1186
1187 }  // namespace runtime