Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ServiceWorkerInfo.h"
#include "ServiceWorkerManager.h"
#include "ServiceWorkerUtils.h"
#include "ServiceWorkerPrivate.h"
#include "ServiceWorkerScriptCache.h"
#include "mozilla/dom/ClientIPCTypes.h"
#include "mozilla/dom/ClientState.h"
#include "mozilla/dom/Promise.h"
#include "mozilla/dom/RemoteWorkerTypes.h"
#include "mozilla/dom/WorkerPrivate.h"
namespace mozilla::dom {
using mozilla::ipc::PrincipalInfo;
static_assert(nsIServiceWorkerInfo::STATE_PARSED ==
static_cast<uint16_t>(ServiceWorkerState::Parsed),
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
static_assert(nsIServiceWorkerInfo::STATE_INSTALLING ==
static_cast<uint16_t>(ServiceWorkerState::Installing),
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
static_assert(nsIServiceWorkerInfo::STATE_INSTALLED ==
static_cast<uint16_t>(ServiceWorkerState::Installed),
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
static_assert(nsIServiceWorkerInfo::STATE_ACTIVATING ==
static_cast<uint16_t>(ServiceWorkerState::Activating),
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
static_assert(nsIServiceWorkerInfo::STATE_ACTIVATED ==
static_cast<uint16_t>(ServiceWorkerState::Activated),
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
static_assert(nsIServiceWorkerInfo::STATE_REDUNDANT ==
static_cast<uint16_t>(ServiceWorkerState::Redundant),
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
static_assert(nsIServiceWorkerInfo::STATE_UNKNOWN ==
ContiguousEnumSize<ServiceWorkerState>::value,
"ServiceWorkerState enumeration value should match state values "
"from nsIServiceWorkerInfo.");
NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo)
NS_IMETHODIMP
ServiceWorkerInfo::GetId(nsAString& aId) {
MOZ_ASSERT(NS_IsMainThread());
aId = mWorkerPrivateId;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec) {
MOZ_ASSERT(NS_IsMainThread());
CopyUTF8toUTF16(mDescriptor.ScriptURL(), aScriptSpec);
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetCacheName(nsAString& aCacheName) {
MOZ_ASSERT(NS_IsMainThread());
aCacheName = mCacheName;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetLaunchCount(uint32_t* aLaunchCount) {
MOZ_ASSERT(aLaunchCount);
MOZ_ASSERT(NS_IsMainThread());
*aLaunchCount = mServiceWorkerPrivate->GetLaunchCount();
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetState(uint16_t* aState) {
MOZ_ASSERT(aState);
MOZ_ASSERT(NS_IsMainThread());
*aState = static_cast<uint16_t>(State());
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetDebugger(nsIWorkerDebugger** aResult) {
if (NS_WARN_IF(!aResult)) {
return NS_ERROR_FAILURE;
}
return mServiceWorkerPrivate->GetDebugger(aResult);
}
NS_IMETHODIMP
ServiceWorkerInfo::GetHandlesFetchEvents(bool* aValue) {
MOZ_ASSERT(aValue);
MOZ_ASSERT(NS_IsMainThread());
if (mHandlesFetch == Unknown) {
return NS_ERROR_FAILURE;
}
*aValue = HandlesFetch();
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetInstalledTime(PRTime* _retval) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(_retval);
*_retval = mInstalledTime;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetActivatedTime(PRTime* _retval) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(_retval);
*_retval = mActivatedTime;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetRedundantTime(PRTime* _retval) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(_retval);
*_retval = mRedundantTime;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetLifetimeDeadline(double* aLifetimeDeadline) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aLifetimeDeadline);
TimeStamp deadline = mServiceWorkerPrivate->GetLifetimeDeadline();
if (deadline.IsNull()) {
*aLifetimeDeadline = 0;
return NS_OK;
}
*aLifetimeDeadline =
(deadline - TimeStamp::ProcessCreation()).ToMilliseconds();
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetNavigationFaultCount(uint32_t* aNavigationFaultCount) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aNavigationFaultCount);
*aNavigationFaultCount = mNavigationFaultCount;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::GetTestingInjectCancellation(
nsresult* aTestingInjectCancellation) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(aTestingInjectCancellation);
*aTestingInjectCancellation = mTestingInjectCancellation;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::SetTestingInjectCancellation(
nsresult aTestingInjectCancellation) {
MOZ_ASSERT(NS_IsMainThread());
mTestingInjectCancellation = aTestingInjectCancellation;
return NS_OK;
}
NS_IMETHODIMP
ServiceWorkerInfo::AttachDebugger() {
return mServiceWorkerPrivate->AttachDebugger();
}
NS_IMETHODIMP
ServiceWorkerInfo::DetachDebugger() {
return mServiceWorkerPrivate->DetachDebugger();
}
NS_IMETHODIMP
ServiceWorkerInfo::TerminateWorker(JSContext* aCx,
mozilla::dom::Promise** aPromise) {
nsIGlobalObject* global = xpc::CurrentNativeGlobal(aCx);
if (NS_WARN_IF(!global)) {
return NS_ERROR_FAILURE;
}
ErrorResult rv;
RefPtr<Promise> outer = Promise::Create(global, rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
mServiceWorkerPrivate->TerminateWorker(Some(outer));
return NS_OK;
}
void ServiceWorkerInfo::UpdateState(ServiceWorkerState aState) {
MOZ_ASSERT(NS_IsMainThread());
#ifdef DEBUG
// Any state can directly transition to redundant, but everything else is
// ordered.
if (aState != ServiceWorkerState::Redundant) {
MOZ_ASSERT_IF(State() == ServiceWorkerState::Installing,
aState == ServiceWorkerState::Installed);
MOZ_ASSERT_IF(State() == ServiceWorkerState::Installed,
aState == ServiceWorkerState::Activating);
MOZ_ASSERT_IF(State() == ServiceWorkerState::Activating,
aState == ServiceWorkerState::Activated);
}
// Activated can only go to redundant.
MOZ_ASSERT_IF(State() == ServiceWorkerState::Activated,
aState == ServiceWorkerState::Redundant);
#endif
// Flush any pending functional events to the worker when it transitions to
// the activated state.
// TODO: Do we care that these events will race with the propagation of the
// state change?
if (State() != aState) {
mServiceWorkerPrivate->UpdateState(aState);
}
mDescriptor.SetState(aState);
if (State() == ServiceWorkerState::Redundant) {
serviceWorkerScriptCache::PurgeCache(mPrincipal, mCacheName);
mServiceWorkerPrivate->NoteDeadServiceWorkerInfo();
}
}
ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
const nsACString& aScope,
uint64_t aRegistrationId,
uint64_t aRegistrationVersion,
const nsACString& aScriptSpec,
const nsAString& aCacheName,
nsLoadFlags aImportsLoadFlags)
: mPrincipal(aPrincipal),
mDescriptor(GetNextID(), aRegistrationId, aRegistrationVersion,
aPrincipal, aScope, aScriptSpec, ServiceWorkerState::Parsed),
mCacheName(aCacheName),
mWorkerPrivateId(ComputeWorkerPrivateId()),
mImportsLoadFlags(aImportsLoadFlags),
mCreationTime(PR_Now()),
mCreationTimeStamp(TimeStamp::Now()),
mInstalledTime(0),
mActivatedTime(0),
mRedundantTime(0),
mServiceWorkerPrivate(new ServiceWorkerPrivate(this)),
mSkipWaitingFlag(false),
mHandlesFetch(Unknown),
mNavigationFaultCount(0),
mTestingInjectCancellation(NS_OK) {
MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default);
MOZ_ASSERT(mPrincipal);
// cache origin attributes so we can use them off main thread
mOriginAttributes = mPrincipal->OriginAttributesRef();
MOZ_ASSERT(!mDescriptor.ScriptURL().IsEmpty());
MOZ_ASSERT(!mCacheName.IsEmpty());
MOZ_ASSERT(!mWorkerPrivateId.IsEmpty());
// Scripts of a service worker should always be loaded bypass service workers.
// Otherwise, we might not be able to update a service worker correctly, if
// there is a service worker generating the script.
MOZ_DIAGNOSTIC_ASSERT(mImportsLoadFlags &
nsIChannel::LOAD_BYPASS_SERVICE_WORKER);
}
ServiceWorkerInfo::~ServiceWorkerInfo() {
MOZ_ASSERT(mServiceWorkerPrivate);
mServiceWorkerPrivate->NoteDeadServiceWorkerInfo();
}
static uint64_t gServiceWorkerInfoCurrentID = 0;
uint64_t ServiceWorkerInfo::GetNextID() const {
return ++gServiceWorkerInfoCurrentID;
}
void ServiceWorkerInfo::PostMessage(RefPtr<ServiceWorkerCloneData>&& aData,
const PostMessageSource& aSource) {
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
if (NS_WARN_IF(!swm)) {
return;
}
ServiceWorkerLifetimeExtension lifetime =
ServiceWorkerLifetimeExtension(NoLifetimeExtension{});
switch (aSource.type()) {
case PostMessageSource::TClientInfoAndState:
lifetime = swm->DetermineLifetimeForClient(
ClientInfo(aSource.get_ClientInfoAndState().info()));
break;
case PostMessageSource::TIPCServiceWorkerDescriptor:
lifetime = swm->DetermineLifetimeForServiceWorker(
ServiceWorkerDescriptor(aSource.get_IPCServiceWorkerDescriptor()));
break;
default:
MOZ_ASSERT_UNREACHABLE("Unexpected source type");
return;
}
mServiceWorkerPrivate->SendMessageEvent(std::move(aData), lifetime, aSource);
}
Maybe<ClientInfo> ServiceWorkerInfo::GetClientInfo() {
return mServiceWorkerPrivate->GetClientInfo();
}
TimeStamp ServiceWorkerInfo::LifetimeDeadline() {
return mServiceWorkerPrivate->GetLifetimeDeadline();
}
void ServiceWorkerInfo::UpdateInstalledTime() {
MOZ_ASSERT(State() == ServiceWorkerState::Installed);
MOZ_ASSERT(mInstalledTime == 0);
mInstalledTime =
mCreationTime +
static_cast<PRTime>(
(TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds());
}
void ServiceWorkerInfo::UpdateActivatedTime() {
MOZ_ASSERT(State() == ServiceWorkerState::Activated);
MOZ_ASSERT(mActivatedTime == 0);
mActivatedTime =
mCreationTime +
static_cast<PRTime>(
(TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds());
}
void ServiceWorkerInfo::UpdateRedundantTime() {
MOZ_ASSERT(State() == ServiceWorkerState::Redundant);
MOZ_ASSERT(mRedundantTime == 0);
mRedundantTime =
mCreationTime +
static_cast<PRTime>(
(TimeStamp::Now() - mCreationTimeStamp).ToMicroseconds());
}
void ServiceWorkerInfo::SetRegistrationVersion(uint64_t aVersion) {
mDescriptor.SetRegistrationVersion(aVersion);
}
} // namespace mozilla::dom