blob: 57580cc05117c0e8c0ba59dae67ad8e0db0445c2 [file] [log] [blame]
// 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/service_worker_context_wrapper.h"
#include <map>
#include <memory>
#include <set>
#include <string>
#include <utility>
#include <vector>
#include "base/barrier_closure.h"
#include "base/bind.h"
#include "base/callback_helpers.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/lazy_instance.h"
#include "base/location.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/observer_list.h"
#include "base/run_loop.h"
#include "base/task/single_thread_task_runner.h"
#include "base/task/thread_pool.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "components/services/storage/service_worker/service_worker_storage_control_impl.h"
#include "content/browser/blob_storage/chrome_blob_storage_context.h"
#include "content/browser/devtools/devtools_instrumentation.h"
#include "content/browser/loader/navigation_url_loader_impl.h"
#include "content/browser/service_worker/embedded_worker_status.h"
#include "content/browser/service_worker/service_worker_container_host.h"
#include "content/browser/service_worker/service_worker_host.h"
#include "content/browser/service_worker/service_worker_object_host.h"
#include "content/browser/service_worker/service_worker_process_manager.h"
#include "content/browser/service_worker/service_worker_quota_client.h"
#include "content/browser/service_worker/service_worker_version.h"
#include "content/browser/storage_partition_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/browser_task_traits.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/content_browser_client.h"
#include "content/public/browser/global_routing_id.h"
#include "content/public/browser/service_worker_context_observer.h"
#include "content/public/browser/storage_usage_info.h"
#include "content/public/browser/web_ui_url_loader_factory.h"
#include "content/public/browser/webui_config.h"
#include "content/public/browser/webui_config_map.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_features.h"
#include "content/public/common/url_constants.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
#include "mojo/public/cpp/bindings/self_owned_receiver.h"
#include "net/base/url_util.h"
#include "services/metrics/public/cpp/ukm_source_id.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/mojom/client_security_state.mojom.h"
#include "storage/browser/quota/quota_client_type.h"
#include "storage/browser/quota/quota_manager_proxy.h"
#include "storage/browser/quota/special_storage_policy.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "third_party/blink/public/common/privacy_budget/identifiability_study_settings.h"
#include "third_party/blink/public/common/service_worker/service_worker_scope_match.h"
#include "third_party/blink/public/common/service_worker/service_worker_status_code.h"
#include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
namespace content {
namespace {
#if BUILDFLAG(IS_ANDROID)
// Enables running ServiceWorkerStorageControl on IO thread instead of UI thread
// on Android.
const base::Feature kServiceWorkerStorageControlOnIOThread{
"ServiceWorkerStorageControlOnIOThread", base::FEATURE_DISABLED_BY_DEFAULT};
#endif
void DidFindRegistrationForStartActiveWorker(
ServiceWorkerContextWrapper::StatusCallback callback,
blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != blink::ServiceWorkerStatusCode::kOk ||
!registration->active_version()) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorNotFound));
return;
}
registration->active_version()->StartWorker(
ServiceWorkerMetrics::EventType::UNKNOWN,
base::BindOnce(
[](ServiceWorkerContextWrapper::StatusCallback callback,
blink::ServiceWorkerStatusCode status) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), status));
},
std::move(callback)));
}
void DidStartWorker(scoped_refptr<ServiceWorkerVersion> version,
ServiceWorkerContext::StartWorkerCallback info_callback,
ServiceWorkerContext::StatusCodeCallback failure_callback,
blink::ServiceWorkerStatusCode start_worker_status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
std::move(failure_callback).Run(start_worker_status);
return;
}
EmbeddedWorkerInstance* instance = version->embedded_worker();
std::move(info_callback)
.Run(version->version_id(), instance->process_id(),
instance->thread_id());
}
void FoundRegistrationForStartWorker(
ServiceWorkerContext::StartWorkerCallback info_callback,
ServiceWorkerContext::StatusCodeCallback failure_callback,
blink::ServiceWorkerStatusCode service_worker_status,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
std::move(failure_callback).Run(service_worker_status);
return;
}
ServiceWorkerVersion* version_ptr = registration->active_version()
? registration->active_version()
: registration->installing_version();
// Since FindRegistrationForScope returned
// blink::ServiceWorkerStatusCode::kOk, there must have been either:
// - an active version, which optionally might have activated from a waiting
// version (as DidFindRegistrationForFindImpl will activate any waiting
// version).
// - or an installing version.
// However, if the installation is rejected, the installing version can go
// away by the time we reach here from DidFindRegistrationForFindImpl.
if (!version_ptr) {
std::move(failure_callback).Run(service_worker_status);
return;
}
// Note: There might be a remote possibility that |registration|'s |version|
// might change between here and DidStartWorker, so bind |version| to
// RunAfterStartWorker.
scoped_refptr<ServiceWorkerVersion> version =
base::WrapRefCounted(version_ptr);
version->RunAfterStartWorker(
ServiceWorkerMetrics::EventType::EXTERNAL_REQUEST,
base::BindOnce(&DidStartWorker, version, std::move(info_callback),
std::move(failure_callback)));
}
void RunOnceClosure(scoped_refptr<ServiceWorkerContextWrapper> ref_holder,
base::OnceClosure task) {
std::move(task).Run();
}
// Helper class to create a callback that takes blink::ServiceWorkerStatusCode
// as the first parameter and calls the original callback with a boolean of
// whether the status is blink::ServiceWorkerStatusCode::kOk or not.
class WrapResultCallbackToTakeStatusCode {
public:
explicit WrapResultCallbackToTakeStatusCode(
ServiceWorkerContext::ResultCallback callback)
: callback_(std::move(callback)) {}
template <typename... Args>
operator base::OnceCallback<void(blink::ServiceWorkerStatusCode, Args...)>() {
return Take<Args...>();
}
private:
template <typename... Args>
base::OnceCallback<void(blink::ServiceWorkerStatusCode, Args...)> Take() {
return base::BindOnce(
[](ServiceWorkerContext::ResultCallback callback,
blink::ServiceWorkerStatusCode status, Args...) {
std::move(callback).Run(status ==
blink::ServiceWorkerStatusCode::kOk);
},
std::move(callback_));
}
ServiceWorkerContext::ResultCallback callback_;
};
void RunOrPostTaskOnUIThread(const base::Location& location,
base::OnceClosure task) {
if (BrowserThread::CurrentlyOn(BrowserThread::UI)) {
std::move(task).Run();
} else {
GetUIThreadTaskRunner({})->PostTask(location, std::move(task));
}
}
} // namespace
// static
bool ServiceWorkerContext::ScopeMatches(const GURL& scope, const GURL& url) {
return blink::ServiceWorkerScopeMatches(scope, url);
}
// static
void ServiceWorkerContext::RunTask(
scoped_refptr<base::SequencedTaskRunner> task_runner,
const base::Location& from_here,
ServiceWorkerContext* service_worker_context,
base::OnceClosure task) {
auto ref = base::WrapRefCounted(
static_cast<ServiceWorkerContextWrapper*>(service_worker_context));
task_runner->PostTask(
from_here,
base::BindOnce(&RunOnceClosure, std::move(ref), std::move(task)));
}
ServiceWorkerContextWrapper::ServiceWorkerContextWrapper(
BrowserContext* browser_context)
: core_observer_list_(
base::MakeRefCounted<ServiceWorkerContextObserverList>()),
process_manager_(
std::make_unique<ServiceWorkerProcessManager>(browser_context)) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Add this object as an observer of the wrapped |context_core_|. This lets us
// forward observer methods to observers outside of content.
core_observer_list_->AddObserver(this);
if (blink::IdentifiabilityStudySettings::Get()->IsActive()) {
identifiability_metrics_ =
std::make_unique<ServiceWorkerIdentifiabilityMetrics>();
core_observer_list_->AddObserver(identifiability_metrics_.get());
}
}
void ServiceWorkerContextWrapper::Init(
const base::FilePath& user_data_directory,
storage::QuotaManagerProxy* quota_manager_proxy,
storage::SpecialStoragePolicy* special_storage_policy,
ChromeBlobStorageContext* blob_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(storage_partition_);
is_incognito_ = user_data_directory.empty();
user_data_directory_ = user_data_directory;
quota_manager_proxy_ = quota_manager_proxy;
InitInternal(quota_manager_proxy, special_storage_policy, blob_context,
storage_partition_->browser_context());
}
void ServiceWorkerContextWrapper::InitInternal(
storage::QuotaManagerProxy* quota_manager_proxy,
storage::SpecialStoragePolicy* special_storage_policy,
ChromeBlobStorageContext* blob_context,
BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
non_network_pending_loader_factory_bundle_for_update_check;
non_network_pending_loader_factory_bundle_for_update_check =
CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck(
browser_context);
context_core_ = std::make_unique<ServiceWorkerContextCore>(
quota_manager_proxy, special_storage_policy,
std::move(non_network_pending_loader_factory_bundle_for_update_check),
core_observer_list_.get(), this);
}
void ServiceWorkerContextWrapper::Shutdown() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ClearRunningServiceWorkers();
storage_partition_ = nullptr;
process_manager_->Shutdown();
storage_control_.reset();
context_core_.reset();
}
void ServiceWorkerContextWrapper::DeleteAndStartOver() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
// The context could be null due to system shutdown or restart failure. In
// either case, we should not have to recover the system, so just return
// here.
return;
}
context_core_->DeleteAndStartOver(base::BindOnce(
&ServiceWorkerContextWrapper::DidDeleteAndStartOver, this));
}
StoragePartitionImpl* ServiceWorkerContextWrapper::storage_partition() const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return storage_partition_;
}
void ServiceWorkerContextWrapper::set_storage_partition(
StoragePartitionImpl* storage_partition) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
storage_partition_ = storage_partition;
process_manager_->set_storage_partition(storage_partition_);
}
BrowserContext* ServiceWorkerContextWrapper::browser_context() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return process_manager()->browser_context();
}
void ServiceWorkerContextWrapper::OnRegistrationCompleted(
int64_t registration_id,
const GURL& scope,
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnRegistrationCompleted(scope);
}
void ServiceWorkerContextWrapper::OnRegistrationStored(
int64_t registration_id,
const GURL& scope,
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnRegistrationStored(registration_id, scope);
}
void ServiceWorkerContextWrapper::OnAllRegistrationsDeletedForStorageKey(
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
}
void ServiceWorkerContextWrapper::OnErrorReported(
int64_t version_id,
const GURL& scope,
const blink::StorageKey& key,
const ServiceWorkerContextObserver::ErrorInfo& info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnErrorReported(version_id, scope, info);
}
void ServiceWorkerContextWrapper::OnReportConsoleMessage(
int64_t version_id,
const GURL& scope,
const blink::StorageKey& key,
const ConsoleMessage& message) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnReportConsoleMessage(version_id, scope, message);
}
void ServiceWorkerContextWrapper::OnControlleeAdded(
int64_t version_id,
const std::string& client_uuid,
const ServiceWorkerClientInfo& client_info) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnControlleeAdded(version_id, client_uuid, client_info);
}
void ServiceWorkerContextWrapper::OnControlleeRemoved(
int64_t version_id,
const std::string& client_uuid) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnControlleeRemoved(version_id, client_uuid);
}
void ServiceWorkerContextWrapper::OnNoControllees(
int64_t version_id,
const GURL& scope,
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnNoControllees(version_id, scope);
}
void ServiceWorkerContextWrapper::OnControlleeNavigationCommitted(
int64_t version_id,
const std::string& uuid,
GlobalRenderFrameHostId render_frame_host_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnControlleeNavigationCommitted(version_id, uuid,
render_frame_host_id);
}
void ServiceWorkerContextWrapper::OnStarted(
int64_t version_id,
const GURL& scope,
int process_id,
const GURL& script_url,
const blink::ServiceWorkerToken& token,
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (is_deleting_and_starting_over_)
return;
// TODO(crbug.com/1199077): Update this when ServiceWorkerContextCoreObserver
// implements StorageKey.
auto insertion_result = running_service_workers_.insert(std::make_pair(
version_id,
ServiceWorkerRunningInfo(script_url, scope, key, process_id, token)));
DCHECK(insertion_result.second);
const auto& running_info = insertion_result.first->second;
for (auto& observer : observer_list_)
observer.OnVersionStartedRunning(version_id, running_info);
}
void ServiceWorkerContextWrapper::OnStopped(int64_t version_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
auto it = running_service_workers_.find(version_id);
if (it != running_service_workers_.end()) {
running_service_workers_.erase(it);
for (auto& observer : observer_list_)
observer.OnVersionStoppedRunning(version_id);
}
}
void ServiceWorkerContextWrapper::OnDeleteAndStartOver() {
is_deleting_and_starting_over_ = true;
ClearRunningServiceWorkers();
}
void ServiceWorkerContextWrapper::OnVersionStateChanged(
int64_t version_id,
const GURL& scope,
const blink::StorageKey& key,
ServiceWorkerVersion::Status status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status == ServiceWorkerVersion::Status::ACTIVATED) {
for (auto& observer : observer_list_)
observer.OnVersionActivated(version_id, scope);
} else if (status == ServiceWorkerVersion::Status::REDUNDANT) {
for (auto& observer : observer_list_)
observer.OnVersionRedundant(version_id, scope);
}
}
void ServiceWorkerContextWrapper::AddObserver(
ServiceWorkerContextObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
observer_list_.AddObserver(observer);
}
void ServiceWorkerContextWrapper::RemoveObserver(
ServiceWorkerContextObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
observer_list_.RemoveObserver(observer);
}
void ServiceWorkerContextWrapper::RegisterServiceWorker(
const GURL& script_url,
const blink::StorageKey& key,
const blink::mojom::ServiceWorkerRegistrationOptions& options,
StatusCodeCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback),
blink::ServiceWorkerStatusCode::kErrorStartWorkerFailed));
return;
}
blink::mojom::ServiceWorkerRegistrationOptions options_to_pass(
net::SimplifyUrlForRequest(options.scope), options.type,
options.update_via_cache);
// TODO(https://crbug.com/1239551): initialize remaining fields
PolicyContainerPolicies policy_container_policies;
policy_container_policies.is_web_secure_context =
network::IsUrlPotentiallyTrustworthy(script_url);
// TODO(bashi): Pass a valid outside fetch client settings object. Perhaps
// changing this method to take a settings object.
context()->RegisterServiceWorker(
net::SimplifyUrlForRequest(script_url), key, options_to_pass,
blink::mojom::FetchClientSettingsObject::New(
network::mojom::ReferrerPolicy::kDefault,
/*outgoing_referrer=*/script_url,
blink::mojom::InsecureRequestsPolicy::kDoNotUpgrade),
base::BindOnce(
[](StatusCodeCallback callback, blink::ServiceWorkerStatusCode status,
const std::string&, int64_t) { std::move(callback).Run(status); },
std::move(callback)),
/*requesting_frame_id=*/GlobalRenderFrameHostId(),
policy_container_policies);
}
void ServiceWorkerContextWrapper::UnregisterServiceWorker(
const GURL& scope,
const blink::StorageKey& key,
ResultCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), false));
return;
}
context()->UnregisterServiceWorker(
net::SimplifyUrlForRequest(scope), key, /*is_immediate=*/false,
WrapResultCallbackToTakeStatusCode(std::move(callback)));
}
ServiceWorkerExternalRequestResult
ServiceWorkerContextWrapper::StartingExternalRequest(
int64_t service_worker_version_id,
ServiceWorkerExternalRequestTimeoutType timeout_type,
const std::string& request_uuid) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context())
return ServiceWorkerExternalRequestResult::kNullContext;
scoped_refptr<ServiceWorkerVersion> version =
context()->GetLiveVersion(service_worker_version_id);
if (!version)
return ServiceWorkerExternalRequestResult::kWorkerNotFound;
return version->StartExternalRequest(request_uuid, timeout_type);
}
bool ServiceWorkerContextWrapper::ExecuteScriptForTest(
const std::string& script,
int64_t service_worker_version_id,
ServiceWorkerScriptExecutionCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context())
return false;
scoped_refptr<ServiceWorkerVersion> version =
context()->GetLiveVersion(service_worker_version_id);
if (!version)
return false;
version->ExecuteScriptForTest(script, std::move(callback)); // IN-TEST
return true;
}
ServiceWorkerExternalRequestResult
ServiceWorkerContextWrapper::FinishedExternalRequest(
int64_t service_worker_version_id,
const std::string& request_uuid) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context())
return ServiceWorkerExternalRequestResult::kNullContext;
scoped_refptr<ServiceWorkerVersion> version =
context()->GetLiveVersion(service_worker_version_id);
if (!version)
return ServiceWorkerExternalRequestResult::kWorkerNotFound;
return version->FinishExternalRequest(request_uuid);
}
size_t ServiceWorkerContextWrapper::CountExternalRequestsForTest(
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::vector<ServiceWorkerVersionInfo> live_version_info =
GetAllLiveVersionInfo();
for (const ServiceWorkerVersionInfo& info : live_version_info) {
ServiceWorkerVersion* version = GetLiveVersion(info.version_id);
if (version && version->key() == key) {
return version->GetExternalRequestCountForTest(); // IN-TEST
}
}
return 0u;
}
bool ServiceWorkerContextWrapper::MaybeHasRegistrationForStorageKey(
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return context() ? context()->MaybeHasRegistrationForStorageKey(key) : true;
}
void ServiceWorkerContextWrapper::GetAllOriginsInfo(
GetUsageInfoCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), std::vector<StorageUsageInfo>()));
return;
}
context()->registry()->GetAllRegistrationsInfos(base::BindOnce(
&ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins,
this, std::move(callback)));
}
void ServiceWorkerContextWrapper::DeleteForStorageKey(
const blink::StorageKey& key,
ResultCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Ensure the callback is called asynchronously.
scoped_refptr<base::TaskRunner> callback_runner = GetUIThreadTaskRunner({});
if (!context_core_) {
callback_runner->PostTask(FROM_HERE,
base::BindOnce(std::move(callback), false));
return;
}
context()->DeleteForStorageKey(
key,
base::BindOnce(
[](ResultCallback callback,
scoped_refptr<base::TaskRunner> callback_runner,
blink::ServiceWorkerStatusCode status) {
callback_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
status == blink::ServiceWorkerStatusCode::kOk));
},
std::move(callback), std::move(callback_runner)));
}
void ServiceWorkerContextWrapper::CheckHasServiceWorker(
const GURL& url,
const blink::StorageKey& key,
CheckHasServiceWorkerCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
ServiceWorkerCapability::NO_SERVICE_WORKER));
return;
}
context()->CheckHasServiceWorker(net::SimplifyUrlForRequest(url), key,
std::move(callback));
}
void ServiceWorkerContextWrapper::CheckOfflineCapability(
const GURL& url,
const blink::StorageKey& key,
CheckOfflineCapabilityCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), OfflineCapability::kUnsupported,
blink::mojom::kInvalidServiceWorkerRegistrationId));
return;
}
context()->CheckOfflineCapability(net::SimplifyUrlForRequest(url), key,
std::move(callback));
}
void ServiceWorkerContextWrapper::ClearAllServiceWorkersForTest(
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
context_core_->ClearAllServiceWorkersForTest(std::move(callback));
}
void ServiceWorkerContextWrapper::StartWorkerForScope(
const GURL& scope,
const blink::StorageKey& key,
StartWorkerCallback info_callback,
StatusCodeCallback failure_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
FindRegistrationForScopeImpl(
scope, key,
/*include_installing_version=*/true,
base::BindOnce(&FoundRegistrationForStartWorker, std::move(info_callback),
std::move(failure_callback)));
}
void ServiceWorkerContextWrapper::StartServiceWorkerAndDispatchMessage(
const GURL& scope,
const blink::StorageKey& key,
blink::TransferableMessage message,
ResultCallback result_callback) {
// Ensure the callback is called asynchronously.
auto wrapped_callback = base::BindOnce(
[](ResultCallback callback, bool success) {
GetUIThreadTaskRunner({})->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), success));
},
std::move(result_callback));
// TODO(https://crbug.com/1295029): Don't post task to the UI thread. Instead,
// make all call sites run on the UI thread.
RunOrPostTaskOnUIThread(
FROM_HERE,
base::BindOnce(&ServiceWorkerContextWrapper::
StartServiceWorkerAndDispatchMessageOnUIThread,
this, scope, key, std::move(message),
base::BindOnce(
[](ResultCallback callback,
scoped_refptr<base::TaskRunner> callback_runner,
bool success) {
callback_runner->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), success));
},
std::move(wrapped_callback),
base::ThreadTaskRunnerHandle::Get())));
}
void ServiceWorkerContextWrapper::
StartServiceWorkerAndDispatchMessageOnUIThread(
const GURL& scope,
const blink::StorageKey& key,
blink::TransferableMessage message,
ResultCallback result_callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(result_callback).Run(/*success=*/false);
return;
}
FindRegistrationForScopeImpl(
net::SimplifyUrlForRequest(scope), key,
/*include_installing_version=*/false,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForMessageDispatch,
this, std::move(message), scope, std::move(result_callback)));
}
void ServiceWorkerContextWrapper::DidFindRegistrationForMessageDispatch(
blink::TransferableMessage message,
const GURL& source_origin,
ResultCallback result_callback,
blink::ServiceWorkerStatusCode service_worker_status,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (service_worker_status != blink::ServiceWorkerStatusCode::kOk) {
LOG(WARNING) << "No registration available, status: "
<< static_cast<int>(service_worker_status);
std::move(result_callback).Run(/*success=*/false);
return;
}
registration->active_version()->StartWorker(
ServiceWorkerMetrics::EventType::MESSAGE,
base::BindOnce(
&ServiceWorkerContextWrapper::DidStartServiceWorkerForMessageDispatch,
this, std::move(message), source_origin, registration,
std::move(result_callback)));
}
void ServiceWorkerContextWrapper::DidStartServiceWorkerForMessageDispatch(
blink::TransferableMessage message,
const GURL& source_origin,
scoped_refptr<ServiceWorkerRegistration> registration,
ResultCallback result_callback,
blink::ServiceWorkerStatusCode status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != blink::ServiceWorkerStatusCode::kOk) {
std::move(result_callback).Run(/*success=*/false);
return;
}
scoped_refptr<ServiceWorkerVersion> version = registration->active_version();
blink::mojom::ExtendableMessageEventPtr event =
blink::mojom::ExtendableMessageEvent::New();
event->message = std::move(message);
event->source_origin = url::Origin::Create(source_origin);
event->source_info_for_service_worker =
version->worker_host()
->container_host()
->GetOrCreateServiceWorkerObjectHost(version)
->CreateCompleteObjectInfoToSend();
int request_id = version->StartRequest(
ServiceWorkerMetrics::EventType::MESSAGE,
WrapResultCallbackToTakeStatusCode(std::move(result_callback)));
version->endpoint()->DispatchExtendableMessageEvent(
std::move(event), version->CreateSimpleEventCallback(request_id));
}
void ServiceWorkerContextWrapper::StartServiceWorkerForNavigationHint(
const GURL& document_url,
const blink::StorageKey& key,
StartServiceWorkerForNavigationHintCallback callback) {
TRACE_EVENT1("ServiceWorker", "StartServiceWorkerForNavigationHint",
"document_url", document_url.spec());
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(callback).Run(StartServiceWorkerForNavigationHintResult::FAILED);
return;
}
context_core_->registry()->FindRegistrationForClientUrl(
net::SimplifyUrlForRequest(document_url), key,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForNavigationHint,
this, std::move(callback)));
}
void ServiceWorkerContextWrapper::StopAllServiceWorkersForStorageKey(
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_.get()) {
return;
}
std::vector<ServiceWorkerVersionInfo> live_versions = GetAllLiveVersionInfo();
for (const ServiceWorkerVersionInfo& info : live_versions) {
ServiceWorkerVersion* version = GetLiveVersion(info.version_id);
if (version && version->key() == key)
version->StopWorker(base::DoNothing());
}
}
void ServiceWorkerContextWrapper::StopAllServiceWorkers(
base::OnceClosure callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_.get()) {
base::ThreadTaskRunnerHandle::Get()->PostTask(FROM_HERE,
std::move(callback));
return;
}
std::vector<ServiceWorkerVersionInfo> live_versions = GetAllLiveVersionInfo();
base::RepeatingClosure barrier =
base::BarrierClosure(live_versions.size(), std::move(callback));
for (const ServiceWorkerVersionInfo& info : live_versions) {
ServiceWorkerVersion* version = GetLiveVersion(info.version_id);
DCHECK(version);
version->StopWorker(barrier);
}
}
const base::flat_map<int64_t, ServiceWorkerRunningInfo>&
ServiceWorkerContextWrapper::GetRunningServiceWorkerInfos() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return running_service_workers_;
}
bool ServiceWorkerContextWrapper::IsLiveRunningServiceWorker(
int64_t service_worker_version_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// We might be shutting down.
if (!context())
return false;
auto* version = context()->GetLiveVersion(service_worker_version_id);
if (!version)
return false;
auto running_status = version->running_status();
if (running_status != EmbeddedWorkerStatus::STARTING &&
running_status != EmbeddedWorkerStatus::RUNNING) {
return false;
}
return true;
}
service_manager::InterfaceProvider&
ServiceWorkerContextWrapper::GetRemoteInterfaces(
int64_t service_worker_version_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
CHECK(IsLiveRunningServiceWorker(service_worker_version_id));
// This function should only be called on live running service workers
// so it should be safe to dereference the returned pointer without
// checking it first.
auto& version = *context()->GetLiveVersion(service_worker_version_id);
return version.worker_host()->remote_interfaces();
}
scoped_refptr<ServiceWorkerRegistration>
ServiceWorkerContextWrapper::GetLiveRegistration(int64_t registration_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return nullptr;
return context_core_->GetLiveRegistration(registration_id);
}
ServiceWorkerVersion* ServiceWorkerContextWrapper::GetLiveVersion(
int64_t version_id) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return nullptr;
return context_core_->GetLiveVersion(version_id);
}
std::vector<ServiceWorkerRegistrationInfo>
ServiceWorkerContextWrapper::GetAllLiveRegistrationInfo() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return std::vector<ServiceWorkerRegistrationInfo>();
return context_core_->GetAllLiveRegistrationInfo();
}
std::vector<ServiceWorkerVersionInfo>
ServiceWorkerContextWrapper::GetAllLiveVersionInfo() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return std::vector<ServiceWorkerVersionInfo>();
return context_core_->GetAllLiveVersionInfo();
}
void ServiceWorkerContextWrapper::HasMainFrameWindowClient(
const blink::StorageKey& key,
BoolCallback callback) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), false));
return;
}
context_core_->HasMainFrameWindowClient(key, std::move(callback));
}
std::unique_ptr<std::vector<GlobalRenderFrameHostId>>
ServiceWorkerContextWrapper::GetWindowClientFrameRoutingIds(
const blink::StorageKey& key) const {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::unique_ptr<std::vector<GlobalRenderFrameHostId>> rfh_ids(
new std::vector<GlobalRenderFrameHostId>());
if (!context_core_)
return rfh_ids;
for (std::unique_ptr<ServiceWorkerContextCore::ContainerHostIterator> it =
context_core_->GetWindowClientContainerHostIterator(
key,
/*include_reserved_clients=*/false);
!it->IsAtEnd(); it->Advance()) {
ServiceWorkerContainerHost* container_host = it->GetContainerHost();
DCHECK(container_host->IsContainerForWindowClient());
rfh_ids->push_back(container_host->GetRenderFrameHostId());
}
return rfh_ids;
}
void ServiceWorkerContextWrapper::FindReadyRegistrationForClientUrl(
const GURL& client_url,
const blink::StorageKey& key,
FindRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
nullptr);
return;
}
context_core_->registry()->FindRegistrationForClientUrl(
net::SimplifyUrlForRequest(client_url), key,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
/*include_installing_version=*/false, std::move(callback)));
}
void ServiceWorkerContextWrapper::FindReadyRegistrationForScope(
const GURL& scope,
const blink::StorageKey& key,
FindRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
nullptr);
return;
}
const bool include_installing_version = false;
context_core_->registry()->FindRegistrationForScope(
net::SimplifyUrlForRequest(scope), key,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
include_installing_version, std::move(callback)));
}
void ServiceWorkerContextWrapper::FindRegistrationForScope(
const GURL& scope,
const blink::StorageKey& key,
FindRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
const bool include_installing_version = true;
FindRegistrationForScopeImpl(scope, key, include_installing_version,
std::move(callback));
}
void ServiceWorkerContextWrapper::FindReadyRegistrationForId(
int64_t registration_id,
const blink::StorageKey& key,
FindRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
nullptr);
return;
}
context_core_->registry()->FindRegistrationForId(
registration_id, key,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
/*include_installing_version=*/false, std::move(callback)));
}
void ServiceWorkerContextWrapper::FindReadyRegistrationForIdOnly(
int64_t registration_id,
FindRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
nullptr);
return;
}
context_core_->registry()->FindRegistrationForIdOnly(
registration_id,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
/*include_installing_version=*/false, std::move(callback)));
}
void ServiceWorkerContextWrapper::GetAllRegistrations(
GetRegistrationsInfosCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort,
std::vector<ServiceWorkerRegistrationInfo>()));
return;
}
context_core_->registry()->GetAllRegistrationsInfos(std::move(callback));
}
void ServiceWorkerContextWrapper::GetRegistrationsForStorageKey(
const blink::StorageKey& key,
GetRegistrationsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(
std::move(callback), blink::ServiceWorkerStatusCode::kErrorAbort,
std::vector<scoped_refptr<ServiceWorkerRegistration>>()));
return;
}
context_core_->registry()->GetRegistrationsForStorageKey(key,
std::move(callback));
}
void ServiceWorkerContextWrapper::GetRegistrationUserData(
int64_t registration_id,
const std::vector<std::string>& keys,
GetUserDataCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), std::vector<std::string>(),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->GetUserData(registration_id, keys,
std::move(callback));
}
void ServiceWorkerContextWrapper::GetRegistrationUserDataByKeyPrefix(
int64_t registration_id,
const std::string& key_prefix,
GetUserDataCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback), std::vector<std::string>(),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->GetUserDataByKeyPrefix(registration_id, key_prefix,
std::move(callback));
}
void ServiceWorkerContextWrapper::GetRegistrationUserKeysAndDataByKeyPrefix(
int64_t registration_id,
const std::string& key_prefix,
GetUserKeysAndDataCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort,
base::flat_map<std::string, std::string>()));
return;
}
context_core_->registry()->GetUserKeysAndDataByKeyPrefix(
registration_id, key_prefix, std::move(callback));
}
void ServiceWorkerContextWrapper::StoreRegistrationUserData(
int64_t registration_id,
const blink::StorageKey& key,
const std::vector<std::pair<std::string, std::string>>& key_value_pairs,
StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->StoreUserData(
registration_id, key, key_value_pairs, std::move(callback));
}
void ServiceWorkerContextWrapper::ClearRegistrationUserData(
int64_t registration_id,
const std::vector<std::string>& keys,
StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Ensure the callback is called asynchronously.
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->ClearUserData(registration_id, keys,
std::move(callback));
}
void ServiceWorkerContextWrapper::ClearRegistrationUserDataByKeyPrefixes(
int64_t registration_id,
const std::vector<std::string>& key_prefixes,
StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->ClearUserDataByKeyPrefixes(
registration_id, key_prefixes, std::move(callback));
}
void ServiceWorkerContextWrapper::GetUserDataForAllRegistrations(
const std::string& key,
GetUserDataForAllRegistrationsCallback callback) {
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
std::vector<std::pair<int64_t, std::string>>(),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->GetUserDataForAllRegistrations(
key, std::move(callback));
}
void ServiceWorkerContextWrapper::GetUserDataForAllRegistrationsByKeyPrefix(
const std::string& key_prefix,
GetUserDataForAllRegistrationsCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Ensure the callback is called asynchronously.
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE,
base::BindOnce(std::move(callback),
std::vector<std::pair<int64_t, std::string>>(),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->GetUserDataForAllRegistrationsByKeyPrefix(
key_prefix, std::move(callback));
}
void ServiceWorkerContextWrapper::ClearUserDataForAllRegistrationsByKeyPrefix(
const std::string& key_prefix,
StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// Ensure the callback is called asynchronously.
if (!context_core_) {
base::SequencedTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->ClearUserDataForAllRegistrationsByKeyPrefix(
key_prefix, std::move(callback));
}
void ServiceWorkerContextWrapper::StartActiveServiceWorker(
const GURL& scope,
const blink::StorageKey& key,
StatusCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
base::ThreadTaskRunnerHandle::Get()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback),
blink::ServiceWorkerStatusCode::kErrorAbort));
return;
}
context_core_->registry()->FindRegistrationForScope(
net::SimplifyUrlForRequest(scope), key,
base::BindOnce(&DidFindRegistrationForStartActiveWorker,
std::move(callback)));
}
void ServiceWorkerContextWrapper::SkipWaitingWorker(
const GURL& scope,
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return;
context_core_->registry()->FindRegistrationForScope(
net::SimplifyUrlForRequest(scope), key,
base::BindOnce([](blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> registration) {
if (status != blink::ServiceWorkerStatusCode::kOk ||
!registration->waiting_version())
return;
registration->waiting_version()->set_skip_waiting(true);
registration->ActivateWaitingVersionWhenReady();
}));
}
void ServiceWorkerContextWrapper::UpdateRegistration(
const GURL& scope,
const blink::StorageKey& key) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return;
context_core_->registry()->FindRegistrationForScope(
net::SimplifyUrlForRequest(scope), key,
base::BindOnce(&ServiceWorkerContextWrapper::DidFindRegistrationForUpdate,
this));
}
void ServiceWorkerContextWrapper::SetForceUpdateOnPageLoad(
bool force_update_on_page_load) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_)
return;
context_core_->set_force_update_on_page_load(force_update_on_page_load);
}
void ServiceWorkerContextWrapper::AddObserver(
ServiceWorkerContextCoreObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
core_observer_list_->AddObserver(observer);
}
void ServiceWorkerContextWrapper::RemoveObserver(
ServiceWorkerContextCoreObserver* observer) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
core_observer_list_->RemoveObserver(observer);
}
ServiceWorkerContextWrapper::~ServiceWorkerContextWrapper() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (auto& observer : observer_list_)
observer.OnDestruct(static_cast<ServiceWorkerContext*>(this));
// Explicitly remove this object as an observer to avoid use-after-frees in
// tests where this object is not guaranteed to outlive the
// ServiceWorkerContextCore it wraps.
core_observer_list_->RemoveObserver(this);
if (identifiability_metrics_)
core_observer_list_->RemoveObserver(identifiability_metrics_.get());
}
void ServiceWorkerContextWrapper::FindRegistrationForScopeImpl(
const GURL& scope,
const blink::StorageKey& key,
bool include_installing_version,
FindRegistrationCallback callback) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!context_core_) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort,
nullptr);
return;
}
context_core_->registry()->FindRegistrationForScope(
net::SimplifyUrlForRequest(scope), key,
base::BindOnce(
&ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl, this,
include_installing_version, std::move(callback)));
}
void ServiceWorkerContextWrapper::DidFindRegistrationForFindImpl(
bool include_installing_version,
FindRegistrationCallback callback,
blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != blink::ServiceWorkerStatusCode::kOk) {
std::move(callback).Run(status, nullptr);
return;
}
// Attempt to activate the waiting version because the registration retrieved
// from the disk might have only the waiting version.
if (registration->waiting_version())
registration->ActivateWaitingVersionWhenReady();
scoped_refptr<ServiceWorkerVersion> active_version =
registration->active_version();
if (active_version) {
if (active_version->status() == ServiceWorkerVersion::ACTIVATING) {
// Wait until the version is activated.
active_version->RegisterStatusChangeCallback(base::BindOnce(
&ServiceWorkerContextWrapper::OnStatusChangedForFindReadyRegistration,
this, std::move(callback), std::move(registration)));
return;
}
DCHECK_EQ(ServiceWorkerVersion::ACTIVATED, active_version->status());
std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk,
std::move(registration));
return;
}
if (include_installing_version && registration->installing_version()) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk,
std::move(registration));
return;
}
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorNotFound,
nullptr);
}
void ServiceWorkerContextWrapper::OnStatusChangedForFindReadyRegistration(
FindRegistrationCallback callback,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
scoped_refptr<ServiceWorkerVersion> active_version =
registration->active_version();
if (!active_version ||
active_version->status() != ServiceWorkerVersion::ACTIVATED) {
std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorNotFound,
nullptr);
return;
}
std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk, registration);
}
void ServiceWorkerContextWrapper::DidDeleteAndStartOver(
blink::ServiceWorkerStatusCode status) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
DCHECK(running_service_workers_.empty());
is_deleting_and_starting_over_ = false;
storage_control_.reset();
if (status != blink::ServiceWorkerStatusCode::kOk) {
context_core_.reset();
return;
}
context_core_ =
std::make_unique<ServiceWorkerContextCore>(context_core_.get(), this);
DVLOG(1) << "Restarted ServiceWorkerContextCore successfully.";
context_core_->OnStorageWiped();
}
void ServiceWorkerContextWrapper::DidGetAllRegistrationsForGetAllOrigins(
GetUsageInfoCallback callback,
blink::ServiceWorkerStatusCode status,
const std::vector<ServiceWorkerRegistrationInfo>& registrations) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::vector<StorageUsageInfo> usage_infos;
std::map<GURL, StorageUsageInfo> origins;
for (const auto& registration_info : registrations) {
GURL origin = registration_info.scope.DeprecatedGetOriginAsURL();
auto it = origins.find(origin);
if (it == origins.end()) {
origins[origin] = StorageUsageInfo(
url::Origin::Create(origin),
registration_info.stored_version_size_bytes, base::Time());
} else {
it->second.total_size_bytes +=
registration_info.stored_version_size_bytes;
}
}
for (const auto& origin_info_pair : origins) {
usage_infos.push_back(origin_info_pair.second);
}
std::move(callback).Run(usage_infos);
}
void ServiceWorkerContextWrapper::DidFindRegistrationForUpdate(
blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> registration) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (status != blink::ServiceWorkerStatusCode::kOk)
return;
if (!context_core_)
return;
DCHECK(registration);
// TODO(jungkees): |force_bypass_cache| is set to true because the call stack
// is initiated by an update button on DevTools that expects the cache is
// bypassed. However, in order to provide options for callers to choose the
// cache bypass mode, plumb |force_bypass_cache| through to
// UpdateRegistration().
context_core_->UpdateServiceWorker(registration.get(),
true /* force_bypass_cache */);
}
void ServiceWorkerContextWrapper::DidFindRegistrationForNavigationHint(
StartServiceWorkerForNavigationHintCallback callback,
blink::ServiceWorkerStatusCode status,
scoped_refptr<ServiceWorkerRegistration> registration) {
TRACE_EVENT1("ServiceWorker", "DidFindRegistrationForNavigationHint",
"status", blink::ServiceWorkerStatusToString(status));
DCHECK_CURRENTLY_ON(BrowserThread::UI);
if (!registration) {
DCHECK_NE(status, blink::ServiceWorkerStatusCode::kOk);
std::move(callback).Run(StartServiceWorkerForNavigationHintResult::
NO_SERVICE_WORKER_REGISTRATION);
return;
}
if (!registration->active_version()) {
std::move(callback).Run(StartServiceWorkerForNavigationHintResult::
NO_ACTIVE_SERVICE_WORKER_VERSION);
return;
}
if (registration->active_version()->fetch_handler_existence() ==
ServiceWorkerVersion::FetchHandlerExistence::DOES_NOT_EXIST) {
std::move(callback).Run(
StartServiceWorkerForNavigationHintResult::NO_FETCH_HANDLER);
return;
}
if (registration->active_version()->running_status() ==
EmbeddedWorkerStatus::RUNNING) {
std::move(callback).Run(
StartServiceWorkerForNavigationHintResult::ALREADY_RUNNING);
return;
}
registration->active_version()->StartWorker(
ServiceWorkerMetrics::EventType::NAVIGATION_HINT,
base::BindOnce(
&ServiceWorkerContextWrapper::DidStartServiceWorkerForNavigationHint,
this, registration->scope(), std::move(callback)));
}
void ServiceWorkerContextWrapper::DidStartServiceWorkerForNavigationHint(
const GURL& scope,
StartServiceWorkerForNavigationHintCallback callback,
blink::ServiceWorkerStatusCode code) {
TRACE_EVENT2("ServiceWorker", "DidStartServiceWorkerForNavigationHint", "url",
scope.spec(), "code", blink::ServiceWorkerStatusToString(code));
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::move(callback).Run(
code == blink::ServiceWorkerStatusCode::kOk
? StartServiceWorkerForNavigationHintResult::STARTED
: StartServiceWorkerForNavigationHintResult::FAILED);
}
ServiceWorkerContextCore* ServiceWorkerContextWrapper::context() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return context_core_.get();
}
std::unique_ptr<blink::PendingURLLoaderFactoryBundle>
ServiceWorkerContextWrapper::
CreateNonNetworkPendingURLLoaderFactoryBundleForUpdateCheck(
BrowserContext* browser_context) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
ContentBrowserClient::NonNetworkURLLoaderFactoryMap non_network_factories;
GetContentClient()
->browser()
->RegisterNonNetworkServiceWorkerUpdateURLLoaderFactories(
browser_context, &non_network_factories);
auto factory_bundle =
std::make_unique<blink::PendingURLLoaderFactoryBundle>();
for (auto& pair : non_network_factories) {
const std::string& scheme = pair.first;
mojo::PendingRemote<network::mojom::URLLoaderFactory>& factory_remote =
pair.second;
factory_bundle->pending_scheme_specific_factories().emplace(
scheme, std::move(factory_remote));
}
return factory_bundle;
}
void ServiceWorkerContextWrapper::BindStorageControl(
mojo::PendingReceiver<storage::mojom::ServiceWorkerStorageControl>
receiver) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
bool run_storage_control_on_ui_thread =
#if BUILDFLAG(IS_ANDROID)
// Run ServiceWorkerStorageControl mojo receiver on IO thread on Android
// for performance reasons if it's specified.
// TODO(chikamune): Use a thread pool sequence instead of IO thread.
!base::FeatureList::IsEnabled(kServiceWorkerStorageControlOnIOThread);
#else
// The storage service always runs out of process on Desktop platforms.
// TODO(crbug.com/1055677): ServiceWorkerStorageControlImpl instance
// should live in the storage service. Currently,
// ServiceWorkerStorageControlImpl runs on UI thread to keep the previous
// behavior.
true;
#endif
if (storage_control_binder_for_test_) {
storage_control_binder_for_test_.Run(std::move(receiver));
} else if (run_storage_control_on_ui_thread) {
// TODO(crbug.com/1055677): Use storage_partition() to bind the control when
// ServiceWorkerStorageControl is sandboxed in the Storage Service.
DCHECK(!storage_control_);
// The database task runner is BLOCK_SHUTDOWN in order to support
// ClearSessionOnlyOrigins() (called due to the "clear on browser exit"
// content setting).
// TODO(falken): Only block shutdown for that particular task, when someday
// task runners support mixing task shutdown behaviors.
scoped_refptr<base::SequencedTaskRunner> database_task_runner =
base::ThreadPool::CreateSequencedTaskRunner(
{base::MayBlock(), base::TaskShutdownBehavior::BLOCK_SHUTDOWN});
storage_control_ =
std::make_unique<storage::ServiceWorkerStorageControlImpl>(
user_data_directory_, std::move(database_task_runner),
std::move(receiver));
} else {
// Drop `receiver` when the browser is shutting down.
if (!storage_partition())
return;
DCHECK(storage_partition()->GetStorageServicePartition());
storage_partition()
->GetStorageServicePartition()
->BindServiceWorkerStorageControl(std::move(receiver));
}
}
void ServiceWorkerContextWrapper::SetStorageControlBinderForTest(
StorageControlBinder binder) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
storage_control_binder_for_test_ = std::move(binder);
}
void ServiceWorkerContextWrapper::SetLoaderFactoryForUpdateCheckForTest(
scoped_refptr<network::SharedURLLoaderFactory> loader_factory) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
loader_factory_for_test_ = std::move(loader_factory);
}
scoped_refptr<network::SharedURLLoaderFactory>
ServiceWorkerContextWrapper::GetLoaderFactoryForUpdateCheck(const GURL& scope) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(https://crbug.com/1211361): Do we want to instrument this with
// devtools? It is currently not recorded at all.
// TODO(https://crbug.com/1239551): pass in proper client security state
return GetLoaderFactoryForBrowserInitiatedRequest(
scope,
/*version_id=*/absl::nullopt, /*client_security_state=*/nullptr);
}
scoped_refptr<network::SharedURLLoaderFactory>
ServiceWorkerContextWrapper::GetLoaderFactoryForMainScriptFetch(
const GURL& scope,
int64_t version_id,
network::mojom::ClientSecurityStatePtr client_security_state) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
return GetLoaderFactoryForBrowserInitiatedRequest(
scope, version_id, std::move(client_security_state));
}
scoped_refptr<network::SharedURLLoaderFactory>
ServiceWorkerContextWrapper::GetLoaderFactoryForBrowserInitiatedRequest(
const GURL& scope,
absl::optional<int64_t> version_id,
network::mojom::ClientSecurityStatePtr client_security_state) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
// TODO(falken): Replace this with URLLoaderInterceptor.
if (loader_factory_for_test_)
return loader_factory_for_test_;
if (!storage_partition()) {
return nullptr;
}
const url::Origin scope_origin = url::Origin::Create(scope);
mojo::PendingRemote<network::mojom::URLLoaderFactory> remote;
mojo::PendingReceiver<network::mojom::URLLoaderFactory> pending_receiver =
remote.InitWithNewPipeAndPassReceiver();
mojo::PendingRemote<network::mojom::TrustedURLLoaderHeaderClient>
header_client;
bool bypass_redirect_checks = false;
// Here we give nullptr for |factory_override|, because CORS is no-op for
// requests for this factory.
// TODO(yhirano): Use |factory_override| because someday not just CORS but
// CORB/CORP will use the factory and those are not no-ops for it
GetContentClient()->browser()->WillCreateURLLoaderFactory(
storage_partition_->browser_context(), /*frame=*/nullptr,
ChildProcessHost::kInvalidUniqueID,
ContentBrowserClient::URLLoaderFactoryType::kServiceWorkerScript,
scope_origin, /*navigation_id=*/absl::nullopt, ukm::kInvalidSourceIdObj,
&pending_receiver, &header_client, &bypass_redirect_checks,
/*disable_secure_dns=*/nullptr,
/*factory_override=*/nullptr);
// If we have a version_id, we are fetching a worker main script. We have a
// DevtoolsAgentHost ready for the worker and we can add the devtools override
// before instantiating the URLFactoryLoader.
if (version_id.has_value()) {
devtools_instrumentation::
WillCreateURLLoaderFactoryForServiceWorkerMainScript(
this, version_id.value(), &pending_receiver);
}
bool use_client_header_factory = header_client.is_valid();
if (use_client_header_factory) {
NavigationURLLoaderImpl::CreateURLLoaderFactoryWithHeaderClient(
std::move(header_client), std::move(pending_receiver),
storage_partition());
} else {
DCHECK(storage_partition());
if (base::FeatureList::IsEnabled(
features::kPrivateNetworkAccessForWorkers)) {
network::mojom::URLLoaderFactoryParamsPtr params =
storage_partition_->CreateURLLoaderFactoryParams();
params->client_security_state = std::move(client_security_state);
storage_partition_->GetNetworkContext()->CreateURLLoaderFactory(
std::move(pending_receiver), std::move(params));
} else {
// Set up a Mojo connection to the network loader factory if it's not been
// created yet.
scoped_refptr<network::SharedURLLoaderFactory> network_factory =
storage_partition_->GetURLLoaderFactoryForBrowserProcess();
network_factory->Clone(std::move(pending_receiver));
}
}
// Clone context()->loader_factory_bundle_for_update_check() and set up the
// default factory.
std::unique_ptr<network::PendingSharedURLLoaderFactory>
loader_factory_bundle_info =
context()->loader_factory_bundle_for_update_check()->Clone();
if (base::FeatureList::IsEnabled(
features::kEnableServiceWorkersForChromeUntrusted) &&
scope.scheme_piece() == kChromeUIUntrustedScheme) {
// If this is a Service Worker for a WebUI, the WebUI's URLDataSource needs
// to be registered. Registering a URLDataSource allows the
// WebUIURLLoaderFactory below to serve the resources for the WebUI. We
// register the URLDataSource here because the WebUI's resources are needed
// for the Service Worker update check to be performed which fetches the
// service worker script.
//
// This is similar to how we create a `WebUI` object in
// RenderFrameHostManager::GetFrameHostForNavigation(). Creating a `WebUI`
// also creates a `WebUIController` which register the URLDataSource for the
// WebUI which allows the navigation to be served correctly. We don't create
// a `WebUI` or a `WebUIController` for WebUI Service Workers so we
// register the URLDataSource directly.
if (auto* config = content::WebUIConfigMap::GetInstance().GetConfig(
browser_context(), scope_origin)) {
config->RegisterURLDataSource(browser_context());
static_cast<blink::PendingURLLoaderFactoryBundle*>(
loader_factory_bundle_info.get())
->pending_scheme_specific_factories()
.emplace(kChromeUIUntrustedScheme,
CreateWebUIServiceWorkerLoaderFactory(
browser_context(), kChromeUIUntrustedScheme,
base::flat_set<std::string>()));
}
}
static_cast<blink::PendingURLLoaderFactoryBundle*>(
loader_factory_bundle_info.get())
->pending_default_factory() = std::move(remote);
static_cast<blink::PendingURLLoaderFactoryBundle*>(
loader_factory_bundle_info.get())
->set_bypass_redirect_checks(bypass_redirect_checks);
return network::SharedURLLoaderFactory::Create(
std::move(loader_factory_bundle_info));
}
void ServiceWorkerContextWrapper::ClearRunningServiceWorkers() {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
for (const auto& kv : running_service_workers_) {
int64_t version_id = kv.first;
for (auto& observer : observer_list_)
observer.OnVersionStoppedRunning(version_id);
}
running_service_workers_.clear();
}
} // namespace content