123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787 |
- // Copyright 2017 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "extensions/browser/service_worker_task_queue.h"
- #include <memory>
- #include <type_traits>
- #include <utility>
- #include <vector>
- #include "base/bind.h"
- #include "base/containers/contains.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/strings/stringprintf.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/syslog_logging.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/child_process_security_policy.h"
- #include "content/public/browser/console_message.h"
- #include "content/public/browser/render_process_host.h"
- #include "content/public/browser/service_worker_context.h"
- #include "content/public/browser/storage_partition.h"
- #include "extensions/browser/event_router.h"
- #include "extensions/browser/extension_error.h"
- #include "extensions/browser/extension_prefs.h"
- #include "extensions/browser/extension_registry.h"
- #include "extensions/browser/extension_util.h"
- #include "extensions/browser/extensions_browser_client.h"
- #include "extensions/browser/process_manager.h"
- #include "extensions/browser/renderer_startup_helper.h"
- #include "extensions/browser/service_worker_task_queue_factory.h"
- #include "extensions/common/constants.h"
- #include "extensions/common/manifest_constants.h"
- #include "extensions/common/manifest_handlers/background_info.h"
- #include "extensions/common/manifest_handlers/incognito_info.h"
- #include "third_party/blink/public/common/storage_key/storage_key.h"
- #include "third_party/blink/public/mojom/service_worker/service_worker_registration_options.mojom.h"
- #include "url/origin.h"
- using content::BrowserContext;
- namespace extensions {
- namespace {
- // A preference key storing the information about an extension that was
- // activated and has a registered worker based background page.
- const char kPrefServiceWorkerRegistrationInfo[] =
- "service_worker_registration_info";
- // The extension version of the registered service worker.
- const char kServiceWorkerVersion[] = "version";
- ServiceWorkerTaskQueue::TestObserver* g_test_observer = nullptr;
- // ServiceWorkerRegistration state of an activated extension.
- enum class RegistrationState {
- // Not registered.
- kNotRegistered,
- // Registration is inflight.
- kPending,
- // Registration is complete.
- kRegistered,
- };
- // Browser process worker state of an activated extension.
- enum class BrowserState {
- // Initial state, not started.
- kInitial,
- // Worker is in the process of starting from the browser process.
- kStarting,
- // Worker has completed starting (i.e. has seen DidStartWorkerForScope).
- kStarted,
- };
- // Render process worker state of an activated extension.
- enum class RendererState {
- // Initial state, neither started nor stopped.
- kInitial,
- // Worker thread has started.
- kStarted,
- // Worker thread has not started or has been stopped.
- kStopped,
- };
- } // namespace
- ServiceWorkerTaskQueue::ServiceWorkerTaskQueue(BrowserContext* browser_context)
- : browser_context_(browser_context) {}
- ServiceWorkerTaskQueue::~ServiceWorkerTaskQueue() {
- for (auto* const service_worker_context : observing_worker_contexts_)
- service_worker_context->RemoveObserver(this);
- }
- ServiceWorkerTaskQueue::TestObserver::TestObserver() {}
- ServiceWorkerTaskQueue::TestObserver::~TestObserver() {}
- // static
- ServiceWorkerTaskQueue* ServiceWorkerTaskQueue::Get(BrowserContext* context) {
- return ServiceWorkerTaskQueueFactory::GetForBrowserContext(context);
- }
- // The current worker related state of an activated extension.
- class ServiceWorkerTaskQueue::WorkerState {
- public:
- WorkerState() = default;
- WorkerState(const WorkerState&) = delete;
- WorkerState& operator=(const WorkerState&) = delete;
- void SetWorkerId(const WorkerId& worker_id, ProcessManager* process_manager) {
- if (worker_id_ && *worker_id_ != worker_id) {
- // Sanity check that the old worker is gone.
- DCHECK(!process_manager->HasServiceWorker(*worker_id_));
- // Clear stale renderer state if there's any.
- renderer_state_ = RendererState::kInitial;
- }
- worker_id_ = worker_id;
- }
- bool ready() const {
- return registration_state_ == RegistrationState::kRegistered &&
- browser_state_ == BrowserState::kStarted &&
- renderer_state_ == RendererState::kStarted && worker_id_.has_value();
- }
- bool has_pending_tasks() const { return !pending_tasks_.empty(); }
- private:
- friend class ServiceWorkerTaskQueue;
- RegistrationState registration_state_ = RegistrationState::kNotRegistered;
- BrowserState browser_state_ = BrowserState::kInitial;
- RendererState renderer_state_ = RendererState::kInitial;
- // Pending tasks that will be run once the worker becomes ready.
- std::vector<PendingTask> pending_tasks_;
- // Contains the worker's WorkerId associated with this WorkerState, once we
- // have discovered info about the worker.
- absl::optional<WorkerId> worker_id_;
- };
- void ServiceWorkerTaskQueue::DidStartWorkerForScope(
- const SequencedContextId& context_id,
- base::Time start_time,
- int64_t version_id,
- int process_id,
- int thread_id) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- const ExtensionId& extension_id = context_id.first.extension_id();
- const ActivationSequence& sequence = context_id.second;
- if (!IsCurrentSequence(extension_id, sequence)) {
- // Extension run with |sequence| was already deactivated.
- // TODO(lazyboy): Add a DCHECK that the worker in question is actually
- // shutting down soon.
- DCHECK(!GetWorkerState(context_id));
- return;
- }
- // HACK: The service worker layer might invoke this callback with an ID for a
- // RenderProcessHost that has already terminated. This isn't the right fix for
- // this, because it results in the internal state here stalling out - we'll
- // wait on the browser side to be ready, which will never happen. This should
- // be cleaned up on the next activation sequence, but this still isn't good.
- // The proper fix here is that the service worker layer shouldn't be invoking
- // this callback with stale processes.
- // https://crbug.com/1335821.
- if (!content::RenderProcessHost::FromID(process_id)) {
- // NOTE: The following is an antipattern [1]. We have this as a temporary
- // hack to avoid crashing for stable users while the bug is addressed.
- // [1]
- // https://chromium.googlesource.com/chromium/src/+/HEAD/styleguide/c++/c++-dos-and-donts.md#guarding-with-dcheck_is_on
- NOTREACHED();
- return;
- }
- UMA_HISTOGRAM_BOOLEAN("Extensions.ServiceWorkerBackground.StartWorkerStatus",
- true);
- UMA_HISTOGRAM_TIMES("Extensions.ServiceWorkerBackground.StartWorkerTime",
- base::Time::Now() - start_time);
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- const WorkerId worker_id = {extension_id, process_id, version_id, thread_id};
- // Note: If the worker has already stopped on worker thread
- // (DidStopServiceWorkerContext) before we got here (i.e. the browser has
- // finished starting the worker), then |worker_state_map_| will hold the
- // worker until deactivation.
- // TODO(lazyboy): We need to ensure that the worker is not stopped in the
- // renderer before we execute tasks in the browser process. This will also
- // avoid holding the worker in |worker_state_map_| until deactivation as noted
- // above.
- DCHECK_NE(BrowserState::kStarted, worker_state->browser_state_)
- << "Worker was already loaded";
- worker_state->SetWorkerId(worker_id, ProcessManager::Get(browser_context_));
- worker_state->browser_state_ = BrowserState::kStarted;
- RunPendingTasksIfWorkerReady(context_id);
- }
- void ServiceWorkerTaskQueue::DidStartWorkerFail(
- const SequencedContextId& context_id,
- blink::ServiceWorkerStatusCode status_code) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (!IsCurrentSequence(context_id.first.extension_id(), context_id.second)) {
- // This can happen is when the registration got unregistered right before we
- // tried to start it. See crbug.com/999027 for details.
- DCHECK(!GetWorkerState(context_id));
- return;
- }
- UMA_HISTOGRAM_BOOLEAN("Extensions.ServiceWorkerBackground.StartWorkerStatus",
- false);
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- if (g_test_observer) {
- g_test_observer->DidStartWorkerFail(context_id.first.extension_id(),
- worker_state->pending_tasks_.size(),
- status_code);
- }
- worker_state->pending_tasks_.clear();
- // TODO(https://crbug/1062936): Needs more thought: extension would be in
- // perma-broken state after this as the registration wouldn't be stored if
- // this happens.
- LOG(ERROR)
- << "DidStartWorkerFail " << context_id.first.extension_id() << ": "
- << static_cast<std::underlying_type_t<blink::ServiceWorkerStatusCode>>(
- status_code);
- }
- void ServiceWorkerTaskQueue::DidInitializeServiceWorkerContext(
- int render_process_id,
- const ExtensionId& extension_id,
- int64_t service_worker_version_id,
- int thread_id) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
- DCHECK(registry);
- const Extension* extension =
- registry->enabled_extensions().GetByID(extension_id);
- // The caller should have validated that the extension is still enabled.
- CHECK(extension);
- content::RenderProcessHost* process_host =
- content::RenderProcessHost::FromID(render_process_id);
- // The caller should have validated that the RenderProcessHost is still
- // active.
- CHECK(process_host);
- util::InitializeFileSchemeAccessForExtension(render_process_id, extension_id,
- browser_context_);
- ProcessManager::Get(browser_context_)
- ->RegisterServiceWorker({extension_id, render_process_id,
- service_worker_version_id, thread_id});
- RendererStartupHelperFactory::GetForBrowserContext(browser_context_)
- ->ActivateExtensionInProcess(*extension, process_host);
- }
- void ServiceWorkerTaskQueue::DidStartServiceWorkerContext(
- int render_process_id,
- const ExtensionId& extension_id,
- ActivationSequence activation_sequence,
- const GURL& service_worker_scope,
- int64_t service_worker_version_id,
- int thread_id) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (!IsCurrentSequence(extension_id, activation_sequence))
- return;
- SequencedContextId context_id(
- LazyContextId(browser_context_, extension_id, service_worker_scope),
- activation_sequence);
- const WorkerId worker_id = {extension_id, render_process_id,
- service_worker_version_id, thread_id};
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- // If |worker_state| had a worker running previously, for which we didn't
- // see DidStopServiceWorkerContext notification (typically happens on render
- // process shutdown), then we'd preserve stale state in |renderer_state_|.
- //
- // This isn't a problem because the next browser process readiness
- // (DidStartWorkerForScope) or the next renderer process readiness
- // (DidStartServiceWorkerContext) will clear the state, whichever happens
- // first.
- //
- // TODO(lazyboy): Update the renderer state in RenderProcessExited() and
- // uncomment the following DCHECK:
- // DCHECK_NE(RendererState::kStarted, worker_state->renderer_state_)
- // << "Worker already started";
- worker_state->SetWorkerId(worker_id, ProcessManager::Get(browser_context_));
- worker_state->renderer_state_ = RendererState::kStarted;
- RunPendingTasksIfWorkerReady(context_id);
- }
- void ServiceWorkerTaskQueue::DidStopServiceWorkerContext(
- int render_process_id,
- const ExtensionId& extension_id,
- ActivationSequence activation_sequence,
- const GURL& service_worker_scope,
- int64_t service_worker_version_id,
- int thread_id) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- if (!IsCurrentSequence(extension_id, activation_sequence))
- return;
- const WorkerId worker_id = {extension_id, render_process_id,
- service_worker_version_id, thread_id};
- ProcessManager::Get(browser_context_)->UnregisterServiceWorker(worker_id);
- SequencedContextId context_id(
- LazyContextId(browser_context_, extension_id, service_worker_scope),
- activation_sequence);
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- if (worker_state->worker_id_ != worker_id) {
- // We can see DidStopServiceWorkerContext right after DidInitialize and
- // without DidStartServiceWorkerContext.
- return;
- }
- DCHECK_NE(RendererState::kStopped, worker_state->renderer_state_);
- worker_state->renderer_state_ = RendererState::kStopped;
- worker_state->worker_id_ = absl::nullopt;
- }
- // static
- void ServiceWorkerTaskQueue::SetObserverForTest(TestObserver* observer) {
- g_test_observer = observer;
- }
- bool ServiceWorkerTaskQueue::ShouldEnqueueTask(BrowserContext* context,
- const Extension* extension) {
- // We call StartWorker every time we want to dispatch an event to an extension
- // Service worker.
- // TODO(lazyboy): Is that a problem?
- return true;
- }
- void ServiceWorkerTaskQueue::AddPendingTask(
- const LazyContextId& lazy_context_id,
- PendingTask task) {
- DCHECK(lazy_context_id.is_for_service_worker());
- // TODO(lazyboy): Do we need to handle incognito context?
- auto sequence = GetCurrentSequence(lazy_context_id.extension_id());
- DCHECK(sequence) << "Trying to add pending task to an inactive extension: "
- << lazy_context_id.extension_id();
- const SequencedContextId context_id(lazy_context_id, *sequence);
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- auto& tasks = worker_state->pending_tasks_;
- bool needs_start_worker = tasks.empty();
- tasks.push_back(std::move(task));
- if (worker_state->registration_state_ != RegistrationState::kRegistered) {
- // If the worker hasn't finished registration, wait for it to complete.
- // DidRegisterServiceWorker will Start worker to run |task| later.
- return;
- }
- // Start worker if there isn't any request to start worker with |context_id|
- // is in progress.
- if (needs_start_worker)
- RunTasksAfterStartWorker(context_id);
- }
- void ServiceWorkerTaskQueue::ActivateExtension(const Extension* extension) {
- DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
- const ExtensionId extension_id = extension->id();
- ActivationSequence current_sequence(++next_activation_sequence_);
- activation_sequences_[extension_id] = current_sequence;
- SequencedContextId context_id(
- LazyContextId(browser_context_, extension_id, extension->url()),
- current_sequence);
- DCHECK(!base::Contains(worker_state_map_, context_id));
- WorkerState& worker_state = worker_state_map_[context_id];
- content::ServiceWorkerContext* service_worker_context =
- GetServiceWorkerContext(extension->id());
- StartObserving(service_worker_context);
- // Note: version.IsValid() = false implies we didn't have any prefs stored.
- base::Version version = RetrieveRegisteredServiceWorkerVersion(extension_id);
- const bool service_worker_already_registered =
- version.IsValid() && version == extension->version();
- if (g_test_observer) {
- g_test_observer->OnActivateExtension(extension_id,
- !service_worker_already_registered);
- }
- if (service_worker_already_registered) {
- worker_state.registration_state_ = RegistrationState::kRegistered;
- VerifyRegistration(service_worker_context, context_id, extension->url());
- return;
- }
- worker_state.registration_state_ = RegistrationState::kPending;
- RegisterServiceWorker(RegistrationReason::REGISTER_ON_EXTENSION_LOAD,
- context_id, *extension);
- }
- void ServiceWorkerTaskQueue::VerifyRegistration(
- content::ServiceWorkerContext* service_worker_context,
- const SequencedContextId& context_id,
- const GURL& scope) {
- service_worker_context->CheckHasServiceWorker(
- scope, blink::StorageKey(url::Origin::Create(scope)),
- base::BindOnce(&ServiceWorkerTaskQueue::DidVerifyRegistration,
- weak_factory_.GetWeakPtr(), context_id));
- }
- void ServiceWorkerTaskQueue::RegisterServiceWorker(
- RegistrationReason reason,
- const SequencedContextId& context_id,
- const Extension& extension) {
- GURL script_url = extension.GetResourceURL(
- BackgroundInfo::GetBackgroundServiceWorkerScript(&extension));
- blink::mojom::ServiceWorkerRegistrationOptions option;
- if (BackgroundInfo::GetBackgroundServiceWorkerType(&extension) ==
- BackgroundServiceWorkerType::kModule) {
- option.type = blink::mojom::ScriptType::kModule;
- }
- option.scope = extension.url();
- content::ServiceWorkerContext* service_worker_context =
- GetServiceWorkerContext(extension.id());
- service_worker_context->RegisterServiceWorker(
- script_url, blink::StorageKey(url::Origin::Create(option.scope)), option,
- base::BindOnce(&ServiceWorkerTaskQueue::DidRegisterServiceWorker,
- weak_factory_.GetWeakPtr(), context_id, reason,
- base::Time::Now()));
- }
- void ServiceWorkerTaskQueue::DeactivateExtension(const Extension* extension) {
- const ExtensionId extension_id = extension->id();
- RemoveRegisteredServiceWorkerInfo(extension_id);
- absl::optional<ActivationSequence> sequence =
- GetCurrentSequence(extension_id);
- // Extension was never activated, this happens in tests.
- if (!sequence)
- return;
- activation_sequences_.erase(extension_id);
- SequencedContextId context_id(
- LazyContextId(browser_context_, extension_id, extension->url()),
- *sequence);
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- // TODO(lazyboy): Run orphaned tasks with nullptr ContextInfo.
- worker_state->pending_tasks_.clear();
- worker_state_map_.erase(context_id);
- content::ServiceWorkerContext* service_worker_context =
- GetServiceWorkerContext(extension->id());
- service_worker_context->UnregisterServiceWorker(
- extension->url(), blink::StorageKey(extension->origin()),
- base::BindOnce(&ServiceWorkerTaskQueue::DidUnregisterServiceWorker,
- weak_factory_.GetWeakPtr(), extension_id, *sequence));
- StopObserving(service_worker_context);
- }
- void ServiceWorkerTaskQueue::RunTasksAfterStartWorker(
- const SequencedContextId& context_id) {
- DCHECK(context_id.first.is_for_service_worker());
- const LazyContextId& lazy_context_id = context_id.first;
- if (lazy_context_id.browser_context() != browser_context_)
- return;
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK_NE(BrowserState::kStarted, worker_state->browser_state_);
- content::StoragePartition* partition =
- util::GetStoragePartitionForExtensionId(
- lazy_context_id.extension_id(), lazy_context_id.browser_context());
- content::ServiceWorkerContext* service_worker_context =
- partition->GetServiceWorkerContext();
- const GURL& scope = context_id.first.service_worker_scope();
- service_worker_context->StartWorkerForScope(
- scope, blink::StorageKey(url::Origin::Create(scope)),
- base::BindOnce(&ServiceWorkerTaskQueue::DidStartWorkerForScope,
- weak_factory_.GetWeakPtr(), context_id, base::Time::Now()),
- base::BindOnce(&ServiceWorkerTaskQueue::DidStartWorkerFail,
- weak_factory_.GetWeakPtr(), context_id));
- }
- void ServiceWorkerTaskQueue::DidRegisterServiceWorker(
- const SequencedContextId& context_id,
- RegistrationReason reason,
- base::Time start_time,
- blink::ServiceWorkerStatusCode status_code) {
- ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
- const ExtensionId& extension_id = context_id.first.extension_id();
- DCHECK(registry);
- const Extension* extension =
- registry->enabled_extensions().GetByID(extension_id);
- if (!extension) {
- return;
- }
- if (!IsCurrentSequence(extension_id, context_id.second))
- return;
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- const bool success = status_code == blink::ServiceWorkerStatusCode::kOk;
- UMA_HISTOGRAM_BOOLEAN("Extensions.ServiceWorkerBackground.RegistrationStatus",
- success);
- if (reason == RegistrationReason::RE_REGISTER_ON_STATE_MISMATCH) {
- UMA_HISTOGRAM_BOOLEAN(
- "Extensions.ServiceWorkerBackground.RegistrationMismatchMitigated",
- success);
- if (g_test_observer)
- g_test_observer->RegistrationMismatchMitigated(success);
- }
- if (!success) {
- std::string msg = base::StringPrintf(
- "Service worker registration failed. Status code: %d",
- static_cast<int>(status_code));
- auto error = std::make_unique<ManifestError>(
- extension_id, base::UTF8ToUTF16(msg),
- base::UTF8ToUTF16(manifest_keys::kBackground),
- base::UTF8ToUTF16(
- BackgroundInfo::GetBackgroundServiceWorkerScript(extension)));
- ExtensionsBrowserClient::Get()->ReportError(browser_context_,
- std::move(error));
- return;
- }
- UMA_HISTOGRAM_TIMES("Extensions.ServiceWorkerBackground.RegistrationTime",
- base::Time::Now() - start_time);
- worker_state->registration_state_ = RegistrationState::kRegistered;
- SetRegisteredServiceWorkerInfo(extension->id(), extension->version());
- if (worker_state->has_pending_tasks()) {
- // TODO(lazyboy): If worker for |context_id| is already running, consider
- // not calling StartWorker. This should be straightforward now that service
- // worker's internal state is on the UI thread rather than the IO thread.
- RunTasksAfterStartWorker(context_id);
- }
- }
- void ServiceWorkerTaskQueue::DidUnregisterServiceWorker(
- const ExtensionId& extension_id,
- ActivationSequence sequence,
- bool success) {
- // Extension run with |sequence| was already deactivated.
- if (!IsCurrentSequence(extension_id, sequence))
- return;
- // TODO(lazyboy): Handle success = false case.
- if (!success)
- LOG(ERROR) << "Failed to unregister service worker!";
- }
- base::Version ServiceWorkerTaskQueue::RetrieveRegisteredServiceWorkerVersion(
- const ExtensionId& extension_id) {
- if (browser_context_->IsOffTheRecord()) {
- auto it = off_the_record_registrations_.find(extension_id);
- return it != off_the_record_registrations_.end() ? it->second
- : base::Version();
- }
- const base::DictionaryValue* info = nullptr;
- ExtensionPrefs::Get(browser_context_)
- ->ReadPrefAsDictionary(extension_id, kPrefServiceWorkerRegistrationInfo,
- &info);
- if (!info) {
- return base::Version();
- }
- if (const std::string* version_string =
- info->FindStringKey(kServiceWorkerVersion)) {
- return base::Version(*version_string);
- }
- return base::Version();
- }
- void ServiceWorkerTaskQueue::SetRegisteredServiceWorkerInfo(
- const ExtensionId& extension_id,
- const base::Version& version) {
- DCHECK(version.IsValid());
- if (browser_context_->IsOffTheRecord()) {
- off_the_record_registrations_[extension_id] = version;
- } else {
- auto info = std::make_unique<base::DictionaryValue>();
- info->SetStringKey(kServiceWorkerVersion, version.GetString());
- ExtensionPrefs::Get(browser_context_)
- ->UpdateExtensionPref(extension_id, kPrefServiceWorkerRegistrationInfo,
- std::move(info));
- }
- }
- void ServiceWorkerTaskQueue::RemoveRegisteredServiceWorkerInfo(
- const ExtensionId& extension_id) {
- if (browser_context_->IsOffTheRecord()) {
- off_the_record_registrations_.erase(extension_id);
- } else {
- ExtensionPrefs::Get(browser_context_)
- ->UpdateExtensionPref(extension_id, kPrefServiceWorkerRegistrationInfo,
- nullptr);
- }
- }
- void ServiceWorkerTaskQueue::RunPendingTasksIfWorkerReady(
- const SequencedContextId& context_id) {
- WorkerState* worker_state = GetWorkerState(context_id);
- DCHECK(worker_state);
- if (!worker_state->ready()) {
- // Worker isn't ready yet, wait for next event and run the tasks then.
- return;
- }
- // Running |pending_tasks_[context_id]| marks the completion of
- // DidStartWorkerForScope, clean up |browser_ready| state of the worker so
- // that new tasks can be queued up.
- worker_state->browser_state_ = BrowserState::kInitial;
- DCHECK(worker_state->has_pending_tasks())
- << "Worker ready, but no tasks to run!";
- std::vector<PendingTask> tasks;
- std::swap(worker_state->pending_tasks_, tasks);
- DCHECK(worker_state->worker_id_);
- const auto& worker_id = *worker_state->worker_id_;
- for (auto& task : tasks) {
- auto context_info = std::make_unique<LazyContextTaskQueue::ContextInfo>(
- context_id.first.extension_id(),
- content::RenderProcessHost::FromID(worker_id.render_process_id),
- worker_id.version_id, worker_id.thread_id,
- context_id.first.service_worker_scope());
- std::move(task).Run(std::move(context_info));
- }
- }
- bool ServiceWorkerTaskQueue::IsCurrentSequence(
- const ExtensionId& extension_id,
- ActivationSequence sequence) const {
- auto current_sequence = GetCurrentSequence(extension_id);
- return current_sequence == sequence;
- }
- absl::optional<ActivationSequence> ServiceWorkerTaskQueue::GetCurrentSequence(
- const ExtensionId& extension_id) const {
- auto iter = activation_sequences_.find(extension_id);
- if (iter == activation_sequences_.end())
- return absl::nullopt;
- return iter->second;
- }
- void ServiceWorkerTaskQueue::OnReportConsoleMessage(
- int64_t version_id,
- const GURL& scope,
- const content::ConsoleMessage& message) {
- if (message.message_level != blink::mojom::ConsoleMessageLevel::kError) {
- // We don't report certain low-severity errors.
- return;
- }
- auto error_instance = std::make_unique<RuntimeError>(
- scope.host(), browser_context_->IsOffTheRecord(),
- base::UTF8ToUTF16(content::MessageSourceToString(message.source)),
- message.message,
- StackTrace(1, StackFrame(message.line_number, 1,
- base::UTF8ToUTF16(message.source_url.spec()),
- u"")) /* Construct a trace to contain
- one frame with the error */
- ,
- message.source_url,
- content::ConsoleMessageLevelToLogSeverity(message.message_level),
- -1 /* a service worker does not have a render_view_id */,
- -1 /* TODO(crbug.com/1218812): Retrieve render_process_id */);
- ExtensionsBrowserClient::Get()->ReportError(browser_context_,
- std::move(error_instance));
- }
- void ServiceWorkerTaskQueue::OnDestruct(
- content::ServiceWorkerContext* context) {
- StopObserving(context);
- }
- size_t ServiceWorkerTaskQueue::GetNumPendingTasksForTest(
- const LazyContextId& lazy_context_id) {
- auto current_sequence = GetCurrentSequence(lazy_context_id.extension_id());
- if (!current_sequence)
- return 0u;
- const SequencedContextId context_id(lazy_context_id, *current_sequence);
- WorkerState* worker_state = GetWorkerState(context_id);
- return worker_state ? worker_state->pending_tasks_.size() : 0u;
- }
- ServiceWorkerTaskQueue::WorkerState* ServiceWorkerTaskQueue::GetWorkerState(
- const SequencedContextId& context_id) {
- auto worker_iter = worker_state_map_.find(context_id);
- return worker_iter == worker_state_map_.end() ? nullptr
- : &worker_iter->second;
- }
- content::ServiceWorkerContext* ServiceWorkerTaskQueue::GetServiceWorkerContext(
- const ExtensionId& extension_id) {
- return util::GetStoragePartitionForExtensionId(extension_id, browser_context_)
- ->GetServiceWorkerContext();
- }
- void ServiceWorkerTaskQueue::StartObserving(
- content::ServiceWorkerContext* service_worker_context) {
- if (observing_worker_contexts_.count(service_worker_context) == 0u)
- service_worker_context->AddObserver(this);
- observing_worker_contexts_.insert(service_worker_context);
- }
- void ServiceWorkerTaskQueue::StopObserving(
- content::ServiceWorkerContext* service_worker_context) {
- auto iter_pair =
- observing_worker_contexts_.equal_range(service_worker_context);
- DCHECK(iter_pair.first != observing_worker_contexts_.end());
- if (std::distance(iter_pair.first, iter_pair.second) == 1)
- service_worker_context->RemoveObserver(this);
- observing_worker_contexts_.erase(iter_pair.first);
- }
- void ServiceWorkerTaskQueue::DidVerifyRegistration(
- const SequencedContextId& context_id,
- content::ServiceWorkerCapability capability) {
- const bool is_registered =
- capability != content::ServiceWorkerCapability::NO_SERVICE_WORKER;
- UMA_HISTOGRAM_BOOLEAN(
- "Extensions.ServiceWorkerBackground.RegistrationWhenExpected",
- is_registered);
- if (is_registered)
- return;
- // We expected a SW registration (as ExtensionPrefs said so), but there isn't
- // one. Re-register SW script if the extension is still installed (it's
- // possible it was uninstalled while we were checking).
- const ExtensionId& extension_id = context_id.first.extension_id();
- ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
- DCHECK(registry);
- const Extension* extension =
- registry->enabled_extensions().GetByID(extension_id);
- if (!extension)
- return;
- UMA_HISTOGRAM_ENUMERATION(
- "Extensions.ServiceWorkerBackground.RegistrationMismatchLocation",
- extension->location());
- RegisterServiceWorker(RegistrationReason::RE_REGISTER_ON_STATE_MISMATCH,
- context_id, *extension);
- }
- void ServiceWorkerTaskQueue::ActivateIncognitoSplitModeExtensions(
- ServiceWorkerTaskQueue* other) {
- DCHECK(browser_context_->IsOffTheRecord())
- << "Only need to activate split mode extensions for an OTR context";
- for (const auto& activated : other->activation_sequences_) {
- ExtensionRegistry* registry = ExtensionRegistry::Get(browser_context_);
- DCHECK(registry);
- const Extension* extension =
- registry->enabled_extensions().GetByID(activated.first);
- if (extension && IncognitoInfo::IsSplitMode(extension))
- ActivateExtension(extension);
- }
- }
- } // namespace extensions
|