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/renderer_host/render_widget_host_view_aura.h"
20 #include "content/browser/web_contents/web_contents_impl.h"
21 #include "content/common/content_switches_internal.h"
22 #include "content/common/render_messages_efl.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/file_select_listener.h"
25 #include "content/public/browser/invalidate_type.h"
26 #include "content/public/browser/navigation_entry.h"
27 #include "content/public/browser/navigation_handle.h"
28 #include "content/public/browser/render_view_host.h"
29 #include "eweb_view.h"
30 #include "eweb_view_callbacks.h"
31 #include "net/base/load_states.h"
32 #include "net/cert/x509_certificate.h"
33 #include "net/http/http_response_headers.h"
34 #include "private/ewk_certificate_private.h"
35 #include "private/ewk_console_message_private.h"
36 #include "private/ewk_error_private.h"
37 #include "private/ewk_policy_decision_private.h"
38 #include "private/ewk_user_media_private.h"
39 #include "private/webview_delegate_ewk.h"
40 #include "third_party/blink/public/common/input/web_input_event.h"
41 #include "third_party/blink/public/common/manifest/manifest_util.h"
42 #include "tizen/system_info.h"
44 #include "web_contents_observer_efl.h"
46 #if BUILDFLAG(IS_TIZEN)
47 #include <app_control.h>
48 #include <app_manager.h>
51 #if defined(TIZEN_MULTIMEDIA)
52 #include "base/functional/callback.h"
53 #include "content/public/browser/media_capture_devices.h"
54 #include "third_party/blink/public/mojom/mediastream/media_stream.mojom.h"
57 #if defined(TIZEN_AUTOFILL_FW)
58 #include "browser/autofill/autofill_request_manager.h"
61 #if defined(TIZEN_AUTOFILL)
62 #include "base/command_line.h"
63 #include "browser/autofill/autofill_client_efl.h"
64 #include "browser/password_manager/password_manager_client_efl.h"
65 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
66 #include "components/autofill/core/browser/autofill_client.h"
67 #include "components/autofill/core/browser/autofill_manager.h"
68 #include "components/autofill/core/common/autofill_switches.h"
69 #include "components/web_modal/web_contents_modal_dialog_manager.h"
71 using autofill::AutofillManager;
72 using autofill::AutofillClientEfl;
73 using autofill::ContentAutofillDriverFactory;
74 using password_manager::PasswordManagerClientEfl;
82 #if BUILDFLAG(IS_TIZEN) && defined(TIZEN_MULTIMEDIA)
83 static const blink::MediaStreamDevice* GetRequestedAudioDevice(
84 const std::string& device_id) {
85 const blink::MediaStreamDevices& audio_devices =
86 MediaCaptureDevices::GetInstance()->GetAudioCaptureDevices();
87 if (audio_devices.empty())
89 if (device_id.length() == 0)
90 return &(*audio_devices.begin());
92 for (blink::MediaStreamDevices::const_iterator i = audio_devices.begin();
93 i != audio_devices.end(); i++) {
94 if (i->id.compare(device_id) == 0)
102 /* LCOV_EXCL_START */
103 static const blink::MediaStreamDevice* GetRequestedVideoDevice(
104 const std::string& device_id) {
105 const blink::MediaStreamDevices& video_devices =
106 MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
107 if (video_devices.empty())
109 if (device_id.length() == 0)
110 return &(*video_devices.begin());
112 for (blink::MediaStreamDevices::const_iterator i = video_devices.begin();
113 i != video_devices.end(); i++) {
114 if (i->id.compare(device_id) == 0)
124 WebContentsDelegateEfl::WebContentsDelegateEfl(EWebView* view)
126 web_contents_(view->web_contents()),
127 contents_observer_(std::make_unique<WebContentsObserverEfl>(view, this)) {
128 #if defined(TIZEN_AUTOFILL)
129 if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
130 autofill::switches::kDisableAutofill)) {
131 AutofillClientEfl::CreateForWebContents(&web_contents_);
132 AutofillClientEfl* autofill_client =
133 AutofillClientEfl::FromWebContents(&web_contents_);
134 autofill_client->SetEWebView(view);
135 PasswordManagerClientEfl::CreateForWebContentsWithAutofillClient(
136 &web_contents_, autofill_client);
137 ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
138 &web_contents_, autofill_client,
139 base::BindRepeating(&autofill::BrowserDriverInitHook, autofill_client,
140 EWebView::GetPlatformLocale()));
142 #if defined(TIZEN_AUTOFILL_FW)
143 LOG(INFO) << "[Autofill] " << __FUNCTION__ << " Create AutofillRequest";
144 autofill::AutofillRequestManager::GetInstance()->CreateAutofillRequest(
145 view->evas_object());
150 WebContentsDelegateEfl::~WebContentsDelegateEfl() {
151 // It's important to delete web_contents_ before dialog_manager_
152 // destructor of web contents uses dialog_manager_
154 delete dialog_manager_;
157 WebContents* WebContentsDelegateEfl::OpenURLFromTab(
159 const OpenURLParams& params) {
160 const GURL& url = params.url;
161 WindowOpenDisposition disposition = params.disposition;
163 if (!source || (disposition != WindowOpenDisposition::CURRENT_TAB &&
164 disposition != WindowOpenDisposition::NEW_FOREGROUND_TAB &&
165 disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB &&
166 disposition != WindowOpenDisposition::OFF_THE_RECORD)) {
171 if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
172 disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
173 disposition == WindowOpenDisposition::OFF_THE_RECORD) {
174 Evas_Object* new_object = NULL;
175 if (IsMobileProfile() &&
176 disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB) {
177 web_view_->SmartCallback<EWebViewCallbacks::CreateNewBackgroundWindow>()
180 web_view_->SmartCallback<EWebViewCallbacks::CreateNewWindow>().call(
187 if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB)
188 ActivateContents(source);
191 WebViewDelegateEwk::GetInstance().GetWebViewFromEvasObject(new_object);
197 ui::PageTransition transition(ui::PageTransitionFromInt(params.transition));
198 source->GetController().LoadURL(url, params.referrer, transition,
203 void WebContentsDelegateEfl::NavigationStateChanged(
204 WebContents* source, InvalidateTypes changed_flags) {
205 // We always notfiy clients about title invalidation, even if its text
206 // didn't actually change. This is to maintain EWK API consistency.
207 if (changed_flags & INVALIDATE_TYPE_TITLE) {
208 web_view_->SmartCallback<EWebViewCallbacks::TitleChange>().call(
209 base::UTF16ToUTF8(source->GetTitle()).c_str());
212 // We only notify clients if visible url actually changed, because on some
213 // pages we would get notifications with flag INVALIDATE_TYPE_URL even when
214 // visible url did not change.
215 if ((changed_flags & INVALIDATE_TYPE_URL) &&
216 last_visible_url_ != source->GetVisibleURL()) {
217 last_visible_url_ = source->GetVisibleURL();
218 const char* url = last_visible_url_.spec().c_str();
220 web_view_->SmartCallback<EWebViewCallbacks::URLChanged>().call(url);
221 web_view_->SmartCallback<EWebViewCallbacks::URIChanged>().call(url);
222 web_view_->ResetContextMenuController();
223 auto rwhva = static_cast<RenderWidgetHostViewAura*>(
224 web_contents_.GetRenderWidgetHostView());
226 auto selection_controller =
227 rwhva->offscreen_helper()->GetSelectionController();
228 if (selection_controller)
229 selection_controller->ClearSelection();
234 void WebContentsDelegateEfl::AddNewContents(
236 std::unique_ptr<WebContents> new_contents,
237 const GURL& target_url,
238 WindowOpenDisposition disposition,
239 const blink::mojom::WindowFeatures& window_features,
242 // Initialize the delegate for the new contents here using source's delegate
243 // as it will be needed to create a new window with the pre-created
244 // new contents in case the opener is suppressed. Otherwise, the delegate
245 // had already been initialized in EWebView::InitializeContent()
246 if (!new_contents->GetDelegate())
247 new_contents->SetDelegate(this);
249 // EWebView takes the ownership of new WebContents in
250 // EWebView::InitializeContent(). So, we can release the ownership of new
252 std::ignore = new_contents.release();
255 bool WebContentsDelegateEfl::ShouldCreateWebContents(
256 WebContents* web_contents,
257 RenderFrameHost* opener,
258 SiteInstance* source_site_instance,
259 mojom::WindowContainerType window_container_type,
260 const GURL& opener_url,
261 const std::string& frame_name,
262 const GURL& target_url,
263 const std::string& partition_id,
264 SessionStorageNamespace* session_storage_namespace) {
265 // We implement the asynchronous version of the function, this
266 // one should never be invoked.
271 void WebContentsDelegateEfl::CloseContents(WebContents* source) {
272 web_view_->SmartCallback<EWebViewCallbacks::WindowClosed>().call();
275 void WebContentsDelegateEfl::EnterFullscreenModeForTab(
276 RenderFrameHost* requesting_frame,
277 const blink::mojom::FullscreenOptions& options) {
278 is_fullscreen_ = true;
279 web_view_->SmartCallback<EWebViewCallbacks::EnterFullscreen>().call();
282 void WebContentsDelegateEfl::ExitFullscreenModeForTab(
283 WebContents* web_contents) {
284 is_fullscreen_ = false;
285 web_view_->SmartCallback<EWebViewCallbacks::ExitFullscreen>().call();
288 bool WebContentsDelegateEfl::IsFullscreenForTabOrPending(
289 const WebContents* web_contents) {
290 return is_fullscreen_;
293 #if defined(TIZEN_MULTIMEDIA)
294 /* LCOV_EXCL_START */
295 void WebContentsDelegateEfl::RequestMediaAccessAllow(
296 const MediaStreamRequest& request,
297 MediaResponseCallback callback) {
298 blink::mojom::StreamDevicesSet stream_devices_set;
299 stream_devices_set.stream_devices.emplace_back(
300 blink::mojom::StreamDevices::New());
301 blink::mojom::StreamDevices& stream_devices =
302 *stream_devices_set.stream_devices[0];
304 if (request.audio_type ==
305 blink::mojom::MediaStreamType::DEVICE_AUDIO_CAPTURE) {
306 const blink::MediaStreamDevice* audio_device =
307 GetRequestedAudioDevice(request.requested_audio_device_id);
309 stream_devices.audio_device = *audio_device;
311 std::move(callback).Run(
312 blink::mojom::StreamDevicesSet(),
313 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
314 std::unique_ptr<MediaStreamUI>());
319 if (request.video_type ==
320 blink::mojom::MediaStreamType::DEVICE_VIDEO_CAPTURE) {
321 const blink::MediaStreamDevice* video_device =
322 GetRequestedVideoDevice(request.requested_video_device_id);
324 stream_devices.video_device = *video_device;
326 std::move(callback).Run(
327 blink::mojom::StreamDevicesSet(),
328 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
329 std::unique_ptr<MediaStreamUI>());
334 std::move(callback).Run(stream_devices_set,
335 blink::mojom::MediaStreamRequestResult::OK,
336 std::unique_ptr<MediaStreamUI>());
340 void WebContentsDelegateEfl::RequestMediaAccessDeny(
341 const MediaStreamRequest& request,
342 MediaResponseCallback callback) {
343 std::move(callback).Run(blink::mojom::StreamDevicesSet(),
344 blink::mojom::MediaStreamRequestResult::NOT_SUPPORTED,
345 std::unique_ptr<MediaStreamUI>());
348 bool WebContentsDelegateEfl::CheckMediaAccessPermission(
349 RenderFrameHost* render_frame_host,
350 const GURL& security_origin,
351 blink::mojom::MediaStreamType type) {
355 void WebContentsDelegateEfl::RequestMediaAccessPermission(
356 WebContents* web_contents,
357 const MediaStreamRequest& request,
358 MediaResponseCallback callback) {
359 std::unique_ptr<_Ewk_User_Media_Permission_Request> media_permission_request(
360 new _Ewk_User_Media_Permission_Request(this, request,
361 std::move(callback)));
363 Eina_Bool callback_result = EINA_FALSE;
364 if (!web_view_->InvokeViewUserMediaPermissionCallback(
365 media_permission_request.get(), &callback_result)) {
366 web_view_->SmartCallback<EWebViewCallbacks::UserMediaPermission>()
367 .call( // LCOV_EXCL_LINE
368 media_permission_request.get());
371 // if policy is suspended, the API takes over the policy object lifetime
372 // and policy will be deleted after decision is made
373 if (media_permission_request->IsSuspended())
374 std::ignore = media_permission_request.release();
375 /* LCOV_EXCL_START */
376 else if (!media_permission_request->IsDecided())
377 media_permission_request->ProceedPermissionCallback(false);
382 void WebContentsDelegateEfl::OnAuthRequired(const std::string& realm,
384 LoginDelegateEfl* login_delegate) {
385 web_view_->InvokeAuthCallback(login_delegate, url, realm);
388 void WebContentsDelegateEfl::RequestCertificateConfirm(
389 WebContents* /*web_contents*/,
391 const net::SSLInfo& ssl_info,
393 bool is_main_frame_request,
394 bool /*strict_enforcement*/,
395 base::OnceCallback<void(CertificateRequestResultType)> callback) {
396 std::string pem_certificate;
397 if (!net::X509Certificate::GetPEMEncoded(ssl_info.cert->cert_buffer(),
399 LOG(INFO) << "Certificate for URL: " << url.spec()
400 << " could not be opened";
401 std::move(callback).Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL);
404 certificate_policy_decision_.reset(new _Ewk_Certificate_Policy_Decision(
405 url, pem_certificate, is_main_frame_request, cert_error,
406 std::move(callback)));
408 web_view_->SmartCallback<EWebViewCallbacks::RequestCertificateConfirm>().call(
409 certificate_policy_decision_.get());
410 LOG(INFO) << "Certificate policy decision called for URL: " << url.spec()
411 << " with cert_error: " << cert_error;
413 // if policy is suspended, the API takes over the policy object lifetime
414 // and policy will be deleted after decision is made
415 if (certificate_policy_decision_->IsSuspended()) {
416 std::ignore = certificate_policy_decision_.release();
417 } else if (!certificate_policy_decision_->IsDecided()) {
418 // When the embeder neither suspended certificate decision nor handled it
419 // inside the smart callback by calling
420 // ewk_certificate_policy_decision_allowed_set
421 // this means the embeder let chromium decide what to do.
422 if (!is_main_frame_request) {
423 // A sub-resource has a certificate error. The user doesn't really
424 // have a context for making the right decision, so block the
426 std::move(certificate_policy_decision_->Callback())
427 .Run(content::CERTIFICATE_REQUEST_RESULT_TYPE_DENY);
429 // By default chromium-efl allows page to be opened with certificate
431 certificate_policy_decision_->SetDecision(true);
432 LOG(WARNING) << "Certificate for " << url.spec() << " was compromised";
437 void WebContentsDelegateEfl::VisibleSecurityStateChanged(WebContents* source) {
438 std::unique_ptr<_Ewk_Certificate_Info> certificate_info;
439 std::string pem_certificate;
440 SSLStatus ssl_status = source->GetController().GetVisibleEntry()->GetSSL();
441 scoped_refptr<net::X509Certificate> cert = ssl_status.certificate;
444 LOG(ERROR) << "Unable to get certificate!";
445 certificate_info.reset(new _Ewk_Certificate_Info(nullptr, false));
447 if (!net::X509Certificate::GetPEMEncoded(cert->cert_buffer(),
449 LOG(ERROR) << "Unable to get encoded PEM!";
452 // Note: The implementation below is a copy of the one in
453 // ChromeAutofillClient::IsContextSecure, and should be kept in sync
454 // until crbug.com/505388 gets implemented.
455 bool is_context_secure =
456 ssl_status.certificate &&
457 !net::IsCertStatusError(ssl_status.cert_status) &&
458 !(ssl_status.content_status & content::SSLStatus::RAN_INSECURE_CONTENT);
460 certificate_info.reset(
461 new _Ewk_Certificate_Info(pem_certificate.c_str(), is_context_secure));
463 web_view_->SmartCallback<EWebViewCallbacks::SSLCertificateChanged>().call(
464 certificate_info.get());
467 void WebContentsDelegateEfl::ActivateContents(WebContents* contents) {
468 #if BUILDFLAG(IS_TIZEN)
469 app_control_h app_control = nullptr;
470 int ret = app_control_create(&app_control);
471 if (ret != APP_CONTROL_ERROR_NONE) {
472 LOG(ERROR) << "app_control_create is failed with err " << ret;
476 std::unique_ptr<std::remove_pointer<app_control_h>::type,
477 decltype(app_control_destroy)*>
478 auto_release{app_control, app_control_destroy};
480 char* app_id = nullptr;
481 ret = app_manager_get_app_id(getpid(), &app_id);
482 if (ret != APP_MANAGER_ERROR_NONE) {
483 LOG(ERROR) << "app_manager_get_app_id is failed with err " << ret;
487 ret = app_control_set_app_id(app_control, app_id);
490 if (ret != APP_CONTROL_ERROR_NONE) {
491 LOG(ERROR) << "app_control_set_app_id is failed with err " << ret;
495 ret = app_control_send_launch_request(app_control, nullptr, nullptr);
496 if (ret != APP_CONTROL_ERROR_NONE) {
497 LOG(ERROR) << "app_control_send_launch_request is failed with err " << ret;
503 void WebContentsDelegateEfl::SetContentSecurityPolicy(
504 const std::string& policy,
505 Ewk_CSP_Header_Type header_type) {
506 contents_observer_->SetContentSecurityPolicy(policy, header_type);
509 #if defined(TIZEN_AUTOFILL)
510 void WebContentsDelegateEfl::UpdateAutofillIfRequired() {
511 if (AutofillClientEfl* autofill_client =
512 AutofillClientEfl::FromWebContents(&web_contents_)) {
513 autofill_client->UpdateAutofillIfRequired();
518 void WebContentsDelegateEfl::OnDidChangeFocusedNodeBounds(
519 const gfx::RectF& focused_node_bounds) {
520 #if defined(TIZEN_AUTOFILL)
521 if (AutofillClientEfl* autofill_client =
522 AutofillClientEfl::FromWebContents(&web_contents_)) {
523 autofill_client->DidChangeFocusedNodeBounds(focused_node_bounds);
528 #if defined(TIZEN_AUTOFILL_FW)
529 void WebContentsDelegateEfl::ResetLastInteractedElements() {
530 if (AutofillClientEfl* autofill_client =
531 AutofillClientEfl::FromWebContents(&web_contents_)) {
532 autofill_client->ResetLastInteractedElements();
537 void WebContentsDelegateEfl::FindReply(WebContents* web_contents,
539 int number_of_matches,
540 const gfx::Rect& selection_rect,
541 int active_match_ordinal,
543 if (final_update && request_id == web_view_->current_find_request_id()) {
544 unsigned int uint_number_of_matches =
545 static_cast<unsigned int>(number_of_matches);
546 web_view_->SmartCallback<EWebViewCallbacks::TextFound>().call(
547 &uint_number_of_matches);
551 void WebContentsDelegateEfl::DidRenderFrame() {
552 // Call FrameRendered callback when loading and first layout is finished.
553 if (!did_render_frame_ && did_first_visually_non_empty_paint_ &&
554 (web_view_->GetProgressValue() > 0.1)) {
555 did_first_visually_non_empty_paint_ = false;
556 did_render_frame_ = true;
557 TTRACE_WEB("WebContentsDelegateEfl::DidRenderFrame");
558 LOG(INFO) << "WebContentsDelegateEfl::DidRenderFrame";
560 // "frame,rendered" message is triggered as soon as rendering of a frame
562 web_view_->SmartCallback<EWebViewCallbacks::FrameRendered>().call(0);
566 void WebContentsDelegateEfl::DidChangeInputType(bool is_password_input) {
567 web_view_->UpdateContextMenu(is_password_input);
570 JavaScriptDialogManager* WebContentsDelegateEfl::GetJavaScriptDialogManager(
571 WebContents* source) {
572 if (!dialog_manager_)
573 dialog_manager_ = new JavaScriptDialogManagerEfl();
574 return dialog_manager_;
577 void WebContentsDelegateEfl::OnUpdateSettings(const Ewk_Settings* settings) {
578 #if defined(TIZEN_AUTOFILL)
579 PasswordManagerClientEfl* client =
580 PasswordManagerClientEfl::FromWebContents(&web_contents_);
582 PrefService* prefs = client->GetPrefs();
583 prefs->SetBoolean(password_manager::prefs::kCredentialsEnableService,
584 settings->autofillPasswordForm());
589 void WebContentsDelegateEfl::DidFirstVisuallyNonEmptyPaint() {
590 did_first_visually_non_empty_paint_ = true;
591 web_view_->SmartCallback<EWebViewCallbacks::LoadNonEmptyLayoutFinished>()
595 void WebContentsDelegateEfl::DidStartLoading() {
596 did_render_frame_ = false;
599 bool WebContentsDelegateEfl::DidAddMessageToConsole(
601 blink::mojom::ConsoleMessageLevel level,
602 const std::u16string& message,
604 const std::u16string& source_id) {
605 std::unique_ptr<_Ewk_Console_Message> console_message(
606 new _Ewk_Console_Message(static_cast<unsigned>(level),
607 base::UTF16ToUTF8(message).c_str(), line_no,
608 base::UTF16ToUTF8(source_id).c_str()));
609 web_view_->SmartCallback<EWebViewCallbacks::ConsoleMessage>().call(
610 console_message.get());
614 void WebContentsDelegateEfl::RunFileChooser(
615 RenderFrameHost* render_frame_host,
616 scoped_refptr<FileSelectListener> listener,
617 const blink::mojom::FileChooserParams& params) {
618 web_view_->ShowFileChooser(render_frame_host, params);
621 std::unique_ptr<ColorChooser> WebContentsDelegateEfl::OpenColorChooser(
622 WebContents* web_contents,
624 const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) {
625 web_view_->RequestColorPicker(SkColorGetR(color), SkColorGetG(color),
626 SkColorGetB(color), SkColorGetA(color));
627 return std::make_unique<ColorChooserEfl>(*web_contents);
630 bool WebContentsDelegateEfl::PreHandleGestureEvent(
632 const blink::WebGestureEvent& event) {
633 blink::WebInputEvent::Type event_type = event.GetType();
634 switch (event_type) {
635 case blink::WebInputEvent::Type::kGestureDoubleTap:
639 case blink::WebInputEvent::Type::kGesturePinchBegin:
640 case blink::WebInputEvent::Type::kGesturePinchUpdate:
641 case blink::WebInputEvent::Type::kGesturePinchEnd:
642 if (!IsPinchToZoomEnabled() ||
643 IsFullscreenForTabOrPending(&web_contents()))
652 void WebContentsDelegateEfl::BackgroundColorReceived(int callback_id,
654 web_view_->OnGetBackgroundColor(callback_id, bg_color);
657 void WebContentsDelegateEfl::RequestManifestInfo(
658 Ewk_View_Request_Manifest_Callback callback,
660 WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
661 ManifestManagerHost* manifest_manager_host =
662 ManifestManagerHost::GetOrCreateForPage(wci->GetPrimaryPage());
663 if (!manifest_manager_host) {
664 web_view_->DidRespondRequestManifest(nullptr, callback, user_data);
668 manifest_manager_host->GetManifest(
669 base::BindOnce(&WebContentsDelegateEfl::OnDidGetManifest,
670 base::Unretained(this), callback, user_data));
673 void WebContentsDelegateEfl::OnDidGetManifest(
674 Ewk_View_Request_Manifest_Callback callback,
676 const GURL& manifest_url,
677 blink::mojom::ManifestPtr manifest) {
678 if (blink::IsEmptyManifest(*manifest)) {
679 web_view_->DidRespondRequestManifest(nullptr, callback, user_data);
681 _Ewk_View_Request_Manifest ewk_manifest(std::move(manifest));
682 web_view_->DidRespondRequestManifest(&ewk_manifest, callback, user_data);
686 #if BUILDFLAG(IS_TIZEN_TV)
687 void WebContentsDelegateEfl::UpdateTargetURL(WebContents* /*source*/,
689 std::string absolute_link_url(url.spec());
690 if (absolute_link_url == last_hovered_url_)
693 if (absolute_link_url.empty()) {
694 // If length is 0, it is not link. send "hover,out,link" callback with the
695 // original hovered URL.
696 web_view_->SmartCallback<EWebViewCallbacks::HoverOutLink>().call(
697 last_hovered_url_.c_str());
699 web_view_->SmartCallback<EWebViewCallbacks::HoverOverLink>().call(
700 absolute_link_url.c_str());
703 // Update latest hovered url.
704 last_hovered_url_ = absolute_link_url;
708 void WebContentsDelegateEfl::OnGetMainFrameScrollbarVisible(int callback_id,
710 web_view_->InvokeMainFrameScrollbarVisibleCallback(callback_id, visible);
712 } // namespace content