mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-30 04:17:31 +09:00
import FIREFOX_52_6_0esr_RELEASE from mozilla-esr52 hg repo
This commit is contained in:
commit
dcd9973243
150858 changed files with 23884658 additions and 0 deletions
74
dom/workers/ChromeWorkerScope.cpp
Normal file
74
dom/workers/ChromeWorkerScope.cpp
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
/* -*- 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 "ChromeWorkerScope.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
|
||||
#include "nsXPCOM.h"
|
||||
#include "nsNativeCharsetUtils.h"
|
||||
#include "nsString.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using namespace mozilla::dom;
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
#ifdef BUILD_CTYPES
|
||||
|
||||
char*
|
||||
UnicodeToNative(JSContext* aCx, const char16_t* aSource, size_t aSourceLen)
|
||||
{
|
||||
nsDependentString unicode(aSource, aSourceLen);
|
||||
|
||||
nsAutoCString native;
|
||||
if (NS_FAILED(NS_CopyUnicodeToNative(unicode, native))) {
|
||||
JS_ReportErrorASCII(aCx, "Could not convert string to native charset!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* result = static_cast<char*>(JS_malloc(aCx, native.Length() + 1));
|
||||
if (!result) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
memcpy(result, native.get(), native.Length());
|
||||
result[native.Length()] = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif // BUILD_CTYPES
|
||||
|
||||
} // namespace
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
bool
|
||||
DefineChromeWorkerFunctions(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
|
||||
{
|
||||
// Currently ctypes is the only special property given to ChromeWorkers.
|
||||
#ifdef BUILD_CTYPES
|
||||
{
|
||||
JS::Rooted<JS::Value> ctypes(aCx);
|
||||
if (!JS_InitCTypesClass(aCx, aGlobal) ||
|
||||
!JS_GetProperty(aCx, aGlobal, "ctypes", &ctypes)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
static const JSCTypesCallbacks callbacks = {
|
||||
UnicodeToNative
|
||||
};
|
||||
|
||||
JS_SetCTypesCallbacks(ctypes.toObjectOrNull(), &callbacks);
|
||||
}
|
||||
#endif // BUILD_CTYPES
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
19
dom/workers/ChromeWorkerScope.h
Normal file
19
dom/workers/ChromeWorkerScope.h
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/* -*- 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_chromeworkerscope_h__
|
||||
#define mozilla_dom_workers_chromeworkerscope_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
bool
|
||||
DefineChromeWorkerFunctions(JSContext* aCx, JS::Handle<JSObject*> aGlobal);
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_chromeworkerscope_h__
|
||||
264
dom/workers/FileReaderSync.cpp
Normal file
264
dom/workers/FileReaderSync.cpp
Normal file
|
|
@ -0,0 +1,264 @@
|
|||
/* -*- 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 "FileReaderSync.h"
|
||||
|
||||
#include "jsfriendapi.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "mozilla/Base64.h"
|
||||
#include "mozilla/dom/EncodingUtils.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "mozilla/dom/FileReaderSyncBinding.h"
|
||||
#include "nsCExternalHandlerService.h"
|
||||
#include "nsComponentManagerUtils.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDOMClassInfoID.h"
|
||||
#include "nsError.h"
|
||||
#include "nsIConverterInputStream.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsISeekableStream.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "RuntimeService.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::GlobalObject;
|
||||
|
||||
// static
|
||||
already_AddRefed<FileReaderSync>
|
||||
FileReaderSync::Constructor(const GlobalObject& aGlobal, ErrorResult& aRv)
|
||||
{
|
||||
RefPtr<FileReaderSync> frs = new FileReaderSync();
|
||||
|
||||
return frs.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
FileReaderSync::WrapObject(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGivenProto,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
return FileReaderSyncBinding::Wrap(aCx, this, aGivenProto, aReflector);
|
||||
}
|
||||
|
||||
void
|
||||
FileReaderSync::ReadAsArrayBuffer(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aScopeObj,
|
||||
Blob& aBlob,
|
||||
JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
uint64_t blobSize = aBlob.GetSize(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<char[], JS::FreePolicy> bufferData(js_pod_malloc<char>(blobSize));
|
||||
if (!bufferData) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t numRead;
|
||||
aRv = stream->Read(bufferData.get(), blobSize, &numRead);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
NS_ASSERTION(numRead == blobSize, "failed to read data");
|
||||
|
||||
JSObject* arrayBuffer = JS_NewArrayBufferWithContents(aCx, blobSize, bufferData.get());
|
||||
if (!arrayBuffer) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
// arrayBuffer takes the ownership when it is not null. Otherwise we
|
||||
// need to release it explicitly.
|
||||
mozilla::Unused << bufferData.release();
|
||||
|
||||
aRetval.set(arrayBuffer);
|
||||
}
|
||||
|
||||
void
|
||||
FileReaderSync::ReadAsBinaryString(Blob& aBlob,
|
||||
nsAString& aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t numRead;
|
||||
do {
|
||||
char readBuf[4096];
|
||||
aRv = stream->Read(readBuf, sizeof(readBuf), &numRead);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t oldLength = aResult.Length();
|
||||
AppendASCIItoUTF16(Substring(readBuf, readBuf + numRead), aResult);
|
||||
if (aResult.Length() - oldLength != numRead) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
} while (numRead > 0);
|
||||
}
|
||||
|
||||
void
|
||||
FileReaderSync::ReadAsText(Blob& aBlob,
|
||||
const Optional<nsAString>& aEncoding,
|
||||
nsAString& aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString encoding;
|
||||
unsigned char sniffBuf[3] = { 0, 0, 0 };
|
||||
uint32_t numRead;
|
||||
aRv = stream->Read(reinterpret_cast<char*>(sniffBuf),
|
||||
sizeof(sniffBuf), &numRead);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
// The BOM sniffing is baked into the "decode" part of the Encoding
|
||||
// Standard, which the File API references.
|
||||
if (!nsContentUtils::CheckForBOM(sniffBuf, numRead, encoding)) {
|
||||
// BOM sniffing failed. Try the API argument.
|
||||
if (!aEncoding.WasPassed() ||
|
||||
!EncodingUtils::FindEncodingForLabel(aEncoding.Value(),
|
||||
encoding)) {
|
||||
// API argument failed. Try the type property of the blob.
|
||||
nsAutoString type16;
|
||||
aBlob.GetType(type16);
|
||||
NS_ConvertUTF16toUTF8 type(type16);
|
||||
nsAutoCString specifiedCharset;
|
||||
bool haveCharset;
|
||||
int32_t charsetStart, charsetEnd;
|
||||
NS_ExtractCharsetFromContentType(type,
|
||||
specifiedCharset,
|
||||
&haveCharset,
|
||||
&charsetStart,
|
||||
&charsetEnd);
|
||||
if (!EncodingUtils::FindEncodingForLabel(specifiedCharset, encoding)) {
|
||||
// Type property failed. Use UTF-8.
|
||||
encoding.AssignLiteral("UTF-8");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISeekableStream> seekable = do_QueryInterface(stream);
|
||||
if (!seekable) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
// Seek to 0 because to undo the BOM sniffing advance. UTF-8 and UTF-16
|
||||
// decoders will swallow the BOM.
|
||||
aRv = seekable->Seek(nsISeekableStream::NS_SEEK_SET, 0);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = ConvertStream(stream, encoding.get(), aResult);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
FileReaderSync::ReadAsDataURL(Blob& aBlob, nsAString& aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsAutoString scratchResult;
|
||||
scratchResult.AssignLiteral("data:");
|
||||
|
||||
nsString contentType;
|
||||
aBlob.GetType(contentType);
|
||||
|
||||
if (contentType.IsEmpty()) {
|
||||
scratchResult.AppendLiteral("application/octet-stream");
|
||||
} else {
|
||||
scratchResult.Append(contentType);
|
||||
}
|
||||
scratchResult.AppendLiteral(";base64,");
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
aBlob.GetInternalStream(getter_AddRefs(stream), aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())){
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t size = aBlob.GetSize(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())){
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> bufferedStream;
|
||||
aRv = NS_NewBufferedInputStream(getter_AddRefs(bufferedStream), stream, size);
|
||||
if (NS_WARN_IF(aRv.Failed())){
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoString encodedData;
|
||||
aRv = Base64EncodeInputStream(bufferedStream, encodedData, size);
|
||||
if (NS_WARN_IF(aRv.Failed())){
|
||||
return;
|
||||
}
|
||||
|
||||
scratchResult.Append(encodedData);
|
||||
|
||||
aResult = scratchResult;
|
||||
}
|
||||
|
||||
nsresult
|
||||
FileReaderSync::ConvertStream(nsIInputStream *aStream,
|
||||
const char *aCharset,
|
||||
nsAString &aResult)
|
||||
{
|
||||
nsCOMPtr<nsIConverterInputStream> converterStream =
|
||||
do_CreateInstance("@mozilla.org/intl/converter-input-stream;1");
|
||||
NS_ENSURE_TRUE(converterStream, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = converterStream->Init(aStream, aCharset, 8192,
|
||||
nsIConverterInputStream::DEFAULT_REPLACEMENT_CHARACTER);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIUnicharInputStream> unicharStream =
|
||||
do_QueryInterface(converterStream);
|
||||
NS_ENSURE_TRUE(unicharStream, NS_ERROR_FAILURE);
|
||||
|
||||
uint32_t numChars;
|
||||
nsString result;
|
||||
while (NS_SUCCEEDED(unicharStream->ReadString(8192, result, &numChars)) &&
|
||||
numChars > 0) {
|
||||
uint32_t oldLength = aResult.Length();
|
||||
aResult.Append(result);
|
||||
if (aResult.Length() - oldLength != result.Length()) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
53
dom/workers/FileReaderSync.h
Normal file
53
dom/workers/FileReaderSync.h
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
/* -*- 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_filereadersync_h__
|
||||
#define mozilla_dom_filereadersync_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
class nsIInputStream;
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
class Blob;
|
||||
class GlobalObject;
|
||||
template<typename> class Optional;
|
||||
|
||||
class FileReaderSync final
|
||||
{
|
||||
NS_INLINE_DECL_REFCOUNTING(FileReaderSync)
|
||||
|
||||
private:
|
||||
// Private destructor, to discourage deletion outside of Release():
|
||||
~FileReaderSync()
|
||||
{
|
||||
}
|
||||
|
||||
nsresult ConvertStream(nsIInputStream *aStream, const char *aCharset,
|
||||
nsAString &aResult);
|
||||
|
||||
public:
|
||||
static already_AddRefed<FileReaderSync>
|
||||
Constructor(const GlobalObject& aGlobal, ErrorResult& aRv);
|
||||
|
||||
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto, JS::MutableHandle<JSObject*> aReflector);
|
||||
|
||||
void ReadAsArrayBuffer(JSContext* aCx, JS::Handle<JSObject*> aScopeObj,
|
||||
Blob& aBlob, JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv);
|
||||
void ReadAsBinaryString(Blob& aBlob, nsAString& aResult, ErrorResult& aRv);
|
||||
void ReadAsText(Blob& aBlob, const Optional<nsAString>& aEncoding,
|
||||
nsAString& aResult, ErrorResult& aRv);
|
||||
void ReadAsDataURL(Blob& aBlob, nsAString& aResult, ErrorResult& aRv);
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_filereadersync_h__
|
||||
45
dom/workers/PServiceWorkerManager.ipdl
Normal file
45
dom/workers/PServiceWorkerManager.ipdl
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* 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 protocol PBackground;
|
||||
|
||||
include PBackgroundSharedTypes;
|
||||
include ServiceWorkerRegistrarTypes;
|
||||
|
||||
using mozilla::PrincipalOriginAttributes from "mozilla/ipc/BackgroundUtils.h";
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
protocol PServiceWorkerManager
|
||||
{
|
||||
manager PBackground;
|
||||
|
||||
parent:
|
||||
async Register(ServiceWorkerRegistrationData data);
|
||||
|
||||
async Unregister(PrincipalInfo principalInfo, nsString scope);
|
||||
|
||||
async PropagateSoftUpdate(PrincipalOriginAttributes originAttributes,
|
||||
nsString scope);
|
||||
async PropagateUnregister(PrincipalInfo principalInfo, nsString scope);
|
||||
|
||||
async PropagateRemove(nsCString host);
|
||||
|
||||
async PropagateRemoveAll();
|
||||
|
||||
async Shutdown();
|
||||
|
||||
child:
|
||||
async NotifyRegister(ServiceWorkerRegistrationData data);
|
||||
async NotifySoftUpdate(PrincipalOriginAttributes originAttributes, nsString scope);
|
||||
async NotifyUnregister(PrincipalInfo principalInfo, nsString scope);
|
||||
async NotifyRemove(nsCString host);
|
||||
async NotifyRemoveAll();
|
||||
|
||||
async __delete__();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
51
dom/workers/Principal.cpp
Normal file
51
dom/workers/Principal.cpp
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/* -*- 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 "Principal.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
struct WorkerPrincipal final : public JSPrincipals
|
||||
{
|
||||
bool write(JSContext* aCx, JSStructuredCloneWriter* aWriter) override {
|
||||
MOZ_CRASH("WorkerPrincipal::write not implemented");
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
JSPrincipals*
|
||||
GetWorkerPrincipal()
|
||||
{
|
||||
static WorkerPrincipal sPrincipal;
|
||||
|
||||
/*
|
||||
* To make sure the the principals refcount is initialized to one, atomically
|
||||
* increment it on every pass though this function. If we discover this wasn't
|
||||
* the first time, decrement it again. This avoids the need for
|
||||
* synchronization.
|
||||
*/
|
||||
int32_t prevRefcount = sPrincipal.refcount++;
|
||||
if (prevRefcount > 0) {
|
||||
--sPrincipal.refcount;
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
sPrincipal.debugToken = kJSPrincipalsDebugToken;
|
||||
#endif
|
||||
}
|
||||
|
||||
return &sPrincipal;
|
||||
}
|
||||
|
||||
void
|
||||
DestroyWorkerPrincipals(JSPrincipals* aPrincipals)
|
||||
{
|
||||
MOZ_ASSERT_UNREACHABLE("Worker principals refcount should never fall below one");
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
22
dom/workers/Principal.h
Normal file
22
dom/workers/Principal.h
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
/* -*- 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_principal_h__
|
||||
#define mozilla_dom_workers_principal_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
JSPrincipals*
|
||||
GetWorkerPrincipal();
|
||||
|
||||
void
|
||||
DestroyWorkerPrincipals(JSPrincipals* aPrincipals);
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_principal_h__ */
|
||||
203
dom/workers/Queue.h
Normal file
203
dom/workers/Queue.h
Normal file
|
|
@ -0,0 +1,203 @@
|
|||
/* -*- 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_queue_h__
|
||||
#define mozilla_dom_workers_queue_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
template <typename T, int TCount>
|
||||
struct StorageWithTArray
|
||||
{
|
||||
typedef AutoTArray<T, TCount> StorageType;
|
||||
|
||||
static void Reverse(StorageType& aStorage)
|
||||
{
|
||||
uint32_t length = aStorage.Length();
|
||||
for (uint32_t index = 0; index < length / 2; index++) {
|
||||
uint32_t reverseIndex = length - 1 - index;
|
||||
|
||||
T t1 = aStorage.ElementAt(index);
|
||||
T t2 = aStorage.ElementAt(reverseIndex);
|
||||
|
||||
aStorage.ReplaceElementsAt(index, 1, t2);
|
||||
aStorage.ReplaceElementsAt(reverseIndex, 1, t1);
|
||||
}
|
||||
}
|
||||
|
||||
static bool IsEmpty(const StorageType& aStorage)
|
||||
{
|
||||
return !!aStorage.IsEmpty();
|
||||
}
|
||||
|
||||
static bool Push(StorageType& aStorage, const T& aEntry)
|
||||
{
|
||||
return !!aStorage.AppendElement(aEntry);
|
||||
}
|
||||
|
||||
static bool Pop(StorageType& aStorage, T& aEntry)
|
||||
{
|
||||
if (IsEmpty(aStorage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t index = aStorage.Length() - 1;
|
||||
aEntry = aStorage.ElementAt(index);
|
||||
aStorage.RemoveElementAt(index);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void Clear(StorageType& aStorage)
|
||||
{
|
||||
aStorage.Clear();
|
||||
}
|
||||
|
||||
static void Compact(StorageType& aStorage)
|
||||
{
|
||||
aStorage.Compact();
|
||||
}
|
||||
};
|
||||
|
||||
class LockingWithMutex
|
||||
{
|
||||
mozilla::Mutex mMutex;
|
||||
|
||||
protected:
|
||||
LockingWithMutex()
|
||||
: mMutex("LockingWithMutex::mMutex")
|
||||
{ }
|
||||
|
||||
void Lock()
|
||||
{
|
||||
mMutex.Lock();
|
||||
}
|
||||
|
||||
void Unlock()
|
||||
{
|
||||
mMutex.Unlock();
|
||||
}
|
||||
|
||||
class AutoLock
|
||||
{
|
||||
LockingWithMutex& mHost;
|
||||
|
||||
public:
|
||||
explicit AutoLock(LockingWithMutex& aHost)
|
||||
: mHost(aHost)
|
||||
{
|
||||
mHost.Lock();
|
||||
}
|
||||
|
||||
~AutoLock()
|
||||
{
|
||||
mHost.Unlock();
|
||||
}
|
||||
};
|
||||
|
||||
friend class AutoLock;
|
||||
};
|
||||
|
||||
class NoLocking
|
||||
{
|
||||
protected:
|
||||
void Lock()
|
||||
{ }
|
||||
|
||||
void Unlock()
|
||||
{ }
|
||||
|
||||
class AutoLock
|
||||
{
|
||||
public:
|
||||
explicit AutoLock(NoLocking& aHost)
|
||||
{ }
|
||||
|
||||
~AutoLock()
|
||||
{ }
|
||||
};
|
||||
};
|
||||
|
||||
template <typename T,
|
||||
int TCount = 256,
|
||||
class LockingPolicy = NoLocking,
|
||||
class StoragePolicy = StorageWithTArray<T, TCount % 2 ?
|
||||
TCount / 2 + 1 :
|
||||
TCount / 2> >
|
||||
class Queue : public LockingPolicy
|
||||
{
|
||||
typedef typename StoragePolicy::StorageType StorageType;
|
||||
typedef typename LockingPolicy::AutoLock AutoLock;
|
||||
|
||||
StorageType mStorage1;
|
||||
StorageType mStorage2;
|
||||
|
||||
StorageType* mFront;
|
||||
StorageType* mBack;
|
||||
|
||||
public:
|
||||
Queue()
|
||||
: mFront(&mStorage1), mBack(&mStorage2)
|
||||
{ }
|
||||
|
||||
bool IsEmpty()
|
||||
{
|
||||
AutoLock lock(*this);
|
||||
return StoragePolicy::IsEmpty(*mFront) &&
|
||||
StoragePolicy::IsEmpty(*mBack);
|
||||
}
|
||||
|
||||
bool Push(const T& aEntry)
|
||||
{
|
||||
AutoLock lock(*this);
|
||||
return StoragePolicy::Push(*mBack, aEntry);
|
||||
}
|
||||
|
||||
bool Pop(T& aEntry)
|
||||
{
|
||||
AutoLock lock(*this);
|
||||
if (StoragePolicy::IsEmpty(*mFront)) {
|
||||
StoragePolicy::Compact(*mFront);
|
||||
StoragePolicy::Reverse(*mBack);
|
||||
StorageType* tmp = mFront;
|
||||
mFront = mBack;
|
||||
mBack = tmp;
|
||||
}
|
||||
return StoragePolicy::Pop(*mFront, aEntry);
|
||||
}
|
||||
|
||||
void Clear()
|
||||
{
|
||||
AutoLock lock(*this);
|
||||
StoragePolicy::Clear(*mFront);
|
||||
StoragePolicy::Clear(*mBack);
|
||||
}
|
||||
|
||||
// XXX Do we need this?
|
||||
void Lock()
|
||||
{
|
||||
LockingPolicy::Lock();
|
||||
}
|
||||
|
||||
// XXX Do we need this?
|
||||
void Unlock()
|
||||
{
|
||||
LockingPolicy::Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
// Queue is not copyable.
|
||||
Queue(const Queue&);
|
||||
Queue & operator=(const Queue&);
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_queue_h__ */
|
||||
55
dom/workers/RegisterBindings.cpp
Normal file
55
dom/workers/RegisterBindings.cpp
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/* -*- 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 "WorkerPrivate.h"
|
||||
#include "ChromeWorkerScope.h"
|
||||
#include "RuntimeService.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/dom/RegisterWorkerBindings.h"
|
||||
#include "mozilla/dom/RegisterWorkerDebuggerBindings.h"
|
||||
#include "mozilla/OSFileConstants.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
using namespace mozilla::dom;
|
||||
|
||||
bool
|
||||
WorkerPrivate::RegisterBindings(JSContext* aCx, JS::Handle<JSObject*> aGlobal)
|
||||
{
|
||||
// Init Web IDL bindings
|
||||
if (!RegisterWorkerBindings(aCx, aGlobal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (IsChromeWorker()) {
|
||||
if (!DefineChromeWorkerFunctions(aCx, aGlobal) ||
|
||||
!DefineOSFileConstants(aCx, aGlobal)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!JS_DefineProfilingFunctions(aCx, aGlobal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerPrivate::RegisterDebuggerBindings(JSContext* aCx,
|
||||
JS::Handle<JSObject*> aGlobal)
|
||||
{
|
||||
// Init Web IDL bindings
|
||||
if (!RegisterWorkerDebuggerBindings(aCx, aGlobal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_DefineDebuggerObject(aCx, aGlobal)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
2966
dom/workers/RuntimeService.cpp
Normal file
2966
dom/workers/RuntimeService.cpp
Normal file
File diff suppressed because it is too large
Load diff
287
dom/workers/RuntimeService.h
Normal file
287
dom/workers/RuntimeService.h
Normal file
|
|
@ -0,0 +1,287 @@
|
|||
/* -*- 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_runtimeservice_h__
|
||||
#define mozilla_dom_workers_runtimeservice_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsITimer;
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class SharedWorker;
|
||||
class WorkerThread;
|
||||
|
||||
class RuntimeService final : public nsIObserver
|
||||
{
|
||||
struct SharedWorkerInfo
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCString mScriptSpec;
|
||||
nsCString mName;
|
||||
|
||||
SharedWorkerInfo(WorkerPrivate* aWorkerPrivate,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsACString& aName)
|
||||
: mWorkerPrivate(aWorkerPrivate), mScriptSpec(aScriptSpec), mName(aName)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct WorkerDomainInfo
|
||||
{
|
||||
nsCString mDomain;
|
||||
nsTArray<WorkerPrivate*> mActiveWorkers;
|
||||
nsTArray<WorkerPrivate*> mActiveServiceWorkers;
|
||||
nsTArray<WorkerPrivate*> mQueuedWorkers;
|
||||
nsClassHashtable<nsCStringHashKey, SharedWorkerInfo> mSharedWorkerInfos;
|
||||
uint32_t mChildWorkerCount;
|
||||
|
||||
WorkerDomainInfo()
|
||||
: mActiveWorkers(1), mChildWorkerCount(0)
|
||||
{ }
|
||||
|
||||
uint32_t
|
||||
ActiveWorkerCount() const
|
||||
{
|
||||
return mActiveWorkers.Length() +
|
||||
mChildWorkerCount;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
ActiveServiceWorkerCount() const
|
||||
{
|
||||
return mActiveServiceWorkers.Length();
|
||||
}
|
||||
|
||||
bool
|
||||
HasNoWorkers() const
|
||||
{
|
||||
return ActiveWorkerCount() == 0 &&
|
||||
ActiveServiceWorkerCount() == 0;
|
||||
}
|
||||
};
|
||||
|
||||
struct IdleThreadInfo;
|
||||
|
||||
mozilla::Mutex mMutex;
|
||||
|
||||
// Protected by mMutex.
|
||||
nsClassHashtable<nsCStringHashKey, WorkerDomainInfo> mDomainMap;
|
||||
|
||||
// Protected by mMutex.
|
||||
nsTArray<IdleThreadInfo> mIdleThreadArray;
|
||||
|
||||
// *Not* protected by mMutex.
|
||||
nsClassHashtable<nsPtrHashKey<nsPIDOMWindowInner>,
|
||||
nsTArray<WorkerPrivate*> > mWindowMap;
|
||||
|
||||
// Only used on the main thread.
|
||||
nsCOMPtr<nsITimer> mIdleThreadTimer;
|
||||
|
||||
static JSSettings sDefaultJSSettings;
|
||||
static bool sDefaultPreferences[WORKERPREF_COUNT];
|
||||
|
||||
public:
|
||||
struct NavigatorProperties
|
||||
{
|
||||
nsString mAppName;
|
||||
nsString mAppNameOverridden;
|
||||
nsString mAppVersion;
|
||||
nsString mAppVersionOverridden;
|
||||
nsString mPlatform;
|
||||
nsString mPlatformOverridden;
|
||||
nsTArray<nsString> mLanguages;
|
||||
};
|
||||
|
||||
private:
|
||||
NavigatorProperties mNavigatorProperties;
|
||||
|
||||
// True when the observer service holds a reference to this object.
|
||||
bool mObserved;
|
||||
bool mShuttingDown;
|
||||
bool mNavigatorPropertiesLoaded;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static RuntimeService*
|
||||
GetOrCreateService();
|
||||
|
||||
static RuntimeService*
|
||||
GetService();
|
||||
|
||||
bool
|
||||
RegisterWorker(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
UnregisterWorker(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
RemoveSharedWorker(WorkerDomainInfo* aDomainInfo,
|
||||
WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
CancelWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
ThawWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
nsresult
|
||||
CreateSharedWorker(const GlobalObject& aGlobal,
|
||||
const nsAString& aScriptURL,
|
||||
const nsACString& aName,
|
||||
SharedWorker** aSharedWorker);
|
||||
|
||||
void
|
||||
ForgetSharedWorker(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
const NavigatorProperties&
|
||||
GetNavigatorProperties() const
|
||||
{
|
||||
return mNavigatorProperties;
|
||||
}
|
||||
|
||||
void
|
||||
NoteIdleThread(WorkerThread* aThread);
|
||||
|
||||
static void
|
||||
GetDefaultJSSettings(JSSettings& aSettings)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
aSettings = sDefaultJSSettings;
|
||||
}
|
||||
|
||||
static void
|
||||
GetDefaultPreferences(bool aPreferences[WORKERPREF_COUNT])
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
memcpy(aPreferences, sDefaultPreferences, WORKERPREF_COUNT * sizeof(bool));
|
||||
}
|
||||
|
||||
static void
|
||||
SetDefaultContextOptions(const JS::ContextOptions& aContextOptions)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
sDefaultJSSettings.contextOptions = aContextOptions;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateAppNameOverridePreference(const nsAString& aValue);
|
||||
|
||||
void
|
||||
UpdateAppVersionOverridePreference(const nsAString& aValue);
|
||||
|
||||
void
|
||||
UpdatePlatformOverridePreference(const nsAString& aValue);
|
||||
|
||||
void
|
||||
UpdateAllWorkerContextOptions();
|
||||
|
||||
void
|
||||
UpdateAllWorkerLanguages(const nsTArray<nsString>& aLanguages);
|
||||
|
||||
void
|
||||
UpdateAllWorkerPreference(WorkerPreference aPref, bool aValue);
|
||||
|
||||
static void
|
||||
SetDefaultJSGCSettings(JSGCParamKey aKey, uint32_t aValue)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
sDefaultJSSettings.ApplyGCSetting(aKey, aValue);
|
||||
}
|
||||
|
||||
void
|
||||
UpdateAllWorkerMemoryParameter(JSGCParamKey aKey, uint32_t aValue);
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
static void
|
||||
SetDefaultGCZeal(uint8_t aGCZeal, uint32_t aFrequency)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
sDefaultJSSettings.gcZeal = aGCZeal;
|
||||
sDefaultJSSettings.gcZealFrequency = aFrequency;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateAllWorkerGCZeal();
|
||||
#endif
|
||||
|
||||
void
|
||||
GarbageCollectAllWorkers(bool aShrinking);
|
||||
|
||||
void
|
||||
CycleCollectAllWorkers();
|
||||
|
||||
void
|
||||
SendOfflineStatusChangeEventToAllWorkers(bool aIsOffline);
|
||||
|
||||
void
|
||||
MemoryPressureAllWorkers();
|
||||
|
||||
uint32_t ClampedHardwareConcurrency() const;
|
||||
|
||||
private:
|
||||
RuntimeService();
|
||||
~RuntimeService();
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
void
|
||||
Shutdown();
|
||||
|
||||
void
|
||||
Cleanup();
|
||||
|
||||
void
|
||||
AddAllTopLevelWorkersToArray(nsTArray<WorkerPrivate*>& aWorkers);
|
||||
|
||||
void
|
||||
GetWorkersForWindow(nsPIDOMWindowInner* aWindow,
|
||||
nsTArray<WorkerPrivate*>& aWorkers);
|
||||
|
||||
bool
|
||||
ScheduleWorker(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
static void
|
||||
ShutdownIdleThreads(nsITimer* aTimer, void* aClosure);
|
||||
|
||||
static void
|
||||
WorkerPrefChanged(const char* aPrefName, void* aClosure);
|
||||
|
||||
static void
|
||||
JSVersionChanged(const char* aPrefName, void* aClosure);
|
||||
|
||||
nsresult
|
||||
CreateSharedWorkerFromLoadInfo(JSContext* aCx,
|
||||
WorkerLoadInfo* aLoadInfo,
|
||||
const nsAString& aScriptURL,
|
||||
const nsACString& aName,
|
||||
SharedWorker** aSharedWorker);
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_runtimeservice_h__ */
|
||||
2290
dom/workers/ScriptLoader.cpp
Normal file
2290
dom/workers/ScriptLoader.cpp
Normal file
File diff suppressed because it is too large
Load diff
68
dom/workers/ScriptLoader.h
Normal file
68
dom/workers/ScriptLoader.h
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/* -*- 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_scriptloader_h__
|
||||
#define mozilla_dom_workers_scriptloader_h__
|
||||
|
||||
#include "Workers.h"
|
||||
#include "nsIContentPolicyBase.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
class nsIURI;
|
||||
class nsIDocument;
|
||||
class nsILoadGroup;
|
||||
class nsString;
|
||||
class nsIChannel;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
enum WorkerScriptType {
|
||||
WorkerScript,
|
||||
DebuggerScript
|
||||
};
|
||||
|
||||
namespace scriptloader {
|
||||
|
||||
nsresult
|
||||
ChannelFromScriptURLMainThread(nsIPrincipal* aPrincipal,
|
||||
nsIURI* aBaseURI,
|
||||
nsIDocument* aParentDoc,
|
||||
nsILoadGroup* aLoadGroup,
|
||||
const nsAString& aScriptURL,
|
||||
nsContentPolicyType aContentPolicyType,
|
||||
bool aDefaultURIEncoding,
|
||||
nsIChannel** aChannel);
|
||||
|
||||
nsresult
|
||||
ChannelFromScriptURLWorkerThread(JSContext* aCx,
|
||||
WorkerPrivate* aParent,
|
||||
const nsAString& aScriptURL,
|
||||
nsIChannel** aChannel);
|
||||
|
||||
void ReportLoadError(ErrorResult& aRv, nsresult aLoadResult,
|
||||
const nsAString& aScriptURL);
|
||||
|
||||
void LoadMainScript(WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aScriptURL,
|
||||
WorkerScriptType aWorkerScriptType,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void Load(WorkerPrivate* aWorkerPrivate,
|
||||
const nsTArray<nsString>& aScriptURLs,
|
||||
WorkerScriptType aWorkerScriptType,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
} // namespace scriptloader
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_scriptloader_h__ */
|
||||
103
dom/workers/ServiceWorker.cpp
Normal file
103
dom/workers/ServiceWorker.cpp
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
/* -*- 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 "ServiceWorker.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "ServiceWorkerClient.h"
|
||||
#include "ServiceWorkerManager.h"
|
||||
#include "ServiceWorkerPrivate.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
using mozilla::ErrorResult;
|
||||
using namespace mozilla::dom;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
bool
|
||||
ServiceWorkerVisible(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
return Preferences::GetBool("dom.serviceWorkers.enabled", false);
|
||||
}
|
||||
|
||||
return IS_INSTANCE_OF(ServiceWorkerGlobalScope, aObj);
|
||||
}
|
||||
|
||||
ServiceWorker::ServiceWorker(nsPIDOMWindowInner* aWindow,
|
||||
ServiceWorkerInfo* aInfo)
|
||||
: DOMEventTargetHelper(aWindow),
|
||||
mInfo(aInfo)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aInfo);
|
||||
|
||||
// This will update our state too.
|
||||
mInfo->AppendWorker(this);
|
||||
}
|
||||
|
||||
ServiceWorker::~ServiceWorker()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mInfo->RemoveWorker(this);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ServiceWorker, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(ServiceWorker, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorker)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
JSObject*
|
||||
ServiceWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return ServiceWorkerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorker::GetScriptURL(nsString& aURL) const
|
||||
{
|
||||
CopyUTF8toUTF16(mInfo->ScriptSpec(), aURL);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (State() == ServiceWorkerState::Redundant) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = do_QueryInterface(GetParentObject());
|
||||
if (!window || !window->GetExtantDoc()) {
|
||||
NS_WARNING("Trying to call post message from an invalid dom object.");
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
UniquePtr<ServiceWorkerClientInfo> clientInfo(new ServiceWorkerClientInfo(window->GetExtantDoc()));
|
||||
ServiceWorkerPrivate* workerPrivate = mInfo->WorkerPrivate();
|
||||
aRv = workerPrivate->SendMessageEvent(aCx, aMessage, aTransferable, Move(clientInfo));
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
85
dom/workers/ServiceWorker.h
Normal file
85
dom/workers/ServiceWorker.h
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
/* -*- 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_serviceworker_h__
|
||||
#define mozilla_dom_workers_serviceworker_h__
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState.
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerInfo;
|
||||
class ServiceWorkerManager;
|
||||
class SharedWorker;
|
||||
|
||||
bool
|
||||
ServiceWorkerVisible(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
class ServiceWorker final : public DOMEventTargetHelper
|
||||
{
|
||||
friend class ServiceWorkerInfo;
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
IMPL_EVENT_HANDLER(statechange)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
ServiceWorkerState
|
||||
State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
void
|
||||
SetState(ServiceWorkerState aState)
|
||||
{
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
void
|
||||
GetScriptURL(nsString& aURL) const;
|
||||
|
||||
void
|
||||
DispatchStateChange(ServiceWorkerState aState)
|
||||
{
|
||||
DOMEventTargetHelper::DispatchTrustedEvent(NS_LITERAL_STRING("statechange"));
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
// This class can only be created from ServiceWorkerInfo::GetOrCreateInstance().
|
||||
ServiceWorker(nsPIDOMWindowInner* aWindow, ServiceWorkerInfo* aInfo);
|
||||
|
||||
// This class is reference-counted and will be destroyed from Release().
|
||||
~ServiceWorker();
|
||||
|
||||
ServiceWorkerState mState;
|
||||
const RefPtr<ServiceWorkerInfo> mInfo;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworker_h__
|
||||
232
dom/workers/ServiceWorkerClient.cpp
Normal file
232
dom/workers/ServiceWorkerClient.cpp
Normal file
|
|
@ -0,0 +1,232 @@
|
|||
/* -*- 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 "ServiceWorkerClient.h"
|
||||
#include "ServiceWorkerContainer.h"
|
||||
|
||||
#include "mozilla/dom/MessageEvent.h"
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/ServiceWorkerMessageEvent.h"
|
||||
#include "mozilla/dom/ServiceWorkerMessageEventBinding.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "ServiceWorker.h"
|
||||
#include "ServiceWorkerPrivate.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClient, mOwner)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClient)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClient)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClient)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
ServiceWorkerClientInfo::ServiceWorkerClientInfo(nsIDocument* aDoc)
|
||||
: mWindowId(0)
|
||||
, mFrameType(FrameType::None)
|
||||
{
|
||||
MOZ_ASSERT(aDoc);
|
||||
nsresult rv = aDoc->GetOrCreateId(mClientId);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to get the UUID of the document.");
|
||||
}
|
||||
|
||||
RefPtr<nsGlobalWindow> innerWindow = nsGlobalWindow::Cast(aDoc->GetInnerWindow());
|
||||
if (innerWindow) {
|
||||
// XXXcatalinb: The inner window can be null if the document is navigating
|
||||
// and was detached.
|
||||
mWindowId = innerWindow->WindowID();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> originalURI = aDoc->GetOriginalURI();
|
||||
if (originalURI) {
|
||||
nsAutoCString spec;
|
||||
originalURI->GetSpec(spec);
|
||||
CopyUTF8toUTF16(spec, mUrl);
|
||||
}
|
||||
mVisibilityState = aDoc->VisibilityState();
|
||||
|
||||
ErrorResult result;
|
||||
mFocused = aDoc->HasFocus(result);
|
||||
if (result.Failed()) {
|
||||
NS_WARNING("Failed to get focus information.");
|
||||
}
|
||||
|
||||
RefPtr<nsGlobalWindow> outerWindow = nsGlobalWindow::Cast(aDoc->GetWindow());
|
||||
if (!outerWindow) {
|
||||
MOZ_ASSERT(mFrameType == FrameType::None);
|
||||
} else if (!outerWindow->IsTopLevelWindow()) {
|
||||
mFrameType = FrameType::Nested;
|
||||
} else if (outerWindow->HadOriginalOpener()) {
|
||||
mFrameType = FrameType::Auxiliary;
|
||||
} else {
|
||||
mFrameType = FrameType::Top_level;
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
ServiceWorkerClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ClientBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ServiceWorkerClientPostMessageRunnable final
|
||||
: public Runnable
|
||||
, public StructuredCloneHolder
|
||||
{
|
||||
uint64_t mWindowId;
|
||||
|
||||
public:
|
||||
explicit ServiceWorkerClientPostMessageRunnable(uint64_t aWindowId)
|
||||
: StructuredCloneHolder(CloningSupported, TransferringSupported,
|
||||
StructuredCloneScope::SameProcessDifferentThread)
|
||||
, mWindowId(aWindowId)
|
||||
{}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
|
||||
if (!window) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
ErrorResult result;
|
||||
dom::Navigator* navigator = window->GetNavigator(result);
|
||||
if (NS_WARN_IF(result.Failed())) {
|
||||
return result.StealNSResult();
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerContainer> container = navigator->ServiceWorker();
|
||||
AutoJSAPI jsapi;
|
||||
if (NS_WARN_IF(!jsapi.Init(window))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
JSContext* cx = jsapi.cx();
|
||||
|
||||
return DispatchDOMEvent(cx, container);
|
||||
}
|
||||
|
||||
private:
|
||||
NS_IMETHOD
|
||||
DispatchDOMEvent(JSContext* aCx, ServiceWorkerContainer* aTargetContainer)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT(aTargetContainer->GetParentObject(),
|
||||
"How come we don't have a window here?!");
|
||||
|
||||
JS::Rooted<JS::Value> messageData(aCx);
|
||||
ErrorResult rv;
|
||||
Read(aTargetContainer->GetParentObject(), aCx, &messageData, rv);
|
||||
if (NS_WARN_IF(rv.Failed())) {
|
||||
xpc::Throw(aCx, rv.StealNSResult());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RootedDictionary<ServiceWorkerMessageEventInit> init(aCx);
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = aTargetContainer->GetParentObject()->PrincipalOrNull();
|
||||
NS_WARNING_ASSERTION(principal, "Why is the principal null here?");
|
||||
|
||||
bool isNullPrincipal = false;
|
||||
bool isSystemPrincipal = false;
|
||||
if (principal) {
|
||||
isNullPrincipal = principal->GetIsNullPrincipal();
|
||||
MOZ_ASSERT(!isNullPrincipal);
|
||||
isSystemPrincipal = principal->GetIsSystemPrincipal();
|
||||
MOZ_ASSERT(!isSystemPrincipal);
|
||||
}
|
||||
|
||||
init.mData = messageData;
|
||||
nsAutoCString origin;
|
||||
if (principal && !isNullPrincipal && !isSystemPrincipal) {
|
||||
principal->GetOrigin(origin);
|
||||
}
|
||||
init.mOrigin = NS_ConvertUTF8toUTF16(origin);
|
||||
|
||||
RefPtr<ServiceWorker> serviceWorker = aTargetContainer->GetController();
|
||||
if (serviceWorker) {
|
||||
init.mSource.SetValue().SetAsServiceWorker() = serviceWorker;
|
||||
}
|
||||
|
||||
if (!TakeTransferredPortsAsSequence(init.mPorts)) {
|
||||
return NS_ERROR_OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerMessageEvent> event =
|
||||
ServiceWorkerMessageEvent::Constructor(aTargetContainer,
|
||||
NS_LITERAL_STRING("message"),
|
||||
init);
|
||||
|
||||
event->SetTrusted(true);
|
||||
bool status = false;
|
||||
aTargetContainer->DispatchEvent(static_cast<dom::Event*>(event.get()),
|
||||
&status);
|
||||
|
||||
if (!status) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
ServiceWorkerClient::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
JS::Rooted<JS::Value> transferable(aCx, JS::UndefinedValue());
|
||||
if (aTransferable.WasPassed()) {
|
||||
const Sequence<JS::Value>& realTransferable = aTransferable.Value();
|
||||
|
||||
JS::HandleValueArray elements =
|
||||
JS::HandleValueArray::fromMarkedLocation(realTransferable.Length(),
|
||||
realTransferable.Elements());
|
||||
|
||||
JSObject* array = JS_NewArrayObject(aCx, elements);
|
||||
if (!array) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
transferable.setObject(*array);
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerClientPostMessageRunnable> runnable =
|
||||
new ServiceWorkerClientPostMessageRunnable(mWindowId);
|
||||
|
||||
runnable->Write(aCx, aMessage, transferable, JS::CloneDataPolicy().denySharedArrayBuffer(),
|
||||
aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = workerPrivate->DispatchToMainThread(runnable.forget());
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
118
dom/workers/ServiceWorkerClient.h
Normal file
118
dom/workers/ServiceWorkerClient.h
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/* -*- 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_serviceworkerclient_h
|
||||
#define mozilla_dom_workers_serviceworkerclient_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/ClientBinding.h"
|
||||
|
||||
class nsIDocument;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerClient;
|
||||
class ServiceWorkerWindowClient;
|
||||
|
||||
// Used as a container object for information needed to create
|
||||
// client objects.
|
||||
class ServiceWorkerClientInfo final
|
||||
{
|
||||
friend class ServiceWorkerClient;
|
||||
friend class ServiceWorkerWindowClient;
|
||||
|
||||
public:
|
||||
explicit ServiceWorkerClientInfo(nsIDocument* aDoc);
|
||||
|
||||
const nsString& ClientId() const
|
||||
{
|
||||
return mClientId;
|
||||
}
|
||||
|
||||
private:
|
||||
nsString mClientId;
|
||||
uint64_t mWindowId;
|
||||
nsString mUrl;
|
||||
|
||||
// Window Clients
|
||||
VisibilityState mVisibilityState;
|
||||
bool mFocused;
|
||||
FrameType mFrameType;
|
||||
};
|
||||
|
||||
class ServiceWorkerClient : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClient)
|
||||
|
||||
ServiceWorkerClient(nsISupports* aOwner,
|
||||
const ServiceWorkerClientInfo& aClientInfo)
|
||||
: mOwner(aOwner)
|
||||
, mId(aClientInfo.mClientId)
|
||||
, mUrl(aClientInfo.mUrl)
|
||||
, mWindowId(aClientInfo.mWindowId)
|
||||
, mFrameType(aClientInfo.mFrameType)
|
||||
{
|
||||
MOZ_ASSERT(aOwner);
|
||||
}
|
||||
|
||||
nsISupports*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
void GetId(nsString& aRetval) const
|
||||
{
|
||||
aRetval = mId;
|
||||
}
|
||||
|
||||
void
|
||||
GetUrl(nsAString& aUrl) const
|
||||
{
|
||||
aUrl.Assign(mUrl);
|
||||
}
|
||||
|
||||
mozilla::dom::FrameType
|
||||
FrameType() const
|
||||
{
|
||||
return mFrameType;
|
||||
}
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
protected:
|
||||
virtual ~ServiceWorkerClient()
|
||||
{ }
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsString mId;
|
||||
nsString mUrl;
|
||||
|
||||
protected:
|
||||
uint64_t mWindowId;
|
||||
mozilla::dom::FrameType mFrameType;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerclient_h
|
||||
864
dom/workers/ServiceWorkerClients.cpp
Normal file
864
dom/workers/ServiceWorkerClients.cpp
Normal file
|
|
@ -0,0 +1,864 @@
|
|||
/* -*- 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 "ServiceWorkerClients.h"
|
||||
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseWorkerProxy.h"
|
||||
|
||||
#include "ServiceWorkerClient.h"
|
||||
#include "ServiceWorkerManager.h"
|
||||
#include "ServiceWorkerPrivate.h"
|
||||
#include "ServiceWorkerWindowClient.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIBrowserDOMWindow.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDOMChromeWindow.h"
|
||||
#include "nsIDOMWindow.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsIWindowMediator.h"
|
||||
#include "nsIWindowWatcher.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIWindowWatcher.h"
|
||||
#include "nsWindowWatcher.h"
|
||||
#include "nsWeakReference.h"
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
#include "AndroidBridge.h"
|
||||
#endif
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(ServiceWorkerClients)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(ServiceWorkerClients)
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(ServiceWorkerClients, mWorkerScope)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(ServiceWorkerClients)
|
||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
ServiceWorkerClients::ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope)
|
||||
: mWorkerScope(aWorkerScope)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerScope);
|
||||
}
|
||||
|
||||
JSObject*
|
||||
ServiceWorkerClients::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ClientsBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class GetRunnable final : public Runnable
|
||||
{
|
||||
class ResolvePromiseWorkerRunnable final : public WorkerRunnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
UniquePtr<ServiceWorkerClientInfo> mValue;
|
||||
nsresult mRv;
|
||||
|
||||
public:
|
||||
ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy,
|
||||
UniquePtr<ServiceWorkerClientInfo>&& aValue,
|
||||
nsresult aRv)
|
||||
: WorkerRunnable(aWorkerPrivate),
|
||||
mPromiseProxy(aPromiseProxy),
|
||||
mValue(Move(aValue)),
|
||||
mRv(Move(aRv))
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
if (NS_FAILED(mRv)) {
|
||||
promise->MaybeReject(mRv);
|
||||
} else if (mValue) {
|
||||
RefPtr<ServiceWorkerWindowClient> windowClient =
|
||||
new ServiceWorkerWindowClient(promise->GetParentObject(), *mValue);
|
||||
promise->MaybeResolve(windowClient.get());
|
||||
} else {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
}
|
||||
mPromiseProxy->CleanUp();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsString mClientId;
|
||||
public:
|
||||
GetRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
const nsAString& aClientId)
|
||||
: mPromiseProxy(aPromiseProxy),
|
||||
mClientId(aClientId)
|
||||
{
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
UniquePtr<ServiceWorkerClientInfo> result;
|
||||
ErrorResult rv;
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
rv = NS_ERROR_FAILURE;
|
||||
} else {
|
||||
result = swm->GetClient(workerPrivate->GetPrincipal(), mClientId, rv);
|
||||
}
|
||||
|
||||
RefPtr<ResolvePromiseWorkerRunnable> r =
|
||||
new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||
mPromiseProxy, Move(result),
|
||||
rv.StealNSResult());
|
||||
rv.SuppressException();
|
||||
|
||||
r->Dispatch();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class MatchAllRunnable final : public Runnable
|
||||
{
|
||||
class ResolvePromiseWorkerRunnable final : public WorkerRunnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsTArray<ServiceWorkerClientInfo> mValue;
|
||||
|
||||
public:
|
||||
ResolvePromiseWorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy,
|
||||
nsTArray<ServiceWorkerClientInfo>& aValue)
|
||||
: WorkerRunnable(aWorkerPrivate),
|
||||
mPromiseProxy(aPromiseProxy)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mValue.SwapElements(aValue);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
nsTArray<RefPtr<ServiceWorkerClient>> ret;
|
||||
for (size_t i = 0; i < mValue.Length(); i++) {
|
||||
ret.AppendElement(RefPtr<ServiceWorkerClient>(
|
||||
new ServiceWorkerWindowClient(promise->GetParentObject(),
|
||||
mValue.ElementAt(i))));
|
||||
}
|
||||
|
||||
promise->MaybeResolve(ret);
|
||||
mPromiseProxy->CleanUp();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsCString mScope;
|
||||
bool mIncludeUncontrolled;
|
||||
public:
|
||||
MatchAllRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
const nsCString& aScope,
|
||||
bool aIncludeUncontrolled)
|
||||
: mPromiseProxy(aPromiseProxy),
|
||||
mScope(aScope),
|
||||
mIncludeUncontrolled(aIncludeUncontrolled)
|
||||
{
|
||||
MOZ_ASSERT(mPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsTArray<ServiceWorkerClientInfo> result;
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->GetAllClients(mPromiseProxy->GetWorkerPrivate()->GetPrincipal(),
|
||||
mScope, mIncludeUncontrolled, result);
|
||||
}
|
||||
RefPtr<ResolvePromiseWorkerRunnable> r =
|
||||
new ResolvePromiseWorkerRunnable(mPromiseProxy->GetWorkerPrivate(),
|
||||
mPromiseProxy, result);
|
||||
|
||||
r->Dispatch();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class ResolveClaimRunnable final : public WorkerRunnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsresult mResult;
|
||||
|
||||
public:
|
||||
ResolveClaimRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy,
|
||||
nsresult aResult)
|
||||
: WorkerRunnable(aWorkerPrivate)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
, mResult(aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
if (NS_SUCCEEDED(mResult)) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
mPromiseProxy->CleanUp();
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ClaimRunnable final : public Runnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsCString mScope;
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
public:
|
||||
ClaimRunnable(PromiseWorkerProxy* aPromiseProxy, const nsCString& aScope)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mScope(aScope)
|
||||
// Safe to call GetWorkerPrivate() since we are being called on the worker
|
||||
// thread via script (so no clean up has occured yet).
|
||||
, mServiceWorkerID(aPromiseProxy->GetWorkerPrivate()->ServiceWorkerID())
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
nsresult rv = NS_OK;
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown
|
||||
rv = NS_ERROR_FAILURE;
|
||||
} else {
|
||||
rv = swm->ClaimClients(workerPrivate->GetPrincipal(), mScope,
|
||||
mServiceWorkerID);
|
||||
}
|
||||
|
||||
RefPtr<ResolveClaimRunnable> r =
|
||||
new ResolveClaimRunnable(workerPrivate, mPromiseProxy, rv);
|
||||
|
||||
r->Dispatch();
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class ResolveOpenWindowRunnable final : public WorkerRunnable
|
||||
{
|
||||
public:
|
||||
ResolveOpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo,
|
||||
const nsresult aStatus)
|
||||
: WorkerRunnable(aPromiseProxy->GetWorkerPrivate())
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
, mClientInfo(Move(aClientInfo))
|
||||
, mStatus(aStatus)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Promise* promise = mPromiseProxy->WorkerPromise();
|
||||
if (NS_WARN_IF(NS_FAILED(mStatus))) {
|
||||
promise->MaybeReject(mStatus);
|
||||
} else if (mClientInfo) {
|
||||
RefPtr<ServiceWorkerWindowClient> client =
|
||||
new ServiceWorkerWindowClient(promise->GetParentObject(),
|
||||
*mClientInfo);
|
||||
promise->MaybeResolve(client);
|
||||
} else {
|
||||
promise->MaybeResolve(JS::NullHandleValue);
|
||||
}
|
||||
|
||||
mPromiseProxy->CleanUp();
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
UniquePtr<ServiceWorkerClientInfo> mClientInfo;
|
||||
const nsresult mStatus;
|
||||
};
|
||||
|
||||
class WebProgressListener final : public nsIWebProgressListener,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener, nsIWebProgressListener)
|
||||
|
||||
WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
|
||||
ServiceWorkerPrivate* aServiceWorkerPrivate,
|
||||
nsPIDOMWindowOuter* aWindow,
|
||||
nsIURI* aBaseURI)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mServiceWorkerPrivate(aServiceWorkerPrivate)
|
||||
, mWindow(aWindow)
|
||||
, mBaseURI(aBaseURI)
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
MOZ_ASSERT(aServiceWorkerPrivate);
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(aWindow->IsOuterWindow());
|
||||
MOZ_ASSERT(aBaseURI);
|
||||
AssertIsOnMainThread();
|
||||
|
||||
mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnStateChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
uint32_t aStateFlags, nsresult aStatus) override
|
||||
{
|
||||
if (!(aStateFlags & STATE_IS_DOCUMENT) ||
|
||||
!(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Our caller keeps a strong reference, so it is safe to remove the listener
|
||||
// from ServiceWorkerPrivate.
|
||||
mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
|
||||
aWebProgress->RemoveProgressListener(this);
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
|
||||
UniquePtr<ServiceWorkerClientInfo> clientInfo;
|
||||
if (doc) {
|
||||
// Check same origin.
|
||||
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
||||
nsContentUtils::GetSecurityManager();
|
||||
nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
|
||||
mBaseURI, false);
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
clientInfo.reset(new ServiceWorkerClientInfo(doc));
|
||||
}
|
||||
}
|
||||
|
||||
RefPtr<ResolveOpenWindowRunnable> r =
|
||||
new ResolveOpenWindowRunnable(mPromiseProxy,
|
||||
Move(clientInfo),
|
||||
NS_OK);
|
||||
r->Dispatch();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnProgressChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
int32_t aCurSelfProgress,
|
||||
int32_t aMaxSelfProgress,
|
||||
int32_t aCurTotalProgress,
|
||||
int32_t aMaxTotalProgress) override
|
||||
{
|
||||
MOZ_ASSERT(false, "Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnLocationChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
nsIURI* aLocation,
|
||||
uint32_t aFlags) override
|
||||
{
|
||||
MOZ_ASSERT(false, "Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnStatusChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
nsresult aStatus, const char16_t* aMessage) override
|
||||
{
|
||||
MOZ_ASSERT(false, "Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnSecurityChange(nsIWebProgress* aWebProgress,
|
||||
nsIRequest* aRequest,
|
||||
uint32_t aState) override
|
||||
{
|
||||
MOZ_ASSERT(false, "Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~WebProgressListener()
|
||||
{ }
|
||||
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
|
||||
nsCOMPtr<nsPIDOMWindowOuter> mWindow;
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
|
||||
mServiceWorkerPrivate, mWindow)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
class OpenWindowRunnable final : public Runnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsString mUrl;
|
||||
nsString mScope;
|
||||
|
||||
public:
|
||||
OpenWindowRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
const nsAString& aUrl,
|
||||
const nsAString& aScope)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mUrl(aUrl)
|
||||
, mScope(aScope)
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
|
||||
aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
// This fires an intent that will start launching Fennec and foreground it,
|
||||
// if necessary.
|
||||
java::GeckoAppShell::OpenWindowForNotification();
|
||||
#endif
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> window;
|
||||
nsresult rv = OpenWindow(getter_AddRefs(window));
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MOZ_ASSERT(window);
|
||||
|
||||
rv = nsContentUtils::DispatchFocusChromeEvent(window);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
||||
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
|
||||
|
||||
if (!webProgress) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
|
||||
MOZ_ASSERT(principal);
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
|
||||
if (NS_WARN_IF(!registration)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
|
||||
registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID());
|
||||
if (NS_WARN_IF(!serviceWorkerInfo)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWebProgressListener> listener =
|
||||
new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
|
||||
window, baseURI);
|
||||
|
||||
rv = webProgress->AddProgressListener(listener,
|
||||
nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
return NS_OK;
|
||||
}
|
||||
#ifdef MOZ_WIDGET_ANDROID
|
||||
else if (rv == NS_ERROR_NOT_AVAILABLE) {
|
||||
// We couldn't get a browser window, so Fennec must not be running.
|
||||
// Send an Intent to launch Fennec and wait for "BrowserChrome:Ready"
|
||||
// to try opening a window again.
|
||||
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
||||
NS_ENSURE_STATE(os);
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = workerPrivate->GetPrincipal();
|
||||
MOZ_ASSERT(principal);
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
|
||||
if (NS_WARN_IF(!registration)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
|
||||
registration->GetServiceWorkerInfoById(workerPrivate->ServiceWorkerID());
|
||||
if (NS_WARN_IF(!serviceWorkerInfo)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
os->AddObserver(static_cast<nsIObserver*>(serviceWorkerInfo->WorkerPrivate()),
|
||||
"BrowserChrome:Ready", true);
|
||||
serviceWorkerInfo->WorkerPrivate()->AddPendingWindow(this);
|
||||
return NS_OK;
|
||||
}
|
||||
#endif
|
||||
|
||||
RefPtr<ResolveOpenWindowRunnable> resolveRunnable =
|
||||
new ResolveOpenWindowRunnable(mPromiseProxy, nullptr, rv);
|
||||
|
||||
Unused << NS_WARN_IF(!resolveRunnable->Dispatch());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsresult
|
||||
OpenWindow(nsPIDOMWindowOuter** aWindow)
|
||||
{
|
||||
MOZ_DIAGNOSTIC_ASSERT(aWindow);
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
|
||||
// [[1. Let url be the result of parsing url with entry settings object's API
|
||||
// base URL.]]
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
WorkerPrivate::LocationInfo& info = workerPrivate->GetLocationInfo();
|
||||
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(baseURI), info.mHref);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
rv = NS_NewURI(getter_AddRefs(uri), mUrl, nullptr, baseURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
// [[6.1 Open Window]]
|
||||
nsCOMPtr<nsIWindowMediator> wm = do_GetService(NS_WINDOWMEDIATOR_CONTRACTID,
|
||||
&rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (XRE_IsContentProcess()) {
|
||||
// ContentProcess
|
||||
nsCOMPtr<nsIWindowWatcher> wwatch =
|
||||
do_GetService(NS_WINDOWWATCHER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsPIWindowWatcher> pwwatch(do_QueryInterface(wwatch));
|
||||
NS_ENSURE_STATE(pwwatch);
|
||||
|
||||
nsCString spec;
|
||||
rv = uri->GetSpec(spec);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIDOMWindowProxy> newWindow;
|
||||
rv = pwwatch->OpenWindow2(nullptr,
|
||||
spec.get(),
|
||||
nullptr,
|
||||
nullptr,
|
||||
false, false, true, nullptr,
|
||||
// Not a spammy popup; we got permission, we swear!
|
||||
/* aIsPopupSpam = */ false,
|
||||
// Don't force noopener. We're not passing in an
|
||||
// opener anyway, and we _do_ want the returned
|
||||
// window.
|
||||
/* aForceNoOpener = */ false,
|
||||
/* aLoadInfp = */ nullptr,
|
||||
getter_AddRefs(newWindow));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
nsCOMPtr<nsPIDOMWindowOuter> pwindow = nsPIDOMWindowOuter::From(newWindow);
|
||||
pwindow.forget(aWindow);
|
||||
MOZ_DIAGNOSTIC_ASSERT(*aWindow);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Find the most recent browser window and open a new tab in it.
|
||||
nsCOMPtr<nsPIDOMWindowOuter> browserWindow =
|
||||
nsContentUtils::GetMostRecentNonPBWindow();
|
||||
if (!browserWindow) {
|
||||
// It is possible to be running without a browser window on Mac OS, so
|
||||
// we need to open a new chrome window.
|
||||
// TODO(catalinb): open new chrome window. Bug 1218080
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDOMChromeWindow> chromeWin = do_QueryInterface(browserWindow);
|
||||
if (NS_WARN_IF(!chromeWin)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBrowserDOMWindow> bwin;
|
||||
chromeWin->GetBrowserDOMWindow(getter_AddRefs(bwin));
|
||||
|
||||
if (NS_WARN_IF(!bwin)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<mozIDOMWindowProxy> win;
|
||||
rv = bwin->OpenURI(uri, nullptr,
|
||||
nsIBrowserDOMWindow::OPEN_DEFAULTWINDOW,
|
||||
nsIBrowserDOMWindow::OPEN_NEW,
|
||||
getter_AddRefs(win));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
NS_ENSURE_STATE(win);
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowOuter> pWin = nsPIDOMWindowOuter::From(win);
|
||||
pWin.forget(aWindow);
|
||||
MOZ_DIAGNOSTIC_ASSERT(*aWindow);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerClients::Get(const nsAString& aClientId, ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<GetRunnable> r =
|
||||
new GetRunnable(promiseProxy, aClientId);
|
||||
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerClients::MatchAll(const ClientQueryOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsString scope;
|
||||
mWorkerScope->GetScope(scope);
|
||||
|
||||
if (aOptions.mType != ClientType::Window) {
|
||||
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<MatchAllRunnable> r =
|
||||
new MatchAllRunnable(promiseProxy,
|
||||
NS_ConvertUTF16toUTF8(scope),
|
||||
aOptions.mIncludeUncontrolled);
|
||||
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerClients::OpenWindow(const nsAString& aUrl,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aUrl.EqualsLiteral("about:blank")) {
|
||||
promise->MaybeReject(NS_ERROR_TYPE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
// [[4. If this algorithm is not allowed to show a popup ..]]
|
||||
// In Gecko the service worker is allowed to show a popup only if the user
|
||||
// just clicked on a notification.
|
||||
if (!workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
|
||||
if (!promiseProxy) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsString scope;
|
||||
mWorkerScope->GetScope(scope);
|
||||
|
||||
RefPtr<OpenWindowRunnable> r = new OpenWindowRunnable(promiseProxy,
|
||||
aUrl, scope);
|
||||
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerClients::Claim(ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(mWorkerScope, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
nsString scope;
|
||||
mWorkerScope->GetScope(scope);
|
||||
|
||||
RefPtr<ClaimRunnable> runnable =
|
||||
new ClaimRunnable(promiseProxy, NS_ConvertUTF16toUTF8(scope));
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(runnable.forget()));
|
||||
return promise.forget();
|
||||
}
|
||||
64
dom/workers/ServiceWorkerClients.h
Normal file
64
dom/workers/ServiceWorkerClients.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- 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_serviceworkerclients_h
|
||||
#define mozilla_dom_workers_serviceworkerclients_h
|
||||
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
#include "mozilla/dom/WorkerScope.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/ClientsBinding.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerClients final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(ServiceWorkerClients)
|
||||
|
||||
explicit ServiceWorkerClients(ServiceWorkerGlobalScope* aWorkerScope);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Get(const nsAString& aClientId, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
MatchAll(const ClientQueryOptions& aOptions, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
OpenWindow(const nsAString& aUrl, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Claim(ErrorResult& aRv);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
ServiceWorkerGlobalScope*
|
||||
GetParentObject() const
|
||||
{
|
||||
return mWorkerScope;
|
||||
}
|
||||
|
||||
private:
|
||||
~ServiceWorkerClients()
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerGlobalScope> mWorkerScope;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerclients_h
|
||||
25
dom/workers/ServiceWorkerCommon.h
Normal file
25
dom/workers/ServiceWorkerCommon.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- 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_ServiceWorkerCommon_h
|
||||
#define mozilla_dom_ServiceWorkerCommon_h
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
// Use multiples of 2 since they can be bitwise ORed when calling
|
||||
// InvalidateServiceWorkerRegistrationWorker.
|
||||
enum class WhichServiceWorker {
|
||||
INSTALLING_WORKER = 1,
|
||||
WAITING_WORKER = 2,
|
||||
ACTIVE_WORKER = 4,
|
||||
};
|
||||
MOZ_MAKE_ENUM_CLASS_BITWISE_OPERATORS(WhichServiceWorker)
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ServiceWorkerCommon_h
|
||||
341
dom/workers/ServiceWorkerContainer.cpp
Normal file
341
dom/workers/ServiceWorkerContainer.cpp
Normal file
|
|
@ -0,0 +1,341 @@
|
|||
/* -*- 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 "ServiceWorkerContainer.h"
|
||||
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
#include "nsIURL.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/Services.h"
|
||||
|
||||
#include "nsCycleCollectionParticipant.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/ServiceWorkerContainerBinding.h"
|
||||
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
|
||||
|
||||
#include "ServiceWorker.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper,
|
||||
mControllerWorker, mReadyPromise)
|
||||
|
||||
/* static */ bool
|
||||
ServiceWorkerContainer::IsEnabled(JSContext* aCx, JSObject* aGlobal)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
JS::Rooted<JSObject*> global(aCx, aGlobal);
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = Navigator::GetWindowFromGlobal(global);
|
||||
if (!window) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (!doc || nsContentUtils::IsInPrivateBrowsing(doc)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return Preferences::GetBool("dom.serviceWorkers.enabled", false);
|
||||
}
|
||||
|
||||
ServiceWorkerContainer::ServiceWorkerContainer(nsPIDOMWindowInner* aWindow)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerContainer::~ServiceWorkerContainer()
|
||||
{
|
||||
RemoveReadyPromise();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerContainer::DisconnectFromOwner()
|
||||
{
|
||||
mControllerWorker = nullptr;
|
||||
RemoveReadyPromise();
|
||||
DOMEventTargetHelper::DisconnectFromOwner();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerContainer::ControllerChanged(ErrorResult& aRv)
|
||||
{
|
||||
mControllerWorker = nullptr;
|
||||
aRv = DispatchTrustedEvent(NS_LITERAL_STRING("controllerchange"));
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerContainer::RemoveReadyPromise()
|
||||
{
|
||||
if (nsCOMPtr<nsPIDOMWindowInner> window = GetOwner()) {
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm =
|
||||
mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
// If the browser is shutting down, we don't need to remove the promise.
|
||||
return;
|
||||
}
|
||||
|
||||
swm->RemoveReadyPromise(window);
|
||||
}
|
||||
}
|
||||
|
||||
JSObject*
|
||||
ServiceWorkerContainer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return ServiceWorkerContainerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
static nsresult
|
||||
CheckForSlashEscapedCharsInPath(nsIURI* aURI)
|
||||
{
|
||||
MOZ_ASSERT(aURI);
|
||||
|
||||
// A URL that can't be downcast to a standard URL is an invalid URL and should
|
||||
// be treated as such and fail with SecurityError.
|
||||
nsCOMPtr<nsIURL> url(do_QueryInterface(aURI));
|
||||
if (NS_WARN_IF(!url)) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
}
|
||||
|
||||
nsAutoCString path;
|
||||
nsresult rv = url->GetFilePath(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
ToLowerCase(path);
|
||||
if (path.Find("%2f") != kNotFound ||
|
||||
path.Find("%5c") != kNotFound) {
|
||||
return NS_ERROR_DOM_TYPE_ERR;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerContainer::Register(const nsAString& aScriptURL,
|
||||
const RegistrationOptions& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> baseURI;
|
||||
|
||||
nsIDocument* doc = GetEntryDocument();
|
||||
if (doc) {
|
||||
baseURI = doc->GetBaseURI();
|
||||
} else {
|
||||
// XXXnsm. One of our devtools browser test calls register() from a content
|
||||
// script where there is no valid entry document. Use the window to resolve
|
||||
// the uri in that case.
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
||||
nsCOMPtr<nsPIDOMWindowOuter> outerWindow;
|
||||
if (window && (outerWindow = window->GetOuterWindow()) &&
|
||||
outerWindow->GetServiceWorkersTestingEnabled()) {
|
||||
baseURI = window->GetDocBaseURI();
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIURI> scriptURI;
|
||||
rv = NS_NewURI(getter_AddRefs(scriptURI), aScriptURL, nullptr, baseURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.ThrowTypeError<MSG_INVALID_URL>(aScriptURL);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = CheckForSlashEscapedCharsInPath(scriptURI);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// In ServiceWorkerContainer.register() the scope argument is parsed against
|
||||
// different base URLs depending on whether it was passed or not.
|
||||
nsCOMPtr<nsIURI> scopeURI;
|
||||
|
||||
// Step 4. If none passed, parse against script's URL
|
||||
if (!aOptions.mScope.WasPassed()) {
|
||||
NS_NAMED_LITERAL_STRING(defaultScope, "./");
|
||||
rv = NS_NewURI(getter_AddRefs(scopeURI), defaultScope,
|
||||
nullptr, scriptURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsAutoCString spec;
|
||||
scriptURI->GetSpec(spec);
|
||||
NS_ConvertUTF8toUTF16 wSpec(spec);
|
||||
aRv.ThrowTypeError<MSG_INVALID_SCOPE>(defaultScope, wSpec);
|
||||
return nullptr;
|
||||
}
|
||||
} else {
|
||||
// Step 5. Parse against entry settings object's base URL.
|
||||
rv = NS_NewURI(getter_AddRefs(scopeURI), aOptions.mScope.Value(),
|
||||
nullptr, baseURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
nsIURI* uri = baseURI ? baseURI : scriptURI;
|
||||
nsAutoCString spec;
|
||||
uri->GetSpec(spec);
|
||||
NS_ConvertUTF8toUTF16 wSpec(spec);
|
||||
aRv.ThrowTypeError<MSG_INVALID_SCOPE>(aOptions.mScope.Value(), wSpec);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
aRv = CheckForSlashEscapedCharsInPath(scopeURI);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// The spec says that the "client" passed to Register() must be the global
|
||||
// where the ServiceWorkerContainer was retrieved from.
|
||||
aRv = swm->Register(GetOwner(), scopeURI, scriptURI, getter_AddRefs(promise));
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<workers::ServiceWorker>
|
||||
ServiceWorkerContainer::GetController()
|
||||
{
|
||||
if (!mControllerWorker) {
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// TODO: What should we do here if the ServiceWorker script fails to load?
|
||||
// In theory the DOM ServiceWorker object can exist without the worker
|
||||
// thread running, but it seems our design does not expect that.
|
||||
nsCOMPtr<nsISupports> serviceWorker;
|
||||
nsresult rv = swm->GetDocumentController(GetOwner(),
|
||||
getter_AddRefs(serviceWorker));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mControllerWorker =
|
||||
static_cast<workers::ServiceWorker*>(serviceWorker.get());
|
||||
}
|
||||
|
||||
RefPtr<workers::ServiceWorker> ref = mControllerWorker;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerContainer::GetRegistrations(ErrorResult& aRv)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
aRv = swm->GetRegistrations(GetOwner(), getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerContainer::GetRegistration(const nsAString& aDocumentURL,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsresult rv;
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = do_GetService(SERVICEWORKERMANAGER_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aRv.Throw(rv);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
aRv = swm->GetRegistration(GetOwner(), aDocumentURL, getter_AddRefs(promise));
|
||||
if (aRv.Failed()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<Promise> ret = static_cast<Promise*>(promise.get());
|
||||
MOZ_ASSERT(ret);
|
||||
return ret.forget();
|
||||
}
|
||||
|
||||
Promise*
|
||||
ServiceWorkerContainer::GetReady(ErrorResult& aRv)
|
||||
{
|
||||
if (mReadyPromise) {
|
||||
return mReadyPromise;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISupports> promise;
|
||||
aRv = swm->GetReadyPromise(GetOwner(), getter_AddRefs(promise));
|
||||
|
||||
mReadyPromise = static_cast<Promise*>(promise.get());
|
||||
return mReadyPromise;
|
||||
}
|
||||
|
||||
// Testing only.
|
||||
void
|
||||
ServiceWorkerContainer::GetScopeForUrl(const nsAString& aUrl,
|
||||
nsString& aScope,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager();
|
||||
if (!swm) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = GetOwner();
|
||||
if (NS_WARN_IF(!window)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = window->GetExtantDoc();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
aRv = swm->GetScopeForUrl(doc->NodePrincipal(),
|
||||
aUrl, aScope);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
87
dom/workers/ServiceWorkerContainer.h
Normal file
87
dom/workers/ServiceWorkerContainer.h
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
/* -*- 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_serviceworkercontainer_h__
|
||||
#define mozilla_dom_serviceworkercontainer_h__
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
struct RegistrationOptions;
|
||||
|
||||
namespace workers {
|
||||
class ServiceWorker;
|
||||
} // namespace workers
|
||||
|
||||
// Lightweight serviceWorker APIs collection.
|
||||
class ServiceWorkerContainer final : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerContainer, DOMEventTargetHelper)
|
||||
|
||||
IMPL_EVENT_HANDLER(controllerchange)
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
|
||||
static bool IsEnabled(JSContext* aCx, JSObject* aGlobal);
|
||||
|
||||
explicit ServiceWorkerContainer(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Register(const nsAString& aScriptURL,
|
||||
const RegistrationOptions& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<workers::ServiceWorker>
|
||||
GetController();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetRegistration(const nsAString& aDocumentURL,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetRegistrations(ErrorResult& aRv);
|
||||
|
||||
Promise*
|
||||
GetReady(ErrorResult& aRv);
|
||||
|
||||
// Testing only.
|
||||
void
|
||||
GetScopeForUrl(const nsAString& aUrl, nsString& aScope, ErrorResult& aRv);
|
||||
|
||||
// DOMEventTargetHelper
|
||||
void DisconnectFromOwner() override;
|
||||
|
||||
// Invalidates |mControllerWorker| and dispatches a "controllerchange"
|
||||
// event.
|
||||
void
|
||||
ControllerChanged(ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
~ServiceWorkerContainer();
|
||||
|
||||
void RemoveReadyPromise();
|
||||
|
||||
// This only changes when a worker hijacks everything in its scope by calling
|
||||
// claim.
|
||||
RefPtr<workers::ServiceWorker> mControllerWorker;
|
||||
|
||||
RefPtr<Promise> mReadyPromise;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_workers_serviceworkercontainer_h__ */
|
||||
1280
dom/workers/ServiceWorkerEvents.cpp
Normal file
1280
dom/workers/ServiceWorkerEvents.cpp
Normal file
File diff suppressed because it is too large
Load diff
316
dom/workers/ServiceWorkerEvents.h
Normal file
316
dom/workers/ServiceWorkerEvents.h
Normal file
|
|
@ -0,0 +1,316 @@
|
|||
/* -*- 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_serviceworkerevents_h__
|
||||
#define mozilla_dom_workers_serviceworkerevents_h__
|
||||
|
||||
#include "mozilla/dom/Event.h"
|
||||
#include "mozilla/dom/ExtendableEventBinding.h"
|
||||
#include "mozilla/dom/ExtendableMessageEventBinding.h"
|
||||
#include "mozilla/dom/FetchEventBinding.h"
|
||||
#include "mozilla/dom/File.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/Response.h"
|
||||
#include "mozilla/dom/workers/bindings/ServiceWorker.h"
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
class nsIInterceptedChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Blob;
|
||||
class MessagePort;
|
||||
class Request;
|
||||
class ResponseOrPromise;
|
||||
|
||||
struct PushEventInit;
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class ServiceWorkerRegistrationInfo;
|
||||
|
||||
class CancelChannelRunnable final : public Runnable
|
||||
{
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
const nsresult mStatus;
|
||||
public:
|
||||
CancelChannelRunnable(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
||||
nsresult aStatus);
|
||||
|
||||
NS_IMETHOD Run() override;
|
||||
};
|
||||
|
||||
class ExtendableEvent : public Event
|
||||
{
|
||||
protected:
|
||||
nsTArray<RefPtr<Promise>> mPromises;
|
||||
|
||||
explicit ExtendableEvent(mozilla::dom::EventTarget* aOwner);
|
||||
~ExtendableEvent() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ExtendableEvent, Event)
|
||||
NS_FORWARD_TO_EVENT
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return mozilla::dom::ExtendableEventBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
static already_AddRefed<ExtendableEvent>
|
||||
Constructor(mozilla::dom::EventTarget* aOwner,
|
||||
const nsAString& aType,
|
||||
const EventInit& aOptions)
|
||||
{
|
||||
RefPtr<ExtendableEvent> e = new ExtendableEvent(aOwner);
|
||||
bool trusted = e->Init(aOwner);
|
||||
e->InitEvent(aType, aOptions.mBubbles, aOptions.mCancelable);
|
||||
e->SetTrusted(trusted);
|
||||
e->SetComposed(aOptions.mComposed);
|
||||
return e.forget();
|
||||
}
|
||||
|
||||
static already_AddRefed<ExtendableEvent>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
const EventInit& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<EventTarget> target = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
return Constructor(target, aType, aOptions);
|
||||
}
|
||||
|
||||
void
|
||||
WaitUntil(JSContext* aCx, Promise& aPromise, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
GetPromise();
|
||||
|
||||
virtual ExtendableEvent* AsExtendableEvent() override
|
||||
{
|
||||
return this;
|
||||
}
|
||||
};
|
||||
|
||||
class FetchEvent final : public ExtendableEvent
|
||||
{
|
||||
nsMainThreadPtrHandle<nsIInterceptedChannel> mChannel;
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
RefPtr<Request> mRequest;
|
||||
nsCString mScriptSpec;
|
||||
nsCString mPreventDefaultScriptSpec;
|
||||
nsString mClientId;
|
||||
uint32_t mPreventDefaultLineNumber;
|
||||
uint32_t mPreventDefaultColumnNumber;
|
||||
bool mIsReload;
|
||||
bool mWaitToRespond;
|
||||
protected:
|
||||
explicit FetchEvent(EventTarget* aOwner);
|
||||
~FetchEvent();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(FetchEvent, ExtendableEvent)
|
||||
|
||||
// Note, we cannot use NS_FORWARD_TO_EVENT because we want a different
|
||||
// PreventDefault(JSContext*) override.
|
||||
NS_FORWARD_NSIDOMEVENT(Event::)
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return FetchEventBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void PostInit(nsMainThreadPtrHandle<nsIInterceptedChannel>& aChannel,
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration,
|
||||
const nsACString& aScriptSpec);
|
||||
|
||||
static already_AddRefed<FetchEvent>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
const FetchEventInit& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
WaitToRespond() const
|
||||
{
|
||||
return mWaitToRespond;
|
||||
}
|
||||
|
||||
Request*
|
||||
Request_() const
|
||||
{
|
||||
MOZ_ASSERT(mRequest);
|
||||
return mRequest;
|
||||
}
|
||||
|
||||
void
|
||||
GetClientId(nsAString& aClientId) const
|
||||
{
|
||||
aClientId = mClientId;
|
||||
}
|
||||
|
||||
bool
|
||||
IsReload() const
|
||||
{
|
||||
return mIsReload;
|
||||
}
|
||||
|
||||
void
|
||||
RespondWith(JSContext* aCx, Promise& aArg, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ForwardTo(const nsAString& aUrl);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Default();
|
||||
|
||||
void
|
||||
PreventDefault(JSContext* aCx) override;
|
||||
|
||||
void
|
||||
ReportCanceled();
|
||||
};
|
||||
|
||||
class PushMessageData final : public nsISupports,
|
||||
public nsWrapperCache
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(PushMessageData)
|
||||
|
||||
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject() const {
|
||||
return mOwner;
|
||||
}
|
||||
|
||||
void Json(JSContext* cx, JS::MutableHandle<JS::Value> aRetval,
|
||||
ErrorResult& aRv);
|
||||
void Text(nsAString& aData);
|
||||
void ArrayBuffer(JSContext* cx, JS::MutableHandle<JSObject*> aRetval,
|
||||
ErrorResult& aRv);
|
||||
already_AddRefed<mozilla::dom::Blob> Blob(ErrorResult& aRv);
|
||||
|
||||
PushMessageData(nsISupports* aOwner, nsTArray<uint8_t>&& aBytes);
|
||||
private:
|
||||
nsCOMPtr<nsISupports> mOwner;
|
||||
nsTArray<uint8_t> mBytes;
|
||||
nsString mDecodedText;
|
||||
~PushMessageData();
|
||||
|
||||
nsresult EnsureDecodedText();
|
||||
uint8_t* GetContentsCopy();
|
||||
};
|
||||
|
||||
class PushEvent final : public ExtendableEvent
|
||||
{
|
||||
RefPtr<PushMessageData> mData;
|
||||
|
||||
protected:
|
||||
explicit PushEvent(mozilla::dom::EventTarget* aOwner);
|
||||
~PushEvent() {}
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(PushEvent, ExtendableEvent)
|
||||
NS_FORWARD_TO_EVENT
|
||||
|
||||
virtual JSObject* WrapObjectInternal(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
static already_AddRefed<PushEvent>
|
||||
Constructor(mozilla::dom::EventTarget* aOwner,
|
||||
const nsAString& aType,
|
||||
const PushEventInit& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<PushEvent>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
const PushEventInit& aOptions,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<EventTarget> owner = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
return Constructor(owner, aType, aOptions, aRv);
|
||||
}
|
||||
|
||||
PushMessageData*
|
||||
GetData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
};
|
||||
|
||||
class ExtendableMessageEvent final : public ExtendableEvent
|
||||
{
|
||||
JS::Heap<JS::Value> mData;
|
||||
nsString mOrigin;
|
||||
nsString mLastEventId;
|
||||
RefPtr<ServiceWorkerClient> mClient;
|
||||
RefPtr<ServiceWorker> mServiceWorker;
|
||||
RefPtr<MessagePort> mMessagePort;
|
||||
nsTArray<RefPtr<MessagePort>> mPorts;
|
||||
|
||||
protected:
|
||||
explicit ExtendableMessageEvent(EventTarget* aOwner);
|
||||
~ExtendableMessageEvent();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(ExtendableMessageEvent,
|
||||
ExtendableEvent)
|
||||
|
||||
NS_FORWARD_TO_EVENT
|
||||
|
||||
virtual JSObject* WrapObjectInternal(
|
||||
JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
return mozilla::dom::ExtendableMessageEventBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
static already_AddRefed<ExtendableMessageEvent>
|
||||
Constructor(mozilla::dom::EventTarget* aOwner,
|
||||
const nsAString& aType,
|
||||
const ExtendableMessageEventInit& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
static already_AddRefed<ExtendableMessageEvent>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
const nsAString& aType,
|
||||
const ExtendableMessageEventInit& aOptions,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetData(JSContext* aCx, JS::MutableHandle<JS::Value> aData,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void GetSource(Nullable<OwningClientOrServiceWorkerOrMessagePort>& aValue) const;
|
||||
|
||||
NS_IMETHOD GetOrigin(nsAString& aOrigin)
|
||||
{
|
||||
aOrigin = mOrigin;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD GetLastEventId(nsAString& aLastEventId)
|
||||
{
|
||||
aLastEventId = mLastEventId;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void GetPorts(nsTArray<RefPtr<MessagePort>>& aPorts);
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_serviceworkerevents_h__ */
|
||||
229
dom/workers/ServiceWorkerInfo.cpp
Normal file
229
dom/workers/ServiceWorkerInfo.cpp
Normal file
|
|
@ -0,0 +1,229 @@
|
|||
/* -*- 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 "ServiceWorkerScriptCache.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
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 == static_cast<uint16_t>(ServiceWorkerState::EndGuard_),
|
||||
"ServiceWorkerState enumeration value should match state values from nsIServiceWorkerInfo.");
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerInfo, nsIServiceWorkerInfo)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::GetScriptSpec(nsAString& aScriptSpec)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
CopyUTF8toUTF16(mScriptSpec, aScriptSpec);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::GetCacheName(nsAString& aCacheName)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
aCacheName = mCacheName;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::GetState(uint16_t* aState)
|
||||
{
|
||||
MOZ_ASSERT(aState);
|
||||
AssertIsOnMainThread();
|
||||
*aState = static_cast<uint16_t>(mState);
|
||||
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::AttachDebugger()
|
||||
{
|
||||
return mServiceWorkerPrivate->AttachDebugger();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerInfo::DetachDebugger()
|
||||
{
|
||||
return mServiceWorkerPrivate->DetachDebugger();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::AppendWorker(ServiceWorker* aWorker)
|
||||
{
|
||||
MOZ_ASSERT(aWorker);
|
||||
#ifdef DEBUG
|
||||
nsAutoString workerURL;
|
||||
aWorker->GetScriptURL(workerURL);
|
||||
MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
|
||||
#endif
|
||||
MOZ_ASSERT(!mInstances.Contains(aWorker));
|
||||
|
||||
mInstances.AppendElement(aWorker);
|
||||
aWorker->SetState(State());
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::RemoveWorker(ServiceWorker* aWorker)
|
||||
{
|
||||
MOZ_ASSERT(aWorker);
|
||||
#ifdef DEBUG
|
||||
nsAutoString workerURL;
|
||||
aWorker->GetScriptURL(workerURL);
|
||||
MOZ_ASSERT(workerURL.Equals(NS_ConvertUTF8toUTF16(mScriptSpec)));
|
||||
#endif
|
||||
MOZ_ASSERT(mInstances.Contains(aWorker));
|
||||
|
||||
mInstances.RemoveElement(aWorker);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ChangeStateUpdater final : public Runnable
|
||||
{
|
||||
public:
|
||||
ChangeStateUpdater(const nsTArray<ServiceWorker*>& aInstances,
|
||||
ServiceWorkerState aState)
|
||||
: mState(aState)
|
||||
{
|
||||
for (size_t i = 0; i < aInstances.Length(); ++i) {
|
||||
mInstances.AppendElement(aInstances[i]);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
// We need to update the state of all instances atomically before notifying
|
||||
// them to make sure that the observed state for all instances inside
|
||||
// statechange event handlers is correct.
|
||||
for (size_t i = 0; i < mInstances.Length(); ++i) {
|
||||
mInstances[i]->SetState(mState);
|
||||
}
|
||||
for (size_t i = 0; i < mInstances.Length(); ++i) {
|
||||
mInstances[i]->DispatchStateChange(mState);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
AutoTArray<RefPtr<ServiceWorker>, 1> mInstances;
|
||||
ServiceWorkerState mState;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerInfo::UpdateState(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
#ifdef DEBUG
|
||||
// Any state can directly transition to redundant, but everything else is
|
||||
// ordered.
|
||||
if (aState != ServiceWorkerState::Redundant) {
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::EndGuard_, aState == ServiceWorkerState::Installing);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installing, aState == ServiceWorkerState::Installed);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Installed, aState == ServiceWorkerState::Activating);
|
||||
MOZ_ASSERT_IF(mState == ServiceWorkerState::Activating, aState == ServiceWorkerState::Activated);
|
||||
}
|
||||
// Activated can only go to redundant.
|
||||
MOZ_ASSERT_IF(mState == 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 (aState == ServiceWorkerState::Activated && mState != aState) {
|
||||
mServiceWorkerPrivate->Activated();
|
||||
}
|
||||
mState = aState;
|
||||
nsCOMPtr<nsIRunnable> r = new ChangeStateUpdater(mInstances, mState);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(r.forget()));
|
||||
if (mState == ServiceWorkerState::Redundant) {
|
||||
serviceWorkerScriptCache::PurgeCache(mPrincipal, mCacheName);
|
||||
}
|
||||
}
|
||||
|
||||
ServiceWorkerInfo::ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName)
|
||||
: mPrincipal(aPrincipal)
|
||||
, mScope(aScope)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mCacheName(aCacheName)
|
||||
, mState(ServiceWorkerState::EndGuard_)
|
||||
, mServiceWorkerID(GetNextID())
|
||||
, mServiceWorkerPrivate(new ServiceWorkerPrivate(this))
|
||||
, mSkipWaitingFlag(false)
|
||||
{
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
// cache origin attributes so we can use them off main thread
|
||||
mOriginAttributes = BasePrincipal::Cast(mPrincipal)->OriginAttributesRef();
|
||||
MOZ_ASSERT(!mScope.IsEmpty());
|
||||
MOZ_ASSERT(!mScriptSpec.IsEmpty());
|
||||
MOZ_ASSERT(!mCacheName.IsEmpty());
|
||||
}
|
||||
|
||||
ServiceWorkerInfo::~ServiceWorkerInfo()
|
||||
{
|
||||
MOZ_ASSERT(mServiceWorkerPrivate);
|
||||
mServiceWorkerPrivate->NoteDeadServiceWorkerInfo();
|
||||
}
|
||||
|
||||
static uint64_t gServiceWorkerInfoCurrentID = 0;
|
||||
|
||||
uint64_t
|
||||
ServiceWorkerInfo::GetNextID() const
|
||||
{
|
||||
return ++gServiceWorkerInfoCurrentID;
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorker>
|
||||
ServiceWorkerInfo::GetOrCreateInstance(nsPIDOMWindowInner* aWindow)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
RefPtr<ServiceWorker> ref;
|
||||
|
||||
for (uint32_t i = 0; i < mInstances.Length(); ++i) {
|
||||
MOZ_ASSERT(mInstances[i]);
|
||||
if (mInstances[i]->GetOwner() == aWindow) {
|
||||
ref = mInstances[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ref) {
|
||||
ref = new ServiceWorker(aWindow, this);
|
||||
}
|
||||
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
151
dom/workers/ServiceWorkerInfo.h
Normal file
151
dom/workers/ServiceWorkerInfo.h
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- 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_serviceworkerinfo_h
|
||||
#define mozilla_dom_workers_serviceworkerinfo_h
|
||||
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h" // For ServiceWorkerState
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorker;
|
||||
class ServiceWorkerPrivate;
|
||||
|
||||
/*
|
||||
* Wherever the spec treats a worker instance and a description of said worker
|
||||
* as the same thing; i.e. "Resolve foo with
|
||||
* _GetNewestWorker(serviceWorkerRegistration)", we represent the description
|
||||
* by this class and spawn a ServiceWorker in the right global when required.
|
||||
*/
|
||||
class ServiceWorkerInfo final : public nsIServiceWorkerInfo
|
||||
{
|
||||
private:
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsCString mScope;
|
||||
const nsCString mScriptSpec;
|
||||
const nsString mCacheName;
|
||||
ServiceWorkerState mState;
|
||||
PrincipalOriginAttributes mOriginAttributes;
|
||||
|
||||
// This id is shared with WorkerPrivate to match requests issued by service
|
||||
// workers to their corresponding serviceWorkerInfo.
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
// We hold rawptrs since the ServiceWorker constructor and destructor ensure
|
||||
// addition and removal.
|
||||
// There is a high chance of there being at least one ServiceWorker
|
||||
// associated with this all the time.
|
||||
AutoTArray<ServiceWorker*, 1> mInstances;
|
||||
|
||||
RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
|
||||
bool mSkipWaitingFlag;
|
||||
|
||||
~ServiceWorkerInfo();
|
||||
|
||||
// Generates a unique id for the service worker, with zero being treated as
|
||||
// invalid.
|
||||
uint64_t
|
||||
GetNextID() const;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERINFO
|
||||
|
||||
class ServiceWorkerPrivate*
|
||||
WorkerPrivate() const
|
||||
{
|
||||
MOZ_ASSERT(mServiceWorkerPrivate);
|
||||
return mServiceWorkerPrivate;
|
||||
}
|
||||
|
||||
nsIPrincipal*
|
||||
GetPrincipal() const
|
||||
{
|
||||
return mPrincipal;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
ScriptSpec() const
|
||||
{
|
||||
return mScriptSpec;
|
||||
}
|
||||
|
||||
const nsCString&
|
||||
Scope() const
|
||||
{
|
||||
return mScope;
|
||||
}
|
||||
|
||||
bool SkipWaitingFlag() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mSkipWaitingFlag;
|
||||
}
|
||||
|
||||
void SetSkipWaitingFlag()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mSkipWaitingFlag = true;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
const nsAString& aCacheName);
|
||||
|
||||
ServiceWorkerState
|
||||
State() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
const PrincipalOriginAttributes&
|
||||
GetOriginAttributes() const
|
||||
{
|
||||
return mOriginAttributes;
|
||||
}
|
||||
|
||||
const nsString&
|
||||
CacheName() const
|
||||
{
|
||||
return mCacheName;
|
||||
}
|
||||
|
||||
uint64_t
|
||||
ID() const
|
||||
{
|
||||
return mServiceWorkerID;
|
||||
}
|
||||
|
||||
void
|
||||
UpdateState(ServiceWorkerState aState);
|
||||
|
||||
// Only used to set initial state when loading from disk!
|
||||
void
|
||||
SetActivateStateUncheckedWithoutEvent(ServiceWorkerState aState)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mState = aState;
|
||||
}
|
||||
|
||||
void
|
||||
AppendWorker(ServiceWorker* aWorker);
|
||||
|
||||
void
|
||||
RemoveWorker(ServiceWorker* aWorker);
|
||||
|
||||
already_AddRefed<ServiceWorker>
|
||||
GetOrCreateInstance(nsPIDOMWindowInner* aWindow);
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerinfo_h
|
||||
246
dom/workers/ServiceWorkerJob.cpp
Normal file
246
dom/workers/ServiceWorkerJob.cpp
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
/* -*- 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 "ServiceWorkerJob.h"
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
ServiceWorkerJob::Type
|
||||
ServiceWorkerJob::GetType() const
|
||||
{
|
||||
return mType;
|
||||
}
|
||||
|
||||
ServiceWorkerJob::State
|
||||
ServiceWorkerJob::GetState() const
|
||||
{
|
||||
return mState;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerJob::Canceled() const
|
||||
{
|
||||
return mCanceled;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerJob::ResultCallbacksInvoked() const
|
||||
{
|
||||
return mResultCallbacksInvoked;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerJob::IsEquivalentTo(ServiceWorkerJob* aJob) const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
return mType == aJob->mType &&
|
||||
mScope.Equals(aJob->mScope) &&
|
||||
mScriptSpec.Equals(aJob->mScriptSpec) &&
|
||||
mPrincipal->Equals(aJob->mPrincipal);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::AppendResultCallback(Callback* aCallback)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState != State::Finished);
|
||||
MOZ_DIAGNOSTIC_ASSERT(aCallback);
|
||||
MOZ_DIAGNOSTIC_ASSERT(mFinalCallback != aCallback);
|
||||
MOZ_ASSERT(!mResultCallbackList.Contains(aCallback));
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
|
||||
mResultCallbackList.AppendElement(aCallback);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::StealResultCallbacksFrom(ServiceWorkerJob* aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
MOZ_ASSERT(aJob->mState == State::Initial);
|
||||
|
||||
// Take the callbacks from the other job immediately to avoid the
|
||||
// any possibility of them existing on both jobs at once.
|
||||
nsTArray<RefPtr<Callback>> callbackList;
|
||||
callbackList.SwapElements(aJob->mResultCallbackList);
|
||||
|
||||
for (RefPtr<Callback>& callback : callbackList) {
|
||||
// Use AppendResultCallback() so that assertion checking is performed on
|
||||
// each callback.
|
||||
AppendResultCallback(callback);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Start(Callback* aFinalCallback)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mCanceled);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(aFinalCallback);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mFinalCallback);
|
||||
MOZ_ASSERT(!mResultCallbackList.Contains(aFinalCallback));
|
||||
mFinalCallback = aFinalCallback;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == State::Initial);
|
||||
mState = State::Started;
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod(this, &ServiceWorkerJob::AsyncExecute);
|
||||
|
||||
// We may have to wait for the PBackground actor to be initialized
|
||||
// before proceeding. We should always be able to get a ServiceWorkerManager,
|
||||
// however, since Start() should not be called during shutdown.
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown
|
||||
return;
|
||||
}
|
||||
if (!swm->HasBackgroundActor()) {
|
||||
// waiting to initialize
|
||||
swm->AppendPendingOperation(runnable);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise start asynchronously. We should never run a job synchronously.
|
||||
MOZ_ALWAYS_TRUE(NS_SUCCEEDED(
|
||||
NS_DispatchToMainThread(runnable.forget())));
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Cancel()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mCanceled);
|
||||
mCanceled = true;
|
||||
}
|
||||
|
||||
ServiceWorkerJob::ServiceWorkerJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec)
|
||||
: mType(aType)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mScope(aScope)
|
||||
, mScriptSpec(aScriptSpec)
|
||||
, mState(State::Initial)
|
||||
, mCanceled(false)
|
||||
, mResultCallbacksInvoked(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mPrincipal);
|
||||
MOZ_ASSERT(!mScope.IsEmpty());
|
||||
// Some job types may have an empty script spec
|
||||
}
|
||||
|
||||
ServiceWorkerJob::~ServiceWorkerJob()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
// Jobs must finish or never be started. Destroying an actively running
|
||||
// job is an error.
|
||||
MOZ_ASSERT(mState != State::Started);
|
||||
MOZ_ASSERT_IF(mState == State::Finished, mResultCallbacksInvoked);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::InvokeResultCallbacks(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mResultCallbacksInvoked);
|
||||
mResultCallbacksInvoked = true;
|
||||
|
||||
nsTArray<RefPtr<Callback>> callbackList;
|
||||
callbackList.SwapElements(mResultCallbackList);
|
||||
|
||||
for (RefPtr<Callback>& callback : callbackList) {
|
||||
// The callback might consume an exception on the ErrorResult, so we need
|
||||
// to clone in order to maintain the error for the next callback.
|
||||
ErrorResult rv;
|
||||
aRv.CloneTo(rv);
|
||||
|
||||
callback->JobFinished(this, rv);
|
||||
|
||||
// The callback might not consume the error.
|
||||
rv.SuppressException();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::InvokeResultCallbacks(nsresult aRv)
|
||||
{
|
||||
ErrorResult converted(aRv);
|
||||
InvokeResultCallbacks(converted);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Finish(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// Avoid double-completion because it can result on operating on cleaned
|
||||
// up data. This should not happen, though, so also assert to try to
|
||||
// narrow down the causes.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mState == State::Started);
|
||||
if (mState != State::Started) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Ensure that we only surface SecurityErr, TypeErr or InvalidStateErr to script.
|
||||
if (aRv.Failed() && !aRv.ErrorCodeIs(NS_ERROR_DOM_SECURITY_ERR) &&
|
||||
!aRv.ErrorCodeIs(NS_ERROR_DOM_TYPE_ERR) &&
|
||||
!aRv.ErrorCodeIs(NS_ERROR_DOM_INVALID_STATE_ERR)) {
|
||||
|
||||
// Remove the old error code so we can replace it with a TypeError.
|
||||
aRv.SuppressException();
|
||||
|
||||
NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
|
||||
NS_ConvertUTF8toUTF16 scope(mScope);
|
||||
|
||||
// Throw the type error with a generic error message.
|
||||
aRv.ThrowTypeError<MSG_SW_INSTALL_ERROR>(scriptSpec, scope);
|
||||
}
|
||||
|
||||
// The final callback may drop the last ref to this object.
|
||||
RefPtr<ServiceWorkerJob> kungFuDeathGrip = this;
|
||||
|
||||
if (!mResultCallbacksInvoked) {
|
||||
InvokeResultCallbacks(aRv);
|
||||
}
|
||||
|
||||
mState = State::Finished;
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mFinalCallback);
|
||||
if (mFinalCallback) {
|
||||
mFinalCallback->JobFinished(this, aRv);
|
||||
mFinalCallback = nullptr;
|
||||
}
|
||||
|
||||
// The callback might not consume the error.
|
||||
aRv.SuppressException();
|
||||
|
||||
// Async release this object to ensure that our caller methods complete
|
||||
// as well.
|
||||
NS_ReleaseOnMainThread(kungFuDeathGrip.forget(), true /* always proxy */);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJob::Finish(nsresult aRv)
|
||||
{
|
||||
ErrorResult converted(aRv);
|
||||
Finish(converted);
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
155
dom/workers/ServiceWorkerJob.h
Normal file
155
dom/workers/ServiceWorkerJob.h
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
/* -*- 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_serviceworkerjob_h
|
||||
#define mozilla_dom_workers_serviceworkerjob_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class ErrorResult;
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerJob
|
||||
{
|
||||
public:
|
||||
// Implement this interface to receive notification when a job completes.
|
||||
class Callback
|
||||
{
|
||||
public:
|
||||
// Called once when the job completes. If the job is started, then this
|
||||
// will be called. If a job is never executed due to browser shutdown,
|
||||
// then this method will never be called. This method is always called
|
||||
// on the main thread asynchronously after Start() completes.
|
||||
virtual void JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
AddRef(void) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType)
|
||||
Release(void) = 0;
|
||||
};
|
||||
|
||||
enum class Type
|
||||
{
|
||||
Register,
|
||||
Update,
|
||||
Unregister
|
||||
};
|
||||
|
||||
enum class State
|
||||
{
|
||||
Initial,
|
||||
Started,
|
||||
Finished
|
||||
};
|
||||
|
||||
Type
|
||||
GetType() const;
|
||||
|
||||
State
|
||||
GetState() const;
|
||||
|
||||
// Determine if the job has been canceled. This does not change the
|
||||
// current State, but indicates that the job should progress to Finished
|
||||
// as soon as possible.
|
||||
bool
|
||||
Canceled() const;
|
||||
|
||||
// Determine if the result callbacks have already been called. This is
|
||||
// equivalent to the spec checked to see if the job promise has settled.
|
||||
bool
|
||||
ResultCallbacksInvoked() const;
|
||||
|
||||
bool
|
||||
IsEquivalentTo(ServiceWorkerJob* aJob) const;
|
||||
|
||||
// Add a callback that will be invoked when the job's result is available.
|
||||
// Some job types will invoke this before the job is actually finished.
|
||||
// If an early callback does not occur, then it will be called automatically
|
||||
// when Finish() is called. These callbacks will be invoked while the job
|
||||
// state is Started.
|
||||
void
|
||||
AppendResultCallback(Callback* aCallback);
|
||||
|
||||
// This takes ownership of any result callbacks associated with the given job
|
||||
// and then appends them to this job's callback list.
|
||||
void
|
||||
StealResultCallbacksFrom(ServiceWorkerJob* aJob);
|
||||
|
||||
// Start the job. All work will be performed asynchronously on
|
||||
// the main thread. The Finish() method must be called exactly
|
||||
// once after this point. A final callback must be provided. It
|
||||
// will be invoked after all other callbacks have been processed.
|
||||
void
|
||||
Start(Callback* aFinalCallback);
|
||||
|
||||
// Set an internal flag indicating that a started job should finish as
|
||||
// soon as possible.
|
||||
void
|
||||
Cancel();
|
||||
|
||||
protected:
|
||||
ServiceWorkerJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec);
|
||||
|
||||
virtual ~ServiceWorkerJob();
|
||||
|
||||
// Invoke the result callbacks immediately. The job must be in the
|
||||
// Started state. The callbacks are cleared after being invoked,
|
||||
// so subsequent method calls have no effect.
|
||||
void
|
||||
InvokeResultCallbacks(ErrorResult& aRv);
|
||||
|
||||
// Convenience method that converts to ErrorResult and calls real method.
|
||||
void
|
||||
InvokeResultCallbacks(nsresult aRv);
|
||||
|
||||
// Indicate that the job has completed. The must be called exactly
|
||||
// once after Start() has initiated job execution. It may not be
|
||||
// called until Start() has returned.
|
||||
void
|
||||
Finish(ErrorResult& aRv);
|
||||
|
||||
// Convenience method that converts to ErrorResult and calls real method.
|
||||
void
|
||||
Finish(nsresult aRv);
|
||||
|
||||
// Specific job types should define AsyncExecute to begin their work.
|
||||
// All errors and successes must result in Finish() being called.
|
||||
virtual void
|
||||
AsyncExecute() = 0;
|
||||
|
||||
const Type mType;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
const nsCString mScope;
|
||||
const nsCString mScriptSpec;
|
||||
|
||||
private:
|
||||
RefPtr<Callback> mFinalCallback;
|
||||
nsTArray<RefPtr<Callback>> mResultCallbackList;
|
||||
State mState;
|
||||
bool mCanceled;
|
||||
bool mResultCallbacksInvoked;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJob)
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerjob_h
|
||||
134
dom/workers/ServiceWorkerJobQueue.cpp
Normal file
134
dom/workers/ServiceWorkerJobQueue.cpp
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/* -*- 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 "ServiceWorkerJobQueue.h"
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerJobQueue::Callback final : public ServiceWorkerJob::Callback
|
||||
{
|
||||
RefPtr<ServiceWorkerJobQueue> mQueue;
|
||||
|
||||
~Callback()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
explicit Callback(ServiceWorkerJobQueue* aQueue)
|
||||
: mQueue(aQueue)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mQueue);
|
||||
}
|
||||
|
||||
virtual void
|
||||
JobFinished(ServiceWorkerJob* aJob, ErrorResult& aStatus) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mQueue->JobFinished(aJob);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJobQueue::Callback, override)
|
||||
};
|
||||
|
||||
ServiceWorkerJobQueue::~ServiceWorkerJobQueue()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mJobList.IsEmpty());
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::JobFinished(ServiceWorkerJob* aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
|
||||
// XXX There are some corner cases where jobs can double-complete. Until
|
||||
// we track all these down we do a non-fatal assert in debug builds and
|
||||
// a runtime check to verify the queue is in the correct state.
|
||||
NS_ASSERTION(!mJobList.IsEmpty(),
|
||||
"Job queue should contain the job that just completed.");
|
||||
NS_ASSERTION(mJobList.SafeElementAt(0, nullptr) == aJob,
|
||||
"Job queue should contain the job that just completed.");
|
||||
if (NS_WARN_IF(mJobList.SafeElementAt(0, nullptr) != aJob)) {
|
||||
return;
|
||||
}
|
||||
|
||||
mJobList.RemoveElementAt(0);
|
||||
|
||||
if (mJobList.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RunJob();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::RunJob()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!mJobList.IsEmpty());
|
||||
MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Initial);
|
||||
|
||||
RefPtr<Callback> callback = new Callback(this);
|
||||
mJobList[0]->Start(callback);
|
||||
}
|
||||
|
||||
ServiceWorkerJobQueue::ServiceWorkerJobQueue()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::ScheduleJob(ServiceWorkerJob* aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aJob);
|
||||
MOZ_ASSERT(!mJobList.Contains(aJob));
|
||||
|
||||
if (mJobList.IsEmpty()) {
|
||||
mJobList.AppendElement(aJob);
|
||||
RunJob();
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started);
|
||||
|
||||
RefPtr<ServiceWorkerJob>& tailJob = mJobList[mJobList.Length() - 1];
|
||||
if (!tailJob->ResultCallbacksInvoked() && aJob->IsEquivalentTo(tailJob)) {
|
||||
tailJob->StealResultCallbacksFrom(aJob);
|
||||
return;
|
||||
}
|
||||
|
||||
mJobList.AppendElement(aJob);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerJobQueue::CancelAll()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
for (RefPtr<ServiceWorkerJob>& job : mJobList) {
|
||||
job->Cancel();
|
||||
}
|
||||
|
||||
// Remove jobs that are queued but not started since they should never
|
||||
// run after being canceled. This means throwing away all jobs except
|
||||
// for the job at the front of the list.
|
||||
if (!mJobList.IsEmpty()) {
|
||||
MOZ_ASSERT(mJobList[0]->GetState() == ServiceWorkerJob::State::Started);
|
||||
mJobList.TruncateLength(1);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
49
dom/workers/ServiceWorkerJobQueue.h
Normal file
49
dom/workers/ServiceWorkerJobQueue.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- 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_serviceworkerjobqueue_h
|
||||
#define mozilla_dom_workers_serviceworkerjobqueue_h
|
||||
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerJob;
|
||||
|
||||
class ServiceWorkerJobQueue final
|
||||
{
|
||||
class Callback;
|
||||
|
||||
nsTArray<RefPtr<ServiceWorkerJob>> mJobList;
|
||||
|
||||
~ServiceWorkerJobQueue();
|
||||
|
||||
void
|
||||
JobFinished(ServiceWorkerJob* aJob);
|
||||
|
||||
void
|
||||
RunJob();
|
||||
|
||||
public:
|
||||
ServiceWorkerJobQueue();
|
||||
|
||||
void
|
||||
ScheduleJob(ServiceWorkerJob* aJob);
|
||||
|
||||
void
|
||||
CancelAll();
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerJobQueue)
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerjobqueue_h
|
||||
3969
dom/workers/ServiceWorkerManager.cpp
Normal file
3969
dom/workers/ServiceWorkerManager.cpp
Normal file
File diff suppressed because it is too large
Load diff
521
dom/workers/ServiceWorkerManager.h
Normal file
521
dom/workers/ServiceWorkerManager.h
Normal file
|
|
@ -0,0 +1,521 @@
|
|||
/* -*- 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/ConsoleReportCollector.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;
|
||||
class nsIConsoleReportCollector;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PrincipalOriginAttributes;
|
||||
|
||||
namespace dom {
|
||||
|
||||
class ServiceWorkerRegistrar;
|
||||
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);
|
||||
|
||||
// Return true if the given content process could potentially be executing
|
||||
// service worker code with the given principal. At the current time, this
|
||||
// just means that we have any registration for the origin, regardless of
|
||||
// scope. This is a very weak guarantee but is the best we can do when push
|
||||
// notifications can currently spin up a service worker in content processes
|
||||
// without our involvement in the parent process.
|
||||
//
|
||||
// In the future when there is only a single ServiceWorkerManager in the
|
||||
// parent process that is entirely in control of spawning and running service
|
||||
// worker code, we will be able to authoritatively indicate whether there is
|
||||
// an activate service worker in the given content process. At that time we
|
||||
// will rename this method HasActiveServiceWorkerInstance and provide
|
||||
// semantics that ensure this method returns true until the worker is known to
|
||||
// have shut down in order to allow the caller to induce a crash for security
|
||||
// reasons without having to worry about shutdown races with the worker.
|
||||
bool
|
||||
MayHaveActiveServiceWorkerInstance(ContentParent* aContent,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
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;
|
||||
|
||||
already_AddRefed<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);
|
||||
|
||||
void
|
||||
FlushReportsToAllClients(const nsACString& aScope,
|
||||
nsIConsoleReportCollector* aReporter);
|
||||
|
||||
// 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);
|
||||
|
||||
void
|
||||
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);
|
||||
|
||||
void
|
||||
WorkerIsIdle(ServiceWorkerInfo* aWorker);
|
||||
|
||||
private:
|
||||
ServiceWorkerManager();
|
||||
~ServiceWorkerManager();
|
||||
|
||||
void
|
||||
Init(ServiceWorkerRegistrar* aRegistrar);
|
||||
|
||||
void
|
||||
MaybeStartShutdown();
|
||||
|
||||
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
|
||||
107
dom/workers/ServiceWorkerManagerChild.cpp
Normal file
107
dom/workers/ServiceWorkerManagerChild.cpp
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/* -*- 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 "ServiceWorkerManagerChild.h"
|
||||
#include "ServiceWorkerManager.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerChild::RecvNotifyRegister(
|
||||
const ServiceWorkerRegistrationData& aData)
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->LoadRegistration(aData);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerChild::RecvNotifySoftUpdate(
|
||||
const PrincipalOriginAttributes& aOriginAttributes,
|
||||
const nsString& aScope)
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->SoftUpdate(aOriginAttributes, NS_ConvertUTF16toUTF8(aScope));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerChild::RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope)
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown
|
||||
return true;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(aPrincipalInfo);
|
||||
if (NS_WARN_IF(!principal)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
nsresult rv = swm->NotifyUnregister(principal, aScope);
|
||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerChild::RecvNotifyRemove(const nsCString& aHost)
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->Remove(aHost);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerChild::RecvNotifyRemoveAll()
|
||||
{
|
||||
if (mShuttingDown) {
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->RemoveAll();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
63
dom/workers/ServiceWorkerManagerChild.h
Normal file
63
dom/workers/ServiceWorkerManagerChild.h
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/* -*- 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_ServiceWorkerManagerChild_h
|
||||
#define mozilla_dom_ServiceWorkerManagerChild_h
|
||||
|
||||
#include "mozilla/dom/PServiceWorkerManagerChild.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PrincipalOriginAttributes;
|
||||
|
||||
namespace ipc {
|
||||
class BackgroundChildImpl;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerManagerChild final : public PServiceWorkerManagerChild
|
||||
{
|
||||
friend class mozilla::ipc::BackgroundChildImpl;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerManagerChild)
|
||||
|
||||
void ManagerShuttingDown()
|
||||
{
|
||||
mShuttingDown = true;
|
||||
}
|
||||
|
||||
virtual bool RecvNotifyRegister(const ServiceWorkerRegistrationData& aData)
|
||||
override;
|
||||
|
||||
virtual bool RecvNotifySoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
|
||||
const nsString& aScope) override;
|
||||
|
||||
virtual bool RecvNotifyUnregister(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope) override;
|
||||
|
||||
virtual bool RecvNotifyRemove(const nsCString& aHost) override;
|
||||
|
||||
virtual bool RecvNotifyRemoveAll() override;
|
||||
|
||||
private:
|
||||
ServiceWorkerManagerChild()
|
||||
: mShuttingDown(false)
|
||||
{}
|
||||
|
||||
~ServiceWorkerManagerChild() {}
|
||||
|
||||
bool mShuttingDown;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ServiceWorkerManagerChild_h
|
||||
330
dom/workers/ServiceWorkerManagerParent.cpp
Normal file
330
dom/workers/ServiceWorkerManagerParent.cpp
Normal file
|
|
@ -0,0 +1,330 @@
|
|||
/* -*- 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 "ServiceWorkerManagerParent.h"
|
||||
#include "ServiceWorkerManagerService.h"
|
||||
#include "mozilla/AppProcessChecker.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/ipc/BackgroundUtils.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
namespace {
|
||||
|
||||
uint64_t sServiceWorkerManagerParentID = 0;
|
||||
|
||||
class RegisterServiceWorkerCallback final : public Runnable
|
||||
{
|
||||
public:
|
||||
RegisterServiceWorkerCallback(const ServiceWorkerRegistrationData& aData,
|
||||
uint64_t aParentID)
|
||||
: mData(aData)
|
||||
, mParentID(aParentID)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<dom::ServiceWorkerRegistrar> service =
|
||||
dom::ServiceWorkerRegistrar::Get();
|
||||
|
||||
// Shutdown during the process of trying to update the registrar. Give
|
||||
// up on this modification.
|
||||
if (!service) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
service->RegisterServiceWorker(mData);
|
||||
|
||||
RefPtr<ServiceWorkerManagerService> managerService =
|
||||
ServiceWorkerManagerService::Get();
|
||||
if (managerService) {
|
||||
managerService->PropagateRegistration(mParentID, mData);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
ServiceWorkerRegistrationData mData;
|
||||
const uint64_t mParentID;
|
||||
};
|
||||
|
||||
class UnregisterServiceWorkerCallback final : public Runnable
|
||||
{
|
||||
public:
|
||||
UnregisterServiceWorkerCallback(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope,
|
||||
uint64_t aParentID)
|
||||
: mPrincipalInfo(aPrincipalInfo)
|
||||
, mScope(aScope)
|
||||
, mParentID(aParentID)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<dom::ServiceWorkerRegistrar> service =
|
||||
dom::ServiceWorkerRegistrar::Get();
|
||||
|
||||
// Shutdown during the process of trying to update the registrar. Give
|
||||
// up on this modification.
|
||||
if (!service) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
service->UnregisterServiceWorker(mPrincipalInfo,
|
||||
NS_ConvertUTF16toUTF8(mScope));
|
||||
|
||||
RefPtr<ServiceWorkerManagerService> managerService =
|
||||
ServiceWorkerManagerService::Get();
|
||||
if (managerService) {
|
||||
managerService->PropagateUnregister(mParentID, mPrincipalInfo,
|
||||
mScope);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
const PrincipalInfo mPrincipalInfo;
|
||||
nsString mScope;
|
||||
uint64_t mParentID;
|
||||
};
|
||||
|
||||
class CheckPrincipalWithCallbackRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
CheckPrincipalWithCallbackRunnable(already_AddRefed<ContentParent> aParent,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
Runnable* aCallback)
|
||||
: mContentParent(aParent)
|
||||
, mPrincipalInfo(aPrincipalInfo)
|
||||
, mCallback(aCallback)
|
||||
, mBackgroundThread(NS_GetCurrentThread())
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
MOZ_ASSERT(mContentParent);
|
||||
MOZ_ASSERT(mCallback);
|
||||
MOZ_ASSERT(mBackgroundThread);
|
||||
}
|
||||
|
||||
NS_IMETHOD Run() override
|
||||
{
|
||||
if (NS_IsMainThread()) {
|
||||
nsCOMPtr<nsIPrincipal> principal = PrincipalInfoToPrincipal(mPrincipalInfo);
|
||||
AssertAppPrincipal(mContentParent, principal);
|
||||
mContentParent = nullptr;
|
||||
|
||||
mBackgroundThread->Dispatch(this, NS_DISPATCH_NORMAL);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
AssertIsOnBackgroundThread();
|
||||
mCallback->Run();
|
||||
mCallback = nullptr;
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
RefPtr<ContentParent> mContentParent;
|
||||
PrincipalInfo mPrincipalInfo;
|
||||
RefPtr<Runnable> mCallback;
|
||||
nsCOMPtr<nsIThread> mBackgroundThread;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
ServiceWorkerManagerParent::ServiceWorkerManagerParent()
|
||||
: mService(ServiceWorkerManagerService::GetOrCreate())
|
||||
, mID(++sServiceWorkerManagerParentID)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
mService->RegisterActor(this);
|
||||
}
|
||||
|
||||
ServiceWorkerManagerParent::~ServiceWorkerManagerParent()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvRegister(
|
||||
const ServiceWorkerRegistrationData& aData)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
// Basic validation.
|
||||
if (aData.scope().IsEmpty() ||
|
||||
aData.principal().type() == PrincipalInfo::TNullPrincipalInfo ||
|
||||
aData.principal().type() == PrincipalInfo::TSystemPrincipalInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<RegisterServiceWorkerCallback> callback =
|
||||
new RegisterServiceWorkerCallback(aData, mID);
|
||||
|
||||
RefPtr<ContentParent> parent =
|
||||
BackgroundParent::GetContentParent(Manager());
|
||||
|
||||
// If the ContentParent is null we are dealing with a same-process actor.
|
||||
if (!parent) {
|
||||
callback->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<CheckPrincipalWithCallbackRunnable> runnable =
|
||||
new CheckPrincipalWithCallbackRunnable(parent.forget(), aData.principal(),
|
||||
callback);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvUnregister(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope)
|
||||
{
|
||||
AssertIsInMainProcess();
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
// Basic validation.
|
||||
if (aScope.IsEmpty() ||
|
||||
aPrincipalInfo.type() == PrincipalInfo::TNullPrincipalInfo ||
|
||||
aPrincipalInfo.type() == PrincipalInfo::TSystemPrincipalInfo) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RefPtr<UnregisterServiceWorkerCallback> callback =
|
||||
new UnregisterServiceWorkerCallback(aPrincipalInfo, aScope, mID);
|
||||
|
||||
RefPtr<ContentParent> parent =
|
||||
BackgroundParent::GetContentParent(Manager());
|
||||
|
||||
// If the ContentParent is null we are dealing with a same-process actor.
|
||||
if (!parent) {
|
||||
callback->Run();
|
||||
return true;
|
||||
}
|
||||
|
||||
RefPtr<CheckPrincipalWithCallbackRunnable> runnable =
|
||||
new CheckPrincipalWithCallbackRunnable(parent.forget(), aPrincipalInfo,
|
||||
callback);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvPropagateSoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
|
||||
const nsString& aScope)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (NS_WARN_IF(!mService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mService->PropagateSoftUpdate(mID, aOriginAttributes, aScope);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvPropagateUnregister(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (NS_WARN_IF(!mService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mService->PropagateUnregister(mID, aPrincipalInfo, aScope);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvPropagateRemove(const nsCString& aHost)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (NS_WARN_IF(!mService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mService->PropagateRemove(mID, aHost);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvPropagateRemoveAll()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (NS_WARN_IF(!mService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mService->PropagateRemoveAll(mID);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerManagerParent::RecvShutdown()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (NS_WARN_IF(!mService)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mService->UnregisterActor(this);
|
||||
mService = nullptr;
|
||||
|
||||
Unused << Send__delete__(this);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerParent::ActorDestroy(ActorDestroyReason aWhy)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mService) {
|
||||
// This object is about to be released and with it, also mService will be
|
||||
// released too.
|
||||
mService->UnregisterActor(this);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
72
dom/workers/ServiceWorkerManagerParent.h
Normal file
72
dom/workers/ServiceWorkerManagerParent.h
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/* -*- 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_ServiceWorkerManagerParent_h
|
||||
#define mozilla_dom_ServiceWorkerManagerParent_h
|
||||
|
||||
#include "mozilla/dom/PServiceWorkerManagerParent.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PrincipalOriginAttributes;
|
||||
|
||||
namespace ipc {
|
||||
class BackgroundParentImpl;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerManagerService;
|
||||
|
||||
class ServiceWorkerManagerParent final : public PServiceWorkerManagerParent
|
||||
{
|
||||
friend class mozilla::ipc::BackgroundParentImpl;
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ServiceWorkerManagerParent)
|
||||
|
||||
uint64_t ID() const
|
||||
{
|
||||
return mID;
|
||||
}
|
||||
|
||||
private:
|
||||
ServiceWorkerManagerParent();
|
||||
~ServiceWorkerManagerParent();
|
||||
|
||||
virtual bool RecvRegister(
|
||||
const ServiceWorkerRegistrationData& aData) override;
|
||||
|
||||
virtual bool RecvUnregister(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope) override;
|
||||
|
||||
virtual bool RecvPropagateSoftUpdate(const PrincipalOriginAttributes& aOriginAttributes,
|
||||
const nsString& aScope) override;
|
||||
|
||||
virtual bool RecvPropagateUnregister(const PrincipalInfo& aPrincipalInfo,
|
||||
const nsString& aScope) override;
|
||||
|
||||
virtual bool RecvPropagateRemove(const nsCString& aHost) override;
|
||||
|
||||
virtual bool RecvPropagateRemoveAll() override;
|
||||
|
||||
virtual bool RecvShutdown() override;
|
||||
|
||||
virtual void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
RefPtr<ServiceWorkerManagerService> mService;
|
||||
|
||||
// We use this ID in the Service in order to avoid the sending of messages to
|
||||
// ourself.
|
||||
uint64_t mID;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ServiceWorkerManagerParent_h
|
||||
237
dom/workers/ServiceWorkerManagerService.cpp
Normal file
237
dom/workers/ServiceWorkerManagerService.cpp
Normal file
|
|
@ -0,0 +1,237 @@
|
|||
/* -*- 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 "ServiceWorkerManagerService.h"
|
||||
#include "ServiceWorkerManagerParent.h"
|
||||
#include "ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/ContentParent.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/Unused.h"
|
||||
#include "nsAutoPtr.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
using namespace ipc;
|
||||
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
namespace {
|
||||
|
||||
ServiceWorkerManagerService* sInstance = nullptr;
|
||||
|
||||
} // namespace
|
||||
|
||||
ServiceWorkerManagerService::ServiceWorkerManagerService()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
// sInstance is a raw ServiceWorkerManagerService*.
|
||||
MOZ_ASSERT(!sInstance);
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
ServiceWorkerManagerService::~ServiceWorkerManagerService()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(sInstance == this);
|
||||
MOZ_ASSERT(mAgents.Count() == 0);
|
||||
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ServiceWorkerManagerService>
|
||||
ServiceWorkerManagerService::Get()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<ServiceWorkerManagerService> instance = sInstance;
|
||||
return instance.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ServiceWorkerManagerService>
|
||||
ServiceWorkerManagerService::GetOrCreate()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<ServiceWorkerManagerService> instance = sInstance;
|
||||
if (!instance) {
|
||||
instance = new ServiceWorkerManagerService();
|
||||
}
|
||||
return instance.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::RegisterActor(ServiceWorkerManagerParent* aParent)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aParent);
|
||||
MOZ_ASSERT(!mAgents.Contains(aParent));
|
||||
|
||||
mAgents.PutEntry(aParent);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::UnregisterActor(ServiceWorkerManagerParent* aParent)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(aParent);
|
||||
MOZ_ASSERT(mAgents.Contains(aParent));
|
||||
|
||||
mAgents.RemoveEntry(aParent);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::PropagateRegistration(
|
||||
uint64_t aParentID,
|
||||
ServiceWorkerRegistrationData& aData)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
DebugOnly<bool> parentFound = false;
|
||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
if (parent->ID() != aParentID) {
|
||||
Unused << parent->SendNotifyRegister(aData);
|
||||
#ifdef DEBUG
|
||||
} else {
|
||||
parentFound = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(parentFound);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::PropagateSoftUpdate(
|
||||
uint64_t aParentID,
|
||||
const PrincipalOriginAttributes& aOriginAttributes,
|
||||
const nsAString& aScope)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
DebugOnly<bool> parentFound = false;
|
||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
nsString scope(aScope);
|
||||
Unused << parent->SendNotifySoftUpdate(aOriginAttributes,
|
||||
scope);
|
||||
|
||||
#ifdef DEBUG
|
||||
if (parent->ID() == aParentID) {
|
||||
parentFound = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(parentFound);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::PropagateUnregister(
|
||||
uint64_t aParentID,
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsAString& aScope)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<dom::ServiceWorkerRegistrar> service =
|
||||
dom::ServiceWorkerRegistrar::Get();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
// It's possible that we don't have any ServiceWorkerManager managing this
|
||||
// scope but we still need to unregister it from the ServiceWorkerRegistrar.
|
||||
service->UnregisterServiceWorker(aPrincipalInfo,
|
||||
NS_ConvertUTF16toUTF8(aScope));
|
||||
|
||||
DebugOnly<bool> parentFound = false;
|
||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
if (parent->ID() != aParentID) {
|
||||
nsString scope(aScope);
|
||||
Unused << parent->SendNotifyUnregister(aPrincipalInfo, scope);
|
||||
#ifdef DEBUG
|
||||
} else {
|
||||
parentFound = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(parentFound);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::PropagateRemove(uint64_t aParentID,
|
||||
const nsACString& aHost)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
DebugOnly<bool> parentFound = false;
|
||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
if (parent->ID() != aParentID) {
|
||||
nsCString host(aHost);
|
||||
Unused << parent->SendNotifyRemove(host);
|
||||
#ifdef DEBUG
|
||||
} else {
|
||||
parentFound = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(parentFound);
|
||||
#endif
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerManagerService::PropagateRemoveAll(uint64_t aParentID)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
RefPtr<dom::ServiceWorkerRegistrar> service =
|
||||
dom::ServiceWorkerRegistrar::Get();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
service->RemoveAll();
|
||||
|
||||
DebugOnly<bool> parentFound = false;
|
||||
for (auto iter = mAgents.Iter(); !iter.Done(); iter.Next()) {
|
||||
RefPtr<ServiceWorkerManagerParent> parent = iter.Get()->GetKey();
|
||||
MOZ_ASSERT(parent);
|
||||
|
||||
if (parent->ID() != aParentID) {
|
||||
Unused << parent->SendNotifyRemoveAll();
|
||||
#ifdef DEBUG
|
||||
} else {
|
||||
parentFound = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(parentFound);
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
67
dom/workers/ServiceWorkerManagerService.h
Normal file
67
dom/workers/ServiceWorkerManagerService.h
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- 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_ServiceWorkerManagerService_h
|
||||
#define mozilla_dom_ServiceWorkerManagerService_h
|
||||
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsHashKeys.h"
|
||||
#include "nsTHashtable.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
class PrincipalOriginAttributes;
|
||||
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
class ServiceWorkerRegistrationData;
|
||||
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerManagerParent;
|
||||
|
||||
class ServiceWorkerManagerService final
|
||||
{
|
||||
public:
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerManagerService)
|
||||
|
||||
static already_AddRefed<ServiceWorkerManagerService> Get();
|
||||
static already_AddRefed<ServiceWorkerManagerService> GetOrCreate();
|
||||
|
||||
void RegisterActor(ServiceWorkerManagerParent* aParent);
|
||||
void UnregisterActor(ServiceWorkerManagerParent* aParent);
|
||||
|
||||
void PropagateRegistration(uint64_t aParentID,
|
||||
ServiceWorkerRegistrationData& aData);
|
||||
|
||||
void PropagateSoftUpdate(uint64_t aParentID,
|
||||
const PrincipalOriginAttributes& aOriginAttributes,
|
||||
const nsAString& aScope);
|
||||
|
||||
void PropagateUnregister(uint64_t aParentID,
|
||||
const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
const nsAString& aScope);
|
||||
|
||||
void PropagateRemove(uint64_t aParentID, const nsACString& aHost);
|
||||
|
||||
void PropagateRemoveAll(uint64_t aParentID);
|
||||
|
||||
private:
|
||||
ServiceWorkerManagerService();
|
||||
~ServiceWorkerManagerService();
|
||||
|
||||
nsTHashtable<nsPtrHashKey<ServiceWorkerManagerParent>> mAgents;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_ServiceWorkerManagerService_h
|
||||
2088
dom/workers/ServiceWorkerPrivate.cpp
Normal file
2088
dom/workers/ServiceWorkerPrivate.cpp
Normal file
File diff suppressed because it is too large
Load diff
236
dom/workers/ServiceWorkerPrivate.h
Normal file
236
dom/workers/ServiceWorkerPrivate.h
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/* -*- 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_serviceworkerprivate_h
|
||||
#define mozilla_dom_workers_serviceworkerprivate_h
|
||||
|
||||
#include "nsCOMPtr.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
#define NOTIFICATION_CLICK_EVENT_NAME "notificationclick"
|
||||
#define NOTIFICATION_CLOSE_EVENT_NAME "notificationclose"
|
||||
|
||||
class nsIInterceptedChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerInfo;
|
||||
class ServiceWorkerRegistrationInfo;
|
||||
class KeepAliveToken;
|
||||
|
||||
class LifeCycleEventCallback : public Runnable
|
||||
{
|
||||
public:
|
||||
// Called on the worker thread.
|
||||
virtual void
|
||||
SetResult(bool aResult) = 0;
|
||||
};
|
||||
|
||||
// ServiceWorkerPrivate is a wrapper for managing the on-demand aspect of
|
||||
// service workers. It handles all event dispatching to the worker and ensures
|
||||
// the worker thread is running when needed.
|
||||
//
|
||||
// Lifetime management: To spin up the worker thread we own a |WorkerPrivate|
|
||||
// object which can be cancelled if no events are received for a certain
|
||||
// amount of time. The worker is kept alive by holding a |KeepAliveToken|
|
||||
// reference.
|
||||
//
|
||||
// Extendable events hold tokens for the duration of their handler execution
|
||||
// and until their waitUntil promise is resolved, while ServiceWorkerPrivate
|
||||
// will hold a token for |dom.serviceWorkers.idle_timeout| seconds after each
|
||||
// new event.
|
||||
//
|
||||
// Note: All timer events must be handled on the main thread because the
|
||||
// worker may block indefinitely the worker thread (e. g. infinite loop in the
|
||||
// script).
|
||||
//
|
||||
// There are 3 cases where we may ignore keep alive tokens:
|
||||
// 1. When ServiceWorkerPrivate's token expired, if there are still waitUntil
|
||||
// handlers holding tokens, we wait another |dom.serviceWorkers.idle_extended_timeout|
|
||||
// seconds before forcibly terminating the worker.
|
||||
// 2. If the worker stopped controlling documents and it is not handling push
|
||||
// events.
|
||||
// 3. The content process is shutting down.
|
||||
//
|
||||
// Adding an API function for a new event requires calling |SpawnWorkerIfNeeded|
|
||||
// with an appropriate reason before any runnable is dispatched to the worker.
|
||||
// If the event is extendable then the runnable should inherit
|
||||
// ExtendableEventWorkerRunnable.
|
||||
class ServiceWorkerPrivate final : public nsIObserver
|
||||
{
|
||||
friend class KeepAliveToken;
|
||||
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS(ServiceWorkerPrivate)
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
explicit ServiceWorkerPrivate(ServiceWorkerInfo* aInfo);
|
||||
|
||||
nsresult
|
||||
SendMessageEvent(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo);
|
||||
|
||||
// This is used to validate the worker script and continue the installation
|
||||
// process.
|
||||
nsresult
|
||||
CheckScriptEvaluation(LifeCycleEventCallback* aCallback);
|
||||
|
||||
nsresult
|
||||
SendLifeCycleEvent(const nsAString& aEventType,
|
||||
LifeCycleEventCallback* aCallback,
|
||||
nsIRunnable* aLoadFailure);
|
||||
|
||||
nsresult
|
||||
SendPushEvent(const nsAString& aMessageId,
|
||||
const Maybe<nsTArray<uint8_t>>& aData,
|
||||
ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
nsresult
|
||||
SendPushSubscriptionChangeEvent();
|
||||
|
||||
nsresult
|
||||
SendNotificationEvent(const nsAString& aEventName,
|
||||
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,
|
||||
const nsAString& aScope);
|
||||
|
||||
nsresult
|
||||
SendFetchEvent(nsIInterceptedChannel* aChannel,
|
||||
nsILoadGroup* aLoadGroup,
|
||||
const nsAString& aDocumentId,
|
||||
bool aIsReload);
|
||||
|
||||
void
|
||||
StoreISupports(nsISupports* aSupports);
|
||||
|
||||
void
|
||||
RemoveISupports(nsISupports* aSupports);
|
||||
|
||||
// This will terminate the current running worker thread and drop the
|
||||
// workerPrivate reference.
|
||||
// Called by ServiceWorkerInfo when [[Clear Registration]] is invoked
|
||||
// or whenever the spec mandates that we terminate the worker.
|
||||
// This is a no-op if the worker has already been stopped.
|
||||
void
|
||||
TerminateWorker();
|
||||
|
||||
void
|
||||
NoteDeadServiceWorkerInfo();
|
||||
|
||||
void
|
||||
NoteStoppedControllingDocuments();
|
||||
|
||||
void
|
||||
Activated();
|
||||
|
||||
nsresult
|
||||
GetDebugger(nsIWorkerDebugger** aResult);
|
||||
|
||||
nsresult
|
||||
AttachDebugger();
|
||||
|
||||
nsresult
|
||||
DetachDebugger();
|
||||
|
||||
bool
|
||||
IsIdle() const;
|
||||
|
||||
void
|
||||
AddPendingWindow(Runnable* aPendingWindow);
|
||||
|
||||
private:
|
||||
enum WakeUpReason {
|
||||
FetchEvent = 0,
|
||||
PushEvent,
|
||||
PushSubscriptionChangeEvent,
|
||||
MessageEvent,
|
||||
NotificationClickEvent,
|
||||
NotificationCloseEvent,
|
||||
LifeCycleEvent,
|
||||
AttachEvent
|
||||
};
|
||||
|
||||
// Timer callbacks
|
||||
void
|
||||
NoteIdleWorkerCallback(nsITimer* aTimer);
|
||||
|
||||
void
|
||||
TerminateWorkerCallback(nsITimer* aTimer);
|
||||
|
||||
void
|
||||
RenewKeepAliveToken(WakeUpReason aWhy);
|
||||
|
||||
void
|
||||
ResetIdleTimeout();
|
||||
|
||||
void
|
||||
AddToken();
|
||||
|
||||
void
|
||||
ReleaseToken();
|
||||
|
||||
// |aLoadFailedRunnable| is a runnable dispatched to the main thread
|
||||
// if the script loader failed for some reason, but can be null.
|
||||
nsresult
|
||||
SpawnWorkerIfNeeded(WakeUpReason aWhy,
|
||||
nsIRunnable* aLoadFailedRunnable,
|
||||
nsILoadGroup* aLoadGroup = nullptr);
|
||||
|
||||
~ServiceWorkerPrivate();
|
||||
|
||||
already_AddRefed<KeepAliveToken>
|
||||
CreateEventKeepAliveToken();
|
||||
|
||||
// The info object owns us. It is possible to outlive it for a brief period
|
||||
// of time if there are pending waitUntil promises, in which case it
|
||||
// will be null and |SpawnWorkerIfNeeded| will always fail.
|
||||
ServiceWorkerInfo* MOZ_NON_OWNING_REF mInfo;
|
||||
|
||||
// The WorkerPrivate object can only be closed by this class or by the
|
||||
// RuntimeService class if gecko is shutting down. Closing the worker
|
||||
// multiple times is OK, since the second attempt will be a no-op.
|
||||
RefPtr<WorkerPrivate> mWorkerPrivate;
|
||||
|
||||
nsCOMPtr<nsITimer> mIdleWorkerTimer;
|
||||
|
||||
// We keep a token for |dom.serviceWorkers.idle_timeout| seconds to give the
|
||||
// worker a grace period after each event.
|
||||
RefPtr<KeepAliveToken> mIdleKeepAliveToken;
|
||||
|
||||
uint64_t mDebuggerCount;
|
||||
|
||||
uint64_t mTokenCount;
|
||||
|
||||
// Meant for keeping objects alive while handling requests from the worker
|
||||
// on the main thread. Access to this array is provided through
|
||||
// |StoreISupports| and |RemoveISupports|. Note that the array is also
|
||||
// cleared whenever the worker is terminated.
|
||||
nsTArray<nsCOMPtr<nsISupports>> mSupportsArray;
|
||||
|
||||
// Array of function event worker runnables that are pending due to
|
||||
// the worker activating. Main thread only.
|
||||
nsTArray<RefPtr<WorkerRunnable>> mPendingFunctionalEvents;
|
||||
|
||||
nsTArray<Runnable*> pendingWindows;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerprivate_h
|
||||
67
dom/workers/ServiceWorkerRegisterJob.cpp
Normal file
67
dom/workers/ServiceWorkerRegisterJob.cpp
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
/* -*- 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 "ServiceWorkerRegisterJob.h"
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
ServiceWorkerRegisterJob::ServiceWorkerRegisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: ServiceWorkerUpdateJob(Type::Register, aPrincipal, aScope, aScriptSpec,
|
||||
aLoadGroup)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegisterJob::AsyncExecute()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (Canceled() || !swm) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(mPrincipal, mScope);
|
||||
|
||||
if (registration) {
|
||||
// If we are resurrecting an uninstalling registration, then persist
|
||||
// it to disk again. We preemptively removed it earlier during
|
||||
// unregister so that closing the window by shutting down the browser
|
||||
// results in the registration being gone on restart.
|
||||
if (registration->mPendingUninstall) {
|
||||
swm->StoreRegistration(mPrincipal, registration);
|
||||
}
|
||||
registration->mPendingUninstall = false;
|
||||
RefPtr<ServiceWorkerInfo> newest = registration->Newest();
|
||||
if (newest && mScriptSpec.Equals(newest->ScriptSpec())) {
|
||||
SetRegistration(registration);
|
||||
Finish(NS_OK);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
registration = swm->CreateNewRegistration(mScope, mPrincipal);
|
||||
}
|
||||
|
||||
SetRegistration(registration);
|
||||
Update();
|
||||
}
|
||||
|
||||
ServiceWorkerRegisterJob::~ServiceWorkerRegisterJob()
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
40
dom/workers/ServiceWorkerRegisterJob.h
Normal file
40
dom/workers/ServiceWorkerRegisterJob.h
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/* -*- 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_serviceworkerregisterjob_h
|
||||
#define mozilla_dom_workers_serviceworkerregisterjob_h
|
||||
|
||||
#include "ServiceWorkerUpdateJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
// The register job. This implements the steps in the spec Register algorithm,
|
||||
// but then uses ServiceWorkerUpdateJob to implement the Update and Install
|
||||
// spec algorithms.
|
||||
class ServiceWorkerRegisterJob final : public ServiceWorkerUpdateJob
|
||||
{
|
||||
public:
|
||||
ServiceWorkerRegisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
|
||||
private:
|
||||
// Implement the Register algorithm steps and then call the parent class
|
||||
// Update() to complete the job execution.
|
||||
virtual void
|
||||
AsyncExecute() override;
|
||||
|
||||
virtual ~ServiceWorkerRegisterJob();
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerregisterjob_h
|
||||
884
dom/workers/ServiceWorkerRegistrar.cpp
Normal file
884
dom/workers/ServiceWorkerRegistrar.cpp
Normal file
|
|
@ -0,0 +1,884 @@
|
|||
|
||||
/* -*- 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 "ServiceWorkerRegistrar.h"
|
||||
#include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
|
||||
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIInputStream.h"
|
||||
#include "nsILineInputStream.h"
|
||||
#include "nsIObserverService.h"
|
||||
#include "nsIOutputStream.h"
|
||||
#include "nsISafeOutputStream.h"
|
||||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "mozilla/ipc/BackgroundParent.h"
|
||||
#include "mozilla/ipc/PBackgroundChild.h"
|
||||
#include "mozilla/ModuleUtils.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "mozilla/StaticPtr.h"
|
||||
#include "nsAppDirectoryServiceDefs.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsDirectoryServiceUtils.h"
|
||||
#include "nsNetCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsThreadUtils.h"
|
||||
#include "nsXULAppAPI.h"
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
namespace {
|
||||
|
||||
static const char* gSupportedRegistrarVersions[] = {
|
||||
SERVICEWORKERREGISTRAR_VERSION,
|
||||
"3",
|
||||
"2"
|
||||
};
|
||||
|
||||
StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
|
||||
|
||||
} // namespace
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
|
||||
nsIObserver)
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::Initialize()
|
||||
{
|
||||
MOZ_ASSERT(!gServiceWorkerRegistrar);
|
||||
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
|
||||
ClearOnShutdown(&gServiceWorkerRegistrar);
|
||||
|
||||
nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
|
||||
if (obs) {
|
||||
DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
|
||||
"profile-after-change", false);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
|
||||
rv = obs->AddObserver(gServiceWorkerRegistrar, "profile-before-change",
|
||||
false);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv));
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<ServiceWorkerRegistrar>
|
||||
ServiceWorkerRegistrar::Get()
|
||||
{
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
MOZ_ASSERT(gServiceWorkerRegistrar);
|
||||
RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
|
||||
return service.forget();
|
||||
}
|
||||
|
||||
ServiceWorkerRegistrar::ServiceWorkerRegistrar()
|
||||
: mMonitor("ServiceWorkerRegistrar.mMonitor")
|
||||
, mDataLoaded(false)
|
||||
, mShuttingDown(false)
|
||||
, mShutdownCompleteFlag(nullptr)
|
||||
, mRunnableCounter(0)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
}
|
||||
|
||||
ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
|
||||
{
|
||||
MOZ_ASSERT(!mRunnableCounter);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::GetRegistrations(
|
||||
nsTArray<ServiceWorkerRegistrationData>& aValues)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aValues.IsEmpty());
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
// If we don't have the profile directory, profile is not started yet (and
|
||||
// probably we are in a utest).
|
||||
if (!mProfileDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We care just about the first execution because this can be blocked by
|
||||
// loading data from disk.
|
||||
static bool firstTime = true;
|
||||
TimeStamp startTime;
|
||||
|
||||
if (firstTime) {
|
||||
startTime = TimeStamp::NowLoRes();
|
||||
}
|
||||
|
||||
// Waiting for data loaded.
|
||||
mMonitor.AssertCurrentThreadOwns();
|
||||
while (!mDataLoaded) {
|
||||
mMonitor.Wait();
|
||||
}
|
||||
|
||||
aValues.AppendElements(mData);
|
||||
|
||||
if (firstTime) {
|
||||
firstTime = false;
|
||||
Telemetry::AccumulateTimeDelta(
|
||||
Telemetry::SERVICE_WORKER_REGISTRATION_LOADING,
|
||||
startTime);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
|
||||
const ServiceWorkerRegistrationData& aRight)
|
||||
{
|
||||
MOZ_ASSERT(aLeft.principal().type() ==
|
||||
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
|
||||
MOZ_ASSERT(aRight.principal().type() ==
|
||||
mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
|
||||
|
||||
const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
|
||||
const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
|
||||
|
||||
// Only compare the attributes, not the spec part of the principal.
|
||||
// The scope comparison above already covers the origin and codebase
|
||||
// principals include the full path in their spec which is not what
|
||||
// we want here.
|
||||
return aLeft.scope() == aRight.scope() &&
|
||||
leftPrincipal.attrs() == rightPrincipal.attrs();
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::RegisterServiceWorker(
|
||||
const ServiceWorkerRegistrationData& aData)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mShuttingDown) {
|
||||
NS_WARNING("Failed to register a serviceWorker during shutting down.");
|
||||
return;
|
||||
}
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(mDataLoaded);
|
||||
RegisterServiceWorkerInternal(aData);
|
||||
}
|
||||
|
||||
ScheduleSaveData();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::UnregisterServiceWorker(
|
||||
const PrincipalInfo& aPrincipalInfo,
|
||||
const nsACString& aScope)
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mShuttingDown) {
|
||||
NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool deleted = false;
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(mDataLoaded);
|
||||
|
||||
ServiceWorkerRegistrationData tmp;
|
||||
tmp.principal() = aPrincipalInfo;
|
||||
tmp.scope() = aScope;
|
||||
|
||||
for (uint32_t i = 0; i < mData.Length(); ++i) {
|
||||
if (Equivalent(tmp, mData[i])) {
|
||||
mData.RemoveElementAt(i);
|
||||
deleted = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
ScheduleSaveData();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::RemoveAll()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mShuttingDown) {
|
||||
NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
|
||||
return;
|
||||
}
|
||||
|
||||
bool deleted = false;
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(mDataLoaded);
|
||||
|
||||
deleted = !mData.IsEmpty();
|
||||
mData.Clear();
|
||||
}
|
||||
|
||||
if (deleted) {
|
||||
ScheduleSaveData();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::LoadData()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
MOZ_ASSERT(!mDataLoaded);
|
||||
|
||||
nsresult rv = ReadData();
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
DeleteData();
|
||||
// Also if the reading failed we have to notify what is waiting for data.
|
||||
}
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_ASSERT(!mDataLoaded);
|
||||
mDataLoaded = true;
|
||||
mMonitor.Notify();
|
||||
}
|
||||
|
||||
nsresult
|
||||
ServiceWorkerRegistrar::ReadData()
|
||||
{
|
||||
// We cannot assert about the correct thread because normally this method
|
||||
// runs on a IO thread, but in gTests we call it from the main-thread.
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
if (!mProfileDir) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool exists;
|
||||
rv = file->Exists(&exists);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!exists) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIInputStream> stream;
|
||||
rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
|
||||
MOZ_ASSERT(lineInputStream);
|
||||
|
||||
nsAutoCString version;
|
||||
bool hasMoreLines;
|
||||
rv = lineInputStream->ReadLine(version, &hasMoreLines);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!IsSupportedVersion(version)) {
|
||||
nsContentUtils::LogMessageToConsole(nsPrintfCString(
|
||||
"Unsupported service worker registrar version: %s", version.get()).get());
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsTArray<ServiceWorkerRegistrationData> tmpData;
|
||||
|
||||
bool overwrite = false;
|
||||
bool dedupe = false;
|
||||
while (hasMoreLines) {
|
||||
ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
|
||||
|
||||
#define GET_LINE(x) \
|
||||
rv = lineInputStream->ReadLine(x, &hasMoreLines); \
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) { \
|
||||
return rv; \
|
||||
} \
|
||||
if (NS_WARN_IF(!hasMoreLines)) { \
|
||||
return NS_ERROR_FAILURE; \
|
||||
}
|
||||
|
||||
nsAutoCString line;
|
||||
nsAutoCString unused;
|
||||
if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
|
||||
nsAutoCString suffix;
|
||||
GET_LINE(suffix);
|
||||
|
||||
PrincipalOriginAttributes attrs;
|
||||
if (!attrs.PopulateFromSuffix(suffix)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
nsAutoCString cacheName;
|
||||
GET_LINE(cacheName);
|
||||
CopyUTF8toUTF16(cacheName, entry->cacheName());
|
||||
} else if (version.EqualsLiteral("3")) {
|
||||
overwrite = true;
|
||||
dedupe = true;
|
||||
|
||||
nsAutoCString suffix;
|
||||
GET_LINE(suffix);
|
||||
|
||||
PrincipalOriginAttributes attrs;
|
||||
if (!attrs.PopulateFromSuffix(suffix)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// principal spec is no longer used; we use scope directly instead
|
||||
GET_LINE(unused);
|
||||
|
||||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
nsAutoCString cacheName;
|
||||
GET_LINE(cacheName);
|
||||
CopyUTF8toUTF16(cacheName, entry->cacheName());
|
||||
} else if (version.EqualsLiteral("2")) {
|
||||
overwrite = true;
|
||||
dedupe = true;
|
||||
|
||||
nsAutoCString suffix;
|
||||
GET_LINE(suffix);
|
||||
|
||||
PrincipalOriginAttributes attrs;
|
||||
if (!attrs.PopulateFromSuffix(suffix)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
// principal spec is no longer used; we use scope directly instead
|
||||
GET_LINE(unused);
|
||||
|
||||
GET_LINE(entry->scope());
|
||||
|
||||
entry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
|
||||
|
||||
// scriptSpec is no more used in latest version.
|
||||
GET_LINE(unused);
|
||||
|
||||
GET_LINE(entry->currentWorkerURL());
|
||||
|
||||
nsAutoCString cacheName;
|
||||
GET_LINE(cacheName);
|
||||
CopyUTF8toUTF16(cacheName, entry->cacheName());
|
||||
|
||||
// waitingCacheName is no more used in latest version.
|
||||
GET_LINE(unused);
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Should never get here!");
|
||||
}
|
||||
|
||||
#undef GET_LINE
|
||||
|
||||
rv = lineInputStream->ReadLine(line, &hasMoreLines);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
stream->Close();
|
||||
|
||||
// Copy data over to mData.
|
||||
for (uint32_t i = 0; i < tmpData.Length(); ++i) {
|
||||
bool match = false;
|
||||
if (dedupe) {
|
||||
MOZ_ASSERT(overwrite);
|
||||
// If this is an old profile, then we might need to deduplicate. In
|
||||
// theory this can be removed in the future (Bug 1248449)
|
||||
for (uint32_t j = 0; j < mData.Length(); ++j) {
|
||||
// Use same comparison as RegisterServiceWorker. Scope contains
|
||||
// basic origin information. Combine with any principal attributes.
|
||||
if (Equivalent(tmpData[i], mData[j])) {
|
||||
// Last match wins, just like legacy loading used to do in
|
||||
// the ServiceWorkerManager.
|
||||
mData[j] = tmpData[i];
|
||||
// Dupe found, so overwrite file with reduced list.
|
||||
match = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
// Otherwise assert no duplications in debug builds.
|
||||
for (uint32_t j = 0; j < mData.Length(); ++j) {
|
||||
MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
|
||||
}
|
||||
#endif
|
||||
}
|
||||
if (!match) {
|
||||
mData.AppendElement(tmpData[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// Overwrite previous version.
|
||||
// Cannot call SaveData directly because gtest uses main-thread.
|
||||
if (overwrite && NS_FAILED(WriteData())) {
|
||||
NS_WARNING("Failed to write data for the ServiceWorker Registations.");
|
||||
DeleteData();
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::DeleteData()
|
||||
{
|
||||
// We cannot assert about the correct thread because normally this method
|
||||
// runs on a IO thread, but in gTests we call it from the main-thread.
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
mData.Clear();
|
||||
|
||||
if (!mProfileDir) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
rv = file->Remove(false);
|
||||
if (rv == NS_ERROR_FILE_NOT_FOUND) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::RegisterServiceWorkerInternal(const ServiceWorkerRegistrationData& aData)
|
||||
{
|
||||
bool found = false;
|
||||
for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
|
||||
if (Equivalent(aData, mData[i])) {
|
||||
mData[i] = aData;
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
mData.AppendElement(aData);
|
||||
}
|
||||
}
|
||||
|
||||
class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable
|
||||
{
|
||||
public:
|
||||
ServiceWorkerRegistrarSaveDataRunnable()
|
||||
: mThread(do_GetCurrentThread())
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
|
||||
MOZ_ASSERT(service);
|
||||
|
||||
service->SaveData();
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved);
|
||||
nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsCOMPtr<nsIThread> mThread;
|
||||
};
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::ScheduleSaveData()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target =
|
||||
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(target, "Must have stream transport service");
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
new ServiceWorkerRegistrarSaveDataRunnable();
|
||||
nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
++mRunnableCounter;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::ShutdownCompleted()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MOZ_ASSERT(mShutdownCompleteFlag && !*mShutdownCompleteFlag);
|
||||
*mShutdownCompleteFlag = true;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::SaveData()
|
||||
{
|
||||
MOZ_ASSERT(!NS_IsMainThread());
|
||||
|
||||
nsresult rv = WriteData();
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to write data for the ServiceWorker Registations.");
|
||||
DeleteData();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::DataSaved()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(mRunnableCounter);
|
||||
|
||||
--mRunnableCounter;
|
||||
MaybeScheduleShutdownCompleted();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
|
||||
if (mRunnableCounter || !mShuttingDown) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Runnable> runnable =
|
||||
NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted);
|
||||
nsresult rv = NS_DispatchToMainThread(runnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrar::IsSupportedVersion(const nsACString& aVersion) const
|
||||
{
|
||||
uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
|
||||
for (uint32_t i = 0; i < numVersions; i++) {
|
||||
if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ServiceWorkerRegistrar::WriteData()
|
||||
{
|
||||
// We cannot assert about the correct thread because normally this method
|
||||
// runs on a IO thread, but in gTests we call it from the main-thread.
|
||||
|
||||
nsCOMPtr<nsIFile> file;
|
||||
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
if (!mProfileDir) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We need a lock to take a snapshot of the data.
|
||||
nsTArray<ServiceWorkerRegistrationData> data;
|
||||
{
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
data = mData;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIOutputStream> stream;
|
||||
rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsAutoCString buffer;
|
||||
buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
|
||||
buffer.Append('\n');
|
||||
|
||||
uint32_t count;
|
||||
rv = stream->Write(buffer.Data(), buffer.Length(), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (count != buffer.Length()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
|
||||
const mozilla::ipc::PrincipalInfo& info = data[i].principal();
|
||||
|
||||
MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
|
||||
|
||||
const mozilla::ipc::ContentPrincipalInfo& cInfo =
|
||||
info.get_ContentPrincipalInfo();
|
||||
|
||||
nsAutoCString suffix;
|
||||
cInfo.attrs().CreateSuffix(suffix);
|
||||
|
||||
buffer.Truncate();
|
||||
buffer.Append(suffix.get());
|
||||
buffer.Append('\n');
|
||||
|
||||
buffer.Append(data[i].scope());
|
||||
buffer.Append('\n');
|
||||
|
||||
buffer.Append(data[i].currentWorkerURL());
|
||||
buffer.Append('\n');
|
||||
|
||||
buffer.Append(NS_ConvertUTF16toUTF8(data[i].cacheName()));
|
||||
buffer.Append('\n');
|
||||
|
||||
buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
|
||||
buffer.Append('\n');
|
||||
|
||||
rv = stream->Write(buffer.Data(), buffer.Length(), &count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (count != buffer.Length()) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
|
||||
MOZ_ASSERT(safeStream);
|
||||
|
||||
rv = safeStream->Finish();
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::ProfileStarted()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
|
||||
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mProfileDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIEventTarget> target =
|
||||
do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
|
||||
MOZ_ASSERT(target, "Must have stream transport service");
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData);
|
||||
rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to dispatch the LoadDataRunnable.");
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::ProfileStopped()
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
MonitorAutoLock lock(mMonitor);
|
||||
|
||||
if (!mProfileDir) {
|
||||
nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
|
||||
getter_AddRefs(mProfileDir));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// We must set the pointer before potentially entering the fast-path shutdown
|
||||
// below.
|
||||
bool completed = false;
|
||||
mShutdownCompleteFlag = &completed;
|
||||
|
||||
PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
|
||||
if (!child) {
|
||||
// Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
|
||||
// issued by the ServiceWorkerManagerService, so the appropriate place to
|
||||
// trigger shutdown is on that thread.
|
||||
//
|
||||
// However, it's quite possible that the PBackground thread was not brought
|
||||
// into existence for xpcshell tests. We don't cause it to be created
|
||||
// ourselves for any reason, for example.
|
||||
//
|
||||
// In this scenario, we know that:
|
||||
// - We will receive exactly one call to ourself from BlockShutdown() and
|
||||
// BlockShutdown() will be called (at most) once.
|
||||
// - The only way our Shutdown() method gets called is via
|
||||
// BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
|
||||
// invoked, which only happens if we get to that send below here that we
|
||||
// can't get to.
|
||||
// - All Shutdown() does is set mShuttingDown=true (essential for
|
||||
// invariants) and invoke MaybeScheduleShutdownCompleted().
|
||||
// - Since there is no PBackground thread, mRunnableCounter must be 0
|
||||
// because only ScheduleSaveData() increments it and it only runs on the
|
||||
// background thread, so it cannot have run. And so we would expect
|
||||
// MaybeScheduleShutdownCompleted() to schedule an invocation of
|
||||
// ShutdownCompleted on the main thread.
|
||||
//
|
||||
// So it's appropriate for us to set mShuttingDown=true (as Shutdown would
|
||||
// do) and directly invoke ShutdownCompleted() (as Shutdown would indirectly
|
||||
// do via MaybeScheduleShutdownCompleted).
|
||||
mShuttingDown = true;
|
||||
ShutdownCompleted();
|
||||
return;
|
||||
}
|
||||
|
||||
child->SendShutdownServiceWorkerRegistrar();
|
||||
|
||||
nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
|
||||
while (true) {
|
||||
MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread));
|
||||
if (completed) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrar::Shutdown()
|
||||
{
|
||||
AssertIsOnBackgroundThread();
|
||||
MOZ_ASSERT(!mShuttingDown);
|
||||
|
||||
mShuttingDown = true;
|
||||
MaybeScheduleShutdownCompleted();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (!strcmp(aTopic, "profile-after-change")) {
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
services::GetObserverService();
|
||||
observerService->RemoveObserver(this, "profile-after-change");
|
||||
|
||||
// The profile is fully loaded, now we can proceed with the loading of data
|
||||
// from disk.
|
||||
ProfileStarted();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (!strcmp(aTopic, "profile-before-change")) {
|
||||
// Hygiene; gServiceWorkerRegistrar should still be keeping a reference
|
||||
// alive well past this phase of shutdown, but it's bad form to drop your
|
||||
// last potentially owning reference and then make a call that requires you
|
||||
// to still be alive, especially when you spin a nested event loop.
|
||||
RefPtr<ServiceWorkerRegistrar> kungFuDeathGrip(this);
|
||||
|
||||
nsCOMPtr<nsIObserverService> observerService =
|
||||
services::GetObserverService();
|
||||
observerService->RemoveObserver(this, "profile-before-change");
|
||||
|
||||
// Shutting down, let's sync the data.
|
||||
ProfileStopped();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
101
dom/workers/ServiceWorkerRegistrar.h
Normal file
101
dom/workers/ServiceWorkerRegistrar.h
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
/* -*- 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_ServiceWorkerRegistrar_h
|
||||
#define mozilla_dom_workers_ServiceWorkerRegistrar_h
|
||||
|
||||
#include "mozilla/Monitor.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsClassHashtable.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#define SERVICEWORKERREGISTRAR_FILE "serviceworker.txt"
|
||||
#define SERVICEWORKERREGISTRAR_VERSION "4"
|
||||
#define SERVICEWORKERREGISTRAR_TERMINATOR "#"
|
||||
#define SERVICEWORKERREGISTRAR_TRUE "true"
|
||||
#define SERVICEWORKERREGISTRAR_FALSE "false"
|
||||
|
||||
class nsIFile;
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
|
||||
class ServiceWorkerRegistrationData;
|
||||
|
||||
class ServiceWorkerRegistrar : public nsIObserver
|
||||
{
|
||||
friend class ServiceWorkerRegistrarSaveDataRunnable;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
static void Initialize();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
void DataSaved();
|
||||
|
||||
static already_AddRefed<ServiceWorkerRegistrar> Get();
|
||||
|
||||
void GetRegistrations(nsTArray<ServiceWorkerRegistrationData>& aValues);
|
||||
|
||||
void RegisterServiceWorker(const ServiceWorkerRegistrationData& aData);
|
||||
void UnregisterServiceWorker(const mozilla::ipc::PrincipalInfo& aPrincipalInfo,
|
||||
const nsACString& aScope);
|
||||
void RemoveAll();
|
||||
|
||||
protected:
|
||||
// These methods are protected because we test this class using gTest
|
||||
// subclassing it.
|
||||
void LoadData();
|
||||
void SaveData();
|
||||
|
||||
nsresult ReadData();
|
||||
nsresult WriteData();
|
||||
void DeleteData();
|
||||
|
||||
void RegisterServiceWorkerInternal(const ServiceWorkerRegistrationData& aData);
|
||||
|
||||
ServiceWorkerRegistrar();
|
||||
virtual ~ServiceWorkerRegistrar();
|
||||
|
||||
private:
|
||||
void ProfileStarted();
|
||||
void ProfileStopped();
|
||||
|
||||
void ScheduleSaveData();
|
||||
void ShutdownCompleted();
|
||||
void MaybeScheduleShutdownCompleted();
|
||||
|
||||
bool IsSupportedVersion(const nsACString& aVersion) const;
|
||||
|
||||
mozilla::Monitor mMonitor;
|
||||
|
||||
protected:
|
||||
// protected by mMonitor.
|
||||
nsCOMPtr<nsIFile> mProfileDir;
|
||||
nsTArray<ServiceWorkerRegistrationData> mData;
|
||||
bool mDataLoaded;
|
||||
|
||||
// PBackground thread only
|
||||
bool mShuttingDown;
|
||||
bool* mShutdownCompleteFlag;
|
||||
uint32_t mRunnableCounter;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_ServiceWorkerRegistrar_h
|
||||
23
dom/workers/ServiceWorkerRegistrarTypes.ipdlh
Normal file
23
dom/workers/ServiceWorkerRegistrarTypes.ipdlh
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
/* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
|
||||
/* vim: set sw=4 ts=8 et tw=80 ft=cpp : */
|
||||
/* 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 PBackgroundSharedTypes;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct ServiceWorkerRegistrationData
|
||||
{
|
||||
nsCString scope;
|
||||
nsCString currentWorkerURL;
|
||||
|
||||
nsString cacheName;
|
||||
|
||||
PrincipalInfo principal;
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
1327
dom/workers/ServiceWorkerRegistration.cpp
Normal file
1327
dom/workers/ServiceWorkerRegistration.cpp
Normal file
File diff suppressed because it is too large
Load diff
124
dom/workers/ServiceWorkerRegistration.h
Normal file
124
dom/workers/ServiceWorkerRegistration.h
Normal file
|
|
@ -0,0 +1,124 @@
|
|||
/* -*- 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_ServiceWorkerRegistration_h
|
||||
#define mozilla_dom_ServiceWorkerRegistration_h
|
||||
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/ServiceWorkerBinding.h"
|
||||
#include "mozilla/dom/ServiceWorkerCommon.h"
|
||||
#include "mozilla/dom/workers/bindings/WorkerHolder.h"
|
||||
#include "nsContentUtils.h" // Required for nsContentUtils::PushEnabled
|
||||
|
||||
// Support for Notification API extension.
|
||||
#include "mozilla/dom/NotificationBinding.h"
|
||||
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
class PushManager;
|
||||
class WorkerListener;
|
||||
|
||||
namespace workers {
|
||||
class ServiceWorker;
|
||||
class WorkerPrivate;
|
||||
} // namespace workers
|
||||
|
||||
// Used by ServiceWorkerManager to notify ServiceWorkerRegistrations of
|
||||
// updatefound event and invalidating ServiceWorker instances.
|
||||
class ServiceWorkerRegistrationListener
|
||||
{
|
||||
public:
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
|
||||
|
||||
virtual void
|
||||
UpdateFound() = 0;
|
||||
|
||||
virtual void
|
||||
InvalidateWorkers(WhichServiceWorker aWhichOnes) = 0;
|
||||
|
||||
virtual void
|
||||
RegistrationRemoved() = 0;
|
||||
|
||||
virtual void
|
||||
GetScope(nsAString& aScope) const = 0;
|
||||
};
|
||||
|
||||
class ServiceWorkerRegistration : public DOMEventTargetHelper
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
IMPL_EVENT_HANDLER(updatefound)
|
||||
|
||||
static bool
|
||||
Visible(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
static bool
|
||||
NotificationAPIVisible(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
|
||||
static already_AddRefed<ServiceWorkerRegistration>
|
||||
CreateForMainThread(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aScope);
|
||||
|
||||
static already_AddRefed<ServiceWorkerRegistration>
|
||||
CreateForWorker(workers::WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aScope);
|
||||
|
||||
JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual already_AddRefed<workers::ServiceWorker>
|
||||
GetInstalling() = 0;
|
||||
|
||||
virtual already_AddRefed<workers::ServiceWorker>
|
||||
GetWaiting() = 0;
|
||||
|
||||
virtual already_AddRefed<workers::ServiceWorker>
|
||||
GetActive() = 0;
|
||||
|
||||
virtual void
|
||||
GetScope(nsAString& aScope) const = 0;
|
||||
|
||||
virtual already_AddRefed<Promise>
|
||||
Update(ErrorResult& aRv) = 0;
|
||||
|
||||
virtual already_AddRefed<Promise>
|
||||
Unregister(ErrorResult& aRv) = 0;
|
||||
|
||||
virtual already_AddRefed<PushManager>
|
||||
GetPushManager(JSContext* aCx, ErrorResult& aRv) = 0;
|
||||
|
||||
virtual already_AddRefed<Promise>
|
||||
ShowNotification(JSContext* aCx,
|
||||
const nsAString& aTitle,
|
||||
const NotificationOptions& aOptions,
|
||||
ErrorResult& aRv) = 0;
|
||||
|
||||
virtual already_AddRefed<Promise>
|
||||
GetNotifications(const GetNotificationOptions& aOptions,
|
||||
ErrorResult& aRv) = 0;
|
||||
|
||||
protected:
|
||||
ServiceWorkerRegistration(nsPIDOMWindowInner* aWindow,
|
||||
const nsAString& aScope);
|
||||
|
||||
virtual ~ServiceWorkerRegistration()
|
||||
{ }
|
||||
|
||||
const nsString mScope;
|
||||
};
|
||||
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif /* mozilla_dom_ServiceWorkerRegistration_h */
|
||||
546
dom/workers/ServiceWorkerRegistrationInfo.cpp
Normal file
546
dom/workers/ServiceWorkerRegistrationInfo.cpp
Normal file
|
|
@ -0,0 +1,546 @@
|
|||
/* -*- 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 "ServiceWorkerRegistrationInfo.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
class ContinueActivateRunnable final : public LifeCycleEventCallback
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
bool mSuccess;
|
||||
|
||||
public:
|
||||
explicit ContinueActivateRunnable(const nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo>& aRegistration)
|
||||
: mRegistration(aRegistration)
|
||||
, mSuccess(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
SetResult(bool aResult) override
|
||||
{
|
||||
mSuccess = aResult;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mRegistration->FinishActivate(mSuccess);
|
||||
mRegistration = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::Clear()
|
||||
{
|
||||
if (mEvaluatingWorker) {
|
||||
mEvaluatingWorker = nullptr;
|
||||
}
|
||||
|
||||
if (mInstallingWorker) {
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mInstallingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
|
||||
mInstallingWorker = nullptr;
|
||||
// FIXME(nsm): Abort any inflight requests from installing worker.
|
||||
}
|
||||
|
||||
if (mWaitingWorker) {
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mWaitingWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
|
||||
mWaitingWorker = nullptr;
|
||||
}
|
||||
|
||||
if (mActiveWorker) {
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mActiveWorker->WorkerPrivate()->NoteDeadServiceWorkerInfo();
|
||||
mActiveWorker = nullptr;
|
||||
}
|
||||
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER |
|
||||
WhichServiceWorker::WAITING_WORKER |
|
||||
WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
ServiceWorkerRegistrationInfo::ServiceWorkerRegistrationInfo(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal)
|
||||
: mControlledDocumentsCounter(0)
|
||||
, mUpdateState(NoUpdate)
|
||||
, mLastUpdateCheckTime(0)
|
||||
, mScope(aScope)
|
||||
, mPrincipal(aPrincipal)
|
||||
, mPendingUninstall(false)
|
||||
{}
|
||||
|
||||
ServiceWorkerRegistrationInfo::~ServiceWorkerRegistrationInfo()
|
||||
{
|
||||
if (IsControllingDocuments()) {
|
||||
NS_WARNING("ServiceWorkerRegistrationInfo is still controlling documents. This can be a bug or a leak in ServiceWorker API or in any other API that takes the document alive.");
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationInfo, nsIServiceWorkerRegistrationInfo)
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetPrincipal(nsIPrincipal** aPrincipal)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
NS_ADDREF(*aPrincipal = mPrincipal);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetScope(nsAString& aScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
CopyUTF8toUTF16(mScope, aScope);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetScriptSpec(nsAString& aScriptSpec)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RefPtr<ServiceWorkerInfo> newest = Newest();
|
||||
if (newest) {
|
||||
CopyUTF8toUTF16(newest->ScriptSpec(), aScriptSpec);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetInstallingWorker(nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mInstallingWorker);
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetWaitingWorker(nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mWaitingWorker);
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetActiveWorker(nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsCOMPtr<nsIServiceWorkerInfo> info = do_QueryInterface(mActiveWorker);
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::GetWorkerByID(uint64_t aID, nsIServiceWorkerInfo **aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aResult);
|
||||
|
||||
RefPtr<ServiceWorkerInfo> info = GetServiceWorkerInfoById(aID);
|
||||
// It is ok to return null for a missing service worker info.
|
||||
info.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::AddListener(
|
||||
nsIServiceWorkerRegistrationInfoListener *aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!aListener || mListeners.Contains(aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mListeners.AppendElement(aListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
ServiceWorkerRegistrationInfo::RemoveListener(
|
||||
nsIServiceWorkerRegistrationInfoListener *aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!aListener || !mListeners.Contains(aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mListeners.RemoveElement(aListener);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
ServiceWorkerRegistrationInfo::GetServiceWorkerInfoById(uint64_t aId)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerInfo> serviceWorker;
|
||||
if (mEvaluatingWorker && mEvaluatingWorker->ID() == aId) {
|
||||
serviceWorker = mEvaluatingWorker;
|
||||
} else if (mInstallingWorker && mInstallingWorker->ID() == aId) {
|
||||
serviceWorker = mInstallingWorker;
|
||||
} else if (mWaitingWorker && mWaitingWorker->ID() == aId) {
|
||||
serviceWorker = mWaitingWorker;
|
||||
} else if (mActiveWorker && mActiveWorker->ID() == aId) {
|
||||
serviceWorker = mActiveWorker;
|
||||
}
|
||||
|
||||
return serviceWorker.forget();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TryToActivateAsync()
|
||||
{
|
||||
MOZ_ALWAYS_SUCCEEDS(
|
||||
NS_DispatchToMainThread(NewRunnableMethod(this,
|
||||
&ServiceWorkerRegistrationInfo::TryToActivate)));
|
||||
}
|
||||
|
||||
/*
|
||||
* TryToActivate should not be called directly, use TryToActivateAsync instead.
|
||||
*/
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TryToActivate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
bool controlling = IsControllingDocuments();
|
||||
bool skipWaiting = mWaitingWorker && mWaitingWorker->SkipWaitingFlag();
|
||||
bool idle = IsIdle();
|
||||
if (idle && (!controlling || skipWaiting)) {
|
||||
Activate();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::Activate()
|
||||
{
|
||||
if (!mWaitingWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown began during async activation step
|
||||
return;
|
||||
}
|
||||
|
||||
TransitionWaitingToActive();
|
||||
|
||||
// FIXME(nsm): Unlink appcache if there is one.
|
||||
|
||||
swm->CheckPendingReadyPromises();
|
||||
|
||||
// "Queue a task to fire a simple event named controllerchange..."
|
||||
nsCOMPtr<nsIRunnable> controllerChangeRunnable =
|
||||
NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
|
||||
swm, &ServiceWorkerManager::FireControllerChange, this);
|
||||
NS_DispatchToMainThread(controllerChangeRunnable);
|
||||
|
||||
nsCOMPtr<nsIRunnable> failRunnable =
|
||||
NewRunnableMethod<bool>(this,
|
||||
&ServiceWorkerRegistrationInfo::FinishActivate,
|
||||
false /* success */);
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerRegistrationInfo> handle(
|
||||
new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(this));
|
||||
RefPtr<LifeCycleEventCallback> callback = new ContinueActivateRunnable(handle);
|
||||
|
||||
ServiceWorkerPrivate* workerPrivate = mActiveWorker->WorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("activate"),
|
||||
callback, failRunnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(failRunnable));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::FinishActivate(bool aSuccess)
|
||||
{
|
||||
if (mPendingUninstall || !mActiveWorker ||
|
||||
mActiveWorker->State() != ServiceWorkerState::Activating) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Activation never fails, so aSuccess is ignored.
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Activated);
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown started during async activation completion step
|
||||
return;
|
||||
}
|
||||
swm->StoreRegistration(mPrincipal, this);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::RefreshLastUpdateCheckTime()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mLastUpdateCheckTime = PR_IntervalNow() / PR_MSEC_PER_SEC;
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrationInfo::IsLastUpdateCheckTimeOverOneDay() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// For testing.
|
||||
if (Preferences::GetBool("dom.serviceWorkers.testUpdateOverOneDay")) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint64_t kSecondsPerDay = 86400;
|
||||
const uint64_t now = PR_IntervalNow() / PR_MSEC_PER_SEC;
|
||||
|
||||
if ((now - mLastUpdateCheckTime) > kSecondsPerDay) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::NotifyListenersOnChange(WhichServiceWorker aChangedWorkers)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aChangedWorkers & (WhichServiceWorker::INSTALLING_WORKER |
|
||||
WhichServiceWorker::WAITING_WORKER |
|
||||
WhichServiceWorker::ACTIVE_WORKER));
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown started
|
||||
return;
|
||||
}
|
||||
|
||||
swm->InvalidateServiceWorkerRegistrationWorker(this, aChangedWorkers);
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> listeners(mListeners);
|
||||
for (size_t index = 0; index < listeners.Length(); ++index) {
|
||||
listeners[index]->OnChange();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::MaybeScheduleTimeCheckAndUpdate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// shutting down, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
if (mUpdateState == NoUpdate) {
|
||||
mUpdateState = NeedTimeCheckAndUpdate;
|
||||
}
|
||||
|
||||
swm->ScheduleUpdateTimer(mPrincipal, mScope);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::MaybeScheduleUpdate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// shutting down, do nothing
|
||||
return;
|
||||
}
|
||||
|
||||
mUpdateState = NeedUpdate;
|
||||
|
||||
swm->ScheduleUpdateTimer(mPrincipal, mScope);
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrationInfo::CheckAndClearIfUpdateNeeded()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
bool result = mUpdateState == NeedUpdate ||
|
||||
(mUpdateState == NeedTimeCheckAndUpdate &&
|
||||
IsLastUpdateCheckTimeOverOneDay());
|
||||
|
||||
mUpdateState = NoUpdate;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetEvaluating() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mEvaluatingWorker;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetInstalling() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mInstallingWorker;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetWaiting() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mWaitingWorker;
|
||||
}
|
||||
|
||||
ServiceWorkerInfo*
|
||||
ServiceWorkerRegistrationInfo::GetActive() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mActiveWorker;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::SetEvaluating(ServiceWorkerInfo* aServiceWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aServiceWorker);
|
||||
MOZ_ASSERT(!mEvaluatingWorker);
|
||||
MOZ_ASSERT(!mInstallingWorker);
|
||||
MOZ_ASSERT(mWaitingWorker != aServiceWorker);
|
||||
MOZ_ASSERT(mActiveWorker != aServiceWorker);
|
||||
|
||||
mEvaluatingWorker = aServiceWorker;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::ClearEvaluating()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!mEvaluatingWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
mEvaluatingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mEvaluatingWorker = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::ClearInstalling()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!mInstallingWorker) {
|
||||
return;
|
||||
}
|
||||
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
mInstallingWorker = nullptr;
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TransitionEvaluatingToInstalling()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mEvaluatingWorker);
|
||||
MOZ_ASSERT(!mInstallingWorker);
|
||||
|
||||
mInstallingWorker = mEvaluatingWorker.forget();
|
||||
mInstallingWorker->UpdateState(ServiceWorkerState::Installing);
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TransitionInstallingToWaiting()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mInstallingWorker);
|
||||
|
||||
if (mWaitingWorker) {
|
||||
MOZ_ASSERT(mInstallingWorker->CacheName() != mWaitingWorker->CacheName());
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
mWaitingWorker = mInstallingWorker.forget();
|
||||
mWaitingWorker->UpdateState(ServiceWorkerState::Installed);
|
||||
NotifyListenersOnChange(WhichServiceWorker::INSTALLING_WORKER |
|
||||
WhichServiceWorker::WAITING_WORKER);
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
// browser shutdown began
|
||||
return;
|
||||
}
|
||||
swm->StoreRegistration(mPrincipal, this);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::SetActive(ServiceWorkerInfo* aServiceWorker)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aServiceWorker);
|
||||
|
||||
// TODO: Assert installing, waiting, and active are nullptr once the SWM
|
||||
// moves to the parent process. After that happens this code will
|
||||
// only run for browser initialization and not for cross-process
|
||||
// overrides.
|
||||
MOZ_ASSERT(mInstallingWorker != aServiceWorker);
|
||||
MOZ_ASSERT(mWaitingWorker != aServiceWorker);
|
||||
MOZ_ASSERT(mActiveWorker != aServiceWorker);
|
||||
|
||||
if (mActiveWorker) {
|
||||
MOZ_ASSERT(aServiceWorker->CacheName() != mActiveWorker->CacheName());
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
// The active worker is being overriden due to initial load or
|
||||
// another process activating a worker. Move straight to the
|
||||
// Activated state.
|
||||
mActiveWorker = aServiceWorker;
|
||||
mActiveWorker->SetActivateStateUncheckedWithoutEvent(ServiceWorkerState::Activated);
|
||||
NotifyListenersOnChange(WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerRegistrationInfo::TransitionWaitingToActive()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mWaitingWorker);
|
||||
|
||||
if (mActiveWorker) {
|
||||
MOZ_ASSERT(mWaitingWorker->CacheName() != mActiveWorker->CacheName());
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Redundant);
|
||||
}
|
||||
|
||||
// We are transitioning from waiting to active normally, so go to
|
||||
// the activating state.
|
||||
mActiveWorker = mWaitingWorker.forget();
|
||||
mActiveWorker->UpdateState(ServiceWorkerState::Activating);
|
||||
NotifyListenersOnChange(WhichServiceWorker::WAITING_WORKER |
|
||||
WhichServiceWorker::ACTIVE_WORKER);
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerRegistrationInfo::IsIdle() const
|
||||
{
|
||||
return !mActiveWorker || mActiveWorker->WorkerPrivate()->IsIdle();
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
184
dom/workers/ServiceWorkerRegistrationInfo.h
Normal file
184
dom/workers/ServiceWorkerRegistrationInfo.h
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* -*- 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_serviceworkerregistrationinfo_h
|
||||
#define mozilla_dom_workers_serviceworkerregistrationinfo_h
|
||||
|
||||
#include "mozilla/dom/workers/ServiceWorkerInfo.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerRegistrationInfo final
|
||||
: public nsIServiceWorkerRegistrationInfo
|
||||
{
|
||||
uint32_t mControlledDocumentsCounter;
|
||||
|
||||
enum
|
||||
{
|
||||
NoUpdate,
|
||||
NeedTimeCheckAndUpdate,
|
||||
NeedUpdate
|
||||
} mUpdateState;
|
||||
|
||||
uint64_t mLastUpdateCheckTime;
|
||||
|
||||
RefPtr<ServiceWorkerInfo> mEvaluatingWorker;
|
||||
RefPtr<ServiceWorkerInfo> mActiveWorker;
|
||||
RefPtr<ServiceWorkerInfo> mWaitingWorker;
|
||||
RefPtr<ServiceWorkerInfo> mInstallingWorker;
|
||||
|
||||
virtual ~ServiceWorkerRegistrationInfo();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISERVICEWORKERREGISTRATIONINFO
|
||||
|
||||
const nsCString mScope;
|
||||
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
|
||||
nsTArray<nsCOMPtr<nsIServiceWorkerRegistrationInfoListener>> mListeners;
|
||||
|
||||
// When unregister() is called on a registration, it is not immediately
|
||||
// removed since documents may be controlled. It is marked as
|
||||
// pendingUninstall and when all controlling documents go away, removed.
|
||||
bool mPendingUninstall;
|
||||
|
||||
ServiceWorkerRegistrationInfo(const nsACString& aScope,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
Newest() const
|
||||
{
|
||||
RefPtr<ServiceWorkerInfo> newest;
|
||||
if (mInstallingWorker) {
|
||||
newest = mInstallingWorker;
|
||||
} else if (mWaitingWorker) {
|
||||
newest = mWaitingWorker;
|
||||
} else {
|
||||
newest = mActiveWorker;
|
||||
}
|
||||
|
||||
return newest.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerInfo>
|
||||
GetServiceWorkerInfoById(uint64_t aId);
|
||||
|
||||
void
|
||||
StartControllingADocument()
|
||||
{
|
||||
++mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
void
|
||||
StopControllingADocument()
|
||||
{
|
||||
MOZ_ASSERT(mControlledDocumentsCounter);
|
||||
--mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
bool
|
||||
IsControllingDocuments() const
|
||||
{
|
||||
return mActiveWorker && mControlledDocumentsCounter;
|
||||
}
|
||||
|
||||
void
|
||||
Clear();
|
||||
|
||||
void
|
||||
TryToActivateAsync();
|
||||
|
||||
void
|
||||
TryToActivate();
|
||||
|
||||
void
|
||||
Activate();
|
||||
|
||||
void
|
||||
FinishActivate(bool aSuccess);
|
||||
|
||||
void
|
||||
RefreshLastUpdateCheckTime();
|
||||
|
||||
bool
|
||||
IsLastUpdateCheckTimeOverOneDay() const;
|
||||
|
||||
void
|
||||
NotifyListenersOnChange(WhichServiceWorker aChangedWorkers);
|
||||
|
||||
void
|
||||
MaybeScheduleTimeCheckAndUpdate();
|
||||
|
||||
void
|
||||
MaybeScheduleUpdate();
|
||||
|
||||
bool
|
||||
CheckAndClearIfUpdateNeeded();
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetEvaluating() const;
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetInstalling() const;
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetWaiting() const;
|
||||
|
||||
ServiceWorkerInfo*
|
||||
GetActive() const;
|
||||
|
||||
// Set the given worker as the evaluating service worker. The worker
|
||||
// state is not changed.
|
||||
void
|
||||
SetEvaluating(ServiceWorkerInfo* aServiceWorker);
|
||||
|
||||
// Remove an existing evaluating worker, if present. The worker will
|
||||
// be transitioned to the Redundant state.
|
||||
void
|
||||
ClearEvaluating();
|
||||
|
||||
// Remove an existing installing worker, if present. The worker will
|
||||
// be transitioned to the Redundant state.
|
||||
void
|
||||
ClearInstalling();
|
||||
|
||||
// Transition the current evaluating worker to be the installing worker. The
|
||||
// worker's state is update to Installing.
|
||||
void
|
||||
TransitionEvaluatingToInstalling();
|
||||
|
||||
// Transition the current installing worker to be the waiting worker. The
|
||||
// worker's state is updated to Installed.
|
||||
void
|
||||
TransitionInstallingToWaiting();
|
||||
|
||||
// Override the current active worker. This is used during browser
|
||||
// initialization to load persisted workers. Its also used to propagate
|
||||
// active workers across child processes in e10s. This second use will
|
||||
// go away once the ServiceWorkerManager moves to the parent process.
|
||||
// The worker is transitioned to the Activated state.
|
||||
void
|
||||
SetActive(ServiceWorkerInfo* aServiceWorker);
|
||||
|
||||
// Transition the current waiting worker to be the new active worker. The
|
||||
// worker is updated to the Activating state.
|
||||
void
|
||||
TransitionWaitingToActive();
|
||||
|
||||
// Determine if the registration is actively performing work.
|
||||
bool
|
||||
IsIdle() const;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerregistrationinfo_h
|
||||
1060
dom/workers/ServiceWorkerScriptCache.cpp
Normal file
1060
dom/workers/ServiceWorkerScriptCache.cpp
Normal file
File diff suppressed because it is too large
Load diff
59
dom/workers/ServiceWorkerScriptCache.h
Normal file
59
dom/workers/ServiceWorkerScriptCache.h
Normal file
|
|
@ -0,0 +1,59 @@
|
|||
/* -*- 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_ServiceWorkerScriptCache_h
|
||||
#define mozilla_dom_workers_ServiceWorkerScriptCache_h
|
||||
|
||||
#include "nsString.h"
|
||||
|
||||
class nsILoadGroup;
|
||||
class nsIPrincipal;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerRegistrationInfo;
|
||||
|
||||
namespace serviceWorkerScriptCache {
|
||||
|
||||
nsresult
|
||||
PurgeCache(nsIPrincipal* aPrincipal, const nsAString& aCacheName);
|
||||
|
||||
nsresult
|
||||
GenerateCacheName(nsAString& aName);
|
||||
|
||||
class CompareCallback
|
||||
{
|
||||
public:
|
||||
/*
|
||||
* If there is an error, ignore aInCacheAndEqual and aNewCacheName.
|
||||
* On success, if the cached result and network result matched,
|
||||
* aInCacheAndEqual will be true and no new cache name is passed, otherwise
|
||||
* use the new cache name to load the ServiceWorker.
|
||||
*/
|
||||
virtual void
|
||||
ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope) = 0;
|
||||
|
||||
NS_IMETHOD_(MozExternalRefCountType) AddRef() = 0;
|
||||
NS_IMETHOD_(MozExternalRefCountType) Release() = 0;
|
||||
};
|
||||
|
||||
nsresult
|
||||
Compare(ServiceWorkerRegistrationInfo* aRegistration,
|
||||
nsIPrincipal* aPrincipal, const nsAString& aCacheName,
|
||||
const nsAString& aURL, CompareCallback* aCallback, nsILoadGroup* aLoadGroup);
|
||||
|
||||
} // namespace serviceWorkerScriptCache
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_ServiceWorkerScriptCache_h
|
||||
151
dom/workers/ServiceWorkerUnregisterJob.cpp
Normal file
151
dom/workers/ServiceWorkerUnregisterJob.cpp
Normal file
|
|
@ -0,0 +1,151 @@
|
|||
/* -*- 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 "ServiceWorkerUnregisterJob.h"
|
||||
|
||||
#include "nsIPushService.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerUnregisterJob::PushUnsubscribeCallback final :
|
||||
public nsIUnsubscribeResultCallback
|
||||
{
|
||||
public:
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
explicit PushUnsubscribeCallback(ServiceWorkerUnregisterJob* aJob)
|
||||
: mJob(aJob)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnUnsubscribe(nsresult aStatus, bool) override
|
||||
{
|
||||
// Warn if unsubscribing fails, but don't prevent the worker from
|
||||
// unregistering.
|
||||
Unused << NS_WARN_IF(NS_FAILED(aStatus));
|
||||
mJob->Unregister();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~PushUnsubscribeCallback()
|
||||
{
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerUnregisterJob> mJob;
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(ServiceWorkerUnregisterJob::PushUnsubscribeCallback,
|
||||
nsIUnsubscribeResultCallback)
|
||||
|
||||
ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
bool aSendToParent)
|
||||
: ServiceWorkerJob(Type::Unregister, aPrincipal, aScope, EmptyCString())
|
||||
, mResult(false)
|
||||
, mSendToParent(aSendToParent)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerUnregisterJob::GetResult() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mResult;
|
||||
}
|
||||
|
||||
ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUnregisterJob::AsyncExecute()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (Canceled()) {
|
||||
Finish(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Push API, section 5: "When a service worker registration is unregistered,
|
||||
// any associated push subscription must be deactivated." To ensure the
|
||||
// service worker registration isn't cleared as we're unregistering, we
|
||||
// unsubscribe first.
|
||||
nsCOMPtr<nsIPushService> pushService =
|
||||
do_GetService("@mozilla.org/push/Service;1");
|
||||
if (NS_WARN_IF(!pushService)) {
|
||||
Unregister();
|
||||
return;
|
||||
}
|
||||
nsCOMPtr<nsIUnsubscribeResultCallback> unsubscribeCallback =
|
||||
new PushUnsubscribeCallback(this);
|
||||
nsresult rv = pushService->Unsubscribe(NS_ConvertUTF8toUTF16(mScope),
|
||||
mPrincipal, unsubscribeCallback);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
Unregister();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUnregisterJob::Unregister()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (Canceled() || !swm) {
|
||||
Finish(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 1 of the Unregister algorithm requires checking that the
|
||||
// client origin matches the scope's origin. We perform this in
|
||||
// registration->update() method directly since we don't have that
|
||||
// client information available here.
|
||||
|
||||
// "Let registration be the result of running [[Get Registration]]
|
||||
// algorithm passing scope as the argument."
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(mPrincipal, mScope);
|
||||
if (!registration) {
|
||||
// "If registration is null, then, resolve promise with false."
|
||||
Finish(NS_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
// Note, we send the message to remove the registration from disk now even
|
||||
// though we may only set the mPendingUninstall flag below. This is
|
||||
// necessary to ensure the registration is removed if the controlled
|
||||
// clients are closed by shutting down the browser. If the registration
|
||||
// is resurrected by clearing mPendingUninstall then it should be saved
|
||||
// to disk again.
|
||||
if (mSendToParent && !registration->mPendingUninstall) {
|
||||
swm->MaybeSendUnregister(mPrincipal, mScope);
|
||||
}
|
||||
|
||||
// "Set registration's uninstalling flag."
|
||||
registration->mPendingUninstall = true;
|
||||
|
||||
// "Resolve promise with true"
|
||||
mResult = true;
|
||||
InvokeResultCallbacks(NS_OK);
|
||||
|
||||
// "If no service worker client is using registration..."
|
||||
if (!registration->IsControllingDocuments() && registration->IsIdle()) {
|
||||
// "Invoke [[Clear Registration]]..."
|
||||
swm->RemoveRegistration(registration);
|
||||
}
|
||||
|
||||
Finish(NS_OK);
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
45
dom/workers/ServiceWorkerUnregisterJob.h
Normal file
45
dom/workers/ServiceWorkerUnregisterJob.h
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- 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_serviceworkerunregisterjob_h
|
||||
#define mozilla_dom_workers_serviceworkerunregisterjob_h
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerUnregisterJob final : public ServiceWorkerJob
|
||||
{
|
||||
public:
|
||||
ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
bool aSendToParent);
|
||||
|
||||
bool
|
||||
GetResult() const;
|
||||
|
||||
private:
|
||||
class PushUnsubscribeCallback;
|
||||
|
||||
virtual ~ServiceWorkerUnregisterJob();
|
||||
|
||||
virtual void
|
||||
AsyncExecute() override;
|
||||
|
||||
void
|
||||
Unregister();
|
||||
|
||||
bool mResult;
|
||||
bool mSendToParent;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerunregisterjob_h
|
||||
552
dom/workers/ServiceWorkerUpdateJob.cpp
Normal file
552
dom/workers/ServiceWorkerUpdateJob.cpp
Normal file
|
|
@ -0,0 +1,552 @@
|
|||
/* -*- 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 "ServiceWorkerUpdateJob.h"
|
||||
|
||||
#include "nsIScriptError.h"
|
||||
#include "nsIURL.h"
|
||||
#include "ServiceWorkerScriptCache.h"
|
||||
#include "Workers.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
namespace {
|
||||
|
||||
/**
|
||||
* The spec mandates slightly different behaviors for computing the scope
|
||||
* prefix string in case a Service-Worker-Allowed header is specified versus
|
||||
* when it's not available.
|
||||
*
|
||||
* With the header:
|
||||
* "Set maxScopeString to "/" concatenated with the strings in maxScope's
|
||||
* path (including empty strings), separated from each other by "/"."
|
||||
* Without the header:
|
||||
* "Set maxScopeString to "/" concatenated with the strings, except the last
|
||||
* string that denotes the script's file name, in registration's registering
|
||||
* script url's path (including empty strings), separated from each other by
|
||||
* "/"."
|
||||
*
|
||||
* In simpler terms, if the header is not present, we should only use the
|
||||
* "directory" part of the pathname, and otherwise the entire pathname should be
|
||||
* used. ScopeStringPrefixMode allows the caller to specify the desired
|
||||
* behavior.
|
||||
*/
|
||||
enum ScopeStringPrefixMode {
|
||||
eUseDirectory,
|
||||
eUsePath
|
||||
};
|
||||
|
||||
nsresult
|
||||
GetRequiredScopeStringPrefix(nsIURI* aScriptURI, nsACString& aPrefix,
|
||||
ScopeStringPrefixMode aPrefixMode)
|
||||
{
|
||||
nsresult rv = aScriptURI->GetPrePath(aPrefix);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
if (aPrefixMode == eUseDirectory) {
|
||||
nsCOMPtr<nsIURL> scriptURL(do_QueryInterface(aScriptURI));
|
||||
if (NS_WARN_IF(!scriptURL)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsAutoCString dir;
|
||||
rv = scriptURL->GetDirectory(dir);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aPrefix.Append(dir);
|
||||
} else if (aPrefixMode == eUsePath) {
|
||||
nsAutoCString path;
|
||||
rv = aScriptURI->GetPath(path);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
aPrefix.Append(path);
|
||||
} else {
|
||||
MOZ_ASSERT_UNREACHABLE("Invalid value for aPrefixMode");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
class ServiceWorkerUpdateJob::CompareCallback final : public serviceWorkerScriptCache::CompareCallback
|
||||
{
|
||||
RefPtr<ServiceWorkerUpdateJob> mJob;
|
||||
|
||||
~CompareCallback()
|
||||
{
|
||||
}
|
||||
|
||||
public:
|
||||
explicit CompareCallback(ServiceWorkerUpdateJob* aJob)
|
||||
: mJob(aJob)
|
||||
{
|
||||
MOZ_ASSERT(mJob);
|
||||
}
|
||||
|
||||
virtual void
|
||||
ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope) override
|
||||
{
|
||||
mJob->ComparisonResult(aStatus, aInCacheAndEqual, aNewCacheName, aMaxScope);
|
||||
}
|
||||
|
||||
NS_INLINE_DECL_REFCOUNTING(ServiceWorkerUpdateJob::CompareCallback, override)
|
||||
};
|
||||
|
||||
class ServiceWorkerUpdateJob::ContinueUpdateRunnable final : public LifeCycleEventCallback
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> mJob;
|
||||
bool mSuccess;
|
||||
|
||||
public:
|
||||
explicit ContinueUpdateRunnable(const nsMainThreadPtrHandle<ServiceWorkerUpdateJob>& aJob)
|
||||
: mJob(aJob)
|
||||
, mSuccess(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
SetResult(bool aResult) override
|
||||
{
|
||||
mSuccess = aResult;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mJob->ContinueUpdateAfterScriptEval(mSuccess);
|
||||
mJob = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class ServiceWorkerUpdateJob::ContinueInstallRunnable final : public LifeCycleEventCallback
|
||||
{
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> mJob;
|
||||
bool mSuccess;
|
||||
|
||||
public:
|
||||
explicit ContinueInstallRunnable(const nsMainThreadPtrHandle<ServiceWorkerUpdateJob>& aJob)
|
||||
: mJob(aJob)
|
||||
, mSuccess(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
void
|
||||
SetResult(bool aResult) override
|
||||
{
|
||||
mSuccess = aResult;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
mJob->ContinueAfterInstallEvent(mSuccess);
|
||||
mJob = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
ServiceWorkerUpdateJob::ServiceWorkerUpdateJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: ServiceWorkerJob(Type::Update, aPrincipal, aScope, aScriptSpec)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
{
|
||||
}
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistrationInfo>
|
||||
ServiceWorkerUpdateJob::GetRegistration() const
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RefPtr<ServiceWorkerRegistrationInfo> ref = mRegistration;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
ServiceWorkerUpdateJob::ServiceWorkerUpdateJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup)
|
||||
: ServiceWorkerJob(aType, aPrincipal, aScope, aScriptSpec)
|
||||
, mLoadGroup(aLoadGroup)
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerUpdateJob::~ServiceWorkerUpdateJob()
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::FailUpdateJob(ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aRv.Failed());
|
||||
|
||||
// Cleanup after a failed installation. This essentially implements
|
||||
// step 12 of the Install algorithm.
|
||||
//
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm
|
||||
//
|
||||
// The spec currently only runs this after an install event fails,
|
||||
// but we must handle many more internal errors. So we check for
|
||||
// cleanup on every non-successful exit.
|
||||
if (mRegistration) {
|
||||
mRegistration->ClearEvaluating();
|
||||
mRegistration->ClearInstalling();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->MaybeRemoveRegistration(mRegistration);
|
||||
}
|
||||
}
|
||||
|
||||
mRegistration = nullptr;
|
||||
|
||||
Finish(aRv);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::FailUpdateJob(nsresult aRv)
|
||||
{
|
||||
ErrorResult rv(aRv);
|
||||
FailUpdateJob(rv);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::AsyncExecute()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(GetType() == Type::Update);
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (Canceled() || !swm) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Begin step 1 of the Update algorithm.
|
||||
//
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#update-algorithm
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(mPrincipal, mScope);
|
||||
|
||||
if (!registration || registration->mPendingUninstall) {
|
||||
ErrorResult rv;
|
||||
rv.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(NS_ConvertUTF8toUTF16(mScope),
|
||||
NS_LITERAL_STRING("uninstalled"));
|
||||
FailUpdateJob(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
// If a Register job with a new script executed ahead of us in the job queue,
|
||||
// then our update for the old script no longer makes sense. Simply abort
|
||||
// in this case.
|
||||
RefPtr<ServiceWorkerInfo> newest = registration->Newest();
|
||||
if (newest && !mScriptSpec.Equals(newest->ScriptSpec())) {
|
||||
ErrorResult rv;
|
||||
rv.ThrowTypeError<MSG_SW_UPDATE_BAD_REGISTRATION>(NS_ConvertUTF8toUTF16(mScope),
|
||||
NS_LITERAL_STRING("changed"));
|
||||
FailUpdateJob(rv);
|
||||
return;
|
||||
}
|
||||
|
||||
SetRegistration(registration);
|
||||
Update();
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::SetRegistration(ServiceWorkerRegistrationInfo* aRegistration)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MOZ_ASSERT(!mRegistration);
|
||||
MOZ_ASSERT(aRegistration);
|
||||
mRegistration = aRegistration;
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::Update()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!Canceled());
|
||||
|
||||
// SetRegistration() must be called before Update().
|
||||
MOZ_ASSERT(mRegistration);
|
||||
MOZ_ASSERT(!mRegistration->GetInstalling());
|
||||
|
||||
// Begin the script download and comparison steps starting at step 5
|
||||
// of the Update algorithm.
|
||||
|
||||
RefPtr<ServiceWorkerInfo> workerInfo = mRegistration->Newest();
|
||||
nsAutoString cacheName;
|
||||
|
||||
// If the script has not changed, we need to perform a byte-for-byte
|
||||
// comparison.
|
||||
if (workerInfo && workerInfo->ScriptSpec().Equals(mScriptSpec)) {
|
||||
cacheName = workerInfo->CacheName();
|
||||
}
|
||||
|
||||
RefPtr<CompareCallback> callback = new CompareCallback(this);
|
||||
|
||||
nsresult rv =
|
||||
serviceWorkerScriptCache::Compare(mRegistration, mPrincipal, cacheName,
|
||||
NS_ConvertUTF8toUTF16(mScriptSpec),
|
||||
callback, mLoadGroup);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(rv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (NS_WARN_IF(Canceled() || !swm)) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle failure of the download or comparison. This is part of Update
|
||||
// step 5 as "If the algorithm asynchronously completes with null, then:".
|
||||
if (NS_WARN_IF(NS_FAILED(aStatus))) {
|
||||
FailUpdateJob(aStatus);
|
||||
return;
|
||||
}
|
||||
|
||||
// The spec validates the response before performing the byte-for-byte check.
|
||||
// Here we perform the comparison in another module and then validate the
|
||||
// script URL and scope. Make sure to do this validation before accepting
|
||||
// an byte-for-byte match since the service-worker-allowed header might have
|
||||
// changed since the last time it was installed.
|
||||
|
||||
// This is step 2 the "validate response" section of Update algorithm step 5.
|
||||
// Step 1 is performed in the serviceWorkerScriptCache code.
|
||||
|
||||
nsCOMPtr<nsIURI> scriptURI;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(scriptURI), mScriptSpec);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> maxScopeURI;
|
||||
if (!aMaxScope.IsEmpty()) {
|
||||
rv = NS_NewURI(getter_AddRefs(maxScopeURI), aMaxScope,
|
||||
nullptr, scriptURI);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
nsAutoCString defaultAllowedPrefix;
|
||||
rv = GetRequiredScopeStringPrefix(scriptURI, defaultAllowedPrefix,
|
||||
eUseDirectory);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
nsAutoCString maxPrefix(defaultAllowedPrefix);
|
||||
if (maxScopeURI) {
|
||||
rv = GetRequiredScopeStringPrefix(maxScopeURI, maxPrefix, eUsePath);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!StringBeginsWith(mRegistration->mScope, maxPrefix)) {
|
||||
nsXPIDLString message;
|
||||
NS_ConvertUTF8toUTF16 reportScope(mRegistration->mScope);
|
||||
NS_ConvertUTF8toUTF16 reportMaxPrefix(maxPrefix);
|
||||
const char16_t* params[] = { reportScope.get(), reportMaxPrefix.get() };
|
||||
|
||||
rv = nsContentUtils::FormatLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
||||
"ServiceWorkerScopePathMismatch",
|
||||
params, message);
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to format localized string");
|
||||
swm->ReportToAllClients(mScope,
|
||||
message,
|
||||
EmptyString(),
|
||||
EmptyString(), 0, 0,
|
||||
nsIScriptError::errorFlag);
|
||||
FailUpdateJob(NS_ERROR_DOM_SECURITY_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// The response has been validated, so now we can consider if its a
|
||||
// byte-for-byte match. This is step 6 of the Update algorithm.
|
||||
if (aInCacheAndEqual) {
|
||||
Finish(NS_OK);
|
||||
return;
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SERVICE_WORKER_UPDATED, 1);
|
||||
|
||||
// Begin step 7 of the Update algorithm to evaluate the new script.
|
||||
|
||||
RefPtr<ServiceWorkerInfo> sw =
|
||||
new ServiceWorkerInfo(mRegistration->mPrincipal,
|
||||
mRegistration->mScope,
|
||||
mScriptSpec, aNewCacheName);
|
||||
|
||||
mRegistration->SetEvaluating(sw);
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
|
||||
new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(this));
|
||||
RefPtr<LifeCycleEventCallback> callback = new ContinueUpdateRunnable(handle);
|
||||
|
||||
ServiceWorkerPrivate* workerPrivate = sw->WorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
rv = workerPrivate->CheckScriptEvaluation(callback);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::ContinueUpdateAfterScriptEval(bool aScriptEvaluationResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (Canceled() || !swm) {
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
// Step 7.5 of the Update algorithm verifying that the script evaluated
|
||||
// successfully.
|
||||
|
||||
if (NS_WARN_IF(!aScriptEvaluationResult)) {
|
||||
ErrorResult error;
|
||||
|
||||
NS_ConvertUTF8toUTF16 scriptSpec(mScriptSpec);
|
||||
NS_ConvertUTF8toUTF16 scope(mRegistration->mScope);
|
||||
error.ThrowTypeError<MSG_SW_SCRIPT_THREW>(scriptSpec, scope);
|
||||
FailUpdateJob(error);
|
||||
return;
|
||||
}
|
||||
|
||||
Install(swm);
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::Install(ServiceWorkerManager* aSWM)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_DIAGNOSTIC_ASSERT(!Canceled());
|
||||
MOZ_DIAGNOSTIC_ASSERT(aSWM);
|
||||
|
||||
MOZ_ASSERT(!mRegistration->GetInstalling());
|
||||
|
||||
// Begin step 2 of the Install algorithm.
|
||||
//
|
||||
// https://slightlyoff.github.io/ServiceWorker/spec/service_worker/index.html#installation-algorithm
|
||||
|
||||
mRegistration->TransitionEvaluatingToInstalling();
|
||||
|
||||
// Step 6 of the Install algorithm resolving the job promise.
|
||||
InvokeResultCallbacks(NS_OK);
|
||||
|
||||
// The job promise cannot be rejected after this point, but the job can
|
||||
// still fail; e.g. if the install event handler throws, etc.
|
||||
|
||||
// fire the updatefound event
|
||||
nsCOMPtr<nsIRunnable> upr =
|
||||
NewRunnableMethod<RefPtr<ServiceWorkerRegistrationInfo>>(
|
||||
aSWM,
|
||||
&ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations,
|
||||
mRegistration);
|
||||
NS_DispatchToMainThread(upr);
|
||||
|
||||
// Call ContinueAfterInstallEvent(false) on main thread if the SW
|
||||
// script fails to load.
|
||||
nsCOMPtr<nsIRunnable> failRunnable = NewRunnableMethod<bool>
|
||||
(this, &ServiceWorkerUpdateJob::ContinueAfterInstallEvent, false);
|
||||
|
||||
nsMainThreadPtrHandle<ServiceWorkerUpdateJob> handle(
|
||||
new nsMainThreadPtrHolder<ServiceWorkerUpdateJob>(this));
|
||||
RefPtr<LifeCycleEventCallback> callback = new ContinueInstallRunnable(handle);
|
||||
|
||||
// Send the install event to the worker thread
|
||||
ServiceWorkerPrivate* workerPrivate =
|
||||
mRegistration->GetInstalling()->WorkerPrivate();
|
||||
nsresult rv = workerPrivate->SendLifeCycleEvent(NS_LITERAL_STRING("install"),
|
||||
callback, failRunnable);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
ContinueAfterInstallEvent(false /* aSuccess */);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
ServiceWorkerUpdateJob::ContinueAfterInstallEvent(bool aInstallEventSuccess)
|
||||
{
|
||||
if (Canceled()) {
|
||||
return FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
// If we haven't been canceled we should have a registration. There appears
|
||||
// to be a path where it gets cleared before we call into here. Assert
|
||||
// to try to catch this condition, but don't crash in release.
|
||||
MOZ_DIAGNOSTIC_ASSERT(mRegistration);
|
||||
if (!mRegistration) {
|
||||
return FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
// Continue executing the Install algorithm at step 12.
|
||||
|
||||
// "If installFailed is true"
|
||||
if (NS_WARN_IF(!aInstallEventSuccess)) {
|
||||
// The installing worker is cleaned up by FailUpdateJob().
|
||||
FailUpdateJob(NS_ERROR_DOM_ABORT_ERR);
|
||||
return;
|
||||
}
|
||||
|
||||
MOZ_DIAGNOSTIC_ASSERT(mRegistration->GetInstalling());
|
||||
mRegistration->TransitionInstallingToWaiting();
|
||||
|
||||
Finish(NS_OK);
|
||||
|
||||
// Step 20 calls for explicitly waiting for queued event tasks to fire. Instead,
|
||||
// we simply queue a runnable to execute Activate. This ensures the events are
|
||||
// flushed from the queue before proceeding.
|
||||
|
||||
// Step 22 of the Install algorithm. Activate is executed after the completion
|
||||
// of this job. The controlling client and skipWaiting checks are performed
|
||||
// in TryToActivate().
|
||||
mRegistration->TryToActivateAsync();
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
104
dom/workers/ServiceWorkerUpdateJob.h
Normal file
104
dom/workers/ServiceWorkerUpdateJob.h
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/* -*- 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_serviceworkerupdatejob_h
|
||||
#define mozilla_dom_workers_serviceworkerupdatejob_h
|
||||
|
||||
#include "ServiceWorkerJob.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerManager;
|
||||
|
||||
// A job class that performs the Update and Install algorithms from the
|
||||
// service worker spec. This class is designed to be inherited and customized
|
||||
// as a different job type. This is necessary because the register job
|
||||
// performs largely the same operations as the update job, but has a few
|
||||
// different starting steps.
|
||||
class ServiceWorkerUpdateJob : public ServiceWorkerJob
|
||||
{
|
||||
public:
|
||||
// Construct an update job to be used only for updates.
|
||||
ServiceWorkerUpdateJob(nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
|
||||
already_AddRefed<ServiceWorkerRegistrationInfo>
|
||||
GetRegistration() const;
|
||||
|
||||
protected:
|
||||
// Construct an update job that is overriden as another job type.
|
||||
ServiceWorkerUpdateJob(Type aType,
|
||||
nsIPrincipal* aPrincipal,
|
||||
const nsACString& aScope,
|
||||
const nsACString& aScriptSpec,
|
||||
nsILoadGroup* aLoadGroup);
|
||||
|
||||
virtual ~ServiceWorkerUpdateJob();
|
||||
|
||||
// FailUpdateJob() must be called if an update job needs Finish() with
|
||||
// an error.
|
||||
void
|
||||
FailUpdateJob(ErrorResult& aRv);
|
||||
|
||||
void
|
||||
FailUpdateJob(nsresult aRv);
|
||||
|
||||
// The entry point when the update job is being used directly. Job
|
||||
// types overriding this class should override this method to
|
||||
// customize behavior.
|
||||
virtual void
|
||||
AsyncExecute() override;
|
||||
|
||||
// Set the registration to be operated on by Update() or to be immediately
|
||||
// returned as a result of the job. This must be called before Update().
|
||||
void
|
||||
SetRegistration(ServiceWorkerRegistrationInfo* aRegistration);
|
||||
|
||||
// Execute the bulk of the update job logic using the registration defined
|
||||
// by a previous SetRegistration() call. This can be called by the overriden
|
||||
// AsyncExecute() to complete the job. The Update() method will always call
|
||||
// Finish(). This method corresponds to the spec Update algorithm.
|
||||
void
|
||||
Update();
|
||||
|
||||
private:
|
||||
class CompareCallback;
|
||||
class ContinueUpdateRunnable;
|
||||
class ContinueInstallRunnable;
|
||||
|
||||
// Utility method called after a script is loaded and compared to
|
||||
// our current cached script.
|
||||
void
|
||||
ComparisonResult(nsresult aStatus,
|
||||
bool aInCacheAndEqual,
|
||||
const nsAString& aNewCacheName,
|
||||
const nsACString& aMaxScope);
|
||||
|
||||
// Utility method called after evaluating the worker script.
|
||||
void
|
||||
ContinueUpdateAfterScriptEval(bool aScriptEvaluationResult);
|
||||
|
||||
// Utility method corresponding to the spec Install algorithm.
|
||||
void
|
||||
Install(ServiceWorkerManager* aSWM);
|
||||
|
||||
// Utility method called after the install event is handled.
|
||||
void
|
||||
ContinueAfterInstallEvent(bool aInstallEventSuccess);
|
||||
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
RefPtr<ServiceWorkerRegistrationInfo> mRegistration;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerupdatejob_h
|
||||
558
dom/workers/ServiceWorkerWindowClient.cpp
Normal file
558
dom/workers/ServiceWorkerWindowClient.cpp
Normal file
|
|
@ -0,0 +1,558 @@
|
|||
/* -*- 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 "ServiceWorkerWindowClient.h"
|
||||
|
||||
#include "js/Value.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/dom/ClientBinding.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseWorkerProxy.h"
|
||||
#include "mozilla/UniquePtr.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIDocShellLoadInfo.h"
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsIWebNavigation.h"
|
||||
#include "nsIWebProgress.h"
|
||||
#include "nsIWebProgressListener.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "ServiceWorker.h"
|
||||
#include "ServiceWorkerInfo.h"
|
||||
#include "ServiceWorkerManager.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
using mozilla::UniquePtr;
|
||||
|
||||
JSObject*
|
||||
ServiceWorkerWindowClient::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return WindowClientBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class ResolveOrRejectPromiseRunnable final : public WorkerRunnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
UniquePtr<ServiceWorkerClientInfo> mClientInfo;
|
||||
nsresult mRv;
|
||||
|
||||
public:
|
||||
// Passing a null clientInfo will resolve the promise with a null value.
|
||||
ResolveOrRejectPromiseRunnable(
|
||||
WorkerPrivate* aWorkerPrivate, PromiseWorkerProxy* aPromiseProxy,
|
||||
UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
|
||||
: WorkerRunnable(aWorkerPrivate)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
, mClientInfo(Move(aClientInfo))
|
||||
, mRv(NS_OK)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
// Reject the promise with passed nsresult.
|
||||
ResolveOrRejectPromiseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy,
|
||||
nsresult aRv)
|
||||
: WorkerRunnable(aWorkerPrivate)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
, mClientInfo(nullptr)
|
||||
, mRv(aRv)
|
||||
{
|
||||
MOZ_ASSERT(NS_FAILED(aRv));
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
||||
MOZ_ASSERT(promise);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(mRv))) {
|
||||
promise->MaybeReject(mRv);
|
||||
} else if (mClientInfo) {
|
||||
RefPtr<ServiceWorkerWindowClient> client =
|
||||
new ServiceWorkerWindowClient(promise->GetParentObject(), *mClientInfo);
|
||||
promise->MaybeResolve(client);
|
||||
} else {
|
||||
promise->MaybeResolve(JS::NullHandleValue);
|
||||
}
|
||||
|
||||
// Release the reference on the worker thread.
|
||||
mPromiseProxy->CleanUp();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class ClientFocusRunnable final : public Runnable
|
||||
{
|
||||
uint64_t mWindowId;
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
|
||||
public:
|
||||
ClientFocusRunnable(uint64_t aWindowId, PromiseWorkerProxy* aPromiseProxy)
|
||||
: mWindowId(aWindowId)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
{
|
||||
MOZ_ASSERT(mPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
|
||||
UniquePtr<ServiceWorkerClientInfo> clientInfo;
|
||||
|
||||
if (window) {
|
||||
nsCOMPtr<nsIDocument> doc = window->GetDocument();
|
||||
if (doc) {
|
||||
nsContentUtils::DispatchFocusChromeEvent(window->GetOuterWindow());
|
||||
clientInfo.reset(new ServiceWorkerClientInfo(doc));
|
||||
}
|
||||
}
|
||||
|
||||
DispatchResult(Move(clientInfo));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
void
|
||||
DispatchResult(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
|
||||
if (aClientInfo) {
|
||||
resolveRunnable = new ResolveOrRejectPromiseRunnable(
|
||||
mPromiseProxy->GetWorkerPrivate(), mPromiseProxy, Move(aClientInfo));
|
||||
} else {
|
||||
resolveRunnable = new ResolveOrRejectPromiseRunnable(
|
||||
mPromiseProxy->GetWorkerPrivate(), mPromiseProxy,
|
||||
NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
}
|
||||
|
||||
resolveRunnable->Dispatch();
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerWindowClient::Focus(ErrorResult& aRv) const
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (workerPrivate->GlobalScope()->WindowInteractionAllowed()) {
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (promiseProxy) {
|
||||
RefPtr<ClientFocusRunnable> r = new ClientFocusRunnable(mWindowId,
|
||||
promiseProxy);
|
||||
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_INVALID_STATE_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
class WebProgressListener final : public nsIWebProgressListener,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
public:
|
||||
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(WebProgressListener,
|
||||
nsIWebProgressListener)
|
||||
|
||||
WebProgressListener(PromiseWorkerProxy* aPromiseProxy,
|
||||
ServiceWorkerPrivate* aServiceWorkerPrivate,
|
||||
nsPIDOMWindowOuter* aWindow, nsIURI* aBaseURI)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mServiceWorkerPrivate(aServiceWorkerPrivate)
|
||||
, mWindow(aWindow)
|
||||
, mBaseURI(aBaseURI)
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
MOZ_ASSERT(aServiceWorkerPrivate);
|
||||
MOZ_ASSERT(aWindow);
|
||||
MOZ_ASSERT(aWindow->IsOuterWindow());
|
||||
MOZ_ASSERT(aBaseURI);
|
||||
AssertIsOnMainThread();
|
||||
|
||||
mServiceWorkerPrivate->StoreISupports(static_cast<nsIWebProgressListener*>(this));
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnStateChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
|
||||
uint32_t aStateFlags, nsresult aStatus) override
|
||||
{
|
||||
if (!(aStateFlags & STATE_IS_DOCUMENT) ||
|
||||
!(aStateFlags & (STATE_STOP | STATE_TRANSFERRING))) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// This is safe because our caller holds a strong ref.
|
||||
mServiceWorkerPrivate->RemoveISupports(static_cast<nsIWebProgressListener*>(this));
|
||||
aWebProgress->RemoveProgressListener(this);
|
||||
|
||||
WorkerPrivate* workerPrivate;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mWindow->GetExtantDoc();
|
||||
|
||||
RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable;
|
||||
UniquePtr<ServiceWorkerClientInfo> clientInfo;
|
||||
if (!doc) {
|
||||
resolveRunnable = new ResolveOrRejectPromiseRunnable(
|
||||
workerPrivate, mPromiseProxy, NS_ERROR_TYPE_ERR);
|
||||
resolveRunnable->Dispatch();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
// Check same origin.
|
||||
nsCOMPtr<nsIScriptSecurityManager> securityManager =
|
||||
nsContentUtils::GetSecurityManager();
|
||||
nsresult rv = securityManager->CheckSameOriginURI(doc->GetOriginalURI(),
|
||||
mBaseURI, false);
|
||||
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
nsContentUtils::DispatchFocusChromeEvent(mWindow->GetOuterWindow());
|
||||
clientInfo.reset(new ServiceWorkerClientInfo(doc));
|
||||
}
|
||||
|
||||
resolveRunnable = new ResolveOrRejectPromiseRunnable(
|
||||
workerPrivate, mPromiseProxy, Move(clientInfo));
|
||||
resolveRunnable->Dispatch();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnProgressChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
|
||||
int32_t aCurSelfProgress, int32_t aMaxSelfProgress,
|
||||
int32_t aCurTotalProgress,
|
||||
int32_t aMaxTotalProgress) override
|
||||
{
|
||||
MOZ_CRASH("Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnLocationChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
|
||||
nsIURI* aLocation, uint32_t aFlags) override
|
||||
{
|
||||
MOZ_CRASH("Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnStatusChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
|
||||
nsresult aStatus, const char16_t* aMessage) override
|
||||
{
|
||||
MOZ_CRASH("Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
OnSecurityChange(nsIWebProgress* aWebProgress, nsIRequest* aRequest,
|
||||
uint32_t aState) override
|
||||
{
|
||||
MOZ_CRASH("Unexpected notification.");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
~WebProgressListener() {}
|
||||
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
RefPtr<ServiceWorkerPrivate> mServiceWorkerPrivate;
|
||||
nsCOMPtr<nsPIDOMWindowOuter> mWindow;
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
};
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebProgressListener)
|
||||
NS_IMPL_CYCLE_COLLECTION(WebProgressListener, mPromiseProxy,
|
||||
mServiceWorkerPrivate, mWindow)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebProgressListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
class ClientNavigateRunnable final : public Runnable
|
||||
{
|
||||
uint64_t mWindowId;
|
||||
nsString mUrl;
|
||||
nsCString mBaseUrl;
|
||||
nsString mScope;
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
MOZ_INIT_OUTSIDE_CTOR WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
public:
|
||||
ClientNavigateRunnable(uint64_t aWindowId, const nsAString& aUrl,
|
||||
const nsAString& aScope,
|
||||
PromiseWorkerProxy* aPromiseProxy)
|
||||
: mWindowId(aWindowId)
|
||||
, mUrl(aUrl)
|
||||
, mScope(aScope)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
, mWorkerPrivate(nullptr)
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
MOZ_ASSERT(aPromiseProxy->GetWorkerPrivate());
|
||||
aPromiseProxy->GetWorkerPrivate()->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIPrincipal> principal;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mWorkerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
|
||||
mBaseUrl = info.mHref;
|
||||
principal = mWorkerPrivate->GetPrincipal();
|
||||
MOZ_DIAGNOSTIC_ASSERT(principal);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> baseUrl;
|
||||
nsCOMPtr<nsIURI> url;
|
||||
nsresult rv = ParseUrl(getter_AddRefs(baseUrl), getter_AddRefs(url));
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return RejectPromise(NS_ERROR_TYPE_ERR);
|
||||
}
|
||||
|
||||
rv = principal->CheckMayLoad(url, true, false);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return RejectPromise(rv);
|
||||
}
|
||||
|
||||
nsGlobalWindow* window;
|
||||
rv = Navigate(url, principal, &window);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return RejectPromise(rv);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
||||
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
|
||||
if (NS_WARN_IF(!webProgress)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (!swm) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
RefPtr<ServiceWorkerRegistrationInfo> registration =
|
||||
swm->GetRegistration(principal, NS_ConvertUTF16toUTF8(mScope));
|
||||
if (NS_WARN_IF(!registration)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
RefPtr<ServiceWorkerInfo> serviceWorkerInfo =
|
||||
registration->GetServiceWorkerInfoById(mWorkerPrivate->ServiceWorkerID());
|
||||
if (NS_WARN_IF(!serviceWorkerInfo)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIWebProgressListener> listener =
|
||||
new WebProgressListener(mPromiseProxy, serviceWorkerInfo->WorkerPrivate(),
|
||||
window->GetOuterWindow(), baseUrl);
|
||||
|
||||
rv = webProgress->AddProgressListener(
|
||||
listener, nsIWebProgress::NOTIFY_STATE_DOCUMENT);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return RejectPromise(rv);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
private:
|
||||
nsresult
|
||||
RejectPromise(nsresult aRv)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
|
||||
new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy, aRv);
|
||||
|
||||
resolveRunnable->Dispatch();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ResolvePromise(UniquePtr<ServiceWorkerClientInfo>&& aClientInfo)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
RefPtr<ResolveOrRejectPromiseRunnable> resolveRunnable =
|
||||
new ResolveOrRejectPromiseRunnable(mWorkerPrivate, mPromiseProxy,
|
||||
Move(aClientInfo));
|
||||
|
||||
resolveRunnable->Dispatch();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
ParseUrl(nsIURI** aBaseUrl, nsIURI** aUrl)
|
||||
{
|
||||
MOZ_ASSERT(aBaseUrl);
|
||||
MOZ_ASSERT(aUrl);
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsIURI> baseUrl;
|
||||
nsresult rv = NS_NewURI(getter_AddRefs(baseUrl), mBaseUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
nsCOMPtr<nsIURI> url;
|
||||
rv = NS_NewURI(getter_AddRefs(url), mUrl, nullptr, baseUrl);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
baseUrl.forget(aBaseUrl);
|
||||
url.forget(aUrl);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
Navigate(nsIURI* aUrl, nsIPrincipal* aPrincipal, nsGlobalWindow** aWindow)
|
||||
{
|
||||
MOZ_ASSERT(aWindow);
|
||||
|
||||
nsGlobalWindow* window = nsGlobalWindow::GetInnerWindowWithId(mWindowId);
|
||||
if (NS_WARN_IF(!window)) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = window->GetDocument();
|
||||
if (NS_WARN_IF(!doc)) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(!doc->IsActive())) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShell> docShell = window->GetDocShell();
|
||||
if (NS_WARN_IF(!docShell)) {
|
||||
return NS_ERROR_TYPE_ERR;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIDocShellLoadInfo> loadInfo;
|
||||
nsresult rv = docShell->CreateLoadInfo(getter_AddRefs(loadInfo));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
loadInfo->SetTriggeringPrincipal(aPrincipal);
|
||||
loadInfo->SetReferrer(doc->GetOriginalURI());
|
||||
loadInfo->SetReferrerPolicy(doc->GetReferrerPolicy());
|
||||
loadInfo->SetLoadType(nsIDocShellLoadInfo::loadStopContentAndReplace);
|
||||
loadInfo->SetSourceDocShell(docShell);
|
||||
rv =
|
||||
docShell->LoadURI(aUrl, loadInfo, nsIWebNavigation::LOAD_FLAGS_NONE, true);
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
*aWindow = window;
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerWindowClient::Navigate(const nsAString& aUrl, ErrorResult& aRv)
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
workerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject());
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(global, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aUrl.EqualsLiteral("about:blank")) {
|
||||
promise->MaybeReject(NS_ERROR_TYPE_ERR);
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
ServiceWorkerGlobalScope* globalScope =
|
||||
static_cast<ServiceWorkerGlobalScope*>(workerPrivate->GlobalScope());
|
||||
nsString scope;
|
||||
globalScope->GetScope(scope);
|
||||
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(workerPrivate, promise);
|
||||
if (promiseProxy) {
|
||||
RefPtr<ClientNavigateRunnable> r =
|
||||
new ClientNavigateRunnable(mWindowId, aUrl, scope, promiseProxy);
|
||||
MOZ_ALWAYS_SUCCEEDS(workerPrivate->DispatchToMainThread(r.forget()));
|
||||
} else {
|
||||
promise->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
|
||||
}
|
||||
|
||||
return promise.forget();
|
||||
}
|
||||
64
dom/workers/ServiceWorkerWindowClient.h
Normal file
64
dom/workers/ServiceWorkerWindowClient.h
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/* -*- 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_serviceworkerwindowclient_h
|
||||
#define mozilla_dom_workers_serviceworkerwindowclient_h
|
||||
|
||||
#include "ServiceWorkerClient.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class Promise;
|
||||
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerWindowClient final : public ServiceWorkerClient
|
||||
{
|
||||
public:
|
||||
ServiceWorkerWindowClient(nsISupports* aOwner,
|
||||
const ServiceWorkerClientInfo& aClientInfo)
|
||||
: ServiceWorkerClient(aOwner, aClientInfo),
|
||||
mVisibilityState(aClientInfo.mVisibilityState),
|
||||
mFocused(aClientInfo.mFocused)
|
||||
{
|
||||
}
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
mozilla::dom::VisibilityState
|
||||
VisibilityState() const
|
||||
{
|
||||
return mVisibilityState;
|
||||
}
|
||||
|
||||
bool
|
||||
Focused() const
|
||||
{
|
||||
return mFocused;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Focus(ErrorResult& aRv) const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Navigate(const nsAString& aUrl, ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
~ServiceWorkerWindowClient()
|
||||
{ }
|
||||
|
||||
mozilla::dom::VisibilityState mVisibilityState;
|
||||
bool mFocused;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_serviceworkerwindowclient_h
|
||||
207
dom/workers/SharedWorker.cpp
Normal file
207
dom/workers/SharedWorker.cpp
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/* -*- 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 "SharedWorker.h"
|
||||
|
||||
#include "nsPIDOMWindow.h"
|
||||
|
||||
#include "mozilla/EventDispatcher.h"
|
||||
#include "mozilla/Preferences.h"
|
||||
#include "mozilla/dom/MessagePort.h"
|
||||
#include "mozilla/dom/SharedWorkerBinding.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIClassInfoImpl.h"
|
||||
#include "nsIDOMEvent.h"
|
||||
|
||||
#include "RuntimeService.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
using mozilla::dom::Optional;
|
||||
using mozilla::dom::Sequence;
|
||||
using mozilla::dom::MessagePort;
|
||||
using namespace mozilla;
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
SharedWorker::SharedWorker(nsPIDOMWindowInner* aWindow,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
MessagePort* aMessagePort)
|
||||
: DOMEventTargetHelper(aWindow)
|
||||
, mWorkerPrivate(aWorkerPrivate)
|
||||
, mMessagePort(aMessagePort)
|
||||
, mFrozen(false)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aMessagePort);
|
||||
}
|
||||
|
||||
SharedWorker::~SharedWorker()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<SharedWorker>
|
||||
SharedWorker::Constructor(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
const nsAString& aScriptURL,
|
||||
const mozilla::dom::Optional<nsAString>& aName,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RuntimeService* rts = RuntimeService::GetOrCreateService();
|
||||
if (!rts) {
|
||||
aRv = NS_ERROR_NOT_AVAILABLE;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCString name;
|
||||
if (aName.WasPassed()) {
|
||||
name = NS_ConvertUTF16toUTF8(aName.Value());
|
||||
}
|
||||
|
||||
RefPtr<SharedWorker> sharedWorker;
|
||||
nsresult rv = rts->CreateSharedWorker(aGlobal, aScriptURL, name,
|
||||
getter_AddRefs(sharedWorker));
|
||||
if (NS_FAILED(rv)) {
|
||||
aRv = rv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SHARED_WORKER_COUNT, 1);
|
||||
|
||||
return sharedWorker.forget();
|
||||
}
|
||||
|
||||
MessagePort*
|
||||
SharedWorker::Port()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return mMessagePort;
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::Freeze()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(!IsFrozen());
|
||||
|
||||
mFrozen = true;
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::Thaw()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(IsFrozen());
|
||||
|
||||
mFrozen = false;
|
||||
|
||||
if (!mFrozenEvents.IsEmpty()) {
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>> events;
|
||||
mFrozenEvents.SwapElements(events);
|
||||
|
||||
for (uint32_t index = 0; index < events.Length(); index++) {
|
||||
nsCOMPtr<nsIDOMEvent>& event = events[index];
|
||||
MOZ_ASSERT(event);
|
||||
|
||||
nsCOMPtr<nsIDOMEventTarget> target;
|
||||
if (NS_SUCCEEDED(event->GetTarget(getter_AddRefs(target)))) {
|
||||
bool ignored;
|
||||
if (NS_FAILED(target->DispatchEvent(event, &ignored))) {
|
||||
NS_WARNING("Failed to dispatch event!");
|
||||
}
|
||||
} else {
|
||||
NS_WARNING("Failed to get target!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::QueueEvent(nsIDOMEvent* aEvent)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(aEvent);
|
||||
MOZ_ASSERT(IsFrozen());
|
||||
|
||||
mFrozenEvents.AppendElement(aEvent);
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::Close()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (mMessagePort) {
|
||||
mMessagePort->Close();
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
MOZ_ASSERT(mMessagePort);
|
||||
|
||||
mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(SharedWorker)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrozenEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
|
||||
DOMEventTargetHelper)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrozenEvents)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
JSObject*
|
||||
SharedWorker::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return SharedWorkerBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
nsresult
|
||||
SharedWorker::PreHandleEvent(EventChainPreVisitor& aVisitor)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (IsFrozen()) {
|
||||
nsCOMPtr<nsIDOMEvent> event = aVisitor.mDOMEvent;
|
||||
if (!event) {
|
||||
event = EventDispatcher::CreateEvent(aVisitor.mEvent->mOriginalTarget,
|
||||
aVisitor.mPresContext,
|
||||
aVisitor.mEvent, EmptyString());
|
||||
}
|
||||
|
||||
QueueEvent(event);
|
||||
|
||||
aVisitor.mCanHandle = false;
|
||||
aVisitor.mParentTarget = nullptr;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
return DOMEventTargetHelper::PreHandleEvent(aVisitor);
|
||||
}
|
||||
105
dom/workers/SharedWorker.h
Normal file
105
dom/workers/SharedWorker.h
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/* -*- 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_sharedworker_h__
|
||||
#define mozilla_dom_workers_sharedworker_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
|
||||
class nsIDOMEvent;
|
||||
class nsPIDOMWindowInner;
|
||||
|
||||
namespace mozilla {
|
||||
class EventChainPreVisitor;
|
||||
|
||||
namespace dom {
|
||||
class MessagePort;
|
||||
}
|
||||
} // namespace mozilla
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class RuntimeService;
|
||||
class WorkerPrivate;
|
||||
|
||||
class SharedWorker final : public DOMEventTargetHelper
|
||||
{
|
||||
friend class RuntimeService;
|
||||
|
||||
typedef mozilla::ErrorResult ErrorResult;
|
||||
typedef mozilla::dom::GlobalObject GlobalObject;
|
||||
|
||||
RefPtr<WorkerPrivate> mWorkerPrivate;
|
||||
RefPtr<MessagePort> mMessagePort;
|
||||
nsTArray<nsCOMPtr<nsIDOMEvent>> mFrozenEvents;
|
||||
bool mFrozen;
|
||||
|
||||
public:
|
||||
static already_AddRefed<SharedWorker>
|
||||
Constructor(const GlobalObject& aGlobal, JSContext* aCx,
|
||||
const nsAString& aScriptURL, const Optional<nsAString>& aName,
|
||||
ErrorResult& aRv);
|
||||
|
||||
MessagePort*
|
||||
Port();
|
||||
|
||||
bool
|
||||
IsFrozen() const
|
||||
{
|
||||
return mFrozen;
|
||||
}
|
||||
|
||||
void
|
||||
Freeze();
|
||||
|
||||
void
|
||||
Thaw();
|
||||
|
||||
void
|
||||
QueueEvent(nsIDOMEvent* aEvent);
|
||||
|
||||
void
|
||||
Close();
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SharedWorker, DOMEventTargetHelper)
|
||||
|
||||
IMPL_EVENT_HANDLER(error)
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual nsresult
|
||||
PreHandleEvent(EventChainPreVisitor& aVisitor) override;
|
||||
|
||||
WorkerPrivate*
|
||||
GetWorkerPrivate() const
|
||||
{
|
||||
return mWorkerPrivate;
|
||||
}
|
||||
|
||||
private:
|
||||
// This class can only be created from the RuntimeService.
|
||||
SharedWorker(nsPIDOMWindowInner* aWindow,
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
MessagePort* aMessagePort);
|
||||
|
||||
// This class is reference-counted and will be destroyed from Release().
|
||||
~SharedWorker();
|
||||
|
||||
// Only called by MessagePort.
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_sharedworker_h__
|
||||
361
dom/workers/WorkerDebuggerManager.cpp
Normal file
361
dom/workers/WorkerDebuggerManager.cpp
Normal file
|
|
@ -0,0 +1,361 @@
|
|||
/* -*- 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 "WorkerDebuggerManager.h"
|
||||
|
||||
#include "nsISimpleEnumerator.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
class RegisterDebuggerMainThreadRunnable final : public mozilla::Runnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
bool mNotifyListeners;
|
||||
|
||||
public:
|
||||
RegisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
bool aNotifyListeners)
|
||||
: mWorkerPrivate(aWorkerPrivate),
|
||||
mNotifyListeners(aNotifyListeners)
|
||||
{ }
|
||||
|
||||
private:
|
||||
~RegisterDebuggerMainThreadRunnable()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
|
||||
MOZ_ASSERT(manager);
|
||||
|
||||
manager->RegisterDebuggerMainThread(mWorkerPrivate, mNotifyListeners);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
class UnregisterDebuggerMainThreadRunnable final : public mozilla::Runnable
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
public:
|
||||
explicit UnregisterDebuggerMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{ }
|
||||
|
||||
private:
|
||||
~UnregisterDebuggerMainThreadRunnable()
|
||||
{ }
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
WorkerDebuggerManager* manager = WorkerDebuggerManager::Get();
|
||||
MOZ_ASSERT(manager);
|
||||
|
||||
manager->UnregisterDebuggerMainThread(mWorkerPrivate);
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
// Does not hold an owning reference.
|
||||
static WorkerDebuggerManager* gWorkerDebuggerManager;
|
||||
|
||||
} /* anonymous namespace */
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerDebuggerEnumerator final : public nsISimpleEnumerator
|
||||
{
|
||||
nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
|
||||
uint32_t mIndex;
|
||||
|
||||
public:
|
||||
explicit WorkerDebuggerEnumerator(
|
||||
const nsTArray<RefPtr<WorkerDebugger>>& aDebuggers)
|
||||
: mDebuggers(aDebuggers), mIndex(0)
|
||||
{
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSISIMPLEENUMERATOR
|
||||
|
||||
private:
|
||||
~WorkerDebuggerEnumerator() {}
|
||||
};
|
||||
|
||||
NS_IMPL_ISUPPORTS(WorkerDebuggerEnumerator, nsISimpleEnumerator);
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebuggerEnumerator::HasMoreElements(bool* aResult)
|
||||
{
|
||||
*aResult = mIndex < mDebuggers.Length();
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebuggerEnumerator::GetNext(nsISupports** aResult)
|
||||
{
|
||||
if (mIndex == mDebuggers.Length()) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
mDebuggers.ElementAt(mIndex++).forget(aResult);
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
WorkerDebuggerManager::WorkerDebuggerManager()
|
||||
: mMutex("WorkerDebuggerManager::mMutex")
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
WorkerDebuggerManager::~WorkerDebuggerManager()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<WorkerDebuggerManager>
|
||||
WorkerDebuggerManager::GetInstance()
|
||||
{
|
||||
RefPtr<WorkerDebuggerManager> manager = WorkerDebuggerManager::GetOrCreate();
|
||||
return manager.forget();
|
||||
}
|
||||
|
||||
// static
|
||||
WorkerDebuggerManager*
|
||||
WorkerDebuggerManager::GetOrCreate()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
if (!gWorkerDebuggerManager) {
|
||||
// The observer service now owns us until shutdown.
|
||||
gWorkerDebuggerManager = new WorkerDebuggerManager();
|
||||
if (NS_FAILED(gWorkerDebuggerManager->Init())) {
|
||||
NS_WARNING("Failed to initialize worker debugger manager!");
|
||||
gWorkerDebuggerManager = nullptr;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return gWorkerDebuggerManager;
|
||||
}
|
||||
|
||||
WorkerDebuggerManager*
|
||||
WorkerDebuggerManager::Get()
|
||||
{
|
||||
MOZ_ASSERT(gWorkerDebuggerManager);
|
||||
return gWorkerDebuggerManager;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(WorkerDebuggerManager, nsIObserver, nsIWorkerDebuggerManager);
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebuggerManager::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
const char16_t* aData)
|
||||
{
|
||||
if (!strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
|
||||
Shutdown();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_NOTREACHED("Unknown observer topic!");
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebuggerManager::GetWorkerDebuggerEnumerator(
|
||||
nsISimpleEnumerator** aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<WorkerDebuggerEnumerator> enumerator =
|
||||
new WorkerDebuggerEnumerator(mDebuggers);
|
||||
enumerator.forget(aResult);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebuggerManager::AddListener(nsIWorkerDebuggerManagerListener* aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (mListeners.Contains(aListener)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mListeners.AppendElement(aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerDebuggerManager::RemoveListener(
|
||||
nsIWorkerDebuggerManagerListener* aListener)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
if (!mListeners.Contains(aListener)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mListeners.RemoveElement(aListener);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerDebuggerManager::Init()
|
||||
{
|
||||
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
|
||||
NS_ENSURE_TRUE(obs, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = obs->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, false);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerManager::Shutdown()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
mListeners.Clear();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerManager::RegisterDebugger(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
// When the parent thread is the main thread, it will always block until all
|
||||
// register liseners have been called, since it cannot continue until the
|
||||
// call to RegisterDebuggerMainThread returns.
|
||||
//
|
||||
// In this case, it is always safe to notify all listeners on the main
|
||||
// thread, even if there were no listeners at the time this method was
|
||||
// called, so we can always pass true for the value of aNotifyListeners.
|
||||
// This avoids having to lock mMutex to check whether mListeners is empty.
|
||||
RegisterDebuggerMainThread(aWorkerPrivate, true);
|
||||
} else {
|
||||
// We guarantee that if any register listeners are called, the worker does
|
||||
// not start running until all register listeners have been called. To
|
||||
// guarantee this, the parent thread should block until all register
|
||||
// listeners have been called.
|
||||
//
|
||||
// However, to avoid overhead when the debugger is not being used, the
|
||||
// parent thread will only block if there were any listeners at the time
|
||||
// this method was called. As a result, we should not notify any listeners
|
||||
// on the main thread if there were no listeners at the time this method was
|
||||
// called, because the parent will not be blocking in that case.
|
||||
bool hasListeners = false;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
hasListeners = !mListeners.IsEmpty();
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new RegisterDebuggerMainThreadRunnable(aWorkerPrivate, hasListeners);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
|
||||
|
||||
if (hasListeners) {
|
||||
aWorkerPrivate->WaitForIsDebuggerRegistered(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerManager::UnregisterDebugger(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
UnregisterDebuggerMainThread(aWorkerPrivate);
|
||||
} else {
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
new UnregisterDebuggerMainThreadRunnable(aWorkerPrivate);
|
||||
MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable, NS_DISPATCH_NORMAL));
|
||||
|
||||
aWorkerPrivate->WaitForIsDebuggerRegistered(false);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerManager::RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
|
||||
bool aNotifyListeners)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
RefPtr<WorkerDebugger> debugger = new WorkerDebugger(aWorkerPrivate);
|
||||
mDebuggers.AppendElement(debugger);
|
||||
|
||||
aWorkerPrivate->SetDebugger(debugger);
|
||||
|
||||
if (aNotifyListeners) {
|
||||
nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
listeners = mListeners;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < listeners.Length(); ++index) {
|
||||
listeners[index]->OnRegister(debugger);
|
||||
}
|
||||
}
|
||||
|
||||
aWorkerPrivate->SetIsDebuggerRegistered(true);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerManager::UnregisterDebuggerMainThread(
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
// There is nothing to do here if the debugger was never succesfully
|
||||
// registered. We need to check this on the main thread because the worker
|
||||
// does not wait for the registration to complete if there were no listeners
|
||||
// installed when it started.
|
||||
if (!aWorkerPrivate->IsDebuggerRegistered()) {
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<WorkerDebugger> debugger = aWorkerPrivate->Debugger();
|
||||
mDebuggers.RemoveElement(debugger);
|
||||
|
||||
aWorkerPrivate->SetDebugger(nullptr);
|
||||
|
||||
nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> listeners;
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
|
||||
listeners = mListeners;
|
||||
}
|
||||
|
||||
for (size_t index = 0; index < listeners.Length(); ++index) {
|
||||
listeners[index]->OnUnregister(debugger);
|
||||
}
|
||||
|
||||
debugger->Close();
|
||||
aWorkerPrivate->SetIsDebuggerRegistered(false);
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
121
dom/workers/WorkerDebuggerManager.h
Normal file
121
dom/workers/WorkerDebuggerManager.h
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
/* -*- 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_workerdebuggermanager_h
|
||||
#define mozilla_dom_workers_workerdebuggermanager_h
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsIObserver.h"
|
||||
#include "nsIWorkerDebuggerManager.h"
|
||||
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#define WORKERDEBUGGERMANAGER_CID \
|
||||
{ 0x62ec8731, 0x55ad, 0x4246, \
|
||||
{ 0xb2, 0xea, 0xf2, 0x6c, 0x1f, 0xe1, 0x9d, 0x2d } }
|
||||
#define WORKERDEBUGGERMANAGER_CONTRACTID \
|
||||
"@mozilla.org/dom/workers/workerdebuggermanager;1"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerDebugger;
|
||||
|
||||
class WorkerDebuggerManager final : public nsIObserver,
|
||||
public nsIWorkerDebuggerManager
|
||||
{
|
||||
Mutex mMutex;
|
||||
|
||||
// Protected by mMutex.
|
||||
nsTArray<nsCOMPtr<nsIWorkerDebuggerManagerListener>> mListeners;
|
||||
|
||||
// Only touched on the main thread.
|
||||
nsTArray<RefPtr<WorkerDebugger>> mDebuggers;
|
||||
|
||||
public:
|
||||
static already_AddRefed<WorkerDebuggerManager>
|
||||
GetInstance();
|
||||
|
||||
static WorkerDebuggerManager*
|
||||
GetOrCreate();
|
||||
|
||||
static WorkerDebuggerManager*
|
||||
Get();
|
||||
|
||||
WorkerDebuggerManager();
|
||||
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIOBSERVER
|
||||
NS_DECL_NSIWORKERDEBUGGERMANAGER
|
||||
|
||||
nsresult
|
||||
Init();
|
||||
|
||||
void
|
||||
Shutdown();
|
||||
|
||||
void
|
||||
RegisterDebugger(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
RegisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate,
|
||||
bool aNotifyListeners);
|
||||
|
||||
void
|
||||
UnregisterDebugger(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
void
|
||||
UnregisterDebuggerMainThread(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
private:
|
||||
virtual ~WorkerDebuggerManager();
|
||||
};
|
||||
|
||||
inline nsresult
|
||||
RegisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
WorkerDebuggerManager* manager;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
manager = WorkerDebuggerManager::GetOrCreate();
|
||||
if (!manager) {
|
||||
NS_WARNING("Failed to create worker debugger manager!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
manager = WorkerDebuggerManager::Get();
|
||||
}
|
||||
|
||||
manager->RegisterDebugger(aWorkerPrivate);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
inline nsresult
|
||||
UnregisterWorkerDebugger(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
WorkerDebuggerManager* manager;
|
||||
|
||||
if (NS_IsMainThread()) {
|
||||
manager = WorkerDebuggerManager::GetOrCreate();
|
||||
if (!manager) {
|
||||
NS_WARNING("Failed to create worker debugger manager!");
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
}
|
||||
else {
|
||||
manager = WorkerDebuggerManager::Get();
|
||||
}
|
||||
|
||||
manager->UnregisterDebugger(aWorkerPrivate);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workerdebuggermanager_h
|
||||
60
dom/workers/WorkerHolder.cpp
Normal file
60
dom/workers/WorkerHolder.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/* -*- 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 "WorkerHolder.h"
|
||||
#include "WorkerPrivate.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
WorkerHolder::WorkerHolder()
|
||||
: mWorkerPrivate(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
WorkerHolder::~WorkerHolder()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(WorkerHolder);
|
||||
ReleaseWorkerInternal();
|
||||
MOZ_ASSERT(mWorkerPrivate == nullptr);
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerHolder::HoldWorker(WorkerPrivate* aWorkerPrivate, Status aFailStatus)
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(WorkerHolder);
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!aWorkerPrivate->AddHolder(this, aFailStatus)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mWorkerPrivate = aWorkerPrivate;
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerHolder::ReleaseWorker()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(WorkerHolder);
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
|
||||
ReleaseWorkerInternal();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerHolder::ReleaseWorkerInternal()
|
||||
{
|
||||
NS_ASSERT_OWNINGTHREAD(WorkerHolder);
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->RemoveHolder(this);
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
96
dom/workers/WorkerHolder.h
Normal file
96
dom/workers/WorkerHolder.h
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/* -*- 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_WorkerHolder_h
|
||||
#define mozilla_dom_workers_WorkerHolder_h
|
||||
|
||||
#include "mozilla/dom/workers/Workers.h"
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
/**
|
||||
* Use this chart to help figure out behavior during each of the closing
|
||||
* statuses. Details below.
|
||||
*
|
||||
* +==============================================================+
|
||||
* | Closing Statuses |
|
||||
* +=============+=============+=================+================+
|
||||
* | status | clear queue | abort execution | close handler |
|
||||
* +=============+=============+=================+================+
|
||||
* | Closing | yes | no | no timeout |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* | Terminating | yes | yes | no timeout |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* | Canceling | yes | yes | short duration |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
* | Killing | yes | yes | doesn't run |
|
||||
* +-------------+-------------+-----------------+----------------+
|
||||
*/
|
||||
|
||||
#ifdef Status
|
||||
/* Xlib headers insist on this for some reason... Nuke it because
|
||||
it'll override our member name */
|
||||
#undef Status
|
||||
#endif
|
||||
enum Status
|
||||
{
|
||||
// Not yet scheduled.
|
||||
Pending = 0,
|
||||
|
||||
// This status means that the close handler has not yet been scheduled.
|
||||
Running,
|
||||
|
||||
// Inner script called close() on the worker global scope. Setting this
|
||||
// status causes the worker to clear its queue of events but does not abort
|
||||
// the currently running script. The close handler is also scheduled with
|
||||
// no expiration time.
|
||||
Closing,
|
||||
|
||||
// Outer script called terminate() on the worker or the worker object was
|
||||
// garbage collected in its outer script. Setting this status causes the
|
||||
// worker to abort immediately, clear its queue of events, and schedules the
|
||||
// close handler with no expiration time.
|
||||
Terminating,
|
||||
|
||||
// Either the user navigated away from the owning page or the owning page fell
|
||||
// out of bfcache. Setting this status causes the worker to abort immediately
|
||||
// and schedules the close handler with a short expiration time. Since the
|
||||
// page has gone away the worker may not post any messages.
|
||||
Canceling,
|
||||
|
||||
// The application is shutting down. Setting this status causes the worker to
|
||||
// abort immediately and the close handler is never scheduled.
|
||||
Killing,
|
||||
|
||||
// The close handler has run and the worker is effectively dead.
|
||||
Dead
|
||||
};
|
||||
|
||||
class WorkerHolder
|
||||
{
|
||||
public:
|
||||
NS_DECL_OWNINGTHREAD
|
||||
|
||||
WorkerHolder();
|
||||
virtual ~WorkerHolder();
|
||||
|
||||
bool HoldWorker(WorkerPrivate* aWorkerPrivate, Status aFailStatus);
|
||||
void ReleaseWorker();
|
||||
|
||||
virtual bool Notify(Status aStatus) = 0;
|
||||
|
||||
protected:
|
||||
void ReleaseWorkerInternal();
|
||||
|
||||
WorkerPrivate* MOZ_NON_OWNING_REF mWorkerPrivate;
|
||||
|
||||
private:
|
||||
void AssertIsOwningThread() const;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif /* mozilla_dom_workers_WorkerHolder_h */
|
||||
25
dom/workers/WorkerInlines.h
Normal file
25
dom/workers/WorkerInlines.h
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
inline
|
||||
void
|
||||
SetJSPrivateSafeish(JSObject* aObj, PrivatizableBase* aBase)
|
||||
{
|
||||
JS_SetPrivate(aObj, aBase);
|
||||
}
|
||||
|
||||
template <class Derived>
|
||||
inline
|
||||
Derived*
|
||||
GetJSPrivateSafeish(JSObject* aObj)
|
||||
{
|
||||
return static_cast<Derived*>(
|
||||
static_cast<PrivatizableBase*>(JS_GetPrivate(aObj)));
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
43
dom/workers/WorkerLocation.cpp
Normal file
43
dom/workers/WorkerLocation.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/* -*- 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 "mozilla/dom/WorkerLocation.h"
|
||||
|
||||
#include "mozilla/dom/WorkerLocationBinding.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_0(WorkerLocation)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerLocation, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerLocation, Release)
|
||||
|
||||
/* static */ already_AddRefed<WorkerLocation>
|
||||
WorkerLocation::Create(workers::WorkerPrivate::LocationInfo& aInfo)
|
||||
{
|
||||
RefPtr<WorkerLocation> location =
|
||||
new WorkerLocation(NS_ConvertUTF8toUTF16(aInfo.mHref),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mProtocol),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mHost),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mHostname),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mPort),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mPathname),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mSearch),
|
||||
NS_ConvertUTF8toUTF16(aInfo.mHash),
|
||||
aInfo.mOrigin);
|
||||
|
||||
return location.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WorkerLocation::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return WorkerLocationBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
116
dom/workers/WorkerLocation.h
Normal file
116
dom/workers/WorkerLocation.h
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
/* -*- 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_location_h__
|
||||
#define mozilla_dom_location_h__
|
||||
|
||||
#include "Workers.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "nsWrapperCache.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class WorkerLocation final : public nsWrapperCache
|
||||
{
|
||||
nsString mHref;
|
||||
nsString mProtocol;
|
||||
nsString mHost;
|
||||
nsString mHostname;
|
||||
nsString mPort;
|
||||
nsString mPathname;
|
||||
nsString mSearch;
|
||||
nsString mHash;
|
||||
nsString mOrigin;
|
||||
|
||||
WorkerLocation(const nsAString& aHref,
|
||||
const nsAString& aProtocol,
|
||||
const nsAString& aHost,
|
||||
const nsAString& aHostname,
|
||||
const nsAString& aPort,
|
||||
const nsAString& aPathname,
|
||||
const nsAString& aSearch,
|
||||
const nsAString& aHash,
|
||||
const nsAString& aOrigin)
|
||||
: mHref(aHref)
|
||||
, mProtocol(aProtocol)
|
||||
, mHost(aHost)
|
||||
, mHostname(aHostname)
|
||||
, mPort(aPort)
|
||||
, mPathname(aPathname)
|
||||
, mSearch(aSearch)
|
||||
, mHash(aHash)
|
||||
, mOrigin(aOrigin)
|
||||
{
|
||||
MOZ_COUNT_CTOR(WorkerLocation);
|
||||
}
|
||||
|
||||
~WorkerLocation()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WorkerLocation);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WorkerLocation)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WorkerLocation)
|
||||
|
||||
static already_AddRefed<WorkerLocation>
|
||||
Create(workers::WorkerPrivate::LocationInfo& aInfo);
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Stringify(nsString& aHref) const
|
||||
{
|
||||
aHref = mHref;
|
||||
}
|
||||
void GetHref(nsString& aHref) const
|
||||
{
|
||||
aHref = mHref;
|
||||
}
|
||||
void GetProtocol(nsString& aProtocol) const
|
||||
{
|
||||
aProtocol = mProtocol;
|
||||
}
|
||||
void GetHost(nsString& aHost) const
|
||||
{
|
||||
aHost = mHost;
|
||||
}
|
||||
void GetHostname(nsString& aHostname) const
|
||||
{
|
||||
aHostname = mHostname;
|
||||
}
|
||||
void GetPort(nsString& aPort) const
|
||||
{
|
||||
aPort = mPort;
|
||||
}
|
||||
void GetPathname(nsString& aPathname) const
|
||||
{
|
||||
aPathname = mPathname;
|
||||
}
|
||||
void GetSearch(nsString& aSearch) const
|
||||
{
|
||||
aSearch = mSearch;
|
||||
}
|
||||
void GetHash(nsString& aHash) const
|
||||
{
|
||||
aHash = mHash;
|
||||
}
|
||||
void GetOrigin(nsString& aOrigin) const
|
||||
{
|
||||
aOrigin = mOrigin;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_location_h__
|
||||
184
dom/workers/WorkerNavigator.cpp
Normal file
184
dom/workers/WorkerNavigator.cpp
Normal file
|
|
@ -0,0 +1,184 @@
|
|||
/* -*- 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 "mozilla/dom/BindingUtils.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseWorkerProxy.h"
|
||||
#include "mozilla/dom/StorageManager.h"
|
||||
#include "mozilla/dom/WorkerNavigator.h"
|
||||
#include "mozilla/dom/WorkerNavigatorBinding.h"
|
||||
|
||||
#include "nsProxyRelease.h"
|
||||
#include "RuntimeService.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
#include "mozilla/dom/Navigator.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager);
|
||||
NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerNavigator, Release)
|
||||
|
||||
/* static */ already_AddRefed<WorkerNavigator>
|
||||
WorkerNavigator::Create(bool aOnLine)
|
||||
{
|
||||
RuntimeService* rts = RuntimeService::GetService();
|
||||
MOZ_ASSERT(rts);
|
||||
|
||||
const RuntimeService::NavigatorProperties& properties =
|
||||
rts->GetNavigatorProperties();
|
||||
|
||||
RefPtr<WorkerNavigator> navigator =
|
||||
new WorkerNavigator(properties, aOnLine);
|
||||
|
||||
return navigator.forget();
|
||||
}
|
||||
|
||||
JSObject*
|
||||
WorkerNavigator::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
return WorkerNavigatorBinding::Wrap(aCx, this, aGivenProto);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerNavigator::SetLanguages(const nsTArray<nsString>& aLanguages)
|
||||
{
|
||||
WorkerNavigatorBinding::ClearCachedLanguagesValue(this);
|
||||
mProperties.mLanguages = aLanguages;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerNavigator::GetAppName(nsString& aAppName) const
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
if (!mProperties.mAppNameOverridden.IsEmpty() &&
|
||||
!workerPrivate->UsesSystemPrincipal()) {
|
||||
aAppName = mProperties.mAppNameOverridden;
|
||||
} else {
|
||||
aAppName = mProperties.mAppName;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerNavigator::GetAppVersion(nsString& aAppVersion) const
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
if (!mProperties.mAppVersionOverridden.IsEmpty() &&
|
||||
!workerPrivate->UsesSystemPrincipal()) {
|
||||
aAppVersion = mProperties.mAppVersionOverridden;
|
||||
} else {
|
||||
aAppVersion = mProperties.mAppVersion;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerNavigator::GetPlatform(nsString& aPlatform) const
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
if (!mProperties.mPlatformOverridden.IsEmpty() &&
|
||||
!workerPrivate->UsesSystemPrincipal()) {
|
||||
aPlatform = mProperties.mPlatformOverridden;
|
||||
} else {
|
||||
aPlatform = mProperties.mPlatform;
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class GetUserAgentRunnable final : public WorkerMainThreadRunnable
|
||||
{
|
||||
nsString& mUA;
|
||||
|
||||
public:
|
||||
GetUserAgentRunnable(WorkerPrivate* aWorkerPrivate, nsString& aUA)
|
||||
: WorkerMainThreadRunnable(aWorkerPrivate,
|
||||
NS_LITERAL_CSTRING("UserAgent getter"))
|
||||
, mUA(aUA)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
virtual bool MainThreadRun() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
nsCOMPtr<nsPIDOMWindowInner> window = mWorkerPrivate->GetWindow();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
if (window && window->GetDocShell()) {
|
||||
nsIDocument* doc = window->GetExtantDoc();
|
||||
if (doc) {
|
||||
doc->NodePrincipal()->GetURI(getter_AddRefs(uri));
|
||||
}
|
||||
}
|
||||
|
||||
bool isCallerChrome = mWorkerPrivate->UsesSystemPrincipal();
|
||||
nsresult rv = dom::Navigator::GetUserAgent(window, uri,
|
||||
isCallerChrome, mUA);
|
||||
if (NS_FAILED(rv)) {
|
||||
NS_WARNING("Failed to retrieve user-agent from the worker thread.");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
void
|
||||
WorkerNavigator::GetUserAgent(nsString& aUserAgent, ErrorResult& aRv) const
|
||||
{
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<GetUserAgentRunnable> runnable =
|
||||
new GetUserAgentRunnable(workerPrivate, aUserAgent);
|
||||
|
||||
runnable->Dispatch(aRv);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
WorkerNavigator::HardwareConcurrency() const
|
||||
{
|
||||
RuntimeService* rts = RuntimeService::GetService();
|
||||
MOZ_ASSERT(rts);
|
||||
|
||||
return rts->ClampedHardwareConcurrency();
|
||||
}
|
||||
|
||||
StorageManager*
|
||||
WorkerNavigator::Storage()
|
||||
{
|
||||
if (!mStorageManager) {
|
||||
WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<nsIGlobalObject> global = workerPrivate->GlobalScope();
|
||||
MOZ_ASSERT(global);
|
||||
|
||||
mStorageManager = new StorageManager(global);
|
||||
}
|
||||
|
||||
return mStorageManager;
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
114
dom/workers/WorkerNavigator.h
Normal file
114
dom/workers/WorkerNavigator.h
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
/* -*- 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_workernavigator_h__
|
||||
#define mozilla_dom_workernavigator_h__
|
||||
|
||||
#include "Workers.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "nsString.h"
|
||||
#include "nsWrapperCache.h"
|
||||
#include "mozilla/dom/StorageManager.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class Promise;
|
||||
class StorageManager;
|
||||
|
||||
class WorkerNavigator final : public nsWrapperCache
|
||||
{
|
||||
typedef struct workers::RuntimeService::NavigatorProperties NavigatorProperties;
|
||||
|
||||
NavigatorProperties mProperties;
|
||||
RefPtr<StorageManager> mStorageManager;
|
||||
bool mOnline;
|
||||
|
||||
WorkerNavigator(const NavigatorProperties& aProperties,
|
||||
bool aOnline)
|
||||
: mProperties(aProperties)
|
||||
, mOnline(aOnline)
|
||||
{
|
||||
MOZ_COUNT_CTOR(WorkerNavigator);
|
||||
}
|
||||
|
||||
~WorkerNavigator()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WorkerNavigator);
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(WorkerNavigator)
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_NATIVE_CLASS(WorkerNavigator)
|
||||
|
||||
static already_AddRefed<WorkerNavigator>
|
||||
Create(bool aOnLine);
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
nsISupports* GetParentObject() const {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void GetAppCodeName(nsString& aAppCodeName) const
|
||||
{
|
||||
aAppCodeName.AssignLiteral("Mozilla");
|
||||
}
|
||||
void GetAppName(nsString& aAppName) const;
|
||||
|
||||
void GetAppVersion(nsString& aAppVersion) const;
|
||||
|
||||
void GetPlatform(nsString& aPlatform) const;
|
||||
|
||||
void GetProduct(nsString& aProduct) const
|
||||
{
|
||||
aProduct.AssignLiteral("Gecko");
|
||||
}
|
||||
|
||||
bool TaintEnabled() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void GetLanguage(nsString& aLanguage) const
|
||||
{
|
||||
if (mProperties.mLanguages.Length() >= 1) {
|
||||
aLanguage.Assign(mProperties.mLanguages[0]);
|
||||
} else {
|
||||
aLanguage.Truncate();
|
||||
}
|
||||
}
|
||||
|
||||
void GetLanguages(nsTArray<nsString>& aLanguages) const
|
||||
{
|
||||
aLanguages = mProperties.mLanguages;
|
||||
}
|
||||
|
||||
void GetUserAgent(nsString& aUserAgent, ErrorResult& aRv) const;
|
||||
|
||||
bool OnLine() const
|
||||
{
|
||||
return mOnline;
|
||||
}
|
||||
|
||||
// Worker thread only!
|
||||
void SetOnLine(bool aOnline)
|
||||
{
|
||||
mOnline = aOnline;
|
||||
}
|
||||
|
||||
void SetLanguages(const nsTArray<nsString>& aLanguages);
|
||||
|
||||
uint64_t HardwareConcurrency() const;
|
||||
|
||||
StorageManager* Storage();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workernavigator_h__
|
||||
49
dom/workers/WorkerPrefs.h
Normal file
49
dom/workers/WorkerPrefs.h
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
/* -*- 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/. */
|
||||
|
||||
// This is the list of the preferences that are exposed to workers.
|
||||
// The format is as follows:
|
||||
//
|
||||
// WORKER_SIMPLE_PREF("foo.bar", FooBar, FOO_BAR, UpdaterFunction)
|
||||
//
|
||||
// * First argument is the name of the pref.
|
||||
// * The name of the getter function. This defines a FindName()
|
||||
// function that returns the value of the pref on WorkerPrivate.
|
||||
// * The macro version of the name. This defines a WORKERPREF_FOO_BAR
|
||||
// macro in Workers.h.
|
||||
// * The name of the function that updates the new value of a pref.
|
||||
//
|
||||
// WORKER_PREF("foo.bar", UpdaterFunction)
|
||||
//
|
||||
// * First argument is the name of the pref.
|
||||
// * The name of the function that updates the new value of a pref.
|
||||
|
||||
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
WORKER_SIMPLE_PREF("browser.dom.window.dump.enabled", DumpEnabled, DUMP)
|
||||
#endif
|
||||
WORKER_SIMPLE_PREF("canvas.imagebitmap_extensions.enabled", ImageBitmapExtensionsEnabled, IMAGEBITMAP_EXTENSIONS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.caches.enabled", DOMCachesEnabled, DOM_CACHES)
|
||||
WORKER_SIMPLE_PREF("dom.caches.testing.enabled", DOMCachesTestingEnabled, DOM_CACHES_TESTING)
|
||||
WORKER_SIMPLE_PREF("dom.performance.enable_user_timing_logging", PerformanceLoggingEnabled, PERFORMANCE_LOGGING_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.webnotifications.enabled", DOMWorkerNotificationEnabled, DOM_WORKERNOTIFICATION)
|
||||
WORKER_SIMPLE_PREF("dom.webnotifications.serviceworker.enabled", DOMServiceWorkerNotificationEnabled, DOM_SERVICEWORKERNOTIFICATION)
|
||||
WORKER_SIMPLE_PREF("dom.webnotifications.requireinteraction.enabled", DOMWorkerNotificationRIEnabled, DOM_WORKERNOTIFICATIONRI)
|
||||
WORKER_SIMPLE_PREF("dom.serviceWorkers.enabled", ServiceWorkersEnabled, SERVICEWORKERS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.serviceWorkers.testing.enabled", ServiceWorkersTestingEnabled, SERVICEWORKERS_TESTING_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.serviceWorkers.openWindow.enabled", OpenWindowEnabled, OPEN_WINDOW_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.storageManager.enabled", StorageManagerEnabled, STORAGEMANAGER_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.push.enabled", PushEnabled, PUSH_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.requestcontext.enabled", RequestContextEnabled, REQUESTCONTEXT_ENABLED)
|
||||
WORKER_SIMPLE_PREF("gfx.offscreencanvas.enabled", OffscreenCanvasEnabled, OFFSCREENCANVAS_ENABLED)
|
||||
WORKER_SIMPLE_PREF("dom.webkitBlink.dirPicker.enabled", WebkitBlinkDirectoryPickerEnabled, DOM_WEBKITBLINK_DIRPICKER_WEBKITBLINK)
|
||||
WORKER_PREF("dom.workers.latestJSVersion", JSVersionChanged)
|
||||
WORKER_PREF("intl.accept_languages", PrefLanguagesChanged)
|
||||
WORKER_PREF("general.appname.override", AppNameOverrideChanged)
|
||||
WORKER_PREF("general.appversion.override", AppVersionOverrideChanged)
|
||||
WORKER_PREF("general.platform.override", PlatformOverrideChanged)
|
||||
#ifdef JS_GC_ZEAL
|
||||
WORKER_PREF("dom.workers.options.gcZeal", LoadGCZealOptions)
|
||||
#endif
|
||||
6752
dom/workers/WorkerPrivate.cpp
Normal file
6752
dom/workers/WorkerPrivate.cpp
Normal file
File diff suppressed because it is too large
Load diff
1594
dom/workers/WorkerPrivate.h
Normal file
1594
dom/workers/WorkerPrivate.h
Normal file
File diff suppressed because it is too large
Load diff
777
dom/workers/WorkerRunnable.cpp
Normal file
777
dom/workers/WorkerRunnable.cpp
Normal file
|
|
@ -0,0 +1,777 @@
|
|||
/* -*- 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 "WorkerRunnable.h"
|
||||
|
||||
#include "nsGlobalWindow.h"
|
||||
#include "nsIEventTarget.h"
|
||||
#include "nsIGlobalObject.h"
|
||||
#include "nsIRunnable.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "mozilla/ErrorResult.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/Telemetry.h"
|
||||
|
||||
#include "js/RootingAPI.h"
|
||||
#include "js/Value.h"
|
||||
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerScope.h"
|
||||
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
namespace {
|
||||
|
||||
const nsIID kWorkerRunnableIID = {
|
||||
0x320cc0b5, 0xef12, 0x4084, { 0x88, 0x6e, 0xca, 0x6a, 0x81, 0xe4, 0x1d, 0x68 }
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
#ifdef DEBUG
|
||||
WorkerRunnable::WorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
|
||||
mCallingCancelWithinRun(false)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
}
|
||||
#endif
|
||||
|
||||
bool
|
||||
WorkerRunnable::IsDebuggerRunnable() const
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIGlobalObject*
|
||||
WorkerRunnable::DefaultGlobalObject() const
|
||||
{
|
||||
if (IsDebuggerRunnable()) {
|
||||
return mWorkerPrivate->DebuggerGlobalScope();
|
||||
} else {
|
||||
return mWorkerPrivate->GlobalScope();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
return aWorkerPrivate->ModifyBusyCount(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::Dispatch()
|
||||
{
|
||||
bool ok = PreDispatch(mWorkerPrivate);
|
||||
if (ok) {
|
||||
ok = DispatchInternal();
|
||||
}
|
||||
PostDispatch(mWorkerPrivate, ok);
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::DispatchInternal()
|
||||
{
|
||||
RefPtr<WorkerRunnable> runnable(this);
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
if (IsDebuggerRunnable()) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchDebuggerRunnable(runnable.forget()));
|
||||
} else {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->Dispatch(runnable.forget()));
|
||||
}
|
||||
}
|
||||
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
|
||||
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
|
||||
return NS_SUCCEEDED(parent->Dispatch(runnable.forget()));
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
#ifdef DEBUG
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (!aDispatchResult) {
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
aWorkerPrivate->ModifyBusyCount(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerRunnable::PreRun(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerRunnable::PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate,
|
||||
bool aRunResult)
|
||||
{
|
||||
MOZ_ASSERT(aCx);
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
|
||||
#ifdef DEBUG
|
||||
switch (mBehavior) {
|
||||
case ParentThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnParentThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadModifyBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
case WorkerThreadUnchangedBusyCount:
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
break;
|
||||
|
||||
default:
|
||||
MOZ_ASSERT_UNREACHABLE("Unknown behavior!");
|
||||
}
|
||||
#endif
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
aWorkerPrivate->ModifyBusyCountFromWorker(false);
|
||||
}
|
||||
}
|
||||
|
||||
// static
|
||||
WorkerRunnable*
|
||||
WorkerRunnable::FromRunnable(nsIRunnable* aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
|
||||
WorkerRunnable* runnable;
|
||||
nsresult rv = aRunnable->QueryInterface(kWorkerRunnableIID,
|
||||
reinterpret_cast<void**>(&runnable));
|
||||
if (NS_FAILED(rv)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(runnable);
|
||||
return runnable;
|
||||
}
|
||||
|
||||
NS_IMPL_ADDREF(WorkerRunnable)
|
||||
NS_IMPL_RELEASE(WorkerRunnable)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN(WorkerRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY(nsICancelableRunnable)
|
||||
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIRunnable)
|
||||
// kWorkerRunnableIID is special in that it does not AddRef its result.
|
||||
if (aIID.Equals(kWorkerRunnableIID)) {
|
||||
*aInstancePtr = this;
|
||||
return NS_OK;
|
||||
}
|
||||
else
|
||||
NS_INTERFACE_MAP_END
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerRunnable::Run()
|
||||
{
|
||||
bool targetIsWorkerThread = mBehavior == WorkerThreadModifyBusyCount ||
|
||||
mBehavior == WorkerThreadUnchangedBusyCount;
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT_IF(mCallingCancelWithinRun, targetIsWorkerThread);
|
||||
if (targetIsWorkerThread) {
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
else {
|
||||
MOZ_ASSERT(mBehavior == ParentThreadUnchangedBusyCount);
|
||||
mWorkerPrivate->AssertIsOnParentThread();
|
||||
}
|
||||
#endif
|
||||
|
||||
if (IsCanceled() && !mCallingCancelWithinRun) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
if (targetIsWorkerThread &&
|
||||
mWorkerPrivate->AllPendingRunnablesShouldBeCanceled() &&
|
||||
!IsCanceled() && !mCallingCancelWithinRun) {
|
||||
|
||||
// Prevent recursion.
|
||||
mCallingCancelWithinRun = true;
|
||||
|
||||
Cancel();
|
||||
|
||||
MOZ_ASSERT(mCallingCancelWithinRun);
|
||||
mCallingCancelWithinRun = false;
|
||||
|
||||
MOZ_ASSERT(IsCanceled(), "Subclass Cancel() didn't set IsCanceled()!");
|
||||
|
||||
if (mBehavior == WorkerThreadModifyBusyCount) {
|
||||
mWorkerPrivate->ModifyBusyCountFromWorker(false);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
bool result = PreRun(mWorkerPrivate);
|
||||
if (!result) {
|
||||
MOZ_ASSERT(targetIsWorkerThread,
|
||||
"The only PreRun implementation that can fail is "
|
||||
"ScriptExecutorRunnable");
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!JS_IsExceptionPending(mWorkerPrivate->GetJSContext()));
|
||||
// We can't enter a useful compartment on the JSContext here; just pass it
|
||||
// in as-is.
|
||||
PostRun(mWorkerPrivate->GetJSContext(), mWorkerPrivate, false);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
// Track down the appropriate global, if any, to use for the AutoEntryScript.
|
||||
nsCOMPtr<nsIGlobalObject> globalObject;
|
||||
bool isMainThread = !targetIsWorkerThread && !mWorkerPrivate->GetParent();
|
||||
MOZ_ASSERT(isMainThread == NS_IsMainThread());
|
||||
RefPtr<WorkerPrivate> kungFuDeathGrip;
|
||||
if (targetIsWorkerThread) {
|
||||
JSContext* cx = GetCurrentThreadJSContext();
|
||||
if (NS_WARN_IF(!cx)) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
JSObject* global = JS::CurrentGlobalOrNull(cx);
|
||||
if (global) {
|
||||
globalObject = xpc::NativeGlobal(global);
|
||||
} else {
|
||||
globalObject = DefaultGlobalObject();
|
||||
}
|
||||
|
||||
// We may still not have a globalObject here: in the case of
|
||||
// CompileScriptRunnable, we don't actually create the global object until
|
||||
// we have the script data, which happens in a syncloop under
|
||||
// CompileScriptRunnable::WorkerRun, so we can't assert that it got created
|
||||
// in the PreRun call above.
|
||||
} else {
|
||||
kungFuDeathGrip = mWorkerPrivate;
|
||||
if (isMainThread) {
|
||||
globalObject = nsGlobalWindow::Cast(mWorkerPrivate->GetWindow());
|
||||
} else {
|
||||
globalObject = mWorkerPrivate->GetParent()->GlobalScope();
|
||||
}
|
||||
}
|
||||
|
||||
// We might run script as part of WorkerRun, so we need an AutoEntryScript.
|
||||
// This is part of the HTML spec for workers at:
|
||||
// http://www.whatwg.org/specs/web-apps/current-work/#run-a-worker
|
||||
// If we don't have a globalObject we have to use an AutoJSAPI instead, but
|
||||
// this is OK as we won't be running script in these circumstances.
|
||||
Maybe<mozilla::dom::AutoJSAPI> maybeJSAPI;
|
||||
Maybe<mozilla::dom::AutoEntryScript> aes;
|
||||
JSContext* cx;
|
||||
AutoJSAPI* jsapi;
|
||||
if (globalObject) {
|
||||
aes.emplace(globalObject, "Worker runnable", isMainThread);
|
||||
jsapi = aes.ptr();
|
||||
cx = aes->cx();
|
||||
} else {
|
||||
maybeJSAPI.emplace();
|
||||
maybeJSAPI->Init();
|
||||
jsapi = maybeJSAPI.ptr();
|
||||
cx = jsapi->cx();
|
||||
}
|
||||
|
||||
// Note that we can't assert anything about mWorkerPrivate->GetWrapper()
|
||||
// existing, since it may in fact have been GCed (and we may be one of the
|
||||
// runnables cleaning up the worker as a result).
|
||||
|
||||
// If we are on the parent thread and that thread is not the main thread,
|
||||
// then we must be a dedicated worker (because there are no
|
||||
// Shared/ServiceWorkers whose parent is itself a worker) and then we
|
||||
// definitely have a globalObject. If it _is_ the main thread, globalObject
|
||||
// can be null for workers started from JSMs or other non-window contexts,
|
||||
// sadly.
|
||||
MOZ_ASSERT_IF(!targetIsWorkerThread && !isMainThread,
|
||||
mWorkerPrivate->IsDedicatedWorker() && globalObject);
|
||||
|
||||
// If we're on the parent thread we might be in a null compartment in the
|
||||
// situation described above when globalObject is null. Make sure to enter
|
||||
// the compartment of the worker's reflector if there is one. There might
|
||||
// not be one if we're just starting to compile the script for this worker.
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (!targetIsWorkerThread && mWorkerPrivate->GetWrapper()) {
|
||||
// If we're on the parent thread and have a reflector and a globalObject,
|
||||
// then the compartments of cx, globalObject, and the worker's reflector
|
||||
// should all match.
|
||||
MOZ_ASSERT_IF(globalObject,
|
||||
js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
|
||||
js::GetContextCompartment(cx));
|
||||
MOZ_ASSERT_IF(globalObject,
|
||||
js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
|
||||
js::GetObjectCompartment(globalObject->GetGlobalJSObject()));
|
||||
|
||||
// If we're on the parent thread and have a reflector, then our
|
||||
// JSContext had better be either in the null compartment (and hence
|
||||
// have no globalObject) or in the compartment of our reflector.
|
||||
MOZ_ASSERT(!js::GetContextCompartment(cx) ||
|
||||
js::GetObjectCompartment(mWorkerPrivate->GetWrapper()) ==
|
||||
js::GetContextCompartment(cx),
|
||||
"Must either be in the null compartment or in our reflector "
|
||||
"compartment");
|
||||
|
||||
ac.emplace(cx, mWorkerPrivate->GetWrapper());
|
||||
}
|
||||
|
||||
MOZ_ASSERT(!jsapi->HasException());
|
||||
result = WorkerRun(cx, mWorkerPrivate);
|
||||
MOZ_ASSERT_IF(result, !jsapi->HasException());
|
||||
jsapi->ReportException();
|
||||
|
||||
// We can't even assert that this didn't create our global, since in the case
|
||||
// of CompileScriptRunnable it _does_.
|
||||
|
||||
// It would be nice to avoid passing a JSContext to PostRun, but in the case
|
||||
// of ScriptExecutorRunnable we need to know the current compartment on the
|
||||
// JSContext (the one we set up based on the global returned from PreRun) so
|
||||
// that we can sanely do exception reporting. In particular, we want to make
|
||||
// sure that we do our JS_SetPendingException while still in that compartment,
|
||||
// because otherwise we might end up trying to create a cross-compartment
|
||||
// wrapper when we try to move the JS exception from our runnable's
|
||||
// ErrorResult to the JSContext, and that's not desirable in this case.
|
||||
//
|
||||
// We _could_ skip passing a JSContext here and then in
|
||||
// ScriptExecutorRunnable::PostRun end up grabbing it from the WorkerPrivate
|
||||
// and looking at its current compartment. But that seems like slightly weird
|
||||
// action-at-a-distance...
|
||||
//
|
||||
// In any case, we do NOT try to change the compartment on the JSContext at
|
||||
// this point; in the one case in which we could do that
|
||||
// (CompileScriptRunnable) it actually doesn't matter which compartment we're
|
||||
// in for PostRun.
|
||||
PostRun(cx, mWorkerPrivate, result);
|
||||
MOZ_ASSERT(!jsapi->HasException());
|
||||
|
||||
return result ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerRunnable::Cancel()
|
||||
{
|
||||
uint32_t canceledCount = ++mCanceled;
|
||||
|
||||
MOZ_ASSERT(canceledCount, "Cancel() overflow!");
|
||||
|
||||
// The docs say that Cancel() should not be called more than once and that we
|
||||
// should throw NS_ERROR_UNEXPECTED if it is.
|
||||
return (canceledCount == 1) ? NS_OK : NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mSyncLoopTarget(aSyncLoopTarget)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mSyncLoopTarget) {
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::WorkerSyncRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount),
|
||||
mSyncLoopTarget(aSyncLoopTarget)
|
||||
{
|
||||
#ifdef DEBUG
|
||||
if (mSyncLoopTarget) {
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
WorkerSyncRunnable::~WorkerSyncRunnable()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerSyncRunnable::DispatchInternal()
|
||||
{
|
||||
if (mSyncLoopTarget) {
|
||||
RefPtr<WorkerSyncRunnable> runnable(this);
|
||||
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
return WorkerRunnable::DispatchInternal();
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadWorkerSyncRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
}
|
||||
|
||||
MainThreadStopSyncLoopRunnable::MainThreadStopSyncLoopRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
|
||||
bool aResult)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget)), mResult(aResult)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
#ifdef DEBUG
|
||||
mWorkerPrivate->AssertValidSyncLoop(mSyncLoopTarget);
|
||||
#endif
|
||||
}
|
||||
|
||||
nsresult
|
||||
MainThreadStopSyncLoopRunnable::Cancel()
|
||||
{
|
||||
nsresult rv = Run();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Run() failed");
|
||||
|
||||
nsresult rv2 = WorkerSyncRunnable::Cancel();
|
||||
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv2), "Cancel() failed");
|
||||
|
||||
return NS_FAILED(rv) ? rv : rv2;
|
||||
}
|
||||
|
||||
bool
|
||||
MainThreadStopSyncLoopRunnable::WorkerRun(JSContext* aCx,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
nsCOMPtr<nsIEventTarget> syncLoopTarget;
|
||||
mSyncLoopTarget.swap(syncLoopTarget);
|
||||
|
||||
aWorkerPrivate->StopSyncLoop(syncLoopTarget, mResult);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
MainThreadStopSyncLoopRunnable::DispatchInternal()
|
||||
{
|
||||
MOZ_ASSERT(mSyncLoopTarget);
|
||||
|
||||
RefPtr<MainThreadStopSyncLoopRunnable> runnable(this);
|
||||
return NS_SUCCEEDED(mSyncLoopTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
|
||||
}
|
||||
|
||||
void
|
||||
MainThreadStopSyncLoopRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
WorkerControlRunnable::WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior)
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
MOZ_ASSERT(aBehavior == ParentThreadUnchangedBusyCount ||
|
||||
aBehavior == WorkerThreadUnchangedBusyCount,
|
||||
"WorkerControlRunnables should not modify the busy count");
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult
|
||||
WorkerControlRunnable::Cancel()
|
||||
{
|
||||
if (NS_FAILED(Run())) {
|
||||
NS_WARNING("WorkerControlRunnable::Run() failed.");
|
||||
}
|
||||
|
||||
return WorkerRunnable::Cancel();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerControlRunnable::DispatchInternal()
|
||||
{
|
||||
RefPtr<WorkerControlRunnable> runnable(this);
|
||||
|
||||
if (mBehavior == WorkerThreadUnchangedBusyCount) {
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchControlRunnable(runnable.forget()));
|
||||
}
|
||||
|
||||
if (WorkerPrivate* parent = mWorkerPrivate->GetParent()) {
|
||||
return NS_SUCCEEDED(parent->DispatchControlRunnable(runnable.forget()));
|
||||
}
|
||||
|
||||
return NS_SUCCEEDED(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerControlRunnable, WorkerRunnable)
|
||||
|
||||
WorkerMainThreadRunnable::WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsACString& aTelemetryKey)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
, mTelemetryKey(aTelemetryKey)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerMainThreadRunnable::Dispatch(ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
TimeStamp startTime = TimeStamp::NowLoRes();
|
||||
|
||||
AutoSyncLoopHolder syncLoop(mWorkerPrivate);
|
||||
|
||||
mSyncLoopTarget = syncLoop.EventTarget();
|
||||
|
||||
DebugOnly<nsresult> rv = mWorkerPrivate->DispatchToMainThread(this);
|
||||
MOZ_ASSERT(NS_SUCCEEDED(rv),
|
||||
"Should only fail after xpcom-shutdown-threads and we're gone by then");
|
||||
|
||||
if (!syncLoop.Run()) {
|
||||
aRv.ThrowUncatchableException();
|
||||
}
|
||||
|
||||
Telemetry::Accumulate(Telemetry::SYNC_WORKER_OPERATION, mTelemetryKey,
|
||||
static_cast<uint32_t>((TimeStamp::NowLoRes() - startTime)
|
||||
.ToMilliseconds()));
|
||||
Unused << startTime; // Shut the compiler up.
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerMainThreadRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
bool runResult = MainThreadRun();
|
||||
|
||||
RefPtr<MainThreadStopSyncLoopRunnable> response =
|
||||
new MainThreadStopSyncLoopRunnable(mWorkerPrivate,
|
||||
mSyncLoopTarget.forget(),
|
||||
runResult);
|
||||
|
||||
MOZ_ALWAYS_TRUE(response->Dispatch());
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WorkerCheckAPIExposureOnMainThreadRunnable::WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate):
|
||||
WorkerMainThreadRunnable(aWorkerPrivate,
|
||||
NS_LITERAL_CSTRING("WorkerCheckAPIExposureOnMainThread"))
|
||||
{}
|
||||
|
||||
WorkerCheckAPIExposureOnMainThreadRunnable::~WorkerCheckAPIExposureOnMainThreadRunnable()
|
||||
{}
|
||||
|
||||
bool
|
||||
WorkerCheckAPIExposureOnMainThreadRunnable::Dispatch()
|
||||
{
|
||||
ErrorResult rv;
|
||||
WorkerMainThreadRunnable::Dispatch(rv);
|
||||
bool ok = !rv.Failed();
|
||||
rv.SuppressException();
|
||||
return ok;
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerSameThreadRunnable::PreDispatch(WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
// We don't call WorkerRunnable::PreDispatch, because we're using
|
||||
// WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
|
||||
// that PreDispatch is on the parent thread in that case.
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerSameThreadRunnable::PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult)
|
||||
{
|
||||
// We don't call WorkerRunnable::PostDispatch, because we're using
|
||||
// WorkerThreadModifyBusyCount for mBehavior, and WorkerRunnable will assert
|
||||
// that PostDispatch is on the parent thread in that case.
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
if (aDispatchResult) {
|
||||
DebugOnly<bool> willIncrement = aWorkerPrivate->ModifyBusyCountFromWorker(true);
|
||||
// Should never fail since if this thread is still running, so should the
|
||||
// parent and it should be able to process a control runnable.
|
||||
MOZ_ASSERT(willIncrement);
|
||||
}
|
||||
}
|
||||
|
||||
WorkerProxyToMainThreadRunnable::WorkerProxyToMainThreadRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
WorkerProxyToMainThreadRunnable::~WorkerProxyToMainThreadRunnable()
|
||||
{}
|
||||
|
||||
bool
|
||||
WorkerProxyToMainThreadRunnable::Dispatch()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (NS_WARN_IF(!HoldWorker())) {
|
||||
RunBackOnWorkerThread();
|
||||
return false;
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(mWorkerPrivate->DispatchToMainThread(this)))) {
|
||||
ReleaseWorker();
|
||||
RunBackOnWorkerThread();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerProxyToMainThreadRunnable::Run()
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
RunOnMainThread();
|
||||
PostDispatchOnMainThread();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerProxyToMainThreadRunnable::PostDispatchOnMainThread()
|
||||
{
|
||||
class ReleaseRunnable final : public MainThreadWorkerControlRunnable
|
||||
{
|
||||
RefPtr<WorkerProxyToMainThreadRunnable> mRunnable;
|
||||
|
||||
public:
|
||||
ReleaseRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
WorkerProxyToMainThreadRunnable* aRunnable)
|
||||
: MainThreadWorkerControlRunnable(aWorkerPrivate)
|
||||
, mRunnable(aRunnable)
|
||||
{
|
||||
MOZ_ASSERT(aRunnable);
|
||||
}
|
||||
|
||||
// We must call RunBackOnWorkerThread() also if the runnable is canceled.
|
||||
nsresult
|
||||
Cancel() override
|
||||
{
|
||||
WorkerRun(nullptr, mWorkerPrivate);
|
||||
return MainThreadWorkerControlRunnable::Cancel();
|
||||
}
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, workers::WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mRunnable) {
|
||||
mRunnable->RunBackOnWorkerThread();
|
||||
|
||||
// Let's release the worker thread.
|
||||
mRunnable->ReleaseWorker();
|
||||
mRunnable = nullptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
~ReleaseRunnable()
|
||||
{}
|
||||
};
|
||||
|
||||
RefPtr<WorkerControlRunnable> runnable =
|
||||
new ReleaseRunnable(mWorkerPrivate, this);
|
||||
Unused << NS_WARN_IF(!runnable->Dispatch());
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerProxyToMainThreadRunnable::HoldWorker()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!mWorkerHolder);
|
||||
|
||||
class SimpleWorkerHolder final : public WorkerHolder
|
||||
{
|
||||
public:
|
||||
bool Notify(Status aStatus) override
|
||||
{
|
||||
// We don't care about the notification. We just want to keep the
|
||||
// mWorkerPrivate alive.
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
UniquePtr<WorkerHolder> workerHolder(new SimpleWorkerHolder());
|
||||
if (NS_WARN_IF(!workerHolder->HoldWorker(mWorkerPrivate, Canceling))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mWorkerHolder = Move(workerHolder);
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerProxyToMainThreadRunnable::ReleaseWorker()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerHolder);
|
||||
mWorkerHolder = nullptr;
|
||||
}
|
||||
509
dom/workers/WorkerRunnable.h
Normal file
509
dom/workers/WorkerRunnable.h
Normal file
|
|
@ -0,0 +1,509 @@
|
|||
/* -*- 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_workerrunnable_h__
|
||||
#define mozilla_dom_workers_workerrunnable_h__
|
||||
|
||||
#include "Workers.h"
|
||||
|
||||
#include "nsICancelableRunnable.h"
|
||||
|
||||
#include "mozilla/Atomics.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "nsThreadUtils.h" /* nsRunnable */
|
||||
|
||||
struct JSContext;
|
||||
class nsIEventTarget;
|
||||
|
||||
namespace mozilla {
|
||||
class ErrorResult;
|
||||
} // namespace mozilla
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerPrivate;
|
||||
|
||||
// Use this runnable to communicate from the worker to its parent or vice-versa.
|
||||
// The busy count must be taken into consideration and declared at construction
|
||||
// time.
|
||||
class WorkerRunnable : public nsIRunnable,
|
||||
public nsICancelableRunnable
|
||||
{
|
||||
public:
|
||||
enum TargetAndBusyBehavior {
|
||||
// Target the main thread for top-level workers, otherwise target the
|
||||
// WorkerThread of the worker's parent. No change to the busy count.
|
||||
ParentThreadUnchangedBusyCount,
|
||||
|
||||
// Target the thread where the worker event loop runs. The busy count will
|
||||
// be incremented before dispatching and decremented (asynchronously) after
|
||||
// running.
|
||||
WorkerThreadModifyBusyCount,
|
||||
|
||||
// Target the thread where the worker event loop runs. The busy count will
|
||||
// not be modified in any way. Besides worker-internal runnables this is
|
||||
// almost always the wrong choice.
|
||||
WorkerThreadUnchangedBusyCount
|
||||
};
|
||||
|
||||
protected:
|
||||
// The WorkerPrivate that this runnable is associated with.
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// See above.
|
||||
TargetAndBusyBehavior mBehavior;
|
||||
|
||||
// It's unclear whether or not Cancel() is supposed to work when called on any
|
||||
// thread. To be safe we're using an atomic but it's likely overkill.
|
||||
Atomic<uint32_t> mCanceled;
|
||||
|
||||
private:
|
||||
// Whether or not Cancel() is currently being called from inside the Run()
|
||||
// method. Avoids infinite recursion when a subclass calls Run() from inside
|
||||
// Cancel(). Only checked and modified on the target thread.
|
||||
bool mCallingCancelWithinRun;
|
||||
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
// If you override Cancel() then you'll need to either call the base class
|
||||
// Cancel() method or override IsCanceled() so that the Run() method bails out
|
||||
// appropriately.
|
||||
nsresult
|
||||
Cancel() override;
|
||||
|
||||
// The return value is true if and only if both PreDispatch and
|
||||
// DispatchInternal return true.
|
||||
bool
|
||||
Dispatch();
|
||||
|
||||
// See above note about Cancel().
|
||||
virtual bool
|
||||
IsCanceled() const
|
||||
{
|
||||
return mCanceled != 0;
|
||||
}
|
||||
|
||||
static WorkerRunnable*
|
||||
FromRunnable(nsIRunnable* aRunnable);
|
||||
|
||||
protected:
|
||||
WorkerRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior = WorkerThreadModifyBusyCount)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: mWorkerPrivate(aWorkerPrivate), mBehavior(aBehavior), mCanceled(0),
|
||||
mCallingCancelWithinRun(false)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
// This class is reference counted.
|
||||
virtual ~WorkerRunnable()
|
||||
{ }
|
||||
|
||||
// Returns true if this runnable should be dispatched to the debugger queue,
|
||||
// and false otherwise.
|
||||
virtual bool
|
||||
IsDebuggerRunnable() const;
|
||||
|
||||
nsIGlobalObject*
|
||||
DefaultGlobalObject() const;
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
// Also increments the busy count of |mWorkerPrivate| if targeting the
|
||||
// WorkerThread.
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// By default asserts that Dispatch() is being called on the right thread
|
||||
// (ParentThread if |mTarget| is WorkerThread, or WorkerThread otherwise).
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult);
|
||||
|
||||
// May be implemented by subclasses if desired if they need to do some sort of
|
||||
// setup before we try to set up our JSContext and compartment for real.
|
||||
// Typically the only thing that should go in here is creation of the worker's
|
||||
// global.
|
||||
//
|
||||
// If false is returned, WorkerRun will not be called at all. PostRun will
|
||||
// still be called, with false passed for aRunResult.
|
||||
virtual bool
|
||||
PreRun(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// Must be implemented by subclasses. Called on the target thread. The return
|
||||
// value will be passed to PostRun(). The JSContext passed in here comes from
|
||||
// an AutoJSAPI (or AutoEntryScript) that we set up on the stack. If
|
||||
// mBehavior is ParentThreadUnchangedBusyCount, it is in the compartment of
|
||||
// mWorkerPrivate's reflector (i.e. the worker object in the parent thread),
|
||||
// unless that reflector is null, in which case it's in the compartment of the
|
||||
// parent global (which is the compartment reflector would have been in), or
|
||||
// in the null compartment if there is no parent global. For other mBehavior
|
||||
// values, we're running on the worker thread and aCx is in whatever
|
||||
// compartment GetCurrentThreadJSContext() was in when nsIRunnable::Run() got
|
||||
// called. This is actually important for cases when a runnable spins a
|
||||
// syncloop and wants everything that happens during the syncloop to happen in
|
||||
// the compartment that runnable set up (which may, for example, be a debugger
|
||||
// sandbox compartment!). If aCx wasn't in a compartment to start with, aCx
|
||||
// will be in either the debugger global's compartment or the worker's
|
||||
// global's compartment depending on whether IsDebuggerRunnable() is true.
|
||||
//
|
||||
// Immediately after WorkerRun returns, the caller will assert that either it
|
||||
// returns false or there is no exception pending on aCx. Then it will report
|
||||
// any pending exceptions on aCx.
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) = 0;
|
||||
|
||||
// By default asserts that Run() (and WorkerRun()) were called on the correct
|
||||
// thread. Also sends an asynchronous message to the ParentThread if the
|
||||
// busy count was previously modified in PreDispatch().
|
||||
//
|
||||
// The aCx passed here is the same one as was passed to WorkerRun and is
|
||||
// still in the same compartment. PostRun implementations must NOT leave an
|
||||
// exception on the JSContext and must not run script, because the incoming
|
||||
// JSContext may be in the null compartment.
|
||||
virtual void
|
||||
PostRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate, bool aRunResult);
|
||||
|
||||
virtual bool
|
||||
DispatchInternal();
|
||||
|
||||
// Calling Run() directly is not supported. Just call Dispatch() and
|
||||
// WorkerRun() will be called on the correct thread automatically.
|
||||
NS_DECL_NSIRUNNABLE
|
||||
};
|
||||
|
||||
// This runnable is used to send a message to a worker debugger.
|
||||
class WorkerDebuggerRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
explicit WorkerDebuggerRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
{
|
||||
}
|
||||
|
||||
virtual ~WorkerDebuggerRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
IsDebuggerRunnable() const override
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override final
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override;
|
||||
};
|
||||
|
||||
// This runnable is used to send a message directly to a worker's sync loop.
|
||||
class WorkerSyncRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
|
||||
// Passing null for aSyncLoopTarget is allowed and will result in the behavior
|
||||
// of a normal WorkerRunnable.
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget);
|
||||
|
||||
WorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget);
|
||||
|
||||
virtual ~WorkerSyncRunnable();
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() override;
|
||||
};
|
||||
|
||||
// This runnable is identical to WorkerSyncRunnable except it is meant to be
|
||||
// created on and dispatched from the main thread only. Its WorkerRun/PostRun
|
||||
// will run on the worker thread.
|
||||
class MainThreadWorkerSyncRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
protected:
|
||||
// Passing null for aSyncLoopTarget is allowed and will result in the behavior
|
||||
// of a normal WorkerRunnable.
|
||||
MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
nsIEventTarget* aSyncLoopTarget)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, aSyncLoopTarget)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
MainThreadWorkerSyncRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget)
|
||||
: WorkerSyncRunnable(aWorkerPrivate, Move(aSyncLoopTarget))
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
virtual ~MainThreadWorkerSyncRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override;
|
||||
};
|
||||
|
||||
// This runnable is processed as soon as it is received by the worker,
|
||||
// potentially running before previously queued runnables and perhaps even with
|
||||
// other JS code executing on the stack. These runnables must not alter the
|
||||
// state of the JS runtime and should only twiddle state values. The busy count
|
||||
// is never modified.
|
||||
class WorkerControlRunnable : public WorkerRunnable
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
protected:
|
||||
WorkerControlRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
TargetAndBusyBehavior aBehavior = WorkerThreadModifyBusyCount)
|
||||
#ifdef DEBUG
|
||||
;
|
||||
#else
|
||||
: WorkerRunnable(aWorkerPrivate, aBehavior)
|
||||
{ }
|
||||
#endif
|
||||
|
||||
virtual ~WorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
nsresult
|
||||
Cancel() override;
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
DispatchInternal() override;
|
||||
|
||||
// Should only be called by WorkerPrivate::DoRunLoop.
|
||||
using WorkerRunnable::Cancel;
|
||||
};
|
||||
|
||||
// A convenience class for WorkerRunnables that are originated on the main
|
||||
// thread.
|
||||
class MainThreadWorkerRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
explicit MainThreadWorkerRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
virtual ~MainThreadWorkerRunnable()
|
||||
{}
|
||||
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate,
|
||||
bool aDispatchResult) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
|
||||
// A convenience class for WorkerControlRunnables that originate on the main
|
||||
// thread.
|
||||
class MainThreadWorkerControlRunnable : public WorkerControlRunnable
|
||||
{
|
||||
protected:
|
||||
explicit MainThreadWorkerControlRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerControlRunnable(aWorkerPrivate, WorkerThreadUnchangedBusyCount)
|
||||
{ }
|
||||
|
||||
virtual ~MainThreadWorkerControlRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
};
|
||||
|
||||
// A WorkerRunnable that should be dispatched from the worker to itself for
|
||||
// async tasks. This will increment the busy count PostDispatch() (only if
|
||||
// dispatch was successful) and decrement it in PostRun().
|
||||
//
|
||||
// Async tasks will almost always want to use this since
|
||||
// a WorkerSameThreadRunnable keeps the Worker from being GCed.
|
||||
class WorkerSameThreadRunnable : public WorkerRunnable
|
||||
{
|
||||
protected:
|
||||
explicit WorkerSameThreadRunnable(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerRunnable(aWorkerPrivate, WorkerThreadModifyBusyCount)
|
||||
{ }
|
||||
|
||||
virtual ~WorkerSameThreadRunnable()
|
||||
{ }
|
||||
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override;
|
||||
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override;
|
||||
|
||||
// We just delegate PostRun to WorkerRunnable, since it does exactly
|
||||
// what we want.
|
||||
};
|
||||
|
||||
// Base class for the runnable objects, which makes a synchronous call to
|
||||
// dispatch the tasks from the worker thread to the main thread.
|
||||
//
|
||||
// Note that the derived class must override MainThreadRun.
|
||||
class WorkerMainThreadRunnable : public Runnable
|
||||
{
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
nsCOMPtr<nsIEventTarget> mSyncLoopTarget;
|
||||
const nsCString mTelemetryKey;
|
||||
|
||||
explicit WorkerMainThreadRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
const nsACString& aTelemetryKey);
|
||||
~WorkerMainThreadRunnable() {}
|
||||
|
||||
virtual bool MainThreadRun() = 0;
|
||||
|
||||
public:
|
||||
// Dispatch the runnable to the main thread. If dispatch to main thread
|
||||
// fails, or if the worker is shut down while dispatching, an error will be
|
||||
// reported on aRv. In that case the error MUST be propagated out to script.
|
||||
void Dispatch(ErrorResult& aRv);
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run() override;
|
||||
};
|
||||
|
||||
// This runnable is an helper class for dispatching something from a worker
|
||||
// thread to the main-thread and back to the worker-thread. During this
|
||||
// operation, this class will keep the worker alive.
|
||||
class WorkerProxyToMainThreadRunnable : public Runnable
|
||||
{
|
||||
protected:
|
||||
explicit WorkerProxyToMainThreadRunnable(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
virtual ~WorkerProxyToMainThreadRunnable();
|
||||
|
||||
// First this method is called on the main-thread.
|
||||
virtual void RunOnMainThread() = 0;
|
||||
|
||||
// After this second method is called on the worker-thread.
|
||||
virtual void RunBackOnWorkerThread() = 0;
|
||||
|
||||
public:
|
||||
bool Dispatch();
|
||||
|
||||
private:
|
||||
NS_IMETHOD Run() override;
|
||||
|
||||
void PostDispatchOnMainThread();
|
||||
|
||||
bool HoldWorker();
|
||||
void ReleaseWorker();
|
||||
|
||||
protected:
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
UniquePtr<WorkerHolder> mWorkerHolder;
|
||||
};
|
||||
|
||||
// Class for checking API exposure. This totally violates the "MUST" in the
|
||||
// comments on WorkerMainThreadRunnable::Dispatch, because API exposure checks
|
||||
// can't throw. Maybe we should change it so they _could_ throw. But for now
|
||||
// we are bad people and should be ashamed of ourselves. Let's hope none of
|
||||
// them happen while a worker is shutting down.
|
||||
//
|
||||
// Do NOT copy what this class is doing elsewhere. Just don't.
|
||||
class WorkerCheckAPIExposureOnMainThreadRunnable
|
||||
: public WorkerMainThreadRunnable
|
||||
{
|
||||
public:
|
||||
explicit
|
||||
WorkerCheckAPIExposureOnMainThreadRunnable(WorkerPrivate* aWorkerPrivate);
|
||||
virtual
|
||||
~WorkerCheckAPIExposureOnMainThreadRunnable();
|
||||
|
||||
// Returns whether the dispatch succeeded. If this returns false, the API
|
||||
// should not be exposed.
|
||||
bool Dispatch();
|
||||
};
|
||||
|
||||
// This runnable is used to stop a sync loop and it's meant to be used on the
|
||||
// main-thread only. As sync loops keep the busy count incremented as long as
|
||||
// they run this runnable does not modify the busy count
|
||||
// in any way.
|
||||
class MainThreadStopSyncLoopRunnable : public WorkerSyncRunnable
|
||||
{
|
||||
bool mResult;
|
||||
|
||||
public:
|
||||
// Passing null for aSyncLoopTarget is not allowed.
|
||||
MainThreadStopSyncLoopRunnable(
|
||||
WorkerPrivate* aWorkerPrivate,
|
||||
already_AddRefed<nsIEventTarget>&& aSyncLoopTarget,
|
||||
bool aResult);
|
||||
|
||||
// By default StopSyncLoopRunnables cannot be canceled since they could leave
|
||||
// a sync loop spinning forever.
|
||||
nsresult
|
||||
Cancel() override;
|
||||
|
||||
protected:
|
||||
virtual ~MainThreadStopSyncLoopRunnable()
|
||||
{ }
|
||||
|
||||
private:
|
||||
virtual bool
|
||||
PreDispatch(WorkerPrivate* aWorkerPrivate) override final
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
return true;
|
||||
}
|
||||
|
||||
virtual void
|
||||
PostDispatch(WorkerPrivate* aWorkerPrivate, bool aDispatchResult) override;
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override;
|
||||
|
||||
virtual bool
|
||||
DispatchInternal() override final;
|
||||
};
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workerrunnable_h__
|
||||
978
dom/workers/WorkerScope.cpp
Normal file
978
dom/workers/WorkerScope.cpp
Normal file
|
|
@ -0,0 +1,978 @@
|
|||
/* -*- 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 "WorkerScope.h"
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/EventListenerManager.h"
|
||||
#include "mozilla/dom/BindingDeclarations.h"
|
||||
#include "mozilla/dom/Console.h"
|
||||
#include "mozilla/dom/DedicatedWorkerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/Fetch.h"
|
||||
#include "mozilla/dom/FunctionBinding.h"
|
||||
#include "mozilla/dom/IDBFactory.h"
|
||||
#include "mozilla/dom/ImageBitmap.h"
|
||||
#include "mozilla/dom/ImageBitmapBinding.h"
|
||||
#include "mozilla/dom/Performance.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
#include "mozilla/dom/PromiseWorkerProxy.h"
|
||||
#include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/SharedWorkerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/SimpleGlobalObject.h"
|
||||
#include "mozilla/dom/WorkerDebuggerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/WorkerGlobalScopeBinding.h"
|
||||
#include "mozilla/dom/WorkerLocation.h"
|
||||
#include "mozilla/dom/WorkerNavigator.h"
|
||||
#include "mozilla/dom/cache/CacheStorage.h"
|
||||
#include "mozilla/Services.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "nsIDocument.h"
|
||||
#include "nsIServiceWorkerManager.h"
|
||||
#include "nsIScriptTimeoutHandler.h"
|
||||
|
||||
#ifdef ANDROID
|
||||
#include <android/log.h>
|
||||
#endif
|
||||
|
||||
#include "Crypto.h"
|
||||
#include "Principal.h"
|
||||
#include "RuntimeService.h"
|
||||
#include "ScriptLoader.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
#include "ServiceWorkerClients.h"
|
||||
#include "ServiceWorkerManager.h"
|
||||
#include "ServiceWorkerRegistration.h"
|
||||
|
||||
#ifdef XP_WIN
|
||||
#undef PostMessage
|
||||
#endif
|
||||
|
||||
extern already_AddRefed<nsIScriptTimeoutHandler>
|
||||
NS_CreateJSTimeoutHandler(JSContext* aCx,
|
||||
mozilla::dom::workers::WorkerPrivate* aWorkerPrivate,
|
||||
mozilla::dom::Function& aFunction,
|
||||
const mozilla::dom::Sequence<JS::Value>& aArguments,
|
||||
mozilla::ErrorResult& aError);
|
||||
|
||||
extern already_AddRefed<nsIScriptTimeoutHandler>
|
||||
NS_CreateJSTimeoutHandler(JSContext* aCx,
|
||||
mozilla::dom::workers::WorkerPrivate* aWorkerPrivate,
|
||||
const nsAString& aExpression);
|
||||
|
||||
using namespace mozilla;
|
||||
using namespace mozilla::dom;
|
||||
USING_WORKERS_NAMESPACE
|
||||
|
||||
using mozilla::dom::cache::CacheStorage;
|
||||
using mozilla::ipc::PrincipalInfo;
|
||||
|
||||
WorkerGlobalScope::WorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
|
||||
: mWindowInteractionsAllowed(0)
|
||||
, mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
WorkerGlobalScope::~WorkerGlobalScope()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerGlobalScope)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCrypto)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mNavigator)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIndexedDB)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCacheStorage)
|
||||
tmp->TraverseHostObjectURIs(cb);
|
||||
tmp->mWorkerPrivate->TraverseTimeouts(cb);
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCrypto)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPerformance)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mLocation)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mNavigator)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIndexedDB)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mCacheStorage)
|
||||
tmp->UnlinkHostObjectURIs();
|
||||
tmp->mWorkerPrivate->UnlinkTimeouts();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(WorkerGlobalScope, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(WorkerGlobalScope, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerGlobalScope)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
|
||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
JSObject*
|
||||
WorkerGlobalScope::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
|
||||
{
|
||||
MOZ_CRASH("We should never get here!");
|
||||
}
|
||||
|
||||
Console*
|
||||
WorkerGlobalScope::GetConsole(ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!mConsole) {
|
||||
mConsole = Console::Create(nullptr, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return mConsole;
|
||||
}
|
||||
|
||||
Crypto*
|
||||
WorkerGlobalScope::GetCrypto(ErrorResult& aError)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!mCrypto) {
|
||||
mCrypto = new Crypto();
|
||||
mCrypto->Init(this);
|
||||
}
|
||||
|
||||
return mCrypto;
|
||||
}
|
||||
|
||||
already_AddRefed<CacheStorage>
|
||||
WorkerGlobalScope::GetCaches(ErrorResult& aRv)
|
||||
{
|
||||
if (!mCacheStorage) {
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mCacheStorage = CacheStorage::CreateOnWorker(cache::DEFAULT_NAMESPACE, this,
|
||||
mWorkerPrivate, aRv);
|
||||
}
|
||||
|
||||
RefPtr<CacheStorage> ref = mCacheStorage;
|
||||
return ref.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
WorkerGlobalScope::IsSecureContext() const
|
||||
{
|
||||
bool globalSecure =
|
||||
JS_GetIsSecureContext(js::GetObjectCompartment(GetWrapperPreserveColor()));
|
||||
MOZ_ASSERT(globalSecure == mWorkerPrivate->IsSecureContext());
|
||||
return globalSecure;
|
||||
}
|
||||
|
||||
already_AddRefed<WorkerLocation>
|
||||
WorkerGlobalScope::Location()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!mLocation) {
|
||||
WorkerPrivate::LocationInfo& info = mWorkerPrivate->GetLocationInfo();
|
||||
|
||||
mLocation = WorkerLocation::Create(info);
|
||||
MOZ_ASSERT(mLocation);
|
||||
}
|
||||
|
||||
RefPtr<WorkerLocation> location = mLocation;
|
||||
return location.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<WorkerNavigator>
|
||||
WorkerGlobalScope::Navigator()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!mNavigator) {
|
||||
mNavigator = WorkerNavigator::Create(mWorkerPrivate->OnLine());
|
||||
MOZ_ASSERT(mNavigator);
|
||||
}
|
||||
|
||||
RefPtr<WorkerNavigator> navigator = mNavigator;
|
||||
return navigator.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<WorkerNavigator>
|
||||
WorkerGlobalScope::GetExistingNavigator() const
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<WorkerNavigator> navigator = mNavigator;
|
||||
return navigator.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::Close(JSContext* aCx, ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (mWorkerPrivate->IsServiceWorker()) {
|
||||
aRv.Throw(NS_ERROR_DOM_INVALID_ACCESS_ERR);
|
||||
} else {
|
||||
mWorkerPrivate->CloseInternal(aCx);
|
||||
}
|
||||
}
|
||||
|
||||
OnErrorEventHandlerNonNull*
|
||||
WorkerGlobalScope::GetOnerror()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
EventListenerManager* elm = GetExistingListenerManager();
|
||||
return elm ? elm->GetOnErrorEventHandler() : nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::SetOnerror(OnErrorEventHandlerNonNull* aHandler)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
EventListenerManager* elm = GetOrCreateListenerManager();
|
||||
if (elm) {
|
||||
elm->SetEventHandler(aHandler);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::ImportScripts(const Sequence<nsString>& aScriptURLs,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
scriptloader::Load(mWorkerPrivate, aScriptURLs, WorkerScript, aRv);
|
||||
}
|
||||
|
||||
int32_t
|
||||
WorkerGlobalScope::SetTimeout(JSContext* aCx,
|
||||
Function& aHandler,
|
||||
const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& aArguments,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> handler =
|
||||
NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aArguments, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, false, aRv);
|
||||
}
|
||||
|
||||
int32_t
|
||||
WorkerGlobalScope::SetTimeout(JSContext* aCx,
|
||||
const nsAString& aHandler,
|
||||
const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& /* unused */,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> handler =
|
||||
NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler);
|
||||
return mWorkerPrivate->SetTimeout(aCx, handler, aTimeout, false, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::ClearTimeout(int32_t aHandle)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->ClearTimeout(aHandle);
|
||||
}
|
||||
|
||||
int32_t
|
||||
WorkerGlobalScope::SetInterval(JSContext* aCx,
|
||||
Function& aHandler,
|
||||
const Optional<int32_t>& aTimeout,
|
||||
const Sequence<JS::Value>& aArguments,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
bool isInterval = aTimeout.WasPassed();
|
||||
int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0;
|
||||
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> handler =
|
||||
NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler, aArguments, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return mWorkerPrivate->SetTimeout(aCx, handler, timeout, isInterval, aRv);
|
||||
}
|
||||
|
||||
int32_t
|
||||
WorkerGlobalScope::SetInterval(JSContext* aCx,
|
||||
const nsAString& aHandler,
|
||||
const Optional<int32_t>& aTimeout,
|
||||
const Sequence<JS::Value>& /* unused */,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Sequence<JS::Value> dummy;
|
||||
|
||||
bool isInterval = aTimeout.WasPassed();
|
||||
int32_t timeout = aTimeout.WasPassed() ? aTimeout.Value() : 0;
|
||||
|
||||
nsCOMPtr<nsIScriptTimeoutHandler> handler =
|
||||
NS_CreateJSTimeoutHandler(aCx, mWorkerPrivate, aHandler);
|
||||
return mWorkerPrivate->SetTimeout(aCx, handler, timeout, isInterval, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::ClearInterval(int32_t aHandle)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->ClearTimeout(aHandle);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
aRv = nsContentUtils::Atob(aAtob, aOutput);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
aRv = nsContentUtils::Btoa(aBtoa, aOutput);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerGlobalScope::Dump(const Optional<nsAString>& aString) const
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!aString.WasPassed()) {
|
||||
return;
|
||||
}
|
||||
|
||||
#if !(defined(DEBUG) || defined(MOZ_ENABLE_JS_DUMP))
|
||||
if (!mWorkerPrivate->DumpEnabled()) {
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
NS_ConvertUTF16toUTF8 str(aString.Value());
|
||||
|
||||
MOZ_LOG(nsContentUtils::DOMDumpLog(), LogLevel::Debug, ("[Worker.Dump] %s", str.get()));
|
||||
#ifdef ANDROID
|
||||
__android_log_print(ANDROID_LOG_INFO, "Gecko", "%s", str.get());
|
||||
#endif
|
||||
fputs(str.get(), stdout);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
Performance*
|
||||
WorkerGlobalScope::GetPerformance()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
if (!mPerformance) {
|
||||
mPerformance = Performance::CreateForWorker(mWorkerPrivate);
|
||||
}
|
||||
|
||||
return mPerformance;
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WorkerGlobalScope::Fetch(const RequestOrUSVString& aInput,
|
||||
const RequestInit& aInit, ErrorResult& aRv)
|
||||
{
|
||||
return FetchRequest(this, aInput, aInit, aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<IDBFactory>
|
||||
WorkerGlobalScope::GetIndexedDB(ErrorResult& aErrorResult)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<IDBFactory> indexedDB = mIndexedDB;
|
||||
|
||||
if (!indexedDB) {
|
||||
if (!mWorkerPrivate->IsStorageAllowed()) {
|
||||
NS_WARNING("IndexedDB is not allowed in this worker!");
|
||||
aErrorResult = NS_ERROR_DOM_SECURITY_ERR;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
JSContext* cx = mWorkerPrivate->GetJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
JS::Rooted<JSObject*> owningObject(cx, GetGlobalJSObject());
|
||||
MOZ_ASSERT(owningObject);
|
||||
|
||||
const PrincipalInfo& principalInfo = mWorkerPrivate->GetPrincipalInfo();
|
||||
|
||||
nsresult rv =
|
||||
IDBFactory::CreateForWorker(cx,
|
||||
owningObject,
|
||||
principalInfo,
|
||||
mWorkerPrivate->WindowID(),
|
||||
getter_AddRefs(indexedDB));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
aErrorResult = rv;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
mIndexedDB = indexedDB;
|
||||
}
|
||||
|
||||
return indexedDB.forget();
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ImageBitmap::Create(this, aImage, Nothing(), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<Promise>
|
||||
WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
|
||||
aRv.Throw(NS_ERROR_NOT_IMPLEMENTED);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return ImageBitmap::Create(this, aImage, Some(gfx::IntRect(aSx, aSy, aSw, aSh)), aRv);
|
||||
}
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise>
|
||||
WorkerGlobalScope::CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
int32_t aOffset, int32_t aLength,
|
||||
ImageBitmapFormat aFormat,
|
||||
const Sequence<ChannelPixelLayout>& aLayout,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
JSContext* cx = GetCurrentThreadJSContext();
|
||||
MOZ_ASSERT(cx);
|
||||
|
||||
if (!ImageBitmap::ExtensionsEnabled(cx, nullptr)) {
|
||||
aRv.Throw(NS_ERROR_TYPE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (aImage.IsArrayBuffer() || aImage.IsArrayBufferView()) {
|
||||
return ImageBitmap::Create(this, aImage, aOffset, aLength, aFormat, aLayout,
|
||||
aRv);
|
||||
} else {
|
||||
aRv.Throw(NS_ERROR_TYPE_ERR);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
DedicatedWorkerGlobalScope::DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate)
|
||||
: WorkerGlobalScope(aWorkerPrivate)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
DedicatedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(!mWorkerPrivate->IsSharedWorker());
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
mWorkerPrivate->CopyJSCompartmentOptions(options);
|
||||
|
||||
const bool usesSystemPrincipal = mWorkerPrivate->UsesSystemPrincipal();
|
||||
|
||||
// Note that xpc::ShouldDiscardSystemSource() and
|
||||
// xpc::ExtraWarningsForSystemJS() read prefs that are cached on the main
|
||||
// thread. This is benignly racey.
|
||||
const bool discardSource = usesSystemPrincipal &&
|
||||
xpc::ShouldDiscardSystemSource();
|
||||
const bool extraWarnings = usesSystemPrincipal &&
|
||||
xpc::ExtraWarningsForSystemJS();
|
||||
|
||||
JS::CompartmentBehaviors& behaviors = options.behaviors();
|
||||
behaviors.setDiscardSource(discardSource)
|
||||
.extraWarningsOverride().set(extraWarnings);
|
||||
|
||||
const bool sharedMemoryEnabled = xpc::SharedMemoryEnabled();
|
||||
|
||||
JS::CompartmentCreationOptions& creationOptions = options.creationOptions();
|
||||
creationOptions.setSharedMemoryAndAtomicsEnabled(sharedMemoryEnabled);
|
||||
|
||||
return DedicatedWorkerGlobalScopeBinding::Wrap(aCx, this, this,
|
||||
options,
|
||||
GetWorkerPrincipal(),
|
||||
true, aReflector);
|
||||
}
|
||||
|
||||
void
|
||||
DedicatedWorkerGlobalScope::PostMessage(JSContext* aCx,
|
||||
JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
mWorkerPrivate->PostMessageToParent(aCx, aMessage, aTransferable, aRv);
|
||||
}
|
||||
|
||||
SharedWorkerGlobalScope::SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
|
||||
const nsCString& aName)
|
||||
: WorkerGlobalScope(aWorkerPrivate), mName(aName)
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
SharedWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->IsSharedWorker());
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
mWorkerPrivate->CopyJSCompartmentOptions(options);
|
||||
|
||||
return SharedWorkerGlobalScopeBinding::Wrap(aCx, this, this, options,
|
||||
GetWorkerPrincipal(),
|
||||
true, aReflector);
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope,
|
||||
mClients, mRegistration)
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(ServiceWorkerGlobalScope)
|
||||
NS_INTERFACE_MAP_END_INHERITING(WorkerGlobalScope)
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
|
||||
NS_IMPL_RELEASE_INHERITED(ServiceWorkerGlobalScope, WorkerGlobalScope)
|
||||
|
||||
ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
|
||||
const nsACString& aScope)
|
||||
: WorkerGlobalScope(aWorkerPrivate),
|
||||
mScope(NS_ConvertUTF8toUTF16(aScope))
|
||||
{
|
||||
}
|
||||
|
||||
ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope()
|
||||
{
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
mWorkerPrivate->CopyJSCompartmentOptions(options);
|
||||
|
||||
return ServiceWorkerGlobalScopeBinding::Wrap(aCx, this, this, options,
|
||||
GetWorkerPrincipal(),
|
||||
true, aReflector);
|
||||
}
|
||||
|
||||
ServiceWorkerClients*
|
||||
ServiceWorkerGlobalScope::Clients()
|
||||
{
|
||||
if (!mClients) {
|
||||
mClients = new ServiceWorkerClients(this);
|
||||
}
|
||||
|
||||
return mClients;
|
||||
}
|
||||
|
||||
ServiceWorkerRegistration*
|
||||
ServiceWorkerGlobalScope::Registration()
|
||||
{
|
||||
if (!mRegistration) {
|
||||
mRegistration =
|
||||
ServiceWorkerRegistration::CreateForWorker(mWorkerPrivate, mScope);
|
||||
}
|
||||
|
||||
return mRegistration;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class SkipWaitingResultRunnable final : public WorkerRunnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
|
||||
public:
|
||||
SkipWaitingResultRunnable(WorkerPrivate* aWorkerPrivate,
|
||||
PromiseWorkerProxy* aPromiseProxy)
|
||||
: WorkerRunnable(aWorkerPrivate)
|
||||
, mPromiseProxy(aPromiseProxy)
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
}
|
||||
|
||||
virtual bool
|
||||
WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
RefPtr<Promise> promise = mPromiseProxy->WorkerPromise();
|
||||
promise->MaybeResolveWithUndefined();
|
||||
|
||||
// Release the reference on the worker thread.
|
||||
mPromiseProxy->CleanUp();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class WorkerScopeSkipWaitingRunnable final : public Runnable
|
||||
{
|
||||
RefPtr<PromiseWorkerProxy> mPromiseProxy;
|
||||
nsCString mScope;
|
||||
|
||||
public:
|
||||
WorkerScopeSkipWaitingRunnable(PromiseWorkerProxy* aPromiseProxy,
|
||||
const nsCString& aScope)
|
||||
: mPromiseProxy(aPromiseProxy)
|
||||
, mScope(aScope)
|
||||
{
|
||||
MOZ_ASSERT(aPromiseProxy);
|
||||
}
|
||||
|
||||
NS_IMETHOD
|
||||
Run() override
|
||||
{
|
||||
AssertIsOnMainThread();
|
||||
|
||||
MutexAutoLock lock(mPromiseProxy->Lock());
|
||||
if (mPromiseProxy->CleanedUp()) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
WorkerPrivate* workerPrivate = mPromiseProxy->GetWorkerPrivate();
|
||||
MOZ_DIAGNOSTIC_ASSERT(workerPrivate);
|
||||
|
||||
RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
|
||||
if (swm) {
|
||||
swm->SetSkipWaitingFlag(workerPrivate->GetPrincipal(), mScope,
|
||||
workerPrivate->ServiceWorkerID());
|
||||
}
|
||||
|
||||
RefPtr<SkipWaitingResultRunnable> runnable =
|
||||
new SkipWaitingResultRunnable(workerPrivate, mPromiseProxy);
|
||||
|
||||
if (!runnable->Dispatch()) {
|
||||
NS_WARNING("Failed to dispatch SkipWaitingResultRunnable to the worker.");
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
already_AddRefed<Promise>
|
||||
ServiceWorkerGlobalScope::SkipWaiting(ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
MOZ_ASSERT(mWorkerPrivate->IsServiceWorker());
|
||||
|
||||
RefPtr<Promise> promise = Promise::Create(this, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
RefPtr<PromiseWorkerProxy> promiseProxy =
|
||||
PromiseWorkerProxy::Create(mWorkerPrivate, promise);
|
||||
if (!promiseProxy) {
|
||||
promise->MaybeResolveWithUndefined();
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
RefPtr<WorkerScopeSkipWaitingRunnable> runnable =
|
||||
new WorkerScopeSkipWaitingRunnable(promiseProxy,
|
||||
NS_ConvertUTF16toUTF8(mScope));
|
||||
|
||||
MOZ_ALWAYS_SUCCEEDS(mWorkerPrivate->DispatchToMainThread(runnable.forget()));
|
||||
return promise.forget();
|
||||
}
|
||||
|
||||
bool
|
||||
ServiceWorkerGlobalScope::OpenWindowEnabled(JSContext* aCx, JSObject* aObj)
|
||||
{
|
||||
WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
|
||||
MOZ_ASSERT(worker);
|
||||
worker->AssertIsOnWorkerThread();
|
||||
return worker->OpenWindowEnabled();
|
||||
}
|
||||
|
||||
WorkerDebuggerGlobalScope::WorkerDebuggerGlobalScope(
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
WorkerDebuggerGlobalScope::~WorkerDebuggerGlobalScope()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerDebuggerGlobalScope)
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConsole)
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK(mConsole)
|
||||
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WorkerDebuggerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
tmp->mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
NS_IMPL_CYCLE_COLLECTION_TRACE_END
|
||||
|
||||
NS_IMPL_ADDREF_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper)
|
||||
NS_IMPL_RELEASE_INHERITED(WorkerDebuggerGlobalScope, DOMEventTargetHelper)
|
||||
|
||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WorkerDebuggerGlobalScope)
|
||||
NS_INTERFACE_MAP_ENTRY(nsIGlobalObject)
|
||||
NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
|
||||
|
||||
bool
|
||||
WorkerDebuggerGlobalScope::WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
JS::CompartmentOptions options;
|
||||
mWorkerPrivate->CopyJSCompartmentOptions(options);
|
||||
|
||||
return WorkerDebuggerGlobalScopeBinding::Wrap(aCx, this, this, options,
|
||||
GetWorkerPrincipal(), true,
|
||||
aReflector);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::GetGlobal(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aGlobal,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
|
||||
if (!scope) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
aGlobal.set(scope->GetWrapper());
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::CreateSandbox(JSContext* aCx, const nsAString& aName,
|
||||
JS::Handle<JSObject*> aPrototype,
|
||||
JS::MutableHandle<JSObject*> aResult,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
aResult.set(nullptr);
|
||||
|
||||
JS::Rooted<JS::Value> protoVal(aCx);
|
||||
protoVal.setObjectOrNull(aPrototype);
|
||||
JS::Rooted<JSObject*> sandbox(aCx,
|
||||
SimpleGlobalObject::Create(SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox,
|
||||
protoVal));
|
||||
|
||||
if (!sandbox) {
|
||||
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!JS_WrapObject(aCx, &sandbox)) {
|
||||
aRv.NoteJSContextException(aCx);
|
||||
return;
|
||||
}
|
||||
|
||||
aResult.set(sandbox);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::LoadSubScript(JSContext* aCx,
|
||||
const nsAString& aURL,
|
||||
const Optional<JS::Handle<JSObject*>>& aSandbox,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
Maybe<JSAutoCompartment> ac;
|
||||
if (aSandbox.WasPassed()) {
|
||||
JS::Rooted<JSObject*> sandbox(aCx, js::CheckedUnwrap(aSandbox.Value()));
|
||||
if (!IsDebuggerSandbox(sandbox)) {
|
||||
aRv.Throw(NS_ERROR_INVALID_ARG);
|
||||
return;
|
||||
}
|
||||
|
||||
ac.emplace(aCx, sandbox);
|
||||
}
|
||||
|
||||
nsTArray<nsString> urls;
|
||||
urls.AppendElement(aURL);
|
||||
scriptloader::Load(mWorkerPrivate, urls, DebuggerScript, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::EnterEventLoop()
|
||||
{
|
||||
mWorkerPrivate->EnterDebuggerEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::LeaveEventLoop()
|
||||
{
|
||||
mWorkerPrivate->LeaveDebuggerEventLoop();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::PostMessage(const nsAString& aMessage)
|
||||
{
|
||||
mWorkerPrivate->PostMessageToDebugger(aMessage);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::SetImmediate(Function& aHandler, ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->SetDebuggerImmediate(aHandler, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::ReportError(JSContext* aCx,
|
||||
const nsAString& aMessage)
|
||||
{
|
||||
JS::AutoFilename chars;
|
||||
uint32_t lineno = 0;
|
||||
JS::DescribeScriptedCaller(aCx, &chars, &lineno);
|
||||
nsString filename(NS_ConvertUTF8toUTF16(chars.get()));
|
||||
mWorkerPrivate->ReportErrorToDebugger(filename, lineno, aMessage);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::RetrieveConsoleEvents(JSContext* aCx,
|
||||
nsTArray<JS::Value>& aEvents,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
|
||||
if (!scope) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Console> console = scope->GetConsole(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
console->RetrieveConsoleEvents(aCx, aEvents, aRv);
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::SetConsoleEventHandler(JSContext* aCx,
|
||||
AnyCallback* aHandler,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
|
||||
if (!scope) {
|
||||
aRv.Throw(NS_ERROR_FAILURE);
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<Console> console = scope->GetConsole(aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return;
|
||||
}
|
||||
|
||||
console->SetConsoleEventHandler(aHandler);
|
||||
}
|
||||
|
||||
Console*
|
||||
WorkerDebuggerGlobalScope::GetConsole(ErrorResult& aRv)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
// Debugger console has its own console object.
|
||||
if (!mConsole) {
|
||||
mConsole = Console::Create(nullptr, aRv);
|
||||
if (NS_WARN_IF(aRv.Failed())) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return mConsole;
|
||||
}
|
||||
|
||||
void
|
||||
WorkerDebuggerGlobalScope::Dump(JSContext* aCx,
|
||||
const Optional<nsAString>& aString) const
|
||||
{
|
||||
WorkerGlobalScope* scope = mWorkerPrivate->GetOrCreateGlobalScope(aCx);
|
||||
if (scope) {
|
||||
scope->Dump(aString);
|
||||
}
|
||||
}
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
bool
|
||||
IsWorkerGlobal(JSObject* object)
|
||||
{
|
||||
return IS_INSTANCE_OF(WorkerGlobalScope, object);
|
||||
}
|
||||
|
||||
bool
|
||||
IsDebuggerGlobal(JSObject* object)
|
||||
{
|
||||
return IS_INSTANCE_OF(WorkerDebuggerGlobalScope, object);
|
||||
}
|
||||
|
||||
bool
|
||||
IsDebuggerSandbox(JSObject* object)
|
||||
{
|
||||
return SimpleGlobalObject::SimpleGlobalType(object) ==
|
||||
SimpleGlobalObject::GlobalType::WorkerDebuggerSandbox;
|
||||
}
|
||||
|
||||
bool
|
||||
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp)
|
||||
{
|
||||
JS_ReportErrorNumberASCII(aCx, js::GetErrorMessage, nullptr, JSMSG_GETTER_ONLY);
|
||||
return false;
|
||||
}
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
389
dom/workers/WorkerScope.h
Normal file
389
dom/workers/WorkerScope.h
Normal file
|
|
@ -0,0 +1,389 @@
|
|||
/* -*- 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_workerscope_h__
|
||||
#define mozilla_dom_workerscope_h__
|
||||
|
||||
#include "Workers.h"
|
||||
#include "mozilla/DOMEventTargetHelper.h"
|
||||
#include "mozilla/dom/Headers.h"
|
||||
#include "mozilla/dom/RequestBinding.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "mozilla/dom/ImageBitmapSource.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
class AnyCallback;
|
||||
struct ChannelPixelLayout;
|
||||
class Console;
|
||||
class Crypto;
|
||||
class Function;
|
||||
class IDBFactory;
|
||||
enum class ImageBitmapFormat : uint32_t;
|
||||
class Performance;
|
||||
class Promise;
|
||||
class RequestOrUSVString;
|
||||
class ServiceWorkerRegistration;
|
||||
class WorkerLocation;
|
||||
class WorkerNavigator;
|
||||
|
||||
namespace cache {
|
||||
|
||||
class CacheStorage;
|
||||
|
||||
} // namespace cache
|
||||
|
||||
namespace workers {
|
||||
|
||||
class ServiceWorkerClients;
|
||||
class WorkerPrivate;
|
||||
|
||||
} // namespace workers
|
||||
|
||||
class WorkerGlobalScope : public DOMEventTargetHelper,
|
||||
public nsIGlobalObject,
|
||||
public nsSupportsWeakReference
|
||||
{
|
||||
typedef mozilla::dom::IDBFactory IDBFactory;
|
||||
|
||||
RefPtr<Console> mConsole;
|
||||
RefPtr<Crypto> mCrypto;
|
||||
RefPtr<WorkerLocation> mLocation;
|
||||
RefPtr<WorkerNavigator> mNavigator;
|
||||
RefPtr<Performance> mPerformance;
|
||||
RefPtr<IDBFactory> mIndexedDB;
|
||||
RefPtr<cache::CacheStorage> mCacheStorage;
|
||||
|
||||
uint32_t mWindowInteractionsAllowed;
|
||||
|
||||
protected:
|
||||
typedef mozilla::dom::workers::WorkerPrivate WorkerPrivate;
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
explicit WorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
|
||||
virtual ~WorkerGlobalScope();
|
||||
|
||||
public:
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
|
||||
|
||||
virtual bool
|
||||
WrapGlobalObject(JSContext* aCx, JS::MutableHandle<JSObject*> aReflector) = 0;
|
||||
|
||||
virtual JSObject*
|
||||
GetGlobalJSObject(void) override
|
||||
{
|
||||
return GetWrapper();
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
WorkerGlobalScope*
|
||||
Self()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
Console*
|
||||
GetConsole(ErrorResult& aRv);
|
||||
|
||||
Console*
|
||||
GetConsoleIfExists() const
|
||||
{
|
||||
return mConsole;
|
||||
}
|
||||
|
||||
Crypto*
|
||||
GetCrypto(ErrorResult& aError);
|
||||
|
||||
already_AddRefed<WorkerLocation>
|
||||
Location();
|
||||
|
||||
already_AddRefed<WorkerNavigator>
|
||||
Navigator();
|
||||
|
||||
already_AddRefed<WorkerNavigator>
|
||||
GetExistingNavigator() const;
|
||||
|
||||
void
|
||||
Close(JSContext* aCx, ErrorResult& aRv);
|
||||
|
||||
OnErrorEventHandlerNonNull*
|
||||
GetOnerror();
|
||||
void
|
||||
SetOnerror(OnErrorEventHandlerNonNull* aHandler);
|
||||
|
||||
void
|
||||
ImportScripts(const Sequence<nsString>& aScriptURLs, ErrorResult& aRv);
|
||||
|
||||
int32_t
|
||||
SetTimeout(JSContext* aCx, Function& aHandler, const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& aArguments, ErrorResult& aRv);
|
||||
int32_t
|
||||
SetTimeout(JSContext* aCx, const nsAString& aHandler, const int32_t aTimeout,
|
||||
const Sequence<JS::Value>& /* unused */, ErrorResult& aRv);
|
||||
void
|
||||
ClearTimeout(int32_t aHandle);
|
||||
int32_t
|
||||
SetInterval(JSContext* aCx, Function& aHandler,
|
||||
const Optional<int32_t>& aTimeout,
|
||||
const Sequence<JS::Value>& aArguments, ErrorResult& aRv);
|
||||
int32_t
|
||||
SetInterval(JSContext* aCx, const nsAString& aHandler,
|
||||
const Optional<int32_t>& aTimeout,
|
||||
const Sequence<JS::Value>& /* unused */, ErrorResult& aRv);
|
||||
void
|
||||
ClearInterval(int32_t aHandle);
|
||||
|
||||
void
|
||||
Atob(const nsAString& aAtob, nsAString& aOutput, ErrorResult& aRv) const;
|
||||
void
|
||||
Btoa(const nsAString& aBtoa, nsAString& aOutput, ErrorResult& aRv) const;
|
||||
|
||||
IMPL_EVENT_HANDLER(online)
|
||||
IMPL_EVENT_HANDLER(offline)
|
||||
|
||||
void
|
||||
Dump(const Optional<nsAString>& aString) const;
|
||||
|
||||
Performance* GetPerformance();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
Fetch(const RequestOrUSVString& aInput, const RequestInit& aInit, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<IDBFactory>
|
||||
GetIndexedDB(ErrorResult& aErrorResult);
|
||||
|
||||
already_AddRefed<cache::CacheStorage>
|
||||
GetCaches(ErrorResult& aRv);
|
||||
|
||||
bool IsSecureContext() const;
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateImageBitmap(const ImageBitmapSource& aImage, ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<Promise>
|
||||
CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
int32_t aSx, int32_t aSy, int32_t aSw, int32_t aSh,
|
||||
ErrorResult& aRv);
|
||||
|
||||
already_AddRefed<mozilla::dom::Promise>
|
||||
CreateImageBitmap(const ImageBitmapSource& aImage,
|
||||
int32_t aOffset, int32_t aLength,
|
||||
mozilla::dom::ImageBitmapFormat aFormat,
|
||||
const mozilla::dom::Sequence<mozilla::dom::ChannelPixelLayout>& aLayout,
|
||||
mozilla::ErrorResult& aRv);
|
||||
|
||||
bool
|
||||
WindowInteractionAllowed() const
|
||||
{
|
||||
return mWindowInteractionsAllowed > 0;
|
||||
}
|
||||
|
||||
void
|
||||
AllowWindowInteraction()
|
||||
{
|
||||
mWindowInteractionsAllowed++;
|
||||
}
|
||||
|
||||
void
|
||||
ConsumeWindowInteraction()
|
||||
{
|
||||
MOZ_ASSERT(mWindowInteractionsAllowed > 0);
|
||||
mWindowInteractionsAllowed--;
|
||||
}
|
||||
};
|
||||
|
||||
class DedicatedWorkerGlobalScope final : public WorkerGlobalScope
|
||||
{
|
||||
~DedicatedWorkerGlobalScope() { }
|
||||
|
||||
public:
|
||||
explicit DedicatedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
virtual bool
|
||||
WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector) override;
|
||||
|
||||
void
|
||||
PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
|
||||
const Optional<Sequence<JS::Value>>& aTransferable,
|
||||
ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
};
|
||||
|
||||
class SharedWorkerGlobalScope final : public WorkerGlobalScope
|
||||
{
|
||||
const nsCString mName;
|
||||
|
||||
~SharedWorkerGlobalScope() { }
|
||||
|
||||
public:
|
||||
SharedWorkerGlobalScope(WorkerPrivate* aWorkerPrivate,
|
||||
const nsCString& aName);
|
||||
|
||||
virtual bool
|
||||
WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector) override;
|
||||
|
||||
void GetName(DOMString& aName) const
|
||||
{
|
||||
aName.AsAString() = NS_ConvertUTF8toUTF16(mName);
|
||||
}
|
||||
|
||||
IMPL_EVENT_HANDLER(connect)
|
||||
};
|
||||
|
||||
class ServiceWorkerGlobalScope final : public WorkerGlobalScope
|
||||
{
|
||||
const nsString mScope;
|
||||
RefPtr<workers::ServiceWorkerClients> mClients;
|
||||
RefPtr<ServiceWorkerRegistration> mRegistration;
|
||||
|
||||
~ServiceWorkerGlobalScope();
|
||||
|
||||
public:
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(ServiceWorkerGlobalScope,
|
||||
WorkerGlobalScope)
|
||||
IMPL_EVENT_HANDLER(notificationclick)
|
||||
IMPL_EVENT_HANDLER(notificationclose)
|
||||
|
||||
ServiceWorkerGlobalScope(WorkerPrivate* aWorkerPrivate, const nsACString& aScope);
|
||||
|
||||
virtual bool
|
||||
WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector) override;
|
||||
|
||||
static bool
|
||||
OpenWindowEnabled(JSContext* aCx, JSObject* aObj);
|
||||
|
||||
void
|
||||
GetScope(nsString& aScope) const
|
||||
{
|
||||
aScope = mScope;
|
||||
}
|
||||
|
||||
workers::ServiceWorkerClients*
|
||||
Clients();
|
||||
|
||||
ServiceWorkerRegistration*
|
||||
Registration();
|
||||
|
||||
already_AddRefed<Promise>
|
||||
SkipWaiting(ErrorResult& aRv);
|
||||
|
||||
IMPL_EVENT_HANDLER(activate)
|
||||
IMPL_EVENT_HANDLER(fetch)
|
||||
IMPL_EVENT_HANDLER(install)
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
|
||||
IMPL_EVENT_HANDLER(push)
|
||||
IMPL_EVENT_HANDLER(pushsubscriptionchange)
|
||||
|
||||
};
|
||||
|
||||
class WorkerDebuggerGlobalScope final : public DOMEventTargetHelper,
|
||||
public nsIGlobalObject
|
||||
{
|
||||
typedef mozilla::dom::workers::WorkerPrivate WorkerPrivate;
|
||||
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
RefPtr<Console> mConsole;
|
||||
|
||||
public:
|
||||
explicit WorkerDebuggerGlobalScope(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_INHERITED(WorkerDebuggerGlobalScope,
|
||||
DOMEventTargetHelper)
|
||||
|
||||
virtual JSObject*
|
||||
WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override
|
||||
{
|
||||
MOZ_CRASH("Shouldn't get here!");
|
||||
}
|
||||
|
||||
virtual bool
|
||||
WrapGlobalObject(JSContext* aCx,
|
||||
JS::MutableHandle<JSObject*> aReflector);
|
||||
|
||||
virtual JSObject*
|
||||
GetGlobalJSObject(void) override
|
||||
{
|
||||
return GetWrapper();
|
||||
}
|
||||
|
||||
void
|
||||
GetGlobal(JSContext* aCx, JS::MutableHandle<JSObject*> aGlobal,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
CreateSandbox(JSContext* aCx, const nsAString& aName,
|
||||
JS::Handle<JSObject*> aPrototype,
|
||||
JS::MutableHandle<JSObject*> aResult,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
LoadSubScript(JSContext* aCx, const nsAString& aURL,
|
||||
const Optional<JS::Handle<JSObject*>>& aSandbox,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
EnterEventLoop();
|
||||
|
||||
void
|
||||
LeaveEventLoop();
|
||||
|
||||
void
|
||||
PostMessage(const nsAString& aMessage);
|
||||
|
||||
IMPL_EVENT_HANDLER(message)
|
||||
|
||||
void
|
||||
SetImmediate(Function& aHandler, ErrorResult& aRv);
|
||||
|
||||
void
|
||||
ReportError(JSContext* aCx, const nsAString& aMessage);
|
||||
|
||||
void
|
||||
RetrieveConsoleEvents(JSContext* aCx, nsTArray<JS::Value>& aEvents,
|
||||
ErrorResult& aRv);
|
||||
|
||||
void
|
||||
SetConsoleEventHandler(JSContext* aCx, AnyCallback* aHandler,
|
||||
ErrorResult& aRv);
|
||||
|
||||
Console*
|
||||
GetConsole(ErrorResult& aRv);
|
||||
|
||||
Console*
|
||||
GetConsoleIfExists() const
|
||||
{
|
||||
return mConsole;
|
||||
}
|
||||
|
||||
void
|
||||
Dump(JSContext* aCx, const Optional<nsAString>& aString) const;
|
||||
|
||||
private:
|
||||
virtual ~WorkerDebuggerGlobalScope();
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
inline nsISupports*
|
||||
ToSupports(mozilla::dom::WorkerGlobalScope* aScope)
|
||||
{
|
||||
return static_cast<nsIDOMEventTarget*>(aScope);
|
||||
}
|
||||
|
||||
#endif /* mozilla_dom_workerscope_h__ */
|
||||
355
dom/workers/WorkerThread.cpp
Normal file
355
dom/workers/WorkerThread.cpp
Normal file
|
|
@ -0,0 +1,355 @@
|
|||
/* -*- 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 "WorkerThread.h"
|
||||
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/ipc/BackgroundChild.h"
|
||||
#include "nsIThreadInternal.h"
|
||||
#include "WorkerPrivate.h"
|
||||
#include "WorkerRunnable.h"
|
||||
|
||||
#ifdef DEBUG
|
||||
#include "nsThreadManager.h"
|
||||
#endif
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
using namespace mozilla::ipc;
|
||||
|
||||
namespace {
|
||||
|
||||
// The C stack size. We use the same stack size on all platforms for
|
||||
// consistency.
|
||||
const uint32_t kWorkerStackSize = 256 * sizeof(size_t) * 1024;
|
||||
|
||||
} // namespace
|
||||
|
||||
WorkerThreadFriendKey::WorkerThreadFriendKey()
|
||||
{
|
||||
MOZ_COUNT_CTOR(WorkerThreadFriendKey);
|
||||
}
|
||||
|
||||
WorkerThreadFriendKey::~WorkerThreadFriendKey()
|
||||
{
|
||||
MOZ_COUNT_DTOR(WorkerThreadFriendKey);
|
||||
}
|
||||
|
||||
class WorkerThread::Observer final
|
||||
: public nsIThreadObserver
|
||||
{
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
public:
|
||||
explicit Observer(WorkerPrivate* aWorkerPrivate)
|
||||
: mWorkerPrivate(aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(aWorkerPrivate);
|
||||
aWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
|
||||
private:
|
||||
~Observer()
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
};
|
||||
|
||||
WorkerThread::WorkerThread()
|
||||
: nsThread(nsThread::NOT_MAIN_THREAD, kWorkerStackSize)
|
||||
, mWorkerPrivateCondVar(mLock, "WorkerThread::mWorkerPrivateCondVar")
|
||||
, mWorkerPrivate(nullptr)
|
||||
, mOtherThreadsDispatchingViaEventTarget(0)
|
||||
#ifdef DEBUG
|
||||
, mAcceptingNonWorkerRunnables(true)
|
||||
#endif
|
||||
{
|
||||
}
|
||||
|
||||
WorkerThread::~WorkerThread()
|
||||
{
|
||||
MOZ_ASSERT(!mWorkerPrivate);
|
||||
MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget);
|
||||
MOZ_ASSERT(mAcceptingNonWorkerRunnables);
|
||||
}
|
||||
|
||||
// static
|
||||
already_AddRefed<WorkerThread>
|
||||
WorkerThread::Create(const WorkerThreadFriendKey& /* aKey */)
|
||||
{
|
||||
RefPtr<WorkerThread> thread = new WorkerThread();
|
||||
if (NS_FAILED(thread->Init())) {
|
||||
NS_WARNING("Failed to create new thread!");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return thread.forget();
|
||||
}
|
||||
|
||||
void
|
||||
WorkerThread::SetWorker(const WorkerThreadFriendKey& /* aKey */,
|
||||
WorkerPrivate* aWorkerPrivate)
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
|
||||
|
||||
if (aWorkerPrivate) {
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(!mWorkerPrivate);
|
||||
MOZ_ASSERT(mAcceptingNonWorkerRunnables);
|
||||
|
||||
mWorkerPrivate = aWorkerPrivate;
|
||||
#ifdef DEBUG
|
||||
mAcceptingNonWorkerRunnables = false;
|
||||
#endif
|
||||
}
|
||||
|
||||
mObserver = new Observer(aWorkerPrivate);
|
||||
MOZ_ALWAYS_SUCCEEDS(AddObserver(mObserver));
|
||||
} else {
|
||||
MOZ_ALWAYS_SUCCEEDS(RemoveObserver(mObserver));
|
||||
mObserver = nullptr;
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
|
||||
MOZ_ASSERT(!mOtherThreadsDispatchingViaEventTarget,
|
||||
"XPCOM Dispatch hapenning at the same time our thread is "
|
||||
"being unset! This should not be possible!");
|
||||
|
||||
while (mOtherThreadsDispatchingViaEventTarget) {
|
||||
mWorkerPrivateCondVar.Wait();
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
mAcceptingNonWorkerRunnables = true;
|
||||
#endif
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerThread::DispatchPrimaryRunnable(const WorkerThreadFriendKey& /* aKey */,
|
||||
already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
||||
|
||||
#ifdef DEBUG
|
||||
MOZ_ASSERT(PR_GetCurrentThread() != mThread);
|
||||
MOZ_ASSERT(runnable);
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(!mWorkerPrivate);
|
||||
MOZ_ASSERT(mAcceptingNonWorkerRunnables);
|
||||
}
|
||||
#endif
|
||||
|
||||
nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
WorkerThread::DispatchAnyThread(const WorkerThreadFriendKey& /* aKey */,
|
||||
already_AddRefed<WorkerRunnable> aWorkerRunnable)
|
||||
{
|
||||
// May be called on any thread!
|
||||
|
||||
#ifdef DEBUG
|
||||
{
|
||||
const bool onWorkerThread = PR_GetCurrentThread() == mThread;
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
MOZ_ASSERT(!mAcceptingNonWorkerRunnables);
|
||||
|
||||
if (onWorkerThread) {
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
nsCOMPtr<nsIRunnable> runnable(aWorkerRunnable);
|
||||
|
||||
nsresult rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
// We don't need to notify the worker's condition variable here because we're
|
||||
// being called from worker-controlled code and it will make sure to wake up
|
||||
// the worker thread if needed.
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS_INHERITED0(WorkerThread, nsThread)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThread::DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable);
|
||||
return Dispatch(runnable.forget(), aFlags);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThread::Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags)
|
||||
{
|
||||
// May be called on any thread!
|
||||
nsCOMPtr<nsIRunnable> runnable(aRunnable); // in case we exit early
|
||||
|
||||
// Workers only support asynchronous dispatch.
|
||||
if (NS_WARN_IF(aFlags != NS_DISPATCH_NORMAL)) {
|
||||
return NS_ERROR_UNEXPECTED;
|
||||
}
|
||||
|
||||
const bool onWorkerThread = PR_GetCurrentThread() == mThread;
|
||||
|
||||
#ifdef DEBUG
|
||||
if (runnable && !onWorkerThread) {
|
||||
nsCOMPtr<nsICancelableRunnable> cancelable = do_QueryInterface(runnable);
|
||||
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
// Only enforce cancelable runnables after we've started the worker loop.
|
||||
if (!mAcceptingNonWorkerRunnables) {
|
||||
MOZ_ASSERT(cancelable,
|
||||
"Only nsICancelableRunnable may be dispatched to a worker!");
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
WorkerPrivate* workerPrivate = nullptr;
|
||||
if (onWorkerThread) {
|
||||
// No need to lock here because it is only modified on this thread.
|
||||
MOZ_ASSERT(mWorkerPrivate);
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
workerPrivate = mWorkerPrivate;
|
||||
} else {
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget < UINT32_MAX);
|
||||
|
||||
if (mWorkerPrivate) {
|
||||
workerPrivate = mWorkerPrivate;
|
||||
|
||||
// Incrementing this counter will make the worker thread sleep if it
|
||||
// somehow tries to unset mWorkerPrivate while we're using it.
|
||||
mOtherThreadsDispatchingViaEventTarget++;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult rv;
|
||||
if (runnable && onWorkerThread) {
|
||||
RefPtr<WorkerRunnable> workerRunnable = workerPrivate->MaybeWrapAsWorkerRunnable(runnable.forget());
|
||||
rv = nsThread::Dispatch(workerRunnable.forget(), NS_DISPATCH_NORMAL);
|
||||
} else {
|
||||
rv = nsThread::Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
|
||||
}
|
||||
|
||||
if (!onWorkerThread && workerPrivate) {
|
||||
// We need to wake the worker thread if we're not already on the right
|
||||
// thread and the dispatch succeeded.
|
||||
if (NS_SUCCEEDED(rv)) {
|
||||
MutexAutoLock workerLock(workerPrivate->mMutex);
|
||||
|
||||
workerPrivate->mCondVar.Notify();
|
||||
}
|
||||
|
||||
// Now unset our waiting flag.
|
||||
{
|
||||
MutexAutoLock lock(mLock);
|
||||
|
||||
MOZ_ASSERT(mOtherThreadsDispatchingViaEventTarget);
|
||||
|
||||
if (!--mOtherThreadsDispatchingViaEventTarget) {
|
||||
mWorkerPrivateCondVar.Notify();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThread::DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t)
|
||||
{
|
||||
return NS_ERROR_NOT_IMPLEMENTED;
|
||||
}
|
||||
|
||||
uint32_t
|
||||
WorkerThread::RecursionDepth(const WorkerThreadFriendKey& /* aKey */) const
|
||||
{
|
||||
MOZ_ASSERT(PR_GetCurrentThread() == mThread);
|
||||
|
||||
return mNestedEventLoopDepth;
|
||||
}
|
||||
|
||||
NS_IMPL_ISUPPORTS(WorkerThread::Observer, nsIThreadObserver)
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThread::Observer::OnDispatchedEvent(nsIThreadInternal* /* aThread */)
|
||||
{
|
||||
MOZ_CRASH("OnDispatchedEvent() should never be called!");
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThread::Observer::OnProcessNextEvent(nsIThreadInternal* /* aThread */,
|
||||
bool aMayWait)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
// If the PBackground child is not created yet, then we must permit
|
||||
// blocking event processing to support
|
||||
// BackgroundChild::SynchronouslyCreateForCurrentThread(). If this occurs
|
||||
// then we are spinning on the event queue at the start of
|
||||
// PrimaryWorkerRunnable::Run() and don't want to process the event in
|
||||
// mWorkerPrivate yet.
|
||||
if (aMayWait) {
|
||||
MOZ_ASSERT(CycleCollectedJSContext::Get()->RecursionDepth() == 2);
|
||||
MOZ_ASSERT(!BackgroundChild::GetForCurrentThread());
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mWorkerPrivate->OnProcessNextEvent();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
WorkerThread::Observer::AfterProcessNextEvent(nsIThreadInternal* /* aThread */,
|
||||
bool /* aEventWasProcessed */)
|
||||
{
|
||||
mWorkerPrivate->AssertIsOnWorkerThread();
|
||||
|
||||
mWorkerPrivate->AfterProcessNextEvent();
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
110
dom/workers/WorkerThread.h
Normal file
110
dom/workers/WorkerThread.h
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
/* -*- 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_WorkerThread_h__
|
||||
#define mozilla_dom_workers_WorkerThread_h__
|
||||
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/CondVar.h"
|
||||
#include "mozilla/DebugOnly.h"
|
||||
#include "nsISupportsImpl.h"
|
||||
#include "mozilla/RefPtr.h"
|
||||
#include "nsThread.h"
|
||||
|
||||
class nsIRunnable;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
|
||||
class RuntimeService;
|
||||
class WorkerPrivate;
|
||||
template <class> class WorkerPrivateParent;
|
||||
class WorkerRunnable;
|
||||
|
||||
// This class lets us restrict the public methods that can be called on
|
||||
// WorkerThread to RuntimeService and WorkerPrivate without letting them gain
|
||||
// full access to private methods (as would happen if they were simply friends).
|
||||
class WorkerThreadFriendKey
|
||||
{
|
||||
friend class RuntimeService;
|
||||
friend class WorkerPrivate;
|
||||
friend class WorkerPrivateParent<WorkerPrivate>;
|
||||
|
||||
WorkerThreadFriendKey();
|
||||
~WorkerThreadFriendKey();
|
||||
};
|
||||
|
||||
class WorkerThread final
|
||||
: public nsThread
|
||||
{
|
||||
class Observer;
|
||||
|
||||
CondVar mWorkerPrivateCondVar;
|
||||
|
||||
// Protected by nsThread::mLock.
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
// Only touched on the target thread.
|
||||
RefPtr<Observer> mObserver;
|
||||
|
||||
// Protected by nsThread::mLock and waited on with mWorkerPrivateCondVar.
|
||||
uint32_t mOtherThreadsDispatchingViaEventTarget;
|
||||
|
||||
#ifdef DEBUG
|
||||
// Protected by nsThread::mLock.
|
||||
bool mAcceptingNonWorkerRunnables;
|
||||
#endif
|
||||
|
||||
public:
|
||||
static already_AddRefed<WorkerThread>
|
||||
Create(const WorkerThreadFriendKey& aKey);
|
||||
|
||||
void
|
||||
SetWorker(const WorkerThreadFriendKey& aKey, WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
nsresult
|
||||
DispatchPrimaryRunnable(const WorkerThreadFriendKey& aKey,
|
||||
already_AddRefed<nsIRunnable> aRunnable);
|
||||
|
||||
nsresult
|
||||
DispatchAnyThread(const WorkerThreadFriendKey& aKey,
|
||||
already_AddRefed<WorkerRunnable> aWorkerRunnable);
|
||||
|
||||
uint32_t
|
||||
RecursionDepth(const WorkerThreadFriendKey& aKey) const;
|
||||
|
||||
// Required for MinGW build #1336527 to handle compiler bug:
|
||||
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=79582
|
||||
NS_IMETHOD
|
||||
RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod> aIdlePeriod) override
|
||||
{
|
||||
return nsThread::RegisterIdlePeriod(already_AddRefed<nsIIdlePeriod>(aIdlePeriod.take()));
|
||||
}
|
||||
|
||||
NS_DECL_ISUPPORTS_INHERITED
|
||||
|
||||
private:
|
||||
WorkerThread();
|
||||
~WorkerThread();
|
||||
|
||||
// This should only be called by consumers that have an
|
||||
// nsIEventTarget/nsIThread pointer.
|
||||
NS_IMETHOD
|
||||
Dispatch(already_AddRefed<nsIRunnable> aRunnable, uint32_t aFlags) override;
|
||||
|
||||
NS_IMETHOD
|
||||
DispatchFromScript(nsIRunnable* aRunnable, uint32_t aFlags) override;
|
||||
|
||||
NS_IMETHOD
|
||||
DelayedDispatch(already_AddRefed<nsIRunnable>, uint32_t) override;
|
||||
};
|
||||
|
||||
} // namespace workers
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_dom_workers_WorkerThread_h__
|
||||
383
dom/workers/Workers.h
Normal file
383
dom/workers/Workers.h
Normal file
|
|
@ -0,0 +1,383 @@
|
|||
/* -*- 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_workers_h__
|
||||
#define mozilla_dom_workers_workers_h__
|
||||
|
||||
#include "jsapi.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
#include "mozilla/Mutex.h"
|
||||
#include <stdint.h>
|
||||
#include "nsAutoPtr.h"
|
||||
#include "nsCOMPtr.h"
|
||||
#include "nsDebug.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTArray.h"
|
||||
|
||||
#include "nsILoadContext.h"
|
||||
#include "nsIWeakReferenceUtils.h"
|
||||
#include "nsIInterfaceRequestor.h"
|
||||
#include "mozilla/dom/ChannelInfo.h"
|
||||
#include "mozilla/net/ReferrerPolicy.h"
|
||||
|
||||
#define BEGIN_WORKERS_NAMESPACE \
|
||||
namespace mozilla { namespace dom { namespace workers {
|
||||
#define END_WORKERS_NAMESPACE \
|
||||
} /* namespace workers */ } /* namespace dom */ } /* namespace mozilla */
|
||||
#define USING_WORKERS_NAMESPACE \
|
||||
using namespace mozilla::dom::workers;
|
||||
|
||||
#define WORKERS_SHUTDOWN_TOPIC "web-workers-shutdown"
|
||||
|
||||
class nsIContentSecurityPolicy;
|
||||
class nsIScriptContext;
|
||||
class nsIGlobalObject;
|
||||
class nsPIDOMWindowInner;
|
||||
class nsIPrincipal;
|
||||
class nsILoadGroup;
|
||||
class nsITabChild;
|
||||
class nsIChannel;
|
||||
class nsIRunnable;
|
||||
class nsIURI;
|
||||
|
||||
namespace mozilla {
|
||||
namespace ipc {
|
||||
class PrincipalInfo;
|
||||
} // namespace ipc
|
||||
|
||||
namespace dom {
|
||||
// If you change this, the corresponding list in nsIWorkerDebugger.idl needs to
|
||||
// be updated too.
|
||||
enum WorkerType
|
||||
{
|
||||
WorkerTypeDedicated,
|
||||
WorkerTypeShared,
|
||||
WorkerTypeService
|
||||
};
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
BEGIN_WORKERS_NAMESPACE
|
||||
|
||||
class WorkerPrivate;
|
||||
|
||||
struct PrivatizableBase
|
||||
{ };
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
AssertIsOnMainThread();
|
||||
#else
|
||||
inline void
|
||||
AssertIsOnMainThread()
|
||||
{ }
|
||||
#endif
|
||||
|
||||
struct JSSettings
|
||||
{
|
||||
enum {
|
||||
// All the GC parameters that we support.
|
||||
JSSettings_JSGC_MAX_BYTES = 0,
|
||||
JSSettings_JSGC_MAX_MALLOC_BYTES,
|
||||
JSSettings_JSGC_HIGH_FREQUENCY_TIME_LIMIT,
|
||||
JSSettings_JSGC_LOW_FREQUENCY_HEAP_GROWTH,
|
||||
JSSettings_JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MIN,
|
||||
JSSettings_JSGC_HIGH_FREQUENCY_HEAP_GROWTH_MAX,
|
||||
JSSettings_JSGC_HIGH_FREQUENCY_LOW_LIMIT,
|
||||
JSSettings_JSGC_HIGH_FREQUENCY_HIGH_LIMIT,
|
||||
JSSettings_JSGC_ALLOCATION_THRESHOLD,
|
||||
JSSettings_JSGC_SLICE_TIME_BUDGET,
|
||||
JSSettings_JSGC_DYNAMIC_HEAP_GROWTH,
|
||||
JSSettings_JSGC_DYNAMIC_MARK_SLICE,
|
||||
JSSettings_JSGC_REFRESH_FRAME_SLICES,
|
||||
// JSGC_MODE not supported
|
||||
|
||||
// This must be last so that we get an accurate count.
|
||||
kGCSettingsArraySize
|
||||
};
|
||||
|
||||
struct JSGCSetting
|
||||
{
|
||||
JSGCParamKey key;
|
||||
uint32_t value;
|
||||
|
||||
JSGCSetting()
|
||||
: key(static_cast<JSGCParamKey>(-1)), value(0)
|
||||
{ }
|
||||
|
||||
bool
|
||||
IsSet() const
|
||||
{
|
||||
return key != static_cast<JSGCParamKey>(-1);
|
||||
}
|
||||
|
||||
void
|
||||
Unset()
|
||||
{
|
||||
key = static_cast<JSGCParamKey>(-1);
|
||||
value = 0;
|
||||
}
|
||||
};
|
||||
|
||||
// There are several settings that we know we need so it makes sense to
|
||||
// preallocate here.
|
||||
typedef JSGCSetting JSGCSettingsArray[kGCSettingsArraySize];
|
||||
|
||||
// Settings that change based on chrome/content context.
|
||||
struct JSContentChromeSettings
|
||||
{
|
||||
JS::CompartmentOptions compartmentOptions;
|
||||
int32_t maxScriptRuntime;
|
||||
|
||||
JSContentChromeSettings()
|
||||
: compartmentOptions(), maxScriptRuntime(0)
|
||||
{ }
|
||||
};
|
||||
|
||||
JSContentChromeSettings chrome;
|
||||
JSContentChromeSettings content;
|
||||
JSGCSettingsArray gcSettings;
|
||||
JS::ContextOptions contextOptions;
|
||||
|
||||
#ifdef JS_GC_ZEAL
|
||||
uint8_t gcZeal;
|
||||
uint32_t gcZealFrequency;
|
||||
#endif
|
||||
|
||||
JSSettings()
|
||||
#ifdef JS_GC_ZEAL
|
||||
: gcZeal(0), gcZealFrequency(0)
|
||||
#endif
|
||||
{
|
||||
for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
|
||||
new (gcSettings + index) JSGCSetting();
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
ApplyGCSetting(JSGCParamKey aKey, uint32_t aValue)
|
||||
{
|
||||
JSSettings::JSGCSetting* firstEmptySetting = nullptr;
|
||||
JSSettings::JSGCSetting* foundSetting = nullptr;
|
||||
|
||||
for (uint32_t index = 0; index < ArrayLength(gcSettings); index++) {
|
||||
JSSettings::JSGCSetting& setting = gcSettings[index];
|
||||
if (setting.key == aKey) {
|
||||
foundSetting = &setting;
|
||||
break;
|
||||
}
|
||||
if (!firstEmptySetting && !setting.IsSet()) {
|
||||
firstEmptySetting = &setting;
|
||||
}
|
||||
}
|
||||
|
||||
if (aValue) {
|
||||
if (!foundSetting) {
|
||||
foundSetting = firstEmptySetting;
|
||||
if (!foundSetting) {
|
||||
NS_ERROR("Not enough space for this value!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
foundSetting->key = aKey;
|
||||
foundSetting->value = aValue;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (foundSetting) {
|
||||
foundSetting->Unset();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
enum WorkerPreference
|
||||
{
|
||||
#define WORKER_SIMPLE_PREF(name, getter, NAME) WORKERPREF_ ## NAME,
|
||||
#define WORKER_PREF(name, callback)
|
||||
#include "mozilla/dom/WorkerPrefs.h"
|
||||
#undef WORKER_SIMPLE_PREF
|
||||
#undef WORKER_PREF
|
||||
WORKERPREF_COUNT
|
||||
};
|
||||
|
||||
// Implemented in WorkerPrivate.cpp
|
||||
|
||||
struct WorkerLoadInfo
|
||||
{
|
||||
// All of these should be released in WorkerPrivateParent::ForgetMainThreadObjects.
|
||||
nsCOMPtr<nsIURI> mBaseURI;
|
||||
nsCOMPtr<nsIURI> mResolvedScriptURI;
|
||||
nsCOMPtr<nsIPrincipal> mPrincipal;
|
||||
nsCOMPtr<nsIScriptContext> mScriptContext;
|
||||
nsCOMPtr<nsPIDOMWindowInner> mWindow;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
nsCOMPtr<nsIChannel> mChannel;
|
||||
nsCOMPtr<nsILoadGroup> mLoadGroup;
|
||||
|
||||
// mLoadFailedAsyncRunnable will execute on main thread if script loading
|
||||
// fails during script loading. If script loading is never started due to
|
||||
// a synchronous error, then the runnable is never executed. The runnable
|
||||
// is guaranteed to be released on the main thread.
|
||||
nsCOMPtr<nsIRunnable> mLoadFailedAsyncRunnable;
|
||||
|
||||
class InterfaceRequestor final : public nsIInterfaceRequestor
|
||||
{
|
||||
NS_DECL_ISUPPORTS
|
||||
|
||||
public:
|
||||
InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup);
|
||||
void MaybeAddTabChild(nsILoadGroup* aLoadGroup);
|
||||
NS_IMETHOD GetInterface(const nsIID& aIID, void** aSink) override;
|
||||
|
||||
private:
|
||||
~InterfaceRequestor() { }
|
||||
|
||||
already_AddRefed<nsITabChild> GetAnyLiveTabChild();
|
||||
|
||||
nsCOMPtr<nsILoadContext> mLoadContext;
|
||||
nsCOMPtr<nsIInterfaceRequestor> mOuterRequestor;
|
||||
|
||||
// Array of weak references to nsITabChild. We do not want to keep TabChild
|
||||
// actors alive for long after their ActorDestroy() methods are called.
|
||||
nsTArray<nsWeakPtr> mTabChildList;
|
||||
};
|
||||
|
||||
// Only set if we have a custom overriden load group
|
||||
RefPtr<InterfaceRequestor> mInterfaceRequestor;
|
||||
|
||||
nsAutoPtr<mozilla::ipc::PrincipalInfo> mPrincipalInfo;
|
||||
nsCString mDomain;
|
||||
|
||||
nsString mServiceWorkerCacheName;
|
||||
|
||||
ChannelInfo mChannelInfo;
|
||||
|
||||
uint64_t mWindowID;
|
||||
uint64_t mServiceWorkerID;
|
||||
|
||||
net::ReferrerPolicy mReferrerPolicy;
|
||||
bool mFromWindow;
|
||||
bool mEvalAllowed;
|
||||
bool mReportCSPViolations;
|
||||
bool mXHRParamsAllowed;
|
||||
bool mPrincipalIsSystem;
|
||||
bool mStorageAllowed;
|
||||
bool mServiceWorkersTestingInWindow;
|
||||
PrincipalOriginAttributes mOriginAttributes;
|
||||
|
||||
WorkerLoadInfo();
|
||||
~WorkerLoadInfo();
|
||||
|
||||
void StealFrom(WorkerLoadInfo& aOther);
|
||||
};
|
||||
|
||||
// All of these are implemented in RuntimeService.cpp
|
||||
|
||||
void
|
||||
CancelWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
FreezeWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
ThawWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
SuspendWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
void
|
||||
ResumeWorkersForWindow(nsPIDOMWindowInner* aWindow);
|
||||
|
||||
// A class that can be used with WorkerCrossThreadDispatcher to run a
|
||||
// bit of C++ code on the worker thread.
|
||||
class WorkerTask
|
||||
{
|
||||
protected:
|
||||
WorkerTask()
|
||||
{ }
|
||||
|
||||
virtual ~WorkerTask()
|
||||
{ }
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerTask)
|
||||
|
||||
// The return value here has the same semantics as the return value
|
||||
// of WorkerRunnable::WorkerRun.
|
||||
virtual bool
|
||||
RunTask(JSContext* aCx) = 0;
|
||||
};
|
||||
|
||||
class WorkerCrossThreadDispatcher
|
||||
{
|
||||
friend class WorkerPrivate;
|
||||
|
||||
// Must be acquired *before* the WorkerPrivate's mutex, when they're both
|
||||
// held.
|
||||
Mutex mMutex;
|
||||
WorkerPrivate* mWorkerPrivate;
|
||||
|
||||
private:
|
||||
// Only created by WorkerPrivate.
|
||||
explicit WorkerCrossThreadDispatcher(WorkerPrivate* aWorkerPrivate);
|
||||
|
||||
// Only called by WorkerPrivate.
|
||||
void
|
||||
Forget()
|
||||
{
|
||||
MutexAutoLock lock(mMutex);
|
||||
mWorkerPrivate = nullptr;
|
||||
}
|
||||
|
||||
~WorkerCrossThreadDispatcher() {}
|
||||
|
||||
public:
|
||||
NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WorkerCrossThreadDispatcher)
|
||||
|
||||
// Generically useful function for running a bit of C++ code on the worker
|
||||
// thread.
|
||||
bool
|
||||
PostTask(WorkerTask* aTask);
|
||||
};
|
||||
|
||||
WorkerCrossThreadDispatcher*
|
||||
GetWorkerCrossThreadDispatcher(JSContext* aCx, const JS::Value& aWorker);
|
||||
|
||||
// Random unique constant to facilitate JSPrincipal debugging
|
||||
const uint32_t kJSPrincipalsDebugToken = 0x7e2df9d2;
|
||||
|
||||
namespace exceptions {
|
||||
|
||||
// Implemented in Exceptions.cpp
|
||||
void
|
||||
ThrowDOMExceptionForNSResult(JSContext* aCx, nsresult aNSResult);
|
||||
|
||||
} // namespace exceptions
|
||||
|
||||
bool
|
||||
IsWorkerGlobal(JSObject* global);
|
||||
|
||||
bool
|
||||
IsDebuggerGlobal(JSObject* global);
|
||||
|
||||
bool
|
||||
IsDebuggerSandbox(JSObject* object);
|
||||
|
||||
// Throws the JSMSG_GETTER_ONLY exception. This shouldn't be used going
|
||||
// forward -- getter-only properties should just use JS_PSG for the setter
|
||||
// (implying no setter at all), which will not throw when set in non-strict
|
||||
// code but will in strict code. Old code should use this only for temporary
|
||||
// compatibility reasons.
|
||||
extern bool
|
||||
GetterOnlyJSNative(JSContext* aCx, unsigned aArgc, JS::Value* aVp);
|
||||
|
||||
END_WORKERS_NAMESPACE
|
||||
|
||||
#endif // mozilla_dom_workers_workers_h__
|
||||
131
dom/workers/moz.build
Normal file
131
dom/workers/moz.build
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*-
|
||||
# vim: set filetype=python:
|
||||
# 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/.
|
||||
|
||||
# Public stuff.
|
||||
EXPORTS.mozilla.dom += [
|
||||
'FileReaderSync.h',
|
||||
'ServiceWorkerCommon.h',
|
||||
'ServiceWorkerContainer.h',
|
||||
'ServiceWorkerEvents.h',
|
||||
'ServiceWorkerRegistrar.h',
|
||||
'ServiceWorkerRegistration.h',
|
||||
'WorkerLocation.h',
|
||||
'WorkerNavigator.h',
|
||||
'WorkerPrefs.h',
|
||||
'WorkerPrivate.h',
|
||||
'WorkerRunnable.h',
|
||||
'WorkerScope.h',
|
||||
]
|
||||
|
||||
EXPORTS.mozilla.dom.workers += [
|
||||
'RuntimeService.h',
|
||||
'ServiceWorkerInfo.h',
|
||||
'ServiceWorkerManager.h',
|
||||
'ServiceWorkerRegistrationInfo.h',
|
||||
'WorkerDebuggerManager.h',
|
||||
'Workers.h',
|
||||
]
|
||||
|
||||
# Stuff needed for the bindings, not really public though.
|
||||
EXPORTS.mozilla.dom.workers.bindings += [
|
||||
'ServiceWorker.h',
|
||||
'ServiceWorkerClient.h',
|
||||
'ServiceWorkerClients.h',
|
||||
'ServiceWorkerWindowClient.h',
|
||||
'SharedWorker.h',
|
||||
'WorkerHolder.h',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'dom_workers'
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIWorkerDebugger.idl',
|
||||
'nsIWorkerDebuggerManager.idl',
|
||||
]
|
||||
|
||||
UNIFIED_SOURCES += [
|
||||
'ChromeWorkerScope.cpp',
|
||||
'FileReaderSync.cpp',
|
||||
'Principal.cpp',
|
||||
'RegisterBindings.cpp',
|
||||
'RuntimeService.cpp',
|
||||
'ScriptLoader.cpp',
|
||||
'ServiceWorker.cpp',
|
||||
'ServiceWorkerClient.cpp',
|
||||
'ServiceWorkerClients.cpp',
|
||||
'ServiceWorkerContainer.cpp',
|
||||
'ServiceWorkerEvents.cpp',
|
||||
'ServiceWorkerInfo.cpp',
|
||||
'ServiceWorkerJob.cpp',
|
||||
'ServiceWorkerJobQueue.cpp',
|
||||
'ServiceWorkerManager.cpp',
|
||||
'ServiceWorkerManagerChild.cpp',
|
||||
'ServiceWorkerManagerParent.cpp',
|
||||
'ServiceWorkerManagerService.cpp',
|
||||
'ServiceWorkerPrivate.cpp',
|
||||
'ServiceWorkerRegisterJob.cpp',
|
||||
'ServiceWorkerRegistrar.cpp',
|
||||
'ServiceWorkerRegistration.cpp',
|
||||
'ServiceWorkerRegistrationInfo.cpp',
|
||||
'ServiceWorkerScriptCache.cpp',
|
||||
'ServiceWorkerUnregisterJob.cpp',
|
||||
'ServiceWorkerUpdateJob.cpp',
|
||||
'ServiceWorkerWindowClient.cpp',
|
||||
'SharedWorker.cpp',
|
||||
'WorkerDebuggerManager.cpp',
|
||||
'WorkerHolder.cpp',
|
||||
'WorkerLocation.cpp',
|
||||
'WorkerNavigator.cpp',
|
||||
'WorkerPrivate.cpp',
|
||||
'WorkerRunnable.cpp',
|
||||
'WorkerScope.cpp',
|
||||
'WorkerThread.cpp',
|
||||
]
|
||||
|
||||
IPDL_SOURCES += [
|
||||
'PServiceWorkerManager.ipdl',
|
||||
'ServiceWorkerRegistrarTypes.ipdlh',
|
||||
]
|
||||
|
||||
LOCAL_INCLUDES += [
|
||||
'../base',
|
||||
'../system',
|
||||
'/dom/base',
|
||||
'/xpcom/build',
|
||||
'/xpcom/threads',
|
||||
]
|
||||
|
||||
include('/ipc/chromium/chromium-config.mozbuild')
|
||||
|
||||
FINAL_LIBRARY = 'xul'
|
||||
|
||||
TEST_DIRS += [
|
||||
'test/extensions/bootstrap',
|
||||
'test/extensions/traditional',
|
||||
]
|
||||
|
||||
MOCHITEST_MANIFESTS += [
|
||||
'test/mochitest.ini',
|
||||
'test/serviceworkers/mochitest.ini',
|
||||
]
|
||||
|
||||
MOCHITEST_CHROME_MANIFESTS += [
|
||||
'test/chrome.ini',
|
||||
'test/serviceworkers/chrome.ini'
|
||||
]
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += [
|
||||
'test/serviceworkers/browser.ini',
|
||||
]
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['test/xpcshell/xpcshell.ini']
|
||||
|
||||
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']
|
||||
|
||||
TEST_DIRS += ['test/gtest']
|
||||
|
||||
if CONFIG['GNU_CXX']:
|
||||
CXXFLAGS += ['-Wno-error=shadow']
|
||||
50
dom/workers/nsIWorkerDebugger.idl
Normal file
50
dom/workers/nsIWorkerDebugger.idl
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
interface mozIDOMWindow;
|
||||
interface nsIPrincipal;
|
||||
|
||||
[scriptable, uuid(9cf3b48e-361d-486a-8917-55cf8d00bb41)]
|
||||
interface nsIWorkerDebuggerListener : nsISupports
|
||||
{
|
||||
void onClose();
|
||||
|
||||
void onError(in DOMString filename, in unsigned long lineno,
|
||||
in DOMString message);
|
||||
|
||||
void onMessage(in DOMString message);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(22f93aa3-8a05-46be-87e0-fa93bf8a8eff)]
|
||||
interface nsIWorkerDebugger : nsISupports
|
||||
{
|
||||
const unsigned long TYPE_DEDICATED = 0;
|
||||
const unsigned long TYPE_SHARED = 1;
|
||||
const unsigned long TYPE_SERVICE = 2;
|
||||
|
||||
readonly attribute bool isClosed;
|
||||
|
||||
readonly attribute bool isChrome;
|
||||
|
||||
readonly attribute bool isInitialized;
|
||||
|
||||
readonly attribute nsIWorkerDebugger parent;
|
||||
|
||||
readonly attribute unsigned long type;
|
||||
|
||||
readonly attribute DOMString url;
|
||||
|
||||
readonly attribute mozIDOMWindow window;
|
||||
|
||||
readonly attribute nsIPrincipal principal;
|
||||
|
||||
readonly attribute unsigned long serviceWorkerID;
|
||||
|
||||
void initialize(in DOMString url);
|
||||
|
||||
[binaryname(PostMessageMoz)]
|
||||
void postMessage(in DOMString message);
|
||||
|
||||
void addListener(in nsIWorkerDebuggerListener listener);
|
||||
|
||||
void removeListener(in nsIWorkerDebuggerListener listener);
|
||||
};
|
||||
22
dom/workers/nsIWorkerDebuggerManager.idl
Normal file
22
dom/workers/nsIWorkerDebuggerManager.idl
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
#include "nsISupports.idl"
|
||||
|
||||
interface nsISimpleEnumerator;
|
||||
interface nsIWorkerDebugger;
|
||||
|
||||
[scriptable, uuid(d2aa74ee-6b98-4d5d-8173-4e23422daf1e)]
|
||||
interface nsIWorkerDebuggerManagerListener : nsISupports
|
||||
{
|
||||
void onRegister(in nsIWorkerDebugger debugger);
|
||||
|
||||
void onUnregister(in nsIWorkerDebugger debugger);
|
||||
};
|
||||
|
||||
[scriptable, builtinclass, uuid(056d7918-dc86-452a-b4e6-86da3405f015)]
|
||||
interface nsIWorkerDebuggerManager : nsISupports
|
||||
{
|
||||
nsISimpleEnumerator getWorkerDebuggerEnumerator();
|
||||
|
||||
void addListener(in nsIWorkerDebuggerManagerListener listener);
|
||||
|
||||
void removeListener(in nsIWorkerDebuggerManagerListener listener);
|
||||
};
|
||||
10
dom/workers/test/404_server.sjs
Normal file
10
dom/workers/test/404_server.sjs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
function handleRequest(request, response)
|
||||
{
|
||||
response.setStatusLine(request.httpVersion, 404, "Not found");
|
||||
|
||||
// Any valid JS.
|
||||
if (request.queryString == 'js') {
|
||||
response.setHeader("Content-Type", "text/javascript", false);
|
||||
response.write('4 + 4');
|
||||
}
|
||||
}
|
||||
3
dom/workers/test/WorkerDebugger.console_childWorker.js
Normal file
3
dom/workers/test/WorkerDebugger.console_childWorker.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
||||
41
dom/workers/test/WorkerDebugger.console_debugger.js
Normal file
41
dom/workers/test/WorkerDebugger.console_debugger.js
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
"use strict"
|
||||
|
||||
function ok(a, msg) {
|
||||
postMessage(JSON.stringify({ type: 'status', what: !!a, msg: msg }));
|
||||
}
|
||||
|
||||
function is(a, b, msg) {
|
||||
ok(a === b, msg);
|
||||
}
|
||||
|
||||
function finish() {
|
||||
postMessage(JSON.stringify({ type: 'finish' }));
|
||||
}
|
||||
|
||||
function magic() {
|
||||
console.log("Hello from the debugger script!");
|
||||
|
||||
var foo = retrieveConsoleEvents();
|
||||
ok(Array.isArray(foo), "We received an array.");
|
||||
ok(foo.length >= 2, "At least 2 messages.");
|
||||
|
||||
is(foo[0].arguments[0], "Can you see this console message?", "First message ok.");
|
||||
is(foo[1].arguments[0], "Can you see this second console message?", "Second message ok.");
|
||||
|
||||
setConsoleEventHandler(function(consoleData) {
|
||||
is(consoleData.arguments[0], "Random message.", "Random message ok!");
|
||||
|
||||
// The consoleEventHandler can be null.
|
||||
setConsoleEventHandler(null);
|
||||
|
||||
finish();
|
||||
});
|
||||
}
|
||||
|
||||
this.onmessage = function (event) {
|
||||
switch (event.data) {
|
||||
case "do magic":
|
||||
magic();
|
||||
break;
|
||||
}
|
||||
};
|
||||
10
dom/workers/test/WorkerDebugger.console_worker.js
Normal file
10
dom/workers/test/WorkerDebugger.console_worker.js
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
"use strict";
|
||||
|
||||
console.log("Can you see this console message?");
|
||||
console.warn("Can you see this second console message?");
|
||||
|
||||
var worker = new Worker("WorkerDebugger.console_childWorker.js");
|
||||
|
||||
setInterval(function() {
|
||||
console.log("Random message.");
|
||||
}, 200);
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
||||
|
||||
debugger;
|
||||
postMessage("worker");
|
||||
6
dom/workers/test/WorkerDebugger.initialize_debugger.js
Normal file
6
dom/workers/test/WorkerDebugger.initialize_debugger.js
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
"use strict";
|
||||
|
||||
var dbg = new Debugger(global);
|
||||
dbg.onDebuggerStatement = function (frame) {
|
||||
frame.eval("postMessage('debugger');");
|
||||
};
|
||||
9
dom/workers/test/WorkerDebugger.initialize_worker.js
Normal file
9
dom/workers/test/WorkerDebugger.initialize_worker.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
var worker = new Worker("WorkerDebugger.initialize_childWorker.js");
|
||||
worker.onmessage = function (event) {
|
||||
postMessage("child:" + event.data);
|
||||
};
|
||||
|
||||
debugger;
|
||||
postMessage("worker");
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
||||
9
dom/workers/test/WorkerDebugger.postMessage_debugger.js
Normal file
9
dom/workers/test/WorkerDebugger.postMessage_debugger.js
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
"use strict"
|
||||
|
||||
this.onmessage = function (event) {
|
||||
switch (event.data) {
|
||||
case "ping":
|
||||
postMessage("pong");
|
||||
break;
|
||||
}
|
||||
};
|
||||
3
dom/workers/test/WorkerDebugger.postMessage_worker.js
Normal file
3
dom/workers/test/WorkerDebugger.postMessage_worker.js
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
var worker = new Worker("WorkerDebugger.postMessage_childWorker.js");
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
const SANDBOX_URL = "WorkerDebuggerGlobalScope.createSandbox_sandbox.js";
|
||||
|
||||
var prototype = {
|
||||
self: this,
|
||||
};
|
||||
var sandbox = createSandbox(SANDBOX_URL, prototype);
|
||||
loadSubScript(SANDBOX_URL, sandbox);
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
"use strict";
|
||||
|
||||
self.addEventListener("message", function(event) {
|
||||
switch (event.data) {
|
||||
case "ping":
|
||||
self.postMessage("pong");
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
"use strict";
|
||||
|
||||
self.onmessage = function () {};
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
"use strict";
|
||||
|
||||
function f() {
|
||||
debugger;
|
||||
}
|
||||
|
||||
self.onmessage = function (event) {
|
||||
switch (event.data) {
|
||||
case "ping":
|
||||
debugger;
|
||||
postMessage("pong");
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
"use strict";
|
||||
|
||||
var frames = [];
|
||||
|
||||
var dbg = new Debugger(global);
|
||||
dbg.onDebuggerStatement = function (frame) {
|
||||
frames.push(frame);
|
||||
postMessage("paused");
|
||||
enterEventLoop();
|
||||
frames.pop();
|
||||
postMessage("resumed");
|
||||
};
|
||||
|
||||
this.onmessage = function (event) {
|
||||
switch (event.data) {
|
||||
case "eval":
|
||||
frames[frames.length - 1].eval("f()");
|
||||
postMessage("evalled");
|
||||
break;
|
||||
|
||||
case "ping":
|
||||
postMessage("pong");
|
||||
break;
|
||||
|
||||
case "resume":
|
||||
leaveEventLoop();
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
"use strict";
|
||||
|
||||
function f() {
|
||||
debugger;
|
||||
}
|
||||
|
||||
var worker = new Worker("WorkerDebuggerGlobalScope.enterEventLoop_childWorker.js");
|
||||
|
||||
worker.onmessage = function (event) {
|
||||
postMessage("child:" + event.data);
|
||||
};
|
||||
|
||||
self.onmessage = function (event) {
|
||||
var message = event.data;
|
||||
if (message.indexOf(":") >= 0) {
|
||||
worker.postMessage(message.split(":")[1]);
|
||||
return;
|
||||
}
|
||||
switch (message) {
|
||||
case "ping":
|
||||
debugger;
|
||||
postMessage("pong");
|
||||
break;
|
||||
};
|
||||
};
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
"use strict";
|
||||
|
||||
self.onerror = function () {
|
||||
postMessage("error");
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
"use strict";
|
||||
|
||||
this.onmessage = function (event) {
|
||||
switch (event.data) {
|
||||
case "report":
|
||||
reportError("reported");
|
||||
break;
|
||||
case "throw":
|
||||
throw new Error("thrown");
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
"use strict";
|
||||
|
||||
var worker = new Worker("WorkerDebuggerGlobalScope.reportError_childWorker.js");
|
||||
|
||||
worker.onmessage = function (event) {
|
||||
postMessage("child:" + event.data);
|
||||
};
|
||||
|
||||
self.onerror = function () {
|
||||
postMessage("error");
|
||||
};
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue