1 // Copyright 2014 Samsung Electronics. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "web_contents_delegate_efl.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "base/trace_event/ttrace.h"
11 #include "browser/input_picker/color_chooser_efl.h"
12 #include "browser/javascript_dialog_manager_efl.h"
13 #include "browser/policy_response_delegate_efl.h"
14 #include "browser_context_efl.h"
15 #include "common/render_messages_ewk.h"
16 #include "components/password_manager/core/common/password_manager_pref_names.h"
17 #include "components/prefs/pref_service.h"
18 #include "content/browser/manifest/manifest_manager_host.h"
19 #include "content/browser/web_contents/web_contents_impl.h"
20 #include "content/common/content_switches_internal.h"
21 #include "content/common/render_messages_efl.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "content/public/browser/file_select_listener.h"
24 #include "content/public/browser/invalidate_type.h"
25 #include "content/public/browser/navigation_entry.h"
26 #include "content/public/browser/navigation_handle.h"
27 #include "content/public/browser/render_view_host.h"
28 #include "eweb_view.h"
29 #include "eweb_view_callbacks.h"
30 #include "net/base/load_states.h"
31 #include "net/cert/x509_certificate.h"
32 #include "net/http/http_response_headers.h"
33 #include "private/ewk_certificate_private.h"
34 #include "private/ewk_console_message_private.h"
35 #include "private/ewk_error_private.h"
36 #include "private/ewk_policy_decision_private.h"
37 #include "private/ewk_user_media_private.h"
38 #include "private/webview_delegate_ewk.h"
39 #include "third_party/blink/public/common/input/web_input_event.h"
40 #include "third_party/blink/public/common/manifest/manifest_util.h"
42 #include "web_contents_observer_efl.h"
44 #if BUILDFLAG(IS_TIZEN)
45 #include <app_control.h>
46 #include <app_manager.h>
49 #if defined(TIZEN_MULTIMEDIA)
50 #include "base/functional/callback.h"
51 #include "content/public/browser/media_capture_devices.h"
52 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
55 #if defined(TIZEN_AUTOFILL_SUPPORT)
56 #include "base/command_line.h"
57 #include "browser/autofill/autofill_client_efl.h"
58 #include "browser/password_manager/password_manager_client_efl.h"
59 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
60 #include "components/autofill/core/browser/autofill_client.h"
61 #include "components/autofill/core/browser/autofill_manager.h"
62 #include "components/autofill/core/common/autofill_switches.h"
63 #include "components/web_modal/web_contents_modal_dialog_manager.h"
65 using autofill::AutofillManager;
66 using autofill::AutofillClientEfl;
67 using autofill::ContentAutofillDriverFactory;
68 using password_manager::PasswordManagerClientEfl;
76 #if BUILDFLAG(IS_TIZEN) && defined(TIZEN_MULTIMEDIA)
77 static const blink::MediaStreamDevice* GetRequestedAudioDevice(
78 const std::string& device_id) {
79 const blink::MediaStreamDevices& audio_devices =
80 MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
81 if (audio_devices.empty())
83 if (device_id.length() == 0)
84 return &(*audio_devices.begin());
86 for (blink::MediaStreamDevices::const_iterator i = audio_devices.begin();
87 i != audio_devices.end(); i++) {
88 if (i->id.compare(device_id) == 0)
97 static const blink::MediaStreamDevice* GetRequestedVideoDevice(
98 const std::string& device_id) {
99 const blink::MediaStreamDevices& video_devices =
100 MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
101 if (video_devices.empty())
103 if (device_id.length() == 0)
104 return &(*video_devices.begin());
106 for (blink::MediaStreamDevices::const_iterator i = video_devices.begin();
107 i != video_devices.end(); i++) {
108 if (i->id.compare(device_id) == 0)
118 WebContentsDelegateEfl::WebContentsDelegateEfl(EWebView* view)
120 web_contents_(view->web_contents()),
121 contents_observer_(std::make_unique<WebContentsObserverEfl>(view, this)) {
122 #if defined(TIZEN_AUTOFILL_SUPPORT)
123 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
124 autofill::switches::kDisableAutofill)) {
125 AutofillClientEfl::CreateForWebContents(&web_contents_);
126 AutofillClientEfl* autofill_client =
127 AutofillClientEfl::FromWebContents(&web_contents_);
128 autofill_client->SetEWebView(view);
129 PasswordManagerClientEfl::CreateForWebContentsWithAutofillClient(
130 &web_contents_, autofill_client);
131 ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
132 &web_contents_, autofill_client,
133 base::BindRepeating(&autofill::BrowserDriverInitHook, autofill_client,
134 EWebView::GetPlatformLocale()));
139 WebContentsDelegateEfl::~WebContentsDelegateEfl() {
140 // It's important to delete web_contents_ before dialog_manager_
141 // destructor of web contents uses dialog_manager_
143 delete dialog_manager_;
146 WebContents* WebContentsDelegateEfl::OpenURLFromTab(
148 const OpenURLParams& params) {
149 const GURL& url = params.url;
150 WindowOpenDisposition disposition = params.disposition;
152 if (!source || (disposition != WindowOpenDisposition::CURRENT_TAB &&
153 disposition != WindowOpenDisposition::NEW_FOREGROUND_TAB &&
154 disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB &&
155 disposition != WindowOpenDisposition::OFF_THE_RECORD)) {
160 if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
161 disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
162 disposition == WindowOpenDisposition::OFF_THE_RECORD) {
163 Evas_Object* new_object = NULL;
164 web_view_->SmartCallback<EWebViewCallbacks::CreateNewWindow>().call(
170 if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB)
171 ActivateContents(source);
174 WebViewDelegateEwk::GetInstance().GetWebViewFromEvasObject(new_object);
180 ui::PageTransition transition(ui::PageTransitionFromInt(params.transition));
181 source->GetController().LoadURL(url, params.referrer, transition,
186 void WebContentsDelegateEfl::NavigationStateChanged(
187 WebContents* source, InvalidateTypes changed_flags) {
188 // We always notfiy clients about title invalidation, even if its text
189 // didn't actually change. This is to maintain EWK API consistency.
190 if (changed_flags & INVALIDATE_TYPE_TITLE) {
191 web_view_->SmartCallback<EWebViewCallbacks::TitleChange>().call(
192 base::UTF16ToUTF8(source->GetTitle()).c_str());
195 // We only notify clients if visible url actually changed, because on some
196 // pages we would get notifications with flag INVALIDATE_TYPE_URL even when
197 // visible url did not change.
198 if ((changed_flags & INVALIDATE_TYPE_URL) &&
199 last_visible_url_ != source->GetVisibleURL()) {
200 last_visible_url_ = source->GetVisibleURL();
201 const char* url = last_visible_url_.spec().c_str();
203 web_view_->SmartCallback<EWebViewCallbacks::URLChanged>().call(url);
204 web_view_->SmartCallback<EWebViewCallbacks::URIChanged>().call(url);
208 void WebContentsDelegateEfl::AddNewContents(
210 std::unique_ptr<WebContents> new_contents,
211 const GURL& target_url,
212 WindowOpenDisposition disposition,
213 const blink::mojom::WindowFeatures& window_features,
216 // Initialize the delegate for the new contents here using source's delegate
217 // as it will be needed to create a new window with the pre-created
218 // new contents in case the opener is suppressed. Otherwise, the delegate
219 // had already been initialized in EWebView::InitializeContent()
220 if (!new_contents->GetDelegate())
221 new_contents->SetDelegate(this);
223 // EWebView takes the ownership of new WebContents in
224 // EWebView::InitializeContent(). So, we can release the ownership of new
226 std::ignore = new_contents.release();
229 bool WebContentsDelegateEfl::ShouldCreateWebContents(
230 WebContents* web_contents,
231 RenderFrameHost* opener,
232 SiteInstance* source_site_instance,
233 mojom::WindowContainerType window_container_type,
234 const GURL& opener_url,
235 const std::string& frame_name,
236 const GURL& target_url,
237 const std::string& partition_id,
238 SessionStorageNamespace* session_storage_namespace) {
239 // We implement the asynchronous version of the function, this
240 // one should never be invoked.
245 void WebContentsDelegateEfl::CloseContents(WebContents* source) {
246 web_view_->SmartCallback<EWebViewCallbacks::WindowClosed>().call();
249 void WebContentsDelegateEfl::EnterFullscreenModeForTab(
250 RenderFrameHost* requesting_frame,
251 const blink::mojom::FullscreenOptions& options) {
252 is_fullscreen_ = true;
253 web_view_->SmartCallback<EWebViewCallbacks::EnterFullscreen>().call();
256 void WebContentsDelegateEfl::ExitFullscreenModeForTab(
257 WebContents* web_contents) {
258 is_fullscreen_ = false;
259 web_view_->SmartCallback<EWebViewCallbacks::ExitFullscreen>().call();
262 bool WebContentsDelegateEfl::IsFullscreenForTabOrPending(
263 const WebContents* web_contents) {
264 return is_fullscreen_;
267 #if defined(TIZEN_MULTIMEDIA)
268 /* LCOV_EXCL_START */
269 void WebContentsDelegateEfl::RequestMediaAccessAllow(
270 const MediaStreamRequest& request,
271 MediaResponseCallback callback) {
272 blink::mojom::StreamDevicesSet stream_devices_set;
273 stream_devices_set.stream_devices.emplace_back(
274 blink::mojom::StreamDevices::New());
275 blink::mojom::StreamDevices& stream_devices =
276 *stream_devices_set.stream_devices[0];
278 if (request.audio_type ==
279 blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
280 const blink::MediaStreamDevice* audio_device =
281 GetRequestedAudioDevice(request.requested_audio_device_id);
283 stream_devices.audio_device = *audio_device;
285 std::move(callback).Run(
286 blink::mojom::StreamDevicesSet(),
287 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
288 std::unique_ptr<MediaStreamUI>());
293 if (request.video_type ==
294 blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
295 const blink::MediaStreamDevice* video_device =
296 GetRequestedVideoDevice(request.requested_video_device_id);
298 stream_devices.video_device = *video_device;
300 std::move(callback).Run(
301 blink::mojom::StreamDevicesSet(),
302 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
303 std::unique_ptr<MediaStreamUI>());
308 std::move(callback).Run(stream_devices_set,
309 blink::mojom::MediaStreamRequestResult::OK,
310 std::unique_ptr<MediaStreamUI>());
314 void WebContentsDelegateEfl::RequestMediaAccessDeny(
315 const MediaStreamRequest& request,
316 MediaResponseCallback callback) {
317 std::move(callback).Run(blink::mojom::StreamDevicesSet(),
318 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
319 std::unique_ptr<MediaStreamUI>());
322 bool WebContentsDelegateEfl::CheckMediaAccessPermission(
323 RenderFrameHost* render_frame_host,
324 const GURL& security_origin,
325 blink::mojom::MediaStreamType type) {
329 void WebContentsDelegateEfl::RequestMediaAccessPermission(
330 WebContents* web_contents,
331 const MediaStreamRequest& request,
332 MediaResponseCallback callback) {
333 std::unique_ptr<_Ewk_User_Media_Permission_Request> media_permission_request(
334 new _Ewk_User_Media_Permission_Request(this, request,
335 std::move(callback)));
337 Eina_Bool callback_result = EINA_FALSE;
338 if (!web_view_->InvokeViewUserMediaPermissionCallback(
339 media_permission_request.get(), &callback_result)) {
340 web_view_->SmartCallback<EWebViewCallbacks::UserMediaPermission>()
341 .call( // LCOV_EXCL_LINE
342 media_permission_request.get());
345 // if policy is suspended, the API takes over the policy object lifetime
346 // and policy will be deleted after decision is made
347 if (media_permission_request->IsSuspended())
348 std::ignore = media_permission_request.release();
349 /* LCOV_EXCL_START */
350 else if (!media_permission_request->IsDecided())
351 media_permission_request->ProceedPermissionCallback(false);
356 void WebContentsDelegateEfl::OnAuthRequired(const std::string& realm,
358 LoginDelegateEfl* login_delegate) {
359 web_view_->InvokeAuthCallback(login_delegate, url, realm);
362 void WebContentsDelegateEfl::RequestCertificateConfirm(
363 WebContents* /*web_contents*/,
365 const net::SSLInfo& ssl_info,
367 bool is_main_frame_request,
368 bool /*strict_enforcement*/,
369 base::OnceCallback<void(CertificateRequestResultType)> callback) {
370 std::string pem_certificate;
371 if (!net::X509Certificate::GetPEMEncoded(ssl_info.cert->cert_buffer(),
373 LOG(INFO) << "Certificate for URL: " << url.spec()
374 << " could not be opened";
375 std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
378 certificate_policy_decision_.reset(new _Ewk_Certificate_Policy_Decision(
379 url, pem_certificate, is_main_frame_request, cert_error,
380 std::move(callback)));
382 web_view_->SmartCallback<EWebViewCallbacks::RequestCertificateConfirm>().call(
383 certificate_policy_decision_.get());
384 LOG(INFO) << "Certificate policy decision called for URL: " << url.spec()
385 << " with cert_error: " << cert_error;
387 // if policy is suspended, the API takes over the policy object lifetime
388 // and policy will be deleted after decision is made
389 if (certificate_policy_decision_->IsSuspended()) {
390 std::ignore = certificate_policy_decision_.release();
391 } else if (!certificate_policy_decision_->IsDecided()) {
392 // When the embeder neither suspended certificate decision nor handled it
393 // inside the smart callback by calling
394 // ewk_certificate_policy_decision_allowed_set
395 // this means the embeder let chromium decide what to do.
396 if (!is_main_frame_request) {
397 // A sub-resource has a certificate error. The user doesn't really
398 // have a context for making the right decision, so block the
400 std::move(certificate_policy_decision_->Callback())
401 .Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
403 // By default chromium-efl allows page to be opened with certificate
405 certificate_policy_decision_->SetDecision(true);
406 LOG(WARNING) << "Certificate for " << url.spec() << " was compromised";
411 void WebContentsDelegateEfl::VisibleSecurityStateChanged(WebContents* source) {
412 std::unique_ptr<_Ewk_Certificate_Info> certificate_info;
413 std::string pem_certificate;
414 SSLStatus ssl_status = source->GetController().GetVisibleEntry()->GetSSL();
415 scoped_refptr<net::X509Certificate> cert = ssl_status.certificate;
418 LOG(ERROR) << "Unable to get certificate!";
419 certificate_info.reset(new _Ewk_Certificate_Info(nullptr, false));
421 if (!net::X509Certificate::GetPEMEncoded(cert->cert_buffer(),
423 LOG(ERROR) << "Unable to get encoded PEM!";
426 // Note: The implementation below is a copy of the one in
427 // ChromeAutofillClient::IsContextSecure, and should be kept in sync
428 // until crbug.com/505388 gets implemented.
429 bool is_context_secure =
430 ssl_status.certificate &&
431 !net::IsCertStatusError(ssl_status.cert_status) &&
432 !(ssl_status.content_status & content::SSLStatus::RAN_INSECURE_CONTENT);
434 certificate_info.reset(
435 new _Ewk_Certificate_Info(pem_certificate.c_str(), is_context_secure));
437 web_view_->SmartCallback<EWebViewCallbacks::SSLCertificateChanged>().call(
438 certificate_info.get());
441 void WebContentsDelegateEfl::ActivateContents(WebContents* contents) {
442 #if BUILDFLAG(IS_TIZEN)
443 app_control_h app_control = nullptr;
444 int ret = app_control_create(&app_control);
445 if (ret != APP_CONTROL_ERROR_NONE) {
446 LOG(ERROR) << "app_control_create is failed with err " << ret;
450 std::unique_ptr<std::remove_pointer<app_control_h>::type,
451 decltype(app_control_destroy)*>
452 auto_release{app_control, app_control_destroy};
454 char* app_id = nullptr;
455 ret = app_manager_get_app_id(getpid(), &app_id);
456 if (ret != APP_MANAGER_ERROR_NONE) {
457 LOG(ERROR) << "app_manager_get_app_id is failed with err " << ret;
461 ret = app_control_set_app_id(app_control, app_id);
464 if (ret != APP_CONTROL_ERROR_NONE) {
465 LOG(ERROR) << "app_control_set_app_id is failed with err " << ret;
469 ret = app_control_send_launch_request(app_control, nullptr, nullptr);
470 if (ret != APP_CONTROL_ERROR_NONE) {
471 LOG(ERROR) << "app_control_send_launch_request is failed with err " << ret;
477 void WebContentsDelegateEfl::SetContentSecurityPolicy(
478 const std::string& policy,
479 Ewk_CSP_Header_Type header_type) {
480 contents_observer_->SetContentSecurityPolicy(policy, header_type);
483 #if defined(TIZEN_AUTOFILL_SUPPORT)
484 void WebContentsDelegateEfl::UpdateAutofillIfRequired() {
485 if (AutofillClientEfl* autofill_client =
486 AutofillClientEfl::FromWebContents(&web_contents_)) {
487 autofill_client->UpdateAutofillIfRequired();
492 void WebContentsDelegateEfl::OnDidChangeFocusedNodeBounds(
493 const gfx::RectF& focused_node_bounds) {
494 #if defined(TIZEN_AUTOFILL_SUPPORT)
495 if (AutofillClientEfl* autofill_client =
496 AutofillClientEfl::FromWebContents(&web_contents_)) {
497 autofill_client->DidChangeFocusedNodeBounds(focused_node_bounds);
502 void WebContentsDelegateEfl::FindReply(WebContents* web_contents,
504 int number_of_matches,
505 const gfx::Rect& selection_rect,
506 int active_match_ordinal,
508 if (final_update && request_id == web_view_->current_find_request_id()) {
509 unsigned int uint_number_of_matches =
510 static_cast<unsigned int>(number_of_matches);
511 web_view_->SmartCallback<EWebViewCallbacks::TextFound>().call(
512 &uint_number_of_matches);
516 void WebContentsDelegateEfl::DidRenderFrame() {
517 // Call FrameRendered callback when loading and first layout is finished.
518 if (!did_render_frame_ && did_first_visually_non_empty_paint_ &&
519 (web_view_->GetProgressValue() > 0.1)) {
520 did_first_visually_non_empty_paint_ = false;
521 did_render_frame_ = true;
522 TTRACE_WEB("WebContentsDelegateEfl::DidRenderFrame");
523 LOG(INFO) << "WebContentsDelegateEfl::DidRenderFrame";
525 // "frame,rendered" message is triggered as soon as rendering of a frame
527 web_view_->SmartCallback<EWebViewCallbacks::FrameRendered>().call(0);
531 JavaScriptDialogManager* WebContentsDelegateEfl::GetJavaScriptDialogManager(
532 WebContents* source) {
533 if (!dialog_manager_)
534 dialog_manager_ = new JavaScriptDialogManagerEfl();
535 return dialog_manager_;
538 void WebContentsDelegateEfl::OnUpdateSettings(const Ewk_Settings* settings) {
539 #if defined(TIZEN_AUTOFILL_SUPPORT)
540 PasswordManagerClientEfl* client =
541 PasswordManagerClientEfl::FromWebContents(&web_contents_);
543 PrefService* prefs = client->GetPrefs();
544 prefs->SetBoolean(password_manager::prefs::kCredentialsEnableService,
545 settings->autofillPasswordForm());
550 void WebContentsDelegateEfl::DidFirstVisuallyNonEmptyPaint() {
551 did_first_visually_non_empty_paint_ = true;
552 web_view_->SmartCallback<EWebViewCallbacks::LoadNonEmptyLayoutFinished>()
556 void WebContentsDelegateEfl::DidStartLoading() {
557 did_render_frame_ = false;
560 bool WebContentsDelegateEfl::DidAddMessageToConsole(
562 blink::mojom::ConsoleMessageLevel level,
563 const std::u16string& message,
565 const std::u16string& source_id) {
566 std::unique_ptr<_Ewk_Console_Message> console_message(
567 new _Ewk_Console_Message(static_cast<unsigned>(level),
568 base::UTF16ToUTF8(message).c_str(), line_no,
569 base::UTF16ToUTF8(source_id).c_str()));
570 web_view_->SmartCallback<EWebViewCallbacks::ConsoleMessage>().call(
571 console_message.get());
575 void WebContentsDelegateEfl::RunFileChooser(
576 RenderFrameHost* render_frame_host,
577 scoped_refptr<FileSelectListener> listener,
578 const blink::mojom::FileChooserParams& params) {
579 web_view_->ShowFileChooser(render_frame_host, params);
582 std::unique_ptr<ColorChooser> WebContentsDelegateEfl::OpenColorChooser(
583 WebContents* web_contents,
585 const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) {
586 web_view_->RequestColorPicker(SkColorGetR(color), SkColorGetG(color),
587 SkColorGetB(color), SkColorGetA(color));
588 return std::make_unique<ColorChooserEfl>(*web_contents);
591 #if !defined(EWK_BRINGUP) // FIXME: m76 bringup
592 void WebContentsDelegateEfl::OpenDateTimeDialog(
593 ui::TextInputType dialog_type,
598 const std::vector<DateTimeSuggestion>& suggestions) {
599 web_view_->InputPickerShow(dialog_type, dialog_value);
603 bool WebContentsDelegateEfl::PreHandleGestureEvent(
605 const blink::WebGestureEvent& event) {
606 blink::WebInputEvent::Type event_type = event.GetType();
607 switch (event_type) {
608 case blink::WebInputEvent::Type::kGestureDoubleTap:
612 case blink::WebInputEvent::Type::kGesturePinchBegin:
613 case blink::WebInputEvent::Type::kGesturePinchUpdate:
614 case blink::WebInputEvent::Type::kGesturePinchEnd:
615 if (!IsPinchToZoomEnabled() ||
616 IsFullscreenForTabOrPending(&web_contents()))
625 void WebContentsDelegateEfl::BackgroundColorReceived(int callback_id,
627 web_view_->OnGetBackgroundColor(callback_id, bg_color);
630 void WebContentsDelegateEfl::RequestManifestInfo(
631 Ewk_View_Request_Manifest_Callback callback,
633 WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
634 ManifestManagerHost* manifest_manager_host =
635 ManifestManagerHost::GetOrCreateForPage(wci->GetPrimaryPage());
636 if (!manifest_manager_host) {
637 web_view_->DidRespondRequestManifest(nullptr, callback, user_data);
641 manifest_manager_host->GetManifest(
642 base::BindOnce(&WebContentsDelegateEfl::OnDidGetManifest,
643 base::Unretained(this), callback, user_data));
646 void WebContentsDelegateEfl::OnDidGetManifest(
647 Ewk_View_Request_Manifest_Callback callback,
649 const GURL& manifest_url,
650 blink::mojom::ManifestPtr manifest) {
651 if (blink::IsEmptyManifest(*manifest)) {
652 web_view_->DidRespondRequestManifest(nullptr, callback, user_data);
654 _Ewk_View_Request_Manifest ewk_manifest(std::move(manifest));
655 web_view_->DidRespondRequestManifest(&ewk_manifest, callback, user_data);
659 } // namespace content