| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/service_worker/embedded_worker_instance.h" |
| |
| #include <utility> |
| |
| #include "base/check_is_test.h" |
| #include "base/containers/contains.h" |
| #include "base/feature_list.h" |
| #include "base/functional/bind.h" |
| #include "base/functional/callback.h" |
| #include "base/functional/callback_helpers.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/observer_list.h" |
| #include "base/time/time.h" |
| #include "base/trace_event/trace_event.h" |
| #include "content/browser/bad_message.h" |
| #include "content/browser/data_url_loader_factory.h" |
| #include "content/browser/devtools/devtools_instrumentation.h" |
| #include "content/browser/devtools/network_service_devtools_observer.h" |
| #include "content/browser/devtools/service_worker_devtools_agent_host.h" |
| #include "content/browser/devtools/service_worker_devtools_manager.h" |
| #include "content/browser/loader/url_loader_factory_utils.h" |
| #include "content/browser/network/cross_origin_embedder_policy_reporter.h" |
| #include "content/browser/process_lock.h" |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| #include "content/browser/security/dip/document_isolation_policy_reporter.h" |
| #include "content/browser/service_worker/service_worker_consts.h" |
| #include "content/browser/service_worker/service_worker_content_settings_proxy_impl.h" |
| #include "content/browser/service_worker/service_worker_context_core.h" |
| #include "content/browser/service_worker/service_worker_context_wrapper.h" |
| #include "content/browser/service_worker/service_worker_host.h" |
| #include "content/browser/service_worker/service_worker_script_loader_factory.h" |
| #include "content/browser/storage_partition_impl.h" |
| #include "content/browser/url_loader_factory_params_helper.h" |
| #include "content/browser/usb/web_usb_service_impl.h" |
| #include "content/common/content_switches_internal.h" |
| #include "content/common/url_schemes.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/child_process_host.h" |
| #include "content/public/browser/hid_delegate.h" |
| #include "content/public/browser/usb_delegate.h" |
| #include "content/public/browser/web_ui_url_loader_factory.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_features.h" |
| #include "content/public/common/content_switches.h" |
| #include "content/public/common/url_constants.h" |
| #include "ipc/constants.mojom.h" |
| #include "mojo/public/cpp/bindings/self_owned_receiver.h" |
| #include "net/base/isolation_info.h" |
| #include "net/base/network_isolation_key.h" |
| #include "net/cookies/site_for_cookies.h" |
| #include "services/metrics/public/cpp/ukm_source_id.h" |
| #include "services/network/public/mojom/client_security_state.mojom.h" |
| #include "third_party/blink/public/common/features.h" |
| #include "third_party/blink/public/common/service_worker/embedded_worker_status.h" |
| #include "third_party/blink/public/mojom/loader/url_loader_factory_bundle.mojom.h" |
| #include "third_party/blink/public/mojom/renderer_preference_watcher.mojom.h" |
| #include "third_party/blink/public/mojom/service_worker/service_worker_object.mojom.h" |
| #include "url/gurl.h" |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| #include "content/browser/hid/hid_service.h" |
| #endif |
| |
| // TODO(crbug.com/40568315): Much of this file, which dealt with thread hops |
| // between UI and IO, can likely be simplified when the service worker core |
| // thread moves to the UI thread. |
| |
| namespace content { |
| |
| namespace { |
| |
| // When a service worker version's failure count exceeds |
| // |kMaxSameProcessFailureCount|, the embedded worker is forced to start in a |
| // new process. |
| const int kMaxSameProcessFailureCount = 2; |
| |
| const char kServiceWorkerTerminationCanceledMesage[] = |
| "Service Worker termination by a timeout timer was canceled because " |
| "DevTools is attached."; |
| |
| bool HasSentStartWorker(EmbeddedWorkerInstance::StartingPhase phase) { |
| switch (phase) { |
| case EmbeddedWorkerInstance::NOT_STARTING: |
| case EmbeddedWorkerInstance::ALLOCATING_PROCESS: |
| return false; |
| case EmbeddedWorkerInstance::SENT_START_WORKER: |
| case EmbeddedWorkerInstance::SCRIPT_DOWNLOADING: |
| case EmbeddedWorkerInstance::SCRIPT_STREAMING: |
| case EmbeddedWorkerInstance::SCRIPT_LOADED: |
| case EmbeddedWorkerInstance::SCRIPT_EVALUATION: |
| return true; |
| case EmbeddedWorkerInstance::STARTING_PHASE_MAX_VALUE: |
| NOTREACHED(); |
| } |
| return false; |
| } |
| |
| void NotifyForegroundServiceWorker(bool added, int process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| RenderProcessHost* rph = RenderProcessHost::FromID(process_id); |
| if (!rph) |
| return; |
| |
| if (added) |
| rph->OnForegroundServiceWorkerAdded(); |
| else |
| rph->OnForegroundServiceWorkerRemoved(); |
| } |
| |
| } // namespace |
| |
| // Created when a renderer process is allocated for the worker. It is destroyed |
| // when the worker stops, and this proxies notifications to DevToolsManager. |
| // Owned by EmbeddedWorkerInstance. |
| // |
| // TODO(crbug.com/40725202): Remove this because we no longer need |
| // proxying the notifications because there's no thread hopping thanks to |
| // ServiceWorkerOnUI. |
| class EmbeddedWorkerInstance::DevToolsProxy { |
| public: |
| DevToolsProxy(int process_id, |
| int agent_route_id, |
| const base::UnguessableToken& devtools_id) |
| : process_id_(process_id), |
| agent_route_id_(agent_route_id), |
| devtools_id_(devtools_id) {} |
| |
| DevToolsProxy(const DevToolsProxy&) = delete; |
| DevToolsProxy& operator=(const DevToolsProxy&) = delete; |
| |
| ~DevToolsProxy() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| ServiceWorkerDevToolsManager::GetInstance()->WorkerStopped(process_id_, |
| agent_route_id_); |
| } |
| |
| void NotifyWorkerReadyForInspection( |
| mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote, |
| mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| ServiceWorkerDevToolsManager::GetInstance()->WorkerReadyForInspection( |
| process_id_, agent_route_id_, std::move(agent_remote), |
| std::move(host_receiver)); |
| } |
| |
| void NotifyWorkerVersionInstalled() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| ServiceWorkerDevToolsManager::GetInstance()->WorkerVersionInstalled( |
| process_id_, agent_route_id_); |
| } |
| |
| bool ShouldNotifyWorkerStopIgnored() const { |
| return !worker_stop_ignored_notified_; |
| } |
| |
| void WorkerStopIgnoredNotified() { worker_stop_ignored_notified_ = true; } |
| |
| int agent_route_id() const { return agent_route_id_; } |
| |
| const base::UnguessableToken& devtools_id() const { return devtools_id_; } |
| |
| private: |
| const int process_id_; |
| const int agent_route_id_; |
| const base::UnguessableToken devtools_id_; |
| bool worker_stop_ignored_notified_ = false; |
| }; |
| |
| // A handle for a renderer process managed by ServiceWorkerProcessManager. |
| // |
| // TODO(crbug.com/40725202): Remove this as a clean up of |
| // ServiceWorkerOnUI. |
| class EmbeddedWorkerInstance::WorkerProcessHandle { |
| public: |
| WorkerProcessHandle( |
| const base::WeakPtr<ServiceWorkerProcessManager>& process_manager, |
| int embedded_worker_id, |
| int process_id) |
| : process_manager_(process_manager), |
| embedded_worker_id_(embedded_worker_id), |
| process_id_(process_id) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK_NE(ChildProcessHost::kInvalidUniqueID, process_id_); |
| } |
| |
| WorkerProcessHandle(const WorkerProcessHandle&) = delete; |
| WorkerProcessHandle& operator=(const WorkerProcessHandle&) = delete; |
| |
| ~WorkerProcessHandle() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| process_manager_->ReleaseWorkerProcess(embedded_worker_id_); |
| } |
| |
| int process_id() const { return process_id_; } |
| |
| private: |
| base::WeakPtr<ServiceWorkerProcessManager> process_manager_; |
| |
| const int embedded_worker_id_; |
| const int process_id_; |
| }; |
| |
| // Info that is recorded as UMA on OnStarted(). |
| struct EmbeddedWorkerInstance::StartInfo { |
| StartInfo(bool is_installed, |
| bool skip_recording_startup_time, |
| base::TimeTicks start_time) |
| : is_installed(is_installed), |
| skip_recording_startup_time(skip_recording_startup_time), |
| start_time(start_time) {} |
| ~StartInfo() = default; |
| |
| // Used for UMA. |
| const bool is_installed; |
| bool skip_recording_startup_time; |
| const base::TimeTicks start_time; |
| base::TimeTicks start_worker_sent_time; |
| }; |
| |
| EmbeddedWorkerInstance::~EmbeddedWorkerInstance() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| in_dtor_ = true; |
| ReleaseProcess(); |
| } |
| |
| void EmbeddedWorkerInstance::Start( |
| blink::mojom::EmbeddedWorkerStartParamsPtr params, |
| StatusCallback callback) { |
| TRACE_EVENT1("ServiceWorker", "EmbeddedWorkerInstance::Start", "script_url", |
| params->script_url.spec()); |
| |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(context_); |
| restart_count_++; |
| DCHECK_EQ(blink::EmbeddedWorkerStatus::kStopped, status_); |
| |
| DCHECK_NE(blink::mojom::kInvalidServiceWorkerVersionId, |
| params->service_worker_version_id); |
| |
| auto start_time = base::TimeTicks::Now(); |
| status_ = blink::EmbeddedWorkerStatus::kStarting; |
| starting_phase_ = ALLOCATING_PROCESS; |
| network_accessed_for_script_ = false; |
| |
| for (auto& observer : listener_list_) |
| observer.OnStarting(); |
| |
| // service_worker_route_id will be set later in SetupOnUIThread |
| params->service_worker_route_id = IPC::mojom::kRoutingIdNone; |
| params->wait_for_debugger = false; |
| params->subresource_loader_updater = |
| subresource_loader_updater_.BindNewPipeAndPassReceiver(); |
| |
| // TODO(crbug.com/41467868): Consider a reset flow since new mojo types |
| // check is_bound strictly. |
| client_.reset(); |
| |
| auto process_info = |
| std::make_unique<ServiceWorkerProcessManager::AllocatedProcessInfo>(); |
| std::unique_ptr<EmbeddedWorkerInstance::DevToolsProxy> devtools_proxy; |
| std::unique_ptr<blink::PendingURLLoaderFactoryBundle> |
| factory_bundle_for_new_scripts; |
| std::unique_ptr<blink::PendingURLLoaderFactoryBundle> |
| factory_bundle_for_renderer; |
| mojo::PendingReceiver<blink::mojom::ReportingObserver> |
| reporting_observer_receiver; |
| |
| ServiceWorkerProcessManager* process_manager = context_->process_manager(); |
| if (!process_manager) { |
| OnSetupFailed(std::move(callback), |
| blink::ServiceWorkerStatusCode::kErrorAbort); |
| return; |
| } |
| |
| // Get a process. |
| bool can_use_existing_process = |
| context_->GetVersionFailureCount(params->service_worker_version_id) < |
| kMaxSameProcessFailureCount; |
| blink::ServiceWorkerStatusCode status = |
| process_manager->AllocateWorkerProcess( |
| embedded_worker_id(), params->script_url, |
| owner_version_->cross_origin_embedder_policy_value(), |
| can_use_existing_process, owner_version_->ancestor_frame_type(), |
| process_info.get()); |
| if (status != blink::ServiceWorkerStatusCode::kOk) { |
| OnSetupFailed(std::move(callback), status); |
| return; |
| } |
| const int process_id = process_info->process_id; |
| RenderProcessHost* rph = RenderProcessHost::FromID(process_id); |
| // TODO(falken): This CHECK should no longer fail, so turn to a DCHECK it if |
| // crash reports agree. Consider also checking for |
| // rph->IsInitializedAndNotDead(). |
| CHECK(rph); |
| |
| // Let the process know that it now has an instance of an origin that matches |
| // the worker's URL. This is needed so that the worker process can access data |
| // belonging to that origin. |
| const url::Origin origin = url::Origin::Create(params->script_url); |
| ChildProcessSecurityPolicyImpl::GetInstance()->AddCommittedOrigin(process_id, |
| origin); |
| |
| rph->BindReceiver(client_.BindNewPipeAndPassReceiver()); |
| client_.set_disconnect_handler( |
| base::BindOnce(&EmbeddedWorkerInstance::Detach, base::Unretained(this))); |
| |
| { |
| auto* storage_partition = |
| static_cast<StoragePartitionImpl*>(rph->GetStoragePartition()); |
| |
| params->cors_exempt_header_list = |
| storage_partition->cors_exempt_header_list(); |
| |
| // Create COEP reporter if COEP value is already available (= this worker is |
| // not a worker which is going to be newly registered). The Mojo remote |
| // `coep_reporter_` has the onwership of the instance. The `coep_reporter` |
| // might be kept null when the COEP value is not known because the main |
| // script has not been loaded yet. In that case, it will be bound after the |
| // main script is loaded. |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| coep_reporter_for_devtools = GetCoepReporterInternal(storage_partition); |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| coep_reporter_for_scripts = GetCoepReporterInternal(storage_partition); |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| coep_reporter_for_subresources = |
| GetCoepReporterInternal(storage_partition); |
| |
| // Create the DIP reporter if the DocumentIsolationPolicy value is already |
| // available. |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| dip_reporter = GetDipReporterInternal(storage_partition); |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| dip_reporter_for_devtools = GetDipReporterInternal(storage_partition); |
| |
| network::mojom::ClientSecurityStatePtr client_security_state = |
| owner_version_->BuildClientSecurityState(); |
| |
| // Pause initializing global scope (https://crbug.com/1431792). |
| if (!pause_initializing_global_scope_) { |
| owner_version_->InitializeGlobalScope(); |
| } |
| |
| // Register to DevTools and update params accordingly. |
| const int routing_id = rph->GetNextRoutingID(); |
| ServiceWorkerDevToolsManager::GetInstance()->WorkerStarting( |
| process_id, routing_id, context_->wrapper(), |
| params->service_worker_version_id, params->script_url, params->scope, |
| params->is_installed, client_security_state.Clone(), |
| std::move(coep_reporter_for_devtools), |
| std::move(dip_reporter_for_devtools), ¶ms->devtools_worker_token, |
| ¶ms->wait_for_debugger); |
| params->service_worker_route_id = routing_id; |
| // Create DevToolsProxy here to ensure that the WorkerCreated() call is |
| // balanced by DevToolsProxy's destructor calling WorkerStopped(). |
| devtools_proxy = std::make_unique<EmbeddedWorkerInstance::DevToolsProxy>( |
| process_id, routing_id, params->devtools_worker_token); |
| |
| // Create factory bundles for this worker to do loading. These bundles don't |
| // support reconnection to the network service, see below comments. |
| |
| // The bundle for new scripts is passed to ServiceWorkerScriptLoaderFactory |
| // and used to request non-installed service worker scripts. It's only |
| // needed for non-installed workers. It's OK to not support reconnection to |
| // the network service because it can only used until the service worker |
| // reaches the 'installed' state. |
| if (!params->is_installed) { |
| factory_bundle_for_new_scripts = CreateFactoryBundle( |
| rph, routing_id, owner_version_->key(), client_security_state.Clone(), |
| std::move(coep_reporter_for_scripts), std::move(dip_reporter), |
| ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript, |
| params->devtools_worker_token.ToString()); |
| } |
| |
| // The bundle for the renderer is passed to the service worker, and |
| // used for subresource loading from the service worker (i.e., fetch()). |
| // It's OK to not support reconnection to the network service because the |
| // service worker terminates itself when the connection breaks, so a new |
| // instance can be started. |
| factory_bundle_for_renderer = CreateFactoryBundle( |
| rph, routing_id, owner_version_->key(), |
| std::move(client_security_state), |
| std::move(coep_reporter_for_subresources), std::move(dip_reporter), |
| ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerSubResource, |
| params->devtools_worker_token.ToString()); |
| } |
| |
| // To enable runtime features, the render process must be locked to the site. |
| // These features are highly privileged, so the renderer process with such |
| // features enabled shouldn't be used for other sites. |
| // |
| // WebUI schemes are process isolated already. To isolate other sites, the |
| // embedder can override ContentBrowserClient::ShouldLockProcessToSite(). |
| if (rph->GetProcessLock().IsLockedToSite()) { |
| GetContentClient() |
| ->browser() |
| ->UpdateEnabledBlinkRuntimeFeaturesInIsolatedWorker( |
| context_->wrapper()->browser_context(), params->script_url, |
| params->forced_enabled_runtime_features); |
| } |
| CHECK(params->forced_enabled_runtime_features.empty() || |
| rph->GetProcessLock().IsLockedToSite()); |
| |
| // TODO(crbug.com/40584626): Support changes to blink::RendererPreferences |
| // while the worker is running. |
| DCHECK(context_->wrapper()->browser_context() || |
| process_manager->IsShutdown()); |
| params->renderer_preferences = blink::RendererPreferences(); |
| GetContentClient()->browser()->UpdateRendererPreferencesForWorker( |
| context_->wrapper()->browser_context(), ¶ms->renderer_preferences); |
| |
| { |
| // Create a RendererPreferenceWatcher to observe updates in the preferences. |
| mojo::PendingRemote<blink::mojom::RendererPreferenceWatcher> watcher_remote; |
| params->preference_watcher_receiver = |
| watcher_remote.InitWithNewPipeAndPassReceiver(); |
| GetContentClient()->browser()->RegisterRendererPreferenceWatcher( |
| context_->wrapper()->browser_context(), std::move(watcher_remote)); |
| } |
| |
| // If we allocated a process, WorkerProcessHandle has to be created before |
| // returning to ensure the process is eventually released. |
| auto process_handle = std::make_unique<WorkerProcessHandle>( |
| process_manager->GetWeakPtr(), embedded_worker_id(), |
| process_info->process_id); |
| |
| ServiceWorkerMetrics::StartSituation start_situation = |
| process_info->start_situation; |
| if (!GetContentClient()->browser()->IsBrowserStartupComplete()) |
| start_situation = ServiceWorkerMetrics::StartSituation::DURING_STARTUP; |
| |
| // Notify the instance that a process is allocated. |
| OnProcessAllocated(std::move(process_handle), start_situation); |
| |
| // Notify the instance that it is registered to the DevTools manager. |
| OnRegisteredToDevToolsManager(std::move(devtools_proxy)); |
| |
| // Send the factory bundle for subresource loading from the service worker |
| // (i.e. fetch()). |
| DCHECK(factory_bundle_for_renderer); |
| params->subresource_loader_factories = std::move(factory_bundle_for_renderer); |
| |
| // Build the URLLoaderFactory for loading new scripts, it's only needed if |
| // this is a non-installed service worker. |
| DCHECK(factory_bundle_for_new_scripts || params->is_installed); |
| if (factory_bundle_for_new_scripts) { |
| params->provider_info->script_loader_factory_remote = |
| MakeScriptLoaderFactoryRemote( |
| std::move(factory_bundle_for_new_scripts)); |
| } |
| |
| // Create cache storage now as an optimization, so the service worker can |
| // use the Cache Storage API immediately on startup. |
| // Without COEP, BindCacheStorage won't bind the cache storage, |
| // which make cache storage set up in the install handler get stuck. |
| // Since this is a performance improvement feature, fallback to the slow |
| // path should be better than making the execution get stuck. |
| if (owner_version_->cross_origin_embedder_policy()) { |
| BindCacheStorage( |
| params->provider_info->cache_storage.InitWithNewPipeAndPassReceiver(), |
| storage::BucketLocator::ForDefaultBucket(owner_version_->key())); |
| } |
| |
| inflight_start_info_ = std::make_unique<StartInfo>( |
| params->is_installed, params->wait_for_debugger, start_time); |
| |
| // Send receivers for COEP and DocumentIsolationPolicy ReportObserver events. |
| params->coep_reporting_observer = |
| std::move(coep_reporting_observer_receiver_); |
| params->dip_reporting_observer = std::move(dip_reporting_observer_receiver_); |
| |
| SendStartWorker(std::move(params)); |
| std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk); |
| } |
| |
| void EmbeddedWorkerInstance::Stop() { |
| TRACE_EVENT1("ServiceWorker", "EmbeddedWorkerInstance::Stop", "script_url", |
| owner_version_->script_url().spec()); |
| DCHECK(status_ == blink::EmbeddedWorkerStatus::kStarting || |
| status_ == blink::EmbeddedWorkerStatus::kRunning) |
| << static_cast<int>(status_); |
| |
| // Discard the info for starting a worker because this worker is going to be |
| // stopped. |
| inflight_start_info_.reset(); |
| |
| // Don't send the StopWorker message if the StartWorker message hasn't |
| // been sent. |
| if (status_ == blink::EmbeddedWorkerStatus::kStarting && |
| !HasSentStartWorker(starting_phase())) { |
| base::WeakPtr<EmbeddedWorkerInstance> weak_this = |
| weak_factory_.GetWeakPtr(); |
| ReleaseProcess(); |
| if (!weak_this) { |
| return; |
| } |
| for (auto& observer : listener_list_) |
| observer.OnStopped( |
| blink::EmbeddedWorkerStatus::kStarting /* old_status */); |
| return; |
| } |
| |
| client_->StopWorker(); |
| status_ = blink::EmbeddedWorkerStatus::kStopping; |
| for (auto& observer : listener_list_) |
| observer.OnStopping(); |
| } |
| |
| void EmbeddedWorkerInstance::StopIfNotAttachedToDevTools() { |
| if (devtools_attached_) { |
| if (devtools_proxy_) { |
| // Check ShouldNotifyWorkerStopIgnored not to show the same message |
| // multiple times in DevTools. |
| if (devtools_proxy_->ShouldNotifyWorkerStopIgnored()) { |
| owner_version_->MaybeReportConsoleMessageToInternals( |
| blink::mojom::ConsoleMessageLevel::kVerbose, |
| kServiceWorkerTerminationCanceledMesage); |
| devtools_proxy_->WorkerStopIgnoredNotified(); |
| } |
| } |
| return; |
| } |
| Stop(); |
| } |
| |
| EmbeddedWorkerInstance::EmbeddedWorkerInstance( |
| ServiceWorkerVersion* owner_version) |
| : context_(owner_version->context()), |
| owner_version_(owner_version), |
| embedded_worker_id_(context_->GetNextEmbeddedWorkerId()), |
| status_(blink::EmbeddedWorkerStatus::kStopped), |
| starting_phase_(NOT_STARTING), |
| restart_count_(0), |
| thread_id_(ServiceWorkerConsts::kInvalidEmbeddedWorkerThreadId), |
| devtools_attached_(false), |
| network_accessed_for_script_(false), |
| foreground_notified_(false) { |
| DCHECK(owner_version_); |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(context_); |
| } |
| |
| void EmbeddedWorkerInstance::OnProcessAllocated( |
| std::unique_ptr<WorkerProcessHandle> handle, |
| ServiceWorkerMetrics::StartSituation start_situation) { |
| DCHECK_EQ(blink::EmbeddedWorkerStatus::kStarting, status_); |
| DCHECK(!process_handle_); |
| |
| process_handle_ = std::move(handle); |
| |
| UpdateForegroundPriority(); |
| |
| start_situation_ = start_situation; |
| for (auto& observer : listener_list_) |
| observer.OnProcessAllocated(); |
| } |
| |
| void EmbeddedWorkerInstance::OnRegisteredToDevToolsManager( |
| std::unique_ptr<DevToolsProxy> devtools_proxy) { |
| if (devtools_proxy) { |
| DCHECK(!devtools_proxy_); |
| devtools_proxy_ = std::move(devtools_proxy); |
| } |
| for (auto& observer : listener_list_) |
| observer.OnRegisteredToDevToolsManager(); |
| } |
| |
| void EmbeddedWorkerInstance::SendStartWorker( |
| blink::mojom::EmbeddedWorkerStartParamsPtr params) { |
| DCHECK(context_); |
| DCHECK(params->service_worker_receiver.is_valid()); |
| DCHECK(params->controller_receiver.is_valid()); |
| DCHECK(!instance_host_receiver_.is_bound()); |
| |
| instance_host_receiver_.Bind( |
| params->instance_host.InitWithNewEndpointAndPassReceiver()); |
| |
| content_settings_ = std::make_unique<ServiceWorkerContentSettingsProxyImpl>( |
| params->script_url, base::WrapRefCounted(context_->wrapper()), |
| params->content_settings_proxy.InitWithNewPipeAndPassReceiver(), |
| params->storage_key); |
| |
| const bool is_script_streaming = !params->installed_scripts_info.is_null(); |
| inflight_start_info_->start_worker_sent_time = base::TimeTicks::Now(); |
| |
| // The host must be alive as long as |params->provider_info| is alive. |
| owner_version_->worker_host()->CompleteStartWorkerPreparation( |
| process_id(), |
| params->provider_info->browser_interface_broker |
| .InitWithNewPipeAndPassReceiver(), |
| params->interface_provider.InitWithNewPipeAndPassRemote()); |
| |
| // TODO(bashi): Always pass a valid outside fetch client settings object. |
| // See crbug.com/937177. |
| if (!params->outside_fetch_client_settings_object) { |
| params->outside_fetch_client_settings_object = |
| blink::mojom::FetchClientSettingsObject::New( |
| network::mojom::ReferrerPolicy::kDefault, |
| /*outgoing_referrer=*/params->script_url, |
| blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade); |
| } |
| |
| client_->StartWorker(std::move(params)); |
| |
| starting_phase_ = is_script_streaming ? SCRIPT_STREAMING : SENT_START_WORKER; |
| for (auto& observer : listener_list_) |
| observer.OnStartWorkerMessageSent(); |
| } |
| |
| void EmbeddedWorkerInstance::RequestTermination( |
| RequestTerminationCallback callback) { |
| if (status() != blink::EmbeddedWorkerStatus::kRunning && |
| status() != blink::EmbeddedWorkerStatus::kStopping) { |
| mojo::ReportBadMessage( |
| "Invalid termination request: Termination should be requested during " |
| "running or stopping"); |
| std::move(callback).Run(true /* will_be_terminated */); |
| return; |
| } |
| const bool will_be_terminated = owner_version_->OnRequestTermination(); |
| TRACE_EVENT1("ServiceWorker", "EmbeddedWorkerInstance::RequestTermination", |
| "will_be_terminated", will_be_terminated); |
| |
| std::move(callback).Run(will_be_terminated); |
| } |
| |
| void EmbeddedWorkerInstance::CountFeature(blink::mojom::WebFeature feature) { |
| owner_version_->CountFeature(feature); |
| } |
| |
| void EmbeddedWorkerInstance::OnReadyForInspection( |
| mojo::PendingRemote<blink::mojom::DevToolsAgent> agent_remote, |
| mojo::PendingReceiver<blink::mojom::DevToolsAgentHost> host_receiver) { |
| if (!devtools_proxy_) |
| return; |
| devtools_proxy_->NotifyWorkerReadyForInspection(std::move(agent_remote), |
| std::move(host_receiver)); |
| } |
| |
| void EmbeddedWorkerInstance::OnScriptLoaded() { |
| if (!inflight_start_info_) |
| return; |
| |
| // Renderer side has started to launch the worker thread. |
| starting_phase_ = SCRIPT_LOADED; |
| |
| for (auto& observer : listener_list_) { |
| observer.OnScriptLoaded(); |
| } |
| } |
| |
| void EmbeddedWorkerInstance::OnWorkerVersionInstalled() { |
| if (devtools_proxy_) |
| devtools_proxy_->NotifyWorkerVersionInstalled(); |
| } |
| |
| void EmbeddedWorkerInstance::OnWorkerVersionDoomed() { |
| if (!context_) { |
| return; |
| } |
| ServiceWorkerDevToolsManager::GetInstance()->WorkerVersionDoomed( |
| process_id(), worker_devtools_agent_route_id(), |
| base::WrapRefCounted(context_->wrapper()), owner_version_->version_id()); |
| } |
| |
| void EmbeddedWorkerInstance::OnScriptEvaluationStart() { |
| if (!inflight_start_info_) |
| return; |
| |
| starting_phase_ = SCRIPT_EVALUATION; |
| for (auto& observer : listener_list_) |
| observer.OnScriptEvaluationStart(); |
| } |
| |
| void EmbeddedWorkerInstance::OnStarted( |
| blink::mojom::ServiceWorkerStartStatus start_status, |
| blink::mojom::ServiceWorkerFetchHandlerType fetch_handler_type, |
| bool has_hid_event_handlers, |
| bool has_usb_event_handlers, |
| int thread_id, |
| blink::mojom::EmbeddedWorkerStartTimingPtr start_timing) { |
| TRACE_EVENT0("ServiceWorker", "EmbeddedWorkerInstance::OnStarted"); |
| if (!(start_timing->start_worker_received_time <= |
| start_timing->script_evaluation_start_time && |
| start_timing->script_evaluation_start_time <= |
| start_timing->script_evaluation_end_time)) { |
| mojo::ReportBadMessage("EWI_BAD_START_TIMING"); |
| return; |
| } |
| |
| // Stop was requested before OnStarted was sent back from the worker. Just |
| // pretend startup didn't happen, so observers don't try to use the running |
| // worker as it will stop soon. |
| if (status_ == blink::EmbeddedWorkerStatus::kStopping) { |
| return; |
| } |
| |
| if (inflight_start_info_->is_installed && |
| !inflight_start_info_->skip_recording_startup_time) { |
| ServiceWorkerMetrics::StartTimes times; |
| times.local_start = inflight_start_info_->start_time; |
| times.local_start_worker_sent = |
| inflight_start_info_->start_worker_sent_time; |
| times.remote_start_worker_received = |
| start_timing->start_worker_received_time; |
| times.remote_script_evaluation_start = |
| start_timing->script_evaluation_start_time; |
| times.remote_script_evaluation_end = |
| start_timing->script_evaluation_end_time; |
| times.local_end = base::TimeTicks::Now(); |
| |
| ServiceWorkerMetrics::RecordStartWorkerTiming(times, start_situation_); |
| } |
| |
| DCHECK_EQ(blink::EmbeddedWorkerStatus::kStarting, status_); |
| status_ = blink::EmbeddedWorkerStatus::kRunning; |
| pause_initializing_global_scope_ = false; |
| thread_id_ = thread_id; |
| inflight_start_info_.reset(); |
| for (auto& observer : listener_list_) { |
| observer.OnStarted(start_status, fetch_handler_type, has_hid_event_handlers, |
| has_usb_event_handlers); |
| // |this| may be destroyed here. Fortunately we know there is only one |
| // observer in production code. |
| } |
| } |
| |
| void EmbeddedWorkerInstance::OnStopped() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| blink::EmbeddedWorkerStatus old_status = status_; |
| base::WeakPtr<EmbeddedWorkerInstance> weak_this = weak_factory_.GetWeakPtr(); |
| ReleaseProcess(); |
| if (!weak_this) { |
| return; |
| } |
| for (auto& observer : listener_list_) |
| observer.OnStopped(old_status); |
| } |
| |
| void EmbeddedWorkerInstance::Detach() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (status() == blink::EmbeddedWorkerStatus::kStopped) { |
| return; |
| } |
| |
| blink::EmbeddedWorkerStatus old_status = status_; |
| base::WeakPtr<EmbeddedWorkerInstance> weak_this = weak_factory_.GetWeakPtr(); |
| ReleaseProcess(); |
| if (!weak_this) { |
| return; |
| } |
| for (auto& observer : listener_list_) |
| observer.OnDetached(old_status); |
| } |
| |
| void EmbeddedWorkerInstance::UpdateForegroundPriority() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (status() == blink::EmbeddedWorkerStatus::kStopping) { |
| return; |
| } |
| |
| if (process_handle_ && |
| owner_version_->ShouldRequireForegroundPriority(process_id())) { |
| NotifyForegroundServiceWorkerAdded(); |
| } else { |
| NotifyForegroundServiceWorkerRemoved(); |
| } |
| } |
| |
| void EmbeddedWorkerInstance::UpdateLoaderFactories( |
| std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle, |
| std::unique_ptr<blink::PendingURLLoaderFactoryBundle> subresource_bundle) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| DCHECK(subresource_loader_updater_.is_bound()); |
| |
| // It's set to nullptr when the caller wants to update script bundle only. |
| if (subresource_bundle) { |
| subresource_loader_updater_->UpdateSubresourceLoaderFactories( |
| std::move(subresource_bundle)); |
| } |
| |
| if (script_loader_factory_) { |
| static_cast<ServiceWorkerScriptLoaderFactory*>( |
| script_loader_factory_->impl()) |
| ->Update(base::MakeRefCounted<blink::URLLoaderFactoryBundle>( |
| std::move(script_bundle))); |
| } |
| } |
| |
| void EmbeddedWorkerInstance::BindCacheStorage( |
| mojo::PendingReceiver<blink::mojom::CacheStorage> receiver, |
| const storage::BucketLocator& bucket_locator) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| pending_cache_storage_requests_.emplace_back(std::move(receiver), |
| bucket_locator); |
| BindCacheStorageInternal(); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) |
| void EmbeddedWorkerInstance::BindHidService( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::HidService> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| HidDelegate* hid_delegate = GetContentClient()->browser()->GetHidDelegate(); |
| if (!hid_delegate) { |
| return; |
| } |
| if (hid_delegate->IsServiceWorkerAllowedForOrigin(origin)) { |
| HidService::Create(owner_version_->GetWeakPtr(), origin, |
| std::move(receiver)); |
| } |
| } |
| #endif // !BUILDFLAG(IS_ANDROID) |
| |
| void EmbeddedWorkerInstance::BindUsbService( |
| const url::Origin& origin, |
| mojo::PendingReceiver<blink::mojom::WebUsbService> receiver) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| UsbDelegate* usb_delegate = GetContentClient()->browser()->GetUsbDelegate(); |
| if (!usb_delegate) { |
| return; |
| } |
| if (usb_delegate->IsServiceWorkerAllowedForOrigin(origin)) { |
| WebUsbServiceImpl::Create(owner_version_->GetWeakPtr(), origin, |
| std::move(receiver)); |
| } |
| } |
| |
| base::WeakPtr<EmbeddedWorkerInstance> EmbeddedWorkerInstance::AsWeakPtr() { |
| return weak_factory_.GetWeakPtr(); |
| } |
| |
| // Returns a factory bundle for doing loads on behalf of the specified |rph| and |
| // |origin|. The returned bundle has a default factory that goes to network and |
| // it may also include scheme-specific factories that don't go to network. |
| // |
| // The network factory does not support reconnection to the network service. |
| std::unique_ptr<blink::PendingURLLoaderFactoryBundle> |
| EmbeddedWorkerInstance::CreateFactoryBundle( |
| RenderProcessHost* rph, |
| int routing_id, |
| const blink::StorageKey& storage_key, |
| network::mojom::ClientSecurityStatePtr client_security_state, |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| coep_reporter, |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| dip_reporter, |
| ContentBrowserClient::URLLoaderFactoryType factory_type, |
| const std::string& devtools_worker_token) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| auto factory_bundle = |
| std::make_unique<blink::PendingURLLoaderFactoryBundle>(); |
| mojo::PendingReceiver<network::mojom::URLLoaderFactory> |
| default_factory_receiver = factory_bundle->pending_default_factory() |
| .InitWithNewPipeAndPassReceiver(); |
| |
| // In certain tests, the worker is started before response headers (and thus |
| // the client security state) are known. Use a default value instead. |
| if (!client_security_state) { |
| client_security_state = network::mojom::ClientSecurityState::New(); |
| } |
| |
| const url::Origin& origin = storage_key.origin(); |
| const net::IsolationInfo& isolation_info = |
| storage_key.ToPartialNetIsolationInfo(); |
| |
| DCHECK(factory_type == |
| ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript || |
| factory_type == ContentBrowserClient::URLLoaderFactoryType:: |
| kServiceWorkerSubResource); |
| |
| network::mojom::URLLoaderFactoryParamsPtr factory_params = |
| URLLoaderFactoryParamsHelper::CreateForWorker( |
| rph, origin, isolation_info, std::move(coep_reporter), |
| std::move(dip_reporter), |
| static_cast<StoragePartitionImpl*>(rph->GetStoragePartition()) |
| ->CreateURLLoaderNetworkObserverForServiceWorker( |
| rph->GetDeprecatedID(), origin), |
| NetworkServiceDevToolsObserver::MakeSelfOwned(devtools_worker_token), |
| std::move(client_security_state), |
| "EmbeddedWorkerInstance::CreateFactoryBundle", |
| /*require_cross_site_request_for_cookies=*/false, |
| /*is_for_service_worker=*/true); |
| |
| // See if the default factory needs to be tweaked by the embedder. |
| bool bypass_redirect_checks = false; |
| url_loader_factory::CreateAndConnectToPendingReceiver( |
| std::move(default_factory_receiver), factory_type, |
| url_loader_factory::TerminalParams::ForNetworkContext( |
| rph->GetStoragePartition()->GetNetworkContext(), |
| std::move(factory_params), |
| url_loader_factory::HeaderClientOption::kAllow, |
| url_loader_factory::FactoryOverrideOption::kAllow), |
| url_loader_factory::ContentClientParams( |
| rph->GetBrowserContext(), nullptr /* frame_host */, |
| rph->GetDeprecatedID(), origin, isolation_info, |
| ukm::kInvalidSourceIdObj, &bypass_redirect_checks), |
| devtools_instrumentation::WillCreateURLLoaderFactoryParams:: |
| ForServiceWorker(*rph, routing_id)); |
| |
| factory_bundle->set_bypass_redirect_checks(bypass_redirect_checks); |
| |
| ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories; |
| non_network_factories[url::kDataScheme] = DataURLLoaderFactory::Create(); |
| // Allow service workers for chrome:// or chrome-untrusted:// based on flags. |
| if (base::FeatureList::IsEnabled( |
| features::kEnableServiceWorkersForChromeScheme) && |
| origin.scheme() == content::kChromeUIScheme) { |
| non_network_factories.emplace( |
| content::kChromeUIScheme, |
| CreateWebUIServiceWorkerLoaderFactory(rph->GetBrowserContext(), |
| content::kChromeUIScheme, |
| base::flat_set<std::string>())); |
| } else if (base::FeatureList::IsEnabled( |
| features::kEnableServiceWorkersForChromeUntrusted) && |
| origin.scheme() == content::kChromeUIUntrustedScheme) { |
| non_network_factories.emplace( |
| content::kChromeUIUntrustedScheme, |
| CreateWebUIServiceWorkerLoaderFactory(rph->GetBrowserContext(), |
| content::kChromeUIUntrustedScheme, |
| base::flat_set<std::string>())); |
| } |
| |
| GetContentClient() |
| ->browser() |
| ->RegisterNonNetworkSubresourceURLLoaderFactories( |
| rph->GetDeprecatedID(), IPC::mojom::kRoutingIdNone, origin, |
| &non_network_factories); |
| |
| for (auto& pair : non_network_factories) { |
| const std::string& scheme = pair.first; |
| mojo::PendingRemote<network::mojom::URLLoaderFactory>& pending_remote = |
| pair.second; |
| |
| // To be safe, ignore schemes that aren't allowed to register service |
| // workers. We assume that importScripts and fetch() should fail on such |
| // schemes. |
| // data: URLs are allowed here, because importScripts() and fetch() to data: |
| // URLs are anyway successful, and in order to allow Extension's WebRequest |
| // redirects to data: URLs in ServiceWorkerGlobalScope |
| // (https://crbug.com/1334249). |
| if (scheme != url::kDataScheme && |
| !base::Contains(GetServiceWorkerSchemes(), scheme)) { |
| continue; |
| } |
| |
| factory_bundle->pending_scheme_specific_factories().emplace( |
| scheme, std::move(pending_remote)); |
| } |
| |
| return factory_bundle; |
| } |
| |
| void EmbeddedWorkerInstance::SetPauseInitializingGlobalScope() { |
| TRACE_EVENT0("ServiceWorker", |
| "EmbeddedWorkerInstance::SetPauseInitializingGlobalScope"); |
| CHECK_EQ(blink::EmbeddedWorkerStatus::kStopped, status_); |
| CHECK(!pause_initializing_global_scope_); |
| pause_initializing_global_scope_ = true; |
| } |
| |
| void EmbeddedWorkerInstance::ResumeInitializingGlobalScope() { |
| TRACE_EVENT0("ServiceWorker", |
| "EmbeddedWorkerInstance::ResumeInitializingGlobalScope"); |
| CHECK_EQ(blink::EmbeddedWorkerStatus::kStarting, status_); |
| CHECK(pause_initializing_global_scope_); |
| pause_initializing_global_scope_ = false; |
| owner_version_->InitializeGlobalScope(); |
| } |
| |
| void EmbeddedWorkerInstance::OnReportException( |
| const std::u16string& error_message, |
| int line_number, |
| int column_number, |
| const GURL& source_url) { |
| for (auto& observer : listener_list_) { |
| observer.OnReportException(error_message, line_number, column_number, |
| source_url); |
| } |
| } |
| |
| void EmbeddedWorkerInstance::OnReportConsoleMessage( |
| blink::mojom::ConsoleMessageSource source, |
| blink::mojom::ConsoleMessageLevel message_level, |
| const std::u16string& message, |
| int line_number, |
| const GURL& source_url) { |
| for (auto& observer : listener_list_) { |
| observer.OnReportConsoleMessage(source, message_level, message, line_number, |
| source_url); |
| } |
| } |
| |
| int EmbeddedWorkerInstance::process_id() const { |
| if (process_handle_) |
| return process_handle_->process_id(); |
| return ChildProcessHost::kInvalidUniqueID; |
| } |
| |
| int EmbeddedWorkerInstance::worker_devtools_agent_route_id() const { |
| if (devtools_proxy_) |
| return devtools_proxy_->agent_route_id(); |
| return IPC::mojom::kRoutingIdNone; |
| } |
| |
| base::UnguessableToken EmbeddedWorkerInstance::WorkerDevtoolsId() const { |
| if (devtools_proxy_) |
| return devtools_proxy_->devtools_id(); |
| return base::UnguessableToken(); |
| } |
| |
| void EmbeddedWorkerInstance::AddObserver(Listener* listener) { |
| listener_list_.AddObserver(listener); |
| } |
| |
| void EmbeddedWorkerInstance::RemoveObserver(Listener* listener) { |
| listener_list_.RemoveObserver(listener); |
| } |
| |
| void EmbeddedWorkerInstance::SetDevToolsAttached(bool attached) { |
| devtools_attached_ = attached; |
| if (!attached) |
| return; |
| if (inflight_start_info_) |
| inflight_start_info_->skip_recording_startup_time = true; |
| } |
| |
| void EmbeddedWorkerInstance::OnNetworkAccessedForScriptLoad() { |
| starting_phase_ = SCRIPT_DOWNLOADING; |
| network_accessed_for_script_ = true; |
| } |
| |
| void EmbeddedWorkerInstance::ReleaseProcess() { |
| // Keeps alive `owner_version_` and `this` during the method. |
| // We don't have to protect `owner_version_` during the destruction because |
| // there should no remaining `scoped_refptr` to `*owner_version_` that could |
| // trigger re-entering `~EmbeddedWorkerInstance()`. |
| scoped_refptr<ServiceWorkerVersion> protect = |
| !in_dtor_ ? owner_version_.get() : nullptr; |
| |
| // Abort an inflight start task. |
| inflight_start_info_.reset(); |
| // NotifyForegroundServiceWorkerRemoved() may trigger a call to |
| // UpdateForegroundPriority(). By setting status_ to kStopping we |
| // prevent NotifyForegroundServiceWorkerAdded() from being called |
| // from UpdateForegroundPriority() since we don't want it to be |
| // re-added at this stage. |
| status_ = blink::EmbeddedWorkerStatus::kStopping; |
| pause_initializing_global_scope_ = false; |
| NotifyForegroundServiceWorkerRemoved(); |
| |
| instance_host_receiver_.reset(); |
| devtools_proxy_.reset(); |
| process_handle_.reset(); |
| subresource_loader_updater_.reset(); |
| coep_reporter_.reset(); |
| status_ = blink::EmbeddedWorkerStatus::kStopped; |
| starting_phase_ = NOT_STARTING; |
| thread_id_ = ServiceWorkerConsts::kInvalidEmbeddedWorkerThreadId; |
| |
| DCHECK(!foreground_notified_); |
| } |
| |
| void EmbeddedWorkerInstance::OnSetupFailed( |
| StatusCallback callback, |
| blink::ServiceWorkerStatusCode status) { |
| blink::EmbeddedWorkerStatus old_status = status_; |
| base::WeakPtr<EmbeddedWorkerInstance> weak_this = weak_factory_.GetWeakPtr(); |
| ReleaseProcess(); |
| std::move(callback).Run(status); |
| if (weak_this && old_status != blink::EmbeddedWorkerStatus::kStopped) { |
| for (auto& observer : weak_this->listener_list_) |
| observer.OnStopped(old_status); |
| } |
| } |
| |
| // static |
| std::string EmbeddedWorkerInstance::StatusToString( |
| blink::EmbeddedWorkerStatus status) { |
| switch (status) { |
| case blink::EmbeddedWorkerStatus::kStopped: |
| return "STOPPED"; |
| case blink::EmbeddedWorkerStatus::kStarting: |
| return "STARTING"; |
| case blink::EmbeddedWorkerStatus::kRunning: |
| return "RUNNING"; |
| case blink::EmbeddedWorkerStatus::kStopping: |
| return "STOPPING"; |
| } |
| NOTREACHED() << static_cast<int>(status); |
| } |
| |
| // static |
| std::string EmbeddedWorkerInstance::StartingPhaseToString(StartingPhase phase) { |
| switch (phase) { |
| case NOT_STARTING: |
| return "Not in STARTING status"; |
| case ALLOCATING_PROCESS: |
| return "Allocating process"; |
| case SENT_START_WORKER: |
| return "Sent StartWorker message to renderer"; |
| case SCRIPT_DOWNLOADING: |
| return "Script downloading"; |
| case SCRIPT_LOADED: |
| return "Script loaded"; |
| case SCRIPT_STREAMING: |
| return "Script streaming"; |
| case SCRIPT_EVALUATION: |
| return "Script evaluation"; |
| case STARTING_PHASE_MAX_VALUE: |
| NOTREACHED(); |
| } |
| NOTREACHED() << phase; |
| } |
| |
| void EmbeddedWorkerInstance::NotifyForegroundServiceWorkerAdded() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!process_handle_ || foreground_notified_) |
| return; |
| |
| foreground_notified_ = true; |
| NotifyForegroundServiceWorker(true /* added */, process_id()); |
| } |
| |
| void EmbeddedWorkerInstance::NotifyForegroundServiceWorkerRemoved() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| |
| if (!process_handle_ || !foreground_notified_) |
| return; |
| |
| foreground_notified_ = false; |
| NotifyForegroundServiceWorker(false /* added */, process_id()); |
| } |
| |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| EmbeddedWorkerInstance::MakeScriptLoaderFactoryRemote( |
| std::unique_ptr<blink::PendingURLLoaderFactoryBundle> script_bundle) { |
| CHECK(context_); |
| mojo::PendingRemote<network::mojom::URLLoaderFactory> |
| script_loader_factory_remote; |
| |
| auto script_bundle_factory = |
| base::MakeRefCounted<blink::URLLoaderFactoryBundle>( |
| std::move(script_bundle)); |
| script_loader_factory_ = mojo::MakeSelfOwnedReceiver( |
| std::make_unique<ServiceWorkerScriptLoaderFactory>( |
| context_, owner_version_->worker_host()->GetWeakPtr(), |
| std::move(script_bundle_factory)), |
| script_loader_factory_remote.InitWithNewPipeAndPassReceiver()); |
| |
| return script_loader_factory_remote; |
| } |
| |
| void EmbeddedWorkerInstance::BindCacheStorageInternal() { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| const network::CrossOriginEmbedderPolicy* coep = |
| owner_version_->cross_origin_embedder_policy(); |
| const network::DocumentIsolationPolicy* dip = |
| owner_version_->document_isolation_policy(); |
| // Prior to PlzServiceWorker launch, the COEP and/or DIP headers might not be |
| // known initially. The in-flight CacheStorage requests are kept until the |
| // main script has loaded the headers and the COEP one is known. |
| // Now that PlzServiceWorker is fully launched, this _should_ no longer be |
| // necessary, but crbug.com/352690275 suggests otherwise. |
| // TODO(crbug.com/352690275): Replace with CHECK once behavior causing missing |
| // headers is better understood. |
| if (!coep || !dip) { |
| return; |
| } |
| |
| for (auto& request : pending_cache_storage_requests_) { |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| coep_reporter_remote; |
| if (coep_reporter_) { |
| coep_reporter_->Clone( |
| coep_reporter_remote.InitWithNewPipeAndPassReceiver()); |
| } |
| |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| dip_reporter_remote; |
| if (dip_reporter_) { |
| dip_reporter_->Clone( |
| dip_reporter_remote.InitWithNewPipeAndPassReceiver()); |
| } |
| |
| auto* rph = RenderProcessHost::FromID(process_id()); |
| if (!rph) |
| return; |
| |
| rph->BindCacheStorage(*coep, std::move(coep_reporter_remote), *dip, |
| std::move(dip_reporter_remote), request.bucket, |
| std::move(request.receiver)); |
| } |
| pending_cache_storage_requests_.clear(); |
| } |
| |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| EmbeddedWorkerInstance::GetCoepReporter() { |
| if (!owner_version_->context() || !owner_version_->context()->wrapper()) { |
| return mojo::NullRemote(); |
| } |
| auto* storage_partition = |
| owner_version_->context()->wrapper()->storage_partition(); |
| if (!storage_partition) { |
| return mojo::NullRemote(); |
| } |
| return GetCoepReporterInternal(storage_partition); |
| } |
| |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| EmbeddedWorkerInstance::GetCoepReporterInternal( |
| StoragePartitionImpl* storage_partition) { |
| mojo::PendingRemote<network::mojom::CrossOriginEmbedderPolicyReporter> |
| new_coep_reporter; |
| if (coep_reporter_) { |
| if (owner_version_->context() && owner_version_->context()->wrapper() && |
| owner_version_->context()->wrapper()->storage_partition()) { |
| if (owner_version_->context()->wrapper()->storage_partition() != |
| storage_partition) { |
| // MockRenderProcessHost::GetStoragePartition() returns a storage |
| // partition generated via the browser context, which is a different |
| // path to obtain the storage partition from the production. |
| // Therefore, the storage partitions mismatches in tests. |
| CHECK_IS_TEST(); |
| } |
| } |
| coep_reporter_->Clone(new_coep_reporter.InitWithNewPipeAndPassReceiver()); |
| return new_coep_reporter; |
| } |
| |
| network::mojom::ClientSecurityStatePtr client_security_state = |
| owner_version_->BuildClientSecurityState(); |
| const network::CrossOriginEmbedderPolicy* coep = |
| client_security_state |
| ? &client_security_state->cross_origin_embedder_policy |
| : nullptr; |
| |
| if (!coep) { |
| return mojo::NullRemote(); |
| } |
| mojo::PendingRemote<blink::mojom::ReportingObserver> |
| reporting_observer_remote; |
| coep_reporting_observer_receiver_ = |
| reporting_observer_remote.InitWithNewPipeAndPassReceiver(); |
| coep_reporter_ = std::make_unique<CrossOriginEmbedderPolicyReporter>( |
| storage_partition->GetWeakPtr(), owner_version_->script_url(), |
| coep->reporting_endpoint, coep->report_only_reporting_endpoint, |
| owner_version_->reporting_source(), |
| owner_version_->key() |
| .ToPartialNetIsolationInfo() |
| .network_anonymization_key()); |
| coep_reporter_->BindObserver(std::move(reporting_observer_remote)); |
| |
| coep_reporter_->Clone(new_coep_reporter.InitWithNewPipeAndPassReceiver()); |
| return new_coep_reporter; |
| } |
| |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| EmbeddedWorkerInstance::GetDipReporter() { |
| if (!owner_version_->context() || !owner_version_->context()->wrapper()) { |
| return mojo::NullRemote(); |
| } |
| auto* storage_partition = |
| owner_version_->context()->wrapper()->storage_partition(); |
| if (!storage_partition) { |
| return mojo::NullRemote(); |
| } |
| return GetDipReporterInternal(storage_partition); |
| } |
| |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| EmbeddedWorkerInstance::GetDipReporterInternal( |
| StoragePartitionImpl* storage_partition) { |
| mojo::PendingRemote<network::mojom::DocumentIsolationPolicyReporter> |
| new_dip_reporter; |
| if (dip_reporter_) { |
| if (owner_version_->context() && owner_version_->context()->wrapper() && |
| owner_version_->context()->wrapper()->storage_partition()) { |
| if (owner_version_->context()->wrapper()->storage_partition() != |
| storage_partition) { |
| // MockRenderProcessHost::GetStoragePartition() returns a storage |
| // partition generated via the browser context, which is a different |
| // path to obtain the storage partition from the production. |
| // Therefore, the storage partitions mismatches in tests. |
| CHECK_IS_TEST(); |
| } |
| } |
| dip_reporter_->Clone(new_dip_reporter.InitWithNewPipeAndPassReceiver()); |
| return new_dip_reporter; |
| } |
| |
| network::mojom::ClientSecurityStatePtr client_security_state = |
| owner_version_->BuildClientSecurityState(); |
| const network::DocumentIsolationPolicy* dip = |
| client_security_state ? &client_security_state->document_isolation_policy |
| : nullptr; |
| |
| if (!dip) { |
| return mojo::NullRemote(); |
| } |
| mojo::PendingRemote<blink::mojom::ReportingObserver> |
| reporting_observer_remote; |
| dip_reporting_observer_receiver_ = |
| reporting_observer_remote.InitWithNewPipeAndPassReceiver(); |
| dip_reporter_ = std::make_unique<DocumentIsolationPolicyReporter>( |
| storage_partition->GetWeakPtr(), owner_version_->script_url(), |
| dip->reporting_endpoint, dip->report_only_reporting_endpoint, |
| owner_version_->reporting_source(), |
| owner_version_->key() |
| .ToPartialNetIsolationInfo() |
| .network_anonymization_key()); |
| dip_reporter_->BindObserver(std::move(reporting_observer_remote)); |
| |
| dip_reporter_->Clone(new_dip_reporter.InitWithNewPipeAndPassReceiver()); |
| return new_dip_reporter; |
| } |
| |
| EmbeddedWorkerInstance::CacheStorageRequest::CacheStorageRequest( |
| mojo::PendingReceiver<blink::mojom::CacheStorage> receiver, |
| storage::BucketLocator bucket) |
| : receiver(std::move(receiver)), bucket(std::move(bucket)) {} |
| |
| EmbeddedWorkerInstance::CacheStorageRequest::CacheStorageRequest( |
| CacheStorageRequest&& other) = default; |
| EmbeddedWorkerInstance::CacheStorageRequest::~CacheStorageRequest() = default; |
| |
| } // namespace content |