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