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 <memory>
6
7 #include "libcef/browser/chrome/chrome_browser_delegate.h"
8
9 #include "libcef/browser/browser_contents_delegate.h"
10 #include "libcef/browser/browser_host_base.h"
11 #include "libcef/browser/browser_info_manager.h"
12 #include "libcef/browser/browser_platform_delegate.h"
13 #include "libcef/browser/chrome/chrome_browser_host_impl.h"
14 #include "libcef/browser/request_context_impl.h"
15 #include "libcef/common/app_manager.h"
16 #include "libcef/common/frame_util.h"
17
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_tabstrip.h"
21 #include "content/public/browser/global_routing_id.h"
22 #include "content/public/browser/keyboard_event_processing_result.h"
23 #include "content/public/browser/native_web_keyboard_event.h"
24
25 using content::KeyboardEventProcessingResult;
26
ChromeBrowserDelegate(Browser * browser,const CefBrowserCreateParams & create_params)27 ChromeBrowserDelegate::ChromeBrowserDelegate(
28 Browser* browser,
29 const CefBrowserCreateParams& create_params)
30 : browser_(browser), create_params_(create_params) {
31 DCHECK(browser_);
32 }
33
34 ChromeBrowserDelegate::~ChromeBrowserDelegate() = default;
35
OnWebContentsCreated(content::WebContents * new_contents)36 void ChromeBrowserDelegate::OnWebContentsCreated(
37 content::WebContents* new_contents) {
38 // Necessary to receive LoadingStateChanged calls during initial navigation.
39 // This will be called again in Browser::SetAsDelegate, which should be fine.
40 new_contents->SetDelegate(browser_);
41
42 SetAsDelegate(new_contents, /*set_delegate=*/true);
43 }
44
SetAsDelegate(content::WebContents * web_contents,bool set_delegate)45 void ChromeBrowserDelegate::SetAsDelegate(content::WebContents* web_contents,
46 bool set_delegate) {
47 DCHECK(web_contents);
48 auto browser_host =
49 ChromeBrowserHostImpl::GetBrowserForContents(web_contents);
50
51 // |set_delegate=false| only makes sense if we already have a browser host.
52 DCHECK(browser_host || set_delegate);
53
54 if (browser_host) {
55 // We already have a browser host, so just change the associated Browser.
56 browser_host->SetBrowser(set_delegate ? browser_ : nullptr);
57 return;
58 }
59
60 auto platform_delegate = CefBrowserPlatformDelegate::Create(create_params_);
61 CHECK(platform_delegate);
62
63 auto browser_info = CefBrowserInfoManager::GetInstance()->CreateBrowserInfo(
64 /*is_popup=*/false, /*is_windowless=*/false, create_params_.extra_info);
65
66 auto request_context_impl =
67 CefRequestContextImpl::GetOrCreateForRequestContext(
68 create_params_.request_context);
69
70 CreateBrowser(web_contents, create_params_.settings, create_params_.client,
71 std::move(platform_delegate), browser_info, /*opener=*/nullptr,
72 request_context_impl);
73 }
74
WebContentsCreated(content::WebContents * source_contents,int opener_render_process_id,int opener_render_frame_id,const std::string & frame_name,const GURL & target_url,content::WebContents * new_contents)75 void ChromeBrowserDelegate::WebContentsCreated(
76 content::WebContents* source_contents,
77 int opener_render_process_id,
78 int opener_render_frame_id,
79 const std::string& frame_name,
80 const GURL& target_url,
81 content::WebContents* new_contents) {
82 CefBrowserSettings settings;
83 CefRefPtr<CefClient> client;
84 std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate;
85 CefRefPtr<CefDictionaryValue> extra_info;
86
87 CefBrowserInfoManager::GetInstance()->WebContentsCreated(
88 target_url,
89 frame_util::MakeGlobalId(opener_render_process_id,
90 opener_render_frame_id),
91 settings, client, platform_delegate, extra_info);
92
93 auto opener = ChromeBrowserHostImpl::GetBrowserForContents(source_contents);
94 if (!opener) {
95 LOG(ERROR) << "No opener found for chrome popup browser";
96 return;
97 }
98
99 auto browser_info =
100 CefBrowserInfoManager::GetInstance()->CreatePopupBrowserInfo(
101 new_contents, /*is_windowless=*/false, extra_info);
102 CHECK(browser_info->is_popup());
103
104 // Popups must share the same RequestContext as the parent.
105 auto request_context_impl = opener->request_context();
106 CHECK(request_context_impl);
107
108 // We don't officially own |new_contents| until AddNewContents() is called.
109 // However, we need to install observers/delegates here.
110 CreateBrowser(new_contents, settings, client, std::move(platform_delegate),
111 browser_info, opener, request_context_impl);
112 }
113
AddNewContents(content::WebContents * source_contents,std::unique_ptr<content::WebContents> new_contents,const GURL & target_url,WindowOpenDisposition disposition,const gfx::Rect & initial_rect,bool user_gesture,bool * was_blocked)114 void ChromeBrowserDelegate::AddNewContents(
115 content::WebContents* source_contents,
116 std::unique_ptr<content::WebContents> new_contents,
117 const GURL& target_url,
118 WindowOpenDisposition disposition,
119 const gfx::Rect& initial_rect,
120 bool user_gesture,
121 bool* was_blocked) {
122 auto new_browser =
123 ChromeBrowserHostImpl::GetBrowserForContents(new_contents.get());
124 if (new_browser) {
125 // Create a new Browser and give it ownership of the WebContents.
126 new_browser->AddNewContents(std::move(new_contents));
127 return;
128 }
129
130 // Fall back to default behavior from Browser::AddNewContents.
131 chrome::AddWebContents(browser_, source_contents, std::move(new_contents),
132 target_url, disposition, initial_rect);
133 }
134
OpenURLFromTab(content::WebContents * source,const content::OpenURLParams & params)135 content::WebContents* ChromeBrowserDelegate::OpenURLFromTab(
136 content::WebContents* source,
137 const content::OpenURLParams& params) {
138 // |source| may be nullptr when opening a link from chrome UI such as the
139 // Reading List sidebar. In that case we default to using the Browser's
140 // currently active WebContents.
141 if (!source) {
142 source = browser_->tab_strip_model()->GetActiveWebContents();
143 DCHECK(source);
144 }
145
146 // Return nullptr to cancel the navigation. Otherwise, proceed with default
147 // chrome handling.
148 if (auto delegate = GetDelegateForWebContents(source)) {
149 return delegate->OpenURLFromTab(source, params);
150 }
151 return nullptr;
152 }
153
LoadingStateChanged(content::WebContents * source,bool should_show_loading_ui)154 void ChromeBrowserDelegate::LoadingStateChanged(content::WebContents* source,
155 bool should_show_loading_ui) {
156 if (auto delegate = GetDelegateForWebContents(source)) {
157 delegate->LoadingStateChanged(source, should_show_loading_ui);
158 }
159 }
160
UpdateTargetURL(content::WebContents * source,const GURL & url)161 void ChromeBrowserDelegate::UpdateTargetURL(content::WebContents* source,
162 const GURL& url) {
163 if (auto delegate = GetDelegateForWebContents(source)) {
164 delegate->UpdateTargetURL(source, url);
165 }
166 }
167
DidAddMessageToConsole(content::WebContents * source,blink::mojom::ConsoleMessageLevel log_level,const std::u16string & message,int32_t line_no,const std::u16string & source_id)168 bool ChromeBrowserDelegate::DidAddMessageToConsole(
169 content::WebContents* source,
170 blink::mojom::ConsoleMessageLevel log_level,
171 const std::u16string& message,
172 int32_t line_no,
173 const std::u16string& source_id) {
174 if (auto delegate = GetDelegateForWebContents(source)) {
175 return delegate->DidAddMessageToConsole(source, log_level, message, line_no,
176 source_id);
177 }
178 return false;
179 }
180
DidNavigatePrimaryMainFramePostCommit(content::WebContents * web_contents)181 void ChromeBrowserDelegate::DidNavigatePrimaryMainFramePostCommit(
182 content::WebContents* web_contents) {
183 if (auto delegate = GetDelegateForWebContents(web_contents)) {
184 delegate->DidNavigatePrimaryMainFramePostCommit(web_contents);
185 }
186 }
187
EnterFullscreenModeForTab(content::RenderFrameHost * requesting_frame,const blink::mojom::FullscreenOptions & options)188 void ChromeBrowserDelegate::EnterFullscreenModeForTab(
189 content::RenderFrameHost* requesting_frame,
190 const blink::mojom::FullscreenOptions& options) {
191 auto web_contents =
192 content::WebContents::FromRenderFrameHost(requesting_frame);
193 if (!web_contents)
194 return;
195
196 if (auto delegate = GetDelegateForWebContents(web_contents)) {
197 delegate->EnterFullscreenModeForTab(requesting_frame, options);
198 }
199 }
200
ExitFullscreenModeForTab(content::WebContents * web_contents)201 void ChromeBrowserDelegate::ExitFullscreenModeForTab(
202 content::WebContents* web_contents) {
203 if (auto delegate = GetDelegateForWebContents(web_contents)) {
204 delegate->ExitFullscreenModeForTab(web_contents);
205 }
206 }
207
PreHandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)208 KeyboardEventProcessingResult ChromeBrowserDelegate::PreHandleKeyboardEvent(
209 content::WebContents* source,
210 const content::NativeWebKeyboardEvent& event) {
211 if (auto delegate = GetDelegateForWebContents(source)) {
212 return delegate->PreHandleKeyboardEvent(source, event);
213 }
214 return KeyboardEventProcessingResult::NOT_HANDLED;
215 }
216
HandleKeyboardEvent(content::WebContents * source,const content::NativeWebKeyboardEvent & event)217 bool ChromeBrowserDelegate::HandleKeyboardEvent(
218 content::WebContents* source,
219 const content::NativeWebKeyboardEvent& event) {
220 if (auto delegate = GetDelegateForWebContents(source)) {
221 return delegate->HandleKeyboardEvent(source, event);
222 }
223 return false;
224 }
225
CreateBrowser(content::WebContents * web_contents,CefBrowserSettings settings,CefRefPtr<CefClient> client,std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,scoped_refptr<CefBrowserInfo> browser_info,CefRefPtr<ChromeBrowserHostImpl> opener,CefRefPtr<CefRequestContextImpl> request_context_impl)226 void ChromeBrowserDelegate::CreateBrowser(
227 content::WebContents* web_contents,
228 CefBrowserSettings settings,
229 CefRefPtr<CefClient> client,
230 std::unique_ptr<CefBrowserPlatformDelegate> platform_delegate,
231 scoped_refptr<CefBrowserInfo> browser_info,
232 CefRefPtr<ChromeBrowserHostImpl> opener,
233 CefRefPtr<CefRequestContextImpl> request_context_impl) {
234 CEF_REQUIRE_UIT();
235 DCHECK(web_contents);
236 DCHECK(platform_delegate);
237 DCHECK(browser_info);
238 DCHECK(request_context_impl);
239
240 // If |opener| is non-nullptr it must be a popup window.
241 DCHECK(!opener.get() || browser_info->is_popup());
242
243 if (!client) {
244 if (auto app = CefAppManager::Get()->GetApplication()) {
245 if (auto bph = app->GetBrowserProcessHandler()) {
246 client = bph->GetDefaultClient();
247 }
248 }
249 }
250
251 if (!client) {
252 LOG(WARNING) << "Creating a chrome browser without a client";
253 }
254
255 // Check if chrome and CEF are using the same browser context.
256 // TODO(chrome-runtime): Verify if/when this might occur.
257 auto chrome_browser_context =
258 CefBrowserContext::FromBrowserContext(browser_->create_params().profile);
259 if (chrome_browser_context != request_context_impl->GetBrowserContext()) {
260 LOG(WARNING) << "Creating a chrome browser with mismatched context";
261 }
262
263 // Remains alive until the associated WebContents is destroyed.
264 CefRefPtr<ChromeBrowserHostImpl> browser_host =
265 new ChromeBrowserHostImpl(settings, client, std::move(platform_delegate),
266 browser_info, request_context_impl);
267 browser_host->Attach(web_contents, opener);
268
269 // The Chrome browser for a popup won't be created until AddNewContents().
270 if (!opener) {
271 browser_host->SetBrowser(browser_);
272 }
273 }
274
GetDelegateForWebContents(content::WebContents * web_contents)275 CefBrowserContentsDelegate* ChromeBrowserDelegate::GetDelegateForWebContents(
276 content::WebContents* web_contents) {
277 auto browser_host =
278 ChromeBrowserHostImpl::GetBrowserForContents(web_contents);
279 if (browser_host)
280 return browser_host->contents_delegate();
281 return nullptr;
282 }
283
284 namespace cef {
285
286 // static
Create(Browser * browser,scoped_refptr<CreateParams> cef_params)287 std::unique_ptr<BrowserDelegate> BrowserDelegate::Create(
288 Browser* browser,
289 scoped_refptr<CreateParams> cef_params) {
290 CefBrowserCreateParams create_params;
291
292 // Parameters from ChromeBrowserHostImpl::Create, or nullptr if the Browser
293 // was created from somewhere else.
294 auto params = static_cast<ChromeBrowserHostImpl::DelegateCreateParams*>(
295 cef_params.get());
296 if (params) {
297 create_params = params->create_params_;
298
299 // Clear these values so they're not persisted to additional Browsers.
300 #if defined(TOOLKIT_VIEWS)
301 params->create_params_.browser_view = nullptr;
302 #endif
303 }
304
305 return std::make_unique<ChromeBrowserDelegate>(browser, create_params);
306 }
307
308 } // namespace cef
309