1 // Copyright (c) 2012 The Chromium 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_context.h"
6
7 #include <map>
8 #include <utility>
9
10 #include "libcef/browser/context.h"
11 #include "libcef/browser/media_router/media_router_manager.h"
12 #include "libcef/browser/request_context_impl.h"
13 #include "libcef/browser/thread_util.h"
14 #include "libcef/common/cef_switches.h"
15 #include "libcef/common/frame_util.h"
16 #include "libcef/features/runtime.h"
17
18 #include "base/files/file_util.h"
19 #include "base/lazy_instance.h"
20 #include "base/logging.h"
21 #include "base/no_destructor.h"
22 #include "base/strings/string_split.h"
23 #include "base/strings/string_util.h"
24 #include "chrome/browser/profiles/profile.h"
25 #include "content/public/browser/browser_context.h"
26 #include "content/public/browser/browser_task_traits.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/storage_partition.h"
29
30 using content::BrowserThread;
31
32 namespace {
33
34 // Manages the global list of Impl instances.
35 class ImplManager {
36 public:
37 using Vector = std::vector<CefBrowserContext*>;
38
ImplManager()39 ImplManager() {}
40
41 ImplManager(const ImplManager&) = delete;
42 ImplManager& operator=(const ImplManager&) = delete;
43
~ImplManager()44 ~ImplManager() {
45 DCHECK(all_.empty());
46 DCHECK(map_.empty());
47 }
48
AddImpl(CefBrowserContext * impl)49 void AddImpl(CefBrowserContext* impl) {
50 CEF_REQUIRE_UIT();
51 DCHECK(!IsValidImpl(impl));
52 all_.push_back(impl);
53 }
54
RemoveImpl(CefBrowserContext * impl,const base::FilePath & path)55 void RemoveImpl(CefBrowserContext* impl, const base::FilePath& path) {
56 CEF_REQUIRE_UIT();
57
58 Vector::iterator it = GetImplPos(impl);
59 DCHECK(it != all_.end());
60 all_.erase(it);
61
62 if (!path.empty()) {
63 PathMap::iterator it = map_.find(path);
64 DCHECK(it != map_.end());
65 if (it != map_.end())
66 map_.erase(it);
67 }
68 }
69
IsValidImpl(const CefBrowserContext * impl)70 bool IsValidImpl(const CefBrowserContext* impl) {
71 CEF_REQUIRE_UIT();
72 return GetImplPos(impl) != all_.end();
73 }
74
GetImplFromGlobalId(const content::GlobalRenderFrameHostId & global_id,bool require_frame_match)75 CefBrowserContext* GetImplFromGlobalId(
76 const content::GlobalRenderFrameHostId& global_id,
77 bool require_frame_match) {
78 CEF_REQUIRE_UIT();
79 for (const auto& context : all_) {
80 if (context->IsAssociatedContext(global_id, require_frame_match)) {
81 return context;
82 }
83 }
84 return nullptr;
85 }
86
GetImplFromBrowserContext(const content::BrowserContext * context)87 CefBrowserContext* GetImplFromBrowserContext(
88 const content::BrowserContext* context) {
89 CEF_REQUIRE_UIT();
90 if (!context)
91 return nullptr;
92
93 for (const auto& bc : all_) {
94 if (bc->AsBrowserContext() == context)
95 return bc;
96 }
97 return nullptr;
98 }
99
SetImplPath(CefBrowserContext * impl,const base::FilePath & path)100 void SetImplPath(CefBrowserContext* impl, const base::FilePath& path) {
101 CEF_REQUIRE_UIT();
102 DCHECK(!path.empty());
103 DCHECK(IsValidImpl(impl));
104 DCHECK(GetImplFromPath(path) == nullptr);
105 map_.insert(std::make_pair(path, impl));
106 }
107
GetImplFromPath(const base::FilePath & path)108 CefBrowserContext* GetImplFromPath(const base::FilePath& path) {
109 CEF_REQUIRE_UIT();
110 DCHECK(!path.empty());
111 PathMap::const_iterator it = map_.find(path);
112 if (it != map_.end())
113 return it->second;
114 return nullptr;
115 }
116
GetAllImpl() const117 const Vector GetAllImpl() const { return all_; }
118
119 private:
GetImplPos(const CefBrowserContext * impl)120 Vector::iterator GetImplPos(const CefBrowserContext* impl) {
121 Vector::iterator it = all_.begin();
122 for (; it != all_.end(); ++it) {
123 if (*it == impl)
124 return it;
125 }
126 return all_.end();
127 }
128
129 using PathMap = std::map<base::FilePath, CefBrowserContext*>;
130 PathMap map_;
131
132 Vector all_;
133 };
134
135 #if DCHECK_IS_ON()
136 // Because of DCHECK()s in the object destructor.
137 base::LazyInstance<ImplManager>::DestructorAtExit g_manager =
138 LAZY_INSTANCE_INITIALIZER;
139 #else
140 base::LazyInstance<ImplManager>::Leaky g_manager = LAZY_INSTANCE_INITIALIZER;
141 #endif
142
GetSelf(base::WeakPtr<CefBrowserContext> self)143 CefBrowserContext* GetSelf(base::WeakPtr<CefBrowserContext> self) {
144 CEF_REQUIRE_UIT();
145 return self.get();
146 }
147
MakeSupportedSchemes(const CefString & schemes_list,bool include_defaults)148 CefBrowserContext::CookieableSchemes MakeSupportedSchemes(
149 const CefString& schemes_list,
150 bool include_defaults) {
151 std::vector<std::string> all_schemes;
152 if (!schemes_list.empty()) {
153 all_schemes =
154 base::SplitString(schemes_list.ToString(), std::string(","),
155 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
156 }
157
158 if (include_defaults) {
159 // Add default schemes that should always support cookies.
160 // This list should match CookieMonster::kDefaultCookieableSchemes.
161 all_schemes.push_back("http");
162 all_schemes.push_back("https");
163 all_schemes.push_back("ws");
164 all_schemes.push_back("wss");
165 }
166
167 return absl::make_optional(all_schemes);
168 }
169
170 } // namespace
171
CefBrowserContext(const CefRequestContextSettings & settings)172 CefBrowserContext::CefBrowserContext(const CefRequestContextSettings& settings)
173 : settings_(settings), weak_ptr_factory_(this) {
174 g_manager.Get().AddImpl(this);
175 getter_ = base::BindRepeating(GetSelf, weak_ptr_factory_.GetWeakPtr());
176 }
177
~CefBrowserContext()178 CefBrowserContext::~CefBrowserContext() {
179 CEF_REQUIRE_UIT();
180 #if DCHECK_IS_ON()
181 DCHECK(is_shutdown_);
182 #endif
183 }
184
Initialize()185 void CefBrowserContext::Initialize() {
186 cache_path_ = base::FilePath(CefString(&settings_.cache_path));
187
188 if (!cache_path_.empty())
189 g_manager.Get().SetImplPath(this, cache_path_);
190
191 iothread_state_ = base::MakeRefCounted<CefIOThreadState>();
192
193 if (settings_.cookieable_schemes_list.length > 0 ||
194 settings_.cookieable_schemes_exclude_defaults) {
195 cookieable_schemes_ =
196 MakeSupportedSchemes(CefString(&settings_.cookieable_schemes_list),
197 !settings_.cookieable_schemes_exclude_defaults);
198 }
199 }
200
Shutdown()201 void CefBrowserContext::Shutdown() {
202 CEF_REQUIRE_UIT();
203
204 #if DCHECK_IS_ON()
205 is_shutdown_ = true;
206 #endif
207
208 // No CefRequestContext should be referencing this object any longer.
209 DCHECK(request_context_set_.empty());
210
211 // Unregister the context first to avoid re-entrancy during shutdown.
212 g_manager.Get().RemoveImpl(this, cache_path_);
213
214 // Destroy objects that may hold references to the MediaRouter.
215 media_router_manager_.reset();
216
217 // Invalidate any Getter references to this object.
218 weak_ptr_factory_.InvalidateWeakPtrs();
219 }
220
AddCefRequestContext(CefRequestContextImpl * context)221 void CefBrowserContext::AddCefRequestContext(CefRequestContextImpl* context) {
222 CEF_REQUIRE_UIT();
223 request_context_set_.insert(context);
224 }
225
RemoveCefRequestContext(CefRequestContextImpl * context)226 void CefBrowserContext::RemoveCefRequestContext(
227 CefRequestContextImpl* context) {
228 CEF_REQUIRE_UIT();
229
230 request_context_set_.erase(context);
231
232 // Delete ourselves when the reference count reaches zero.
233 if (request_context_set_.empty()) {
234 Shutdown();
235
236 // Allow the current call stack to unwind before deleting |this|.
237 content::BrowserThread::DeleteSoon(CEF_UIT, FROM_HERE, this);
238 }
239 }
240
241 // static
FromCachePath(const base::FilePath & cache_path)242 CefBrowserContext* CefBrowserContext::FromCachePath(
243 const base::FilePath& cache_path) {
244 return g_manager.Get().GetImplFromPath(cache_path);
245 }
246
247 // static
FromGlobalId(const content::GlobalRenderFrameHostId & global_id,bool require_frame_match)248 CefBrowserContext* CefBrowserContext::FromGlobalId(
249 const content::GlobalRenderFrameHostId& global_id,
250 bool require_frame_match) {
251 return g_manager.Get().GetImplFromGlobalId(global_id, require_frame_match);
252 }
253
254 // static
FromBrowserContext(const content::BrowserContext * context)255 CefBrowserContext* CefBrowserContext::FromBrowserContext(
256 const content::BrowserContext* context) {
257 return g_manager.Get().GetImplFromBrowserContext(context);
258 }
259
260 // static
FromProfile(const Profile * profile)261 CefBrowserContext* CefBrowserContext::FromProfile(const Profile* profile) {
262 auto* cef_context = FromBrowserContext(profile);
263 if (cef_context)
264 return cef_context;
265
266 if (cef::IsChromeRuntimeEnabled()) {
267 auto* original_profile = profile->GetOriginalProfile();
268 if (original_profile != profile) {
269 // With the Chrome runtime if the user launches an incognito window via
270 // the UI we might be associated with the original Profile instead of the
271 // (current) incognito profile.
272 return FromBrowserContext(original_profile);
273 }
274 }
275
276 return nullptr;
277 }
278
279 // static
GetAll()280 std::vector<CefBrowserContext*> CefBrowserContext::GetAll() {
281 return g_manager.Get().GetAllImpl();
282 }
283
OnRenderFrameCreated(CefRequestContextImpl * request_context,const content::GlobalRenderFrameHostId & global_id,bool is_main_frame,bool is_guest_view)284 void CefBrowserContext::OnRenderFrameCreated(
285 CefRequestContextImpl* request_context,
286 const content::GlobalRenderFrameHostId& global_id,
287 bool is_main_frame,
288 bool is_guest_view) {
289 CEF_REQUIRE_UIT();
290 DCHECK(frame_util::IsValidGlobalId(global_id));
291
292 render_id_set_.insert(global_id);
293
294 CefRefPtr<CefRequestContextHandler> handler = request_context->GetHandler();
295 if (handler) {
296 handler_map_.AddHandler(global_id, handler);
297
298 CEF_POST_TASK(CEF_IOT, base::BindOnce(&CefIOThreadState::AddHandler,
299 iothread_state_, global_id, handler));
300 }
301 }
302
OnRenderFrameDeleted(CefRequestContextImpl * request_context,const content::GlobalRenderFrameHostId & global_id,bool is_main_frame,bool is_guest_view)303 void CefBrowserContext::OnRenderFrameDeleted(
304 CefRequestContextImpl* request_context,
305 const content::GlobalRenderFrameHostId& global_id,
306 bool is_main_frame,
307 bool is_guest_view) {
308 CEF_REQUIRE_UIT();
309 DCHECK(frame_util::IsValidGlobalId(global_id));
310
311 auto it1 = render_id_set_.find(global_id);
312 if (it1 != render_id_set_.end())
313 render_id_set_.erase(it1);
314
315 CefRefPtr<CefRequestContextHandler> handler = request_context->GetHandler();
316 if (handler) {
317 handler_map_.RemoveHandler(global_id);
318
319 CEF_POST_TASK(CEF_IOT, base::BindOnce(&CefIOThreadState::RemoveHandler,
320 iothread_state_, global_id));
321 }
322 }
323
GetHandler(const content::GlobalRenderFrameHostId & global_id,bool require_frame_match) const324 CefRefPtr<CefRequestContextHandler> CefBrowserContext::GetHandler(
325 const content::GlobalRenderFrameHostId& global_id,
326 bool require_frame_match) const {
327 CEF_REQUIRE_UIT();
328 return handler_map_.GetHandler(global_id, require_frame_match);
329 }
330
IsAssociatedContext(const content::GlobalRenderFrameHostId & global_id,bool require_frame_match) const331 bool CefBrowserContext::IsAssociatedContext(
332 const content::GlobalRenderFrameHostId& global_id,
333 bool require_frame_match) const {
334 CEF_REQUIRE_UIT();
335
336 if (frame_util::IsValidGlobalId(global_id)) {
337 const auto it1 = render_id_set_.find(global_id);
338 if (it1 != render_id_set_.end())
339 return true;
340 }
341
342 if (frame_util::IsValidChildId(global_id.child_id) && !require_frame_match) {
343 // Choose an arbitrary handler for the same process.
344 for (const auto& render_ids : render_id_set_) {
345 if (render_ids.child_id == global_id.child_id)
346 return true;
347 }
348 }
349
350 return false;
351 }
352
RegisterSchemeHandlerFactory(const CefString & scheme_name,const CefString & domain_name,CefRefPtr<CefSchemeHandlerFactory> factory)353 void CefBrowserContext::RegisterSchemeHandlerFactory(
354 const CefString& scheme_name,
355 const CefString& domain_name,
356 CefRefPtr<CefSchemeHandlerFactory> factory) {
357 CEF_POST_TASK(
358 CEF_IOT,
359 base::BindOnce(&CefIOThreadState::RegisterSchemeHandlerFactory,
360 iothread_state_, scheme_name, domain_name, factory));
361 }
362
ClearSchemeHandlerFactories()363 void CefBrowserContext::ClearSchemeHandlerFactories() {
364 CEF_POST_TASK(CEF_IOT,
365 base::BindOnce(&CefIOThreadState::ClearSchemeHandlerFactories,
366 iothread_state_));
367 }
368
LoadExtension(const CefString & root_directory,CefRefPtr<CefDictionaryValue> manifest,CefRefPtr<CefExtensionHandler> handler,CefRefPtr<CefRequestContext> loader_context)369 void CefBrowserContext::LoadExtension(
370 const CefString& root_directory,
371 CefRefPtr<CefDictionaryValue> manifest,
372 CefRefPtr<CefExtensionHandler> handler,
373 CefRefPtr<CefRequestContext> loader_context) {
374 NOTIMPLEMENTED();
375 if (handler)
376 handler->OnExtensionLoadFailed(ERR_ABORTED);
377 }
378
GetExtensions(std::vector<CefString> & extension_ids)379 bool CefBrowserContext::GetExtensions(std::vector<CefString>& extension_ids) {
380 NOTIMPLEMENTED();
381 return false;
382 }
383
GetExtension(const CefString & extension_id)384 CefRefPtr<CefExtension> CefBrowserContext::GetExtension(
385 const CefString& extension_id) {
386 NOTIMPLEMENTED();
387 return nullptr;
388 }
389
UnloadExtension(const CefString & extension_id)390 bool CefBrowserContext::UnloadExtension(const CefString& extension_id) {
391 NOTIMPLEMENTED();
392 return false;
393 }
394
IsPrintPreviewSupported() const395 bool CefBrowserContext::IsPrintPreviewSupported() const {
396 return true;
397 }
398
GetNetworkContext()399 network::mojom::NetworkContext* CefBrowserContext::GetNetworkContext() {
400 CEF_REQUIRE_UIT();
401 auto browser_context = AsBrowserContext();
402 return browser_context->GetDefaultStoragePartition()->GetNetworkContext();
403 }
404
GetMediaRouterManager()405 CefMediaRouterManager* CefBrowserContext::GetMediaRouterManager() {
406 CEF_REQUIRE_UIT();
407 if (!media_router_manager_) {
408 media_router_manager_.reset(new CefMediaRouterManager(AsBrowserContext()));
409 }
410 return media_router_manager_.get();
411 }
412
GetCookieableSchemes() const413 CefBrowserContext::CookieableSchemes CefBrowserContext::GetCookieableSchemes()
414 const {
415 CEF_REQUIRE_UIT();
416 if (cookieable_schemes_)
417 return cookieable_schemes_;
418
419 return GetGlobalCookieableSchemes();
420 }
421
422 // static
423 CefBrowserContext::CookieableSchemes
GetGlobalCookieableSchemes()424 CefBrowserContext::GetGlobalCookieableSchemes() {
425 CEF_REQUIRE_UIT();
426
427 static base::NoDestructor<CookieableSchemes> schemes(
428 []() -> CookieableSchemes {
429 const auto& settings = CefContext::Get()->settings();
430 if (settings.cookieable_schemes_list.length > 0 ||
431 settings.cookieable_schemes_exclude_defaults) {
432 return MakeSupportedSchemes(
433 CefString(&settings.cookieable_schemes_list),
434 !settings.cookieable_schemes_exclude_defaults);
435 }
436 return absl::nullopt;
437 }());
438 return *schemes;
439 }
440