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