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 #ifdef PROFILE_MOBILE
147 // window signal callback
148 const char *kEdjeShowProgressSignal = "show,progress,signal";
149 const char *kEdjeHideProgressSignal = "hide,progress,signal";
152 const std::string kViewmodeTypeFullscreen = "fullscreen";
153 const std::string kViewmodeTypeWindowed = "windowed";
155 // Looking for added privilege by Application developer in config.xml.
156 bool FindPrivilegeFromConfig(common::ApplicationData* app_data,
157 const std::string& privilege) {
158 if (app_data->permissions_info().get() == NULL) return false;
159 LOGGER(INFO) << "Finding privilege from config.xml";
160 auto it = app_data->permissions_info()->GetAPIPermissions().begin();
161 auto end = app_data->permissions_info()->GetAPIPermissions().end();
162 for (; it != end; ++it) {
163 if (*it == privilege) return true;
168 // Looking for given default privilege when application installed.
169 bool FindPrivilegeFromCynara(const std::string& privilege_name) {
170 LOGGER(INFO) << "Finding privilege from cynara db";
171 static constexpr char kSmackLabelFilePath[] = "/proc/self/attr/current";
172 std::ifstream file(kSmackLabelFilePath);
173 if (!file.is_open()) {
174 LOGGER(ERROR) << "Failed to open " << kSmackLabelFilePath;
179 cynara* p_cynara = NULL;
180 ret = cynara_initialize(&p_cynara, 0);
181 if (CYNARA_API_SUCCESS != ret) {
182 LOGGER(ERROR) << "Failed. The result of cynara_initialize() : " << ret;
186 std::string uid = std::to_string(getuid());
187 std::string smack_label{std::istreambuf_iterator<char>(file),
188 std::istreambuf_iterator<char>()};
191 ret = cynara_check(p_cynara, smack_label.c_str(), "", uid.c_str(), privilege_name.c_str());
192 if (CYNARA_API_ACCESS_ALLOWED != ret) {
193 LOGGER(ERROR) << "Access denied. The result of cynara_check() : " << ret;
195 LOGGER(INFO) << "Access allowed! The result of cynara_check() : " << ret;
200 ret = cynara_finish(p_cynara);
201 if (CYNARA_API_SUCCESS != ret) {
202 LOGGER(ERROR) << "Failed. The result of cynara_finish() : " << ret;
209 static void SendDownloadRequest(const std::string& url) {
210 common::AppControl request;
211 request.set_operation(APP_CONTROL_OPERATION_DOWNLOAD);
212 request.set_uri(url);
213 request.LaunchRequest();
216 static void InitializeNotificationCallback(Ewk_Context* ewk_context,
217 WebApplication* app) {
218 auto show = [](Ewk_Context*, Ewk_Notification* noti, void* user_data) {
219 WebApplication* self = static_cast<WebApplication*>(user_data);
220 if (self == NULL) return;
221 uint64_t id = ewk_notification_id_get(noti);
222 std::string title(ewk_notification_title_get(noti)
223 ? ewk_notification_title_get(noti)
226 ewk_notification_body_get(noti) ? ewk_notification_body_get(noti) : "");
227 std::string icon_path = self->data_path() + "/" + kNotiIconFile;
228 if (!ewk_notification_icon_save_as_png(noti, icon_path.c_str())) {
231 if (NotificationManager::GetInstance()->Show(id, title, body, icon_path))
232 ewk_notification_showed(id);
234 auto hide = [](Ewk_Context*, uint64_t noti_id, void*) {
235 NotificationManager::GetInstance()->Hide(noti_id);
236 ewk_notification_closed(noti_id, EINA_FALSE);
238 ewk_context_notification_callbacks_set(ewk_context, show, hide, app);
241 static Eina_Bool ExitAppIdlerCallback(void* data) {
242 WebApplication* app = static_cast<WebApplication*>(data);
245 LOGGER(DEBUG) << "Terminate";
249 return ECORE_CALLBACK_CANCEL;
252 static bool ClearCookie(Ewk_Context* ewk_context) {
253 Ewk_Cookie_Manager* cookie_manager =
254 ewk_context_cookie_manager_get(ewk_context);
255 if (!cookie_manager) {
256 LOGGER(ERROR) << "Fail to get cookie manager";
259 ewk_cookie_manager_cookies_clear(cookie_manager);
263 static bool ProcessWellKnownScheme(const std::string& url) {
264 if (common::utils::StartsWith(url, "file:") ||
265 common::utils::StartsWith(url, "app:") ||
266 common::utils::StartsWith(url, "data:") ||
267 common::utils::StartsWith(url, "http:") ||
268 common::utils::StartsWith(url, "https:") ||
269 common::utils::StartsWith(url, "widget:") ||
270 common::utils::StartsWith(url, "about:") ||
271 common::utils::StartsWith(url, "blob:")) {
275 std::unique_ptr<common::AppControl> request(
276 common::AppControl::MakeAppcontrolFromURL(url));
277 if (request.get() == NULL || !request->LaunchRequest()) {
278 LOGGER(ERROR) << "Fail to send appcontrol request";
279 SLoggerE("Fail to send appcontrol request [%s]", url.c_str());
282 // Should return true, to stop the WebEngine progress step about this URL
288 std::vector<unsigned> ParseTizenVersion(const std::string& tizen_string) {
289 std::vector<unsigned> version(3, 0);
290 for (unsigned i = 0, index = 0; i < tizen_string.size(); i++) {
292 if (isdigit(tizen_string[i]) && index < version.size())
293 version[index++] = atoi(&tizen_string[i]);
295 return std::vector<unsigned>(3, 0);
296 } else if (tizen_string[i] != '.')
297 return std::vector<unsigned>(3, 0);
302 WebApplication::WebApplication(
303 NativeWindow* window,
304 common::ApplicationData* app_data)
307 verbose_mode_(false),
308 lang_changed_mode_(false),
309 is_terminate_called_(false),
310 is_close_page_called_(false),
312 ewk_context_new_with_injected_bundle_path(INJECTED_BUNDLE_PATH)),
313 has_ownership_of_ewk_context_(true),
315 appid_(app_data->app_id()),
317 locale_manager_(new common::LocaleManager()) {
321 WebApplication::WebApplication(
322 NativeWindow* window,
323 common::ApplicationData* app_data,
324 Ewk_Context* context)
327 verbose_mode_(false),
328 lang_changed_mode_(false),
329 is_terminate_called_(false),
330 is_close_page_called_(false),
331 ewk_context_(context),
332 has_ownership_of_ewk_context_(false),
334 appid_(app_data->app_id()),
336 locale_manager_(new common::LocaleManager()) {
340 WebApplication::~WebApplication() {
341 window_->SetContent(NULL);
342 auto it = view_stack_.begin();
343 for (; it != view_stack_.end(); ++it) {
348 if (ewk_context_ && has_ownership_of_ewk_context_)
349 ewk_context_delete(ewk_context_);
352 bool WebApplication::Initialize() {
354 std::unique_ptr<char, decltype(std::free)*> path{app_get_data_path(),
356 app_data_path_ = path.get();
358 if (app_data_->setting_info() != NULL &&
359 app_data_->setting_info()->screen_orientation() ==
360 wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
361 ewk_context_tizen_extensible_api_string_set(ewk_context_,
362 kRotationLockFeature, true);
363 window_->SetAutoRotation();
364 } else if (app_data_->setting_info() != NULL &&
365 app_data_->setting_info()->screen_orientation() ==
366 wgt::parse::SettingInfo::ScreenOrientation::PORTRAIT) {
367 window_->SetRotationLock(NativeWindow::ScreenOrientation::PORTRAIT_PRIMARY);
368 } else if (app_data_->setting_info() != NULL &&
369 app_data_->setting_info()->screen_orientation() ==
370 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
371 window_->SetRotationLock(
372 NativeWindow::ScreenOrientation::LANDSCAPE_PRIMARY);
375 splash_screen_.reset(new SplashScreen(
376 window_, app_data_->splash_screen_info(), app_data_->application_path()));
377 resource_manager_.reset(
378 new common::ResourceManager(app_data_, locale_manager_.get()));
379 resource_manager_->set_base_resource_path(app_data_->application_path());
381 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
382 extension_server->SetupIPC(ewk_context_);
385 ewk_context_cache_model_set(ewk_context_, EWK_CACHE_MODEL_DOCUMENT_BROWSER);
388 auto cookie_manager = ewk_context_cookie_manager_get(ewk_context_);
389 ewk_cookie_manager_accept_policy_set(cookie_manager,
390 EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
392 // set persistent storage path
393 std::string cookie_path = data_path() + ".cookie";
394 ewk_cookie_manager_persistent_storage_set(
395 cookie_manager, cookie_path.c_str(),
396 EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
398 // vibration callback
399 auto vibration_start_callback = [](uint64_t ms, void*) {
400 platform::VibrationManager::GetInstance()->Start(static_cast<int>(ms));
402 auto vibration_stop_callback = [](void* /*user_data*/) {
403 platform::VibrationManager::GetInstance()->Stop();
405 ewk_context_vibration_client_callbacks_set(
406 ewk_context_, vibration_start_callback, vibration_stop_callback, NULL);
408 auto download_callback = [](const char* downloadUrl, void* /*data*/) {
409 SendDownloadRequest(downloadUrl);
411 ewk_context_did_start_download_callback_set(ewk_context_, download_callback,
413 InitializeNotificationCallback(ewk_context_, this);
415 if (FindPrivilegeFromConfig(app_data_, kFullscreenPrivilege)) {
416 ewk_context_tizen_extensible_api_string_set(ewk_context_,
417 kFullscreenFeature, true);
420 if (app_data_->setting_info() != NULL &&
421 app_data_->setting_info()->background_support_enabled()) {
422 ewk_context_tizen_extensible_api_string_set(
423 ewk_context_, kVisibilitySuspendFeature, true);
424 ewk_context_tizen_extensible_api_string_set(ewk_context_,
425 kBackgroundMusicFeature, true);
427 ewk_context_tizen_extensible_api_string_set(
428 ewk_context_, kVisibilitySuspendFeature, false);
429 ewk_context_tizen_extensible_api_string_set(ewk_context_,
430 kBackgroundMusicFeature, false);
432 ewk_context_tizen_extensible_api_string_set(ewk_context_,
433 kMediastreamRecordFeature, true);
434 ewk_context_tizen_extensible_api_string_set(ewk_context_,
435 kEncryptedDatabaseFeature, true);
437 if (app_data_->setting_info() != NULL &&
438 app_data_->setting_info()->sound_mode() ==
439 wgt::parse::SettingInfo::SoundMode::EXCLUSIVE) {
440 ewk_context_tizen_extensible_api_string_set(ewk_context_, kSoundModeFeature,
444 if (app_data_->setting_info() != NULL &&
445 app_data_->setting_info()->background_vibration()) {
446 ewk_context_tizen_extensible_api_string_set(
447 ewk_context_, kBackgroundVibrationFeature, true);
450 if (app_data_->widget_info() != NULL &&
451 !app_data_->widget_info()->default_locale().empty()) {
452 locale_manager_->SetDefaultLocale(
453 app_data_->widget_info()->default_locale());
456 if (app_data_->widget_info() != NULL &&
457 app_data_->widget_info()->view_modes() == kViewmodeTypeFullscreen) {
458 window_->SetCurrentViewModeFullScreen(true);
459 window_->FullScreen(true);
462 if (app_data_->csp_info() != NULL || app_data_->csp_report_info() != NULL ||
463 app_data_->allowed_navigation_info() != NULL) {
464 security_model_version_ = 2;
465 if (app_data_->csp_info() == NULL ||
466 app_data_->csp_info()->security_rules().empty()) {
467 // Add the workaround codes for backward compatibility with tizen 2.x
468 // by request of webengine. In the webapp of tizen 2.x, this patch allows
469 // the all CSP policy by using "allow *;" option.
470 if (app_data_->tizen_application_info() != NULL &&
471 !app_data_->tizen_application_info()->required_version().empty()) {
472 std::string tizen_version = app_data_->tizen_application_info()->required_version();
473 if (tizen_version[0] == '2')
474 csp_rule_ = "allow *;";
476 csp_rule_ = kDefaultCSPRule;
478 csp_rule_ = kDefaultCSPRule;
481 csp_rule_ = app_data_->csp_info()->security_rules();
483 if (app_data_->csp_report_info() != NULL &&
484 !app_data_->csp_report_info()->security_rules().empty()) {
485 csp_report_rule_ = app_data_->csp_report_info()->security_rules();
487 ewk_context_tizen_extensible_api_string_set(ewk_context_, kCSPFeature,
490 security_model_version_ = 1;
493 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
494 // Set manual rotation
495 window_->EnableManualRotation(true);
496 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
498 main_loop.application = NULL;
499 main_loop.timer = NULL;
500 session_counter.application = NULL;
501 session_counter.timer = NULL;
502 m_tizenCompatibilitySettings.m_major = 0;
503 m_tizenCompatibilitySettings.m_minor = 0;
504 m_tizenCompatibilitySettings.m_release = 0;
509 void WebApplication::SetupTizenVersion() {
510 if (app_data_->tizen_application_info() != NULL &&
511 !app_data_->tizen_application_info()->required_version().empty()) {
512 std::string tizen_version = app_data_->tizen_application_info()->required_version();
513 std::vector<unsigned> parsed_tizen_version = ParseTizenVersion(tizen_version);
514 m_tizenCompatibilitySettings.m_major = parsed_tizen_version[0];
515 m_tizenCompatibilitySettings.m_minor = parsed_tizen_version[1];
516 m_tizenCompatibilitySettings.m_release = parsed_tizen_version[2];
520 bool WebApplication::tizenWebKitCompatibilityEnabled() const {
521 return m_tizenCompatibilitySettings.tizenWebKitCompatibilityEnabled();
524 void WebApplication::Launch(std::unique_ptr<common::AppControl> appcontrol) {
525 // send widget info to injected bundle
526 ewk_context_tizen_app_id_set(ewk_context_, appid_.c_str());
531 WebView* view = new WebView(window_, ewk_context_);
533 SetupWebViewCompatibilitySettings(view);
535 std::unique_ptr<common::ResourceManager::Resource> res =
536 resource_manager_->GetStartResource(appcontrol.get());
537 view->SetDefaultEncoding(res->encoding());
539 STEP_PROFILE_END("OnCreate -> URL Set");
540 STEP_PROFILE_START("URL Set -> Rendered");
542 window_->SetContent(view->evas_object());
544 #ifdef PROFILE_MOBILE
545 // rotate and resize window forcibily for landscape mode.
546 // window rotate event is generated after window show. so
547 // when app get width and height from viewport, wrong value can be returned.
548 if (app_data_->setting_info()->screen_orientation() ==
549 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
550 LOGGER(DEBUG) << "rotate and resize window for landscape mode";
551 elm_win_rotation_with_resize_set(window_->evas_object(), 270);
552 evas_norender(evas_object_evas_get(window_->evas_object()));
554 #endif // PROFILE_MOBILE
556 view->LoadUrl(res->uri(), res->mime());
557 view_stack_.push_front(view);
559 #ifdef PROFILE_WEARABLE
560 // ewk_view_bg_color_set is not working at webview initialization.
561 if (app_data_->app_type() == common::ApplicationData::WATCH) {
562 view->SetBGColor(0, 0, 0, 255);
564 #endif // PROFILE_WEARABLE
566 if (appcontrol->data(AUL_K_DEBUG) == "1") {
568 LaunchInspector(appcontrol.get());
570 if (appcontrol->data(kVerboseKey) == "true") {
571 verbose_mode_ = true;
576 #ifdef PROFILE_MOBILE
577 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
578 LOGGER(DEBUG) << "Show window after launch for remote URL";
582 #endif // PROFILE_MOBILE
585 void WebApplication::AppControl(
586 std::unique_ptr<common::AppControl> appcontrol) {
587 std::unique_ptr<common::ResourceManager::Resource> res =
588 resource_manager_->GetStartResource(appcontrol.get());
590 bool do_reset = res->should_reset();
593 std::string current_page = view_stack_.front()->GetUrl();
594 std::string localized_page = res->uri();
596 if (current_page != localized_page) {
599 SendAppControlEvent();
603 // handle http://tizen.org/appcontrol/operation/main operation specially.
604 // only menu-screen app can send launch request with main operation.
605 // in this case, web app should have to resume web app not reset.
606 if (do_reset && (appcontrol->operation() == kAppControlMain)){
607 LOGGER(DEBUG) << "resume app for main operation";
609 SendAppControlEvent();
615 WebView* view = view_stack_.front();
617 SetupWebViewCompatibilitySettings(view);
618 view->SetDefaultEncoding(res->encoding());
619 view->LoadUrl(res->uri(), res->mime());
620 window_->SetContent(view->evas_object());
623 if (!debug_mode_ && appcontrol->data(AUL_K_DEBUG) == "1") {
625 LaunchInspector(appcontrol.get());
627 if (!verbose_mode_ && appcontrol->data(kVerboseKey) == "true") {
628 verbose_mode_ = true;
633 void WebApplication::SendAppControlEvent() {
634 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
635 view_stack_.front()->EvalJavascript(kAppControlEventScript);
638 void WebApplication::ClearViewStack() {
639 window_->SetContent(NULL);
640 WebView* front = view_stack_.front();
641 auto it = view_stack_.begin();
642 for (; it != view_stack_.end(); ++it) {
649 view_stack_.push_front(front);
652 void WebApplication::Resume() {
653 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
654 view_stack_.front()->SetVisibility(true);
656 if (app_data_->setting_info() != NULL &&
657 app_data_->setting_info()->background_support_enabled()) {
661 auto it = view_stack_.begin();
662 for (; it != view_stack_.end(); ++it) {
667 void WebApplication::Suspend() {
668 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
669 view_stack_.front()->SetVisibility(false);
671 if (app_data_->setting_info() != NULL &&
672 app_data_->setting_info()->background_support_enabled()) {
673 LOGGER(DEBUG) << "gone background (backgroud support enabed)";
677 auto it = view_stack_.begin();
678 for (; it != view_stack_.end(); ++it) {
683 void WebApplication::Terminate() {
684 // Just process closing page once.
685 if (is_terminate_called_ || is_close_page_called_)
688 is_terminate_called_ = true;
693 Eina_Bool WebApplication::ClosePageInExtendedMainLoop(void* user_data)
696 struct Timer* main_loop = static_cast<Timer*>(user_data);
697 main_loop->application->ClosePage();
698 return ECORE_CALLBACK_CANCEL;
701 void WebApplication::ProcessClosingPage() {
704 main_loop.application = this;
705 main_loop.timer = ecore_timer_add(MAIN_LOOP_INTERVAL,
706 ClosePageInExtendedMainLoop, &main_loop);
707 if (!main_loop.timer)
708 LOGGER(ERROR) << "It's failed to create main_loop timer";
709 LOGGER(DEBUG) << "Defer termination of main loop";
710 ecore_main_loop_begin();
711 ecore_timer_del(main_loop.timer);
712 ecore_timer_del(session_counter.timer);
715 void WebApplication::ClosePage() {
717 is_close_page_called_ = true;
719 int valid_evas_object_count = 0;
720 auto it = view_stack_.begin();
721 if (it != view_stack_.end()) {
722 for (; it != view_stack_.end(); ++it) {
723 (*it)->ReplyToJavascriptDialog();
724 view_stack_.front()->SetVisibility(false);
725 if (ewk_view_page_close((*it)->evas_object())) {
726 LOGGER(DEBUG) << "ewk_view_page_close returns true";
727 valid_evas_object_count++;
729 LOGGER(DEBUG) << "ewk_view_page_close returns false";
733 if (valid_evas_object_count == 0)
737 void WebApplication::Exit() {
738 if (!is_terminate_called_)
739 ecore_main_loop_quit();
741 switch (app_data_->app_type()) {
742 case common::ApplicationData::AppType::UI:
743 LOGGER(ERROR) << "app_ui_exit";
746 #ifdef WATCH_FACE_FEATURE_SUPPORT
747 case common::ApplicationData::AppType::WATCH:
748 LOGGER(ERROR) << "watch_ui_exit";
753 LOGGER(ERROR) << "default terminator";
758 void WebApplication::OnCreatedNewWebView(WebView* /*view*/, WebView* new_view) {
759 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
760 view_stack_.front()->SetVisibility(false);
762 SetupWebView(new_view);
763 SetupWebViewCompatibilitySettings(new_view);
764 view_stack_.push_front(new_view);
765 window_->SetContent(new_view->evas_object());
768 void WebApplication::RemoveWebViewFromStack(WebView* view) {
769 if (view_stack_.size() == 0)
772 WebView* current = view_stack_.front();
773 if (current == view) {
774 // In order to prevent the crash issue due to the callback
775 // which occur after destroying WebApplication class,
776 // we have to set the 'SetEventListener' to NULL.
777 view->SetEventListener(NULL);
778 view_stack_.pop_front();
780 auto found = std::find(view_stack_.begin(), view_stack_.end(), view);
781 if (found != view_stack_.end()) {
782 // In order to prevent the crash issue due to the callback
783 // which occur after destroying WebApplication class,
784 // we have to set the 'SetEventListener' to NULL.
785 view->SetEventListener(NULL);
786 view_stack_.erase(found);
790 if (view_stack_.size() == 0) {
791 auto extension_server = XWalkExtensionServer::GetInstance();
792 LOGGER(DEBUG) << "Shutdown extension server";
793 extension_server->Shutdown();
794 #if !defined(TIZEN_PRODUCT_TV)
795 // Hide the window object for preventing the white screen
796 // during termination of web application.
797 evas_object_hide(window_->evas_object());
800 } else if (current != view_stack_.front()) {
801 view_stack_.front()->SetVisibility(true);
802 window_->SetContent(view_stack_.front()->evas_object());
805 // Delete after the callback context(for ewk view) was not used
806 ecore_idler_add([](void* view) {
807 WebView* obj = static_cast<WebView*>(view);
814 Eina_Bool WebApplication::CheckPluginSession(void* user_data)
816 struct Timer* session_counter = static_cast<Timer*>(user_data);
817 if(XWalkExtensionRendererController::plugin_session_count > 0) {
818 LOGGER(ERROR) << "plugin_session_count : " <<
819 XWalkExtensionRendererController::plugin_session_count;
820 return ECORE_CALLBACK_RENEW;
822 LOGGER(DEBUG) << "plugin_session_count : " <<
823 XWalkExtensionRendererController::plugin_session_count;
824 LOGGER(DEBUG) << "Execute deferred termination of main loop";
825 session_counter->application->Exit();
826 return ECORE_CALLBACK_CANCEL;
829 void WebApplication::OnClosedWebView(WebView* view) {
830 // Reply to javascript dialog for preventing freeze issue.
831 view->ReplyToJavascriptDialog();
832 RemoveWebViewFromStack(view);
834 #if defined(TIZEN_PRODUCT_TV)
835 LOGGER(DEBUG) << "plugin_session_count : " <<
836 XWalkExtensionRendererController::plugin_session_count;
838 // Hide the window object for preventing the white screen
839 // during termination of web application.
840 evas_object_hide(window_->evas_object());
842 session_counter.application = this;
843 session_counter.timer = ecore_timer_add(SESSION_COUNTER_INTERVAL,
844 CheckPluginSession, &session_counter);
845 if (!session_counter.timer)
846 LOGGER(ERROR) << "It's failed to create session_counter timer";
850 void WebApplication::OnReceivedWrtMessage(WebView* view,
851 Ewk_IPC_Wrt_Message_Data* msg) {
852 Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg);
854 #define TYPE_BEGIN(x) (!strncmp(msg_type, x, strlen(x)))
855 #define TYPE_IS(x) (!strcmp(msg_type, x))
857 if (TYPE_BEGIN("xwalk://")) {
858 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
859 extension_server->HandleIPCMessage(msg);
861 Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg);
862 Eina_Stringshare* msg_ref_id =
863 ewk_ipc_wrt_message_data_reference_id_get(msg);
864 Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg);
866 if (TYPE_IS("tizen://hide")) {
869 } else if (TYPE_IS("tizen://exit")) {
871 // Reply to javascript dialog for preventing freeze issue.
872 view->ReplyToJavascriptDialog();
873 ecore_idler_add(ExitAppIdlerCallback, this);
874 } else if (TYPE_IS("tizen://changeUA")) {
876 // Change UserAgent of current WebView
878 if (view_stack_.size() > 0 && view_stack_.front() != NULL) {
879 ret = view_stack_.front()->SetUserAgent(std::string(msg_value));
882 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
883 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
884 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
886 ewk_ipc_wrt_message_data_value_set(ans, "success");
888 ewk_ipc_wrt_message_data_value_set(ans, "failed");
889 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
890 LOGGER(ERROR) << "Failed to send response";
892 ewk_ipc_wrt_message_data_del(ans);
893 } else if (TYPE_IS("tizen://deleteAllCookies")) {
894 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
895 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
896 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
897 if (ClearCookie(ewk_context_))
898 ewk_ipc_wrt_message_data_value_set(ans, "success");
900 ewk_ipc_wrt_message_data_value_set(ans, "failed");
901 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
902 LOGGER(ERROR) << "Failed to send response";
904 ewk_ipc_wrt_message_data_del(ans);
905 } else if (TYPE_IS("tizen://hide_splash_screen")) {
906 splash_screen_->HideSplashScreen(SplashScreen::HideReason::CUSTOM);
909 eina_stringshare_del(msg_ref_id);
910 eina_stringshare_del(msg_id);
911 eina_stringshare_del(msg_value);
917 eina_stringshare_del(msg_type);
920 void WebApplication::OnOrientationLock(
921 WebView* view, bool lock,
922 NativeWindow::ScreenOrientation preferred_rotation) {
923 if (view_stack_.size() == 0) return;
925 // Only top-most view can set the orientation relate operation
926 if (view_stack_.front() != view) return;
928 // This is for 2.4 compatibility. Requested by Webengine Team.
930 // In Tizen 2.4 WebKit locking screen orientation was possible with Web API
931 // screen.lockOrientation(). This API was deprecated and replaced with
932 // screen.orientation.lock(). But for compatibility case we need to support
934 if(!tizenWebKitCompatibilityEnabled()) {
935 auto orientaion_setting = app_data_->setting_info() != NULL
936 ? app_data_->setting_info()->screen_orientation()
937 : wgt::parse::SettingInfo::ScreenOrientation::AUTO;
938 if (wgt::parse::SettingInfo::ScreenOrientation::AUTO != orientaion_setting) {
939 // If Tizen version is 3.0, it return.
945 window_->SetRotationLock(preferred_rotation);
947 window_->SetAutoRotation();
951 void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
952 // NOTE: This code is added to enable back-key on remote URL
953 bool enabled = app_data_->setting_info() != NULL
954 ? app_data_->setting_info()->hwkey_enabled()
957 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
958 if (kKeyNameBack == keyname) {
959 LOGGER(DEBUG) << "Back to previous page for remote URL";
961 view->EvalJavascript(kBackKeyEventScript);
962 if (!view->Backward()) {
963 if (view_stack_.size() == 1) {
964 LOGGER(DEBUG) << "Terminate";
967 RemoveWebViewFromStack(view_stack_.front());
974 if (enabled && kKeyNameBack == keyname) {
975 view->EvalJavascript(kBackKeyEventScript);
976 // NOTE: This code is added for backward compatibility.
977 // If the 'backbutton_presence' is true, WebView should be navigated back.
978 if ((app_data_->setting_info() != NULL &&
979 app_data_->setting_info()->backbutton_presence()) ||
980 (app_data_->widget_info() != NULL &&
981 app_data_->widget_info()->view_modes() == kViewmodeTypeWindowed)) {
982 if (!view->Backward()) {
983 if (view_stack_.size() == 1) {
984 LOGGER(DEBUG) << "Terminate";
987 RemoveWebViewFromStack(view_stack_.front());
991 if (app_data_->widget_info()->view_modes() == kViewmodeTypeFullscreen) {
992 ewk_view_fullscreen_exit(view->evas_object());
996 } else if (enabled && kKeyNameMenu == keyname) {
997 view->EvalJavascript(kMenuKeyEventScript);
1001 void WebApplication::OnLanguageChanged() {
1002 lang_changed_mode_ = true;
1003 locale_manager_->UpdateSystemLocale();
1004 ewk_context_cache_clear(ewk_context_);
1005 auto it = view_stack_.begin();
1006 for (; it != view_stack_.end(); ++it) {
1011 void WebApplication::OnConsoleMessage(const std::string& msg, int level) {
1012 static bool enabled = (getenv(kConsoleLogEnableKey) != NULL);
1015 std::string split_msg = msg;
1016 std::size_t pos = msg.find(kResWgtPath);
1017 if (pos != std::string::npos) {
1018 split_msg = msg.substr(pos + strlen(kResWgtPath));
1021 if (debug_mode_ || verbose_mode_ || enabled) {
1022 int dlog_level = DLOG_DEBUG;
1024 case EWK_CONSOLE_MESSAGE_LEVEL_WARNING:
1025 dlog_level = DLOG_WARN;
1027 case EWK_CONSOLE_MESSAGE_LEVEL_ERROR:
1028 dlog_level = DLOG_ERROR;
1031 dlog_level = DLOG_DEBUG;
1034 LOGGER_RAW(dlog_level, kConsoleMessageLogTag)
1035 << "[" << app_data_->pkg_id() << "] " << split_msg;
1039 void WebApplication::OnLowMemory() {
1040 ewk_context_cache_clear(ewk_context_);
1041 ewk_context_notify_low_memory(ewk_context_);
1044 void WebApplication::OnSoftKeyboardChangeEvent(WebView* /*view*/,
1045 SoftKeyboardChangeEventValue softkeyboard_value) {
1046 LOGGER(DEBUG) << "OnSoftKeyboardChangeEvent";
1047 std::stringstream script;
1050 << "var __event = document.createEvent(\"CustomEvent\");\n"
1051 << "var __detail = {};\n"
1052 << "__event.initCustomEvent(\"softkeyboardchange\",true,true,__detail);\n"
1053 << "__event.state = \"" << softkeyboard_value.state << "\";\n"
1054 << "__event.width = " << softkeyboard_value.width << ";\n"
1055 << "__event.height = " << softkeyboard_value.height << ";\n"
1056 << "document.dispatchEvent(__event);\n"
1058 << "for (var i=0; i < window.frames.length; i++)\n"
1059 << "{ window.frames[i].document.dispatchEvent(__event); }"
1061 std::string kSoftKeyboardScript = script.str();
1062 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1063 view_stack_.front()->EvalJavascript(kSoftKeyboardScript.c_str());
1066 #ifdef ROTARY_EVENT_FEATURE_SUPPORT
1067 void WebApplication::OnRotaryEvent(WebView* /*view*/,
1068 RotaryEventType type) {
1069 LOGGER(DEBUG) << "OnRotaryEvent";
1070 std::stringstream script;
1073 << "var __event = document.createEvent(\"CustomEvent\");\n"
1074 << "var __detail = {};\n"
1075 << "__event.initCustomEvent(\"rotarydetent\", true, true, __detail);\n"
1076 << "__event.detail.direction = \""
1077 << (type == RotaryEventType::CLOCKWISE ? "CW" : "CCW")
1079 << "document.dispatchEvent(__event);\n"
1081 << "for (var i=0; i < window.frames.length; i++)\n"
1082 << "{ window.frames[i].document.dispatchEvent(__event); }"
1084 std::string kRotaryEventScript = script.str();
1085 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1086 view_stack_.front()->EvalJavascript(kRotaryEventScript.c_str());
1088 #endif // ROTARY_EVENT_FEATURE_SUPPORT
1090 void WebApplication::OnTimeTick(long time) {
1092 LOGGER(DEBUG) << "TimeTick";
1093 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1094 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1098 void WebApplication::OnAmbientTick(long time) {
1099 LOGGER(DEBUG) << "AmbientTick";
1100 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1101 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
1104 void WebApplication::OnAmbientChanged(bool ambient_mode) {
1105 LOGGER(DEBUG) << "AmbientChanged";
1106 std::stringstream script;
1109 << "var __event = document.createEvent(\"CustomEvent\");\n"
1110 << "var __detail = {};\n"
1111 << "__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);\n"
1112 << "__event.detail.ambientMode = "
1113 << (ambient_mode ? "true" : "false") << ";\n"
1114 << "document.dispatchEvent(__event);\n"
1116 << "for (var i=0; i < window.frames.length; i++)\n"
1117 << "{ window.frames[i].document.dispatchEvent(__event); }"
1119 std::string kAmbientChangedEventScript = script.str();
1120 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
1121 view_stack_.front()->EvalJavascript(kAmbientChangedEventScript.c_str());
1124 bool WebApplication::OnContextMenuDisabled(WebView* /*view*/) {
1125 return !(app_data_->setting_info() != NULL
1126 ? app_data_->setting_info()->context_menu_enabled()
1130 void WebApplication::OnLoadStart(WebView* /*view*/) {
1131 LOGGER(DEBUG) << "LoadStart";
1134 #ifdef PROFILE_MOBILE
1135 void WebApplication::OnLoadProgress(WebView* view, double persent) {
1136 LOGGER(DEBUG) << "LoadProgress, progress ;"<<persent;
1138 if ((app_data_->setting_info() != NULL &&
1139 app_data_->setting_info()->progressbar_presence()) ||
1140 (app_data_->widget_info() != NULL &&
1141 app_data_->widget_info()->view_modes() == kViewmodeTypeWindowed)) {
1142 if (persent == 1.0) {
1143 window_->SignalEmit(kEdjeHideProgressSignal, "");
1145 window_->SignalEmit(kEdjeShowProgressSignal, "");
1146 window_->UpdateProgress(persent);
1152 void WebApplication::OnLoadFinished(WebView* /*view*/) {
1153 LOGGER(DEBUG) << "LoadFinished";
1154 #ifdef PROFILE_MOBILE
1155 if ((app_data_->setting_info() != NULL &&
1156 app_data_->setting_info()->progressbar_presence()) ||
1157 (app_data_->widget_info() != NULL &&
1158 app_data_->widget_info()->view_modes() == kViewmodeTypeWindowed)) {
1159 window_->SignalEmit(kEdjeHideProgressSignal, "");
1162 splash_screen_->HideSplashScreen(SplashScreen::HideReason::LOADFINISHED);
1165 void WebApplication::OnRendered(WebView* view) {
1166 STEP_PROFILE_END("URL Set -> Rendered");
1167 STEP_PROFILE_END("Start -> Launch Completed");
1168 LOGGER(DEBUG) << "Rendered";
1169 splash_screen_->HideSplashScreen(SplashScreen::HideReason::RENDERED);
1171 // Do not show(), active() for language change
1172 if(lang_changed_mode_ == false){
1173 // Show window after frame rendered.
1174 #ifdef PROFILE_MOBILE
1175 if (common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
1179 #else // PROFILE_MOBILE
1185 lang_changed_mode_ = false;
1189 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
1190 void WebApplication::OnRotatePrepared(WebView* /*view*/) {
1191 window_->ManualRotationDone();
1193 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
1195 void WebApplication::LaunchInspector(common::AppControl* appcontrol) {
1196 unsigned int port = ewk_context_inspector_server_start(ewk_context_, 0);
1197 std::stringstream ss;
1199 std::map<std::string, std::vector<std::string>> data;
1200 data[kPortKey] = {ss.str()};
1201 appcontrol->Reply(data);
1204 void WebApplication::SetupWebView(WebView* view) {
1205 view->SetEventListener(this);
1208 if (security_model_version_ == 2) {
1209 view->SetCSPRule(csp_rule_, false);
1210 if (!csp_report_rule_.empty()) {
1211 view->SetCSPRule(csp_report_rule_, true);
1215 // Setup longpolling value
1216 if (app_data_->setting_info() != NULL &&
1217 app_data_->setting_info()->long_polling()) {
1218 boost::optional <unsigned int> polling_val(app_data_->setting_info()->long_polling());
1219 unsigned long *ptr = reinterpret_cast <unsigned long *> (&polling_val.get());
1220 view->SetLongPolling(*ptr);
1224 void WebApplication::SetupWebViewCompatibilitySettings(WebView* view) {
1225 if (tizenWebKitCompatibilityEnabled()) {
1226 Ewk_Settings* settings = ewk_view_settings_get(view->evas_object());
1227 ewk_settings_tizen_compatibility_mode_set(settings,
1228 m_tizenCompatibilitySettings.m_major,
1229 m_tizenCompatibilitySettings.m_minor,
1230 m_tizenCompatibilitySettings.m_release);
1231 ewk_settings_text_autosizing_enabled_set(settings, EINA_FALSE);
1235 bool WebApplication::OnDidNavigation(WebView* /*view*/,
1236 const std::string& url) {
1238 // except(file , http, https, app) pass to appcontrol and return false
1239 if (ProcessWellKnownScheme(url)) {
1243 // send launch request for blocked URL to guarrenty backward-compatibility.
1244 if (resource_manager_->AllowNavigation(url)) {
1247 LOGGER(DEBUG) << "URL is blocked. send launch request for URL : " << url;
1248 std::unique_ptr<common::AppControl> request(
1249 common::AppControl::MakeAppcontrolFromURL(url));
1250 if (request.get() == NULL || !request->LaunchRequest()) {
1251 LOGGER(ERROR) << "Fail to send appcontrol request";
1257 void WebApplication::OnNotificationPermissionRequest(
1258 WebView*, const std::string& url,
1259 std::function<void(bool)> result_handler) {
1260 auto db = common::AppDB::GetInstance();
1261 std::string reminder =
1262 db->Get(kDBPrivateSection, kNotificationPermissionPrefix + url);
1263 if (reminder == "allowed") {
1264 result_handler(true);
1266 } else if (reminder == "denied") {
1267 result_handler(false);
1271 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1272 // Remote Domain: Popup user prompt.
1273 if (common::utils::StartsWith(url, "file://") &&
1274 FindPrivilegeFromConfig(app_data_, kNotificationPrivilege)) {
1275 result_handler(true);
1279 Popup* popup = Popup::CreatePopup(window_);
1280 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1281 popup->SetTitle(popup_string::kPopupTitleWebNotification);
1282 popup->SetBody(popup_string::kPopupBodyWebNotification);
1283 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1284 popup->SetResultHandler(
1285 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1286 bool result = popup->GetButtonResult();
1287 bool remember = popup->GetCheckBoxResult();
1289 db->Set(kDBPrivateSection, kNotificationPermissionPrefix + url,
1290 result ? "allowed" : "denied");
1292 result_handler(result);
1298 void WebApplication::OnGeolocationPermissionRequest(
1299 WebView*, const std::string& url,
1300 std::function<void(bool)> result_handler) {
1301 auto db = common::AppDB::GetInstance();
1302 std::string reminder =
1303 db->Get(kDBPrivateSection, kGeolocationPermissionPrefix + url);
1304 if (reminder == "allowed") {
1305 result_handler(true);
1307 } else if (reminder == "denied") {
1308 result_handler(false);
1312 // Local Domain: Grant permission if defined, otherwise block execution.
1313 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1314 if (!FindPrivilegeFromConfig(app_data_, kLocationPrivilege) &&
1315 !FindPrivilegeFromCynara(kLocationPrivilege)) {
1316 result_handler(false);
1320 if (common::utils::StartsWith(url, "file://")) {
1321 result_handler(true);
1325 Popup* popup = Popup::CreatePopup(window_);
1326 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1327 popup->SetTitle(popup_string::kPopupTitleGeoLocation);
1328 popup->SetBody(popup_string::kPopupBodyGeoLocation);
1329 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1330 popup->SetResultHandler(
1331 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1332 bool result = popup->GetButtonResult();
1333 bool remember = popup->GetCheckBoxResult();
1335 db->Set(kDBPrivateSection, kGeolocationPermissionPrefix + url,
1336 result ? "allowed" : "denied");
1338 result_handler(result);
1344 void WebApplication::OnQuotaExceed(WebView*, const std::string& url,
1345 std::function<void(bool)> result_handler) {
1346 auto db = common::AppDB::GetInstance();
1347 std::string reminder =
1348 db->Get(kDBPrivateSection, kQuotaPermissionPrefix + url);
1349 if (reminder == "allowed") {
1350 result_handler(true);
1352 } else if (reminder == "denied") {
1353 result_handler(false);
1357 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1358 // Remote Domain: Popup user prompt.
1359 if (common::utils::StartsWith(url, "file://") &&
1360 FindPrivilegeFromConfig(app_data_, kStoragePrivilege)) {
1361 result_handler(true);
1365 Popup* popup = Popup::CreatePopup(window_);
1366 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1367 popup->SetTitle(popup_string::kPopupTitleWebStorage);
1368 popup->SetBody(popup_string::kPopupBodyWebStorage);
1369 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1370 popup->SetResultHandler(
1371 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1372 bool result = popup->GetButtonResult();
1373 bool remember = popup->GetCheckBoxResult();
1375 db->Set(kDBPrivateSection, kQuotaPermissionPrefix + url,
1376 result ? "allowed" : "denied");
1378 result_handler(result);
1384 void WebApplication::OnAuthenticationRequest(
1385 WebView*, const std::string& /*url*/, const std::string& /*message*/,
1386 std::function<void(bool submit, const std::string& id,
1387 const std::string& password)> result_handler) {
1388 Popup* popup = Popup::CreatePopup(window_);
1389 popup->SetButtonType(Popup::ButtonType::LoginCancelButton);
1390 popup->SetFirstEntry(popup_string::kPopupLabelAuthusername,
1391 Popup::EntryType::Edit);
1392 popup->SetSecondEntry(popup_string::kPopupLabelPassword,
1393 Popup::EntryType::PwEdit);
1394 popup->SetTitle(popup_string::kPopupTitleAuthRequest);
1395 popup->SetBody(popup_string::kPopupBodyAuthRequest);
1396 popup->SetResultHandler([result_handler](Popup* popup, void* /*user_data*/) {
1397 bool result = popup->GetButtonResult();
1398 std::string id = popup->GetFirstEntryResult();
1399 std::string passwd = popup->GetSecondEntryResult();
1400 result_handler(result, id, passwd);
1406 void WebApplication::OnCertificateAllowRequest(
1407 WebView*, const std::string& url, const std::string& pem,
1408 std::function<void(bool allow)> result_handler) {
1409 auto db = common::AppDB::GetInstance();
1410 std::string reminder =
1411 db->Get(kDBPrivateSection, kCertificateAllowPrefix + pem);
1412 if (reminder == "allowed") {
1413 result_handler(true);
1415 } else if (reminder == "denied") {
1416 result_handler(false);
1420 Popup* popup = Popup::CreatePopup(window_);
1421 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1422 popup->SetTitle(popup_string::kPopupTitleCert);
1423 popup->SetBody(popup_string::GetText(
1424 popup_string::kPopupBodyCert) + "\n\n" + url);
1425 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1426 popup->SetResultHandler(
1427 [db, result_handler, pem](Popup* popup, void* /*user_data*/) {
1428 bool result = popup->GetButtonResult();
1429 bool remember = popup->GetCheckBoxResult();
1431 db->Set(kDBPrivateSection, kCertificateAllowPrefix + pem,
1432 result ? "allowed" : "denied");
1434 result_handler(result);
1440 void WebApplication::OnUsermediaPermissionRequest(
1441 WebView*, const std::string& url,
1442 std::function<void(bool)> result_handler) {
1443 auto db = common::AppDB::GetInstance();
1444 std::string reminder =
1445 db->Get(kDBPrivateSection, kUsermediaPermissionPrefix + url);
1446 if (reminder == "allowed") {
1447 result_handler(true);
1449 } else if (reminder == "denied") {
1450 result_handler(false);
1454 // Local Domain: Grant permission if defined, otherwise block execution.
1455 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1456 if (!FindPrivilegeFromConfig(app_data_, kUsermediaPrivilege) &&
1457 !(FindPrivilegeFromCynara(kCameraPrivilege) && FindPrivilegeFromCynara(kRecordPrivilege))) {
1458 result_handler(false);
1462 if (common::utils::StartsWith(url, "file://")) {
1463 result_handler(true);
1467 Popup* popup = Popup::CreatePopup(window_);
1468 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1469 popup->SetTitle(popup_string::kPopupTitleUserMedia);
1470 popup->SetBody(popup_string::kPopupBodyUserMedia);
1471 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1472 popup->SetResultHandler(
1473 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1474 bool result = popup->GetButtonResult();
1475 bool remember = popup->GetCheckBoxResult();
1477 db->Set(kDBPrivateSection, kUsermediaPermissionPrefix + url,
1478 result ? "allowed" : "denied");
1480 result_handler(result);
1486 } // namespace runtime