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/features/runtime.h"
16
17 #include "base/files/file_util.h"
18 #include "base/lazy_instance.h"
19 #include "base/logging.h"
20 #include "base/no_destructor.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "content/public/browser/browser_context.h"
25 #include "content/public/browser/browser_task_traits.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/storage_partition.h"
28
29 using content::BrowserThread;
30
31 namespace {
32
33 // Manages the global list of Impl instances.
34 class ImplManager {
35 public:
36 typedef std::vector<CefBrowserContext*> Vector;
37
ImplManager()38 ImplManager() {}
~ImplManager()39 ~ImplManager() {
40 DCHECK(all_.empty());
41 DCHECK(map_.empty());
42 }
43
AddImpl(CefBrowserContext * impl)44 void AddImpl(CefBrowserContext* impl) {
45 CEF_REQUIRE_UIT();
46 DCHECK(!IsValidImpl(impl));
47 all_.push_back(impl);
48 }
49
RemoveImpl(CefBrowserContext * impl,const base::FilePath & path)50 void RemoveImpl(CefBrowserContext* impl, const base::FilePath& path) {
51 CEF_REQUIRE_UIT();
52
53 Vector::iterator it = GetImplPos(impl);
54 DCHECK(it != all_.end());
55 all_.erase(it);
56
57 if (!path.empty()) {
58 PathMap::iterator it = map_.find(path);
59 DCHECK(it != map_.end());
60 if (it != map_.end())
61 map_.erase(it);
62 }
63 }
64
IsValidImpl(const CefBrowserContext * impl)65 bool IsValidImpl(const CefBrowserContext* impl) {
66 CEF_REQUIRE_UIT();
67 return GetImplPos(impl) != all_.end();
68 }
69
GetImplFromIDs(int render_process_id,int render_frame_id,int frame_tree_node_id,bool require_frame_match)70 CefBrowserContext* GetImplFromIDs(int render_process_id,
71 int render_frame_id,
72 int frame_tree_node_id,
73 bool require_frame_match) {
74 CEF_REQUIRE_UIT();
75 for (const auto& context : all_) {
76 if (context->IsAssociatedContext(render_process_id, render_frame_id,
77 frame_tree_node_id,
78 require_frame_match)) {
79 return context;
80 }
81 }
82 return nullptr;
83 }
84
GetImplFromBrowserContext(const content::BrowserContext * context)85 CefBrowserContext* GetImplFromBrowserContext(
86 const content::BrowserContext* context) {
87 CEF_REQUIRE_UIT();
88 if (!context)
89 return nullptr;
90
91 for (const auto& bc : all_) {
92 if (bc->AsBrowserContext() == context)
93 return bc;
94 }
95 return nullptr;
96 }
97
SetImplPath(CefBrowserContext * impl,const base::FilePath & path)98 void SetImplPath(CefBrowserContext* impl, const base::FilePath& path) {
99 CEF_REQUIRE_UIT();
100 DCHECK(!path.empty());
101 DCHECK(IsValidImpl(impl));
102 DCHECK(GetImplFromPath(path) == nullptr);
103 map_.insert(std::make_pair(path, impl));
104 }
105
GetImplFromPath(const base::FilePath & path)106 CefBrowserContext* GetImplFromPath(const base::FilePath& path) {
107 CEF_REQUIRE_UIT();
108 DCHECK(!path.empty());
109 PathMap::const_iterator it = map_.find(path);
110 if (it != map_.end())
111 return it->second;
112 return nullptr;
113 }
114
GetAllImpl() const115 const Vector GetAllImpl() const { return all_; }
116
117 private:
GetImplPos(const CefBrowserContext * impl)118 Vector::iterator GetImplPos(const CefBrowserContext* impl) {
119 Vector::iterator it = all_.begin();
120 for (; it != all_.end(); ++it) {
121 if (*it == impl)
122 return it;
123 }
124 return all_.end();
125 }
126
127 typedef std::map<base::FilePath, CefBrowserContext*> PathMap;
128 PathMap map_;
129
130 Vector all_;
131
132 DISALLOW_COPY_AND_ASSIGN(ImplManager);
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 base::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
AddCefRequestContext(CefRequestContextImpl * context)218 void CefBrowserContext::AddCefRequestContext(CefRequestContextImpl* context) {
219 CEF_REQUIRE_UIT();
220 request_context_set_.insert(context);
221 }
222
RemoveCefRequestContext(CefRequestContextImpl * context)223 void CefBrowserContext::RemoveCefRequestContext(
224 CefRequestContextImpl* context) {
225 CEF_REQUIRE_UIT();
226
227 request_context_set_.erase(context);
228
229 // Delete ourselves when the reference count reaches zero.
230 if (request_context_set_.empty()) {
231 Shutdown();
232 delete this;
233 }
234 }
235
236 // static
FromCachePath(const base::FilePath & cache_path)237 CefBrowserContext* CefBrowserContext::FromCachePath(
238 const base::FilePath& cache_path) {
239 return g_manager.Get().GetImplFromPath(cache_path);
240 }
241
242 // static
FromIDs(int render_process_id,int render_frame_id,int frame_tree_node_id,bool require_frame_match)243 CefBrowserContext* CefBrowserContext::FromIDs(int render_process_id,
244 int render_frame_id,
245 int frame_tree_node_id,
246 bool require_frame_match) {
247 return g_manager.Get().GetImplFromIDs(render_process_id, render_frame_id,
248 frame_tree_node_id,
249 require_frame_match);
250 }
251
252 // static
FromBrowserContext(const content::BrowserContext * context)253 CefBrowserContext* CefBrowserContext::FromBrowserContext(
254 const content::BrowserContext* context) {
255 return g_manager.Get().GetImplFromBrowserContext(context);
256 }
257
258 // static
FromProfile(const Profile * profile)259 CefBrowserContext* CefBrowserContext::FromProfile(const Profile* profile) {
260 auto* cef_context = FromBrowserContext(profile);
261 if (cef_context)
262 return cef_context;
263
264 if (cef::IsChromeRuntimeEnabled()) {
265 auto* original_profile = profile->GetOriginalProfile();
266 if (original_profile != profile) {
267 // With the Chrome runtime if the user launches an incognito window via
268 // the UI we might be associated with the original Profile instead of the
269 // (current) incognito profile.
270 return FromBrowserContext(original_profile);
271 }
272 }
273
274 return nullptr;
275 }
276
277 // static
GetAll()278 std::vector<CefBrowserContext*> CefBrowserContext::GetAll() {
279 return g_manager.Get().GetAllImpl();
280 }
281
OnRenderFrameCreated(CefRequestContextImpl * request_context,int render_process_id,int render_frame_id,int frame_tree_node_id,bool is_main_frame,bool is_guest_view)282 void CefBrowserContext::OnRenderFrameCreated(
283 CefRequestContextImpl* request_context,
284 int render_process_id,
285 int render_frame_id,
286 int frame_tree_node_id,
287 bool is_main_frame,
288 bool is_guest_view) {
289 CEF_REQUIRE_UIT();
290 DCHECK_GE(render_process_id, 0);
291 DCHECK_GE(render_frame_id, 0);
292 DCHECK_GE(frame_tree_node_id, 0);
293
294 render_id_set_.insert(std::make_pair(render_process_id, render_frame_id));
295 node_id_set_.insert(frame_tree_node_id);
296
297 CefRefPtr<CefRequestContextHandler> handler = request_context->GetHandler();
298 if (handler) {
299 handler_map_.AddHandler(render_process_id, render_frame_id,
300 frame_tree_node_id, handler);
301
302 CEF_POST_TASK(CEF_IOT,
303 base::Bind(&CefIOThreadState::AddHandler, iothread_state_,
304 render_process_id, render_frame_id,
305 frame_tree_node_id, handler));
306 }
307 }
308
OnRenderFrameDeleted(CefRequestContextImpl * request_context,int render_process_id,int render_frame_id,int frame_tree_node_id,bool is_main_frame,bool is_guest_view)309 void CefBrowserContext::OnRenderFrameDeleted(
310 CefRequestContextImpl* request_context,
311 int render_process_id,
312 int render_frame_id,
313 int frame_tree_node_id,
314 bool is_main_frame,
315 bool is_guest_view) {
316 CEF_REQUIRE_UIT();
317 DCHECK_GE(render_process_id, 0);
318 DCHECK_GE(render_frame_id, 0);
319 DCHECK_GE(frame_tree_node_id, 0);
320
321 auto it1 =
322 render_id_set_.find(std::make_pair(render_process_id, render_frame_id));
323 if (it1 != render_id_set_.end())
324 render_id_set_.erase(it1);
325
326 auto it2 = node_id_set_.find(frame_tree_node_id);
327 if (it2 != node_id_set_.end())
328 node_id_set_.erase(it2);
329
330 CefRefPtr<CefRequestContextHandler> handler = request_context->GetHandler();
331 if (handler) {
332 handler_map_.RemoveHandler(render_process_id, render_frame_id,
333 frame_tree_node_id);
334
335 CEF_POST_TASK(CEF_IOT, base::Bind(&CefIOThreadState::RemoveHandler,
336 iothread_state_, render_process_id,
337 render_frame_id, frame_tree_node_id));
338 }
339
340 if (is_main_frame) {
341 ClearPluginLoadDecision(render_process_id);
342 }
343 }
344
GetHandler(int render_process_id,int render_frame_id,int frame_tree_node_id,bool require_frame_match) const345 CefRefPtr<CefRequestContextHandler> CefBrowserContext::GetHandler(
346 int render_process_id,
347 int render_frame_id,
348 int frame_tree_node_id,
349 bool require_frame_match) const {
350 CEF_REQUIRE_UIT();
351 return handler_map_.GetHandler(render_process_id, render_frame_id,
352 frame_tree_node_id, require_frame_match);
353 }
354
IsAssociatedContext(int render_process_id,int render_frame_id,int frame_tree_node_id,bool require_frame_match) const355 bool CefBrowserContext::IsAssociatedContext(int render_process_id,
356 int render_frame_id,
357 int frame_tree_node_id,
358 bool require_frame_match) const {
359 CEF_REQUIRE_UIT();
360
361 if (render_process_id >= 0 && render_frame_id >= 0) {
362 const auto it1 =
363 render_id_set_.find(std::make_pair(render_process_id, render_frame_id));
364 if (it1 != render_id_set_.end())
365 return true;
366 }
367
368 if (frame_tree_node_id >= 0) {
369 const auto it2 = node_id_set_.find(frame_tree_node_id);
370 if (it2 != node_id_set_.end())
371 return true;
372 }
373
374 if (render_process_id >= 0 && !require_frame_match) {
375 // Choose an arbitrary handler for the same process.
376 for (const auto& render_ids : render_id_set_) {
377 if (render_ids.first == render_process_id)
378 return true;
379 }
380 }
381
382 return false;
383 }
384
AddPluginLoadDecision(int render_process_id,const base::FilePath & plugin_path,bool is_main_frame,const url::Origin & main_frame_origin,chrome::mojom::PluginStatus status)385 void CefBrowserContext::AddPluginLoadDecision(
386 int render_process_id,
387 const base::FilePath& plugin_path,
388 bool is_main_frame,
389 const url::Origin& main_frame_origin,
390 chrome::mojom::PluginStatus status) {
391 CEF_REQUIRE_UIT();
392 DCHECK_GE(render_process_id, 0);
393 DCHECK(!plugin_path.empty());
394
395 plugin_load_decision_map_.insert(std::make_pair(
396 std::make_pair(std::make_pair(render_process_id, plugin_path),
397 std::make_pair(is_main_frame, main_frame_origin)),
398 status));
399 }
400
HasPluginLoadDecision(int render_process_id,const base::FilePath & plugin_path,bool is_main_frame,const url::Origin & main_frame_origin,chrome::mojom::PluginStatus * status) const401 bool CefBrowserContext::HasPluginLoadDecision(
402 int render_process_id,
403 const base::FilePath& plugin_path,
404 bool is_main_frame,
405 const url::Origin& main_frame_origin,
406 chrome::mojom::PluginStatus* status) const {
407 CEF_REQUIRE_UIT();
408 DCHECK_GE(render_process_id, 0);
409 DCHECK(!plugin_path.empty());
410
411 PluginLoadDecisionMap::const_iterator it = plugin_load_decision_map_.find(
412 std::make_pair(std::make_pair(render_process_id, plugin_path),
413 std::make_pair(is_main_frame, main_frame_origin)));
414 if (it == plugin_load_decision_map_.end())
415 return false;
416
417 *status = it->second;
418 return true;
419 }
420
ClearPluginLoadDecision(int render_process_id)421 void CefBrowserContext::ClearPluginLoadDecision(int render_process_id) {
422 CEF_REQUIRE_UIT();
423
424 if (render_process_id == -1) {
425 plugin_load_decision_map_.clear();
426 } else {
427 PluginLoadDecisionMap::iterator it = plugin_load_decision_map_.begin();
428 while (it != plugin_load_decision_map_.end()) {
429 if (it->first.first.first == render_process_id)
430 it = plugin_load_decision_map_.erase(it);
431 else
432 ++it;
433 }
434 }
435 }
436
RegisterSchemeHandlerFactory(const CefString & scheme_name,const CefString & domain_name,CefRefPtr<CefSchemeHandlerFactory> factory)437 void CefBrowserContext::RegisterSchemeHandlerFactory(
438 const CefString& scheme_name,
439 const CefString& domain_name,
440 CefRefPtr<CefSchemeHandlerFactory> factory) {
441 CEF_POST_TASK(CEF_IOT,
442 base::Bind(&CefIOThreadState::RegisterSchemeHandlerFactory,
443 iothread_state_, scheme_name, domain_name, factory));
444 }
445
ClearSchemeHandlerFactories()446 void CefBrowserContext::ClearSchemeHandlerFactories() {
447 CEF_POST_TASK(CEF_IOT,
448 base::Bind(&CefIOThreadState::ClearSchemeHandlerFactories,
449 iothread_state_));
450 }
451
LoadExtension(const CefString & root_directory,CefRefPtr<CefDictionaryValue> manifest,CefRefPtr<CefExtensionHandler> handler,CefRefPtr<CefRequestContext> loader_context)452 void CefBrowserContext::LoadExtension(
453 const CefString& root_directory,
454 CefRefPtr<CefDictionaryValue> manifest,
455 CefRefPtr<CefExtensionHandler> handler,
456 CefRefPtr<CefRequestContext> loader_context) {
457 NOTIMPLEMENTED();
458 if (handler)
459 handler->OnExtensionLoadFailed(ERR_ABORTED);
460 }
461
GetExtensions(std::vector<CefString> & extension_ids)462 bool CefBrowserContext::GetExtensions(std::vector<CefString>& extension_ids) {
463 NOTIMPLEMENTED();
464 return false;
465 }
466
GetExtension(const CefString & extension_id)467 CefRefPtr<CefExtension> CefBrowserContext::GetExtension(
468 const CefString& extension_id) {
469 NOTIMPLEMENTED();
470 return nullptr;
471 }
472
UnloadExtension(const CefString & extension_id)473 bool CefBrowserContext::UnloadExtension(const CefString& extension_id) {
474 NOTIMPLEMENTED();
475 return false;
476 }
477
IsPrintPreviewSupported() const478 bool CefBrowserContext::IsPrintPreviewSupported() const {
479 return true;
480 }
481
GetNetworkContext()482 network::mojom::NetworkContext* CefBrowserContext::GetNetworkContext() {
483 CEF_REQUIRE_UIT();
484 auto browser_context = AsBrowserContext();
485 return browser_context->GetDefaultStoragePartition(browser_context)
486 ->GetNetworkContext();
487 }
488
GetMediaRouterManager()489 CefMediaRouterManager* CefBrowserContext::GetMediaRouterManager() {
490 CEF_REQUIRE_UIT();
491 if (!media_router_manager_) {
492 media_router_manager_.reset(new CefMediaRouterManager(AsBrowserContext()));
493 }
494 return media_router_manager_.get();
495 }
496
GetCookieableSchemes() const497 CefBrowserContext::CookieableSchemes CefBrowserContext::GetCookieableSchemes()
498 const {
499 CEF_REQUIRE_UIT();
500 if (cookieable_schemes_)
501 return cookieable_schemes_;
502
503 return GetGlobalCookieableSchemes();
504 }
505
506 // static
507 CefBrowserContext::CookieableSchemes
GetGlobalCookieableSchemes()508 CefBrowserContext::GetGlobalCookieableSchemes() {
509 CEF_REQUIRE_UIT();
510
511 static base::NoDestructor<CookieableSchemes> schemes(
512 []() -> CookieableSchemes {
513 const auto& settings = CefContext::Get()->settings();
514 if (settings.cookieable_schemes_list.length > 0 ||
515 settings.cookieable_schemes_exclude_defaults) {
516 return MakeSupportedSchemes(
517 CefString(&settings.cookieable_schemes_list),
518 !settings.cookieable_schemes_exclude_defaults);
519 }
520 return base::nullopt;
521 }());
522 return *schemes;
523 }
524