• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2020 The Chromium Embedded Framework Authors. 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 "libcef/browser/browser_contents_delegate.h"
6 
7 #include "libcef/browser/browser_host_base.h"
8 #include "libcef/browser/browser_platform_delegate.h"
9 #include "libcef/browser/browser_util.h"
10 
11 #include "content/public/browser/keyboard_event_processing_result.h"
12 #include "content/public/browser/native_web_keyboard_event.h"
13 #include "content/public/browser/navigation_entry.h"
14 #include "content/public/browser/navigation_handle.h"
15 #include "content/public/browser/notification_details.h"
16 #include "content/public/browser/notification_source.h"
17 #include "content/public/browser/notification_types.h"
18 #include "content/public/browser/render_view_host.h"
19 #include "third_party/blink/public/mojom/favicon/favicon_url.mojom.h"
20 
21 using content::KeyboardEventProcessingResult;
22 
CefBrowserContentsDelegate(scoped_refptr<CefBrowserInfo> browser_info)23 CefBrowserContentsDelegate::CefBrowserContentsDelegate(
24     scoped_refptr<CefBrowserInfo> browser_info)
25     : browser_info_(browser_info) {
26   DCHECK(browser_info_->browser());
27 }
28 
ObserveWebContents(content::WebContents * new_contents)29 void CefBrowserContentsDelegate::ObserveWebContents(
30     content::WebContents* new_contents) {
31   WebContentsObserver::Observe(new_contents);
32 
33   if (new_contents) {
34     registrar_.reset(new content::NotificationRegistrar);
35 
36     // When navigating through the history, the restored NavigationEntry's title
37     // will be used. If the entry ends up having the same title after we return
38     // to it, as will usually be the case, the
39     // NOTIFICATION_WEB_CONTENTS_TITLE_UPDATED will then be suppressed, since
40     // the NavigationEntry's title hasn't changed.
41     registrar_->Add(this, content::NOTIFICATION_LOAD_STOP,
42                     content::Source<content::NavigationController>(
43                         &new_contents->GetController()));
44 
45     // Make sure RenderFrameCreated is called at least one time.
46     // Create the frame representation before OnAfterCreated is called for a new
47     // browser. Additionally, RenderFrameCreated is otherwise not called at all
48     // for new popup browsers.
49     RenderFrameCreated(new_contents->GetMainFrame());
50   } else {
51     registrar_.reset();
52   }
53 }
54 
AddObserver(Observer * observer)55 void CefBrowserContentsDelegate::AddObserver(Observer* observer) {
56   observers_.AddObserver(observer);
57 }
58 
RemoveObserver(Observer * observer)59 void CefBrowserContentsDelegate::RemoveObserver(Observer* observer) {
60   observers_.RemoveObserver(observer);
61 }
62 
63 // |source| may be NULL for navigations in the current tab, or if the
64 // navigation originates from a guest view via MaybeAllowNavigation.
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)65 content::WebContents* CefBrowserContentsDelegate::OpenURLFromTab(
66     content::WebContents* source,
67     const content::OpenURLParams& params) {
68   bool cancel = false;
69 
70   if (auto c = client()) {
71     if (auto handler = c->GetRequestHandler()) {
72       // May return nullptr for omnibox navigations.
73       auto frame = browser()->GetFrame(params.frame_tree_node_id);
74       if (!frame)
75         frame = browser()->GetMainFrame();
76       cancel = handler->OnOpenURLFromTab(
77           browser(), frame, params.url.spec(),
78           static_cast<cef_window_open_disposition_t>(params.disposition),
79           params.user_gesture);
80     }
81   }
82 
83   // Returning nullptr will cancel the navigation.
84   return cancel ? nullptr : web_contents();
85 }
86 
LoadingStateChanged(content::WebContents * source,bool to_different_document)87 void CefBrowserContentsDelegate::LoadingStateChanged(
88     content::WebContents* source,
89     bool to_different_document) {
90   const int current_index =
91       source->GetController().GetLastCommittedEntryIndex();
92   const int max_index = source->GetController().GetEntryCount() - 1;
93 
94   const bool is_loading = source->IsLoading();
95   const bool can_go_back = (current_index > 0);
96   const bool can_go_forward = (current_index < max_index);
97 
98   // This method may be called multiple times in a row with |is_loading|
99   // true as a result of https://crrev.com/5e750ad0. Ignore the 2nd+ times.
100   if (is_loading_ == is_loading && can_go_back_ == can_go_back &&
101       can_go_forward_ == can_go_forward) {
102     return;
103   }
104 
105   is_loading_ = is_loading;
106   can_go_back_ = can_go_back;
107   can_go_forward_ = can_go_forward;
108   OnStateChanged(State::kNavigation);
109 
110   if (auto c = client()) {
111     if (auto handler = c->GetLoadHandler()) {
112       auto navigation_lock = browser_info_->CreateNavigationLock();
113       handler->OnLoadingStateChange(browser(), is_loading, can_go_back,
114                                     can_go_forward);
115     }
116   }
117 }
118 
UpdateTargetURL(content::WebContents * source,const GURL & url)119 void CefBrowserContentsDelegate::UpdateTargetURL(content::WebContents* source,
120                                                  const GURL& url) {
121   if (auto c = client()) {
122     if (auto handler = c->GetDisplayHandler()) {
123       handler->OnStatusMessage(browser(), url.spec());
124     }
125   }
126 }
127 
DidAddMessageToConsole(content::WebContents * source,blink::mojom::ConsoleMessageLevel log_level,const std::u16string & message,int32_t line_no,const std::u16string & source_id)128 bool CefBrowserContentsDelegate::DidAddMessageToConsole(
129     content::WebContents* source,
130     blink::mojom::ConsoleMessageLevel log_level,
131     const std::u16string& message,
132     int32_t line_no,
133     const std::u16string& source_id) {
134   if (auto c = client()) {
135     if (auto handler = c->GetDisplayHandler()) {
136       // Use LOGSEVERITY_DEBUG for unrecognized |level| values.
137       cef_log_severity_t cef_level = LOGSEVERITY_DEBUG;
138       switch (log_level) {
139         case blink::mojom::ConsoleMessageLevel::kVerbose:
140           cef_level = LOGSEVERITY_DEBUG;
141           break;
142         case blink::mojom::ConsoleMessageLevel::kInfo:
143           cef_level = LOGSEVERITY_INFO;
144           break;
145         case blink::mojom::ConsoleMessageLevel::kWarning:
146           cef_level = LOGSEVERITY_WARNING;
147           break;
148         case blink::mojom::ConsoleMessageLevel::kError:
149           cef_level = LOGSEVERITY_ERROR;
150           break;
151       }
152 
153       return handler->OnConsoleMessage(browser(), cef_level, message, source_id,
154                                        line_no);
155     }
156   }
157 
158   return false;
159 }
160 
DidNavigateMainFramePostCommit(content::WebContents * web_contents)161 void CefBrowserContentsDelegate::DidNavigateMainFramePostCommit(
162     content::WebContents* web_contents) {
163   has_document_ = false;
164   OnStateChanged(State::kDocument);
165 }
166 
EnterFullscreenModeForTab(content::RenderFrameHost * requesting_frame,const blink::mojom::FullscreenOptions & options)167 void CefBrowserContentsDelegate::EnterFullscreenModeForTab(
168     content::RenderFrameHost* requesting_frame,
169     const blink::mojom::FullscreenOptions& options) {
170   OnFullscreenModeChange(/*fullscreen=*/true);
171 }
172 
ExitFullscreenModeForTab(content::WebContents * web_contents)173 void CefBrowserContentsDelegate::ExitFullscreenModeForTab(
174     content::WebContents* web_contents) {
175   OnFullscreenModeChange(/*fullscreen=*/false);
176 }
177 
178 KeyboardEventProcessingResult
PreHandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)179 CefBrowserContentsDelegate::PreHandleKeyboardEvent(
180     content::WebContents* source,
181     const content::NativeWebKeyboardEvent& event) {
182   if (auto delegate = platform_delegate()) {
183     if (auto c = client()) {
184       if (auto handler = c->GetKeyboardHandler()) {
185         CefKeyEvent cef_event;
186         if (browser_util::GetCefKeyEvent(event, cef_event)) {
187           cef_event.focus_on_editable_field = focus_on_editable_field_;
188 
189           auto event_handle = delegate->GetEventHandle(event);
190           bool is_keyboard_shortcut = false;
191           bool result = handler->OnPreKeyEvent(
192               browser(), cef_event, event_handle, &is_keyboard_shortcut);
193           if (result) {
194             return KeyboardEventProcessingResult::HANDLED;
195           } else if (is_keyboard_shortcut) {
196             return KeyboardEventProcessingResult::NOT_HANDLED_IS_SHORTCUT;
197           }
198         }
199       }
200     }
201   }
202 
203   return KeyboardEventProcessingResult::NOT_HANDLED;
204 }
205 
HandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)206 bool CefBrowserContentsDelegate::HandleKeyboardEvent(
207     content::WebContents* source,
208     const content::NativeWebKeyboardEvent& event) {
209   // Check to see if event should be ignored.
210   if (event.skip_in_browser)
211     return false;
212 
213   if (auto delegate = platform_delegate()) {
214     if (auto c = client()) {
215       if (auto handler = c->GetKeyboardHandler()) {
216         CefKeyEvent cef_event;
217         if (browser_util::GetCefKeyEvent(event, cef_event)) {
218           cef_event.focus_on_editable_field = focus_on_editable_field_;
219 
220           auto event_handle = delegate->GetEventHandle(event);
221           if (handler->OnKeyEvent(browser(), cef_event, event_handle)) {
222             return true;
223           }
224         }
225       }
226     }
227   }
228 
229   return false;
230 }
231 
RenderFrameCreated(content::RenderFrameHost * render_frame_host)232 void CefBrowserContentsDelegate::RenderFrameCreated(
233     content::RenderFrameHost* render_frame_host) {
234   browser_info_->MaybeCreateFrame(render_frame_host, false /* is_guest_view */);
235 
236   if (render_frame_host->GetParent() == nullptr) {
237     // May be already registered if the renderer crashed previously.
238     auto render_view_host = render_frame_host->GetRenderViewHost();
239     if (!registrar_->IsRegistered(
240             this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
241             content::Source<content::RenderViewHost>(render_view_host))) {
242       registrar_->Add(
243           this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
244           content::Source<content::RenderViewHost>(render_view_host));
245     }
246   }
247 }
248 
RenderFrameHostChanged(content::RenderFrameHost * old_host,content::RenderFrameHost * new_host)249 void CefBrowserContentsDelegate::RenderFrameHostChanged(
250     content::RenderFrameHost* old_host,
251     content::RenderFrameHost* new_host) {
252   // Just in case RenderFrameCreated wasn't called for some reason.
253   RenderFrameCreated(new_host);
254 }
255 
RenderFrameDeleted(content::RenderFrameHost * render_frame_host)256 void CefBrowserContentsDelegate::RenderFrameDeleted(
257     content::RenderFrameHost* render_frame_host) {
258   const auto frame_id = CefFrameHostImpl::MakeFrameId(render_frame_host);
259   browser_info_->RemoveFrame(render_frame_host);
260 
261   if (focused_frame_ && focused_frame_->GetIdentifier() == frame_id) {
262     focused_frame_ = nullptr;
263     OnStateChanged(State::kFocusedFrame);
264   }
265 }
266 
RenderViewDeleted(content::RenderViewHost * render_view_host)267 void CefBrowserContentsDelegate::RenderViewDeleted(
268     content::RenderViewHost* render_view_host) {
269   if (registrar_->IsRegistered(
270           this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
271           content::Source<content::RenderViewHost>(render_view_host))) {
272     registrar_->Remove(
273         this, content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE,
274         content::Source<content::RenderViewHost>(render_view_host));
275   }
276 }
277 
RenderViewReady()278 void CefBrowserContentsDelegate::RenderViewReady() {
279   if (auto c = client()) {
280     if (auto handler = c->GetRequestHandler()) {
281       handler->OnRenderViewReady(browser());
282     }
283   }
284 }
285 
RenderProcessGone(base::TerminationStatus status)286 void CefBrowserContentsDelegate::RenderProcessGone(
287     base::TerminationStatus status) {
288   cef_termination_status_t ts = TS_ABNORMAL_TERMINATION;
289   if (status == base::TERMINATION_STATUS_PROCESS_WAS_KILLED)
290     ts = TS_PROCESS_WAS_KILLED;
291   else if (status == base::TERMINATION_STATUS_PROCESS_CRASHED)
292     ts = TS_PROCESS_CRASHED;
293   else if (status == base::TERMINATION_STATUS_OOM)
294     ts = TS_PROCESS_OOM;
295   else if (status != base::TERMINATION_STATUS_ABNORMAL_TERMINATION)
296     return;
297 
298   if (auto c = client()) {
299     if (auto handler = c->GetRequestHandler()) {
300       auto navigation_lock = browser_info_->CreateNavigationLock();
301       handler->OnRenderProcessTerminated(browser(), ts);
302     }
303   }
304 }
305 
OnFrameFocused(content::RenderFrameHost * render_frame_host)306 void CefBrowserContentsDelegate::OnFrameFocused(
307     content::RenderFrameHost* render_frame_host) {
308   CefRefPtr<CefFrameHostImpl> frame = static_cast<CefFrameHostImpl*>(
309       browser_info_->GetFrameForHost(render_frame_host).get());
310   if (!frame || frame->IsFocused())
311     return;
312 
313   CefRefPtr<CefFrameHostImpl> previous_frame = focused_frame_;
314   if (frame->IsMain())
315     focused_frame_ = nullptr;
316   else
317     focused_frame_ = frame;
318 
319   if (!previous_frame) {
320     // The main frame is focused by default.
321     previous_frame = browser_info_->GetMainFrame();
322   }
323 
324   if (previous_frame->GetIdentifier() != frame->GetIdentifier()) {
325     previous_frame->SetFocused(false);
326     frame->SetFocused(true);
327   }
328 
329   OnStateChanged(State::kFocusedFrame);
330 }
331 
DocumentAvailableInMainFrame(content::RenderFrameHost * render_frame_host)332 void CefBrowserContentsDelegate::DocumentAvailableInMainFrame(
333     content::RenderFrameHost* render_frame_host) {
334   has_document_ = true;
335   OnStateChanged(State::kDocument);
336 
337   if (auto c = client()) {
338     if (auto handler = c->GetRequestHandler()) {
339       handler->OnDocumentAvailableInMainFrame(browser());
340     }
341   }
342 }
343 
LoadProgressChanged(double progress)344 void CefBrowserContentsDelegate::LoadProgressChanged(double progress) {
345   if (auto c = client()) {
346     if (auto handler = c->GetDisplayHandler()) {
347       handler->OnLoadingProgressChange(browser(), progress);
348     }
349   }
350 }
351 
DidStopLoading()352 void CefBrowserContentsDelegate::DidStopLoading() {
353   // Notify all renderers that loading has stopped. We used to use
354   // RenderFrameObserver::DidStopLoading in the renderer process but that was
355   // removed in https://crrev.com/3e37dd0ead. However, that callback wasn't
356   // necessarily accurate because it wasn't called in all of the cases where
357   // RenderFrameImpl sends the FrameHostMsg_DidStopLoading message. This adds
358   // an additional round trip but should provide the same or improved
359   // functionality.
360   for (const auto& frame : browser_info_->GetAllFrames()) {
361     frame->MaybeSendDidStopLoading();
362   }
363 }
364 
DidFinishNavigation(content::NavigationHandle * navigation_handle)365 void CefBrowserContentsDelegate::DidFinishNavigation(
366     content::NavigationHandle* navigation_handle) {
367   const net::Error error_code = navigation_handle->GetNetErrorCode();
368 
369   // Skip calls where the navigation has not yet committed and there is no
370   // error code. For example, when creating a browser without loading a URL.
371   if (!navigation_handle->HasCommitted() && error_code == net::OK)
372     return;
373 
374   const bool is_main_frame = navigation_handle->IsInMainFrame();
375   const GURL& url =
376       (error_code == net::OK ? navigation_handle->GetURL() : GURL());
377 
378   auto browser_info = browser_info_;
379 
380   // May return NULL when starting a new navigation if the previous navigation
381   // caused the renderer process to crash during load.
382   CefRefPtr<CefFrameHostImpl> frame = browser_info->GetFrameForFrameTreeNode(
383       navigation_handle->GetFrameTreeNodeId());
384   if (!frame) {
385     if (is_main_frame) {
386       frame = browser_info->GetMainFrame();
387     } else {
388       frame =
389           browser_info->CreateTempSubFrame(CefFrameHostImpl::kInvalidFrameId);
390     }
391   }
392   frame->RefreshAttributes();
393 
394   if (error_code == net::OK) {
395     // The navigation has been committed and there is no error.
396     DCHECK(navigation_handle->HasCommitted());
397 
398     // Don't call OnLoadStart for same page navigations (fragments,
399     // history state).
400     if (!navigation_handle->IsSameDocument()) {
401       OnLoadStart(frame.get(), navigation_handle->GetPageTransition());
402     }
403 
404     if (is_main_frame) {
405       OnAddressChange(url);
406     }
407   } else {
408     // The navigation failed with an error. This may happen before commit
409     // (e.g. network error) or after commit (e.g. response filter error).
410     // If the error happened before commit then this call will originate from
411     // RenderFrameHostImpl::OnDidFailProvisionalLoadWithError.
412     // OnLoadStart/OnLoadEnd will not be called.
413     OnLoadError(frame.get(), navigation_handle->GetURL(), error_code);
414   }
415 }
416 
DidFailLoad(content::RenderFrameHost * render_frame_host,const GURL & validated_url,int error_code)417 void CefBrowserContentsDelegate::DidFailLoad(
418     content::RenderFrameHost* render_frame_host,
419     const GURL& validated_url,
420     int error_code) {
421   // The navigation failed after commit. OnLoadStart was called so we also
422   // call OnLoadEnd.
423   auto frame = browser_info_->GetFrameForHost(render_frame_host);
424   frame->RefreshAttributes();
425   OnLoadError(frame, validated_url, error_code);
426   OnLoadEnd(frame, validated_url, error_code);
427 }
428 
OnMessageReceived(const IPC::Message & message,content::RenderFrameHost * render_frame_host)429 bool CefBrowserContentsDelegate::OnMessageReceived(
430     const IPC::Message& message,
431     content::RenderFrameHost* render_frame_host) {
432   // Messages may arrive after a frame is detached. Ignore those messages.
433   auto frame = browser_info_->GetFrameForHost(render_frame_host);
434   if (frame) {
435     return static_cast<CefFrameHostImpl*>(frame.get())
436         ->OnMessageReceived(message);
437   }
438   return false;
439 }
440 
TitleWasSet(content::NavigationEntry * entry)441 void CefBrowserContentsDelegate::TitleWasSet(content::NavigationEntry* entry) {
442   // |entry| may be NULL if a popup is created via window.open and never
443   // navigated.
444   if (entry)
445     OnTitleChange(entry->GetTitle());
446   else if (web_contents())
447     OnTitleChange(web_contents()->GetTitle());
448 }
449 
PluginCrashed(const base::FilePath & plugin_path,base::ProcessId plugin_pid)450 void CefBrowserContentsDelegate::PluginCrashed(
451     const base::FilePath& plugin_path,
452     base::ProcessId plugin_pid) {
453   if (auto c = client()) {
454     if (auto handler = c->GetRequestHandler()) {
455       handler->OnPluginCrashed(browser(), plugin_path.value());
456     }
457   }
458 }
459 
DidUpdateFaviconURL(content::RenderFrameHost * render_frame_host,const std::vector<blink::mojom::FaviconURLPtr> & candidates)460 void CefBrowserContentsDelegate::DidUpdateFaviconURL(
461     content::RenderFrameHost* render_frame_host,
462     const std::vector<blink::mojom::FaviconURLPtr>& candidates) {
463   if (auto c = client()) {
464     if (auto handler = c->GetDisplayHandler()) {
465       std::vector<CefString> icon_urls;
466       for (const auto& icon : candidates) {
467         if (icon->icon_type == blink::mojom::FaviconIconType::kFavicon) {
468           icon_urls.push_back(icon->icon_url.spec());
469         }
470       }
471       if (!icon_urls.empty()) {
472         handler->OnFaviconURLChange(browser(), icon_urls);
473       }
474     }
475   }
476 }
477 
OnWebContentsFocused(content::RenderWidgetHost * render_widget_host)478 void CefBrowserContentsDelegate::OnWebContentsFocused(
479     content::RenderWidgetHost* render_widget_host) {
480   if (auto c = client()) {
481     if (auto handler = c->GetFocusHandler()) {
482       handler->OnGotFocus(browser());
483     }
484   }
485 }
486 
WebContentsDestroyed()487 void CefBrowserContentsDelegate::WebContentsDestroyed() {
488   auto wc = web_contents();
489   ObserveWebContents(nullptr);
490   for (auto& observer : observers_) {
491     observer.OnWebContentsDestroyed(wc);
492   }
493 }
494 
Observe(int type,const content::NotificationSource & source,const content::NotificationDetails & details)495 void CefBrowserContentsDelegate::Observe(
496     int type,
497     const content::NotificationSource& source,
498     const content::NotificationDetails& details) {
499   DCHECK(type == content::NOTIFICATION_LOAD_STOP ||
500          type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE);
501 
502   if (type == content::NOTIFICATION_LOAD_STOP) {
503     content::NavigationController* controller =
504         content::Source<content::NavigationController>(source).ptr();
505     OnTitleChange(controller->GetWebContents()->GetTitle());
506   } else if (type == content::NOTIFICATION_FOCUS_CHANGED_IN_PAGE) {
507     focus_on_editable_field_ = *content::Details<bool>(details).ptr();
508   }
509 }
510 
OnLoadEnd(CefRefPtr<CefFrame> frame,const GURL & url,int http_status_code)511 void CefBrowserContentsDelegate::OnLoadEnd(CefRefPtr<CefFrame> frame,
512                                            const GURL& url,
513                                            int http_status_code) {
514   if (auto c = client()) {
515     if (auto handler = c->GetLoadHandler()) {
516       auto navigation_lock = browser_info_->CreateNavigationLock();
517       handler->OnLoadEnd(browser(), frame, http_status_code);
518     }
519   }
520 }
521 
OnSetFocus(cef_focus_source_t source)522 bool CefBrowserContentsDelegate::OnSetFocus(cef_focus_source_t source) {
523   // SetFocus() might be called while inside the OnSetFocus() callback. If
524   // so, don't re-enter the callback.
525   if (is_in_onsetfocus_)
526     return true;
527 
528   if (auto c = client()) {
529     if (auto handler = c->GetFocusHandler()) {
530       is_in_onsetfocus_ = true;
531       bool handled = handler->OnSetFocus(browser(), source);
532       is_in_onsetfocus_ = false;
533 
534       return handled;
535     }
536   }
537 
538   return false;
539 }
540 
client() const541 CefRefPtr<CefClient> CefBrowserContentsDelegate::client() const {
542   if (auto b = browser()) {
543     return b->GetHost()->GetClient();
544   }
545   return nullptr;
546 }
547 
browser() const548 CefRefPtr<CefBrowser> CefBrowserContentsDelegate::browser() const {
549   return browser_info_->browser();
550 }
551 
platform_delegate() const552 CefBrowserPlatformDelegate* CefBrowserContentsDelegate::platform_delegate()
553     const {
554   auto browser = browser_info_->browser();
555   if (browser)
556     return browser->platform_delegate();
557   return nullptr;
558 }
559 
OnAddressChange(const GURL & url)560 void CefBrowserContentsDelegate::OnAddressChange(const GURL& url) {
561   if (auto c = client()) {
562     if (auto handler = c->GetDisplayHandler()) {
563       // On the handler of an address change.
564       handler->OnAddressChange(browser(), browser_info_->GetMainFrame(),
565                                url.spec());
566     }
567   }
568 }
569 
OnLoadStart(CefRefPtr<CefFrame> frame,ui::PageTransition transition_type)570 void CefBrowserContentsDelegate::OnLoadStart(
571     CefRefPtr<CefFrame> frame,
572     ui::PageTransition transition_type) {
573   if (auto c = client()) {
574     if (auto handler = c->GetLoadHandler()) {
575       auto navigation_lock = browser_info_->CreateNavigationLock();
576       // On the handler that loading has started.
577       handler->OnLoadStart(browser(), frame,
578                            static_cast<cef_transition_type_t>(transition_type));
579     }
580   }
581 }
582 
OnLoadError(CefRefPtr<CefFrame> frame,const GURL & url,int error_code)583 void CefBrowserContentsDelegate::OnLoadError(CefRefPtr<CefFrame> frame,
584                                              const GURL& url,
585                                              int error_code) {
586   if (auto c = client()) {
587     if (auto handler = c->GetLoadHandler()) {
588       auto navigation_lock = browser_info_->CreateNavigationLock();
589       // On the handler that loading has failed.
590       handler->OnLoadError(browser(), frame,
591                            static_cast<cef_errorcode_t>(error_code),
592                            net::ErrorToShortString(error_code), url.spec());
593     }
594   }
595 }
596 
OnTitleChange(const std::u16string & title)597 void CefBrowserContentsDelegate::OnTitleChange(const std::u16string& title) {
598   if (auto c = client()) {
599     if (auto handler = c->GetDisplayHandler()) {
600       handler->OnTitleChange(browser(), title);
601     }
602   }
603 }
604 
OnFullscreenModeChange(bool fullscreen)605 void CefBrowserContentsDelegate::OnFullscreenModeChange(bool fullscreen) {
606   if (fullscreen == is_fullscreen_)
607     return;
608 
609   is_fullscreen_ = fullscreen;
610   OnStateChanged(State::kFullscreen);
611 
612   if (auto c = client()) {
613     if (auto handler = c->GetDisplayHandler()) {
614       handler->OnFullscreenModeChange(browser(), fullscreen);
615     }
616   }
617 }
618 
OnStateChanged(State state_changed)619 void CefBrowserContentsDelegate::OnStateChanged(State state_changed) {
620   for (auto& observer : observers_) {
621     observer.OnStateChanged(state_changed);
622   }
623 }
624