2 * Copyright (c) 2015 Samsung Electronics Co., Ltd All Rights Reserved
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 #include "runtime/browser/web_application.h"
21 #include <cynara-client.h>
31 #ifdef WATCH_FACE_FEATURE_SUPPORT
32 #include <appcore-watch.h>
35 #include "common/application_data.h"
36 #include "common/app_db.h"
37 #include "common/app_control.h"
38 #include "common/command_line.h"
39 #include "common/locale_manager.h"
40 #include "common/logger.h"
41 #include "common/profiler.h"
42 #include "common/resource_manager.h"
43 #include "common/string_utils.h"
44 #include "extensions/renderer/xwalk_extension_renderer_controller.h"
45 #include "runtime/browser/native_window.h"
46 #include "runtime/browser/notification_manager.h"
47 #include "runtime/browser/popup.h"
48 #include "runtime/browser/popup_string.h"
49 #include "runtime/browser/vibration_manager.h"
50 #include "runtime/browser/web_view.h"
51 #include "runtime/browser/splash_screen.h"
52 #include "runtime/browser/ui_runtime.h"
53 #include "extensions/common/xwalk_extension_server.h"
55 #ifndef INJECTED_BUNDLE_PATH
56 #error INJECTED_BUNDLE_PATH is not set.
59 #define SESSION_COUNTER_INTERVAL 0.1
60 #define MAIN_LOOP_INTERVAL 1
62 using namespace extensions;
67 const char* kKeyNameBack = "back";
68 const char* kKeyNameMenu = "menu";
70 const char* kConsoleLogEnableKey = "WRT_CONSOLE_LOG_ENABLE";
71 const char* kConsoleMessageLogTag = "ConsoleMessage";
73 const char* kVerboseKey = "verbose";
74 const char* kPortKey = "port";
76 const char* kAppControlEventScript =
78 "var __event = document.createEvent(\"CustomEvent\");\n"
79 "__event.initCustomEvent(\"appcontrol\", true, true, null);\n"
80 "document.dispatchEvent(__event);\n"
82 "for (var i=0; i < window.frames.length; i++)\n"
83 "{ window.frames[i].document.dispatchEvent(__event); }"
85 const char* kBackKeyEventScript =
87 "var __event = document.createEvent(\"CustomEvent\");\n"
88 "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
89 "__event.keyName = \"back\";\n"
90 "document.dispatchEvent(__event);\n"
92 "for (var i=0; i < window.frames.length; i++)\n"
93 "{ window.frames[i].document.dispatchEvent(__event); }"
95 const char* kMenuKeyEventScript =
97 "var __event = document.createEvent(\"CustomEvent\");\n"
98 "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
99 "__event.keyName = \"menu\";\n"
100 "document.dispatchEvent(__event);\n"
102 "for (var i=0; i < window.frames.length; i++)\n"
103 "{ window.frames[i].document.dispatchEvent(__event); }"
105 const char* kAmbientTickEventScript =
107 "var __event = document.createEvent(\"CustomEvent\");\n"
108 "__event.initCustomEvent(\"timetick\", true, true);\n"
109 "document.dispatchEvent(__event);\n"
111 "for (var i=0; i < window.frames.length; i++)\n"
112 "{ window.frames[i].document.dispatchEvent(__event); }"
114 const char* kCameraPrivilege = "http://tizen.org/privilege/camera";
115 const char* kFullscreenPrivilege = "http://tizen.org/privilege/fullscreen";
116 const char* kFullscreenFeature = "fullscreen";
117 const char* kNotificationPrivilege = "http://tizen.org/privilege/notification";
118 const char* kLocationPrivilege = "http://tizen.org/privilege/location";
119 const char* kRecordPrivilege = "http://tizen.org/privilege/recorder";
120 const char* kStoragePrivilege = "http://tizen.org/privilege/unlimitedstorage";
121 const char* kUsermediaPrivilege = "http://tizen.org/privilege/mediacapture";
122 const char* kNotiIconFile = "noti_icon.png";
123 const char* kFileScheme = "file://";
125 const char* kVisibilitySuspendFeature = "visibility,suspend";
126 const char* kMediastreamRecordFeature = "mediastream,record";
127 const char* kEncryptedDatabaseFeature = "encrypted,database";
128 const char* kRotationLockFeature = "rotation,lock";
129 const char* kBackgroundMusicFeature = "background,music";
130 const char* kSoundModeFeature = "sound,mode";
131 const char* kBackgroundVibrationFeature = "background,vibration";
132 const char* kCSPFeature = "csp";
134 const char* kGeolocationPermissionPrefix = "__WRT_GEOPERM_";
135 const char* kNotificationPermissionPrefix = "__WRT_NOTIPERM_";
136 const char* kQuotaPermissionPrefix = "__WRT_QUOTAPERM_";
137 const char* kCertificateAllowPrefix = "__WRT_CERTIPERM_";
138 const char* kUsermediaPermissionPrefix = "__WRT_USERMEDIAPERM_";
139 const char* kDBPrivateSection = "private";
141 const char* kDefaultCSPRule =
142 "default-src *; script-src 'self'; style-src 'self'; object-src 'none';";
143 const char* kResWgtPath = "res/wgt/";
144 const char* kAppControlMain = "http://tizen.org/appcontrol/operation/main";
146 // Looking for added privilege by Application developer in config.xml.
147 bool FindPrivilegeFromConfig(common::ApplicationData* app_data,
148 const std::string& privilege) {
149 if (app_data->permissions_info().get() == NULL) return false;
150 LOGGER(INFO) << "Finding privilege from config.xml";
151 auto it = app_data->permissions_info()->GetAPIPermissions().begin();
152 auto end = app_data->permissions_info()->GetAPIPermissions().end();
153 for (; it != end; ++it) {
154 if (*it == privilege) return true;
159 // Looking for given default privilege when application installed.
160 bool FindPrivilegeFromCynara(const std::string& privilege_name) {
161 LOGGER(INFO) << "Finding privilege from cynara db";
162 static constexpr char kSmackLabelFilePath[] = "/proc/self/attr/current";
163 std::ifstream file(kSmackLabelFilePath);
164 if (!file.is_open()) {
165 LOGGER(ERROR) << "Failed to open " << kSmackLabelFilePath;
170 cynara* p_cynara = NULL;
171 ret = cynara_initialize(&p_cynara, 0);
172 if (CYNARA_API_SUCCESS != ret) {
173 LOGGER(ERROR) << "Failed. The result of cynara_initialize() : " << ret;
177 std::string uid = std::to_string(getuid());
178 std::string smack_label{std::istreambuf_iterator<char>(file),
179 std::istreambuf_iterator<char>()};
182 ret = cynara_check(p_cynara, smack_label.c_str(), "", uid.c_str(), privilege_name.c_str());
183 if (CYNARA_API_ACCESS_ALLOWED != ret) {
184 LOGGER(ERROR) << "Access denied. The result of cynara_check() : " << ret;
186 LOGGER(INFO) << "Access allowed! The result of cynara_check() : " << ret;
191 ret = cynara_finish(p_cynara);
192 if (CYNARA_API_SUCCESS != ret) {
193 LOGGER(ERROR) << "Failed. The result of cynara_finish() : " << ret;
200 static void SendDownloadRequest(const std::string& url) {
201 common::AppControl request;
202 request.set_operation(APP_CONTROL_OPERATION_DOWNLOAD);
203 request.set_uri(url);
204 request.LaunchRequest();
207 static void InitializeNotificationCallback(Ewk_Context* ewk_context,
208 WebApplication* app) {
209 auto show = [](Ewk_Context*, Ewk_Notification* noti, void* user_data) {
210 WebApplication* self = static_cast<WebApplication*>(user_data);
211 if (self == NULL) return;
212 uint64_t id = ewk_notification_id_get(noti);
213 std::string title(ewk_notification_title_get(noti)
214 ? ewk_notification_title_get(noti)
217 ewk_notification_body_get(noti) ? ewk_notification_body_get(noti) : "");
218 std::string icon_path = self->data_path() + "/" + kNotiIconFile;
219 if (!ewk_notification_icon_save_as_png(noti, icon_path.c_str())) {
222 if (NotificationManager::GetInstance()->Show(id, title, body, icon_path))
223 ewk_notification_showed(id);
225 auto hide = [](Ewk_Context*, uint64_t noti_id, void*) {
226 NotificationManager::GetInstance()->Hide(noti_id);
227 ewk_notification_closed(noti_id, EINA_FALSE);
229 ewk_context_notification_callbacks_set(ewk_context, show, hide, app);
232 static Eina_Bool ExitAppIdlerCallback(void* data) {
233 WebApplication* app = static_cast<WebApplication*>(data);
236 LOGGER(DEBUG) << "Terminate";
240 return ECORE_CALLBACK_CANCEL;
243 static bool ClearCookie(Ewk_Context* ewk_context) {
244 Ewk_Cookie_Manager* cookie_manager =
245 ewk_context_cookie_manager_get(ewk_context);
246 if (!cookie_manager) {
247 LOGGER(ERROR) << "Fail to get cookie manager";
250 ewk_cookie_manager_cookies_clear(cookie_manager);
254 static bool ProcessWellKnownScheme(const std::string& url) {
255 if (common::utils::StartsWith(url, "file:") ||
256 common::utils::StartsWith(url, "app:") ||
257 common::utils::StartsWith(url, "data:") ||
258 common::utils::StartsWith(url, "http:") ||
259 common::utils::StartsWith(url, "https:") ||
260 common::utils::StartsWith(url, "widget:") ||
261 common::utils::StartsWith(url, "about:") ||
262 common::utils::StartsWith(url, "blob:")) {
266 std::unique_ptr<common::AppControl> request(
267 common::AppControl::MakeAppcontrolFromURL(url));
268 if (request.get() == NULL || !request->LaunchRequest()) {
269 LOGGER(ERROR) << "Fail to send appcontrol request";
270 SLoggerE("Fail to send appcontrol request [%s]", url.c_str());
273 // Should return true, to stop the WebEngine progress step about this URL
279 std::vector<unsigned> ParseTizenVersion(const std::string& tizen_string) {
280 std::vector<unsigned> version(3, 0);
281 for (unsigned i = 0, index = 0; i < tizen_string.size(); i++) {
283 if (isdigit(tizen_string[i]) && index < version.size())
284 version[index++] = atoi(&tizen_string[i]);
286 return std::vector<unsigned>(3, 0);
287 } else if (tizen_string[i] != '.')
288 return std::vector<unsigned>(3, 0);
293 WebApplication::WebApplication(
294 NativeWindow* window,
295 common::ApplicationData* app_data)
298 verbose_mode_(false),
299 lang_changed_mode_(false),
300 is_terminate_called_(false),
301 is_close_page_called_(false),
303 ewk_context_new_with_injected_bundle_path(INJECTED_BUNDLE_PATH)),
304 has_ownership_of_ewk_context_(true),
306 appid_(app_data->app_id()),
308 locale_manager_(new common::LocaleManager()) {
312 WebApplication::WebApplication(
313 NativeWindow* window,
314 common::ApplicationData* app_data,
315 Ewk_Context* context)
318 verbose_mode_(false),
319 lang_changed_mode_(false),
320 is_terminate_called_(false),
321 is_close_page_called_(false),
322 ewk_context_(context),
323 has_ownership_of_ewk_context_(false),
325 appid_(app_data->app_id()),
327 locale_manager_(new common::LocaleManager()) {
331 WebApplication::~WebApplication() {
332 window_->SetContent(NULL);
333 auto it = view_stack_.begin();
334 for (; it != view_stack_.end(); ++it) {
339 if (ewk_context_ && has_ownership_of_ewk_context_)
340 ewk_context_delete(ewk_context_);
343 bool WebApplication::Initialize() {
345 std::unique_ptr<char, decltype(std::free)*> path{app_get_data_path(),
347 app_data_path_ = path.get();
349 if (app_data_->setting_info() != NULL &&
350 app_data_->setting_info()->screen_orientation() ==
351 wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
352 ewk_context_tizen_extensible_api_string_set(ewk_context_,
353 kRotationLockFeature, true);
354 window_->SetAutoRotation();
355 } else if (app_data_->setting_info() != NULL &&
356 app_data_->setting_info()->screen_orientation() ==
357 wgt::parse::SettingInfo::ScreenOrientation::PORTRAIT) {
358 window_->SetRotationLock(NativeWindow::ScreenOrientation::PORTRAIT_PRIMARY);
359 } else if (app_data_->setting_info() != NULL &&
360 app_data_->setting_info()->screen_orientation() ==
361 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
362 window_->SetRotationLock(
363 NativeWindow::ScreenOrientation::LANDSCAPE_PRIMARY);
366 splash_screen_.reset(new SplashScreen(
367 window_, app_data_->splash_screen_info(), app_data_->application_path()));
368 resource_manager_.reset(
369 new common::ResourceManager(app_data_, locale_manager_.get()));
370 resource_manager_->set_base_resource_path(app_data_->application_path());
372 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
373 extension_server->SetupIPC(ewk_context_);
376 ewk_context_cache_model_set(ewk_context_, EWK_CACHE_MODEL_DOCUMENT_BROWSER);
379 auto cookie_manager = ewk_context_cookie_manager_get(ewk_context_);
380 ewk_cookie_manager_accept_policy_set(cookie_manager,
381 EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
383 // set persistent storage path
384 std::string cookie_path = data_path() + ".cookie";
385 ewk_cookie_manager_persistent_storage_set(
386 cookie_manager, cookie_path.c_str(),
387 EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
389 // vibration callback
390 auto vibration_start_callback = [](uint64_t ms, void*) {
391 platform::VibrationManager::GetInstance()->Start(static_cast<int>(ms));
393 auto vibration_stop_callback = [](void* /*user_data*/) {
394 platform::VibrationManager::GetInstance()->Stop();
396 ewk_context_vibration_client_callbacks_set(
397 ewk_context_, vibration_start_callback, vibration_stop_callback, NULL);
399 auto download_callback = [](const char* downloadUrl, void* /*data*/) {
400 SendDownloadRequest(downloadUrl);
402 ewk_context_did_start_download_callback_set(ewk_context_, download_callback,
404 InitializeNotificationCallback(ewk_context_, this);
406 if (FindPrivilegeFromConfig(app_data_, kFullscreenPrivilege)) {
407 ewk_context_tizen_extensible_api_string_set(ewk_context_,
408 kFullscreenFeature, true);
411 if (app_data_->setting_info() != NULL &&
412 app_data_->setting_info()->background_support_enabled()) {
413 ewk_context_tizen_extensible_api_string_set(
414 ewk_context_, kVisibilitySuspendFeature, true);
415 ewk_context_tizen_extensible_api_string_set(ewk_context_,
416 kBackgroundMusicFeature, true);
418 ewk_context_tizen_extensible_api_string_set(
419 ewk_context_, kVisibilitySuspendFeature, false);
420 ewk_context_tizen_extensible_api_string_set(ewk_context_,
421 kBackgroundMusicFeature, false);
423 ewk_context_tizen_extensible_api_string_set(ewk_context_,
424 kMediastreamRecordFeature, true);
425 ewk_context_tizen_extensible_api_string_set(ewk_context_,
426 kEncryptedDatabaseFeature, true);
428 if (app_data_->setting_info() != NULL &&
429 app_data_->setting_info()->sound_mode() ==
430 wgt::parse::SettingInfo::SoundMode::EXCLUSIVE) {
431 ewk_context_tizen_extensible_api_string_set(ewk_context_, kSoundModeFeature,
435 if (app_data_->setting_info() != NULL &&
436 app_data_->setting_info()->background_vibration()) {
437 ewk_context_tizen_extensible_api_string_set(
438 ewk_context_, kBackgroundVibrationFeature, true);
441 if (app_data_->widget_info() != NULL &&
442 !app_data_->widget_info()->default_locale().empty()) {
443 locale_manager_->SetDefaultLocale(
444 app_data_->widget_info()->default_locale());
447 if (app_data_->widget_info() != NULL &&
448 app_data_->widget_info()->view_modes() == "fullscreen") {
449 window_->SetCurrentViewModeFullScreen(true);
450 window_->FullScreen(true);
453 if (app_data_->csp_info() != NULL || app_data_->csp_report_info() != NULL ||
454 app_data_->allowed_navigation_info() != NULL) {
455 security_model_version_ = 2;
456 if (app_data_->csp_info() == NULL ||
457 app_data_->csp_info()->security_rules().empty()) {
458 csp_rule_ = kDefaultCSPRule;
460 csp_rule_ = app_data_->csp_info()->security_rules();
462 if (app_data_->csp_report_info() != NULL &&
463 !app_data_->csp_report_info()->security_rules().empty()) {
464 csp_report_rule_ = app_data_->csp_report_info()->security_rules();
466 ewk_context_tizen_extensible_api_string_set(ewk_context_, kCSPFeature,
469 security_model_version_ = 1;
472 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
473 // Set manual rotation
474 window_->EnableManualRotation(true);
475 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
477 main_loop.application = NULL;
478 main_loop.timer = NULL;
479 session_counter.application = NULL;
480 session_counter.timer = NULL;
481 m_tizenCompatibilitySettings.m_major = 0;
482 m_tizenCompatibilitySettings.m_minor = 0;
483 m_tizenCompatibilitySettings.m_release = 0;
488 void WebApplication::SetupTizenVersion() {
489 if (app_data_->tizen_application_info() != NULL &&
490 !app_data_->tizen_application_info()->required_version().empty()) {
491 std::string tizen_version = app_data_->tizen_application_info()->required_version();
492 std::vector<unsigned> parsed_tizen_version = ParseTizenVersion(tizen_version);
493 m_tizenCompatibilitySettings.m_major = parsed_tizen_version[0];
494 m_tizenCompatibilitySettings.m_minor = parsed_tizen_version[1];
495 m_tizenCompatibilitySettings.m_release = parsed_tizen_version[2];
499 bool WebApplication::tizenWebKitCompatibilityEnabled() const {
500 return m_tizenCompatibilitySettings.tizenWebKitCompatibilityEnabled();
503 void WebApplication::Launch(std::unique_ptr<common::AppControl> appcontrol) {
504 // send widget info to injected bundle
505 ewk_context_tizen_app_id_set(ewk_context_, appid_.c_str());
510 WebView* view = new WebView(window_, ewk_context_);
512 SetupWebViewCompatibilitySettings(view);
514 std::unique_ptr<common::ResourceManager::Resource> res =
515 resource_manager_->GetStartResource(appcontrol.get());
516 view->SetDefaultEncoding(res->encoding());
518 STEP_PROFILE_END("OnCreate -> URL Set");
519 STEP_PROFILE_START("URL Set -> Rendered");
521 window_->SetContent(view->evas_object());
523 #ifdef PROFILE_MOBILE
524 // rotate and resize window forcibily for landscape mode.
525 // window rotate event is generated after window show. so
526 // when app get width and height from viewport, wrong value can be returned.
527 if (app_data_->setting_info()->screen_orientation() ==
528 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
529 LOGGER(DEBUG) << "rotate and resize window for landscape mode";
530 elm_win_rotation_with_resize_set(window_->evas_object(), 270);
531 evas_norender(evas_object_evas_get(window_->evas_object()));
533 #endif // PROFILE_MOBILE
535 view->LoadUrl(res->uri(), res->mime());
536 view_stack_.push_front(view);
538 #ifdef PROFILE_WEARABLE
539 // ewk_view_bg_color_set is not working at webview initialization.
540 if (app_data_->app_type() == common::ApplicationData::WATCH) {
541 view->SetBGColor(0, 0, 0, 255);
543 #endif // PROFILE_WEARABLE
545 if (appcontrol->data(AUL_K_DEBUG) == "1") {
547 LaunchInspector(appcontrol.get());
549 if (appcontrol->data(kVerboseKey) == "true") {
550 verbose_mode_ = true;
555 #ifdef PROFILE_MOBILE
556 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
557 LOGGER(DEBUG) << "Show window after launch for remote URL";
561 #endif // PROFILE_MOBILE
564 void WebApplication::AppControl(
565 std::unique_ptr<common::AppControl> appcontrol) {
566 std::unique_ptr<common::ResourceManager::Resource> res =
567 resource_manager_->GetStartResource(appcontrol.get());
569 bool do_reset = res->should_reset();
572 std::string current_page = view_stack_.front()->GetUrl();
573 std::string localized_page = res->uri();
575 if (current_page != localized_page) {
578 SendAppControlEvent();
582 // handle http://tizen.org/appcontrol/operation/main operation specially.
583 // only menu-screen app can send launch request with main operation.
584 // in this case, web app should have to resume web app not reset.
585 if (do_reset && (appcontrol->operation() == kAppControlMain)){
586 LOGGER(DEBUG) << "resume app for main operation";
588 SendAppControlEvent();
594 WebView* view = view_stack_.front();
596 SetupWebViewCompatibilitySettings(view);
597 view->SetDefaultEncoding(res->encoding());
598 view->LoadUrl(res->uri(), res->mime());
599 window_->SetContent(view->evas_object());
602 if (!debug_mode_ && appcontrol->data(AUL_K_DEBUG) == "1") {
604 LaunchInspector(appcontrol.get());
606 if (!verbose_mode_ && appcontrol->data(kVerboseKey) == "true") {
607 verbose_mode_ = true;
612 void WebApplication::SendAppControlEvent() {
613 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
614 view_stack_.front()->EvalJavascript(kAppControlEventScript);
617 void WebApplication::ClearViewStack() {
618 window_->SetContent(NULL);
619 WebView* front = view_stack_.front();
620 auto it = view_stack_.begin();
621 for (; it != view_stack_.end(); ++it) {
628 view_stack_.push_front(front);
631 void WebApplication::Resume() {
632 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
633 view_stack_.front()->SetVisibility(true);
635 if (app_data_->setting_info() != NULL &&
636 app_data_->setting_info()->background_support_enabled()) {
640 auto it = view_stack_.begin();
641 for (; it != view_stack_.end(); ++it) {
646 void WebApplication::Suspend() {
647 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
648 view_stack_.front()->SetVisibility(false);
650 if (app_data_->setting_info() != NULL &&
651 app_data_->setting_info()->background_support_enabled()) {
652 LOGGER(DEBUG) << "gone background (backgroud support enabed)";
656 auto it = view_stack_.begin();
657 for (; it != view_stack_.end(); ++it) {
662 void WebApplication::Terminate() {
663 // Just process closing page once.
664 if (is_terminate_called_ || is_close_page_called_)
667 is_terminate_called_ = true;
672 Eina_Bool WebApplication::ClosePageInExtendedMainLoop(void* user_data)
675 struct Timer* main_loop = static_cast<Timer*>(user_data);
676 main_loop->application->ClosePage();
677 return ECORE_CALLBACK_CANCEL;
680 void WebApplication::ProcessClosingPage() {
683 main_loop.application = this;
684 main_loop.timer = ecore_timer_add(MAIN_LOOP_INTERVAL,
685 ClosePageInExtendedMainLoop, &main_loop);
686 if (!main_loop.timer)
687 LOGGER(ERROR) << "It's failed to create main_loop timer";
688 LOGGER(DEBUG) << "Defer termination of main loop";
689 ecore_main_loop_begin();
690 ecore_timer_del(main_loop.timer);
691 ecore_timer_del(session_counter.timer);
694 void WebApplication::ClosePage() {
696 is_close_page_called_ = true;
698 int valid_evas_object_count = 0;
699 auto it = view_stack_.begin();
700 if (it != view_stack_.end()) {
701 for (; it != view_stack_.end(); ++it) {
702 (*it)->ReplyToJavascriptDialog();
703 view_stack_.front()->SetVisibility(false);
704 if (ewk_view_page_close((*it)->evas_object())) {
705 LOGGER(DEBUG) << "ewk_view_page_close returns true";
706 valid_evas_object_count++;
708 LOGGER(DEBUG) << "ewk_view_page_close returns false";
712 if (valid_evas_object_count == 0)
716 void WebApplication::Exit() {
717 if (!is_terminate_called_)
718 ecore_main_loop_quit();
720 switch (app_data_->app_type()) {
721 case common::ApplicationData::AppType::UI:
722 LOGGER(ERROR) << "app_ui_exit";
725 #ifdef WATCH_FACE_FEATURE_SUPPORT
726 case common::ApplicationData::AppType::WATCH:
727 LOGGER(ERROR) << "watch_ui_exit";
732 LOGGER(ERROR) << "default terminator";
737 void WebApplication::OnCreatedNewWebView(WebView* /*view*/, WebView* new_view) {
738 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
739 view_stack_.front()->SetVisibility(false);
741 SetupWebView(new_view);
742 SetupWebViewCompatibilitySettings(new_view);
743 view_stack_.push_front(new_view);
744 window_->SetContent(new_view->evas_object());
747 void WebApplication::RemoveWebViewFromStack(WebView* view) {
748 if (view_stack_.size() == 0)
751 WebView* current = view_stack_.front();
752 if (current == view) {
753 // In order to prevent the crash issue due to the callback
754 // which occur after destroying WebApplication class,
755 // we have to set the 'SetEventListener' to NULL.
756 view->SetEventListener(NULL);
757 view_stack_.pop_front();
759 auto found = std::find(view_stack_.begin(), view_stack_.end(), view);
760 if (found != view_stack_.end()) {
761 // In order to prevent the crash issue due to the callback
762 // which occur after destroying WebApplication class,
763 // we have to set the 'SetEventListener' to NULL.
764 view->SetEventListener(NULL);
765 view_stack_.erase(found);
769 if (view_stack_.size() == 0) {
770 auto extension_server = XWalkExtensionServer::GetInstance();
771 LOGGER(DEBUG) << "Shutdown extension server";
772 extension_server->Shutdown();
773 #if !defined(TIZEN_PRODUCT_TV)
774 // Hide the window object for preventing the white screen
775 // during termination of web application.
776 evas_object_hide(window_->evas_object());
779 } else if (current != view_stack_.front()) {
780 view_stack_.front()->SetVisibility(true);
781 window_->SetContent(view_stack_.front()->evas_object());
784 // Delete after the callback context(for ewk view) was not used
785 ecore_idler_add([](void* view) {
786 WebView* obj = static_cast<WebView*>(view);
793 Eina_Bool WebApplication::CheckPluginSession(void* user_data)
795 struct Timer* session_counter = static_cast<Timer*>(user_data);
796 if(XWalkExtensionRendererController::plugin_session_count > 0) {
797 LOGGER(ERROR) << "plugin_session_count : " <<
798 XWalkExtensionRendererController::plugin_session_count;
799 return ECORE_CALLBACK_RENEW;
801 LOGGER(DEBUG) << "plugin_session_count : " <<
802 XWalkExtensionRendererController::plugin_session_count;
803 LOGGER(DEBUG) << "Execute deferred termination of main loop";
804 session_counter->application->Exit();
805 return ECORE_CALLBACK_CANCEL;
808 void WebApplication::OnClosedWebView(WebView* view) {
809 // Reply to javascript dialog for preventing freeze issue.
810 view->ReplyToJavascriptDialog();
811 RemoveWebViewFromStack(view);
813 #if defined(TIZEN_PRODUCT_TV)
814 LOGGER(DEBUG) << "plugin_session_count : " <<
815 XWalkExtensionRendererController::plugin_session_count;
817 // Hide the window object for preventing the white screen
818 // during termination of web application.
819 evas_object_hide(window_->evas_object());
821 session_counter.application = this;
822 session_counter.timer = ecore_timer_add(SESSION_COUNTER_INTERVAL,
823 CheckPluginSession, &session_counter);
824 if (!session_counter.timer)
825 LOGGER(ERROR) << "It's failed to create session_counter timer";
829 void WebApplication::OnReceivedWrtMessage(WebView* view,
830 Ewk_IPC_Wrt_Message_Data* msg) {
831 Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg);
833 #define TYPE_BEGIN(x) (!strncmp(msg_type, x, strlen(x)))
834 #define TYPE_IS(x) (!strcmp(msg_type, x))
836 if (TYPE_BEGIN("xwalk://")) {
837 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
838 extension_server->HandleIPCMessage(msg);
840 Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg);
841 Eina_Stringshare* msg_ref_id =
842 ewk_ipc_wrt_message_data_reference_id_get(msg);
843 Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg);
845 if (TYPE_IS("tizen://hide")) {
848 } else if (TYPE_IS("tizen://exit")) {
850 // Reply to javascript dialog for preventing freeze issue.
851 view->ReplyToJavascriptDialog();
852 ecore_idler_add(ExitAppIdlerCallback, this);
853 } else if (TYPE_IS("tizen://changeUA")) {
855 // Change UserAgent of current WebView
857 if (view_stack_.size() > 0 && view_stack_.front() != NULL) {
858 ret = view_stack_.front()->SetUserAgent(std::string(msg_value));
861 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
862 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
863 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
865 ewk_ipc_wrt_message_data_value_set(ans, "success");
867 ewk_ipc_wrt_message_data_value_set(ans, "failed");
868 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
869 LOGGER(ERROR) << "Failed to send response";
871 ewk_ipc_wrt_message_data_del(ans);
872 } else if (TYPE_IS("tizen://deleteAllCookies")) {
873 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
874 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
875 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
876 if (ClearCookie(ewk_context_))
877 ewk_ipc_wrt_message_data_value_set(ans, "success");
879 ewk_ipc_wrt_message_data_value_set(ans, "failed");
880 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
881 LOGGER(ERROR) << "Failed to send response";
883 ewk_ipc_wrt_message_data_del(ans);
884 } else if (TYPE_IS("tizen://hide_splash_screen")) {
885 splash_screen_->HideSplashScreen(SplashScreen::HideReason::CUSTOM);
888 eina_stringshare_del(msg_ref_id);
889 eina_stringshare_del(msg_id);
890 eina_stringshare_del(msg_value);
896 eina_stringshare_del(msg_type);
899 void WebApplication::OnOrientationLock(
900 WebView* view, bool lock,
901 NativeWindow::ScreenOrientation preferred_rotation) {
902 if (view_stack_.size() == 0) return;
904 // Only top-most view can set the orientation relate operation
905 if (view_stack_.front() != view) return;
907 // This is for 2.4 compatibility. Requested by Webengine Team.
909 // In Tizen 2.4 WebKit locking screen orientation was possible with Web API
910 // screen.lockOrientation(). This API was deprecated and replaced with
911 // screen.orientation.lock(). But for compatibility case we need to support
913 if(!tizenWebKitCompatibilityEnabled()) {
914 auto orientaion_setting = app_data_->setting_info() != NULL
915 ? app_data_->setting_info()->screen_orientation()
916 : wgt::parse::SettingInfo::ScreenOrientation::AUTO;
917 if (wgt::parse::SettingInfo::ScreenOrientation::AUTO != orientaion_setting) {
918 // If Tizen version is 3.0, it return.
924 window_->SetRotationLock(preferred_rotation);
926 window_->SetAutoRotation();
930 void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
931 // NOTE: This code is added to enable back-key on remote URL
932 bool enabled = app_data_->setting_info() != NULL
933 ? app_data_->setting_info()->hwkey_enabled()
936 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
937 if (kKeyNameBack == keyname) {
938 LOGGER(DEBUG) << "Back to previous page for remote URL";
940 view->EvalJavascript(kBackKeyEventScript);
941 if (!view->Backward()) {
942 LOGGER(DEBUG) << "Terminate";
949 if (enabled && kKeyNameBack == keyname) {
950 view->EvalJavascript(kBackKeyEventScript);
951 // NOTE: This code is added for backward compatibility.
952 // If the 'backbutton_presence' is true, WebView should be navigated back.
953 if ((app_data_->setting_info() != NULL &&
954 app_data_->setting_info()->backbutton_presence()) ||
955 (app_data_->widget_info() != NULL &&
956 app_data_->widget_info()->view_modes() == "windowed")) {
957 if (!view->Backward()) {
958 LOGGER(DEBUG) << "Terminate";
962 } else if (enabled && kKeyNameMenu == keyname) {
963 view->EvalJavascript(kMenuKeyEventScript);
967 void WebApplication::OnLanguageChanged() {
968 lang_changed_mode_ = true;
969 locale_manager_->UpdateSystemLocale();
970 ewk_context_cache_clear(ewk_context_);
971 auto it = view_stack_.begin();
972 for (; it != view_stack_.end(); ++it) {
977 void WebApplication::OnConsoleMessage(const std::string& msg, int level) {
978 static bool enabled = (getenv(kConsoleLogEnableKey) != NULL);
981 std::string split_msg = msg;
982 std::size_t pos = msg.find(kResWgtPath);
983 if (pos != std::string::npos) {
984 split_msg = msg.substr(pos + strlen(kResWgtPath));
987 if (debug_mode_ || verbose_mode_ || enabled) {
988 int dlog_level = DLOG_DEBUG;
990 case EWK_CONSOLE_MESSAGE_LEVEL_WARNING:
991 dlog_level = DLOG_WARN;
993 case EWK_CONSOLE_MESSAGE_LEVEL_ERROR:
994 dlog_level = DLOG_ERROR;
997 dlog_level = DLOG_DEBUG;
1000 LOGGER_RAW(dlog_level, kConsoleMessageLogTag)
1001 << "[" << app_data_->pkg_id() << "] " << split_msg;
1005 void WebApplication::OnLowMemory() {
1006 ewk_context_cache_clear(ewk_context_);
1007 ewk_context_notify_low_memory(ewk_context_);
1010 void WebApplication::OnSoftKeyboardChangeEvent(WebView* /*view*/,
1011 SoftKeyboardChangeEventValue softkeyboard_value) {
1012 LOGGER(DEBUG) << "OnSoftKeyboardChangeEvent";
1013 std::stringstream script;
1016 << "var __event = document.createEvent(\"CustomEvent\");\n"
1017 << "var __detail = {};\n"
1018 << "__event.initCustomEvent(\"softkeyboardchange\",true,true,__detail);\n"
1019 << "__event.state = \"" << softkeyboard_value.state << "\";\n"
1020 << "__event.width = " << softkeyboard_value.width << ";\n"
1021 << "__event.height = " << softkeyboard_value.height << ";\n"
1022 << "document.dispatchEvent(__event);\n"
1024 << "for (var i=0; i < window.frames.length; i++)\n"
1025 << "{ window.frames[i].document.dispatchEvent(__event); }"
1027 std::string kSoftKeyboardScript = script.str();
1028 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1029 view_stack_.front()->EvalJavascript(kSoftKeyboardScript.c_str());
1032 #ifdef ROTARY_EVENT_FEATURE_SUPPORT
1033 void WebApplication::OnRotaryEvent(WebView* /*view*/,
1034 RotaryEventType type) {
1035 LOGGER(DEBUG) << "OnRotaryEvent";
1036 std::stringstream script;
1039 << "var __event = document.createEvent(\"CustomEvent\");\n"
1040 << "var __detail = {};\n"
1041 << "__event.initCustomEvent(\"rotarydetent\", true, true, __detail);\n"
1042 << "__event.detail.direction = \""
1043 << (type == RotaryEventType::CLOCKWISE ? "CW" : "CCW")
1045 << "document.dispatchEvent(__event);\n"
1047 << "for (var i=0; i < window.frames.length; i++)\n"
1048 << "{ window.frames[i].document.dispatchEvent(__event); }"
1050 std::string kRotaryEventScript = script.str();
1051 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1052 view_stack_.front()->EvalJavascript(kRotaryEventScript.c_str());
1054 #endif // ROTARY_EVENT_FEATURE_SUPPORT
1056 void WebApplication::OnTimeTick(long time) {
1058 LOGGER(DEBUG) << "TimeTick";
1059 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1060 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1064 void WebApplication::OnAmbientTick(long time) {
1065 LOGGER(DEBUG) << "AmbientTick";
1066 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1067 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1070 void WebApplication::OnAmbientChanged(bool ambient_mode) {
1071 LOGGER(DEBUG) << "AmbientChanged";
1072 std::stringstream script;
1075 << "var __event = document.createEvent(\"CustomEvent\");\n"
1076 << "var __detail = {};\n"
1077 << "__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);\n"
1078 << "__event.detail.ambientMode = "
1079 << (ambient_mode ? "true" : "false") << ";\n"
1080 << "document.dispatchEvent(__event);\n"
1082 << "for (var i=0; i < window.frames.length; i++)\n"
1083 << "{ window.frames[i].document.dispatchEvent(__event); }"
1085 std::string kAmbientChangedEventScript = script.str();
1086 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1087 view_stack_.front()->EvalJavascript(kAmbientChangedEventScript.c_str());
1090 bool WebApplication::OnContextMenuDisabled(WebView* /*view*/) {
1091 return !(app_data_->setting_info() != NULL
1092 ? app_data_->setting_info()->context_menu_enabled()
1096 void WebApplication::OnLoadStart(WebView* /*view*/) {
1097 LOGGER(DEBUG) << "LoadStart";
1100 void WebApplication::OnLoadFinished(WebView* /*view*/) {
1101 LOGGER(DEBUG) << "LoadFinished";
1102 splash_screen_->HideSplashScreen(SplashScreen::HideReason::LOADFINISHED);
1105 void WebApplication::OnRendered(WebView* view) {
1106 STEP_PROFILE_END("URL Set -> Rendered");
1107 STEP_PROFILE_END("Start -> Launch Completed");
1108 LOGGER(DEBUG) << "Rendered";
1109 splash_screen_->HideSplashScreen(SplashScreen::HideReason::RENDERED);
1111 // Do not show(), active() for language change
1112 if(lang_changed_mode_ == false){
1113 // Show window after frame rendered.
1114 #ifdef PROFILE_MOBILE
1115 if (common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
1119 #else // PROFILE_MOBILE
1125 lang_changed_mode_ = false;
1129 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
1130 void WebApplication::OnRotatePrepared(WebView* /*view*/) {
1131 window_->ManualRotationDone();
1133 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
1135 void WebApplication::LaunchInspector(common::AppControl* appcontrol) {
1136 unsigned int port = ewk_context_inspector_server_start(ewk_context_, 0);
1137 std::stringstream ss;
1139 std::map<std::string, std::vector<std::string>> data;
1140 data[kPortKey] = {ss.str()};
1141 appcontrol->Reply(data);
1144 void WebApplication::SetupWebView(WebView* view) {
1145 view->SetEventListener(this);
1148 if (security_model_version_ == 2) {
1149 view->SetCSPRule(csp_rule_, false);
1150 if (!csp_report_rule_.empty()) {
1151 view->SetCSPRule(csp_report_rule_, true);
1155 // Setup longpolling value
1156 if (app_data_->setting_info() != NULL &&
1157 app_data_->setting_info()->long_polling()) {
1158 boost::optional <unsigned int> polling_val(app_data_->setting_info()->long_polling());
1159 unsigned long *ptr = reinterpret_cast <unsigned long *> (&polling_val.get());
1160 view->SetLongPolling(*ptr);
1164 void WebApplication::SetupWebViewCompatibilitySettings(WebView* view) {
1165 if (tizenWebKitCompatibilityEnabled()) {
1166 Ewk_Settings* settings = ewk_view_settings_get(view->evas_object());
1167 ewk_settings_tizen_compatibility_mode_set(settings,
1168 m_tizenCompatibilitySettings.m_major,
1169 m_tizenCompatibilitySettings.m_minor,
1170 m_tizenCompatibilitySettings.m_release);
1171 ewk_settings_text_autosizing_enabled_set(settings, EINA_FALSE);
1175 bool WebApplication::OnDidNavigation(WebView* /*view*/,
1176 const std::string& url) {
1178 // except(file , http, https, app) pass to appcontrol and return false
1179 if (ProcessWellKnownScheme(url)) {
1183 // send launch request for blocked URL to guarrenty backward-compatibility.
1184 if (resource_manager_->AllowNavigation(url)) {
1187 LOGGER(DEBUG) << "URL is blocked. send launch request for URL : " << url;
1188 std::unique_ptr<common::AppControl> request(
1189 common::AppControl::MakeAppcontrolFromURL(url));
1190 if (request.get() == NULL || !request->LaunchRequest()) {
1191 LOGGER(ERROR) << "Fail to send appcontrol request";
1197 void WebApplication::OnNotificationPermissionRequest(
1198 WebView*, const std::string& url,
1199 std::function<void(bool)> result_handler) {
1200 auto db = common::AppDB::GetInstance();
1201 std::string reminder =
1202 db->Get(kDBPrivateSection, kNotificationPermissionPrefix + url);
1203 if (reminder == "allowed") {
1204 result_handler(true);
1206 } else if (reminder == "denied") {
1207 result_handler(false);
1211 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1212 // Remote Domain: Popup user prompt.
1213 if (common::utils::StartsWith(url, "file://") &&
1214 FindPrivilegeFromConfig(app_data_, kNotificationPrivilege)) {
1215 result_handler(true);
1219 Popup* popup = Popup::CreatePopup(window_);
1220 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1221 popup->SetTitle(popup_string::kPopupTitleWebNotification);
1222 popup->SetBody(popup_string::kPopupBodyWebNotification);
1223 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1224 popup->SetResultHandler(
1225 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1226 bool result = popup->GetButtonResult();
1227 bool remember = popup->GetCheckBoxResult();
1229 db->Set(kDBPrivateSection, kNotificationPermissionPrefix + url,
1230 result ? "allowed" : "denied");
1232 result_handler(result);
1238 void WebApplication::OnGeolocationPermissionRequest(
1239 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, kGeolocationPermissionPrefix + url);
1244 if (reminder == "allowed") {
1245 result_handler(true);
1247 } else if (reminder == "denied") {
1248 result_handler(false);
1252 // Local Domain: Grant permission if defined, otherwise block execution.
1253 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1254 if (!FindPrivilegeFromConfig(app_data_, kLocationPrivilege) &&
1255 !FindPrivilegeFromCynara(kLocationPrivilege)) {
1256 result_handler(false);
1260 if (common::utils::StartsWith(url, "file://")) {
1261 result_handler(true);
1265 Popup* popup = Popup::CreatePopup(window_);
1266 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1267 popup->SetTitle(popup_string::kPopupTitleGeoLocation);
1268 popup->SetBody(popup_string::kPopupBodyGeoLocation);
1269 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1270 popup->SetResultHandler(
1271 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1272 bool result = popup->GetButtonResult();
1273 bool remember = popup->GetCheckBoxResult();
1275 db->Set(kDBPrivateSection, kGeolocationPermissionPrefix + url,
1276 result ? "allowed" : "denied");
1278 result_handler(result);
1284 void WebApplication::OnQuotaExceed(WebView*, const std::string& url,
1285 std::function<void(bool)> result_handler) {
1286 auto db = common::AppDB::GetInstance();
1287 std::string reminder =
1288 db->Get(kDBPrivateSection, kQuotaPermissionPrefix + url);
1289 if (reminder == "allowed") {
1290 result_handler(true);
1292 } else if (reminder == "denied") {
1293 result_handler(false);
1297 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1298 // Remote Domain: Popup user prompt.
1299 if (common::utils::StartsWith(url, "file://") &&
1300 FindPrivilegeFromConfig(app_data_, kStoragePrivilege)) {
1301 result_handler(true);
1305 Popup* popup = Popup::CreatePopup(window_);
1306 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1307 popup->SetTitle(popup_string::kPopupTitleWebStorage);
1308 popup->SetBody(popup_string::kPopupBodyWebStorage);
1309 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1310 popup->SetResultHandler(
1311 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1312 bool result = popup->GetButtonResult();
1313 bool remember = popup->GetCheckBoxResult();
1315 db->Set(kDBPrivateSection, kQuotaPermissionPrefix + url,
1316 result ? "allowed" : "denied");
1318 result_handler(result);
1324 void WebApplication::OnAuthenticationRequest(
1325 WebView*, const std::string& /*url*/, const std::string& /*message*/,
1326 std::function<void(bool submit, const std::string& id,
1327 const std::string& password)> result_handler) {
1328 Popup* popup = Popup::CreatePopup(window_);
1329 popup->SetButtonType(Popup::ButtonType::LoginCancelButton);
1330 popup->SetFirstEntry(popup_string::kPopupLabelAuthusername,
1331 Popup::EntryType::Edit);
1332 popup->SetSecondEntry(popup_string::kPopupLabelPassword,
1333 Popup::EntryType::PwEdit);
1334 popup->SetTitle(popup_string::kPopupTitleAuthRequest);
1335 popup->SetBody(popup_string::kPopupBodyAuthRequest);
1336 popup->SetResultHandler([result_handler](Popup* popup, void* /*user_data*/) {
1337 bool result = popup->GetButtonResult();
1338 std::string id = popup->GetFirstEntryResult();
1339 std::string passwd = popup->GetSecondEntryResult();
1340 result_handler(result, id, passwd);
1346 void WebApplication::OnCertificateAllowRequest(
1347 WebView*, const std::string& url, const std::string& pem,
1348 std::function<void(bool allow)> result_handler) {
1349 auto db = common::AppDB::GetInstance();
1350 std::string reminder =
1351 db->Get(kDBPrivateSection, kCertificateAllowPrefix + pem);
1352 if (reminder == "allowed") {
1353 result_handler(true);
1355 } else if (reminder == "denied") {
1356 result_handler(false);
1360 Popup* popup = Popup::CreatePopup(window_);
1361 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1362 popup->SetTitle(popup_string::kPopupTitleCert);
1363 popup->SetBody(popup_string::GetText(
1364 popup_string::kPopupBodyCert) + "\n\n" + url);
1365 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1366 popup->SetResultHandler(
1367 [db, result_handler, pem](Popup* popup, void* /*user_data*/) {
1368 bool result = popup->GetButtonResult();
1369 bool remember = popup->GetCheckBoxResult();
1371 db->Set(kDBPrivateSection, kCertificateAllowPrefix + pem,
1372 result ? "allowed" : "denied");
1374 result_handler(result);
1380 void WebApplication::OnUsermediaPermissionRequest(
1381 WebView*, const std::string& url,
1382 std::function<void(bool)> result_handler) {
1383 auto db = common::AppDB::GetInstance();
1384 std::string reminder =
1385 db->Get(kDBPrivateSection, kUsermediaPermissionPrefix + url);
1386 if (reminder == "allowed") {
1387 result_handler(true);
1389 } else if (reminder == "denied") {
1390 result_handler(false);
1394 // Local Domain: Grant permission if defined, otherwise block execution.
1395 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1396 if (!FindPrivilegeFromConfig(app_data_, kUsermediaPrivilege) &&
1397 !(FindPrivilegeFromCynara(kCameraPrivilege) && FindPrivilegeFromCynara(kRecordPrivilege))) {
1398 result_handler(false);
1402 if (common::utils::StartsWith(url, "file://")) {
1403 result_handler(true);
1407 Popup* popup = Popup::CreatePopup(window_);
1408 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1409 popup->SetTitle(popup_string::kPopupTitleUserMedia);
1410 popup->SetBody(popup_string::kPopupBodyUserMedia);
1411 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1412 popup->SetResultHandler(
1413 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1414 bool result = popup->GetButtonResult();
1415 bool remember = popup->GetCheckBoxResult();
1417 db->Set(kDBPrivateSection, kUsermediaPermissionPrefix + url,
1418 result ? "allowed" : "denied");
1420 result_handler(result);
1426 } // namespace runtime