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 #include "common/application_data.h"
32 #include "common/app_db.h"
33 #include "common/app_control.h"
34 #include "common/command_line.h"
35 #include "common/locale_manager.h"
36 #include "common/logger.h"
37 #include "common/profiler.h"
38 #include "common/resource_manager.h"
39 #include "common/string_utils.h"
40 #include "extensions/renderer/xwalk_extension_renderer_controller.h"
41 #include "runtime/browser/native_window.h"
42 #include "runtime/browser/notification_manager.h"
43 #include "runtime/browser/popup.h"
44 #include "runtime/browser/popup_string.h"
45 #include "runtime/browser/vibration_manager.h"
46 #include "runtime/browser/web_view.h"
47 #include "runtime/browser/splash_screen.h"
48 #include "runtime/browser/ui_runtime.h"
49 #include "extensions/common/xwalk_extension_server.h"
51 #ifndef INJECTED_BUNDLE_PATH
52 #error INJECTED_BUNDLE_PATH is not set.
55 #define SESSION_COUNTER_INTERVAL 0.1
56 #define MAIN_LOOP_INTERVAL 1
58 using namespace extensions;
63 const char* kKeyNameBack = "back";
64 const char* kKeyNameMenu = "menu";
66 const char* kConsoleLogEnableKey = "WRT_CONSOLE_LOG_ENABLE";
67 const char* kConsoleMessageLogTag = "ConsoleMessage";
69 const char* kVerboseKey = "verbose";
70 const char* kPortKey = "port";
72 const char* kAppControlEventScript =
74 "var __event = document.createEvent(\"CustomEvent\");\n"
75 "__event.initCustomEvent(\"appcontrol\", true, true, null);\n"
76 "document.dispatchEvent(__event);\n"
78 "for (var i=0; i < window.frames.length; i++)\n"
79 "{ window.frames[i].document.dispatchEvent(__event); }"
81 const char* kBackKeyEventScript =
83 "var __event = document.createEvent(\"CustomEvent\");\n"
84 "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
85 "__event.keyName = \"back\";\n"
86 "document.dispatchEvent(__event);\n"
88 "for (var i=0; i < window.frames.length; i++)\n"
89 "{ window.frames[i].document.dispatchEvent(__event); }"
91 const char* kMenuKeyEventScript =
93 "var __event = document.createEvent(\"CustomEvent\");\n"
94 "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
95 "__event.keyName = \"menu\";\n"
96 "document.dispatchEvent(__event);\n"
98 "for (var i=0; i < window.frames.length; i++)\n"
99 "{ window.frames[i].document.dispatchEvent(__event); }"
101 const char* kAmbientTickEventScript =
103 "var __event = document.createEvent(\"CustomEvent\");\n"
104 "__event.initCustomEvent(\"timetick\", true, true);\n"
105 "document.dispatchEvent(__event);\n"
107 "for (var i=0; i < window.frames.length; i++)\n"
108 "{ window.frames[i].document.dispatchEvent(__event); }"
110 const char* kCameraPrivilege = "http://tizen.org/privilege/camera";
111 const char* kFullscreenPrivilege = "http://tizen.org/privilege/fullscreen";
112 const char* kFullscreenFeature = "fullscreen";
113 const char* kNotificationPrivilege = "http://tizen.org/privilege/notification";
114 const char* kLocationPrivilege = "http://tizen.org/privilege/location";
115 const char* kRecordPrivilege = "http://tizen.org/privilege/recorder";
116 const char* kStoragePrivilege = "http://tizen.org/privilege/unlimitedstorage";
117 const char* kUsermediaPrivilege = "http://tizen.org/privilege/mediacapture";
118 const char* kNotiIconFile = "noti_icon.png";
119 const char* kFileScheme = "file://";
121 const char* kVisibilitySuspendFeature = "visibility,suspend";
122 const char* kMediastreamRecordFeature = "mediastream,record";
123 const char* kEncryptedDatabaseFeature = "encrypted,database";
124 const char* kRotationLockFeature = "rotation,lock";
125 const char* kBackgroundMusicFeature = "background,music";
126 const char* kSoundModeFeature = "sound,mode";
127 const char* kBackgroundVibrationFeature = "background,vibration";
128 const char* kCSPFeature = "csp";
130 const char* kGeolocationPermissionPrefix = "__WRT_GEOPERM_";
131 const char* kNotificationPermissionPrefix = "__WRT_NOTIPERM_";
132 const char* kQuotaPermissionPrefix = "__WRT_QUOTAPERM_";
133 const char* kCertificateAllowPrefix = "__WRT_CERTIPERM_";
134 const char* kUsermediaPermissionPrefix = "__WRT_USERMEDIAPERM_";
135 const char* kDBPrivateSection = "private";
137 const char* kDefaultCSPRule =
138 "default-src *; script-src 'self'; style-src 'self'; object-src 'none';";
139 const char* kResWgtPath = "res/wgt/";
140 const char* kAppControlMain = "http://tizen.org/appcontrol/operation/main";
142 // Looking for added privilege by Application developer in config.xml.
143 bool FindPrivilegeFromConfig(common::ApplicationData* app_data,
144 const std::string& privilege) {
145 if (app_data->permissions_info().get() == NULL) return false;
146 LOGGER(INFO) << "Finding privilege from config.xml";
147 auto it = app_data->permissions_info()->GetAPIPermissions().begin();
148 auto end = app_data->permissions_info()->GetAPIPermissions().end();
149 for (; it != end; ++it) {
150 if (*it == privilege) return true;
155 // Looking for given default privilege when application installed.
156 bool FindPrivilegeFromCynara(const std::string& privilege_name) {
157 LOGGER(INFO) << "Finding privilege from cynara db";
158 static constexpr char kSmackLabelFilePath[] = "/proc/self/attr/current";
159 std::ifstream file(kSmackLabelFilePath);
160 if (!file.is_open()) {
161 LOGGER(ERROR) << "Failed to open " << kSmackLabelFilePath;
166 cynara* p_cynara = NULL;
167 ret = cynara_initialize(&p_cynara, 0);
168 if (CYNARA_API_SUCCESS != ret) {
169 LOGGER(ERROR) << "Failed. The result of cynara_initialize() : " << ret;
173 std::string uid = std::to_string(getuid());
174 std::string smack_label{std::istreambuf_iterator<char>(file),
175 std::istreambuf_iterator<char>()};
178 ret = cynara_check(p_cynara, smack_label.c_str(), "", uid.c_str(), privilege_name.c_str());
179 if (CYNARA_API_ACCESS_ALLOWED != ret) {
180 LOGGER(ERROR) << "Access denied. The result of cynara_check() : " << ret;
182 LOGGER(INFO) << "Access allowed! The result of cynara_check() : " << ret;
187 ret = cynara_finish(p_cynara);
188 if (CYNARA_API_SUCCESS != ret) {
189 LOGGER(ERROR) << "Failed. The result of cynara_finish() : " << ret;
196 static void SendDownloadRequest(const std::string& url) {
197 common::AppControl request;
198 request.set_operation(APP_CONTROL_OPERATION_DOWNLOAD);
199 request.set_uri(url);
200 request.LaunchRequest();
203 static void InitializeNotificationCallback(Ewk_Context* ewk_context,
204 WebApplication* app) {
205 auto show = [](Ewk_Context*, Ewk_Notification* noti, void* user_data) {
206 WebApplication* self = static_cast<WebApplication*>(user_data);
207 if (self == NULL) return;
208 uint64_t id = ewk_notification_id_get(noti);
209 std::string title(ewk_notification_title_get(noti)
210 ? ewk_notification_title_get(noti)
213 ewk_notification_body_get(noti) ? ewk_notification_body_get(noti) : "");
214 std::string icon_path = self->data_path() + "/" + kNotiIconFile;
215 if (!ewk_notification_icon_save_as_png(noti, icon_path.c_str())) {
218 if (NotificationManager::GetInstance()->Show(id, title, body, icon_path))
219 ewk_notification_showed(id);
221 auto hide = [](Ewk_Context*, uint64_t noti_id, void*) {
222 NotificationManager::GetInstance()->Hide(noti_id);
223 ewk_notification_closed(noti_id, EINA_FALSE);
225 ewk_context_notification_callbacks_set(ewk_context, show, hide, app);
228 static Eina_Bool ExitAppIdlerCallback(void* data) {
229 WebApplication* app = static_cast<WebApplication*>(data);
232 LOGGER(DEBUG) << "Terminate";
236 return ECORE_CALLBACK_CANCEL;
239 static bool ClearCookie(Ewk_Context* ewk_context) {
240 Ewk_Cookie_Manager* cookie_manager =
241 ewk_context_cookie_manager_get(ewk_context);
242 if (!cookie_manager) {
243 LOGGER(ERROR) << "Fail to get cookie manager";
246 ewk_cookie_manager_cookies_clear(cookie_manager);
250 static bool ProcessWellKnownScheme(const std::string& url) {
251 if (common::utils::StartsWith(url, "file:") ||
252 common::utils::StartsWith(url, "app:") ||
253 common::utils::StartsWith(url, "data:") ||
254 common::utils::StartsWith(url, "http:") ||
255 common::utils::StartsWith(url, "https:") ||
256 common::utils::StartsWith(url, "widget:") ||
257 common::utils::StartsWith(url, "about:") ||
258 common::utils::StartsWith(url, "blob:")) {
262 std::unique_ptr<common::AppControl> request(
263 common::AppControl::MakeAppcontrolFromURL(url));
264 if (request.get() == NULL || !request->LaunchRequest()) {
265 LOGGER(ERROR) << "Fail to send appcontrol request";
266 SLoggerE("Fail to send appcontrol request [%s]", url.c_str());
269 // Should return true, to stop the WebEngine progress step about this URL
275 std::vector<unsigned> ParseTizenVersion(const std::string& tizen_string) {
276 std::vector<unsigned> version(3, 0);
277 for (unsigned i = 0, index = 0; i < tizen_string.size(); i++) {
279 if (isdigit(tizen_string[i]) && index < version.size())
280 version[index++] = atoi(&tizen_string[i]);
282 return std::vector<unsigned>(3, 0);
283 } else if (tizen_string[i] != '.')
284 return std::vector<unsigned>(3, 0);
289 WebApplication::WebApplication(
290 NativeWindow* window,
291 common::ApplicationData* app_data)
294 verbose_mode_(false),
295 lang_changed_mode_(false),
296 is_terminate_called_(false),
297 is_close_page_called_(false),
299 ewk_context_new_with_injected_bundle_path(INJECTED_BUNDLE_PATH)),
300 has_ownership_of_ewk_context_(true),
302 appid_(app_data->app_id()),
304 locale_manager_(new common::LocaleManager()),
309 WebApplication::WebApplication(
310 NativeWindow* window,
311 common::ApplicationData* app_data,
312 Ewk_Context* context)
315 verbose_mode_(false),
316 is_terminate_called_(false),
317 is_close_page_called_(false),
318 ewk_context_(context),
319 has_ownership_of_ewk_context_(false),
321 appid_(app_data->app_id()),
323 locale_manager_(new common::LocaleManager()),
328 WebApplication::~WebApplication() {
329 window_->SetContent(NULL);
330 auto it = view_stack_.begin();
331 for (; it != view_stack_.end(); ++it) {
336 if (ewk_context_ && has_ownership_of_ewk_context_)
337 ewk_context_delete(ewk_context_);
340 bool WebApplication::Initialize() {
342 std::unique_ptr<char, decltype(std::free)*> path{app_get_data_path(),
344 app_data_path_ = path.get();
346 if (app_data_->setting_info() != NULL &&
347 app_data_->setting_info()->screen_orientation() ==
348 wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
349 ewk_context_tizen_extensible_api_string_set(ewk_context_,
350 kRotationLockFeature, true);
351 window_->SetAutoRotation();
352 } else if (app_data_->setting_info() != NULL &&
353 app_data_->setting_info()->screen_orientation() ==
354 wgt::parse::SettingInfo::ScreenOrientation::PORTRAIT) {
355 window_->SetRotationLock(NativeWindow::ScreenOrientation::PORTRAIT_PRIMARY);
356 } else if (app_data_->setting_info() != NULL &&
357 app_data_->setting_info()->screen_orientation() ==
358 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
359 window_->SetRotationLock(
360 NativeWindow::ScreenOrientation::LANDSCAPE_PRIMARY);
363 splash_screen_.reset(new SplashScreen(
364 window_, app_data_->splash_screen_info(), app_data_->application_path()));
365 resource_manager_.reset(
366 new common::ResourceManager(app_data_, locale_manager_.get()));
367 resource_manager_->set_base_resource_path(app_data_->application_path());
369 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
370 extension_server->SetupIPC(ewk_context_);
373 ewk_context_cache_model_set(ewk_context_, EWK_CACHE_MODEL_DOCUMENT_BROWSER);
376 auto cookie_manager = ewk_context_cookie_manager_get(ewk_context_);
377 ewk_cookie_manager_accept_policy_set(cookie_manager,
378 EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
380 // set persistent storage path
381 std::string cookie_path = data_path() + ".cookie";
382 ewk_cookie_manager_persistent_storage_set(
383 cookie_manager, cookie_path.c_str(),
384 EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
386 // vibration callback
387 auto vibration_start_callback = [](uint64_t ms, void*) {
388 platform::VibrationManager::GetInstance()->Start(static_cast<int>(ms));
390 auto vibration_stop_callback = [](void* /*user_data*/) {
391 platform::VibrationManager::GetInstance()->Stop();
393 ewk_context_vibration_client_callbacks_set(
394 ewk_context_, vibration_start_callback, vibration_stop_callback, NULL);
396 auto download_callback = [](const char* downloadUrl, void* /*data*/) {
397 SendDownloadRequest(downloadUrl);
399 ewk_context_did_start_download_callback_set(ewk_context_, download_callback,
401 InitializeNotificationCallback(ewk_context_, this);
403 if (FindPrivilegeFromConfig(app_data_, kFullscreenPrivilege)) {
404 ewk_context_tizen_extensible_api_string_set(ewk_context_,
405 kFullscreenFeature, true);
408 if (app_data_->setting_info() != NULL &&
409 app_data_->setting_info()->background_support_enabled()) {
410 ewk_context_tizen_extensible_api_string_set(
411 ewk_context_, kVisibilitySuspendFeature, true);
412 ewk_context_tizen_extensible_api_string_set(ewk_context_,
413 kBackgroundMusicFeature, true);
415 ewk_context_tizen_extensible_api_string_set(
416 ewk_context_, kVisibilitySuspendFeature, false);
417 ewk_context_tizen_extensible_api_string_set(ewk_context_,
418 kBackgroundMusicFeature, false);
420 ewk_context_tizen_extensible_api_string_set(ewk_context_,
421 kMediastreamRecordFeature, true);
422 ewk_context_tizen_extensible_api_string_set(ewk_context_,
423 kEncryptedDatabaseFeature, true);
425 if (app_data_->setting_info() != NULL &&
426 app_data_->setting_info()->sound_mode() ==
427 wgt::parse::SettingInfo::SoundMode::EXCLUSIVE) {
428 ewk_context_tizen_extensible_api_string_set(ewk_context_, kSoundModeFeature,
432 if (app_data_->setting_info() != NULL &&
433 app_data_->setting_info()->background_vibration()) {
434 ewk_context_tizen_extensible_api_string_set(
435 ewk_context_, kBackgroundVibrationFeature, true);
438 if (app_data_->widget_info() != NULL &&
439 !app_data_->widget_info()->default_locale().empty()) {
440 locale_manager_->SetDefaultLocale(
441 app_data_->widget_info()->default_locale());
444 if (app_data_->widget_info() != NULL &&
445 app_data_->widget_info()->view_modes() == "fullscreen") {
446 window_->SetCurrentViewModeFullScreen(true);
447 window_->FullScreen(true);
450 if (app_data_->csp_info() != NULL || app_data_->csp_report_info() != NULL ||
451 app_data_->allowed_navigation_info() != NULL) {
452 security_model_version_ = 2;
453 if (app_data_->csp_info() == NULL ||
454 app_data_->csp_info()->security_rules().empty()) {
455 csp_rule_ = kDefaultCSPRule;
457 csp_rule_ = app_data_->csp_info()->security_rules();
459 if (app_data_->csp_report_info() != NULL &&
460 !app_data_->csp_report_info()->security_rules().empty()) {
461 csp_report_rule_ = app_data_->csp_report_info()->security_rules();
463 ewk_context_tizen_extensible_api_string_set(ewk_context_, kCSPFeature,
466 security_model_version_ = 1;
469 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
470 // Set manual rotation
471 window_->EnableManualRotation(true);
472 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
477 void WebApplication::SetupTizenVersion() {
478 if (app_data_->tizen_application_info() != NULL &&
479 !app_data_->tizen_application_info()->required_version().empty()) {
480 std::string tizen_version = app_data_->tizen_application_info()->required_version();
481 std::vector<unsigned> parsed_tizen_version = ParseTizenVersion(tizen_version);
482 m_tizenCompatibilitySettings.m_major = parsed_tizen_version[0];
483 m_tizenCompatibilitySettings.m_minor = parsed_tizen_version[1];
484 m_tizenCompatibilitySettings.m_release = parsed_tizen_version[2];
488 bool WebApplication::tizenWebKitCompatibilityEnabled() const {
489 return m_tizenCompatibilitySettings.tizenWebKitCompatibilityEnabled();
492 void WebApplication::Launch(std::unique_ptr<common::AppControl> appcontrol) {
493 // send widget info to injected bundle
494 ewk_context_tizen_app_id_set(ewk_context_, appid_.c_str());
499 WebView* view = new WebView(window_, ewk_context_);
501 SetupWebViewCompatibilitySettings(view);
503 std::unique_ptr<common::ResourceManager::Resource> res =
504 resource_manager_->GetStartResource(appcontrol.get());
505 view->SetDefaultEncoding(res->encoding());
507 STEP_PROFILE_END("OnCreate -> URL Set");
508 STEP_PROFILE_START("URL Set -> Rendered");
510 window_->SetContent(view->evas_object());
512 #ifdef PROFILE_MOBILE
513 // rotate and resize window forcibily for landscape mode.
514 // window rotate event is generated after window show. so
515 // when app get width and height from viewport, wrong value can be returned.
516 if (app_data_->setting_info()->screen_orientation() ==
517 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
518 LOGGER(DEBUG) << "rotate and resize window for landscape mode";
519 elm_win_rotation_with_resize_set(window_->evas_object(), 270);
520 evas_norender(evas_object_evas_get(window_->evas_object()));
522 #endif // PROFILE_MOBILE
524 view->LoadUrl(res->uri(), res->mime());
525 view_stack_.push_front(view);
527 #ifdef PROFILE_WEARABLE
528 // ewk_view_bg_color_set is not working at webview initialization.
529 if (app_data_->app_type() == common::ApplicationData::WATCH) {
530 view->SetBGColor(0, 0, 0, 255);
532 #endif // PROFILE_WEARABLE
534 if (appcontrol->data(AUL_K_DEBUG) == "1") {
536 LaunchInspector(appcontrol.get());
538 if (appcontrol->data(kVerboseKey) == "true") {
539 verbose_mode_ = true;
544 #ifdef PROFILE_MOBILE
545 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
546 LOGGER(DEBUG) << "Show window after launch for remote URL";
550 #endif // PROFILE_MOBILE
553 void WebApplication::AppControl(
554 std::unique_ptr<common::AppControl> appcontrol) {
555 std::unique_ptr<common::ResourceManager::Resource> res =
556 resource_manager_->GetStartResource(appcontrol.get());
558 bool do_reset = res->should_reset();
561 std::string current_page = view_stack_.front()->GetUrl();
562 std::string localized_page = res->uri();
564 if (current_page != localized_page) {
567 SendAppControlEvent();
571 // handle http://tizen.org/appcontrol/operation/main operation specially.
572 // only menu-screen app can send launch request with main operation.
573 // in this case, web app should have to resume web app not reset.
574 if (do_reset && (appcontrol->operation() == kAppControlMain)){
575 LOGGER(DEBUG) << "resume app for main operation";
577 SendAppControlEvent();
583 WebView* view = view_stack_.front();
585 SetupWebViewCompatibilitySettings(view);
586 view->SetDefaultEncoding(res->encoding());
587 view->LoadUrl(res->uri(), res->mime());
588 window_->SetContent(view->evas_object());
591 if (!debug_mode_ && appcontrol->data(AUL_K_DEBUG) == "1") {
593 LaunchInspector(appcontrol.get());
595 if (!verbose_mode_ && appcontrol->data(kVerboseKey) == "true") {
596 verbose_mode_ = true;
601 void WebApplication::SendAppControlEvent() {
602 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
603 view_stack_.front()->EvalJavascript(kAppControlEventScript);
606 void WebApplication::ClearViewStack() {
607 window_->SetContent(NULL);
608 WebView* front = view_stack_.front();
609 auto it = view_stack_.begin();
610 for (; it != view_stack_.end(); ++it) {
617 view_stack_.push_front(front);
620 void WebApplication::Resume() {
621 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
622 view_stack_.front()->SetVisibility(true);
624 if (app_data_->setting_info() != NULL &&
625 app_data_->setting_info()->background_support_enabled()) {
629 auto it = view_stack_.begin();
630 for (; it != view_stack_.end(); ++it) {
635 void WebApplication::Suspend() {
636 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
637 view_stack_.front()->SetVisibility(false);
639 if (app_data_->setting_info() != NULL &&
640 app_data_->setting_info()->background_support_enabled()) {
641 LOGGER(DEBUG) << "gone background (backgroud support enabed)";
645 auto it = view_stack_.begin();
646 for (; it != view_stack_.end(); ++it) {
651 void WebApplication::Terminate() {
652 // Just process closing page once.
653 if (is_terminate_called_ || is_close_page_called_)
656 is_terminate_called_ = true;
661 Eina_Bool WebApplication::ClosePageInExtendedMainLoop(void* user_data)
664 struct Timer* main_loop = static_cast<Timer*>(user_data);
665 main_loop->application->ClosePage();
666 return ECORE_CALLBACK_CANCEL;
669 void WebApplication::ProcessClosingPage() {
672 main_loop.application = this;
673 main_loop.timer = ecore_timer_add(MAIN_LOOP_INTERVAL,
674 ClosePageInExtendedMainLoop, &main_loop);
675 if (!main_loop.timer)
676 LOGGER(ERROR) << "It's failed to create main_loop timer";
677 LOGGER(DEBUG) << "Defer termination of main loop";
678 ecore_main_loop_begin();
679 ecore_timer_del(main_loop.timer);
680 ecore_timer_del(session_counter.timer);
683 void WebApplication::ClosePage() {
685 is_close_page_called_ = true;
687 int valid_evas_object_count = 0;
688 auto it = view_stack_.begin();
689 if (it != view_stack_.end()) {
690 for (; it != view_stack_.end(); ++it) {
691 (*it)->ReplyToJavascriptDialog();
692 view_stack_.front()->SetVisibility(false);
693 if (ewk_view_page_close((*it)->evas_object())) {
694 LOGGER(DEBUG) << "ewk_view_page_close returns true";
695 valid_evas_object_count++;
697 LOGGER(DEBUG) << "ewk_view_page_close returns false";
701 if (valid_evas_object_count == 0)
705 void WebApplication::Exit() {
706 if (!is_terminate_called_)
707 ecore_main_loop_quit();
710 LOGGER(DEBUG) << "terminator_";
713 LOGGER(ERROR) << "There's no registered terminator.";
718 void WebApplication::OnCreatedNewWebView(WebView* /*view*/, WebView* new_view) {
719 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
720 view_stack_.front()->SetVisibility(false);
722 SetupWebView(new_view);
723 SetupWebViewCompatibilitySettings(new_view);
724 view_stack_.push_front(new_view);
725 window_->SetContent(new_view->evas_object());
728 void WebApplication::RemoveWebViewFromStack(WebView* view) {
729 if (view_stack_.size() == 0)
732 WebView* current = view_stack_.front();
733 if (current == view) {
734 // In order to prevent the crash issue due to the callback
735 // which occur after destroying WebApplication class,
736 // we have to set the 'SetEventListener' to NULL.
737 view->SetEventListener(NULL);
738 view_stack_.pop_front();
740 auto found = std::find(view_stack_.begin(), view_stack_.end(), view);
741 if (found != view_stack_.end()) {
742 // In order to prevent the crash issue due to the callback
743 // which occur after destroying WebApplication class,
744 // we have to set the 'SetEventListener' to NULL.
745 view->SetEventListener(NULL);
746 view_stack_.erase(found);
750 if (view_stack_.size() == 0) {
751 auto extension_server = XWalkExtensionServer::GetInstance();
752 LOGGER(DEBUG) << "Shutdown extension server";
753 extension_server->Shutdown();
754 } else if (current != view_stack_.front()) {
755 view_stack_.front()->SetVisibility(true);
756 window_->SetContent(view_stack_.front()->evas_object());
759 // Delete after the callback context(for ewk view) was not used
760 ecore_idler_add([](void* view) {
761 WebView* obj = static_cast<WebView*>(view);
768 Eina_Bool WebApplication::CheckPluginSession(void* user_data)
770 struct Timer* session_counter = static_cast<Timer*>(user_data);
771 if(XWalkExtensionRendererController::plugin_session_count > 0) {
772 LOGGER(ERROR) << "plugin_session_count : " <<
773 XWalkExtensionRendererController::plugin_session_count;
774 return ECORE_CALLBACK_RENEW;
776 LOGGER(DEBUG) << "plugin_session_count : " <<
777 XWalkExtensionRendererController::plugin_session_count;
778 LOGGER(DEBUG) << "Execute deferred termination of main loop";
779 session_counter->application->Exit();
780 return ECORE_CALLBACK_CANCEL;
783 void WebApplication::OnClosedWebView(WebView* view) {
784 // Reply to javascript dialog for preventing freeze issue.
785 view->ReplyToJavascriptDialog();
786 RemoveWebViewFromStack(view);
788 LOGGER(DEBUG) << "plugin_session_count : " <<
789 XWalkExtensionRendererController::plugin_session_count;
790 session_counter.application = this;
791 session_counter.timer = ecore_timer_add(SESSION_COUNTER_INTERVAL,
792 CheckPluginSession, &session_counter);
793 if (!session_counter.timer)
794 LOGGER(ERROR) << "It's failed to create session_counter timer";
797 void WebApplication::OnReceivedWrtMessage(WebView* view,
798 Ewk_IPC_Wrt_Message_Data* msg) {
799 Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg);
801 #define TYPE_BEGIN(x) (!strncmp(msg_type, x, strlen(x)))
802 #define TYPE_IS(x) (!strcmp(msg_type, x))
804 if (TYPE_BEGIN("xwalk://")) {
805 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
806 extension_server->HandleIPCMessage(msg);
808 Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg);
809 Eina_Stringshare* msg_ref_id =
810 ewk_ipc_wrt_message_data_reference_id_get(msg);
811 Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg);
813 if (TYPE_IS("tizen://hide")) {
816 } else if (TYPE_IS("tizen://exit")) {
818 // Reply to javascript dialog for preventing freeze issue.
819 view->ReplyToJavascriptDialog();
820 ecore_idler_add(ExitAppIdlerCallback, this);
821 } else if (TYPE_IS("tizen://changeUA")) {
823 // Change UserAgent of current WebView
825 if (view_stack_.size() > 0 && view_stack_.front() != NULL) {
826 ret = view_stack_.front()->SetUserAgent(std::string(msg_value));
829 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
830 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
831 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
833 ewk_ipc_wrt_message_data_value_set(ans, "success");
835 ewk_ipc_wrt_message_data_value_set(ans, "failed");
836 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
837 LOGGER(ERROR) << "Failed to send response";
839 ewk_ipc_wrt_message_data_del(ans);
840 } else if (TYPE_IS("tizen://deleteAllCookies")) {
841 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
842 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
843 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
844 if (ClearCookie(ewk_context_))
845 ewk_ipc_wrt_message_data_value_set(ans, "success");
847 ewk_ipc_wrt_message_data_value_set(ans, "failed");
848 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
849 LOGGER(ERROR) << "Failed to send response";
851 ewk_ipc_wrt_message_data_del(ans);
852 } else if (TYPE_IS("tizen://hide_splash_screen")) {
853 splash_screen_->HideSplashScreen(SplashScreen::HideReason::CUSTOM);
856 eina_stringshare_del(msg_ref_id);
857 eina_stringshare_del(msg_id);
858 eina_stringshare_del(msg_value);
864 eina_stringshare_del(msg_type);
867 void WebApplication::OnOrientationLock(
868 WebView* view, bool lock,
869 NativeWindow::ScreenOrientation preferred_rotation) {
870 if (view_stack_.size() == 0) return;
872 // Only top-most view can set the orientation relate operation
873 if (view_stack_.front() != view) return;
875 // This is for 2.4 compatibility. Requested by Webengine Team.
877 // In Tizen 2.4 WebKit locking screen orientation was possible with Web API
878 // screen.lockOrientation(). This API was deprecated and replaced with
879 // screen.orientation.lock(). But for compatibility case we need to support
881 if(!tizenWebKitCompatibilityEnabled()) {
882 auto orientaion_setting = app_data_->setting_info() != NULL
883 ? app_data_->setting_info()->screen_orientation()
884 : wgt::parse::SettingInfo::ScreenOrientation::AUTO;
885 if (wgt::parse::SettingInfo::ScreenOrientation::AUTO != orientaion_setting) {
886 // If Tizen version is 3.0, it return.
892 window_->SetRotationLock(preferred_rotation);
894 window_->SetAutoRotation();
898 void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
899 // NOTE: This code is added to enable back-key on remote URL
900 bool enabled = app_data_->setting_info() != NULL
901 ? app_data_->setting_info()->hwkey_enabled()
904 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
905 if (kKeyNameBack == keyname) {
906 LOGGER(DEBUG) << "Back to previous page for remote URL";
908 view->EvalJavascript(kBackKeyEventScript);
909 if (!view->Backward()) {
910 LOGGER(DEBUG) << "Terminate";
917 if (enabled && kKeyNameBack == keyname) {
918 view->EvalJavascript(kBackKeyEventScript);
919 // NOTE: This code is added for backward compatibility.
920 // If the 'backbutton_presence' is true, WebView should be navigated back.
921 if ((app_data_->setting_info() != NULL &&
922 app_data_->setting_info()->backbutton_presence()) ||
923 (app_data_->widget_info() != NULL &&
924 app_data_->widget_info()->view_modes() == "windowed")) {
925 if (!view->Backward()) {
926 LOGGER(DEBUG) << "Terminate";
930 } else if (enabled && kKeyNameMenu == keyname) {
931 view->EvalJavascript(kMenuKeyEventScript);
935 void WebApplication::OnLanguageChanged() {
936 lang_changed_mode_ = true;
937 locale_manager_->UpdateSystemLocale();
938 ewk_context_cache_clear(ewk_context_);
939 auto it = view_stack_.begin();
940 for (; it != view_stack_.end(); ++it) {
945 void WebApplication::OnConsoleMessage(const std::string& msg, int level) {
946 static bool enabled = (getenv(kConsoleLogEnableKey) != NULL);
949 std::string split_msg = msg;
950 std::size_t pos = msg.find(kResWgtPath);
951 if (pos != std::string::npos) {
952 split_msg = msg.substr(pos + strlen(kResWgtPath));
955 if (debug_mode_ || verbose_mode_ || enabled) {
956 int dlog_level = DLOG_DEBUG;
958 case EWK_CONSOLE_MESSAGE_LEVEL_WARNING:
959 dlog_level = DLOG_WARN;
961 case EWK_CONSOLE_MESSAGE_LEVEL_ERROR:
962 dlog_level = DLOG_ERROR;
965 dlog_level = DLOG_DEBUG;
968 LOGGER_RAW(dlog_level, kConsoleMessageLogTag)
969 << "[" << app_data_->pkg_id() << "] " << split_msg;
973 void WebApplication::OnLowMemory() {
974 ewk_context_cache_clear(ewk_context_);
975 ewk_context_notify_low_memory(ewk_context_);
978 void WebApplication::OnSoftKeyboardChangeEvent(WebView* /*view*/,
979 SoftKeyboardChangeEventValue softkeyboard_value) {
980 LOGGER(DEBUG) << "OnSoftKeyboardChangeEvent";
981 std::stringstream script;
984 << "var __event = document.createEvent(\"CustomEvent\");\n"
985 << "var __detail = {};\n"
986 << "__event.initCustomEvent(\"softkeyboardchange\",true,true,__detail);\n"
987 << "__event.state = \"" << softkeyboard_value.state << "\";\n"
988 << "__event.width = " << softkeyboard_value.width << ";\n"
989 << "__event.height = " << softkeyboard_value.height << ";\n"
990 << "document.dispatchEvent(__event);\n"
992 << "for (var i=0; i < window.frames.length; i++)\n"
993 << "{ window.frames[i].document.dispatchEvent(__event); }"
995 std::string kSoftKeyboardScript = script.str();
996 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
997 view_stack_.front()->EvalJavascript(kSoftKeyboardScript.c_str());
1000 #ifdef ROTARY_EVENT_FEATURE_SUPPORT
1001 void WebApplication::OnRotaryEvent(WebView* /*view*/,
1002 RotaryEventType type) {
1003 LOGGER(DEBUG) << "OnRotaryEvent";
1004 std::stringstream script;
1007 << "var __event = document.createEvent(\"CustomEvent\");\n"
1008 << "var __detail = {};\n"
1009 << "__event.initCustomEvent(\"rotarydetent\", true, true, __detail);\n"
1010 << "__event.detail.direction = \""
1011 << (type == RotaryEventType::CLOCKWISE ? "CW" : "CCW")
1013 << "document.dispatchEvent(__event);\n"
1015 << "for (var i=0; i < window.frames.length; i++)\n"
1016 << "{ window.frames[i].document.dispatchEvent(__event); }"
1018 std::string kRotaryEventScript = script.str();
1019 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1020 view_stack_.front()->EvalJavascript(kRotaryEventScript.c_str());
1022 #endif // ROTARY_EVENT_FEATURE_SUPPORT
1024 void WebApplication::OnTimeTick(long time) {
1026 LOGGER(DEBUG) << "TimeTick";
1027 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1028 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1032 void WebApplication::OnAmbientTick(long time) {
1033 LOGGER(DEBUG) << "AmbientTick";
1034 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1035 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1038 void WebApplication::OnAmbientChanged(bool ambient_mode) {
1039 LOGGER(DEBUG) << "AmbientChanged";
1040 std::stringstream script;
1043 << "var __event = document.createEvent(\"CustomEvent\");\n"
1044 << "var __detail = {};\n"
1045 << "__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);\n"
1046 << "__event.detail.ambientMode = "
1047 << (ambient_mode ? "true" : "false") << ";\n"
1048 << "document.dispatchEvent(__event);\n"
1050 << "for (var i=0; i < window.frames.length; i++)\n"
1051 << "{ window.frames[i].document.dispatchEvent(__event); }"
1053 std::string kAmbientChangedEventScript = script.str();
1054 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1055 view_stack_.front()->EvalJavascript(kAmbientChangedEventScript.c_str());
1058 bool WebApplication::OnContextMenuDisabled(WebView* /*view*/) {
1059 return !(app_data_->setting_info() != NULL
1060 ? app_data_->setting_info()->context_menu_enabled()
1064 void WebApplication::OnLoadStart(WebView* /*view*/) {
1065 LOGGER(DEBUG) << "LoadStart";
1068 void WebApplication::OnLoadFinished(WebView* /*view*/) {
1069 LOGGER(DEBUG) << "LoadFinished";
1070 splash_screen_->HideSplashScreen(SplashScreen::HideReason::LOADFINISHED);
1073 void WebApplication::OnRendered(WebView* view) {
1074 STEP_PROFILE_END("URL Set -> Rendered");
1075 STEP_PROFILE_END("Start -> Launch Completed");
1076 LOGGER(DEBUG) << "Rendered";
1077 splash_screen_->HideSplashScreen(SplashScreen::HideReason::RENDERED);
1079 // Do not show(), active() for language change
1080 if(lang_changed_mode_ == false){
1081 // Show window after frame rendered.
1082 #ifdef PROFILE_MOBILE
1083 if (common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
1087 #else // PROFILE_MOBILE
1093 lang_changed_mode_ = false;
1097 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
1098 void WebApplication::OnRotatePrepared(WebView* /*view*/) {
1099 window_->ManualRotationDone();
1101 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
1103 void WebApplication::LaunchInspector(common::AppControl* appcontrol) {
1104 unsigned int port = ewk_context_inspector_server_start(ewk_context_, 0);
1105 std::stringstream ss;
1107 std::map<std::string, std::vector<std::string>> data;
1108 data[kPortKey] = {ss.str()};
1109 appcontrol->Reply(data);
1112 void WebApplication::SetupWebView(WebView* view) {
1113 view->SetEventListener(this);
1116 if (security_model_version_ == 2) {
1117 view->SetCSPRule(csp_rule_, false);
1118 if (!csp_report_rule_.empty()) {
1119 view->SetCSPRule(csp_report_rule_, true);
1123 // Setup longpolling value
1124 if (app_data_->setting_info() != NULL &&
1125 app_data_->setting_info()->long_polling()) {
1126 boost::optional <unsigned int> polling_val(app_data_->setting_info()->long_polling());
1127 unsigned long *ptr = reinterpret_cast <unsigned long *> (&polling_val.get());
1128 view->SetLongPolling(*ptr);
1132 void WebApplication::SetupWebViewCompatibilitySettings(WebView* view) {
1133 if (tizenWebKitCompatibilityEnabled()) {
1134 Ewk_Settings* settings = ewk_view_settings_get(view->evas_object());
1135 ewk_settings_tizen_compatibility_mode_set(settings,
1136 m_tizenCompatibilitySettings.m_major,
1137 m_tizenCompatibilitySettings.m_minor,
1138 m_tizenCompatibilitySettings.m_release);
1139 ewk_settings_text_autosizing_enabled_set(settings, EINA_FALSE);
1143 bool WebApplication::OnDidNavigation(WebView* /*view*/,
1144 const std::string& url) {
1146 // except(file , http, https, app) pass to appcontrol and return false
1147 if (ProcessWellKnownScheme(url)) {
1151 // send launch request for blocked URL to guarrenty backward-compatibility.
1152 if (resource_manager_->AllowNavigation(url)) {
1155 LOGGER(DEBUG) << "URL is blocked. send launch request for URL : " << url;
1156 std::unique_ptr<common::AppControl> request(
1157 common::AppControl::MakeAppcontrolFromURL(url));
1158 if (request.get() == NULL || !request->LaunchRequest()) {
1159 LOGGER(ERROR) << "Fail to send appcontrol request";
1165 void WebApplication::OnNotificationPermissionRequest(
1166 WebView*, const std::string& url,
1167 std::function<void(bool)> result_handler) {
1168 auto db = common::AppDB::GetInstance();
1169 std::string reminder =
1170 db->Get(kDBPrivateSection, kNotificationPermissionPrefix + url);
1171 if (reminder == "allowed") {
1172 result_handler(true);
1174 } else if (reminder == "denied") {
1175 result_handler(false);
1179 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1180 // Remote Domain: Popup user prompt.
1181 if (common::utils::StartsWith(url, "file://") &&
1182 FindPrivilegeFromConfig(app_data_, kNotificationPrivilege)) {
1183 result_handler(true);
1187 Popup* popup = Popup::CreatePopup(window_);
1188 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1189 popup->SetTitle(popup_string::kPopupTitleWebNotification);
1190 popup->SetBody(popup_string::kPopupBodyWebNotification);
1191 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1192 popup->SetResultHandler(
1193 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1194 bool result = popup->GetButtonResult();
1195 bool remember = popup->GetCheckBoxResult();
1197 db->Set(kDBPrivateSection, kNotificationPermissionPrefix + url,
1198 result ? "allowed" : "denied");
1200 result_handler(result);
1206 void WebApplication::OnGeolocationPermissionRequest(
1207 WebView*, const std::string& url,
1208 std::function<void(bool)> result_handler) {
1209 auto db = common::AppDB::GetInstance();
1210 std::string reminder =
1211 db->Get(kDBPrivateSection, kGeolocationPermissionPrefix + url);
1212 if (reminder == "allowed") {
1213 result_handler(true);
1215 } else if (reminder == "denied") {
1216 result_handler(false);
1220 // Local Domain: Grant permission if defined, otherwise block execution.
1221 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1222 if (!FindPrivilegeFromConfig(app_data_, kLocationPrivilege) &&
1223 !FindPrivilegeFromCynara(kLocationPrivilege)) {
1224 result_handler(false);
1228 if (common::utils::StartsWith(url, "file://")) {
1229 result_handler(true);
1233 Popup* popup = Popup::CreatePopup(window_);
1234 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1235 popup->SetTitle(popup_string::kPopupTitleGeoLocation);
1236 popup->SetBody(popup_string::kPopupBodyGeoLocation);
1237 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1238 popup->SetResultHandler(
1239 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1240 bool result = popup->GetButtonResult();
1241 bool remember = popup->GetCheckBoxResult();
1243 db->Set(kDBPrivateSection, kGeolocationPermissionPrefix + url,
1244 result ? "allowed" : "denied");
1246 result_handler(result);
1252 void WebApplication::OnQuotaExceed(WebView*, const std::string& url,
1253 std::function<void(bool)> result_handler) {
1254 auto db = common::AppDB::GetInstance();
1255 std::string reminder =
1256 db->Get(kDBPrivateSection, kQuotaPermissionPrefix + url);
1257 if (reminder == "allowed") {
1258 result_handler(true);
1260 } else if (reminder == "denied") {
1261 result_handler(false);
1265 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1266 // Remote Domain: Popup user prompt.
1267 if (common::utils::StartsWith(url, "file://") &&
1268 FindPrivilegeFromConfig(app_data_, kStoragePrivilege)) {
1269 result_handler(true);
1273 Popup* popup = Popup::CreatePopup(window_);
1274 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1275 popup->SetTitle(popup_string::kPopupTitleWebStorage);
1276 popup->SetBody(popup_string::kPopupBodyWebStorage);
1277 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1278 popup->SetResultHandler(
1279 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1280 bool result = popup->GetButtonResult();
1281 bool remember = popup->GetCheckBoxResult();
1283 db->Set(kDBPrivateSection, kQuotaPermissionPrefix + url,
1284 result ? "allowed" : "denied");
1286 result_handler(result);
1292 void WebApplication::OnAuthenticationRequest(
1293 WebView*, const std::string& /*url*/, const std::string& /*message*/,
1294 std::function<void(bool submit, const std::string& id,
1295 const std::string& password)> result_handler) {
1296 Popup* popup = Popup::CreatePopup(window_);
1297 popup->SetButtonType(Popup::ButtonType::LoginCancelButton);
1298 popup->SetFirstEntry(popup_string::kPopupLabelAuthusername,
1299 Popup::EntryType::Edit);
1300 popup->SetSecondEntry(popup_string::kPopupLabelPassword,
1301 Popup::EntryType::PwEdit);
1302 popup->SetTitle(popup_string::kPopupTitleAuthRequest);
1303 popup->SetBody(popup_string::kPopupBodyAuthRequest);
1304 popup->SetResultHandler([result_handler](Popup* popup, void* /*user_data*/) {
1305 bool result = popup->GetButtonResult();
1306 std::string id = popup->GetFirstEntryResult();
1307 std::string passwd = popup->GetSecondEntryResult();
1308 result_handler(result, id, passwd);
1314 void WebApplication::OnCertificateAllowRequest(
1315 WebView*, const std::string& url, const std::string& pem,
1316 std::function<void(bool allow)> result_handler) {
1317 auto db = common::AppDB::GetInstance();
1318 std::string reminder =
1319 db->Get(kDBPrivateSection, kCertificateAllowPrefix + pem);
1320 if (reminder == "allowed") {
1321 result_handler(true);
1323 } else if (reminder == "denied") {
1324 result_handler(false);
1328 Popup* popup = Popup::CreatePopup(window_);
1329 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1330 popup->SetTitle(popup_string::kPopupTitleCert);
1331 popup->SetBody(popup_string::GetText(
1332 popup_string::kPopupBodyCert) + "\n\n" + url);
1333 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1334 popup->SetResultHandler(
1335 [db, result_handler, pem](Popup* popup, void* /*user_data*/) {
1336 bool result = popup->GetButtonResult();
1337 bool remember = popup->GetCheckBoxResult();
1339 db->Set(kDBPrivateSection, kCertificateAllowPrefix + pem,
1340 result ? "allowed" : "denied");
1342 result_handler(result);
1348 void WebApplication::OnUsermediaPermissionRequest(
1349 WebView*, const std::string& url,
1350 std::function<void(bool)> result_handler) {
1351 auto db = common::AppDB::GetInstance();
1352 std::string reminder =
1353 db->Get(kDBPrivateSection, kUsermediaPermissionPrefix + url);
1354 if (reminder == "allowed") {
1355 result_handler(true);
1357 } else if (reminder == "denied") {
1358 result_handler(false);
1362 // Local Domain: Grant permission if defined, otherwise block execution.
1363 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1364 if (!FindPrivilegeFromConfig(app_data_, kUsermediaPrivilege) &&
1365 !(FindPrivilegeFromCynara(kCameraPrivilege) && FindPrivilegeFromCynara(kRecordPrivilege))) {
1366 result_handler(false);
1370 if (common::utils::StartsWith(url, "file://")) {
1371 result_handler(true);
1375 Popup* popup = Popup::CreatePopup(window_);
1376 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1377 popup->SetTitle(popup_string::kPopupTitleUserMedia);
1378 popup->SetBody(popup_string::kPopupBodyUserMedia);
1379 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1380 popup->SetResultHandler(
1381 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1382 bool result = popup->GetButtonResult();
1383 bool remember = popup->GetCheckBoxResult();
1385 db->Set(kDBPrivateSection, kUsermediaPermissionPrefix + url,
1386 result ? "allowed" : "denied");
1388 result_handler(result);
1394 } // namespace runtime