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