Locate ewk_view_page_close in extended main loop
[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::ClosePage() {
663   LOGGER(DEBUG);
664   int valid_evas_object_count = 0;
665   auto it = view_stack_.begin();
666   if (it != view_stack_.end()) {
667     for (; it != view_stack_.end(); ++it) {
668       (*it)->ReplyToJavascriptDialog();
669       view_stack_.front()->SetVisibility(false);
670       if (ewk_view_page_close((*it)->evas_object())) {
671         LOGGER(DEBUG) << "ewk_view_page_close returns true";
672         valid_evas_object_count++;
673       } else {
674         LOGGER(DEBUG) << "ewk_view_page_close returns false";
675       }
676     }
677   }
678   if (valid_evas_object_count == 0) {
679     if (is_terminate_called_) {
680       ecore_main_loop_quit();
681     } else {
682       if (terminator_) {
683         LOGGER(DEBUG) << "terminator_";
684         terminator_();
685       } else {
686         LOGGER(ERROR) << "There's no registered terminator.";
687         elm_exit();
688       }
689     }
690   }
691 }
692
693 void WebApplication::OnCreatedNewWebView(WebView* /*view*/, WebView* new_view) {
694   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
695     view_stack_.front()->SetVisibility(false);
696
697   SetupWebView(new_view);
698   SetupWebViewCompatibilitySettings(new_view);
699   view_stack_.push_front(new_view);
700   window_->SetContent(new_view->evas_object());
701 }
702
703 void WebApplication::RemoveWebViewFromStack(WebView* view) {
704   if (view_stack_.size() == 0)
705     return;
706
707   WebView* current = view_stack_.front();
708   if (current == view) {
709     // In order to prevent the crash issue due to the callback
710     // which occur after destroying WebApplication class,
711     // we have to set the 'SetEventListener' to NULL.
712     view->SetEventListener(NULL);
713     view_stack_.pop_front();
714   } else {
715     auto found = std::find(view_stack_.begin(), view_stack_.end(), view);
716     if (found != view_stack_.end()) {
717       // In order to prevent the crash issue due to the callback
718       // which occur after destroying WebApplication class,
719       // we have to set the 'SetEventListener' to NULL.
720       view->SetEventListener(NULL);
721       view_stack_.erase(found);
722     }
723   }
724
725   if (view_stack_.size() == 0) {
726     // If |Terminate()| hasn't been called,
727     // main loop shouldn't be terminated here.
728     if (!is_terminate_called_) {
729       auto extension_server = XWalkExtensionServer::GetInstance();
730       LOGGER(DEBUG) << "Shutdown extension server";
731       extension_server->Shutdown();
732     }
733   } else if (current != view_stack_.front()) {
734     view_stack_.front()->SetVisibility(true);
735     window_->SetContent(view_stack_.front()->evas_object());
736   }
737
738   // Delete after the callback context(for ewk view) was not used
739   ecore_idler_add([](void* view) {
740                     WebView* obj = static_cast<WebView*>(view);
741                     delete obj;
742                     return EINA_FALSE;
743                   },
744                   view);
745 }
746
747 Eina_Bool WebApplication::CheckPluginSession(void* user_data)
748 {
749   WebApplication* that = static_cast<WebApplication*>(user_data);
750   if(XWalkExtensionRendererController::plugin_session_count > 0) {
751     LOGGER(ERROR) << "plugin_session_count : " <<
752         XWalkExtensionRendererController::plugin_session_count;
753     return ECORE_CALLBACK_RENEW;
754   }
755   LOGGER(DEBUG) << "plugin_session_count : " <<
756       XWalkExtensionRendererController::plugin_session_count;
757   LOGGER(DEBUG) << "Execute deferred termination of main loop";
758   if (that->is_terminate_called_) {
759     ecore_main_loop_quit();
760   } else {
761     if (that->terminator_) {
762       LOGGER(DEBUG) << "terminator_";
763       that->terminator_();
764     } else {
765       LOGGER(ERROR) << "There's no registered terminator.";
766       elm_exit();
767     }
768   }
769   return ECORE_CALLBACK_CANCEL;
770 }
771
772 void WebApplication::OnClosedWebView(WebView* view) {
773   Ecore_Timer* timeout_id = NULL;
774   // Reply to javascript dialog for preventing freeze issue.
775   view->ReplyToJavascriptDialog();
776   RemoveWebViewFromStack(view);
777
778   LOGGER(DEBUG) << "plugin_session_count : " <<
779       XWalkExtensionRendererController::plugin_session_count;
780   if (!ecore_timer_add(TIMER_INTERVAL, CheckPluginSession, this))
781     LOGGER(ERROR) << "It's failed to create timer";
782 }
783
784 void WebApplication::OnReceivedWrtMessage(WebView* view,
785                                           Ewk_IPC_Wrt_Message_Data* msg) {
786   Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg);
787
788 #define TYPE_BEGIN(x) (!strncmp(msg_type, x, strlen(x)))
789 #define TYPE_IS(x) (!strcmp(msg_type, x))
790
791   if (TYPE_BEGIN("xwalk://")) {
792     auto extension_server = extensions::XWalkExtensionServer::GetInstance();
793     extension_server->HandleIPCMessage(msg);
794   } else {
795     Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg);
796     Eina_Stringshare* msg_ref_id =
797         ewk_ipc_wrt_message_data_reference_id_get(msg);
798     Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg);
799
800     if (TYPE_IS("tizen://hide")) {
801       // One Way Message
802       window_->InActive();
803     } else if (TYPE_IS("tizen://exit")) {
804       // One Way Message
805       // Reply to javascript dialog for preventing freeze issue.
806       view->ReplyToJavascriptDialog();
807       ecore_idler_add(ExitAppIdlerCallback, this);
808     } else if (TYPE_IS("tizen://changeUA")) {
809       // Async Message
810       // Change UserAgent of current WebView
811       bool ret = false;
812       if (view_stack_.size() > 0 && view_stack_.front() != NULL) {
813         ret = view_stack_.front()->SetUserAgent(std::string(msg_value));
814       }
815       // Send response
816       Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
817       ewk_ipc_wrt_message_data_type_set(ans, msg_type);
818       ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
819       if (ret)
820         ewk_ipc_wrt_message_data_value_set(ans, "success");
821       else
822         ewk_ipc_wrt_message_data_value_set(ans, "failed");
823       if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
824         LOGGER(ERROR) << "Failed to send response";
825       }
826       ewk_ipc_wrt_message_data_del(ans);
827     } else if (TYPE_IS("tizen://deleteAllCookies")) {
828       Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
829       ewk_ipc_wrt_message_data_type_set(ans, msg_type);
830       ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
831       if (ClearCookie(ewk_context_))
832         ewk_ipc_wrt_message_data_value_set(ans, "success");
833       else
834         ewk_ipc_wrt_message_data_value_set(ans, "failed");
835       if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
836         LOGGER(ERROR) << "Failed to send response";
837       }
838       ewk_ipc_wrt_message_data_del(ans);
839     } else if (TYPE_IS("tizen://hide_splash_screen")) {
840       splash_screen_->HideSplashScreen(SplashScreen::HideReason::CUSTOM);
841     }
842
843     eina_stringshare_del(msg_ref_id);
844     eina_stringshare_del(msg_id);
845     eina_stringshare_del(msg_value);
846   }
847
848 #undef TYPE_IS
849 #undef TYPE_BEGIN
850
851   eina_stringshare_del(msg_type);
852 }
853
854 void WebApplication::OnOrientationLock(
855     WebView* view, bool lock,
856     NativeWindow::ScreenOrientation preferred_rotation) {
857   if (view_stack_.size() == 0) return;
858
859   // Only top-most view can set the orientation relate operation
860   if (view_stack_.front() != view) return;
861
862   // This is for 2.4 compatibility. Requested by Webengine Team.
863   //
864   // In Tizen 2.4 WebKit locking screen orientation was possible with Web API
865   // screen.lockOrientation(). This API was deprecated and replaced with
866   // screen.orientation.lock(). But for compatibility case we need to support
867   // old API.
868   if(!tizenWebKitCompatibilityEnabled()) {
869     auto orientaion_setting = app_data_->setting_info() != NULL
870         ? app_data_->setting_info()->screen_orientation()
871         : wgt::parse::SettingInfo::ScreenOrientation::AUTO;
872     if (wgt::parse::SettingInfo::ScreenOrientation::AUTO != orientaion_setting) {
873         // If Tizen version is 3.0, it return.
874         return;
875     }
876   }
877
878   if (lock) {
879     window_->SetRotationLock(preferred_rotation);
880   } else {
881     window_->SetAutoRotation();
882   }
883 }
884
885 void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
886   // NOTE: This code is added to enable back-key on remote URL
887   bool enabled = app_data_->setting_info() != NULL
888                      ? app_data_->setting_info()->hwkey_enabled()
889                      : true;
890
891   if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
892     if (kKeyNameBack == keyname) {
893       LOGGER(DEBUG) << "Back to previous page for remote URL";
894       if(enabled)
895         view->EvalJavascript(kBackKeyEventScript);
896       if (!view->Backward()) {
897         LOGGER(DEBUG) << "Terminate";
898         Terminate();
899       }
900     }
901     return;
902   }
903
904   if (enabled && kKeyNameBack == keyname) {
905     view->EvalJavascript(kBackKeyEventScript);
906     // NOTE: This code is added for backward compatibility.
907     // If the 'backbutton_presence' is true, WebView should be navigated back.
908     if ((app_data_->setting_info() != NULL &&
909          app_data_->setting_info()->backbutton_presence()) ||
910         (app_data_->widget_info() != NULL &&
911          app_data_->widget_info()->view_modes() == "windowed")) {
912       if (!view->Backward()) {
913         LOGGER(DEBUG) << "Terminate";
914         Terminate();
915       }
916     }
917   } else if (enabled && kKeyNameMenu == keyname) {
918     view->EvalJavascript(kMenuKeyEventScript);
919   }
920 }
921
922 void WebApplication::OnLanguageChanged() {
923   lang_changed_mode_ = true;
924   locale_manager_->UpdateSystemLocale();
925   ewk_context_cache_clear(ewk_context_);
926   auto it = view_stack_.begin();
927   for (; it != view_stack_.end(); ++it) {
928     (*it)->Reload();
929   }
930 }
931
932 void WebApplication::OnConsoleMessage(const std::string& msg, int level) {
933   static bool enabled = (getenv(kConsoleLogEnableKey) != NULL);
934   enabled = true;
935
936   std::string split_msg = msg;
937   std::size_t pos = msg.find(kResWgtPath);
938   if (pos != std::string::npos) {
939     split_msg = msg.substr(pos + strlen(kResWgtPath));
940   }
941
942   if (debug_mode_ || verbose_mode_ || enabled) {
943     int dlog_level = DLOG_DEBUG;
944     switch (level) {
945       case EWK_CONSOLE_MESSAGE_LEVEL_WARNING:
946         dlog_level = DLOG_WARN;
947         break;
948       case EWK_CONSOLE_MESSAGE_LEVEL_ERROR:
949         dlog_level = DLOG_ERROR;
950         break;
951       default:
952         dlog_level = DLOG_DEBUG;
953         break;
954     }
955     LOGGER_RAW(dlog_level, kConsoleMessageLogTag)
956       << "[" << app_data_->pkg_id() << "] " << split_msg;
957   }
958 }
959
960 void WebApplication::OnLowMemory() {
961   ewk_context_cache_clear(ewk_context_);
962   ewk_context_notify_low_memory(ewk_context_);
963 }
964
965 void WebApplication::OnSoftKeyboardChangeEvent(WebView* /*view*/,
966                                SoftKeyboardChangeEventValue softkeyboard_value) {
967   LOGGER(DEBUG) << "OnSoftKeyboardChangeEvent";
968   std::stringstream script;
969   script
970     << "(function(){"
971     << "var __event = document.createEvent(\"CustomEvent\");\n"
972     << "var __detail = {};\n"
973     << "__event.initCustomEvent(\"softkeyboardchange\",true,true,__detail);\n"
974     << "__event.state = \"" << softkeyboard_value.state << "\";\n"
975     << "__event.width = " << softkeyboard_value.width << ";\n"
976     << "__event.height = " << softkeyboard_value.height << ";\n"
977     << "document.dispatchEvent(__event);\n"
978     << "\n"
979     << "for (var i=0; i < window.frames.length; i++)\n"
980     << "{ window.frames[i].document.dispatchEvent(__event); }"
981     << "})()";
982   std::string kSoftKeyboardScript = script.str();
983   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
984     view_stack_.front()->EvalJavascript(kSoftKeyboardScript.c_str());
985 }
986
987 #ifdef ROTARY_EVENT_FEATURE_SUPPORT
988 void WebApplication::OnRotaryEvent(WebView* /*view*/,
989                                    RotaryEventType type) {
990   LOGGER(DEBUG) << "OnRotaryEvent";
991   std::stringstream script;
992   script
993     << "(function(){"
994     << "var __event = document.createEvent(\"CustomEvent\");\n"
995     << "var __detail = {};\n"
996     << "__event.initCustomEvent(\"rotarydetent\", true, true, __detail);\n"
997     << "__event.detail.direction = \""
998     << (type == RotaryEventType::CLOCKWISE ? "CW" : "CCW")
999     << "\";\n"
1000     << "document.dispatchEvent(__event);\n"
1001     << "\n"
1002     << "for (var i=0; i < window.frames.length; i++)\n"
1003     << "{ window.frames[i].document.dispatchEvent(__event); }"
1004     << "})()";
1005   std::string kRotaryEventScript = script.str();
1006   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1007     view_stack_.front()->EvalJavascript(kRotaryEventScript.c_str());
1008 }
1009 #endif  // ROTARY_EVENT_FEATURE_SUPPORT
1010
1011 void WebApplication::OnTimeTick(long time) {
1012 #if 0
1013   LOGGER(DEBUG) << "TimeTick";
1014   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1015     view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1016 #endif
1017 }
1018
1019 void WebApplication::OnAmbientTick(long time) {
1020   LOGGER(DEBUG) << "AmbientTick";
1021   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1022     view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1023 }
1024
1025 void WebApplication::OnAmbientChanged(bool ambient_mode) {
1026   LOGGER(DEBUG) << "AmbientChanged";
1027   std::stringstream script;
1028   script
1029     << "(function(){"
1030     << "var __event = document.createEvent(\"CustomEvent\");\n"
1031     << "var __detail = {};\n"
1032     << "__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);\n"
1033     << "__event.detail.ambientMode = "
1034     << (ambient_mode ? "true" : "false") << ";\n"
1035     << "document.dispatchEvent(__event);\n"
1036     << "\n"
1037     << "for (var i=0; i < window.frames.length; i++)\n"
1038     << "{ window.frames[i].document.dispatchEvent(__event); }"
1039     << "})()";
1040   std::string kAmbientChangedEventScript = script.str();
1041   if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1042     view_stack_.front()->EvalJavascript(kAmbientChangedEventScript.c_str());
1043 }
1044
1045 bool WebApplication::OnContextMenuDisabled(WebView* /*view*/) {
1046   return !(app_data_->setting_info() != NULL
1047                ? app_data_->setting_info()->context_menu_enabled()
1048                : true);
1049 }
1050
1051 void WebApplication::OnLoadStart(WebView* /*view*/) {
1052   LOGGER(DEBUG) << "LoadStart";
1053 }
1054
1055 void WebApplication::OnLoadFinished(WebView* /*view*/) {
1056   LOGGER(DEBUG) << "LoadFinished";
1057   splash_screen_->HideSplashScreen(SplashScreen::HideReason::LOADFINISHED);
1058 }
1059
1060 void WebApplication::OnRendered(WebView* view) {
1061   STEP_PROFILE_END("URL Set -> Rendered");
1062   STEP_PROFILE_END("Start -> Launch Completed");
1063   LOGGER(DEBUG) << "Rendered";
1064   splash_screen_->HideSplashScreen(SplashScreen::HideReason::RENDERED);
1065
1066   // Do not show(), active() for language change
1067   if(lang_changed_mode_ == false){
1068       // Show window after frame rendered.
1069 #ifdef PROFILE_MOBILE
1070       if (common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
1071         window_->Show();
1072         window_->Active();
1073       }
1074 #else  // PROFILE_MOBILE
1075       window_->Show();
1076       window_->Active();
1077 #endif
1078   }
1079   else{
1080       lang_changed_mode_ = false;
1081   }
1082 }
1083
1084 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
1085 void WebApplication::OnRotatePrepared(WebView* /*view*/) {
1086   window_->ManualRotationDone();
1087 }
1088 #endif  // MANUAL_ROTATE_FEATURE_SUPPORT
1089
1090 void WebApplication::LaunchInspector(common::AppControl* appcontrol) {
1091   unsigned int port = ewk_context_inspector_server_start(ewk_context_, 0);
1092   std::stringstream ss;
1093   ss << port;
1094   std::map<std::string, std::vector<std::string>> data;
1095   data[kPortKey] = {ss.str()};
1096   appcontrol->Reply(data);
1097 }
1098
1099 void WebApplication::SetupWebView(WebView* view) {
1100   view->SetEventListener(this);
1101
1102   // Setup CSP Rule
1103   if (security_model_version_ == 2) {
1104     view->SetCSPRule(csp_rule_, false);
1105     if (!csp_report_rule_.empty()) {
1106       view->SetCSPRule(csp_report_rule_, true);
1107     }
1108   }
1109
1110 // Setup longpolling value
1111   if (app_data_->setting_info() != NULL &&
1112      app_data_->setting_info()->long_polling()) {
1113     boost::optional <unsigned int> polling_val(app_data_->setting_info()->long_polling());
1114     unsigned long *ptr =  reinterpret_cast <unsigned long *> (&polling_val.get());
1115     view->SetLongPolling(*ptr);
1116   }
1117 }
1118
1119 void WebApplication::SetupWebViewCompatibilitySettings(WebView* view) {
1120   if (tizenWebKitCompatibilityEnabled()) {
1121     Ewk_Settings* settings = ewk_view_settings_get(view->evas_object());
1122     ewk_settings_tizen_compatibility_mode_set(settings,
1123             m_tizenCompatibilitySettings.m_major,
1124             m_tizenCompatibilitySettings.m_minor,
1125             m_tizenCompatibilitySettings.m_release);
1126     ewk_settings_text_autosizing_enabled_set(settings, EINA_FALSE);
1127   }
1128 }
1129
1130 bool WebApplication::OnDidNavigation(WebView* /*view*/,
1131                                      const std::string& url) {
1132   // scheme handling
1133   // except(file , http, https, app) pass to appcontrol and return false
1134   if (ProcessWellKnownScheme(url)) {
1135     return false;
1136   }
1137
1138   // send launch request for blocked URL to guarrenty backward-compatibility.
1139   if (resource_manager_->AllowNavigation(url)) {
1140     return true;
1141   } else {
1142     LOGGER(DEBUG) << "URL is blocked. send launch request for URL : " << url;
1143     std::unique_ptr<common::AppControl> request(
1144       common::AppControl::MakeAppcontrolFromURL(url));
1145     if (request.get() == NULL || !request->LaunchRequest()) {
1146       LOGGER(ERROR) << "Fail to send appcontrol request";
1147     }
1148     return false;
1149   }
1150 }
1151
1152 void WebApplication::OnNotificationPermissionRequest(
1153     WebView*, const std::string& url,
1154     std::function<void(bool)> result_handler) {
1155   auto db = common::AppDB::GetInstance();
1156   std::string reminder =
1157       db->Get(kDBPrivateSection, kNotificationPermissionPrefix + url);
1158   if (reminder == "allowed") {
1159     result_handler(true);
1160     return;
1161   } else if (reminder == "denied") {
1162     result_handler(false);
1163     return;
1164   }
1165
1166   // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1167   // Remote Domain: Popup user prompt.
1168   if (common::utils::StartsWith(url, "file://") &&
1169       FindPrivilegeFromConfig(app_data_, kNotificationPrivilege)) {
1170     result_handler(true);
1171     return;
1172   }
1173
1174   Popup* popup = Popup::CreatePopup(window_);
1175   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1176   popup->SetTitle(popup_string::kPopupTitleWebNotification);
1177   popup->SetBody(popup_string::kPopupBodyWebNotification);
1178   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1179   popup->SetResultHandler(
1180       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1181         bool result = popup->GetButtonResult();
1182         bool remember = popup->GetCheckBoxResult();
1183         if (remember) {
1184           db->Set(kDBPrivateSection, kNotificationPermissionPrefix + url,
1185                   result ? "allowed" : "denied");
1186         }
1187         result_handler(result);
1188       },
1189       this);
1190   popup->Show();
1191 }
1192
1193 void WebApplication::OnGeolocationPermissionRequest(
1194     WebView*, const std::string& url,
1195     std::function<void(bool)> result_handler) {
1196   auto db = common::AppDB::GetInstance();
1197   std::string reminder =
1198       db->Get(kDBPrivateSection, kGeolocationPermissionPrefix + url);
1199   if (reminder == "allowed") {
1200     result_handler(true);
1201     return;
1202   } else if (reminder == "denied") {
1203     result_handler(false);
1204     return;
1205   }
1206
1207   // Local Domain: Grant permission if defined, otherwise block execution.
1208   // Remote Domain: Popup user prompt if defined, otherwise block execution.
1209   if (!FindPrivilegeFromConfig(app_data_, kLocationPrivilege) &&
1210       !FindPrivilegeFromCynara(kLocationPrivilege)) {
1211     result_handler(false);
1212     return;
1213   }
1214
1215   if (common::utils::StartsWith(url, "file://")) {
1216     result_handler(true);
1217     return;
1218   }
1219
1220   Popup* popup = Popup::CreatePopup(window_);
1221   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1222   popup->SetTitle(popup_string::kPopupTitleGeoLocation);
1223   popup->SetBody(popup_string::kPopupBodyGeoLocation);
1224   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1225   popup->SetResultHandler(
1226       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1227         bool result = popup->GetButtonResult();
1228         bool remember = popup->GetCheckBoxResult();
1229         if (remember) {
1230           db->Set(kDBPrivateSection, kGeolocationPermissionPrefix + url,
1231                   result ? "allowed" : "denied");
1232         }
1233         result_handler(result);
1234       },
1235       this);
1236   popup->Show();
1237 }
1238
1239 void WebApplication::OnQuotaExceed(WebView*, const std::string& url,
1240                                    std::function<void(bool)> result_handler) {
1241   auto db = common::AppDB::GetInstance();
1242   std::string reminder =
1243       db->Get(kDBPrivateSection, kQuotaPermissionPrefix + url);
1244   if (reminder == "allowed") {
1245     result_handler(true);
1246     return;
1247   } else if (reminder == "denied") {
1248     result_handler(false);
1249     return;
1250   }
1251
1252   // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1253   // Remote Domain: Popup user prompt.
1254   if (common::utils::StartsWith(url, "file://") &&
1255       FindPrivilegeFromConfig(app_data_, kStoragePrivilege)) {
1256     result_handler(true);
1257     return;
1258   }
1259
1260   Popup* popup = Popup::CreatePopup(window_);
1261   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1262   popup->SetTitle(popup_string::kPopupTitleWebStorage);
1263   popup->SetBody(popup_string::kPopupBodyWebStorage);
1264   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1265   popup->SetResultHandler(
1266       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1267         bool result = popup->GetButtonResult();
1268         bool remember = popup->GetCheckBoxResult();
1269         if (remember) {
1270           db->Set(kDBPrivateSection, kQuotaPermissionPrefix + url,
1271                   result ? "allowed" : "denied");
1272         }
1273         result_handler(result);
1274       },
1275       this);
1276   popup->Show();
1277 }
1278
1279 void WebApplication::OnAuthenticationRequest(
1280     WebView*, const std::string& /*url*/, const std::string& /*message*/,
1281     std::function<void(bool submit, const std::string& id,
1282                        const std::string& password)> result_handler) {
1283   Popup* popup = Popup::CreatePopup(window_);
1284   popup->SetButtonType(Popup::ButtonType::LoginCancelButton);
1285   popup->SetFirstEntry(popup_string::kPopupLabelAuthusername,
1286                        Popup::EntryType::Edit);
1287   popup->SetSecondEntry(popup_string::kPopupLabelPassword,
1288                         Popup::EntryType::PwEdit);
1289   popup->SetTitle(popup_string::kPopupTitleAuthRequest);
1290   popup->SetBody(popup_string::kPopupBodyAuthRequest);
1291   popup->SetResultHandler([result_handler](Popup* popup, void* /*user_data*/) {
1292                             bool result = popup->GetButtonResult();
1293                             std::string id = popup->GetFirstEntryResult();
1294                             std::string passwd = popup->GetSecondEntryResult();
1295                             result_handler(result, id, passwd);
1296                           },
1297                           this);
1298   popup->Show();
1299 }
1300
1301 void WebApplication::OnCertificateAllowRequest(
1302     WebView*, const std::string& url, const std::string& pem,
1303     std::function<void(bool allow)> result_handler) {
1304   auto db = common::AppDB::GetInstance();
1305   std::string reminder =
1306       db->Get(kDBPrivateSection, kCertificateAllowPrefix + pem);
1307   if (reminder == "allowed") {
1308     result_handler(true);
1309     return;
1310   } else if (reminder == "denied") {
1311     result_handler(false);
1312     return;
1313   }
1314
1315   Popup* popup = Popup::CreatePopup(window_);
1316   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1317   popup->SetTitle(popup_string::kPopupTitleCert);
1318   popup->SetBody(popup_string::GetText(
1319                  popup_string::kPopupBodyCert) + "\n\n" + url);
1320   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1321   popup->SetResultHandler(
1322       [db, result_handler, pem](Popup* popup, void* /*user_data*/) {
1323         bool result = popup->GetButtonResult();
1324         bool remember = popup->GetCheckBoxResult();
1325         if (remember) {
1326           db->Set(kDBPrivateSection, kCertificateAllowPrefix + pem,
1327                   result ? "allowed" : "denied");
1328         }
1329         result_handler(result);
1330       },
1331       this);
1332   popup->Show();
1333 }
1334
1335 void WebApplication::OnUsermediaPermissionRequest(
1336     WebView*, const std::string& url,
1337     std::function<void(bool)> result_handler) {
1338   auto db = common::AppDB::GetInstance();
1339   std::string reminder =
1340       db->Get(kDBPrivateSection, kUsermediaPermissionPrefix + url);
1341   if (reminder == "allowed") {
1342     result_handler(true);
1343     return;
1344   } else if (reminder == "denied") {
1345     result_handler(false);
1346     return;
1347   }
1348
1349   // Local Domain: Grant permission if defined, otherwise block execution.
1350   // Remote Domain: Popup user prompt if defined, otherwise block execution.
1351   if (!FindPrivilegeFromConfig(app_data_, kUsermediaPrivilege) &&
1352       !(FindPrivilegeFromCynara(kCameraPrivilege) && FindPrivilegeFromCynara(kRecordPrivilege))) {
1353     result_handler(false);
1354     return;
1355   }
1356
1357   if (common::utils::StartsWith(url, "file://")) {
1358     result_handler(true);
1359     return;
1360   }
1361
1362   Popup* popup = Popup::CreatePopup(window_);
1363   popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1364   popup->SetTitle(popup_string::kPopupTitleUserMedia);
1365   popup->SetBody(popup_string::kPopupBodyUserMedia);
1366   popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1367   popup->SetResultHandler(
1368       [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1369         bool result = popup->GetButtonResult();
1370         bool remember = popup->GetCheckBoxResult();
1371         if (remember) {
1372           db->Set(kDBPrivateSection, kUsermediaPermissionPrefix + url,
1373                   result ? "allowed" : "denied");
1374         }
1375         result_handler(result);
1376       },
1377       this);
1378   popup->Show();
1379 }
1380
1381 }  // namespace runtime