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