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