[M108 Migration][API] Bring up autofill
[platform/framework/web/chromium-efl.git] / tizen_src / ewk / efl_integration / web_contents_delegate_efl.cc
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.
4
5 #include "web_contents_delegate_efl.h"
6
7 #include "base/strings/utf_string_conversions.h"
8 #include "base/trace_event/ttrace.h"
9 #include "browser/input_picker/color_chooser_efl.h"
10 #include "browser/javascript_dialog_manager_efl.h"
11 #include "browser/policy_response_delegate_efl.h"
12 #include "browser_context_efl.h"
13 #include "common/render_messages_ewk.h"
14 #include "components/password_manager/core/common/password_manager_pref_names.h"
15 #include "components/prefs/pref_service.h"
16 #include "content/browser/manifest/manifest_manager_host.h"
17 #include "content/browser/web_contents/web_contents_impl.h"
18 #include "content/common/content_switches_internal.h"
19 #include "content/common/render_messages_efl.h"
20 #include "content/public/browser/browser_thread.h"
21 #include "content/public/browser/file_select_listener.h"
22 #include "content/public/browser/invalidate_type.h"
23 #include "content/public/browser/navigation_entry.h"
24 #include "content/public/browser/navigation_handle.h"
25 #include "content/public/browser/render_view_host.h"
26 #include "eweb_view.h"
27 #include "eweb_view_callbacks.h"
28 #include "net/base/load_states.h"
29 #include "net/http/http_response_headers.h"
30 #include "private/ewk_certificate_private.h"
31 #include "private/ewk_console_message_private.h"
32 #include "private/ewk_custom_handlers_private.h"
33 #include "private/ewk_error_private.h"
34 #include "private/ewk_policy_decision_private.h"
35 #include "private/ewk_user_media_private.h"
36 #include "private/webview_delegate_ewk.h"
37 #include "third_party/blink/public/common/input/web_input_event.h"
38 #include "third_party/blink/public/common/manifest/manifest_util.h"
39 #include "url/gurl.h"
40 #include "web_contents_observer_efl.h"
41
42 #if BUILDFLAG(IS_TIZEN)
43 #include <app_control.h>
44 #include <app_manager.h>
45 #endif
46
47 #if defined(TIZEN_MULTIMEDIA_SUPPORT)
48 #include "content/public/browser/media_capture_devices.h"
49 #include "media/capture/video/tizen/video_capture_device_tizen.h"
50 #endif
51
52 #if defined(TIZEN_AUTOFILL_SUPPORT)
53 #include "base/command_line.h"
54 #include "browser/autofill/autofill_client_efl.h"
55 #include "browser/password_manager/password_manager_client_efl.h"
56 #include "components/autofill/content/browser/content_autofill_driver_factory.h"
57 #include "components/autofill/core/browser/autofill_client.h"
58 #include "components/autofill/core/browser/autofill_manager.h"
59 #include "components/autofill/core/common/autofill_switches.h"
60 #include "components/web_modal/web_contents_modal_dialog_manager.h"
61
62 using autofill::AutofillManager;
63 using autofill::AutofillClientEfl;
64 using autofill::ContentAutofillDriverFactory;
65 using password_manager::PasswordManagerClientEfl;
66 #endif
67
68 using std::u16string;
69 using namespace ui;
70
71 namespace content {
72
73 #if BUILDFLAG(IS_TIZEN) && defined(TIZEN_MULTIMEDIA_SUPPORT)
74 static const MediaStreamDevice* GetRequestedVideoDevice(
75     const std::string& device_id) {
76   const MediaStreamDevices& video_devices =
77       MediaCaptureDevices::GetInstance()->GetVideoCaptureDevices();
78   if (video_devices.empty())
79     return NULL;
80   if (device_id.length() == 0)
81     return &(*video_devices.begin());
82   return video_devices.FindById(device_id);
83 }
84 #endif
85
86 WebContentsDelegateEfl::WebContentsDelegateEfl(EWebView* view)
87     : web_view_(view),
88       web_contents_(view->web_contents()),
89       contents_observer_(std::make_unique<WebContentsObserverEfl>(view, this)) {
90 #if defined(TIZEN_AUTOFILL_SUPPORT)
91   if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
92           autofill::switches::kDisableAutofill)) {
93     AutofillClientEfl::CreateForWebContents(&web_contents_);
94     AutofillClientEfl* autofill_client =
95         AutofillClientEfl::FromWebContents(&web_contents_);
96     autofill_client->SetEWebView(view);
97     PasswordManagerClientEfl::CreateForWebContentsWithAutofillClient(
98         &web_contents_, autofill_client);
99     ContentAutofillDriverFactory::CreateForWebContentsAndDelegate(
100         &web_contents_, autofill_client,
101         base::BindRepeating(&autofill::BrowserDriverInitHook, autofill_client,
102                             EWebView::GetPlatformLocale()));
103   }
104 #endif
105 }
106
107 WebContentsDelegateEfl::~WebContentsDelegateEfl() {
108   // It's important to delete web_contents_ before dialog_manager_
109   // destructor of web contents uses dialog_manager_
110   if (dialog_manager_)
111     delete dialog_manager_;
112 }
113
114 WebContents* WebContentsDelegateEfl::OpenURLFromTab(
115     WebContents* source,
116     const OpenURLParams& params) {
117   const GURL& url = params.url;
118   WindowOpenDisposition disposition = params.disposition;
119
120   if (!source || (disposition != WindowOpenDisposition::CURRENT_TAB &&
121                   disposition != WindowOpenDisposition::NEW_FOREGROUND_TAB &&
122                   disposition != WindowOpenDisposition::NEW_BACKGROUND_TAB &&
123                   disposition != WindowOpenDisposition::OFF_THE_RECORD)) {
124     NOTIMPLEMENTED();
125     return NULL;
126   }
127
128   if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB ||
129       disposition == WindowOpenDisposition::NEW_BACKGROUND_TAB ||
130       disposition == WindowOpenDisposition::OFF_THE_RECORD) {
131     Evas_Object* new_object = NULL;
132     web_view_->SmartCallback<EWebViewCallbacks::CreateNewWindow>().call(
133         &new_object);
134
135     if (!new_object)
136       return NULL;
137
138     if (disposition == WindowOpenDisposition::NEW_FOREGROUND_TAB)
139       ActivateContents(source);
140
141     EWebView* wv =
142         WebViewDelegateEwk::GetInstance().GetWebViewFromEvasObject(new_object);
143     DCHECK(wv);
144     wv->SetURL(url);
145     return NULL;
146   }
147
148   ui::PageTransition transition(ui::PageTransitionFromInt(params.transition));
149   source->GetController().LoadURL(url, params.referrer, transition,
150                                   std::string());
151   return source;
152 }
153
154 void WebContentsDelegateEfl::NavigationStateChanged(
155     WebContents* source, InvalidateTypes changed_flags) {
156   // We always notfiy clients about title invalidation, even if its text
157   // didn't actually change. This is to maintain EWK API consistency.
158   if (changed_flags & INVALIDATE_TYPE_TITLE) {
159     web_view_->SmartCallback<EWebViewCallbacks::TitleChange>().call(
160         base::UTF16ToUTF8(source->GetTitle()).c_str());
161   }
162
163   // We only notify clients if visible url actually changed, because on some
164   // pages we would get notifications with flag INVALIDATE_TYPE_URL even when
165   // visible url did not change.
166   if ((changed_flags & INVALIDATE_TYPE_URL) &&
167        last_visible_url_ != source->GetVisibleURL()) {
168     last_visible_url_ = source->GetVisibleURL();
169     const char* url = last_visible_url_.spec().c_str();
170
171     web_view_->SmartCallback<EWebViewCallbacks::URLChanged>().call(url);
172     web_view_->SmartCallback<EWebViewCallbacks::URIChanged>().call(url);
173   }
174 }
175
176 void WebContentsDelegateEfl::LoadingStateChanged(WebContents* source,
177                                                  bool to_different_document) {
178   if (source->IsLoading())
179     web_view_->SmartCallback<EWebViewCallbacks::LoadProgressStarted>().call();
180   else
181     web_view_->SmartCallback<EWebViewCallbacks::LoadProgressFinished>().call();
182 }
183
184 void WebContentsDelegateEfl::AddNewContents(
185     WebContents* source,
186     std::unique_ptr<WebContents> new_contents,
187     const GURL& target_url,
188     WindowOpenDisposition disposition,
189     const blink::mojom::WindowFeatures& window_features,
190     bool user_gesture,
191     bool* was_blocked) {
192   // Initialize the delegate for the new contents here using source's delegate
193   // as it will be needed to create a new window with the pre-created
194   // new contents in case the opener is suppressed. Otherwise, the delegate
195   // had already been initialized in EWebView::InitializeContent()
196   if (!new_contents->GetDelegate())
197     new_contents->SetDelegate(this);
198
199   // EWebView takes the ownership of new WebContents in
200   // EWebView::InitializeContent(). So, we can release the ownership of new
201   // WebContents here.
202   std::ignore = new_contents.release();
203 }
204
205 bool WebContentsDelegateEfl::ShouldCreateWebContents(
206     WebContents* web_contents,
207     RenderFrameHost* opener,
208     SiteInstance* source_site_instance,
209     mojom::WindowContainerType window_container_type,
210     const GURL& opener_url,
211     const std::string& frame_name,
212     const GURL& target_url,
213     const std::string& partition_id,
214     SessionStorageNamespace* session_storage_namespace) {
215   // We implement the asynchronous version of the function, this
216   // one should never be invoked.
217   NOTREACHED();
218   return false;
219 }
220
221 void WebContentsDelegateEfl::CloseContents(WebContents* source) {
222   web_view_->SmartCallback<EWebViewCallbacks::WindowClosed>().call();
223 }
224
225 void WebContentsDelegateEfl::EnterFullscreenModeForTab(
226     RenderFrameHost* requesting_frame,
227     const blink::mojom::FullscreenOptions& options) {
228   is_fullscreen_ = true;
229   web_view_->SmartCallback<EWebViewCallbacks::EnterFullscreen>().call();
230 }
231
232 void WebContentsDelegateEfl::ExitFullscreenModeForTab(
233     WebContents* web_contents) {
234   is_fullscreen_ = false;
235   web_view_->SmartCallback<EWebViewCallbacks::ExitFullscreen>().call();
236 }
237
238 bool WebContentsDelegateEfl::IsFullscreenForTabOrPending(
239     const WebContents* web_contents) {
240   return is_fullscreen_;
241 }
242
243 void WebContentsDelegateEfl::RegisterProtocolHandler(
244     RenderFrameHost* host,
245     const std::string& protocol,
246     const GURL& url,
247     bool user_gesture) {
248   Ewk_Custom_Handlers_Data protocol_data(protocol.c_str(), url.host().c_str(),
249                                          url.spec().c_str());
250   web_view_->SmartCallback<EWebViewCallbacks::RegisterProtocolHandler>().call(
251       &protocol_data);
252 }
253
254 void WebContentsDelegateEfl::UnregisterProtocolHandler(
255     RenderFrameHost* host,
256     const std::string& protocol,
257     const GURL& url,
258     bool user_gesture) {
259   Ewk_Custom_Handlers_Data protocol_data(protocol.c_str(), url.host().c_str(),
260                                          url.spec().c_str());
261   web_view_->SmartCallback<EWebViewCallbacks::UnregisterProtocolHandler>().call(
262       &protocol_data);
263 }
264
265 #if defined(TIZEN_MULTIMEDIA_SUPPORT)
266 void WebContentsDelegateEfl::RequestMediaAccessAllow(
267     const MediaStreamRequest& request,
268     MediaResponseCallback callback) {
269   MediaStreamDevices devices;
270
271   if (request.audio_type == MEDIA_DEVICE_AUDIO_CAPTURE) {
272     devices.push_back(
273         MediaStreamDevice(request.audio_type, "default", "Default"));
274   }
275
276   if (request.video_type == MEDIA_DEVICE_VIDEO_CAPTURE) {
277 #if BUILDFLAG(IS_TIZEN)
278     const MediaStreamDevice* video_device =
279         GetRequestedVideoDevice(request.requested_video_device_id);
280     if (video_device) {
281       devices.push_back(*video_device);
282     } else {
283       std::move(callback).Run(MediaStreamDevices(), MEDIA_DEVICE_NOT_SUPPORTED,
284                               std::unique_ptr<MediaStreamUI>());
285     }
286 #else
287     devices.push_back(
288         MediaStreamDevice(request.video_type, "/dev/video0", "1"));
289 #endif
290   }
291
292   std::move(callback).Run(devices, MEDIA_DEVICE_OK,
293                           std::unique_ptr<MediaStreamUI>());
294 }
295
296 void WebContentsDelegateEfl::RequestMediaAccessDeny(
297     const MediaStreamRequest& request,
298     MediaResponseCallback callback) {
299   LOG(ERROR) << __FUNCTION__ << " Decline request with empty list";
300   std::move(callback).Run(MediaStreamDevices(), MEDIA_DEVICE_NOT_SUPPORTED,
301                           std::unique_ptr<MediaStreamUI>());
302 }
303
304 bool WebContentsDelegateEfl::CheckMediaAccessPermission(
305     WebContents* web_contents,
306     const GURL& security_origin,
307     MediaStreamType type) {
308   return true;
309 }
310
311 void WebContentsDelegateEfl::RequestMediaAccessPermission(
312     WebContents* web_contents,
313     const MediaStreamRequest& request,
314     MediaResponseCallback callback) {
315   std::unique_ptr<_Ewk_User_Media_Permission_Request> media_permission_request(
316       new _Ewk_User_Media_Permission_Request(this, request,
317                                              std::move(callback)));
318
319   Eina_Bool callback_result = EINA_FALSE;
320   if (!web_view_->InvokeViewUserMediaPermissionCallback(
321           media_permission_request.get(), &callback_result)) {
322     web_view_->SmartCallback<EWebViewCallbacks::UserMediaPermission>().call(
323         media_permission_request.get());
324   }
325
326   // if policy is suspended, the API takes over the policy object lifetime
327   // and policy will be deleted after decision is made
328   if (media_permission_request->IsSuspended())
329     ignore_result(media_permission_request.release());
330   else if (!media_permission_request->IsDecided()) {
331     callback.Run(MediaStreamDevices(), MEDIA_DEVICE_NOT_SUPPORTED,
332                  std::unique_ptr<MediaStreamUI>());
333   }
334 }
335 #endif
336
337 void WebContentsDelegateEfl::OnAuthRequired(const std::string& realm,
338                                             const GURL& url,
339                                             LoginDelegateEfl* login_delegate) {
340   web_view_->InvokeAuthCallback(login_delegate, url, realm);
341 }
342
343 void WebContentsDelegateEfl::RequestCertificateConfirm(
344     WebContents* /*web_contents*/,
345     int cert_error,
346     const net::SSLInfo& ssl_info,
347     const GURL& url,
348     bool /*overridable*/,
349     bool /*strict_enforcement*/,
350     base::OnceCallback<void(CertificateRequestResultType)> callback) {
351 #if !defined(EWK_BRINGUP)  // FIXME: m67 bringup
352   // FIXME: EWK_BRINGUP definition should be removed.
353   DCHECK(result);
354   std::string pem_certificate;
355   if (!net::X509Certificate::GetPEMEncoded(ssl_info.cert->os_cert_handle(),
356                                            &pem_certificate)) {
357     *result = CERTIFICATE_REQUEST_RESULT_TYPE_CANCEL;
358     return;
359   }
360   certificate_policy_decision_.reset(new _Ewk_Certificate_Policy_Decision(
361       url, pem_certificate, cert_error, std::move(callback)));
362 #endif  // !defined(EWK_BRINGUP)
363   web_view_->SmartCallback<EWebViewCallbacks::RequestCertificateConfirm>().call(
364       certificate_policy_decision_.get());
365
366   // if policy is suspended, the API takes over the policy object lifetime
367   // and policy will be deleted after decision is made
368 #if !defined(EWK_BRINGUP)  // FIXME: m108 bringup
369   if (certificate_policy_decision_->isSuspended()) {
370     ignore_result(certificate_policy_decision_.release());
371   } else {
372     certificate_policy_decision_->setDecision(true);
373   }
374 #endif
375 }
376
377 void WebContentsDelegateEfl::ActivateContents(WebContents* contents) {
378 #if BUILDFLAG(IS_TIZEN)
379   app_control_h app_control = nullptr;
380   int ret = app_control_create(&app_control);
381   if (ret != APP_CONTROL_ERROR_NONE) {
382     LOG(ERROR) << "app_control_create is failed with err " << ret;
383     return;
384   }
385
386   std::unique_ptr<std::remove_pointer<app_control_h>::type,
387                   decltype(app_control_destroy)*>
388       auto_release{app_control, app_control_destroy};
389
390   char* app_id = nullptr;
391   ret = app_manager_get_app_id(getpid(), &app_id);
392   if (ret != APP_MANAGER_ERROR_NONE) {
393     LOG(ERROR) << "app_manager_get_app_id is failed with err " << ret;
394     return;
395   }
396
397   ret = app_control_set_app_id(app_control, app_id);
398   if (app_id)
399     free(app_id);
400   if (ret != APP_CONTROL_ERROR_NONE) {
401     LOG(ERROR) << "app_control_set_app_id is failed with err " << ret;
402     return;
403   }
404
405   ret = app_control_send_launch_request(app_control, nullptr, nullptr);
406   if (ret != APP_CONTROL_ERROR_NONE) {
407     LOG(ERROR) << "app_control_send_launch_request is failed with err " << ret;
408     return;
409   }
410 #endif
411 }
412
413 void WebContentsDelegateEfl::SetContentSecurityPolicy(
414     const std::string& policy,
415     Ewk_CSP_Header_Type header_type) {
416   contents_observer_->SetContentSecurityPolicy(policy, header_type);
417 }
418
419 #if defined(TIZEN_AUTOFILL_SUPPORT)
420 void WebContentsDelegateEfl::UpdateAutofillIfRequired() {
421   if (AutofillClientEfl* autofill_client =
422           AutofillClientEfl::FromWebContents(&web_contents_)) {
423     autofill_client->UpdateAutofillIfRequired();
424   }
425 }
426 #endif
427
428 void WebContentsDelegateEfl::OnDidChangeFocusedNodeBounds(
429     const gfx::RectF& focused_node_bounds) {
430 #if defined(TIZEN_AUTOFILL_SUPPORT)
431   if (AutofillClientEfl* autofill_client =
432           AutofillClientEfl::FromWebContents(&web_contents_)) {
433     autofill_client->DidChangeFocusedNodeBounds(focused_node_bounds);
434   }
435 #endif
436 }
437
438 void WebContentsDelegateEfl::FindReply(WebContents* web_contents,
439                                        int request_id,
440                                        int number_of_matches,
441                                        const gfx::Rect& selection_rect,
442                                        int active_match_ordinal,
443                                        bool final_update) {
444   if (final_update && request_id == web_view_->current_find_request_id()) {
445     unsigned int uint_number_of_matches =
446         static_cast<unsigned int>(number_of_matches);
447     web_view_->SmartCallback<EWebViewCallbacks::TextFound>().call(
448         &uint_number_of_matches);
449   }
450 }
451
452 void WebContentsDelegateEfl::DidRenderFrame() {
453   // Call FrameRendered callback when loading and first layout is finished.
454   if (!did_render_frame_ && did_first_visually_non_empty_paint_ &&
455       (web_view_->GetProgressValue() > 0.1)) {
456     did_first_visually_non_empty_paint_ = false;
457     did_render_frame_ = true;
458     TTRACE_WEB("WebContentsDelegateEfl::DidRenderFrame");
459     LOG(INFO) << "WebContentsDelegateEfl::DidRenderFrame";
460
461     // "frame,rendered" message is triggered as soon as rendering of a frame
462     // is completed.
463     web_view_->SmartCallback<EWebViewCallbacks::FrameRendered>().call(0);
464   }
465 }
466
467 JavaScriptDialogManager* WebContentsDelegateEfl::GetJavaScriptDialogManager(
468     WebContents* source) {
469   if (!dialog_manager_)
470     dialog_manager_ = new JavaScriptDialogManagerEfl();
471   return dialog_manager_;
472 }
473
474 void WebContentsDelegateEfl::OnUpdateSettings(const Ewk_Settings* settings) {
475 #if defined(TIZEN_AUTOFILL_SUPPORT)
476   PasswordManagerClientEfl* client =
477       PasswordManagerClientEfl::FromWebContents(&web_contents_);
478   if (client) {
479     PrefService* prefs = client->GetPrefs();
480     prefs->SetBoolean(password_manager::prefs::kCredentialsEnableService,
481                       settings->autofillPasswordForm());
482   }
483 #endif
484 }
485
486 void WebContentsDelegateEfl::DidFirstVisuallyNonEmptyPaint() {
487   did_first_visually_non_empty_paint_ = true;
488   web_view_->SmartCallback<EWebViewCallbacks::LoadNonEmptyLayoutFinished>()
489       .call();
490 }
491
492 void WebContentsDelegateEfl::DidStartLoading() {
493   did_render_frame_ = false;
494 }
495
496 bool WebContentsDelegateEfl::DidAddMessageToConsole(
497     WebContents* source,
498     blink::mojom::ConsoleMessageLevel level,
499     const std::u16string& message,
500     int32_t line_no,
501     const std::u16string& source_id) {
502   std::unique_ptr<_Ewk_Console_Message> console_message(
503       new _Ewk_Console_Message(static_cast<unsigned>(level),
504                                base::UTF16ToUTF8(message).c_str(), line_no,
505                                base::UTF16ToUTF8(source_id).c_str()));
506   web_view_->SmartCallback<EWebViewCallbacks::ConsoleMessage>().call(
507       console_message.get());
508   return true;
509 }
510
511 void WebContentsDelegateEfl::RunFileChooser(
512     RenderFrameHost* render_frame_host,
513     scoped_refptr<FileSelectListener> listener,
514     const blink::mojom::FileChooserParams& params) {
515   web_view_->ShowFileChooser(render_frame_host, params);
516 }
517
518 std::unique_ptr<ColorChooser> WebContentsDelegateEfl::OpenColorChooser(
519     WebContents* web_contents,
520     SkColor color,
521     const std::vector<blink::mojom::ColorSuggestionPtr>& suggestions) {
522   web_view_->RequestColorPicker(SkColorGetR(color), SkColorGetG(color),
523                                 SkColorGetB(color), SkColorGetA(color));
524   return std::make_unique<ColorChooserEfl>(*web_contents);
525 }
526
527 #if !defined(EWK_BRINGUP)  // FIXME: m76 bringup
528 void WebContentsDelegateEfl::OpenDateTimeDialog(
529     ui::TextInputType dialog_type,
530     double dialog_value,
531     double min,
532     double max,
533     double step,
534     const std::vector<DateTimeSuggestion>& suggestions) {
535   web_view_->InputPickerShow(dialog_type, dialog_value);
536 }
537 #endif
538
539 bool WebContentsDelegateEfl::PreHandleGestureEvent(
540     WebContents* source,
541     const blink::WebGestureEvent& event) {
542   blink::WebInputEvent::Type event_type = event.GetType();
543   switch (event_type) {
544     case blink::WebInputEvent::Type::kGestureDoubleTap:
545       if (is_fullscreen_)
546         return true;
547       break;
548     case blink::WebInputEvent::Type::kGesturePinchBegin:
549     case blink::WebInputEvent::Type::kGesturePinchUpdate:
550     case blink::WebInputEvent::Type::kGesturePinchEnd:
551       if (!IsPinchToZoomEnabled() ||
552           IsFullscreenForTabOrPending(&web_contents()))
553         return true;
554       break;
555     default:
556       break;
557   }
558   return false;
559 }
560
561 void WebContentsDelegateEfl::RequestManifestInfo(
562     Ewk_View_Request_Manifest_Callback callback,
563     void* user_data) {
564   WebContentsImpl* wci = static_cast<WebContentsImpl*>(&web_contents_);
565   ManifestManagerHost* manifest_manager_host =
566       ManifestManagerHost::GetOrCreateForPage(wci->GetPrimaryPage());
567   if (!manifest_manager_host) {
568     web_view_->DidRespondRequestManifest(nullptr, callback, user_data);
569     return;
570   }
571
572   manifest_manager_host->GetManifest(
573       base::BindOnce(&WebContentsDelegateEfl::OnDidGetManifest,
574                      base::Unretained(this), callback, user_data));
575 }
576
577 void WebContentsDelegateEfl::OnDidGetManifest(
578     Ewk_View_Request_Manifest_Callback callback,
579     void* user_data,
580     const GURL& manifest_url,
581     blink::mojom::ManifestPtr manifest) {
582   if (blink::IsEmptyManifest(*manifest)) {
583     web_view_->DidRespondRequestManifest(nullptr, callback, user_data);
584   } else {
585     _Ewk_View_Request_Manifest ewk_manifest(std::move(manifest));
586     web_view_->DidRespondRequestManifest(&ewk_manifest, callback, user_data);
587   }
588 }
589
590 }  // namespace content