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