mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-02-27 04:38:02 +00:00

Add an error message of the following form for when a register/update job fails for network reasons: Failed to register/update a ServiceWorker for scope ‘http://mochi.test:8888/tests/dom/workers/test/serviceworkers/network_error/’: Load failed with status 404 for script ‘http://mochi.test:8888/tests/dom/workers/test/serviceworkers/404.js’. A mochitest is added that verifies this. To simplify the process of logging error messages, ServiceWorkerManager gains a new LocalizeAndReportToAllClients method that always provides the SW scope as the first argument to the localized string since all good error messages should include it. Its argument list takes an nsTArray<nsString> in order to reduce the potential for use-after-free scenarios from the char16_t** signature that unfortunately has rippled outwards from the nsIStringBundle interface. This potentially results in more memory allocation and byte shuffling than is strictly necessary, but we're also talking about rare error logging where it's better to optimize for easily adding the messages without needing to get hung up on the life-cycle of temporaries. nsTArray gained a std::initializer_list in bug 1228641. It is explicit, so inline argument usages may take a form along the lines of: `nsTArray<nsString> { string1, string2, ... }` This change did necessitate a change to nsContentUtils to add an nsTArray variant of FormatLocalizedString since the existing public function was slightly too clever. It used a template function to statically acquire the number of arguments at compile time, which is not compatible with the dynamic nsTArray usage. Since nsTArray may be useful to other consumers as well, I placed the conversion logic in nsContentUtils.
490 lines
16 KiB
C++
490 lines
16 KiB
C++
/* -*- 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/. */
|
|
|
|
#ifndef mozilla_dom_workers_serviceworkermanager_h
|
|
#define mozilla_dom_workers_serviceworkermanager_h
|
|
|
|
#include "nsIServiceWorkerManager.h"
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "ipc/IPCMessageUtils.h"
|
|
#include "mozilla/Attributes.h"
|
|
#include "mozilla/AutoRestore.h"
|
|
#include "mozilla/LinkedList.h"
|
|
#include "mozilla/Preferences.h"
|
|
#include "mozilla/TypedEnumBits.h"
|
|
#include "mozilla/UniquePtr.h"
|
|
#include "mozilla/WeakPtr.h"
|
|
#include "mozilla/dom/BindingUtils.h"
|
|
#include "mozilla/dom/Promise.h"
|
|
#include "mozilla/dom/ServiceWorkerCommon.h"
|
|
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
|
#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
|
|
#include "mozilla/dom/workers/ServiceWorkerRegistrationInfo.h"
|
|
#include "mozilla/ipc/BackgroundUtils.h"
|
|
#include "nsClassHashtable.h"
|
|
#include "nsDataHashtable.h"
|
|
#include "nsIIPCBackgroundChildCreateCallback.h"
|
|
#include "nsRefPtrHashtable.h"
|
|
#include "nsTArrayForwardDeclare.h"
|
|
#include "nsTObserverArray.h"
|
|
|
|
class mozIApplicationClearPrivateDataParams;
|
|
|
|
namespace mozilla {
|
|
|
|
class PrincipalOriginAttributes;
|
|
|
|
namespace dom {
|
|
|
|
class ServiceWorkerRegistrationListener;
|
|
|
|
namespace workers {
|
|
|
|
class ServiceWorkerClientInfo;
|
|
class ServiceWorkerInfo;
|
|
class ServiceWorkerJobQueue;
|
|
class ServiceWorkerManagerChild;
|
|
class ServiceWorkerPrivate;
|
|
|
|
class ServiceWorkerUpdateFinishCallback
|
|
{
|
|
protected:
|
|
virtual ~ServiceWorkerUpdateFinishCallback()
|
|
{}
|
|
|
|
public:
|
|
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateFinishCallback)
|
|
|
|
virtual
|
|
void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) = 0;
|
|
|
|
virtual
|
|
void UpdateFailed(ErrorResult& aStatus) = 0;
|
|
};
|
|
|
|
#define NS_SERVICEWORKERMANAGER_IMPL_IID \
|
|
{ /* f4f8755a-69ca-46e8-a65d-775745535990 */ \
|
|
0xf4f8755a, \
|
|
0x69ca, \
|
|
0x46e8, \
|
|
{ 0xa6, 0x5d, 0x77, 0x57, 0x45, 0x53, 0x59, 0x90 } \
|
|
}
|
|
|
|
/*
|
|
* The ServiceWorkerManager is a per-process global that deals with the
|
|
* installation, querying and event dispatch of ServiceWorkers for all the
|
|
* origins in the process.
|
|
*/
|
|
class ServiceWorkerManager final
|
|
: public nsIServiceWorkerManager
|
|
, public nsIIPCBackgroundChildCreateCallback
|
|
, public nsIObserver
|
|
{
|
|
friend class GetReadyPromiseRunnable;
|
|
friend class GetRegistrationsRunnable;
|
|
friend class GetRegistrationRunnable;
|
|
friend class ServiceWorkerJob;
|
|
friend class ServiceWorkerRegistrationInfo;
|
|
friend class ServiceWorkerUnregisterJob;
|
|
friend class ServiceWorkerUpdateJob;
|
|
friend class UpdateTimerCallback;
|
|
|
|
public:
|
|
NS_DECL_ISUPPORTS
|
|
NS_DECL_NSISERVICEWORKERMANAGER
|
|
NS_DECL_NSIIPCBACKGROUNDCHILDCREATECALLBACK
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
struct RegistrationDataPerPrincipal;
|
|
nsClassHashtable<nsCStringHashKey, RegistrationDataPerPrincipal> mRegistrationInfos;
|
|
|
|
nsTObserverArray<ServiceWorkerRegistrationListener*> mServiceWorkerRegistrationListeners;
|
|
|
|
nsRefPtrHashtable<nsISupportsHashKey, ServiceWorkerRegistrationInfo> mControlledDocuments;
|
|
|
|
// Track all documents that have attempted to register a service worker for a
|
|
// given scope.
|
|
typedef nsTArray<nsCOMPtr<nsIWeakReference>> WeakDocumentList;
|
|
nsClassHashtable<nsCStringHashKey, WeakDocumentList> mRegisteringDocuments;
|
|
|
|
// Track all intercepted navigation channels for a given scope. Channels are
|
|
// placed in the appropriate list before dispatch the FetchEvent to the worker
|
|
// thread and removed once FetchEvent processing dispatches back to the main
|
|
// thread.
|
|
//
|
|
// Note: Its safe to use weak references here because a RAII-style callback
|
|
// is registered with the channel before its added to this list. We
|
|
// are guaranteed the callback will fire before and remove the ref
|
|
// from this list before the channel is destroyed.
|
|
typedef nsTArray<nsIInterceptedChannel*> InterceptionList;
|
|
nsClassHashtable<nsCStringHashKey, InterceptionList> mNavigationInterceptions;
|
|
|
|
bool
|
|
IsAvailable(nsIPrincipal* aPrincipal, nsIURI* aURI);
|
|
|
|
bool
|
|
IsControlled(nsIDocument* aDocument, ErrorResult& aRv);
|
|
|
|
void
|
|
DispatchFetchEvent(const PrincipalOriginAttributes& aOriginAttributes,
|
|
nsIDocument* aDoc,
|
|
const nsAString& aDocumentIdForTopLevelNavigation,
|
|
nsIInterceptedChannel* aChannel,
|
|
bool aIsReload,
|
|
bool aIsSubresourceLoad,
|
|
ErrorResult& aRv);
|
|
|
|
void
|
|
Update(nsIPrincipal* aPrincipal,
|
|
const nsACString& aScope,
|
|
ServiceWorkerUpdateFinishCallback* aCallback);
|
|
|
|
void
|
|
SoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
|
|
const nsACString& aScope);
|
|
|
|
void
|
|
PropagateSoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
|
|
const nsAString& aScope);
|
|
|
|
void
|
|
PropagateRemove(const nsACString& aHost);
|
|
|
|
void
|
|
Remove(const nsACString& aHost);
|
|
|
|
void
|
|
PropagateRemoveAll();
|
|
|
|
void
|
|
RemoveAll();
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetRegistration(nsIPrincipal* aPrincipal, const nsACString& aScope) const;
|
|
|
|
ServiceWorkerRegistrationInfo*
|
|
CreateNewRegistration(const nsCString& aScope, nsIPrincipal* aPrincipal);
|
|
|
|
void
|
|
RemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void StoreRegistration(nsIPrincipal* aPrincipal,
|
|
ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
FinishFetch(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
/**
|
|
* Report an error for the given scope to any window we think might be
|
|
* interested, failing over to the Browser Console if we couldn't find any.
|
|
*
|
|
* Error messages should be localized, so you probably want to call
|
|
* LocalizeAndReportToAllClients instead, which in turn calls us after
|
|
* localizing the error.
|
|
*/
|
|
void
|
|
ReportToAllClients(const nsCString& aScope,
|
|
const nsString& aMessage,
|
|
const nsString& aFilename,
|
|
const nsString& aLine,
|
|
uint32_t aLineNumber,
|
|
uint32_t aColumnNumber,
|
|
uint32_t aFlags);
|
|
|
|
/**
|
|
* Report a localized error for the given scope to any window we think might
|
|
* be interested.
|
|
*
|
|
* Note that this method takes an nsTArray<nsString> for the parameters, not
|
|
* bare chart16_t*[]. You can use a std::initializer_list constructor inline
|
|
* so that argument might look like: nsTArray<nsString> { some_nsString,
|
|
* PromiseFlatString(some_nsSubString_aka_nsAString),
|
|
* NS_ConvertUTF8toUTF16(some_nsCString_or_nsCSubString),
|
|
* NS_LITERAL_STRING("some literal") }. If you have anything else, like a
|
|
* number, you can use an nsAutoString with AppendInt/friends.
|
|
*
|
|
* @param [aFlags]
|
|
* The nsIScriptError flag, one of errorFlag (0x0), warningFlag (0x1),
|
|
* infoFlag (0x8). We default to error if omitted because usually we're
|
|
* logging exceptional and/or obvious breakage.
|
|
*/
|
|
static void
|
|
LocalizeAndReportToAllClients(const nsCString& aScope,
|
|
const char* aStringKey,
|
|
const nsTArray<nsString>& aParamArray,
|
|
uint32_t aFlags = 0x0,
|
|
const nsString& aFilename = EmptyString(),
|
|
const nsString& aLine = EmptyString(),
|
|
uint32_t aLineNumber = 0,
|
|
uint32_t aColumnNumber = 0);
|
|
|
|
// Always consumes the error by reporting to consoles of all controlled
|
|
// documents.
|
|
void
|
|
HandleError(JSContext* aCx,
|
|
nsIPrincipal* aPrincipal,
|
|
const nsCString& aScope,
|
|
const nsString& aWorkerURL,
|
|
const nsString& aMessage,
|
|
const nsString& aFilename,
|
|
const nsString& aLine,
|
|
uint32_t aLineNumber,
|
|
uint32_t aColumnNumber,
|
|
uint32_t aFlags,
|
|
JSExnType aExnType);
|
|
|
|
UniquePtr<ServiceWorkerClientInfo>
|
|
GetClient(nsIPrincipal* aPrincipal,
|
|
const nsAString& aClientId,
|
|
ErrorResult& aRv);
|
|
|
|
void
|
|
GetAllClients(nsIPrincipal* aPrincipal,
|
|
const nsCString& aScope,
|
|
bool aIncludeUncontrolled,
|
|
nsTArray<ServiceWorkerClientInfo>& aDocuments);
|
|
|
|
void
|
|
MaybeClaimClient(nsIDocument* aDocument,
|
|
ServiceWorkerRegistrationInfo* aWorkerRegistration);
|
|
|
|
nsresult
|
|
ClaimClients(nsIPrincipal* aPrincipal, const nsCString& aScope, uint64_t aId);
|
|
|
|
nsresult
|
|
SetSkipWaitingFlag(nsIPrincipal* aPrincipal, const nsCString& aScope,
|
|
uint64_t aServiceWorkerID);
|
|
|
|
static already_AddRefed<ServiceWorkerManager>
|
|
GetInstance();
|
|
|
|
void
|
|
LoadRegistration(const ServiceWorkerRegistrationData& aRegistration);
|
|
|
|
void
|
|
LoadRegistrations(const nsTArray<ServiceWorkerRegistrationData>& aRegistrations);
|
|
|
|
// Used by remove() and removeAll() when clearing history.
|
|
// MUST ONLY BE CALLED FROM UnregisterIfMatchesHost!
|
|
void
|
|
ForceUnregister(RegistrationDataPerPrincipal* aRegistrationData,
|
|
ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
NS_IMETHOD
|
|
AddRegistrationEventListener(const nsAString& aScope,
|
|
ServiceWorkerRegistrationListener* aListener);
|
|
|
|
NS_IMETHOD
|
|
RemoveRegistrationEventListener(const nsAString& aScope,
|
|
ServiceWorkerRegistrationListener* aListener);
|
|
|
|
void
|
|
MaybeCheckNavigationUpdate(nsIDocument* aDoc);
|
|
|
|
nsresult
|
|
SendPushEvent(const nsACString& aOriginAttributes,
|
|
const nsACString& aScope,
|
|
const nsAString& aMessageId,
|
|
const Maybe<nsTArray<uint8_t>>& aData);
|
|
|
|
nsresult
|
|
NotifyUnregister(nsIPrincipal* aPrincipal, const nsAString& aScope);
|
|
|
|
private:
|
|
ServiceWorkerManager();
|
|
~ServiceWorkerManager();
|
|
|
|
void
|
|
Init();
|
|
|
|
already_AddRefed<ServiceWorkerJobQueue>
|
|
GetOrCreateJobQueue(const nsACString& aOriginSuffix,
|
|
const nsACString& aScope);
|
|
|
|
void
|
|
MaybeRemoveRegistrationInfo(const nsACString& aScopeKey);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetRegistration(const nsACString& aScopeKey,
|
|
const nsACString& aScope) const;
|
|
|
|
void
|
|
AbortCurrentUpdate(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
nsresult
|
|
Update(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
nsresult
|
|
GetDocumentRegistration(nsIDocument* aDoc,
|
|
ServiceWorkerRegistrationInfo** aRegistrationInfo);
|
|
|
|
nsresult
|
|
GetServiceWorkerForScope(nsPIDOMWindowInner* aWindow,
|
|
const nsAString& aScope,
|
|
WhichServiceWorker aWhichWorker,
|
|
nsISupports** aServiceWorker);
|
|
|
|
ServiceWorkerInfo*
|
|
GetActiveWorkerInfoForScope(const PrincipalOriginAttributes& aOriginAttributes,
|
|
const nsACString& aScope);
|
|
|
|
ServiceWorkerInfo*
|
|
GetActiveWorkerInfoForDocument(nsIDocument* aDocument);
|
|
|
|
void
|
|
InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration,
|
|
WhichServiceWorker aWhichOnes);
|
|
|
|
void
|
|
NotifyServiceWorkerRegistrationRemoved(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration,
|
|
nsIDocument* aDoc,
|
|
const nsAString& aDocumentId);
|
|
|
|
void
|
|
StopControllingADocument(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(nsPIDOMWindowInner* aWindow);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(nsIDocument* aDoc);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(nsIPrincipal* aPrincipal, nsIURI* aURI);
|
|
|
|
already_AddRefed<ServiceWorkerRegistrationInfo>
|
|
GetServiceWorkerRegistrationInfo(const nsACString& aScopeKey,
|
|
nsIURI* aURI);
|
|
|
|
// This method generates a key using appId and isInElementBrowser from the
|
|
// principal. We don't use the origin because it can change during the
|
|
// loading.
|
|
static nsresult
|
|
PrincipalToScopeKey(nsIPrincipal* aPrincipal, nsACString& aKey);
|
|
|
|
static void
|
|
AddScopeAndRegistration(const nsACString& aScope,
|
|
ServiceWorkerRegistrationInfo* aRegistation);
|
|
|
|
static bool
|
|
FindScopeForPath(const nsACString& aScopeKey,
|
|
const nsACString& aPath,
|
|
RegistrationDataPerPrincipal** aData, nsACString& aMatch);
|
|
|
|
static bool
|
|
HasScope(nsIPrincipal* aPrincipal, const nsACString& aScope);
|
|
|
|
static void
|
|
RemoveScopeAndRegistration(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
QueueFireEventOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration,
|
|
const nsAString& aName);
|
|
|
|
void
|
|
FireUpdateFoundOnServiceWorkerRegistrations(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
FireControllerChange(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
StorePendingReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
|
|
Promise* aPromise);
|
|
|
|
void
|
|
CheckPendingReadyPromises();
|
|
|
|
bool
|
|
CheckReadyPromise(nsPIDOMWindowInner* aWindow, nsIURI* aURI,
|
|
Promise* aPromise);
|
|
|
|
struct PendingReadyPromise final
|
|
{
|
|
PendingReadyPromise(nsIURI* aURI, Promise* aPromise)
|
|
: mURI(aURI), mPromise(aPromise)
|
|
{}
|
|
|
|
nsCOMPtr<nsIURI> mURI;
|
|
RefPtr<Promise> mPromise;
|
|
};
|
|
|
|
void AppendPendingOperation(nsIRunnable* aRunnable);
|
|
|
|
bool HasBackgroundActor() const
|
|
{
|
|
return !!mActor;
|
|
}
|
|
|
|
nsClassHashtable<nsISupportsHashKey, PendingReadyPromise> mPendingReadyPromises;
|
|
|
|
void
|
|
MaybeRemoveRegistration(ServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
// Removes all service worker registrations that matches the given pattern.
|
|
void
|
|
RemoveAllRegistrations(OriginAttributesPattern* aPattern);
|
|
|
|
RefPtr<ServiceWorkerManagerChild> mActor;
|
|
|
|
nsTArray<nsCOMPtr<nsIRunnable>> mPendingOperations;
|
|
|
|
bool mShuttingDown;
|
|
|
|
nsTArray<nsCOMPtr<nsIServiceWorkerManagerListener>> mListeners;
|
|
|
|
void
|
|
NotifyListenersOnRegister(nsIServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
NotifyListenersOnUnregister(nsIServiceWorkerRegistrationInfo* aRegistration);
|
|
|
|
void
|
|
AddRegisteringDocument(const nsACString& aScope, nsIDocument* aDoc);
|
|
|
|
class InterceptionReleaseHandle;
|
|
|
|
void
|
|
AddNavigationInterception(const nsACString& aScope,
|
|
nsIInterceptedChannel* aChannel);
|
|
|
|
void
|
|
RemoveNavigationInterception(const nsACString& aScope,
|
|
nsIInterceptedChannel* aChannel);
|
|
|
|
void
|
|
ScheduleUpdateTimer(nsIPrincipal* aPrincipal, const nsACString& aScope);
|
|
|
|
void
|
|
UpdateTimerFired(nsIPrincipal* aPrincipal, const nsACString& aScope);
|
|
|
|
void
|
|
MaybeSendUnregister(nsIPrincipal* aPrincipal, const nsACString& aScope);
|
|
|
|
nsresult
|
|
SendNotificationEvent(const nsAString& aEventName,
|
|
const nsACString& aOriginSuffix,
|
|
const nsACString& aScope,
|
|
const nsAString& aID,
|
|
const nsAString& aTitle,
|
|
const nsAString& aDir,
|
|
const nsAString& aLang,
|
|
const nsAString& aBody,
|
|
const nsAString& aTag,
|
|
const nsAString& aIcon,
|
|
const nsAString& aData,
|
|
const nsAString& aBehavior);
|
|
};
|
|
|
|
} // namespace workers
|
|
} // namespace dom
|
|
} // namespace mozilla
|
|
|
|
#endif // mozilla_dom_workers_serviceworkermanager_h
|