| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "ios/web/public/browser_state.h" |
| |
| #include <memory> |
| |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/guid.h" |
| #include "base/location.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/no_destructor.h" |
| #include "base/process/process_handle.h" |
| #include "base/task/post_task.h" |
| #include "base/token.h" |
| #include "ios/web/public/certificate_policy_cache.h" |
| #include "ios/web/public/network_context_owner.h" |
| #include "ios/web/public/service_manager_connection.h" |
| #include "ios/web/public/service_names.mojom.h" |
| #include "ios/web/public/web_client.h" |
| #include "ios/web/public/web_task_traits.h" |
| #include "ios/web/public/web_thread.h" |
| #include "ios/web/webui/url_data_manager_ios_backend.h" |
| #include "mojo/public/cpp/bindings/interface_request.h" |
| #include "mojo/public/cpp/bindings/remote.h" |
| #include "net/url_request/url_request_context_getter.h" |
| #include "net/url_request/url_request_context_getter_observer.h" |
| #include "services/network/network_context.h" |
| #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "services/service_manager/public/mojom/service.mojom.h" |
| |
| #if !defined(__has_feature) || !__has_feature(objc_arc) |
| #error "This file requires ARC support." |
| #endif |
| |
| namespace web { |
| namespace { |
| |
| // Maps service instance group IDs to associated BrowserState instances. |
| std::map<base::Token, BrowserState*>& GetInstanceGroupToBrowserState() { |
| static base::NoDestructor<std::map<base::Token, BrowserState*>> |
| instance_group_to_browser_state; |
| return *instance_group_to_browser_state; |
| } |
| |
| // Private key used for safe conversion of base::SupportsUserData to |
| // web::BrowserState in web::BrowserState::FromSupportsUserData. |
| const char kBrowserStateIdentifierKey[] = "BrowserStateIdentifierKey"; |
| |
| // Data key names. |
| const char kCertificatePolicyCacheKeyName[] = "cert_policy_cache"; |
| const char kServiceManagerConnection[] = "service-manager-connection"; |
| const char kServiceInstanceGroup[] = "service-instance-group"; |
| |
| // Wraps a CertificatePolicyCache as a SupportsUserData::Data; this is necessary |
| // since reference counted objects can't be user data. |
| struct CertificatePolicyCacheHandle : public base::SupportsUserData::Data { |
| explicit CertificatePolicyCacheHandle(CertificatePolicyCache* cache) |
| : policy_cache(cache) {} |
| |
| scoped_refptr<CertificatePolicyCache> policy_cache; |
| }; |
| |
| // Container for a service instance group ID to support association between |
| // BrowserStates and service instance groups. |
| class ServiceInstanceGroupHolder : public base::SupportsUserData::Data { |
| public: |
| explicit ServiceInstanceGroupHolder(const base::Token& instance_group) |
| : instance_group_(instance_group) {} |
| ~ServiceInstanceGroupHolder() override = default; |
| |
| const base::Token& instance_group() const { return instance_group_; } |
| |
| private: |
| base::Token instance_group_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ServiceInstanceGroupHolder); |
| }; |
| |
| // Eliminates the mapping from |browser_state|'s associated instance group ID |
| // (if any) to |browser_state|. |
| void RemoveBrowserStateFromInstanceGroupMap(BrowserState* browser_state) { |
| ServiceInstanceGroupHolder* holder = static_cast<ServiceInstanceGroupHolder*>( |
| browser_state->GetUserData(kServiceInstanceGroup)); |
| if (holder) { |
| GetInstanceGroupToBrowserState().erase(holder->instance_group()); |
| } |
| } |
| |
| // Container for a ServiceManagerConnection to support association between |
| // a BrowserState and the ServiceManagerConnection initiated on behalf of that |
| // BrowserState. |
| class BrowserStateServiceManagerConnectionHolder |
| : public base::SupportsUserData::Data { |
| public: |
| BrowserStateServiceManagerConnectionHolder( |
| BrowserState* browser_state, |
| service_manager::mojom::ServiceRequest request) |
| : browser_state_(browser_state), |
| service_manager_connection_(ServiceManagerConnection::Create( |
| std::move(request), |
| base::CreateSingleThreadTaskRunnerWithTraits({WebThread::IO}))) { |
| service_manager_connection_->SetDefaultServiceRequestHandler( |
| base::BindRepeating( |
| &BrowserStateServiceManagerConnectionHolder::OnServiceRequest, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| ~BrowserStateServiceManagerConnectionHolder() override {} |
| |
| ServiceManagerConnection* service_manager_connection() { |
| return service_manager_connection_.get(); |
| } |
| |
| private: |
| void OnServiceRequest(const std::string& service_name, |
| service_manager::mojom::ServiceRequest request) { |
| std::unique_ptr<service_manager::Service> service = |
| browser_state_->HandleServiceRequest(service_name, std::move(request)); |
| if (!service) { |
| LOG(ERROR) << "Ignoring request for unknown per-browser-state service:" |
| << service_name; |
| return; |
| } |
| |
| auto* raw_service = service.get(); |
| service->set_termination_closure(base::BindOnce( |
| &BrowserStateServiceManagerConnectionHolder::OnServiceQuit, |
| base::Unretained(this), raw_service)); |
| running_services_.emplace(raw_service, std::move(service)); |
| } |
| |
| void OnServiceQuit(service_manager::Service* service) { |
| running_services_.erase(service); |
| } |
| |
| BrowserState* const browser_state_; |
| std::unique_ptr<ServiceManagerConnection> service_manager_connection_; |
| std::map<service_manager::Service*, std::unique_ptr<service_manager::Service>> |
| running_services_; |
| |
| base::WeakPtrFactory<BrowserStateServiceManagerConnectionHolder> |
| weak_ptr_factory_{this}; |
| |
| DISALLOW_COPY_AND_ASSIGN(BrowserStateServiceManagerConnectionHolder); |
| }; |
| |
| } // namespace |
| |
| // static |
| scoped_refptr<CertificatePolicyCache> BrowserState::GetCertificatePolicyCache( |
| BrowserState* browser_state) { |
| DCHECK_CURRENTLY_ON(WebThread::UI); |
| if (!browser_state->GetUserData(kCertificatePolicyCacheKeyName)) { |
| browser_state->SetUserData(kCertificatePolicyCacheKeyName, |
| std::make_unique<CertificatePolicyCacheHandle>( |
| new CertificatePolicyCache())); |
| } |
| |
| CertificatePolicyCacheHandle* handle = |
| static_cast<CertificatePolicyCacheHandle*>( |
| browser_state->GetUserData(kCertificatePolicyCacheKeyName)); |
| return handle->policy_cache; |
| } |
| |
| BrowserState::BrowserState() : url_data_manager_ios_backend_(nullptr) { |
| // (Refcounted)?BrowserStateKeyedServiceFactories needs to be able to convert |
| // a base::SupportsUserData to a BrowserState. Moreover, since the factories |
| // may be passed a content::BrowserContext instead of a BrowserState, attach |
| // an empty object to this via a private key. |
| SetUserData(kBrowserStateIdentifierKey, |
| std::make_unique<SupportsUserData::Data>()); |
| |
| // Set up shared_url_loader_factory_ for lazy creation. |
| shared_url_loader_factory_ = |
| base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>( |
| base::BindOnce(&BrowserState::GetURLLoaderFactory, |
| base::Unretained(this) /* safe due to Detach call */)); |
| } |
| |
| BrowserState::~BrowserState() { |
| CHECK(GetUserData(kServiceInstanceGroup)) |
| << "Attempting to destroy a BrowserState that never called " |
| << "Initialize()"; |
| shared_url_loader_factory_->Detach(); |
| |
| if (network_context_) { |
| web::WebThread::DeleteSoon(web::WebThread::IO, FROM_HERE, |
| network_context_owner_.release()); |
| } |
| |
| RemoveBrowserStateFromInstanceGroupMap(this); |
| |
| // Delete the URLDataManagerIOSBackend instance on the IO thread if it has |
| // been created. Note that while this check can theoretically race with a |
| // call to |GetURLDataManagerIOSBackendOnIOThread()|, if any clients of this |
| // BrowserState are still accessing it on the IO thread at this point, |
| // they're going to have a bad time anyway. |
| if (url_data_manager_ios_backend_) { |
| bool posted = web::WebThread::DeleteSoon(web::WebThread::IO, FROM_HERE, |
| url_data_manager_ios_backend_); |
| if (!posted) |
| delete url_data_manager_ios_backend_; |
| } |
| } |
| |
| network::mojom::URLLoaderFactory* BrowserState::GetURLLoaderFactory() { |
| if (!url_loader_factory_) { |
| CreateNetworkContextIfNecessary(); |
| auto url_loader_factory_params = |
| network::mojom::URLLoaderFactoryParams::New(); |
| url_loader_factory_params->process_id = network::mojom::kBrowserProcessId; |
| url_loader_factory_params->is_corb_enabled = false; |
| network_context_->CreateURLLoaderFactory( |
| mojo::MakeRequest(&url_loader_factory_), |
| std::move(url_loader_factory_params)); |
| } |
| |
| return url_loader_factory_.get(); |
| } |
| |
| network::mojom::CookieManager* BrowserState::GetCookieManager() { |
| if (!cookie_manager_) { |
| CreateNetworkContextIfNecessary(); |
| network_context_->GetCookieManager(mojo::MakeRequest(&cookie_manager_)); |
| } |
| return cookie_manager_.get(); |
| } |
| |
| void BrowserState::GetProxyResolvingSocketFactory( |
| network::mojom::ProxyResolvingSocketFactoryRequest request) { |
| CreateNetworkContextIfNecessary(); |
| |
| network_context_->CreateProxyResolvingSocketFactory(std::move(request)); |
| } |
| |
| scoped_refptr<network::SharedURLLoaderFactory> |
| BrowserState::GetSharedURLLoaderFactory() { |
| return shared_url_loader_factory_; |
| } |
| |
| URLDataManagerIOSBackend* |
| BrowserState::GetURLDataManagerIOSBackendOnIOThread() { |
| DCHECK_CURRENTLY_ON(web::WebThread::IO); |
| if (!url_data_manager_ios_backend_) |
| url_data_manager_ios_backend_ = new URLDataManagerIOSBackend(); |
| return url_data_manager_ios_backend_; |
| } |
| |
| void BrowserState::CreateNetworkContextIfNecessary() { |
| if (network_context_owner_) |
| return; |
| |
| DCHECK(!network_context_); |
| |
| net::URLRequestContextGetter* request_context = GetRequestContext(); |
| DCHECK(request_context); |
| network::mojom::NetworkContextParamsPtr network_context_params = |
| network::mojom::NetworkContextParams::New(); |
| UpdateCorsExemptHeader(network_context_params.get()); |
| network_context_owner_ = std::make_unique<NetworkContextOwner>( |
| request_context, network_context_params->cors_exempt_header_list, |
| &network_context_); |
| } |
| |
| // static |
| BrowserState* BrowserState::FromSupportsUserData( |
| base::SupportsUserData* supports_user_data) { |
| if (!supports_user_data || |
| !supports_user_data->GetUserData(kBrowserStateIdentifierKey)) { |
| return nullptr; |
| } |
| return static_cast<BrowserState*>(supports_user_data); |
| } |
| |
| // static |
| void BrowserState::Initialize(BrowserState* browser_state, |
| const base::FilePath& path) { |
| base::Token new_group = base::Token::CreateRandom(); |
| |
| // Note: If the file service is ever used on iOS, code needs to be added here |
| // to have the file service associate |path| as the user dir of the instance |
| // group of |browser_state| (see corresponding code in |
| // content::BrowserContext::Initialize). crbug.com/739450 |
| |
| RemoveBrowserStateFromInstanceGroupMap(browser_state); |
| GetInstanceGroupToBrowserState()[new_group] = browser_state; |
| browser_state->SetUserData( |
| kServiceInstanceGroup, |
| std::make_unique<ServiceInstanceGroupHolder>(new_group)); |
| |
| ServiceManagerConnection* service_manager_connection = |
| ServiceManagerConnection::Get(); |
| if (service_manager_connection && base::ThreadTaskRunnerHandle::IsSet()) { |
| // NOTE: Many unit tests create a TestBrowserState without initializing |
| // Mojo or the global service manager connection. |
| |
| // Have the global service manager connection start an instance of the |
| // web_browser service that is associated with this BrowserState (via |
| // |new_group|). |
| mojo::PendingRemote<service_manager::mojom::Service> service; |
| auto service_receiver = service.InitWithNewPipeAndPassReceiver(); |
| |
| mojo::Remote<service_manager::mojom::ProcessMetadata> metadata; |
| service_manager::Identity identity(mojom::kBrowserServiceName, new_group, |
| base::Token{}, |
| base::Token::CreateRandom()); |
| service_manager_connection->GetConnector()->RegisterServiceInstance( |
| identity, std::move(service), metadata.BindNewPipeAndPassReceiver()); |
| metadata->SetPID(base::GetCurrentProcId()); |
| |
| auto connection_holder = |
| std::make_unique<BrowserStateServiceManagerConnectionHolder>( |
| browser_state, std::move(service_receiver)); |
| |
| ServiceManagerConnection* connection = |
| connection_holder->service_manager_connection(); |
| |
| browser_state->SetUserData(kServiceManagerConnection, |
| std::move(connection_holder)); |
| |
| connection->Start(); |
| } |
| } |
| |
| // static |
| const base::Token& BrowserState::GetServiceInstanceGroupFor( |
| BrowserState* browser_state) { |
| ServiceInstanceGroupHolder* holder = static_cast<ServiceInstanceGroupHolder*>( |
| browser_state->GetUserData(kServiceInstanceGroup)); |
| CHECK(holder) |
| << "Attempting to get the instance group for a BrowserState that was " |
| << "never Initialize()ed."; |
| return holder->instance_group(); |
| } |
| |
| // static |
| service_manager::Connector* BrowserState::GetConnectorFor( |
| BrowserState* browser_state) { |
| ServiceManagerConnection* connection = |
| GetServiceManagerConnectionFor(browser_state); |
| return connection ? connection->GetConnector() : nullptr; |
| } |
| |
| // static |
| ServiceManagerConnection* BrowserState::GetServiceManagerConnectionFor( |
| BrowserState* browser_state) { |
| BrowserStateServiceManagerConnectionHolder* connection_holder = |
| static_cast<BrowserStateServiceManagerConnectionHolder*>( |
| browser_state->GetUserData(kServiceManagerConnection)); |
| return connection_holder ? connection_holder->service_manager_connection() |
| : nullptr; |
| } |
| |
| std::unique_ptr<service_manager::Service> BrowserState::HandleServiceRequest( |
| const std::string& service_name, |
| service_manager::mojom::ServiceRequest request) { |
| return nullptr; |
| } |
| |
| } // namespace web |