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"
29 #include "common/application_data.h"
30 #include "common/app_db.h"
31 #include "common/app_control.h"
32 #include "common/command_line.h"
33 #include "common/locale_manager.h"
34 #include "common/logger.h"
35 #include "common/profiler.h"
36 #include "common/resource_manager.h"
37 #include "common/string_utils.h"
38 #include "runtime/browser/native_window.h"
39 #include "runtime/browser/notification_manager.h"
40 #include "runtime/browser/popup.h"
41 #include "runtime/browser/popup_string.h"
42 #include "runtime/browser/vibration_manager.h"
43 #include "runtime/browser/web_view.h"
44 #include "runtime/browser/splash_screen.h"
45 #include "extensions/common/xwalk_extension_server.h"
47 #ifndef INJECTED_BUNDLE_PATH
48 #error INJECTED_BUNDLE_PATH is not set.
54 const char* kKeyNameBack = "back";
55 const char* kKeyNameMenu = "menu";
57 const char* kConsoleLogEnableKey = "WRT_CONSOLE_LOG_ENABLE";
58 const char* kConsoleMessageLogTag = "ConsoleMessage";
60 const char* kVerboseKey = "verbose";
61 const char* kPortKey = "port";
63 const char* kAppControlEventScript =
65 "var __event = document.createEvent(\"CustomEvent\");\n"
66 "__event.initCustomEvent(\"appcontrol\", true, true, null);\n"
67 "document.dispatchEvent(__event);\n"
69 "for (var i=0; i < window.frames.length; i++)\n"
70 "{ window.frames[i].document.dispatchEvent(__event); }"
72 const char* kBackKeyEventScript =
74 "var __event = document.createEvent(\"CustomEvent\");\n"
75 "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
76 "__event.keyName = \"back\";\n"
77 "document.dispatchEvent(__event);\n"
79 "for (var i=0; i < window.frames.length; i++)\n"
80 "{ window.frames[i].document.dispatchEvent(__event); }"
82 const char* kMenuKeyEventScript =
84 "var __event = document.createEvent(\"CustomEvent\");\n"
85 "__event.initCustomEvent(\"tizenhwkey\", true, true, null);\n"
86 "__event.keyName = \"menu\";\n"
87 "document.dispatchEvent(__event);\n"
89 "for (var i=0; i < window.frames.length; i++)\n"
90 "{ window.frames[i].document.dispatchEvent(__event); }"
92 const char* kAmbientTickEventScript =
94 "var __event = document.createEvent(\"CustomEvent\");\n"
95 "__event.initCustomEvent(\"timetick\", true, true);\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* kFullscreenPrivilege = "http://tizen.org/privilege/fullscreen";
102 const char* kFullscreenFeature = "fullscreen";
103 const char* kNotificationPrivilege = "http://tizen.org/privilege/notification";
104 const char* kLocationPrivilege = "http://tizen.org/privilege/location";
105 const char* kStoragePrivilege = "http://tizen.org/privilege/unlimitedstorage";
106 const char* kUsermediaPrivilege = "http://tizen.org/privilege/mediacapture";
107 const char* kNotiIconFile = "noti_icon.png";
108 const char* kFileScheme = "file://";
110 const char* kVisibilitySuspendFeature = "visibility,suspend";
111 const char* kMediastreamRecordFeature = "mediastream,record";
112 const char* kEncryptedDatabaseFeature = "encrypted,database";
113 const char* kRotationLockFeature = "rotation,lock";
114 const char* kBackgroundMusicFeature = "background,music";
115 const char* kSoundModeFeature = "sound,mode";
116 const char* kBackgroundVibrationFeature = "background,vibration";
117 const char* kCSPFeature = "csp";
119 const char* kGeolocationPermissionPrefix = "__WRT_GEOPERM_";
120 const char* kNotificationPermissionPrefix = "__WRT_NOTIPERM_";
121 const char* kQuotaPermissionPrefix = "__WRT_QUOTAPERM_";
122 const char* kCertificateAllowPrefix = "__WRT_CERTIPERM_";
123 const char* kUsermediaPermissionPrefix = "__WRT_USERMEDIAPERM_";
124 const char* kDBPrivateSection = "private";
126 const char* kDefaultCSPRule =
127 "default-src *; script-src 'self'; style-src 'self'; object-src 'none';";
128 const char* kResWgtPath = "res/wgt/";
129 const char* kAppControlMain = "http://tizen.org/appcontrol/operation/main";
131 bool FindPrivilege(common::ApplicationData* app_data,
132 const std::string& privilege) {
133 if (app_data->permissions_info().get() == NULL) return false;
134 auto it = app_data->permissions_info()->GetAPIPermissions().begin();
135 auto end = app_data->permissions_info()->GetAPIPermissions().end();
136 for (; it != end; ++it) {
137 if (*it == privilege) return true;
142 static void SendDownloadRequest(const std::string& url) {
143 common::AppControl request;
144 request.set_operation(APP_CONTROL_OPERATION_DOWNLOAD);
145 request.set_uri(url);
146 request.LaunchRequest();
149 static void InitializeNotificationCallback(Ewk_Context* ewk_context,
150 WebApplication* app) {
151 auto show = [](Ewk_Context*, Ewk_Notification* noti, void* user_data) {
152 WebApplication* self = static_cast<WebApplication*>(user_data);
153 if (self == NULL) return;
154 uint64_t id = ewk_notification_id_get(noti);
155 std::string title(ewk_notification_title_get(noti)
156 ? ewk_notification_title_get(noti)
159 ewk_notification_body_get(noti) ? ewk_notification_body_get(noti) : "");
160 std::string icon_path = self->data_path() + "/" + kNotiIconFile;
161 if (!ewk_notification_icon_save_as_png(noti, icon_path.c_str())) {
164 if (NotificationManager::GetInstance()->Show(id, title, body, icon_path))
165 ewk_notification_showed(id);
167 auto hide = [](Ewk_Context*, uint64_t noti_id, void*) {
168 NotificationManager::GetInstance()->Hide(noti_id);
169 ewk_notification_closed(noti_id, EINA_FALSE);
171 ewk_context_notification_callbacks_set(ewk_context, show, hide, app);
174 static Eina_Bool ExitAppIdlerCallback(void* data) {
175 WebApplication* app = static_cast<WebApplication*>(data);
178 return ECORE_CALLBACK_CANCEL;
181 static bool ClearCookie(Ewk_Context* ewk_context) {
182 Ewk_Cookie_Manager* cookie_manager =
183 ewk_context_cookie_manager_get(ewk_context);
184 if (!cookie_manager) {
185 LOGGER(ERROR) << "Fail to get cookie manager";
188 ewk_cookie_manager_cookies_clear(cookie_manager);
192 static bool ProcessWellKnownScheme(const std::string& url) {
193 if (common::utils::StartsWith(url, "file:") ||
194 common::utils::StartsWith(url, "app:") ||
195 common::utils::StartsWith(url, "data:") ||
196 common::utils::StartsWith(url, "http:") ||
197 common::utils::StartsWith(url, "https:") ||
198 common::utils::StartsWith(url, "widget:") ||
199 common::utils::StartsWith(url, "about:") ||
200 common::utils::StartsWith(url, "blob:")) {
204 std::unique_ptr<common::AppControl> request(
205 common::AppControl::MakeAppcontrolFromURL(url));
206 if (request.get() == NULL || !request->LaunchRequest()) {
207 LOGGER(ERROR) << "Fail to send appcontrol request";
208 SLoggerE("Fail to send appcontrol request [%s]", url.c_str());
211 // Should return true, to stop the WebEngine progress step about this URL
217 WebApplication::WebApplication(
218 NativeWindow* window,
219 common::ApplicationData* app_data)
222 verbose_mode_(false),
223 lang_changed_mode_(false),
225 ewk_context_new_with_injected_bundle_path(INJECTED_BUNDLE_PATH)),
226 has_ownership_of_ewk_context_(true),
228 appid_(app_data->app_id()),
230 locale_manager_(new common::LocaleManager()),
235 WebApplication::WebApplication(
236 NativeWindow* window,
237 common::ApplicationData* app_data,
238 Ewk_Context* context)
241 verbose_mode_(false),
242 ewk_context_(context),
243 has_ownership_of_ewk_context_(false),
245 appid_(app_data->app_id()),
247 locale_manager_(new common::LocaleManager()),
252 WebApplication::~WebApplication() {
253 window_->SetContent(NULL);
254 auto it = view_stack_.begin();
255 for (; it != view_stack_.end(); ++it) {
259 if (ewk_context_ && has_ownership_of_ewk_context_)
260 ewk_context_delete(ewk_context_);
263 bool WebApplication::Initialize() {
265 std::unique_ptr<char, decltype(std::free)*> path{app_get_data_path(),
267 app_data_path_ = path.get();
269 if (app_data_->setting_info() != NULL &&
270 app_data_->setting_info()->screen_orientation() ==
271 wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
272 ewk_context_tizen_extensible_api_string_set(ewk_context_,
273 kRotationLockFeature, true);
274 window_->SetAutoRotation();
275 } else if (app_data_->setting_info() != NULL &&
276 app_data_->setting_info()->screen_orientation() ==
277 wgt::parse::SettingInfo::ScreenOrientation::PORTRAIT) {
278 window_->SetRotationLock(NativeWindow::ScreenOrientation::PORTRAIT_PRIMARY);
279 } else if (app_data_->setting_info() != NULL &&
280 app_data_->setting_info()->screen_orientation() ==
281 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
282 window_->SetRotationLock(
283 NativeWindow::ScreenOrientation::LANDSCAPE_PRIMARY);
286 splash_screen_.reset(new SplashScreen(
287 window_, app_data_->splash_screen_info(), app_data_->application_path()));
288 resource_manager_.reset(
289 new common::ResourceManager(app_data_, locale_manager_.get()));
290 resource_manager_->set_base_resource_path(app_data_->application_path());
292 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
293 extension_server->SetupIPC(ewk_context_);
296 ewk_context_cache_model_set(ewk_context_, EWK_CACHE_MODEL_DOCUMENT_BROWSER);
299 auto cookie_manager = ewk_context_cookie_manager_get(ewk_context_);
300 ewk_cookie_manager_accept_policy_set(cookie_manager,
301 EWK_COOKIE_ACCEPT_POLICY_ALWAYS);
303 // set persistent storage path
304 std::string cookie_path = data_path() + ".cookie";
305 ewk_cookie_manager_persistent_storage_set(
306 cookie_manager, cookie_path.c_str(),
307 EWK_COOKIE_PERSISTENT_STORAGE_SQLITE);
309 // vibration callback
310 auto vibration_start_callback = [](uint64_t ms, void*) {
311 platform::VibrationManager::GetInstance()->Start(static_cast<int>(ms));
313 auto vibration_stop_callback = [](void* /*user_data*/) {
314 platform::VibrationManager::GetInstance()->Stop();
316 ewk_context_vibration_client_callbacks_set(
317 ewk_context_, vibration_start_callback, vibration_stop_callback, NULL);
319 auto download_callback = [](const char* downloadUrl, void* /*data*/) {
320 SendDownloadRequest(downloadUrl);
322 ewk_context_did_start_download_callback_set(ewk_context_, download_callback,
324 InitializeNotificationCallback(ewk_context_, this);
326 if (FindPrivilege(app_data_, kFullscreenPrivilege)) {
327 ewk_context_tizen_extensible_api_string_set(ewk_context_,
328 kFullscreenFeature, true);
331 if (app_data_->setting_info() != NULL &&
332 app_data_->setting_info()->background_support_enabled()) {
333 ewk_context_tizen_extensible_api_string_set(
334 ewk_context_, kVisibilitySuspendFeature, true);
335 ewk_context_tizen_extensible_api_string_set(ewk_context_,
336 kBackgroundMusicFeature, true);
338 ewk_context_tizen_extensible_api_string_set(
339 ewk_context_, kVisibilitySuspendFeature, false);
340 ewk_context_tizen_extensible_api_string_set(ewk_context_,
341 kBackgroundMusicFeature, false);
343 ewk_context_tizen_extensible_api_string_set(ewk_context_,
344 kMediastreamRecordFeature, true);
345 ewk_context_tizen_extensible_api_string_set(ewk_context_,
346 kEncryptedDatabaseFeature, true);
348 if (app_data_->setting_info() != NULL &&
349 app_data_->setting_info()->sound_mode() ==
350 wgt::parse::SettingInfo::SoundMode::EXCLUSIVE) {
351 ewk_context_tizen_extensible_api_string_set(ewk_context_, kSoundModeFeature,
355 if (app_data_->setting_info() != NULL &&
356 app_data_->setting_info()->background_vibration()) {
357 ewk_context_tizen_extensible_api_string_set(
358 ewk_context_, kBackgroundVibrationFeature, true);
361 if (app_data_->widget_info() != NULL &&
362 !app_data_->widget_info()->default_locale().empty()) {
363 locale_manager_->SetDefaultLocale(
364 app_data_->widget_info()->default_locale());
367 if (app_data_->widget_info() != NULL &&
368 app_data_->widget_info()->view_modes() == "fullscreen") {
369 window_->FullScreen(true);
372 if (app_data_->csp_info() != NULL || app_data_->csp_report_info() != NULL ||
373 app_data_->allowed_navigation_info() != NULL) {
374 security_model_version_ = 2;
375 if (app_data_->csp_info() == NULL ||
376 app_data_->csp_info()->security_rules().empty()) {
377 csp_rule_ = kDefaultCSPRule;
379 csp_rule_ = app_data_->csp_info()->security_rules();
381 if (app_data_->csp_report_info() != NULL &&
382 !app_data_->csp_report_info()->security_rules().empty()) {
383 csp_report_rule_ = app_data_->csp_report_info()->security_rules();
385 ewk_context_tizen_extensible_api_string_set(ewk_context_, kCSPFeature,
388 security_model_version_ = 1;
391 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
392 // Set manual rotation
393 window_->EnableManualRotation(true);
394 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
399 void WebApplication::Launch(std::unique_ptr<common::AppControl> appcontrol) {
400 // send widget info to injected bundle
401 ewk_context_tizen_app_id_set(ewk_context_, appid_.c_str());
404 WebView* view = new WebView(window_, ewk_context_);
407 std::unique_ptr<common::ResourceManager::Resource> res =
408 resource_manager_->GetStartResource(appcontrol.get());
409 view->SetDefaultEncoding(res->encoding());
411 STEP_PROFILE_END("OnCreate -> URL Set");
412 STEP_PROFILE_START("URL Set -> Rendered");
414 window_->SetContent(view->evas_object());
416 #ifdef PROFILE_MOBILE
417 // rotate and resize window forcibily for landscape mode.
418 // window rotate event is generated after window show. so
419 // when app get width and height from viewport, wrong value can be returned.
420 if (app_data_->setting_info()->screen_orientation() ==
421 wgt::parse::SettingInfo::ScreenOrientation::LANDSCAPE) {
422 LOGGER(DEBUG) << "rotate and resize window for landscape mode";
423 elm_win_rotation_with_resize_set(window_->evas_object(), 270);
424 evas_norender(evas_object_evas_get(window_->evas_object()));
426 #endif // PROFILE_MOBILE
428 view->LoadUrl(res->uri(), res->mime());
429 view_stack_.push_front(view);
431 #ifdef PROFILE_WEARABLE
432 // ewk_view_bg_color_set is not working at webview initialization.
433 if (app_data_->app_type() == common::ApplicationData::WATCH) {
434 view->SetBGColor(0, 0, 0, 255);
436 #endif // PROFILE_WEARABLE
438 if (appcontrol->data(AUL_K_DEBUG) == "1") {
440 LaunchInspector(appcontrol.get());
442 if (appcontrol->data(kVerboseKey) == "true") {
443 verbose_mode_ = true;
448 #ifdef PROFILE_MOBILE
449 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
450 LOGGER(DEBUG) << "Show window after launch for remote URL";
454 #endif // PROFILE_MOBILE
457 void WebApplication::AppControl(
458 std::unique_ptr<common::AppControl> appcontrol) {
459 std::unique_ptr<common::ResourceManager::Resource> res =
460 resource_manager_->GetStartResource(appcontrol.get());
462 bool do_reset = res->should_reset();
465 std::string current_page = view_stack_.front()->GetUrl();
466 std::string localized_page = res->uri();
468 if (current_page != localized_page) {
471 SendAppControlEvent();
475 // handle http://tizen.org/appcontrol/operation/main operation specially.
476 // only menu-screen app can send launch request with main operation.
477 // in this case, web app should have to resume web app not reset.
478 if (do_reset && (appcontrol->operation() == kAppControlMain)){
479 LOGGER(DEBUG) << "resume app for main operation";
481 SendAppControlEvent();
487 WebView* view = view_stack_.front();
489 view->SetDefaultEncoding(res->encoding());
490 view->LoadUrl(res->uri(), res->mime());
491 window_->SetContent(view->evas_object());
494 if (!debug_mode_ && appcontrol->data(AUL_K_DEBUG) == "1") {
496 LaunchInspector(appcontrol.get());
498 if (!verbose_mode_ && appcontrol->data(kVerboseKey) == "true") {
499 verbose_mode_ = true;
504 void WebApplication::SendAppControlEvent() {
505 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
506 view_stack_.front()->EvalJavascript(kAppControlEventScript);
509 void WebApplication::ClearViewStack() {
510 window_->SetContent(NULL);
511 WebView* front = view_stack_.front();
512 auto it = view_stack_.begin();
513 for (; it != view_stack_.end(); ++it) {
520 view_stack_.push_front(front);
523 void WebApplication::Resume() {
524 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
525 view_stack_.front()->SetVisibility(true);
527 if (app_data_->setting_info() != NULL &&
528 app_data_->setting_info()->background_support_enabled()) {
532 auto it = view_stack_.begin();
533 for (; it != view_stack_.end(); ++it) {
538 void WebApplication::Suspend() {
539 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
540 view_stack_.front()->SetVisibility(false);
542 if (app_data_->setting_info() != NULL &&
543 app_data_->setting_info()->background_support_enabled()) {
544 LOGGER(DEBUG) << "gone background (backgroud support enabed)";
548 auto it = view_stack_.begin();
549 for (; it != view_stack_.end(); ++it) {
554 void WebApplication::Terminate() {
560 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
561 extension_server->Shutdown();
564 void WebApplication::OnCreatedNewWebView(WebView* /*view*/, WebView* new_view) {
565 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
566 view_stack_.front()->SetVisibility(false);
568 SetupWebView(new_view);
569 view_stack_.push_front(new_view);
570 window_->SetContent(new_view->evas_object());
573 void WebApplication::RemoveWebViewFromStack(WebView* view) {
574 if (view_stack_.size() == 0) return;
576 WebView* current = view_stack_.front();
577 if (current == view) {
578 // In order to prevent the crash issue due to the callback
579 // which occur after destroying WebApplication class,
580 // we have to set the 'SetEventListener' to NULL.
581 view->SetEventListener(NULL);
582 view_stack_.pop_front();
584 auto found = std::find(view_stack_.begin(), view_stack_.end(), view);
585 if (found != view_stack_.end()) {
586 // In order to prevent the crash issue due to the callback
587 // which occur after destroying WebApplication class,
588 // we have to set the 'SetEventListener' to NULL.
589 view->SetEventListener(NULL);
590 view_stack_.erase(found);
594 if (view_stack_.size() == 0) {
596 } else if (current != view_stack_.front()) {
597 view_stack_.front()->SetVisibility(true);
598 window_->SetContent(view_stack_.front()->evas_object());
601 // Delete after the callback context(for ewk view) was not used
602 ecore_idler_add([](void* view) {
603 WebView* obj = static_cast<WebView*>(view);
610 void WebApplication::OnClosedWebView(WebView* view) {
611 RemoveWebViewFromStack(view);
614 void WebApplication::OnReceivedWrtMessage(WebView* /*view*/,
615 Ewk_IPC_Wrt_Message_Data* msg) {
616 Eina_Stringshare* msg_type = ewk_ipc_wrt_message_data_type_get(msg);
618 #define TYPE_BEGIN(x) (!strncmp(msg_type, x, strlen(x)))
619 #define TYPE_IS(x) (!strcmp(msg_type, x))
621 if (TYPE_BEGIN("xwalk://")) {
622 auto extension_server = extensions::XWalkExtensionServer::GetInstance();
623 extension_server->HandleIPCMessage(msg);
625 Eina_Stringshare* msg_id = ewk_ipc_wrt_message_data_id_get(msg);
626 Eina_Stringshare* msg_ref_id =
627 ewk_ipc_wrt_message_data_reference_id_get(msg);
628 Eina_Stringshare* msg_value = ewk_ipc_wrt_message_data_value_get(msg);
630 if (TYPE_IS("tizen://hide")) {
633 } else if (TYPE_IS("tizen://exit")) {
635 ecore_idler_add(ExitAppIdlerCallback, this);
636 } else if (TYPE_IS("tizen://changeUA")) {
638 // Change UserAgent of current WebView
640 if (view_stack_.size() > 0 && view_stack_.front() != NULL) {
641 ret = view_stack_.front()->SetUserAgent(std::string(msg_value));
644 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
645 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
646 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
648 ewk_ipc_wrt_message_data_value_set(ans, "success");
650 ewk_ipc_wrt_message_data_value_set(ans, "failed");
651 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
652 LOGGER(ERROR) << "Failed to send response";
654 ewk_ipc_wrt_message_data_del(ans);
655 } else if (TYPE_IS("tizen://deleteAllCookies")) {
656 Ewk_IPC_Wrt_Message_Data* ans = ewk_ipc_wrt_message_data_new();
657 ewk_ipc_wrt_message_data_type_set(ans, msg_type);
658 ewk_ipc_wrt_message_data_reference_id_set(ans, msg_id);
659 if (ClearCookie(ewk_context_))
660 ewk_ipc_wrt_message_data_value_set(ans, "success");
662 ewk_ipc_wrt_message_data_value_set(ans, "failed");
663 if (!ewk_ipc_wrt_message_send(ewk_context_, ans)) {
664 LOGGER(ERROR) << "Failed to send response";
666 ewk_ipc_wrt_message_data_del(ans);
667 } else if (TYPE_IS("tizen://hide_splash_screen")) {
668 splash_screen_->HideSplashScreen(SplashScreen::HideReason::CUSTOM);
671 eina_stringshare_del(msg_ref_id);
672 eina_stringshare_del(msg_id);
673 eina_stringshare_del(msg_value);
679 eina_stringshare_del(msg_type);
682 void WebApplication::OnOrientationLock(
683 WebView* view, bool lock,
684 NativeWindow::ScreenOrientation preferred_rotation) {
685 if (view_stack_.size() == 0) return;
687 // Only top-most view can set the orientation relate operation
688 if (view_stack_.front() != view) return;
690 auto orientaion_setting =
691 app_data_->setting_info() != NULL
692 ? app_data_->setting_info()->screen_orientation()
694 wgt::parse::SettingInfo::ScreenOrientation::AUTO;
695 if (orientaion_setting != wgt::parse::SettingInfo::ScreenOrientation::AUTO) {
700 window_->SetRotationLock(preferred_rotation);
702 window_->SetAutoRotation();
706 void WebApplication::OnHardwareKey(WebView* view, const std::string& keyname) {
707 // NOTE: This code is added to enable back-key on remote URL
708 bool enabled = app_data_->setting_info() != NULL
709 ? app_data_->setting_info()->hwkey_enabled()
712 if (!common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
713 if (kKeyNameBack == keyname) {
714 LOGGER(DEBUG) << "Back to previous page for remote URL";
715 if (!view->Backward()) {
717 view->EvalJavascript(kBackKeyEventScript);
718 RemoveWebViewFromStack(view_stack_.front());
724 if (enabled && kKeyNameBack == keyname) {
725 view->EvalJavascript(kBackKeyEventScript);
726 // NOTE: This code is added for backward compatibility.
727 // If the 'backbutton_presence' is true, WebView should be navigated back.
728 if ((app_data_->setting_info() != NULL &&
729 app_data_->setting_info()->backbutton_presence()) ||
730 (app_data_->widget_info() != NULL &&
731 app_data_->widget_info()->view_modes() == "windowed")) {
732 if (!view->Backward()) {
733 RemoveWebViewFromStack(view_stack_.front());
736 } else if (enabled && kKeyNameMenu == keyname) {
737 view->EvalJavascript(kMenuKeyEventScript);
741 void WebApplication::OnLanguageChanged() {
742 lang_changed_mode_ = true;
743 locale_manager_->UpdateSystemLocale();
744 ewk_context_cache_clear(ewk_context_);
745 auto it = view_stack_.begin();
746 for (; it != view_stack_.end(); ++it) {
751 void WebApplication::OnConsoleMessage(const std::string& msg, int level) {
752 static bool enabled = (getenv(kConsoleLogEnableKey) != NULL);
755 std::string split_msg = msg;
756 std::size_t pos = msg.find(kResWgtPath);
757 if (pos != std::string::npos) {
758 split_msg = msg.substr(pos + strlen(kResWgtPath));
761 if (debug_mode_ || verbose_mode_ || enabled) {
762 int dlog_level = DLOG_DEBUG;
764 case EWK_CONSOLE_MESSAGE_LEVEL_WARNING:
765 dlog_level = DLOG_WARN;
767 case EWK_CONSOLE_MESSAGE_LEVEL_ERROR:
768 dlog_level = DLOG_ERROR;
771 dlog_level = DLOG_DEBUG;
774 LOGGER_RAW(dlog_level, kConsoleMessageLogTag)
775 << "[" << app_data_->pkg_id() << "] " << split_msg;
779 void WebApplication::OnLowMemory() {
780 ewk_context_cache_clear(ewk_context_);
781 ewk_context_notify_low_memory(ewk_context_);
784 void WebApplication::OnSoftKeyboardChangeEvent(WebView* /*view*/,
785 SoftKeyboardChangeEventValue softkeyboard_value) {
786 LOGGER(DEBUG) << "OnSoftKeyboardChangeEvent";
787 std::stringstream script;
790 << "var __event = document.createEvent(\"CustomEvent\");\n"
791 << "var __detail = {};\n"
792 << "__event.initCustomEvent(\"softkeyboardchange\",true,true,__detail);\n"
793 << "__event.state = \"" << softkeyboard_value.state << "\";\n"
794 << "__event.width = " << softkeyboard_value.width << ";\n"
795 << "__event.height = " << softkeyboard_value.height << ";\n"
796 << "document.dispatchEvent(__event);\n"
798 << "for (var i=0; i < window.frames.length; i++)\n"
799 << "{ window.frames[i].document.dispatchEvent(__event); }"
801 std::string kSoftKeyboardScript = script.str();
802 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
803 view_stack_.front()->EvalJavascript(kSoftKeyboardScript.c_str());
806 #ifdef ROTARY_EVENT_FEATURE_SUPPORT
807 void WebApplication::OnRotaryEvent(WebView* /*view*/,
808 RotaryEventType type) {
809 LOGGER(DEBUG) << "OnRotaryEvent";
810 std::stringstream script;
813 << "var __event = document.createEvent(\"CustomEvent\");\n"
814 << "var __detail = {};\n"
815 << "__event.initCustomEvent(\"rotarydetent\", true, true, __detail);\n"
816 << "__event.detail.direction = \""
817 << (type == RotaryEventType::CLOCKWISE ? "CW" : "CCW")
819 << "document.dispatchEvent(__event);\n"
821 << "for (var i=0; i < window.frames.length; i++)\n"
822 << "{ window.frames[i].document.dispatchEvent(__event); }"
824 std::string kRotaryEventScript = script.str();
825 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
826 view_stack_.front()->EvalJavascript(kRotaryEventScript.c_str());
828 #endif // ROTARY_EVENT_FEATURE_SUPPORT
830 void WebApplication::OnTimeTick(long time) {
832 LOGGER(DEBUG) << "TimeTick";
833 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
834 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
838 void WebApplication::OnAmbientTick(long time) {
839 LOGGER(DEBUG) << "AmbientTick";
840 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
841 view_stack_.front()->EvalJavascript(kAmbientTickEventScript);
844 void WebApplication::OnAmbientChanged(bool ambient_mode) {
845 LOGGER(DEBUG) << "AmbientChanged";
846 std::stringstream script;
849 << "var __event = document.createEvent(\"CustomEvent\");\n"
850 << "var __detail = {};\n"
851 << "__event.initCustomEvent(\"ambientmodechanged\",true,true,__detail);\n"
852 << "__event.detail.ambientMode = "
853 << (ambient_mode ? "true" : "false") << ";\n"
854 << "document.dispatchEvent(__event);\n"
856 << "for (var i=0; i < window.frames.length; i++)\n"
857 << "{ window.frames[i].document.dispatchEvent(__event); }"
859 std::string kAmbientChangedEventScript = script.str();
860 if (view_stack_.size() > 0 && view_stack_.front() != NULL)
861 view_stack_.front()->EvalJavascript(kAmbientChangedEventScript.c_str());
864 bool WebApplication::OnContextMenuDisabled(WebView* /*view*/) {
865 return !(app_data_->setting_info() != NULL
866 ? app_data_->setting_info()->context_menu_enabled()
870 void WebApplication::OnLoadStart(WebView* /*view*/) {
871 LOGGER(DEBUG) << "LoadStart";
874 void WebApplication::OnLoadFinished(WebView* /*view*/) {
875 LOGGER(DEBUG) << "LoadFinished";
876 splash_screen_->HideSplashScreen(SplashScreen::HideReason::LOADFINISHED);
879 void WebApplication::OnRendered(WebView* view) {
880 STEP_PROFILE_END("URL Set -> Rendered");
881 STEP_PROFILE_END("Start -> Launch Completed");
882 LOGGER(DEBUG) << "Rendered";
883 splash_screen_->HideSplashScreen(SplashScreen::HideReason::RENDERED);
885 // Do not show(), active() for language change
886 if(lang_changed_mode_ == false){
887 // Show window after frame rendered.
888 #ifdef PROFILE_MOBILE
889 if (common::utils::StartsWith(view->GetUrl(), kFileScheme)) {
893 #else // PROFILE_MOBILE
899 lang_changed_mode_ = false;
903 #ifdef MANUAL_ROTATE_FEATURE_SUPPORT
904 void WebApplication::OnRotatePrepared(WebView* /*view*/) {
905 window_->ManualRotationDone();
907 #endif // MANUAL_ROTATE_FEATURE_SUPPORT
909 void WebApplication::LaunchInspector(common::AppControl* appcontrol) {
910 unsigned int port = ewk_context_inspector_server_start(ewk_context_, 0);
911 std::stringstream ss;
913 std::map<std::string, std::vector<std::string>> data;
914 data[kPortKey] = {ss.str()};
915 appcontrol->Reply(data);
918 void WebApplication::SetupWebView(WebView* view) {
919 view->SetEventListener(this);
922 if (security_model_version_ == 2) {
923 view->SetCSPRule(csp_rule_, false);
924 if (!csp_report_rule_.empty()) {
925 view->SetCSPRule(csp_report_rule_, true);
929 // Setup longpolling value
930 if (app_data_->setting_info() != NULL &&
931 app_data_->setting_info()->long_polling()) {
932 boost::optional <unsigned int> polling_val(app_data_->setting_info()->long_polling());
933 unsigned long *ptr = reinterpret_cast <unsigned long *> (&polling_val.get());
934 view->SetLongPolling(*ptr);
938 bool WebApplication::OnDidNavigation(WebView* /*view*/,
939 const std::string& url) {
941 // except(file , http, https, app) pass to appcontrol and return false
942 if (ProcessWellKnownScheme(url)) {
946 // send launch request for blocked URL to guarrenty backward-compatibility.
947 if (resource_manager_->AllowNavigation(url)) {
950 LOGGER(DEBUG) << "URL is blocked. send launch request for URL : " << url;
951 std::unique_ptr<common::AppControl> request(
952 common::AppControl::MakeAppcontrolFromURL(url));
953 if (request.get() == NULL || !request->LaunchRequest()) {
954 LOGGER(ERROR) << "Fail to send appcontrol request";
960 void WebApplication::OnNotificationPermissionRequest(
961 WebView*, const std::string& url,
962 std::function<void(bool)> result_handler) {
963 auto db = common::AppDB::GetInstance();
964 std::string reminder =
965 db->Get(kDBPrivateSection, kNotificationPermissionPrefix + url);
966 if (reminder == "allowed") {
967 result_handler(true);
969 } else if (reminder == "denied") {
970 result_handler(false);
974 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
975 // Remote Domain: Popup user prompt.
976 if (common::utils::StartsWith(url, "file://") &&
977 FindPrivilege(app_data_, kNotificationPrivilege)) {
978 result_handler(true);
982 Popup* popup = Popup::CreatePopup(window_);
983 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
984 popup->SetTitle(popup_string::kPopupTitleWebNotification);
985 popup->SetBody(popup_string::kPopupBodyWebNotification);
986 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
987 popup->SetResultHandler(
988 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
989 bool result = popup->GetButtonResult();
990 bool remember = popup->GetCheckBoxResult();
992 db->Set(kDBPrivateSection, kNotificationPermissionPrefix + url,
993 result ? "allowed" : "denied");
995 result_handler(result);
1001 void WebApplication::OnGeolocationPermissionRequest(
1002 WebView*, const std::string& url,
1003 std::function<void(bool)> result_handler) {
1004 auto db = common::AppDB::GetInstance();
1005 std::string reminder =
1006 db->Get(kDBPrivateSection, kGeolocationPermissionPrefix + url);
1007 if (reminder == "allowed") {
1008 result_handler(true);
1010 } else if (reminder == "denied") {
1011 result_handler(false);
1015 // Local Domain: Grant permission if defined, otherwise block execution.
1016 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1017 if (!FindPrivilege(app_data_, kLocationPrivilege)) {
1018 result_handler(false);
1022 if (common::utils::StartsWith(url, "file://")) {
1023 result_handler(true);
1027 Popup* popup = Popup::CreatePopup(window_);
1028 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1029 popup->SetTitle(popup_string::kPopupTitleGeoLocation);
1030 popup->SetBody(popup_string::kPopupBodyGeoLocation);
1031 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1032 popup->SetResultHandler(
1033 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1034 bool result = popup->GetButtonResult();
1035 bool remember = popup->GetCheckBoxResult();
1037 db->Set(kDBPrivateSection, kGeolocationPermissionPrefix + url,
1038 result ? "allowed" : "denied");
1040 result_handler(result);
1046 void WebApplication::OnQuotaExceed(WebView*, const std::string& url,
1047 std::function<void(bool)> result_handler) {
1048 auto db = common::AppDB::GetInstance();
1049 std::string reminder =
1050 db->Get(kDBPrivateSection, kQuotaPermissionPrefix + url);
1051 if (reminder == "allowed") {
1052 result_handler(true);
1054 } else if (reminder == "denied") {
1055 result_handler(false);
1059 // Local Domain: Grant permission if defined, otherwise Popup user prompt.
1060 // Remote Domain: Popup user prompt.
1061 if (common::utils::StartsWith(url, "file://") &&
1062 FindPrivilege(app_data_, kStoragePrivilege)) {
1063 result_handler(true);
1067 Popup* popup = Popup::CreatePopup(window_);
1068 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1069 popup->SetTitle(popup_string::kPopupTitleWebStorage);
1070 popup->SetBody(popup_string::kPopupBodyWebStorage);
1071 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1072 popup->SetResultHandler(
1073 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1074 bool result = popup->GetButtonResult();
1075 bool remember = popup->GetCheckBoxResult();
1077 db->Set(kDBPrivateSection, kQuotaPermissionPrefix + url,
1078 result ? "allowed" : "denied");
1080 result_handler(result);
1086 void WebApplication::OnAuthenticationRequest(
1087 WebView*, const std::string& /*url*/, const std::string& /*message*/,
1088 std::function<void(bool submit, const std::string& id,
1089 const std::string& password)> result_handler) {
1090 Popup* popup = Popup::CreatePopup(window_);
1091 popup->SetButtonType(Popup::ButtonType::LoginCancelButton);
1092 popup->SetFirstEntry(popup_string::kPopupLabelAuthusername,
1093 Popup::EntryType::Edit);
1094 popup->SetSecondEntry(popup_string::kPopupLabelPassword,
1095 Popup::EntryType::PwEdit);
1096 popup->SetTitle(popup_string::kPopupTitleAuthRequest);
1097 popup->SetBody(popup_string::kPopupBodyAuthRequest);
1098 popup->SetResultHandler([result_handler](Popup* popup, void* /*user_data*/) {
1099 bool result = popup->GetButtonResult();
1100 std::string id = popup->GetFirstEntryResult();
1101 std::string passwd = popup->GetSecondEntryResult();
1102 result_handler(result, id, passwd);
1108 void WebApplication::OnCertificateAllowRequest(
1109 WebView*, const std::string& url, const std::string& pem,
1110 std::function<void(bool allow)> result_handler) {
1111 auto db = common::AppDB::GetInstance();
1112 std::string reminder =
1113 db->Get(kDBPrivateSection, kCertificateAllowPrefix + pem);
1114 if (reminder == "allowed") {
1115 result_handler(true);
1117 } else if (reminder == "denied") {
1118 result_handler(false);
1122 Popup* popup = Popup::CreatePopup(window_);
1123 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1124 popup->SetTitle(popup_string::kPopupTitleCert);
1125 popup->SetBody(popup_string::GetText(
1126 popup_string::kPopupBodyCert) + "\n\n" + url);
1127 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1128 popup->SetResultHandler(
1129 [db, result_handler, pem](Popup* popup, void* /*user_data*/) {
1130 bool result = popup->GetButtonResult();
1131 bool remember = popup->GetCheckBoxResult();
1133 db->Set(kDBPrivateSection, kCertificateAllowPrefix + pem,
1134 result ? "allowed" : "denied");
1136 result_handler(result);
1142 void WebApplication::OnUsermediaPermissionRequest(
1143 WebView*, const std::string& url,
1144 std::function<void(bool)> result_handler) {
1145 auto db = common::AppDB::GetInstance();
1146 std::string reminder =
1147 db->Get(kDBPrivateSection, kUsermediaPermissionPrefix + url);
1148 if (reminder == "allowed") {
1149 result_handler(true);
1151 } else if (reminder == "denied") {
1152 result_handler(false);
1156 // Local Domain: Grant permission if defined, otherwise block execution.
1157 // Remote Domain: Popup user prompt if defined, otherwise block execution.
1158 if (!FindPrivilege(app_data_, kUsermediaPrivilege)) {
1159 result_handler(false);
1163 if (common::utils::StartsWith(url, "file://")) {
1164 result_handler(true);
1168 Popup* popup = Popup::CreatePopup(window_);
1169 popup->SetButtonType(Popup::ButtonType::AllowDenyButton);
1170 popup->SetTitle(popup_string::kPopupTitleUserMedia);
1171 popup->SetBody(popup_string::kPopupBodyUserMedia);
1172 popup->SetCheckBox(popup_string::kPopupCheckRememberPreference);
1173 popup->SetResultHandler(
1174 [db, result_handler, url](Popup* popup, void* /*user_data*/) {
1175 bool result = popup->GetButtonResult();
1176 bool remember = popup->GetCheckBoxResult();
1178 db->Set(kDBPrivateSection, kUsermediaPermissionPrefix + url,
1179 result ? "allowed" : "denied");
1181 result_handler(result);
1187 } // namespace runtime