author | arthur.iakab <aiakab@mozilla.com> |
Mon, 21 Oct 2019 12:55:26 +0300 | |
changeset 498398 | d42c22627c8dee9e9286fd5d7bbc3d02afbfad44 |
parent 498397 | b34beb21e1cf9d283ef0d855f68f63bcc15e40fb (current diff) |
parent 498306 | 28171f948a0874143e59c9551712b8fa08e50f55 (diff) |
child 498399 | a62162b09b7a41dfd7163d066e7c87572d9c5b66 |
push id | 114157 |
push user | nbeleuzu@mozilla.com |
push date | Mon, 21 Oct 2019 22:00:13 +0000 |
treeherder | mozilla-inbound@563f437f24b9 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 71.0a1 |
first release with | nightly linux32
d42c22627c8d
/
71.0a1
/
20191021095628
/
files
nightly linux64
d42c22627c8d
/
71.0a1
/
20191021095628
/
files
nightly mac
d42c22627c8d
/
71.0a1
/
20191021095628
/
files
nightly win32
d42c22627c8d
/
71.0a1
/
20191021095628
/
files
nightly win64
d42c22627c8d
/
71.0a1
/
20191021095628
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
71.0a1
/
20191021095628
/
pushlog to previous
nightly linux64
71.0a1
/
20191021095628
/
pushlog to previous
nightly mac
71.0a1
/
20191021095628
/
pushlog to previous
nightly win32
71.0a1
/
20191021095628
/
pushlog to previous
nightly win64
71.0a1
/
20191021095628
/
pushlog to previous
|
--- a/dom/base/BodyConsumer.cpp +++ b/dom/base/BodyConsumer.cpp @@ -235,29 +235,29 @@ class ConsumeBodyDoneObserver final : pu if (NS_WARN_IF(!r->Dispatch())) { return NS_ERROR_FAILURE; } // We haven't taken ownership of the data. return NS_OK; } - virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob, - nsresult aRv) override { + virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, + BlobImpl* aBlobImpl, nsresult aRv) override { // On error. if (NS_FAILED(aRv)) { OnStreamComplete(nullptr, nullptr, aRv, 0, nullptr); return; } // The loading is completed. Let's nullify the pump before continuing the // consuming of the body. mBodyConsumer->NullifyConsumeBodyPump(); - mBodyConsumer->OnBlobResult(aBlob, mWorkerRef); + mBodyConsumer->OnBlobResult(aBlobImpl, mWorkerRef); } private: ~ConsumeBodyDoneObserver() = default; RefPtr<BodyConsumer> mBodyConsumer; RefPtr<ThreadSafeWorkerRef> mWorkerRef; }; @@ -401,17 +401,17 @@ class FileCreationHandler final : public } RefPtr<Blob> blob; if (NS_WARN_IF(NS_FAILED(UNWRAP_OBJECT(Blob, &aValue.toObject(), blob)))) { mConsumer->OnBlobResult(nullptr, mWorkerRef); return; } - mConsumer->OnBlobResult(blob, mWorkerRef); + mConsumer->OnBlobResult(blob->Impl(), mWorkerRef); } void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue) override { AssertIsOnMainThread(); mConsumer->OnBlobResult(nullptr, mWorkerRef); } @@ -527,18 +527,18 @@ void BodyConsumer::BeginConsumeBodyMainT return; } RefPtr<ConsumeBodyDoneObserver> p = new ConsumeBodyDoneObserver(this, aWorkerRef); nsCOMPtr<nsIStreamListener> listener; if (mConsumeType == CONSUME_BLOB) { - listener = new MutableBlobStreamListener( - mBlobStorageType, nullptr, mBodyMimeType, p, mMainThreadEventTarget); + listener = new MutableBlobStreamListener(mBlobStorageType, mBodyMimeType, p, + mMainThreadEventTarget); } else { nsCOMPtr<nsIStreamLoader> loader; rv = NS_NewStreamLoader(getter_AddRefs(loader), p); if (NS_WARN_IF(NS_FAILED(rv))) { return; } listener = loader; @@ -569,20 +569,21 @@ void BodyConsumer::BeginConsumeBodyMainT } /* * OnBlobResult() is called when a blob body is ready to be consumed (when its * network transfer completes in BeginConsumeBodyRunnable or its local File has * been wrapped by FileCreationHandler). The blob is sent to the target thread * and ContinueConsumeBody is called. */ -void BodyConsumer::OnBlobResult(Blob* aBlob, ThreadSafeWorkerRef* aWorkerRef) { +void BodyConsumer::OnBlobResult(BlobImpl* aBlobImpl, + ThreadSafeWorkerRef* aWorkerRef) { AssertIsOnMainThread(); - DispatchContinueConsumeBlobBody(aBlob ? aBlob->Impl() : nullptr, aWorkerRef); + DispatchContinueConsumeBlobBody(aBlobImpl, aWorkerRef); } void BodyConsumer::DispatchContinueConsumeBlobBody( BlobImpl* aBlobImpl, ThreadSafeWorkerRef* aWorkerRef) { AssertIsOnMainThread(); // Main-thread. if (!aWorkerRef) { @@ -749,17 +750,20 @@ void BodyConsumer::ContinueConsumeBlobBo } mBodyConsumed = true; MOZ_ASSERT(mConsumePromise); RefPtr<Promise> localPromise = mConsumePromise.forget(); if (!aShuttingDown) { RefPtr<dom::Blob> blob = dom::Blob::Create(mGlobal, aBlobImpl); - MOZ_ASSERT(blob); + if (NS_WARN_IF(!blob)) { + localPromise->MaybeReject(NS_ERROR_FAILURE); + return; + } localPromise->MaybeResolve(blob); } ReleaseObject(); } void BodyConsumer::ShutDownMainThreadConsuming() {
--- a/dom/base/BodyConsumer.h +++ b/dom/base/BodyConsumer.h @@ -65,17 +65,18 @@ class BodyConsumer final : public nsIObs const nsAString& aBodyLocalPath, const nsACString& aBodyMimeType, MutableBlobStorage::MutableBlobStorageType aBlobStorageType, ErrorResult& aRv); void ReleaseObject(); void BeginConsumeBodyMainThread(ThreadSafeWorkerRef* aWorkerRef); - void OnBlobResult(Blob* aBlob, ThreadSafeWorkerRef* aWorkerRef = nullptr); + void OnBlobResult(BlobImpl* aBlobImpl, + ThreadSafeWorkerRef* aWorkerRef = nullptr); void ContinueConsumeBody(nsresult aStatus, uint32_t aLength, uint8_t* aResult, bool aShuttingDown = false); void ContinueConsumeBlobBody(BlobImpl* aBlobImpl, bool aShuttingDown = false); void DispatchContinueConsumeBlobBody(BlobImpl* aBlobImpl, ThreadSafeWorkerRef* aWorkerRef);
--- a/dom/base/BodyUtil.cpp +++ b/dom/base/BodyUtil.cpp @@ -251,16 +251,20 @@ class MOZ_STACK_CLASS FormDataParser { *p++ = *bodyIter++; } p = nullptr; RefPtr<Blob> file = File::CreateMemoryFile( mParentObject, reinterpret_cast<void*>(copy), body.Length(), NS_ConvertUTF8toUTF16(mFilename), NS_ConvertUTF8toUTF16(mContentType), /* aLastModifiedDate */ 0); + if (NS_WARN_IF(!file)) { + return false; + } + Optional<nsAString> dummy; ErrorResult rv; mFormData->Append(name, *file, dummy, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); return false; } } @@ -393,17 +397,17 @@ void BodyUtil::ConsumeArrayBuffer(JSCont JS_ClearPendingException(aCx); aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } aValue.set(arrayBuffer); } // static -already_AddRefed<Blob> BodyUtil::ConsumeBlob(nsISupports* aParent, +already_AddRefed<Blob> BodyUtil::ConsumeBlob(nsIGlobalObject* aParent, const nsString& aMimeType, uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv) { RefPtr<Blob> blob = Blob::CreateMemoryBlob( aParent, reinterpret_cast<void*>(aInput), aInputLength, aMimeType); if (!blob) {
--- a/dom/base/BodyUtil.h +++ b/dom/base/BodyUtil.h @@ -31,17 +31,17 @@ class BodyUtil final { JS::MutableHandle<JSObject*> aValue, uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv); /** * Creates an in-memory blob from an array. The blob takes ownership of * |aInput|, which must be allocated by |malloc|. */ - static already_AddRefed<Blob> ConsumeBlob(nsISupports* aParent, + static already_AddRefed<Blob> ConsumeBlob(nsIGlobalObject* aParent, const nsString& aMimeType, uint32_t aInputLength, uint8_t* aInput, ErrorResult& aRv); /** * Creates a form data object from a UTF-8 encoded |aStr|. Returns |nullptr| * and sets |aRv| to MSG_BAD_FORMDATA if |aStr| contains invalid data. */
--- a/dom/base/ImageEncoder.cpp +++ b/dom/base/ImageEncoder.cpp @@ -1,16 +1,17 @@ /* -*- 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 "ImageEncoder.h" #include "mozilla/dom/CanvasRenderingContext2D.h" +#include "mozilla/dom/MemoryBlobImpl.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/gfx/2D.h" #include "mozilla/gfx/DataSurfaceHelpers.h" #include "mozilla/layers/AsyncCanvasRenderer.h" #include "mozilla/RefPtr.h" #include "mozilla/SyncRunnable.h" #include "mozilla/Unused.h" #include "gfxUtils.h" @@ -95,24 +96,20 @@ class EncodingCompleteEvent : public Can // bug 1535398. MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { nsresult rv = NS_OK; // We want to null out mEncodeCompleteCallback no matter what. RefPtr<EncodeCompleteCallback> callback(mEncodeCompleteCallback.forget()); if (!mFailed) { - // The correct parentObject has to be set by the mEncodeCompleteCallback. - RefPtr<Blob> blob = - Blob::CreateMemoryBlob(nullptr, mImgData, mImgSize, mType); - MOZ_ASSERT(blob); - - rv = callback->ReceiveBlob(blob.forget()); + RefPtr<BlobImpl> blobImpl = new MemoryBlobImpl(mImgData, mImgSize, mType); + rv = callback->ReceiveBlobImpl(blobImpl.forget()); } else { - rv = callback->ReceiveBlob(nullptr); + rv = callback->ReceiveBlobImpl(nullptr); } return rv; } void SetMembers(void* aImgData, uint64_t aImgSize, const nsAutoString& aType) { mImgData = aImgData;
--- a/dom/base/ImageEncoder.h +++ b/dom/base/ImageEncoder.h @@ -97,25 +97,25 @@ class ImageEncoder { static already_AddRefed<imgIEncoder> GetImageEncoder(nsAString& aType); friend class EncodingRunnable; friend class EncoderThreadPoolTerminator; }; /** * The callback interface of ExtractDataAsync and - * ExtractDataFromLayersImageAsync. ReceiveBlob() is called on main thread when - * encoding is complete. + * ExtractDataFromLayersImageAsync. ReceiveBlobImpl() is called on main thread + * when encoding is complete. */ class EncodeCompleteCallback { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(EncodeCompleteCallback) MOZ_CAN_RUN_SCRIPT - virtual nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) = 0; + virtual nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) = 0; protected: virtual ~EncodeCompleteCallback() {} }; } // namespace dom } // namespace mozilla
--- a/dom/base/PostMessageEvent.cpp +++ b/dom/base/PostMessageEvent.cpp @@ -159,17 +159,17 @@ PostMessageEvent::Run() { IgnoredErrorResult rv; JS::Rooted<JS::Value> messageData(cx); nsCOMPtr<mozilla::dom::EventTarget> eventTarget = do_QueryObject(targetWindow); StructuredCloneHolder* holder; if (mHolder.constructed<StructuredCloneHolder>()) { - mHolder.ref<StructuredCloneHolder>().Read(ToSupports(targetWindow), cx, + mHolder.ref<StructuredCloneHolder>().Read(targetWindow->AsGlobal(), cx, &messageData, rv); holder = &mHolder.ref<StructuredCloneHolder>(); } else { MOZ_ASSERT(mHolder.constructed<ipc::StructuredCloneData>()); mHolder.ref<ipc::StructuredCloneData>().Read(cx, &messageData, rv); holder = &mHolder.ref<ipc::StructuredCloneData>(); } if (NS_WARN_IF(rv.Failed())) {
--- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -228,17 +228,17 @@ bool StructuredCloneHolderBase::CustomCa // StructuredCloneHolder class StructuredCloneHolder::StructuredCloneHolder( CloningSupport aSupportsCloning, TransferringSupport aSupportsTransferring, StructuredCloneScope aScope) : StructuredCloneHolderBase(aScope), mSupportsCloning(aSupportsCloning == CloningSupported), mSupportsTransferring(aSupportsTransferring == TransferringSupported), - mParent(nullptr) + mGlobal(nullptr) #ifdef DEBUG , mCreationEventTarget(GetCurrentThreadEventTarget()) #endif { } StructuredCloneHolder::~StructuredCloneHolder() { @@ -261,26 +261,26 @@ void StructuredCloneHolder::Write(JSCont if (!StructuredCloneHolderBase::Write(aCx, aValue, aTransfer, cloneDataPolicy)) { aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } } -void StructuredCloneHolder::Read(nsISupports* aParent, JSContext* aCx, +void StructuredCloneHolder::Read(nsIGlobalObject* aGlobal, JSContext* aCx, JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv) { MOZ_ASSERT_IF( mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread, mCreationEventTarget->IsOnCurrentThread()); - MOZ_ASSERT(aParent); + MOZ_ASSERT(aGlobal); - mozilla::AutoRestore<nsISupports*> guard(mParent); - mParent = aParent; + mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal); + mGlobal = aGlobal; if (!StructuredCloneHolderBase::Read(aCx, aValue)) { JS_ClearPendingException(aCx); aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); return; } // If we are tranferring something, we cannot call 'Read()' more than once. @@ -288,37 +288,39 @@ void StructuredCloneHolder::Read(nsISupp mBlobImplArray.Clear(); mWasmModuleArray.Clear(); mClonedSurfaces.Clear(); mInputStreamArray.Clear(); Clear(); } } -void StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent, JSContext* aCx, +void StructuredCloneHolder::ReadFromBuffer(nsIGlobalObject* aGlobal, + JSContext* aCx, JSStructuredCloneData& aBuffer, JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv) { - ReadFromBuffer(aParent, aCx, aBuffer, JS_STRUCTURED_CLONE_VERSION, aValue, + ReadFromBuffer(aGlobal, aCx, aBuffer, JS_STRUCTURED_CLONE_VERSION, aValue, aRv); } -void StructuredCloneHolder::ReadFromBuffer(nsISupports* aParent, JSContext* aCx, +void StructuredCloneHolder::ReadFromBuffer(nsIGlobalObject* aGlobal, + JSContext* aCx, JSStructuredCloneData& aBuffer, uint32_t aAlgorithmVersion, JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv) { MOZ_ASSERT_IF( mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread, mCreationEventTarget->IsOnCurrentThread()); MOZ_ASSERT(!mBuffer, "ReadFromBuffer() must be called without a Write()."); - mozilla::AutoRestore<nsISupports*> guard(mParent); - mParent = aParent; + mozilla::AutoRestore<nsIGlobalObject*> guard(mGlobal); + mGlobal = aGlobal; if (!JS_ReadStructuredClone(aCx, aBuffer, aAlgorithmVersion, mStructuredCloneScope, aValue, &sCallbacks, this)) { JS_ClearPendingException(aCx); aRv.Throw(NS_ERROR_DOM_DATA_CLONE_ERR); } } @@ -348,20 +350,20 @@ JSObject* StructuredCloneHolder::ReadFul } JS::RootedValue result(aCx); { // nsJSPrincipals::ReadKnownPrincipalType addrefs for us, but because of // the casting between JSPrincipals* and nsIPrincipal* we can't use // getter_AddRefs above and have to already_AddRefed here. nsCOMPtr<nsIPrincipal> principal = - already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin)); + already_AddRefed<nsIPrincipal>(nsJSPrincipals::get(prin)); nsresult rv = nsContentUtils::WrapNative( - aCx, principal, &NS_GET_IID(nsIPrincipal), &result); + aCx, principal, &NS_GET_IID(nsIPrincipal), &result); if (NS_FAILED(rv)) { xpc::Throw(aCx, NS_ERROR_DOM_DATA_CLONE_ERR); return nullptr; } } return result.toObjectOrNull(); } @@ -444,17 +446,21 @@ JSObject* ReadBlob(JSContext* aCx, uint3 // toObject() is called because the static analysis thinks releasing XPCOM // objects can GC (because in some cases it can!), and a return statement // with a JSObject* type means that JSObject* is on the stack as a raw // pointer while destructors are running. RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[aIndex]; MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false)); - RefPtr<Blob> blob = Blob::Create(aHolder->ParentDuringRead(), blobImpl); + RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl); + if (NS_WARN_IF(!blob)) { + return nullptr; + } + if (!ToJSValue(aCx, blob, &val)) { return nullptr; } } return &val.toObject(); } @@ -516,17 +522,17 @@ already_AddRefed<Directory> ReadDirector nsCOMPtr<nsIFile> file; nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } RefPtr<Directory> directory = - Directory::Create(aHolder->ParentDuringRead(), file); + Directory::Create(aHolder->GlobalDuringRead(), file); return directory.forget(); } JSObject* ReadDirectory(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aPathLength, StructuredCloneHolder* aHolder) { MOZ_ASSERT(aCx); MOZ_ASSERT(aReader); MOZ_ASSERT(aHolder); @@ -555,17 +561,17 @@ JSObject* ReadDirectory(JSContext* aCx, // Read the WriteFileList for the format. JSObject* ReadFileList(JSContext* aCx, JSStructuredCloneReader* aReader, uint32_t aCount, StructuredCloneHolder* aHolder) { MOZ_ASSERT(aCx); MOZ_ASSERT(aReader); JS::Rooted<JS::Value> val(aCx); { - RefPtr<FileList> fileList = new FileList(aHolder->ParentDuringRead()); + RefPtr<FileList> fileList = new FileList(aHolder->GlobalDuringRead()); uint32_t zero, index; // |index| is the index of the first blobImpl. if (!JS_ReadUint32Pair(aReader, &zero, &index)) { return nullptr; } MOZ_ASSERT(zero == 0); @@ -580,17 +586,21 @@ JSObject* ReadFileList(JSContext* aCx, J #endif MOZ_ASSERT(pos < aHolder->BlobImpls().Length()); RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[pos]; MOZ_ASSERT(blobImpl->IsFile()); MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false)); - RefPtr<File> file = File::Create(aHolder->ParentDuringRead(), blobImpl); + RefPtr<File> file = File::Create(aHolder->GlobalDuringRead(), blobImpl); + if (NS_WARN_IF(!file)) { + return nullptr; + } + if (!fileList->Append(file)) { return nullptr; } } if (!ToJSValue(aCx, fileList, &val)) { return nullptr; } @@ -633,17 +643,17 @@ JSObject* ReadFormData(JSContext* aCx, J uint32_t aCount, StructuredCloneHolder* aHolder) { MOZ_ASSERT(aCx); MOZ_ASSERT(aReader); MOZ_ASSERT(aHolder); // See the serialization of the FormData for the format. JS::Rooted<JS::Value> val(aCx); { - RefPtr<FormData> formData = new FormData(aHolder->ParentDuringRead()); + RefPtr<FormData> formData = new FormData(aHolder->GlobalDuringRead()); Optional<nsAString> thirdArg; for (uint32_t i = 0; i < aCount; ++i) { nsAutoString name; if (!StructuredCloneHolder::ReadString(aReader, name)) { return nullptr; } @@ -658,18 +668,20 @@ JSObject* ReadFormData(JSContext* aCx, J return nullptr; } #endif MOZ_ASSERT(indexOrLengthOfString < aHolder->BlobImpls().Length()); RefPtr<BlobImpl> blobImpl = aHolder->BlobImpls()[indexOrLengthOfString]; MOZ_ALWAYS_SUCCEEDS(blobImpl->SetMutable(false)); - RefPtr<Blob> blob = Blob::Create(aHolder->ParentDuringRead(), blobImpl); - MOZ_ASSERT(blob); + RefPtr<Blob> blob = Blob::Create(aHolder->GlobalDuringRead(), blobImpl); + if (NS_WARN_IF(!blob)) { + return nullptr; + } ErrorResult rv; formData->Append(name, *blob, thirdArg, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); return nullptr; } @@ -833,17 +845,17 @@ JSObject* ReadInputStream(JSContext* aCx } #endif MOZ_ASSERT(aIndex < aHolder->InputStreams().Length()); JS::RootedValue result(aCx); { nsCOMPtr<nsIInputStream> inputStream = aHolder->InputStreams()[aIndex]; nsresult rv = nsContentUtils::WrapNative( - aCx, inputStream, &NS_GET_IID(nsIInputStream), &result); + aCx, inputStream, &NS_GET_IID(nsIInputStream), &result); if (NS_FAILED(rv)) { return nullptr; } } return &result.toObject(); } @@ -890,19 +902,18 @@ JSObject* StructuredCloneHolder::CustomR if (aTag == SCTAG_DOM_IMAGEBITMAP && (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { // Get the current global object. // This can be null. JS::RootedObject result(aCx); { - nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent); // aIndex is the index of the cloned image. - result = ImageBitmap::ReadStructuredClone(aCx, aReader, parent, + result = ImageBitmap::ReadStructuredClone(aCx, aReader, mGlobal, GetSurfaces(), aIndex); } return result; } if (aTag == SCTAG_DOM_STRUCTURED_CLONE_HOLDER) { return StructuredCloneBlob::ReadStructuredClone(aCx, aReader, this); } @@ -1027,20 +1038,18 @@ bool StructuredCloneHolder::CustomReadTr #ifdef FUZZING if (aExtraData >= mPortIdentifiers.Length()) { return false; } #endif MOZ_ASSERT(aExtraData < mPortIdentifiers.Length()); const MessagePortIdentifier& portIdentifier = mPortIdentifiers[aExtraData]; - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent); - ErrorResult rv; - RefPtr<MessagePort> port = MessagePort::Create(global, portIdentifier, rv); + RefPtr<MessagePort> port = MessagePort::Create(mGlobal, portIdentifier, rv); if (NS_WARN_IF(rv.Failed())) { rv.SuppressException(); return false; } mTransferredPorts.AppendElement(port); JS::Rooted<JS::Value> value(aCx); @@ -1055,19 +1064,18 @@ bool StructuredCloneHolder::CustomReadTr if (aTag == SCTAG_DOM_CANVAS && (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); OffscreenCanvasCloneData* data = static_cast<OffscreenCanvasCloneData*>(aContent); - nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent); RefPtr<OffscreenCanvas> canvas = - OffscreenCanvas::CreateFromCloneData(parent, data); + OffscreenCanvas::CreateFromCloneData(mGlobal, data); delete data; JS::Rooted<JS::Value> value(aCx); if (!GetOrCreateDOMReflector(aCx, canvas, &value)) { JS_ClearPendingException(aCx); return false; } @@ -1076,18 +1084,18 @@ bool StructuredCloneHolder::CustomReadTr } if (aTag == SCTAG_DOM_IMAGEBITMAP && (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); ImageBitmapCloneData* data = static_cast<ImageBitmapCloneData*>(aContent); - nsCOMPtr<nsIGlobalObject> parent = do_QueryInterface(mParent); - RefPtr<ImageBitmap> bitmap = ImageBitmap::CreateFromCloneData(parent, data); + RefPtr<ImageBitmap> bitmap = + ImageBitmap::CreateFromCloneData(mGlobal, data); delete data; JS::Rooted<JS::Value> value(aCx); if (!GetOrCreateDOMReflector(aCx, bitmap, &value)) { JS_ClearPendingException(aCx); return false; }
--- a/dom/base/StructuredCloneHolder.h +++ b/dom/base/StructuredCloneHolder.h @@ -8,23 +8,23 @@ #define mozilla_dom_StructuredCloneHolder_h #include "jsapi.h" #include "js/StructuredClone.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Move.h" #include "mozilla/UniquePtr.h" #include "mozilla/dom/BindingDeclarations.h" -#include "nsISupports.h" #include "nsTArray.h" #ifdef DEBUG # include "nsIThread.h" #endif +class nsIGlobalObject; class nsIInputStream; namespace mozilla { class ErrorResult; namespace layers { class Image; } @@ -157,17 +157,17 @@ class StructuredCloneHolder : public Str // Normally you should just use Write() and Read(). void Write(JSContext* aCx, JS::Handle<JS::Value> aValue, ErrorResult& aRv); void Write(JSContext* aCx, JS::Handle<JS::Value> aValue, JS::Handle<JS::Value> aTransfer, JS::CloneDataPolicy cloneDataPolicy, ErrorResult& aRv); - void Read(nsISupports* aParent, JSContext* aCx, + void Read(nsIGlobalObject* aGlobal, JSContext* aCx, JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv); // Call this method to know if this object is keeping some DOM object alive. bool HasClonedDOMObjects() const { return !mBlobImplArray.IsEmpty() || !mWasmModuleArray.IsEmpty() || !mClonedSurfaces.IsEmpty() || !mInputStreamArray.IsEmpty(); } @@ -186,19 +186,19 @@ class StructuredCloneHolder : public Str nsTArray<nsCOMPtr<nsIInputStream>>& InputStreams() { MOZ_ASSERT(mSupportsCloning, "InputStreams cannot be taken/set if cloning is not supported."); return mInputStreamArray; } StructuredCloneScope CloneScope() const { return mStructuredCloneScope; } - // The parent object is set internally just during the Read(). This method + // The global object is set internally just during the Read(). This method // can be used by read functions to retrieve it. - nsISupports* ParentDuringRead() const { return mParent; } + nsIGlobalObject* GlobalDuringRead() const { return mGlobal; } // This must be called if the transferring has ports generated by Read(). // MessagePorts are not thread-safe and they must be retrieved in the thread // where they are created. nsTArray<RefPtr<MessagePort>>&& TakeTransferredPorts() { MOZ_ASSERT(mSupportsTransferring); return std::move(mTransferredPorts); } @@ -265,21 +265,21 @@ class StructuredCloneHolder : public Str const nsString& aString); static const JSStructuredCloneCallbacks sCallbacks; protected: // If you receive a buffer from IPC, you can use this method to retrieve a // JS::Value. It can happen that you want to pre-populate the array of Blobs // and/or the PortIdentifiers. - void ReadFromBuffer(nsISupports* aParent, JSContext* aCx, + void ReadFromBuffer(nsIGlobalObject* aGlobal, JSContext* aCx, JSStructuredCloneData& aBuffer, JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv); - void ReadFromBuffer(nsISupports* aParent, JSContext* aCx, + void ReadFromBuffer(nsIGlobalObject* aGlobal, JSContext* aCx, JSStructuredCloneData& aBuffer, uint32_t aAlgorithmVersion, JS::MutableHandle<JS::Value> aValue, ErrorResult& aRv); bool mSupportsCloning; bool mSupportsTransferring; // SizeOfExcludingThis is inherited from StructuredCloneHolderBase. It doesn't @@ -299,17 +299,17 @@ class StructuredCloneHolder : public Str // This is used for sharing the backend of ImageBitmaps. // The DataSourceSurface object must be thread-safely reference-counted. // The DataSourceSurface object will not be written ever via any ImageBitmap // instance, so no race condition will occur. nsTArray<RefPtr<gfx::DataSourceSurface>> mClonedSurfaces; // This raw pointer is only set within ::Read() and is unset by the end. - nsISupports* MOZ_NON_OWNING_REF mParent; + nsIGlobalObject* MOZ_NON_OWNING_REF mGlobal; // This array contains the ports once we've finished the reading. It's // generated from the mPortIdentifiers array. nsTArray<RefPtr<MessagePort>> mTransferredPorts; // This array contains the identifiers of the MessagePorts. Based on these we // are able to reconnect the new transferred ports with the other // MessageChannel ports.
--- a/dom/base/nsDOMDataChannel.cpp +++ b/dom/base/nsDOMDataChannel.cpp @@ -279,18 +279,20 @@ nsresult nsDOMDataChannel::DoOnMessageAv } JSContext* cx = jsapi.cx(); JS::Rooted<JS::Value> jsData(cx); if (aBinary) { if (mBinaryType == DC_BINARY_TYPE_BLOB) { RefPtr<Blob> blob = - Blob::CreateStringBlob(GetOwner(), aData, EmptyString()); - MOZ_ASSERT(blob); + Blob::CreateStringBlob(GetOwnerGlobal(), aData, EmptyString()); + if (NS_WARN_IF(!blob)) { + return NS_ERROR_FAILURE; + } if (!ToJSValue(cx, blob, &jsData)) { return NS_ERROR_FAILURE; } } else if (mBinaryType == DC_BINARY_TYPE_ARRAYBUFFER) { JS::Rooted<JSObject*> arrayBuf(cx); rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address()); NS_ENSURE_SUCCESS(rv, rv);
--- a/dom/canvas/CanvasRenderingContextHelper.cpp +++ b/dom/canvas/CanvasRenderingContextHelper.cpp @@ -27,29 +27,29 @@ void CanvasRenderingContextHelper::ToBlo // Encoder callback when encoding is complete. class EncodeCallback : public EncodeCompleteCallback { public: EncodeCallback(nsIGlobalObject* aGlobal, BlobCallback* aCallback) : mGlobal(aGlobal), mBlobCallback(aCallback) {} // This is called on main thread. MOZ_CAN_RUN_SCRIPT - nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) override { - RefPtr<Blob> blob = aBlob; + nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override { + RefPtr<BlobImpl> blobImpl = aBlobImpl; - RefPtr<Blob> newBlob; + RefPtr<Blob> blob; - if (blob) { - newBlob = Blob::Create(mGlobal, blob->Impl()); + if (blobImpl) { + blob = Blob::Create(mGlobal, blobImpl); } RefPtr<BlobCallback> callback(mBlobCallback.forget()); ErrorResult rv; - callback->Call(newBlob, rv); + callback->Call(blob, rv); mGlobal = nullptr; MOZ_ASSERT(!mBlobCallback); return rv.StealNSResult(); } nsCOMPtr<nsIGlobalObject> mGlobal;
--- a/dom/canvas/OffscreenCanvas.cpp +++ b/dom/canvas/OffscreenCanvas.cpp @@ -224,22 +224,26 @@ already_AddRefed<Promise> OffscreenCanva // Encoder callback when encoding is complete. class EncodeCallback : public EncodeCompleteCallback { public: EncodeCallback(nsIGlobalObject* aGlobal, Promise* aPromise) : mGlobal(aGlobal), mPromise(aPromise) {} // This is called on main thread. - nsresult ReceiveBlob(already_AddRefed<Blob> aBlob) override { - RefPtr<Blob> blob = aBlob; + nsresult ReceiveBlobImpl(already_AddRefed<BlobImpl> aBlobImpl) override { + RefPtr<BlobImpl> blobImpl = aBlobImpl; if (mPromise) { - RefPtr<Blob> newBlob = Blob::Create(mGlobal, blob->Impl()); - mPromise->MaybeResolve(newBlob); + RefPtr<Blob> blob = Blob::Create(mGlobal, blobImpl); + if (NS_WARN_IF(!blob)) { + mPromise->MaybeReject(NS_ERROR_FAILURE); + } else { + mPromise->MaybeResolve(blob); + } } mGlobal = nullptr; mPromise = nullptr; return NS_OK; }
--- a/dom/console/Console.cpp +++ b/dom/console/Console.cpp @@ -67,17 +67,17 @@ #define STORAGE_MAX_EVENTS 1000 using namespace mozilla::dom::exceptions; namespace mozilla { namespace dom { struct ConsoleStructuredCloneData { - nsCOMPtr<nsISupports> mParent; + nsCOMPtr<nsIGlobalObject> mGlobal; nsTArray<RefPtr<BlobImpl>> mBlobs; }; /** * Console API in workers uses the Structured Clone Algorithm to move any value * from the worker thread to the main-thread. Some object cannot be moved and, * in these cases, we convert them to strings. * It's not the best, but at least we are able to show something. @@ -286,18 +286,19 @@ class ConsoleRunnable : public Structure uint32_t aTag, uint32_t aIndex) override { AssertIsOnMainThread(); if (aTag == CONSOLE_TAG_BLOB) { MOZ_ASSERT(mClonedData.mBlobs.Length() > aIndex); JS::Rooted<JS::Value> val(aCx); { - RefPtr<Blob> blob = Blob::Create(mClonedData.mParent, - mClonedData.mBlobs.ElementAt(aIndex)); + nsCOMPtr<nsIGlobalObject> global = mClonedData.mGlobal; + RefPtr<Blob> blob = + Blob::Create(global, mClonedData.mBlobs.ElementAt(aIndex)); if (!ToJSValue(aCx, blob, &val)) { return nullptr; } } return &val.toObject(); } @@ -446,17 +447,17 @@ class ConsoleRunnable : public Structure void ProcessProfileData(JSContext* aCx, Console::MethodName aMethodName, const nsAString& aAction) { AssertIsOnMainThread(); ConsoleCommon::ClearException ce(aCx); JS::Rooted<JS::Value> argumentsValue(aCx); bool ok = Read(aCx, &argumentsValue); - mClonedData.mParent = nullptr; + mClonedData.mGlobal = nullptr; if (!ok) { return; } MOZ_ASSERT(argumentsValue.isObject()); JS::Rooted<JSObject*> argumentsObj(aCx, &argumentsValue.toObject()); if (NS_WARN_IF(!argumentsObj)) { @@ -652,17 +653,18 @@ class ConsoleWorkerRunnable : public Wor return; } nsCOMPtr<nsPIDOMWindowOuter> outerWindow = aWindow->GetOuterWindow(); if (NS_WARN_IF(!outerWindow)) { return; } - RunConsole(jsapi.cx(), aWorkerPrivate, outerWindow, aWindow); + RunConsole(jsapi.cx(), aWindow->AsGlobal(), aWorkerPrivate, outerWindow, + aWindow); } void RunWindowless(WorkerPrivate* aWorkerPrivate) { MOZ_ASSERT(aWorkerPrivate); AssertIsOnMainThread(); WorkerPrivate* wp = aWorkerPrivate; while (wp->GetParent()) { @@ -683,31 +685,37 @@ class ConsoleWorkerRunnable : public Wor } // The GetOrCreateSandbox call returns a proxy to the actual sandbox object. // We don't need a proxy here. global = js::UncheckedUnwrap(global); JSAutoRealm ar(cx, global); - RunConsole(cx, aWorkerPrivate, nullptr, nullptr); + nsCOMPtr<nsIGlobalObject> globalObject = xpc::NativeGlobal(global); + if (NS_WARN_IF(!globalObject)) { + return; + } + + RunConsole(cx, globalObject, aWorkerPrivate, nullptr, nullptr); } void RunBackOnWorkerThreadForCleanup(WorkerPrivate* aWorkerPrivate) override { MOZ_ASSERT(aWorkerPrivate); aWorkerPrivate->AssertIsOnWorkerThread(); ReleaseData(); mConsole = nullptr; } // This method is called in the owning thread of the Console object. virtual bool PreDispatch(JSContext* aCx) = 0; // This method is called in the main-thread. - virtual void RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + virtual void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal, + WorkerPrivate* aWorkerPrivate, nsPIDOMWindowOuter* aOuterWindow, nsPIDOMWindowInner* aInnerWindow) = 0; // This method is called in the owning thread of the Console object. virtual void ReleaseData() = 0; bool ForMessaging() const override { return true; } @@ -730,19 +738,21 @@ class ConsoleCallDataWorkerRunnable fina private: ~ConsoleCallDataWorkerRunnable() override { MOZ_ASSERT(!mCallData); } bool PreDispatch(JSContext* aCx) override { return StoreConsoleData(aCx, mCallData); } - void RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal, + WorkerPrivate* aWorkerPrivate, nsPIDOMWindowOuter* aOuterWindow, nsPIDOMWindowInner* aInnerWindow) override { + MOZ_ASSERT(aGlobal); MOZ_ASSERT(aWorkerPrivate); AssertIsOnMainThread(); // The windows have to run in parallel. MOZ_ASSERT(!!aOuterWindow == !!aInnerWindow); if (aOuterWindow) { mCallData->SetIDs(aOuterWindow->WindowID(), aInnerWindow->WindowID()); @@ -763,22 +773,21 @@ class ConsoleCallDataWorkerRunnable fina CopyASCIItoUTF16(aWorkerPrivate->ServiceWorkerScope(), id); } else { innerID = NS_LITERAL_STRING("Worker"); } mCallData->SetIDs(id, innerID); } - // Now we could have the correct window (if we are not window-less). - mClonedData.mParent = aInnerWindow; + mClonedData.mGlobal = aGlobal; ProcessCallData(aCx, mConsole, mCallData); - mClonedData.mParent = nullptr; + mClonedData.mGlobal = nullptr; } virtual void ReleaseData() override { ReleaseCallData(mConsole, mCallData); mCallData = nullptr; } RefPtr<ConsoleCallData> mCallData; @@ -851,27 +860,28 @@ class ConsoleProfileWorkerRunnable final MOZ_ASSERT(aConsole); } private: bool PreDispatch(JSContext* aCx) override { return StoreProfileData(aCx, mArguments); } - void RunConsole(JSContext* aCx, WorkerPrivate* aWorkerPrivate, + void RunConsole(JSContext* aCx, nsIGlobalObject* aGlobal, + WorkerPrivate* aWorkerPrivate, nsPIDOMWindowOuter* aOuterWindow, nsPIDOMWindowInner* aInnerWindow) override { AssertIsOnMainThread(); - - // Now we could have the correct window (if we are not window-less). - mClonedData.mParent = aInnerWindow; + MOZ_ASSERT(aGlobal); + + mClonedData.mGlobal = aGlobal; ProcessProfileData(aCx, mName, mAction); - mClonedData.mParent = nullptr; + mClonedData.mGlobal = nullptr; } virtual void ReleaseData() override {} Console::MethodName mName; nsString mAction; // This is a reference of the sequence of arguments we receive from the DOM
--- a/dom/events/DataTransferItem.cpp +++ b/dom/events/DataTransferItem.cpp @@ -281,52 +281,53 @@ already_AddRefed<File> DataTransferItem: MOZ_ASSERT(!aRv.Failed() && supports, "File objects should be stored as nsISupports variants"); if (aRv.Failed() || !supports) { return nullptr; } if (RefPtr<Blob> blob = do_QueryObject(supports)) { mCachedFile = blob->ToFile(); - } else if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports)) { - MOZ_ASSERT(blobImpl->IsFile()); - mCachedFile = File::Create(mDataTransfer, blobImpl); - } else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(supports)) { - mCachedFile = File::CreateFromFile(mDataTransfer, ifile); } else { - MOZ_ASSERT(false, "One of the above code paths should be taken"); - return nullptr; + nsCOMPtr<nsIGlobalObject> global = GetGlobalFromDataTransfer(); + if (NS_WARN_IF(!global)) { + return nullptr; + } + + if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports)) { + MOZ_ASSERT(blobImpl->IsFile()); + mCachedFile = File::Create(global, blobImpl); + if (NS_WARN_IF(!mCachedFile)) { + return nullptr; + } + } else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(supports)) { + mCachedFile = File::CreateFromFile(global, ifile); + if (NS_WARN_IF(!mCachedFile)) { + return nullptr; + } + } else { + MOZ_ASSERT(false, "One of the above code paths should be taken"); + return nullptr; + } } } RefPtr<File> file = mCachedFile; return file.forget(); } already_AddRefed<FileSystemEntry> DataTransferItem::GetAsEntry( nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv); if (NS_WARN_IF(aRv.Failed()) || !file) { return nullptr; } - nsCOMPtr<nsIGlobalObject> global; - // This is annoying, but DataTransfer may have various things as parent. - nsCOMPtr<EventTarget> target = - do_QueryInterface(mDataTransfer->GetParentObject()); - if (target) { - global = target->GetOwnerGlobal(); - } else { - RefPtr<Event> event = do_QueryObject(mDataTransfer->GetParentObject()); - if (event) { - global = event->GetParentObject(); - } - } - - if (!global) { + nsCOMPtr<nsIGlobalObject> global = GetGlobalFromDataTransfer(); + if (NS_WARN_IF(!global)) { return nullptr; } RefPtr<FileSystem> fs = FileSystem::Create(global); RefPtr<FileSystemEntry> entry; BlobImpl* impl = file->Impl(); MOZ_ASSERT(impl); @@ -385,17 +386,22 @@ already_AddRefed<File> DataTransferItem: uint64_t available; void* data = nullptr; rv = NS_ReadInputStreamToBuffer(aStream, &data, -1, &available); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } - return File::CreateMemoryFile(mDataTransfer, data, available, fileName, mType, + nsCOMPtr<nsIGlobalObject> global = GetGlobalFromDataTransfer(); + if (NS_WARN_IF(!global)) { + return nullptr; + } + + return File::CreateMemoryFile(global, data, available, fileName, mType, PR_Now()); } void DataTransferItem::GetAsString(FunctionStringCallback* aCallback, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { if (!aCallback) { return; @@ -546,10 +552,28 @@ already_AddRefed<nsIVariant> DataTransfe return nullptr; } } } return variant.forget(); } +already_AddRefed<nsIGlobalObject> +DataTransferItem::GetGlobalFromDataTransfer() { + nsCOMPtr<nsIGlobalObject> global; + // This is annoying, but DataTransfer may have various things as parent. + nsCOMPtr<EventTarget> target = + do_QueryInterface(mDataTransfer->GetParentObject()); + if (target) { + global = target->GetOwnerGlobal(); + } else { + RefPtr<Event> event = do_QueryObject(mDataTransfer->GetParentObject()); + if (event) { + global = event->GetParentObject(); + } + } + + return global.forget(); +} + } // namespace dom } // namespace mozilla
--- a/dom/events/DataTransferItem.h +++ b/dom/events/DataTransferItem.h @@ -100,16 +100,18 @@ class DataTransferItem final : public ns void SetChromeOnly(bool aChromeOnly) { mChromeOnly = aChromeOnly; } static eKind KindFromData(nsIVariant* aData); private: ~DataTransferItem() {} already_AddRefed<File> CreateFileFromInputStream(nsIInputStream* aStream); + already_AddRefed<nsIGlobalObject> GetGlobalFromDataTransfer(); + // The index in the 2d mIndexedItems array uint32_t mIndex; bool mChromeOnly; eKind mKind; const nsString mType; nsCOMPtr<nsIVariant> mData; nsCOMPtr<nsIPrincipal> mPrincipal;
--- a/dom/file/Blob.cpp +++ b/dom/file/Blob.cpp @@ -8,34 +8,35 @@ #include "EmptyBlobImpl.h" #include "File.h" #include "MemoryBlobImpl.h" #include "mozilla/dom/BlobBinding.h" #include "mozilla/dom/BodyStream.h" #include "mozilla/dom/WorkerCommon.h" #include "mozilla/dom/WorkerPrivate.h" #include "MultipartBlobImpl.h" +#include "nsIGlobalObject.h" #include "nsIInputStream.h" #include "nsPIDOMWindow.h" #include "StreamBlobImpl.h" #include "StringBlobImpl.h" #include "js/GCAPI.h" namespace mozilla { namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(Blob) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Blob) - NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Blob) - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(Blob) NS_IMPL_CYCLE_COLLECTION_TRACE_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_TRACE_END NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Blob) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY @@ -62,54 +63,62 @@ void Blob::MakeValidBlobType(nsAString& if (c >= 'A' && c <= 'Z') { *iter = c + ('a' - 'A'); } } } /* static */ -Blob* Blob::Create(nsISupports* aParent, BlobImpl* aImpl) { +Blob* Blob::Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl) { MOZ_ASSERT(aImpl); - return aImpl->IsFile() ? new File(aParent, aImpl) : new Blob(aParent, aImpl); + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + + return aImpl->IsFile() ? new File(aGlobal, aImpl) : new Blob(aGlobal, aImpl); } /* static */ -already_AddRefed<Blob> Blob::CreateEmptyBlob(nsISupports* aParent, - const nsAString& aContentType) { - RefPtr<Blob> blob = Blob::Create(aParent, new EmptyBlobImpl(aContentType)); +already_AddRefed<Blob> Blob::CreateStringBlob(nsIGlobalObject* aGlobal, + const nsACString& aData, + const nsAString& aContentType) { + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + + RefPtr<BlobImpl> blobImpl = StringBlobImpl::Create(aData, aContentType); + RefPtr<Blob> blob = Blob::Create(aGlobal, blobImpl); MOZ_ASSERT(!blob->mImpl->IsFile()); return blob.forget(); } /* static */ -already_AddRefed<Blob> Blob::CreateStringBlob(nsISupports* aParent, - const nsACString& aData, +already_AddRefed<Blob> Blob::CreateMemoryBlob(nsIGlobalObject* aGlobal, + void* aMemoryBuffer, + uint64_t aLength, const nsAString& aContentType) { - RefPtr<BlobImpl> blobImpl = StringBlobImpl::Create(aData, aContentType); - RefPtr<Blob> blob = Blob::Create(aParent, blobImpl); + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + + RefPtr<Blob> blob = Blob::Create( + aGlobal, new MemoryBlobImpl(aMemoryBuffer, aLength, aContentType)); MOZ_ASSERT(!blob->mImpl->IsFile()); return blob.forget(); } -/* static */ -already_AddRefed<Blob> Blob::CreateMemoryBlob(nsISupports* aParent, - void* aMemoryBuffer, - uint64_t aLength, - const nsAString& aContentType) { - RefPtr<Blob> blob = Blob::Create( - aParent, new MemoryBlobImpl(aMemoryBuffer, aLength, aContentType)); - MOZ_ASSERT(!blob->mImpl->IsFile()); - return blob.forget(); -} - -Blob::Blob(nsISupports* aParent, BlobImpl* aImpl) - : mImpl(aImpl), mParent(aParent) { +Blob::Blob(nsIGlobalObject* aGlobal, BlobImpl* aImpl) + : mImpl(aImpl), mGlobal(aGlobal) { MOZ_ASSERT(mImpl); + MOZ_ASSERT(mGlobal); } Blob::~Blob() = default; bool Blob::IsFile() const { return mImpl->IsFile(); } const nsTArray<RefPtr<BlobImpl>>* Blob::GetSubBlobImpls() const { return mImpl->GetSubBlobImpls(); @@ -119,17 +128,17 @@ already_AddRefed<File> Blob::ToFile() { if (!mImpl->IsFile()) { return nullptr; } RefPtr<File> file; if (HasFileInterface()) { file = static_cast<File*>(this); } else { - file = new File(mParent, mImpl); + file = new File(mGlobal, mImpl); } return file.forget(); } already_AddRefed<File> Blob::ToFile(const nsAString& aName, ErrorResult& aRv) const { AutoTArray<RefPtr<BlobImpl>, 1> blobImpls({mImpl}); @@ -138,30 +147,30 @@ already_AddRefed<File> Blob::ToFile(cons mImpl->GetType(contentType); RefPtr<MultipartBlobImpl> impl = MultipartBlobImpl::Create(std::move(blobImpls), aName, contentType, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - RefPtr<File> file = new File(mParent, impl); + RefPtr<File> file = new File(mGlobal, impl); return file.forget(); } already_AddRefed<Blob> Blob::CreateSlice(uint64_t aStart, uint64_t aLength, const nsAString& aContentType, ErrorResult& aRv) { RefPtr<BlobImpl> impl = mImpl->CreateSlice(aStart, aLength, aContentType, aRv); if (aRv.Failed()) { return nullptr; } - RefPtr<Blob> blob = Blob::Create(mParent, impl); + RefPtr<Blob> blob = Blob::Create(mGlobal, impl); return blob.forget(); } uint64_t Blob::GetSize(ErrorResult& aRv) { return mImpl->GetSize(aRv); } void Blob::GetType(nsAString& aType) { mImpl->GetType(aType); } void Blob::GetBlobImplType(nsAString& aBlobImplType) { @@ -177,17 +186,17 @@ already_AddRefed<Blob> Blob::Slice(const contentType = aContentType.Value(); } RefPtr<BlobImpl> impl = mImpl->Slice(aStart, aEnd, contentType, aRv); if (aRv.Failed()) { return nullptr; } - RefPtr<Blob> blob = Blob::Create(mParent, impl); + RefPtr<Blob> blob = Blob::Create(mGlobal, impl); return blob.forget(); } size_t Blob::GetAllocationSize() const { return mImpl->GetAllocationSize(); } // contentTypeWithCharset can be set to the contentType or // contentType+charset based on what the spec says. // See: https://fetch.spec.whatwg.org/#concept-bodyinit-extract @@ -223,17 +232,20 @@ already_AddRefed<Blob> Blob::Constructor } if (NS_WARN_IF(aRv.Failed())) { return nullptr; } MOZ_ASSERT(!impl->IsFile()); - RefPtr<Blob> blob = Blob::Create(aGlobal.GetAsSupports(), impl); + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_ASSERT(global); + + RefPtr<Blob> blob = Blob::Create(global, impl); return blob.forget(); } int64_t Blob::GetFileId() { return mImpl->GetFileId(); } bool Blob::IsMemoryFile() const { return mImpl->IsMemoryFile(); } void Blob::CreateInputStream(nsIInputStream** aStream, ErrorResult& aRv) { @@ -256,40 +268,39 @@ already_AddRefed<Promise> Blob::Text(Err } already_AddRefed<Promise> Blob::ArrayBuffer(ErrorResult& aRv) { return ConsumeBody(BodyConsumer::CONSUME_ARRAYBUFFER, aRv); } already_AddRefed<Promise> Blob::ConsumeBody( BodyConsumer::ConsumeType aConsumeType, ErrorResult& aRv) { - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(mParent); - if (NS_WARN_IF(!global)) { + if (NS_WARN_IF(!mGlobal)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } nsCOMPtr<nsIEventTarget> mainThreadEventTarget; if (!NS_IsMainThread()) { WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); MOZ_ASSERT(workerPrivate); mainThreadEventTarget = workerPrivate->MainThreadEventTarget(); } else { - mainThreadEventTarget = global->EventTargetFor(TaskCategory::Other); + mainThreadEventTarget = mGlobal->EventTargetFor(TaskCategory::Other); } MOZ_ASSERT(mainThreadEventTarget); nsCOMPtr<nsIInputStream> inputStream; CreateInputStream(getter_AddRefs(inputStream), aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - return BodyConsumer::Create(global, mainThreadEventTarget, inputStream, + return BodyConsumer::Create(mGlobal, mainThreadEventTarget, inputStream, nullptr, aConsumeType, VoidCString(), VoidString(), VoidCString(), MutableBlobStorage::eOnlyInMemory, aRv); } namespace { class BlobBodyStreamHolder final : public BodyStreamHolder { @@ -348,25 +359,24 @@ NS_INTERFACE_MAP_END_INHERITING(BodyStre void Blob::Stream(JSContext* aCx, JS::MutableHandle<JSObject*> aStream, ErrorResult& aRv) { nsCOMPtr<nsIInputStream> stream; CreateInputStream(getter_AddRefs(stream), aRv); if (NS_WARN_IF(aRv.Failed())) { return; } - nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(GetParentObject()); - if (NS_WARN_IF(!global)) { + if (NS_WARN_IF(!mGlobal)) { aRv.Throw(NS_ERROR_FAILURE); return; } RefPtr<BlobBodyStreamHolder> holder = new BlobBodyStreamHolder(); - BodyStream::Create(aCx, holder, global, stream, aRv); + BodyStream::Create(aCx, holder, mGlobal, stream, aRv); if (NS_WARN_IF(aRv.Failed())) { return; } aStream.set(holder->GetReadableStreamBody()); } } // namespace dom
--- a/dom/file/Blob.h +++ b/dom/file/Blob.h @@ -13,16 +13,17 @@ #include "mozilla/dom/BlobImpl.h" #include "mozilla/dom/BodyConsumer.h" #include "nsCycleCollectionParticipant.h" #include "nsCOMPtr.h" #include "nsIMutable.h" #include "nsWrapperCache.h" #include "nsWeakReference.h" +class nsIGlobalObject; class nsIInputStream; namespace mozilla { namespace dom { struct BlobPropertyBag; class File; class OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString; @@ -43,29 +44,26 @@ class Blob : public nsIMutable, NS_DECL_CYCLE_COLLECTING_ISUPPORTS_FINAL NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(Blob, nsIMutable) NS_DECLARE_STATIC_IID_ACCESSOR(NS_DOM_BLOB_IID) typedef OwningArrayBufferViewOrArrayBufferOrBlobOrUSVString BlobPart; // This creates a Blob or a File based on the type of BlobImpl. - static Blob* Create(nsISupports* aParent, BlobImpl* aImpl); + static Blob* Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl); - static already_AddRefed<Blob> CreateEmptyBlob(nsISupports* aParent, - const nsAString& aContentType); - - static already_AddRefed<Blob> CreateStringBlob(nsISupports* aParent, + static already_AddRefed<Blob> CreateStringBlob(nsIGlobalObject* aGlobal, const nsACString& aData, const nsAString& aContentType); // The returned Blob takes ownership of aMemoryBuffer. aMemoryBuffer will be // freed by free so it must be allocated by malloc or something // compatible with it. - static already_AddRefed<Blob> CreateMemoryBlob(nsISupports* aParent, + static already_AddRefed<Blob> CreateMemoryBlob(nsIGlobalObject* aGlobal, void* aMemoryBuffer, uint64_t aLength, const nsAString& aContentType); BlobImpl* Impl() const { return mImpl; } bool IsFile() const; @@ -92,17 +90,17 @@ class Blob : public nsIMutable, // blob: no codepoints outside the ASCII range (otherwise type becomes empty) // and lowercase ASCII only. We can't just use our existing nsContentUtils // ASCII-related helpers because we need the "outside ASCII range" check, and // we can't use NS_IsAscii because its definition of "ASCII" (chars all <= // 0x7E) differs from the file API definition (which excludes control chars). static void MakeValidBlobType(nsAString& aType); // WebIDL methods - nsISupports* GetParentObject() const { return mParent; } + nsIGlobalObject* GetParentObject() const { return mGlobal; } bool IsMemoryFile() const; // Blob constructor static already_AddRefed<Blob> Constructor( const GlobalObject& aGlobal, const Optional<Sequence<BlobPart>>& aData, const BlobPropertyBag& aBag, ErrorResult& aRv); @@ -127,32 +125,32 @@ class Blob : public nsIMutable, void Stream(JSContext* aCx, JS::MutableHandle<JSObject*> aStream, ErrorResult& aRv); already_AddRefed<Promise> Text(ErrorResult& aRv); already_AddRefed<Promise> ArrayBuffer(ErrorResult& aRv); protected: // File constructor should never be used directly. Use Blob::Create instead. - Blob(nsISupports* aParent, BlobImpl* aImpl); + Blob(nsIGlobalObject* aGlobal, BlobImpl* aImpl); virtual ~Blob(); virtual bool HasFileInterface() const { return false; } already_AddRefed<Promise> ConsumeBody(BodyConsumer::ConsumeType aConsumeType, ErrorResult& aRv); // The member is the real backend implementation of this File/Blob. // It's thread-safe and not CC-able and it's the only element that is moved // between threads. // Note: we should not store any other state in this class! RefPtr<BlobImpl> mImpl; private: - nsCOMPtr<nsISupports> mParent; + nsCOMPtr<nsIGlobalObject> mGlobal; }; NS_DEFINE_STATIC_IID_ACCESSOR(Blob, NS_DOM_BLOB_IID) // Override BindingJSObjectMallocBytes for blobs to tell the JS GC how much // memory is held live by the binding object. size_t BindingJSObjectMallocBytes(Blob* aBlob);
--- a/dom/file/File.cpp +++ b/dom/file/File.cpp @@ -13,71 +13,98 @@ #include "mozilla/dom/FileCreatorHelper.h" #include "mozilla/dom/FileSystemUtils.h" #include "mozilla/dom/Promise.h" #include "nsXULAppAPI.h" namespace mozilla { namespace dom { -File::File(nsISupports* aParent, BlobImpl* aImpl) : Blob(aParent, aImpl) { +File::File(nsIGlobalObject* aGlobal, BlobImpl* aImpl) : Blob(aGlobal, aImpl) { MOZ_ASSERT(aImpl->IsFile()); } File::~File() {} /* static */ -File* File::Create(nsISupports* aParent, BlobImpl* aImpl) { +File* File::Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl) { MOZ_ASSERT(aImpl); MOZ_ASSERT(aImpl->IsFile()); - return new File(aParent, aImpl); + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + + return new File(aGlobal, aImpl); } /* static */ -already_AddRefed<File> File::Create(nsISupports* aParent, +already_AddRefed<File> File::Create(nsIGlobalObject* aGlobal, const nsAString& aName, const nsAString& aContentType, uint64_t aLength, int64_t aLastModifiedDate) { + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + RefPtr<File> file = new File( - aParent, new BaseBlobImpl(NS_LITERAL_STRING("BaseBlobImpl"), aName, + aGlobal, new BaseBlobImpl(NS_LITERAL_STRING("BaseBlobImpl"), aName, aContentType, aLength, aLastModifiedDate)); return file.forget(); } /* static */ -already_AddRefed<File> File::CreateMemoryFile(nsISupports* aParent, +already_AddRefed<File> File::CreateMemoryFile(nsIGlobalObject* aGlobal, void* aMemoryBuffer, uint64_t aLength, const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate) { + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + RefPtr<File> file = - new File(aParent, new MemoryBlobImpl(aMemoryBuffer, aLength, aName, + new File(aGlobal, new MemoryBlobImpl(aMemoryBuffer, aLength, aName, aContentType, aLastModifiedDate)); return file.forget(); } /* static */ -already_AddRefed<File> File::CreateFromFile(nsISupports* aParent, +already_AddRefed<File> File::CreateFromFile(nsIGlobalObject* aGlobal, nsIFile* aFile) { MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); - RefPtr<File> file = new File(aParent, new FileBlobImpl(aFile)); + + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + + RefPtr<File> file = new File(aGlobal, new FileBlobImpl(aFile)); return file.forget(); } /* static */ -already_AddRefed<File> File::CreateFromFile(nsISupports* aParent, +already_AddRefed<File> File::CreateFromFile(nsIGlobalObject* aGlobal, nsIFile* aFile, const nsAString& aName, const nsAString& aContentType) { MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess()); + + MOZ_ASSERT(aGlobal); + if (NS_WARN_IF(!aGlobal)) { + return nullptr; + } + RefPtr<File> file = - new File(aParent, new FileBlobImpl(aFile, aName, aContentType)); + new File(aGlobal, new FileBlobImpl(aFile, aName, aContentType)); return file.forget(); } JSObject* File::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return File_Binding::Wrap(aCx, this, aGivenProto); } void File::GetName(nsAString& aFileName) const { mImpl->GetName(aFileName); } @@ -127,27 +154,36 @@ already_AddRefed<File> File::Constructor return nullptr; } MOZ_ASSERT(impl->IsFile()); if (aBag.mLastModified.WasPassed()) { impl->SetLastModified(aBag.mLastModified.Value()); } - RefPtr<File> file = new File(aGlobal.GetAsSupports(), impl); + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_ASSERT(global); + + RefPtr<File> file = new File(global, impl); return file.forget(); } /* static */ already_AddRefed<Promise> File::CreateFromNsIFile( const GlobalObject& aGlobal, nsIFile* aData, const ChromeFilePropertyBag& aBag, SystemCallerGuarantee aGuarantee, ErrorResult& aRv) { nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_ASSERT(global); + if (NS_WARN_IF(!global)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + RefPtr<Promise> promise = FileCreatorHelper::CreateFile(global, aData, aBag, true, aRv); return promise.forget(); } /* static */ already_AddRefed<Promise> File::CreateFromFileName( const GlobalObject& aGlobal, const nsAString& aPath, @@ -156,15 +192,21 @@ already_AddRefed<Promise> File::CreateFr nsCOMPtr<nsIFile> file; aRv = NS_NewLocalFile(aPath, false, getter_AddRefs(file)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + MOZ_ASSERT(global); + if (NS_WARN_IF(!global)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + RefPtr<Promise> promise = FileCreatorHelper::CreateFile(global, file, aBag, false, aRv); return promise.forget(); } } // namespace dom } // namespace mozilla
--- a/dom/file/File.h +++ b/dom/file/File.h @@ -20,44 +20,44 @@ struct FilePropertyBag; class Promise; class File final : public Blob { friend class Blob; public: // Note: BlobImpl must be a File in order to use this method. // Check impl->IsFile(). - static File* Create(nsISupports* aParent, BlobImpl* aImpl); + static File* Create(nsIGlobalObject* aGlobal, BlobImpl* aImpl); - static already_AddRefed<File> Create(nsISupports* aParent, + static already_AddRefed<File> Create(nsIGlobalObject* aGlobal, const nsAString& aName, const nsAString& aContentType, uint64_t aLength, int64_t aLastModifiedDate); // The returned File takes ownership of aMemoryBuffer. aMemoryBuffer will be // freed by free so it must be allocated by malloc or something // compatible with it. - static already_AddRefed<File> CreateMemoryFile(nsISupports* aParent, + static already_AddRefed<File> CreateMemoryFile(nsIGlobalObject* aGlobal, void* aMemoryBuffer, uint64_t aLength, const nsAString& aName, const nsAString& aContentType, int64_t aLastModifiedDate); // This method creates a BlobFileImpl for the new File object. This is // thread-safe, cross-process, cross-thread as any other BlobImpl, but, when // GetType() is called, it must dispatch a runnable to the main-thread in // order to use nsIMIMEService. // Would be nice if we try to avoid to use this method outside the // main-thread to avoid extra runnables. - static already_AddRefed<File> CreateFromFile(nsISupports* aParent, + static already_AddRefed<File> CreateFromFile(nsIGlobalObject* aGlobal, nsIFile* aFile); - static already_AddRefed<File> CreateFromFile(nsISupports* aParent, + static already_AddRefed<File> CreateFromFile(nsIGlobalObject* aGlobal, nsIFile* aFile, const nsAString& aName, const nsAString& aContentType); // WebIDL methods virtual JSObject* WrapObject(JSContext* cx, JS::Handle<JSObject*> aGivenProto) override; @@ -93,16 +93,16 @@ class File final : public Blob { void GetMozFullPathInternal(nsAString& aName, ErrorResult& aRv); protected: virtual bool HasFileInterface() const override { return true; } private: // File constructor should never be used directly. Use Blob::Create or // File::Create. - File(nsISupports* aParent, BlobImpl* aImpl); + File(nsIGlobalObject* aGlobal, BlobImpl* aImpl); ~File(); }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_File_h
--- a/dom/file/MutableBlobStorage.cpp +++ b/dom/file/MutableBlobStorage.cpp @@ -26,52 +26,51 @@ namespace dom { namespace { // This class uses the callback to inform when the Blob is created or when the // error must be propagated. class BlobCreationDoneRunnable final : public Runnable { public: BlobCreationDoneRunnable(MutableBlobStorage* aBlobStorage, - MutableBlobStorageCallback* aCallback, Blob* aBlob, - nsresult aRv) + MutableBlobStorageCallback* aCallback, + BlobImpl* aBlobImpl, nsresult aRv) : Runnable("dom::BlobCreationDoneRunnable"), mBlobStorage(aBlobStorage), mCallback(aCallback), - mBlob(aBlob), + mBlobImpl(aBlobImpl), mRv(aRv) { MOZ_ASSERT(aBlobStorage); MOZ_ASSERT(aCallback); - MOZ_ASSERT((NS_FAILED(aRv) && !aBlob) || (NS_SUCCEEDED(aRv) && aBlob)); + MOZ_ASSERT((NS_FAILED(aRv) && !aBlobImpl) || + (NS_SUCCEEDED(aRv) && aBlobImpl)); } NS_IMETHOD Run() override { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mBlobStorage); - mCallback->BlobStoreCompleted(mBlobStorage, mBlob, mRv); + mCallback->BlobStoreCompleted(mBlobStorage, mBlobImpl, mRv); mCallback = nullptr; - mBlob = nullptr; + mBlobImpl = nullptr; return NS_OK; } private: ~BlobCreationDoneRunnable() { MOZ_ASSERT(mBlobStorage); // If something when wrong, we still have to release these objects in the // correct thread. NS_ProxyRelease("BlobCreationDoneRunnable::mCallback", mBlobStorage->EventTarget(), mCallback.forget()); - NS_ProxyRelease("BlobCreationDoneRunnable::mBlob", - mBlobStorage->EventTarget(), mBlob.forget()); } RefPtr<MutableBlobStorage> mBlobStorage; RefPtr<MutableBlobStorageCallback> mCallback; - RefPtr<Blob> mBlob; + RefPtr<BlobImpl> mBlobImpl; nsresult mRv; }; // Simple runnable to propagate the error to the BlobStorage. class ErrorPropagationRunnable final : public Runnable { public: ErrorPropagationRunnable(MutableBlobStorage* aBlobStorage, nsresult aRv) : Runnable("dom::ErrorPropagationRunnable"), @@ -186,107 +185,94 @@ class CloseFileRunnable final : public R class CreateBlobRunnable final : public Runnable, public TemporaryIPCBlobChildCallback { public: // We need to always declare refcounting because // TemporaryIPCBlobChildCallback has pure-virtual refcounting. NS_DECL_ISUPPORTS_INHERITED CreateBlobRunnable(MutableBlobStorage* aBlobStorage, - already_AddRefed<nsISupports> aParent, const nsACString& aContentType, already_AddRefed<MutableBlobStorageCallback> aCallback) : Runnable("dom::CreateBlobRunnable"), mBlobStorage(aBlobStorage), - mParent(aParent), mContentType(aContentType), mCallback(aCallback) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aBlobStorage); } NS_IMETHOD Run() override { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mBlobStorage); mBlobStorage->AskForBlob(this, mContentType); return NS_OK; } void OperationSucceeded(BlobImpl* aBlobImpl) override { - nsCOMPtr<nsISupports> parent(std::move(mParent)); RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback)); - - RefPtr<Blob> blob = Blob::Create(parent, aBlobImpl); - callback->BlobStoreCompleted(mBlobStorage, blob, NS_OK); + callback->BlobStoreCompleted(mBlobStorage, aBlobImpl, NS_OK); } void OperationFailed(nsresult aRv) override { RefPtr<MutableBlobStorageCallback> callback(std::move(mCallback)); callback->BlobStoreCompleted(mBlobStorage, nullptr, aRv); } private: ~CreateBlobRunnable() { MOZ_ASSERT(mBlobStorage); // If something when wrong, we still have to release data in the correct // thread. - NS_ProxyRelease("CreateBlobRunnable::mParent", mBlobStorage->EventTarget(), - mParent.forget()); NS_ProxyRelease("CreateBlobRunnable::mCallback", mBlobStorage->EventTarget(), mCallback.forget()); } RefPtr<MutableBlobStorage> mBlobStorage; - nsCOMPtr<nsISupports> mParent; nsCString mContentType; RefPtr<MutableBlobStorageCallback> mCallback; }; NS_IMPL_ISUPPORTS_INHERITED0(CreateBlobRunnable, Runnable) // This task is used to know when the writing is completed. From the IO thread // it dispatches a CreateBlobRunnable to the main-thread. class LastRunnable final : public Runnable { public: - LastRunnable(MutableBlobStorage* aBlobStorage, nsISupports* aParent, - const nsACString& aContentType, + LastRunnable(MutableBlobStorage* aBlobStorage, const nsACString& aContentType, MutableBlobStorageCallback* aCallback) : Runnable("dom::LastRunnable"), mBlobStorage(aBlobStorage), - mParent(aParent), mContentType(aContentType), mCallback(aCallback) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mBlobStorage); MOZ_ASSERT(aCallback); } NS_IMETHOD Run() override { MOZ_ASSERT(!NS_IsMainThread()); - RefPtr<Runnable> runnable = new CreateBlobRunnable( - mBlobStorage, mParent.forget(), mContentType, mCallback.forget()); + RefPtr<Runnable> runnable = + new CreateBlobRunnable(mBlobStorage, mContentType, mCallback.forget()); return mBlobStorage->EventTarget()->Dispatch(runnable, NS_DISPATCH_NORMAL); } private: ~LastRunnable() { MOZ_ASSERT(mBlobStorage); // If something when wrong, we still have to release data in the correct // thread. - NS_ProxyRelease("LastRunnable::mParent", mBlobStorage->EventTarget(), - mParent.forget()); NS_ProxyRelease("LastRunnable::mCallback", mBlobStorage->EventTarget(), mCallback.forget()); } RefPtr<MutableBlobStorage> mBlobStorage; - nsCOMPtr<nsISupports> mParent; nsCString mContentType; RefPtr<MutableBlobStorageCallback> mCallback; }; } // anonymous namespace MutableBlobStorage::MutableBlobStorage(MutableBlobStorageType aType, nsIEventTarget* aEventTarget, @@ -327,19 +313,18 @@ MutableBlobStorage::~MutableBlobStorage( } if (mActor) { NS_ProxyRelease("MutableBlobStorage::mActor", EventTarget(), mActor.forget()); } } -void MutableBlobStorage::GetBlobWhenReady( - nsISupports* aParent, const nsACString& aContentType, - MutableBlobStorageCallback* aCallback) { +void MutableBlobStorage::GetBlobImplWhenReady( + const nsACString& aContentType, MutableBlobStorageCallback* aCallback) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aCallback); MutexAutoLock lock(mMutex); // GetBlob can be called just once. MOZ_ASSERT(mStorageState != eClosed); StorageState previousState = mStorageState; @@ -356,28 +341,26 @@ void MutableBlobStorage::GetBlobWhenRead } MOZ_ASSERT(mActor); // We want to wait until all the WriteRunnable are completed. The way we do // this is to go to the I/O thread and then we come back: the runnables are // executed in order and this LastRunnable will be... the last one. // This Runnable will also close the FD on the I/O thread. - RefPtr<Runnable> runnable = - new LastRunnable(this, aParent, aContentType, aCallback); + RefPtr<Runnable> runnable = new LastRunnable(this, aContentType, aCallback); // If the dispatching fails, we are shutting down and it's fine to do not // run the callback. Unused << DispatchToIOThread(runnable.forget()); return; } // If we are waiting for the temporary file, it's better to wait... if (previousState == eWaitingForTemporaryFile) { - mPendingParent = aParent; mPendingContentType = aContentType; mPendingCallback = aCallback; return; } RefPtr<BlobImpl> blobImpl; if (mData) { @@ -386,19 +369,18 @@ void MutableBlobStorage::GetBlobWhenRead mData = nullptr; // The MemoryBlobImpl takes ownership of the buffer mDataLen = 0; mDataBufferLen = 0; } else { blobImpl = new EmptyBlobImpl(NS_ConvertUTF8toUTF16(aContentType)); } - RefPtr<Blob> blob = Blob::Create(aParent, blobImpl); RefPtr<BlobCreationDoneRunnable> runnable = - new BlobCreationDoneRunnable(this, aCallback, blob, NS_OK); + new BlobCreationDoneRunnable(this, aCallback, blobImpl, NS_OK); nsresult error = EventTarget()->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL); if (NS_WARN_IF(NS_FAILED(error))) { return; } } @@ -586,29 +568,28 @@ void MutableBlobStorage::TemporaryFileCr mData = nullptr; nsresult rv = DispatchToIOThread(runnable.forget()); if (NS_WARN_IF(NS_FAILED(rv))) { // Shutting down, we cannot continue. return; } - // If we are closed, it means that GetBlobWhenReady() has been called when we - // were already waiting for a temporary file-descriptor. Finally we are here, - // AdoptBuffer runnable is going to write the current buffer into this file. - // After that, there is nothing else to write, and we dispatch LastRunnable - // which ends up calling mPendingCallback via CreateBlobRunnable. + // If we are closed, it means that GetBlobImplWhenReady() has been called when + // we were already waiting for a temporary file-descriptor. Finally we are + // here, AdoptBuffer runnable is going to write the current buffer into this + // file. After that, there is nothing else to write, and we dispatch + // LastRunnable which ends up calling mPendingCallback via CreateBlobRunnable. if (mStorageState == eClosed) { MOZ_ASSERT(mPendingCallback); - RefPtr<Runnable> runnable = new LastRunnable( - this, mPendingParent, mPendingContentType, mPendingCallback); + RefPtr<Runnable> runnable = + new LastRunnable(this, mPendingContentType, mPendingCallback); Unused << DispatchToIOThread(runnable.forget()); - mPendingParent = nullptr; mPendingCallback = nullptr; } } void MutableBlobStorage::AskForBlob(TemporaryIPCBlobChildCallback* aCallback, const nsACString& aContentType) { MOZ_ASSERT(NS_IsMainThread());
--- a/dom/file/MutableBlobStorage.h +++ b/dom/file/MutableBlobStorage.h @@ -26,18 +26,18 @@ class BlobImpl; class MutableBlobStorage; class TemporaryIPCBlobChild; class TemporaryIPCBlobChildCallback; class MutableBlobStorageCallback { public: NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING - virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob, - nsresult aRv) = 0; + virtual void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, + BlobImpl* aBlob, nsresult aRv) = 0; }; // This class is must be created and used on main-thread, except for Append() // that can be called on any thread. class MutableBlobStorage final { public: NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MutableBlobStorage) @@ -48,19 +48,19 @@ class MutableBlobStorage final { explicit MutableBlobStorage(MutableBlobStorageType aType, nsIEventTarget* aEventTarget = nullptr, uint32_t aMaxMemory = 0); nsresult Append(const void* aData, uint32_t aLength); // This method can be called just once. - // The callback will be called when the Blob is ready. - void GetBlobWhenReady(nsISupports* aParent, const nsACString& aContentType, - MutableBlobStorageCallback* aCallback); + // The callback will be called when the BlobImpl is ready. + void GetBlobImplWhenReady(const nsACString& aContentType, + MutableBlobStorageCallback* aCallback); void TemporaryFileCreated(PRFileDesc* aFD); void AskForBlob(TemporaryIPCBlobChildCallback* aCallback, const nsACString& aContentType); void ErrorPropagated(nsresult aRv); @@ -112,17 +112,16 @@ class MutableBlobStorage final { PRFileDesc* mFD; nsresult mErrorResult; RefPtr<TaskQueue> mTaskQueue; nsCOMPtr<nsIEventTarget> mEventTarget; - nsCOMPtr<nsISupports> mPendingParent; nsCString mPendingContentType; RefPtr<MutableBlobStorageCallback> mPendingCallback; RefPtr<TemporaryIPCBlobChild> mActor; // This value is used when we go from eInMemory to eWaitingForTemporaryFile // and eventually eInTemporaryFile. If the size of the buffer is >= // mMaxMemory, the creation of the temporary file will start.
--- a/dom/file/MutableBlobStreamListener.cpp +++ b/dom/file/MutableBlobStreamListener.cpp @@ -9,20 +9,19 @@ #include "nsIInputStream.h" #include "nsThreadUtils.h" namespace mozilla { namespace dom { MutableBlobStreamListener::MutableBlobStreamListener( MutableBlobStorage::MutableBlobStorageType aStorageType, - nsISupports* aParent, const nsACString& aContentType, - MutableBlobStorageCallback* aCallback, nsIEventTarget* aEventTarget) + const nsACString& aContentType, MutableBlobStorageCallback* aCallback, + nsIEventTarget* aEventTarget) : mCallback(aCallback), - mParent(aParent), mStorageType(aStorageType), mContentType(aContentType), mEventTarget(aEventTarget) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(aCallback); if (!mEventTarget) { mEventTarget = GetMainThreadEventTarget(); @@ -59,17 +58,17 @@ MutableBlobStreamListener::OnStopRequest storage.swap(mStorage); // Let's propagate the error simulating a failure of the storage. if (NS_FAILED(aStatus)) { mCallback->BlobStoreCompleted(storage, nullptr, aStatus); return NS_OK; } - storage->GetBlobWhenReady(mParent, mContentType, mCallback); + storage->GetBlobImplWhenReady(mContentType, mCallback); return NS_OK; } NS_IMETHODIMP MutableBlobStreamListener::OnDataAvailable(nsIRequest* aRequest, nsIInputStream* aStream, uint64_t aSourceOffset, uint32_t aCount) {
--- a/dom/file/MutableBlobStreamListener.h +++ b/dom/file/MutableBlobStreamListener.h @@ -21,32 +21,30 @@ class MutableBlobStreamListener final public nsIThreadRetargetableStreamListener { public: NS_DECL_THREADSAFE_ISUPPORTS NS_DECL_NSISTREAMLISTENER NS_DECL_NSITHREADRETARGETABLESTREAMLISTENER NS_DECL_NSIREQUESTOBSERVER MutableBlobStreamListener(MutableBlobStorage::MutableBlobStorageType aType, - nsISupports* aParent, const nsACString& aContentType, MutableBlobStorageCallback* aCallback, nsIEventTarget* aEventTarget = nullptr); private: ~MutableBlobStreamListener(); static nsresult WriteSegmentFun(nsIInputStream* aWriter, void* aClosure, const char* aFromSegment, uint32_t aOffset, uint32_t aCount, uint32_t* aWriteCount); RefPtr<MutableBlobStorage> mStorage; RefPtr<MutableBlobStorageCallback> mCallback; - nsCOMPtr<nsISupports> mParent; MutableBlobStorage::MutableBlobStorageType mStorageType; nsCString mContentType; nsCOMPtr<nsIEventTarget> mEventTarget; }; } // namespace dom } // namespace mozilla
--- a/dom/file/ipc/FileCreatorChild.cpp +++ b/dom/file/ipc/FileCreatorChild.cpp @@ -35,16 +35,21 @@ mozilla::ipc::IPCResult FileCreatorChild } MOZ_ASSERT(aResult.type() == FileCreationResult::TFileCreationSuccessResult); RefPtr<dom::BlobImpl> impl = dom::IPCBlobUtils::Deserialize( aResult.get_FileCreationSuccessResult().blob()); RefPtr<File> file = File::Create(promise->GetParentObject(), impl); + if (NS_WARN_IF(!file)) { + promise->MaybeReject(NS_ERROR_FAILURE); + return IPC_OK(); + } + promise->MaybeResolve(file); return IPC_OK(); } void FileCreatorChild::ActorDestroy(ActorDestroyReason aWhy) { if (mPromise) { mPromise->MaybeReject(NS_ERROR_FAILURE); mPromise = nullptr;
--- a/dom/file/moz.build +++ b/dom/file/moz.build @@ -9,16 +9,17 @@ with Files("**"): DIRS += ['ipc', 'uri' ] EXPORTS.mozilla.dom += [ 'BaseBlobImpl.h', 'Blob.h', 'BlobImpl.h', 'BlobSet.h', + 'EmptyBlobImpl.h', 'File.h', 'FileBlobImpl.h', 'FileCreatorHelper.h', 'FileList.h', 'FileReader.h', 'FileReaderSync.h', 'MemoryBlobImpl.h', 'MultipartBlobImpl.h',
--- a/dom/filesystem/Directory.cpp +++ b/dom/filesystem/Directory.cpp @@ -22,25 +22,25 @@ namespace dom { NS_IMPL_CYCLE_COLLECTION_CLASS(Directory) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(Directory) if (tmp->mFileSystem) { tmp->mFileSystem->Unlink(); tmp->mFileSystem = nullptr; } - NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(Directory) if (tmp->mFileSystem) { tmp->mFileSystem->Traverse(cb); } - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(Directory) NS_IMPL_CYCLE_COLLECTING_ADDREF(Directory) NS_IMPL_CYCLE_COLLECTING_RELEASE(Directory) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Directory) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY @@ -52,48 +52,54 @@ already_AddRefed<Directory> Directory::C const nsAString& aRealPath, ErrorResult& aRv) { nsCOMPtr<nsIFile> path; aRv = NS_NewLocalFile(aRealPath, true, getter_AddRefs(path)); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } - return Create(aGlobal.GetAsSupports(), path); + nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); + if (NS_WARN_IF(!global)) { + aRv.Throw(NS_ERROR_FAILURE); + return nullptr; + } + + return Create(global, path); } /* static */ -already_AddRefed<Directory> Directory::Create(nsISupports* aParent, +already_AddRefed<Directory> Directory::Create(nsIGlobalObject* aGlobal, nsIFile* aFile, FileSystemBase* aFileSystem) { - MOZ_ASSERT(aParent); + MOZ_ASSERT(aGlobal); MOZ_ASSERT(aFile); - RefPtr<Directory> directory = new Directory(aParent, aFile, aFileSystem); + RefPtr<Directory> directory = new Directory(aGlobal, aFile, aFileSystem); return directory.forget(); } -Directory::Directory(nsISupports* aParent, nsIFile* aFile, +Directory::Directory(nsIGlobalObject* aGlobal, nsIFile* aFile, FileSystemBase* aFileSystem) - : mParent(aParent), mFile(aFile) { + : mGlobal(aGlobal), mFile(aFile) { MOZ_ASSERT(aFile); // aFileSystem can be null. In this case we create a OSFileSystem when needed. if (aFileSystem) { // More likely, this is a OSFileSystem. This object keeps a reference of - // mParent but it's not cycle collectable and to avoid manual + // mGlobal but it's not cycle collectable and to avoid manual // addref/release, it's better to have 1 object per directory. For this // reason we clone it here. mFileSystem = aFileSystem->Clone(); } } Directory::~Directory() {} -nsISupports* Directory::GetParentObject() const { return mParent; } +nsIGlobalObject* Directory::GetParentObject() const { return mGlobal; } JSObject* Directory::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) { return Directory_Binding::Wrap(aCx, this, aGivenProto); } void Directory::GetName(nsAString& aRetval, ErrorResult& aRv) { aRetval.Truncate(); @@ -178,17 +184,17 @@ FileSystemBase* Directory::GetFileSystem if (!mFileSystem) { nsAutoString path; aRv = mFile->GetPath(path); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } RefPtr<OSFileSystem> fs = new OSFileSystem(path); - fs->Init(mParent); + fs->Init(mGlobal); mFileSystem = fs; } return mFileSystem; } } // namespace dom
--- a/dom/filesystem/Directory.h +++ b/dom/filesystem/Directory.h @@ -25,23 +25,23 @@ class Directory final : public nsISuppor public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(Directory) static already_AddRefed<Directory> Constructor(const GlobalObject& aGlobal, const nsAString& aRealPath, ErrorResult& aRv); - static already_AddRefed<Directory> Create(nsISupports* aParent, + static already_AddRefed<Directory> Create(nsIGlobalObject* aGlobal, nsIFile* aDirectory, FileSystemBase* aFileSystem = 0); // ========= Begin WebIDL bindings. =========== - nsISupports* GetParentObject() const; + nsIGlobalObject* GetParentObject() const; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override; void GetName(nsAString& aRetval, ErrorResult& aRv); // From // https://microsoftedge.github.io/directory-upload/proposal.html#directory-interface @@ -81,26 +81,26 @@ class Directory final : public nsISuppor */ void SetContentFilters(const nsAString& aFilters); FileSystemBase* GetFileSystem(ErrorResult& aRv); nsIFile* GetInternalNsIFile() const { return mFile; } private: - Directory(nsISupports* aParent, nsIFile* aFile, + Directory(nsIGlobalObject* aGlobal, nsIFile* aFile, FileSystemBase* aFileSystem = nullptr); ~Directory(); /* * Convert relative DOM path to the absolute real path. */ nsresult DOMPathToRealPath(const nsAString& aPath, nsIFile** aFile) const; - nsCOMPtr<nsISupports> mParent; + nsCOMPtr<nsIGlobalObject> mGlobal; RefPtr<FileSystemBase> mFileSystem; nsCOMPtr<nsIFile> mFile; nsString mFilters; nsString mPath; }; } // namespace dom
--- a/dom/filesystem/FileSystemBase.cpp +++ b/dom/filesystem/FileSystemBase.cpp @@ -16,17 +16,17 @@ FileSystemBase::FileSystemBase() : mShut FileSystemBase::~FileSystemBase() { AssertIsOnOwningThread(); } void FileSystemBase::Shutdown() { AssertIsOnOwningThread(); mShutdown = true; } -nsISupports* FileSystemBase::GetParentObject() const { +nsIGlobalObject* FileSystemBase::GetParentObject() const { AssertIsOnOwningThread(); return nullptr; } bool FileSystemBase::GetRealPath(BlobImpl* aFile, nsIFile** aPath) const { AssertIsOnOwningThread(); MOZ_ASSERT(aFile, "aFile Should not be null."); MOZ_ASSERT(aPath);
--- a/dom/filesystem/FileSystemBase.h +++ b/dom/filesystem/FileSystemBase.h @@ -25,17 +25,17 @@ class FileSystemBase { // SerializeDOMPath the FileSystem to string. virtual void SerializeDOMPath(nsAString& aOutput) const = 0; virtual already_AddRefed<FileSystemBase> Clone() = 0; virtual bool ShouldCreateDirectory() = 0; - virtual nsISupports* GetParentObject() const; + virtual nsIGlobalObject* GetParentObject() const; virtual void GetDirectoryName(nsIFile* aFile, nsAString& aRetval, ErrorResult& aRv) const; void GetDOMPath(nsIFile* aFile, nsAString& aRetval, ErrorResult& aRv) const; /* * Return the local root path of the FileSystem implementation.
--- a/dom/filesystem/GetDirectoryListingTask.cpp +++ b/dom/filesystem/GetDirectoryListingTask.cpp @@ -32,22 +32,18 @@ GetDirectoryListingTaskChild::Create(Fil Directory* aDirectory, nsIFile* aTargetPath, const nsAString& aFilters, ErrorResult& aRv) { MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aDirectory); aFileSystem->AssertIsOnOwningThread(); - nsCOMPtr<nsIGlobalObject> globalObject = - do_QueryInterface(aFileSystem->GetParentObject()); - if (NS_WARN_IF(!globalObject)) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } + nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject(); + MOZ_ASSERT(globalObject); RefPtr<GetDirectoryListingTaskChild> task = new GetDirectoryListingTaskChild( globalObject, aFileSystem, aDirectory, aTargetPath, aFilters); // aTargetPath can be null. In this case SetError will be called. task->mPromise = Promise::Create(globalObject, aRv); if (NS_WARN_IF(aRv.Failed())) { @@ -118,18 +114,20 @@ void GetDirectoryListingTaskChild::SetSu if (data.type() == FileSystemDirectoryListingResponseData:: TFileSystemDirectoryListingResponseFile) { const FileSystemDirectoryListingResponseFile& d = data.get_FileSystemDirectoryListingResponseFile(); RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(d.blob()); MOZ_ASSERT(blobImpl); - RefPtr<File> file = - File::Create(mFileSystem->GetParentObject(), blobImpl); + nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject(); + MOZ_ASSERT(globalObject); + + RefPtr<File> file = File::Create(globalObject, blobImpl); MOZ_ASSERT(file); ofd->SetAsFile() = file; } else { MOZ_ASSERT(data.type() == FileSystemDirectoryListingResponseData:: TFileSystemDirectoryListingResponseDirectory); const FileSystemDirectoryListingResponseDirectory& d =
--- a/dom/filesystem/GetFileOrDirectoryTask.cpp +++ b/dom/filesystem/GetFileOrDirectoryTask.cpp @@ -25,18 +25,17 @@ namespace dom { /* static */ already_AddRefed<GetFileOrDirectoryTaskChild> GetFileOrDirectoryTaskChild::Create(FileSystemBase* aFileSystem, nsIFile* aTargetPath, ErrorResult& aRv) { MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); MOZ_ASSERT(aFileSystem); - nsCOMPtr<nsIGlobalObject> globalObject = - do_QueryInterface(aFileSystem->GetParentObject()); + nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject(); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } RefPtr<GetFileOrDirectoryTaskChild> task = new GetFileOrDirectoryTaskChild(globalObject, aFileSystem, aTargetPath); @@ -86,18 +85,23 @@ void GetFileOrDirectoryTaskChild::SetSuc MOZ_ASSERT(NS_IsMainThread(), "Only call on main thread!"); switch (aValue.type()) { case FileSystemResponseValue::TFileSystemFileResponse: { FileSystemFileResponse r = aValue; RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(r.blob()); MOZ_ASSERT(blobImpl); - mResultFile = File::Create(mFileSystem->GetParentObject(), blobImpl); - MOZ_ASSERT(mResultFile); + nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject(); + MOZ_ASSERT(globalObject); + + mResultFile = File::Create(globalObject, blobImpl); + if (NS_WARN_IF(!mResultFile)) { + aRv.Throw(NS_ERROR_FAILURE); + } break; } case FileSystemResponseValue::TFileSystemDirectoryResponse: { FileSystemDirectoryResponse r = aValue; nsCOMPtr<nsIFile> file; aRv = NS_NewLocalFile(r.realPath(), true, getter_AddRefs(file)); if (NS_WARN_IF(aRv.Failed())) {
--- a/dom/filesystem/GetFilesHelper.cpp +++ b/dom/filesystem/GetFilesHelper.cpp @@ -19,82 +19,71 @@ namespace dom { namespace { // This class is used in the DTOR of GetFilesHelper to release resources in the // correct thread. class ReleaseRunnable final : public Runnable { public: static void MaybeReleaseOnMainThread( nsTArray<RefPtr<Promise>>& aPromises, - nsTArray<RefPtr<GetFilesCallback>>& aCallbacks, - Sequence<RefPtr<File>>& aFiles, - already_AddRefed<nsIGlobalObject> aGlobal) { - nsCOMPtr<nsIGlobalObject> global(aGlobal); + nsTArray<RefPtr<GetFilesCallback>>& aCallbacks) { if (NS_IsMainThread()) { return; } RefPtr<ReleaseRunnable> runnable = - new ReleaseRunnable(aPromises, aCallbacks, aFiles, global.forget()); + new ReleaseRunnable(aPromises, aCallbacks); FileSystemUtils::DispatchRunnable(nullptr, runnable.forget()); } NS_IMETHOD Run() override { MOZ_ASSERT(NS_IsMainThread()); mPromises.Clear(); mCallbacks.Clear(); - mFiles.Clear(); - mGlobal = nullptr; return NS_OK; } private: ReleaseRunnable(nsTArray<RefPtr<Promise>>& aPromises, - nsTArray<RefPtr<GetFilesCallback>>& aCallbacks, - Sequence<RefPtr<File>>& aFiles, - already_AddRefed<nsIGlobalObject> aGlobal) + nsTArray<RefPtr<GetFilesCallback>>& aCallbacks) : Runnable("dom::ReleaseRunnable") { mPromises.SwapElements(aPromises); mCallbacks.SwapElements(aCallbacks); - mFiles.SwapElements(aFiles); - mGlobal = aGlobal; } nsTArray<RefPtr<Promise>> mPromises; nsTArray<RefPtr<GetFilesCallback>> mCallbacks; - Sequence<RefPtr<File>> mFiles; - nsCOMPtr<nsIGlobalObject> mGlobal; }; } // namespace /////////////////////////////////////////////////////////////////////////////// // GetFilesHelper Base class already_AddRefed<GetFilesHelper> GetFilesHelper::Create( - nsIGlobalObject* aGlobal, const nsTArray<OwningFileOrDirectory>& aFilesOrDirectory, bool aRecursiveFlag, ErrorResult& aRv) { RefPtr<GetFilesHelper> helper; if (XRE_IsParentProcess()) { - helper = new GetFilesHelper(aGlobal, aRecursiveFlag); + helper = new GetFilesHelper(aRecursiveFlag); } else { - helper = new GetFilesHelperChild(aGlobal, aRecursiveFlag); + helper = new GetFilesHelperChild(aRecursiveFlag); } nsAutoString directoryPath; for (uint32_t i = 0; i < aFilesOrDirectory.Length(); ++i) { const OwningFileOrDirectory& data = aFilesOrDirectory[i]; if (data.IsFile()) { - if (!helper->mFiles.AppendElement(data.GetAsFile(), fallible)) { + if (!helper->mTargetBlobImplArray.AppendElement(data.GetAsFile()->Impl(), + fallible)) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return nullptr; } } else { MOZ_ASSERT(data.IsDirectory()); // We support the upload of only 1 top-level directory from our // directory picker. This means that we cannot have more than 1 @@ -112,39 +101,37 @@ already_AddRefed<GetFilesHelper> GetFile } // No directories to explore. if (directoryPath.IsEmpty()) { helper->mListingCompleted = true; return helper.forget(); } - MOZ_ASSERT(helper->mFiles.IsEmpty()); + MOZ_ASSERT(helper->mTargetBlobImplArray.IsEmpty()); helper->SetDirectoryPath(directoryPath); helper->Work(aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } return helper.forget(); } -GetFilesHelper::GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag) +GetFilesHelper::GetFilesHelper(bool aRecursiveFlag) : Runnable("GetFilesHelper"), GetFilesHelperBase(aRecursiveFlag), - mGlobal(aGlobal), mListingCompleted(false), mErrorResult(NS_OK), mMutex("GetFilesHelper::mMutex"), mCanceled(false) {} GetFilesHelper::~GetFilesHelper() { - ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks, mFiles, - mGlobal.forget()); + ReleaseRunnable::MaybeReleaseOnMainThread(mPromises, mCallbacks); } void GetFilesHelper::AddPromise(Promise* aPromise) { MOZ_ASSERT(aPromise); // Still working. if (!mListingCompleted) { mPromises.AppendElement(aPromise); @@ -164,33 +151,29 @@ void GetFilesHelper::AddCallback(GetFile return; } MOZ_ASSERT(mCallbacks.IsEmpty()); RunCallback(aCallback); } void GetFilesHelper::Unlink() { - mGlobal = nullptr; - mFiles.Clear(); mPromises.Clear(); mCallbacks.Clear(); { MutexAutoLock lock(mMutex); mCanceled = true; } Cancel(); } void GetFilesHelper::Traverse(nsCycleCollectionTraversalCallback& cb) { GetFilesHelper* tmp = this; - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal); - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles); NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPromises); } void GetFilesHelper::Work(ErrorResult& aRv) { nsCOMPtr<nsIEventTarget> target = do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); MOZ_ASSERT(target); @@ -218,18 +201,16 @@ GetFilesHelper::Run() { } // We are here, but we should not do anything on this thread because, in the // meantime, the operation has been canceled. if (IsCanceled()) { return NS_OK; } - RunMainThread(); - OperationCompleted(); return NS_OK; } void GetFilesHelper::OperationCompleted() { // We mark the operation as completed here. mListingCompleted = true; @@ -269,39 +250,16 @@ void GetFilesHelper::RunIO() { nsAutoString domPath; domPath.AssignLiteral(FILESYSTEM_DOM_PATH_SEPARATOR_LITERAL); domPath.Append(leafName); mErrorResult = ExploreDirectory(domPath, file); } -void GetFilesHelper::RunMainThread() { - MOZ_ASSERT(NS_IsMainThread()); - MOZ_ASSERT(!mDirectoryPath.IsEmpty()); - MOZ_ASSERT(!mListingCompleted); - - // If there is an error, do nothing. - if (NS_FAILED(mErrorResult)) { - return; - } - - // Create the sequence of Files. - for (uint32_t i = 0; i < mTargetBlobImplArray.Length(); ++i) { - RefPtr<File> domFile = File::Create(mGlobal, mTargetBlobImplArray[i]); - MOZ_ASSERT(domFile); - - if (!mFiles.AppendElement(domFile, fallible)) { - mErrorResult = NS_ERROR_OUT_OF_MEMORY; - mFiles.Clear(); - return; - } - } -} - nsresult GetFilesHelperBase::ExploreDirectory(const nsAString& aDOMPath, nsIFile* aFile) { MOZ_ASSERT(!NS_IsMainThread()); MOZ_ASSERT(aFile); // We check if this operation has to be terminated at each recursion. if (IsCanceled()) { return NS_OK; @@ -436,31 +394,51 @@ bool GetFilesHelperBase::ShouldFollowSym return !mExploredDirectories.Contains(targetPath); } void GetFilesHelper::ResolveOrRejectPromise(Promise* aPromise) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mListingCompleted); MOZ_ASSERT(aPromise); + Sequence<RefPtr<File>> files; + + if (NS_SUCCEEDED(mErrorResult)) { + for (uint32_t i = 0; i < mTargetBlobImplArray.Length(); ++i) { + RefPtr<File> domFile = + File::Create(aPromise->GetParentObject(), mTargetBlobImplArray[i]); + if (NS_WARN_IF(!domFile)) { + mErrorResult = NS_ERROR_FAILURE; + files.Clear(); + break; + } + + if (!files.AppendElement(domFile, fallible)) { + mErrorResult = NS_ERROR_OUT_OF_MEMORY; + files.Clear(); + break; + } + } + } + // Error propagation. if (NS_FAILED(mErrorResult)) { aPromise->MaybeReject(mErrorResult); return; } - aPromise->MaybeResolve(mFiles); + aPromise->MaybeResolve(files); } void GetFilesHelper::RunCallback(GetFilesCallback* aCallback) { MOZ_ASSERT(NS_IsMainThread()); MOZ_ASSERT(mListingCompleted); MOZ_ASSERT(aCallback); - aCallback->Callback(mErrorResult, mFiles); + aCallback->Callback(mErrorResult, mTargetBlobImplArray); } /////////////////////////////////////////////////////////////////////////////// // GetFilesHelperChild class void GetFilesHelperChild::Work(ErrorResult& aRv) { ContentChild* cc = ContentChild::GetSingleton(); if (NS_WARN_IF(!cc)) { @@ -491,20 +469,17 @@ void GetFilesHelperChild::Cancel() { cc->DeleteGetFilesRequest(mUUID, this); } bool GetFilesHelperChild::AppendBlobImpl(BlobImpl* aBlobImpl) { MOZ_ASSERT(mPendingOperation); MOZ_ASSERT(aBlobImpl); MOZ_ASSERT(aBlobImpl->IsFile()); - RefPtr<File> file = File::Create(mGlobal, aBlobImpl); - MOZ_ASSERT(file); - - return mFiles.AppendElement(file, fallible); + return mTargetBlobImplArray.AppendElement(aBlobImpl, fallible); } void GetFilesHelperChild::Finished(nsresult aError) { MOZ_ASSERT(mPendingOperation); MOZ_ASSERT(NS_SUCCEEDED(mErrorResult)); mPendingOperation = false; mErrorResult = aError; @@ -518,31 +493,31 @@ void GetFilesHelperChild::Finished(nsres class GetFilesHelperParentCallback final : public GetFilesCallback { public: explicit GetFilesHelperParentCallback(GetFilesHelperParent* aParent) : mParent(aParent) { MOZ_ASSERT(aParent); } void Callback(nsresult aStatus, - const Sequence<RefPtr<File>>& aFiles) override { + const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override { if (NS_FAILED(aStatus)) { mParent->mContentParent->SendGetFilesResponseAndForget( mParent->mUUID, GetFilesResponseFailure(aStatus)); return; } GetFilesResponseSuccess success; nsTArray<IPCBlob>& ipcBlobs = success.blobs(); - ipcBlobs.SetLength(aFiles.Length()); + ipcBlobs.SetLength(aBlobImpls.Length()); - for (uint32_t i = 0; i < aFiles.Length(); ++i) { + for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) { nsresult rv = IPCBlobUtils::Serialize( - aFiles[i]->Impl(), mParent->mContentParent, ipcBlobs[i]); + aBlobImpls[i], mParent->mContentParent, ipcBlobs[i]); if (NS_WARN_IF(NS_FAILED(rv))) { mParent->mContentParent->SendGetFilesResponseAndForget( mParent->mUUID, GetFilesResponseFailure(NS_ERROR_OUT_OF_MEMORY)); return; } } mParent->mContentParent->SendGetFilesResponseAndForget(mParent->mUUID, @@ -552,17 +527,17 @@ class GetFilesHelperParentCallback final private: // Raw pointer because this callback is kept alive by this parent object. GetFilesHelperParent* mParent; }; GetFilesHelperParent::GetFilesHelperParent(const nsID& aUUID, ContentParent* aContentParent, bool aRecursiveFlag) - : GetFilesHelper(nullptr, aRecursiveFlag), + : GetFilesHelper(aRecursiveFlag), mContentParent(aContentParent), mUUID(aUUID) {} GetFilesHelperParent::~GetFilesHelperParent() { NS_ReleaseOnMainThreadSystemGroup("GetFilesHelperParent::mContentParent", mContentParent.forget()); }
--- a/dom/filesystem/GetFilesHelper.h +++ b/dom/filesystem/GetFilesHelper.h @@ -25,17 +25,17 @@ class GetFilesHelperParent; class OwningFileOrDirectory; class Promise; class GetFilesCallback { public: NS_INLINE_DECL_REFCOUNTING(GetFilesCallback); virtual void Callback(nsresult aStatus, - const Sequence<RefPtr<File>>& aFiles) = 0; + const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) = 0; protected: virtual ~GetFilesCallback() {} }; class GetFilesHelperBase { protected: explicit GetFilesHelperBase(bool aRecursiveFlag) @@ -60,30 +60,29 @@ class GetFilesHelperBase { // Retrieving the list of files can be very time/IO consuming. We use this // helper class to do it just once. class GetFilesHelper : public Runnable, public GetFilesHelperBase { friend class GetFilesHelperParent; public: static already_AddRefed<GetFilesHelper> Create( - nsIGlobalObject* aGlobal, const nsTArray<OwningFileOrDirectory>& aFilesOrDirectory, bool aRecursiveFlag, ErrorResult& aRv); void AddPromise(Promise* aPromise); void AddCallback(GetFilesCallback* aCallback); // CC methods void Unlink(); void Traverse(nsCycleCollectionTraversalCallback& cb); protected: - GetFilesHelper(nsIGlobalObject* aGlobal, bool aRecursiveFlag); + explicit GetFilesHelper(bool aRecursiveFlag); virtual ~GetFilesHelper(); void SetDirectoryPath(const nsAString& aDirectoryPath) { mDirectoryPath = aDirectoryPath; } virtual bool IsCanceled() override { @@ -95,48 +94,41 @@ class GetFilesHelper : public Runnable, virtual void Cancel(){}; NS_IMETHOD Run() override; void RunIO(); - void RunMainThread(); - void OperationCompleted(); void ResolveOrRejectPromise(Promise* aPromise); void RunCallback(GetFilesCallback* aCallback); - nsCOMPtr<nsIGlobalObject> mGlobal; - bool mListingCompleted; nsString mDirectoryPath; - // This is the real File sequence that we expose via Promises. - Sequence<RefPtr<File>> mFiles; - // Error code to propagate. nsresult mErrorResult; nsTArray<RefPtr<Promise>> mPromises; nsTArray<RefPtr<GetFilesCallback>> mCallbacks; Mutex mMutex; // This variable is protected by mutex. bool mCanceled; }; class GetFilesHelperChild final : public GetFilesHelper { public: - GetFilesHelperChild(nsIGlobalObject* aGlobal, bool aRecursiveFlag) - : GetFilesHelper(aGlobal, aRecursiveFlag), mPendingOperation(false) {} + explicit GetFilesHelperChild(bool aRecursiveFlag) + : GetFilesHelper(aRecursiveFlag), mPendingOperation(false) {} virtual void Work(ErrorResult& aRv) override; virtual void Cancel() override; bool AppendBlobImpl(BlobImpl* aBlobImpl); void Finished(nsresult aResult);
--- a/dom/filesystem/GetFilesTask.cpp +++ b/dom/filesystem/GetFilesTask.cpp @@ -27,18 +27,17 @@ namespace dom { /* static */ already_AddRefed<GetFilesTaskChild> GetFilesTaskChild::Create( FileSystemBase* aFileSystem, Directory* aDirectory, nsIFile* aTargetPath, bool aRecursiveFlag, ErrorResult& aRv) { MOZ_ASSERT(aFileSystem); MOZ_ASSERT(aDirectory); aFileSystem->AssertIsOnOwningThread(); - nsCOMPtr<nsIGlobalObject> globalObject = - do_QueryInterface(aFileSystem->GetParentObject()); + nsCOMPtr<nsIGlobalObject> globalObject = aFileSystem->GetParentObject(); if (NS_WARN_IF(!globalObject)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } RefPtr<GetFilesTaskChild> task = new GetFilesTaskChild( globalObject, aFileSystem, aDirectory, aTargetPath, aRecursiveFlag); @@ -102,22 +101,29 @@ void GetFilesTaskChild::SetSuccessReques FileSystemFilesResponse r = aValue; if (!mTargetData.SetLength(r.data().Length(), mozilla::fallible_t())) { aRv.Throw(NS_ERROR_OUT_OF_MEMORY); return; } + nsCOMPtr<nsIGlobalObject> globalObject = mFileSystem->GetParentObject(); + MOZ_ASSERT(globalObject); + for (uint32_t i = 0; i < r.data().Length(); ++i) { const FileSystemFileResponse& data = r.data()[i]; RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(data.blob()); MOZ_ASSERT(blobImpl); - mTargetData[i] = File::Create(mFileSystem->GetParentObject(), blobImpl); + mTargetData[i] = File::Create(globalObject, blobImpl); + if (NS_WARN_IF(!mTargetData[i])) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } } } void GetFilesTaskChild::HandlerCallback() { mFileSystem->AssertIsOnOwningThread(); if (mFileSystem->IsShutdown()) { mPromise = nullptr; return;
--- a/dom/filesystem/OSFileSystem.cpp +++ b/dom/filesystem/OSFileSystem.cpp @@ -20,39 +20,34 @@ namespace dom { OSFileSystem::OSFileSystem(const nsAString& aRootDir) { mLocalRootPath = aRootDir; } already_AddRefed<FileSystemBase> OSFileSystem::Clone() { AssertIsOnOwningThread(); RefPtr<OSFileSystem> fs = new OSFileSystem(mLocalRootPath); - if (mParent) { - fs->Init(mParent); + if (mGlobal) { + fs->Init(mGlobal); } return fs.forget(); } -void OSFileSystem::Init(nsISupports* aParent) { +void OSFileSystem::Init(nsIGlobalObject* aGlobal) { AssertIsOnOwningThread(); - MOZ_ASSERT(!mParent, "No duple Init() calls"); - MOZ_ASSERT(aParent); + MOZ_ASSERT(!mGlobal, "No duple Init() calls"); + MOZ_ASSERT(aGlobal); - mParent = aParent; - -#ifdef DEBUG - nsCOMPtr<nsIGlobalObject> obj = do_QueryInterface(aParent); - MOZ_ASSERT(obj); -#endif + mGlobal = aGlobal; } -nsISupports* OSFileSystem::GetParentObject() const { +nsIGlobalObject* OSFileSystem::GetParentObject() const { AssertIsOnOwningThread(); - return mParent; + return mGlobal; } bool OSFileSystem::IsSafeFile(nsIFile* aFile) const { // The concept of "safe files" is specific to the Device Storage API where // files are only "safe" if they're of a type that is appropriate for the // area of device storage that is being used. MOZ_CRASH("Don't use OSFileSystem with the Device Storage API"); return true; @@ -63,24 +58,24 @@ bool OSFileSystem::IsSafeDirectory(Direc // where a directory is only "safe" if it belongs to the area of device // storage that it is being used with. MOZ_CRASH("Don't use OSFileSystem with the Device Storage API"); return true; } void OSFileSystem::Unlink() { AssertIsOnOwningThread(); - mParent = nullptr; + mGlobal = nullptr; } void OSFileSystem::Traverse(nsCycleCollectionTraversalCallback& cb) { AssertIsOnOwningThread(); OSFileSystem* tmp = this; - NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent); + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal); } void OSFileSystem::SerializeDOMPath(nsAString& aOutput) const { AssertIsOnOwningThread(); aOutput = mLocalRootPath; } /**
--- a/dom/filesystem/OSFileSystem.h +++ b/dom/filesystem/OSFileSystem.h @@ -11,61 +11,61 @@ namespace mozilla { namespace dom { class OSFileSystem final : public FileSystemBase { public: explicit OSFileSystem(const nsAString& aRootDir); - void Init(nsISupports* aParent); + void Init(nsIGlobalObject* aGlobal); // Overrides FileSystemBase virtual already_AddRefed<FileSystemBase> Clone() override; virtual bool ShouldCreateDirectory() override { MOZ_CRASH("This should not be called."); // Because OSFileSystem should not be used when the creation of directories // is needed. For that we have OSFileSystemParent. return false; } - virtual nsISupports* GetParentObject() const override; + virtual nsIGlobalObject* GetParentObject() const override; virtual bool IsSafeFile(nsIFile* aFile) const override; virtual bool IsSafeDirectory(Directory* aDir) const override; virtual void SerializeDOMPath(nsAString& aOutput) const override; // CC methods virtual void Unlink() override; virtual void Traverse(nsCycleCollectionTraversalCallback& cb) override; private: virtual ~OSFileSystem() {} - nsCOMPtr<nsISupports> mParent; + nsCOMPtr<nsIGlobalObject> mGlobal; }; class OSFileSystemParent final : public FileSystemBase { public: explicit OSFileSystemParent(const nsAString& aRootDir); // Overrides FileSystemBase virtual already_AddRefed<FileSystemBase> Clone() override { MOZ_CRASH("This should not be called on the PBackground thread."); return nullptr; } virtual bool ShouldCreateDirectory() override { return false; } - virtual nsISupports* GetParentObject() const override { + virtual nsIGlobalObject* GetParentObject() const override { MOZ_CRASH("This should not be called on the PBackground thread."); return nullptr; } virtual void GetDirectoryName(nsIFile* aFile, nsAString& aRetval, ErrorResult& aRv) const override { MOZ_CRASH("This should not be called on the PBackground thread."); }
--- a/dom/filesystem/compat/FileSystemFileEntry.cpp +++ b/dom/filesystem/compat/FileSystemFileEntry.cpp @@ -25,18 +25,16 @@ class FileCallbackRunnable final : publi } // MOZ_CAN_RUN_SCRIPT_BOUNDARY until Runnable::Run is MOZ_CAN_RUN_SCRIPT. See // bug 1535398. MOZ_CAN_RUN_SCRIPT_BOUNDARY NS_IMETHOD Run() override { // Here we clone the File object. RefPtr<File> file = File::Create(mFile->GetParentObject(), mFile->Impl()); - MOZ_ASSERT(file); - mCallback->Call(*file); return NS_OK; } private: const RefPtr<FileCallback> mCallback; RefPtr<File> mFile; };
--- a/dom/html/HTMLCanvasElement.cpp +++ b/dom/html/HTMLCanvasElement.cpp @@ -878,18 +878,21 @@ nsresult HTMLCanvasElement::MozGetAsFile void* imgData = nullptr; rv = NS_ReadInputStreamToBuffer(stream, &imgData, -1, &imgSize); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsPIDOMWindowInner> win = do_QueryInterface(OwnerDoc()->GetScopeObject()); // The File takes ownership of the buffer - RefPtr<File> file = - File::CreateMemoryFile(win, imgData, imgSize, aName, type, PR_Now()); + RefPtr<File> file = File::CreateMemoryFile(win->AsGlobal(), imgData, imgSize, + aName, type, PR_Now()); + if (NS_WARN_IF(!file)) { + return NS_ERROR_FAILURE; + } file.forget(aResult); return NS_OK; } nsresult HTMLCanvasElement::GetContext(const nsAString& aContextId, nsISupports** aContext) { ErrorResult rv;
--- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -238,22 +238,29 @@ const double HTMLInputElement::kMsPerDay // events at the end of the exploration of the directories. class DispatchChangeEventCallback final : public GetFilesCallback { public: explicit DispatchChangeEventCallback(HTMLInputElement* aInputElement) : mInputElement(aInputElement) { MOZ_ASSERT(aInputElement); } - virtual void Callback(nsresult aStatus, - const Sequence<RefPtr<File>>& aFiles) override { + virtual void Callback( + nsresult aStatus, + const FallibleTArray<RefPtr<BlobImpl>>& aBlobImpls) override { nsTArray<OwningFileOrDirectory> array; - for (uint32_t i = 0; i < aFiles.Length(); ++i) { + for (uint32_t i = 0; i < aBlobImpls.Length(); ++i) { OwningFileOrDirectory* element = array.AppendElement(); - element->SetAsFile() = aFiles[i]; + RefPtr<File> file = + File::Create(mInputElement->GetOwnerGlobal(), aBlobImpls[i]); + if (NS_WARN_IF(!file)) { + break; + } + + element->SetAsFile() = file; } mInputElement->SetFilesOrDirectories(array, true); Unused << NS_WARN_IF(NS_FAILED(DispatchEvents())); } MOZ_CAN_RUN_SCRIPT_BOUNDARY nsresult DispatchEvents() { @@ -1997,17 +2004,19 @@ void HTMLInputElement::MozSetFileArray( MOZ_ASSERT(global); if (!global) { return; } nsTArray<OwningFileOrDirectory> files; for (uint32_t i = 0; i < aFiles.Length(); ++i) { RefPtr<File> file = File::Create(global, aFiles[i].get()->Impl()); - MOZ_ASSERT(file); + if (NS_WARN_IF(!file)) { + return; + } OwningFileOrDirectory* element = files.AppendElement(); element->SetAsFile() = file; } SetFilesOrDirectories(files, true); } @@ -2045,16 +2054,20 @@ void HTMLInputElement::MozSetFileNameArr nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject(); if (!global) { aRv.Throw(NS_ERROR_FAILURE); return; } RefPtr<File> domFile = File::CreateFromFile(global, file); + if (NS_WARN_IF(!domFile)) { + aRv.Throw(NS_ERROR_FAILURE); + return; + } OwningFileOrDirectory* element = files.AppendElement(); element->SetAsFile() = domFile; } SetFilesOrDirectories(files, true); } @@ -2071,17 +2084,17 @@ void HTMLInputElement::MozSetDirectory(c } nsPIDOMWindowInner* window = OwnerDoc()->GetInnerWindow(); if (NS_WARN_IF(!window)) { aRv.Throw(NS_ERROR_FAILURE); return; } - RefPtr<Directory> directory = Directory::Create(window, file); + RefPtr<Directory> directory = Directory::Create(window->AsGlobal(), file); MOZ_ASSERT(directory); nsTArray<OwningFileOrDirectory> array; OwningFileOrDirectory* element = array.AppendElement(); element->SetAsDirectory() = directory; SetFilesOrDirectories(array, true); } @@ -6107,31 +6120,34 @@ static nsTArray<OwningFileOrDirectory> R nsTArray<OwningFileOrDirectory> res(aData.Length()); for (auto& it : aData) { if (it.type() == FileContentData::TBlobImpl) { if (!it.get_BlobImpl()) { // Serialization failed, skip this file. continue; } - RefPtr<File> file = File::Create(aWindow, it.get_BlobImpl()); - MOZ_ASSERT(file); + RefPtr<File> file = File::Create(aWindow->AsGlobal(), it.get_BlobImpl()); + if (NS_WARN_IF(!file)) { + continue; + } OwningFileOrDirectory* element = res.AppendElement(); element->SetAsFile() = file; } else { MOZ_ASSERT(it.type() == FileContentData::TnsString); nsCOMPtr<nsIFile> file; nsresult rv = NS_NewLocalFile(it.get_nsString(), true, getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } - RefPtr<Directory> directory = Directory::Create(aWindow, file); + RefPtr<Directory> directory = + Directory::Create(aWindow->AsGlobal(), file); MOZ_ASSERT(directory); OwningFileOrDirectory* element = res.AppendElement(); element->SetAsDirectory() = directory; } } return res; } @@ -7129,38 +7145,31 @@ JSObject* HTMLInputElement::WrapNode(JSC JS::Handle<JSObject*> aGivenProto) { return HTMLInputElement_Binding::Wrap(aCx, this, aGivenProto); } GetFilesHelper* HTMLInputElement::GetOrCreateGetFilesHelper(bool aRecursiveFlag, ErrorResult& aRv) { MOZ_ASSERT(mFileData); - nsCOMPtr<nsIGlobalObject> global = OwnerDoc()->GetScopeObject(); - MOZ_ASSERT(global); - if (!global) { - aRv.Throw(NS_ERROR_FAILURE); - return nullptr; - } - if (aRecursiveFlag) { if (!mFileData->mGetFilesRecursiveHelper) { mFileData->mGetFilesRecursiveHelper = GetFilesHelper::Create( - global, GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv); + GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } return mFileData->mGetFilesRecursiveHelper; } if (!mFileData->mGetFilesNonRecursiveHelper) { mFileData->mGetFilesNonRecursiveHelper = GetFilesHelper::Create( - global, GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv); + GetFilesOrDirectoriesInternal(), aRecursiveFlag, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; } } return mFileData->mGetFilesNonRecursiveHelper; }
--- a/dom/indexedDB/ActorsChild.cpp +++ b/dom/indexedDB/ActorsChild.cpp @@ -531,16 +531,17 @@ void DeserializeStructuredCloneFiles( const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob(); const RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(ipcBlob); MOZ_ASSERT(blobImpl); RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwnerGlobal(), blobImpl); + MOZ_ASSERT(blob); StructuredCloneFile* const file = aFiles.AppendElement(); MOZ_ASSERT(file); file->mType = StructuredCloneFile::eBlob; file->mBlob.swap(blob); break; @@ -597,16 +598,17 @@ void DeserializeStructuredCloneFiles( const IPCBlob& ipcBlob = blobOrMutableFile.get_IPCBlob(); const RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(ipcBlob); MOZ_ASSERT(blobImpl); RefPtr<Blob> blob = Blob::Create(aDatabase->GetOwnerGlobal(), blobImpl); + MOZ_ASSERT(blob); StructuredCloneFile* const file = aFiles.AppendElement(); MOZ_ASSERT(file); file->mType = StructuredCloneFile::eStructuredClone; file->mBlob.swap(blob); } else { MOZ_ASSERT(blobOrMutableFile.type() == BlobOrMutableFile::Tnull_t);
--- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -621,69 +621,76 @@ class ValueDeserializationHelper { MOZ_ASSERT(aCx); MOZ_ASSERT(aData.tag == SCTAG_DOM_FILE || aData.tag == SCTAG_DOM_FILE_WITHOUT_LASTMODIFIEDDATE || aData.tag == SCTAG_DOM_BLOB); MOZ_ASSERT(aFile.mType == StructuredCloneFile::eBlob); RefPtr<Blob> blob = aFile.mBlob; + // It can happen that this IDB is chrome code, so there is no parent, but + // still we want to set a correct parent for the new File object. + nsCOMPtr<nsIGlobalObject> global; + if (NS_IsMainThread()) { + if (aDatabase && aDatabase->GetParentObject()) { + global = aDatabase->GetParentObject(); + } else { + global = xpc::CurrentNativeGlobal(aCx); + } + } else { + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + MOZ_ASSERT(workerPrivate); + + WorkerGlobalScope* globalScope = workerPrivate->GlobalScope(); + MOZ_ASSERT(globalScope); + + global = do_QueryObject(globalScope); + } + + MOZ_ASSERT(global); + /* If we are creating an index, we do not have an mBlob but do have an * mInfo. Unlike other index or upgrade cases, we do need a real-looking * Blob/File instance because the index's key path can reference their * properties. Rather than create a fake-looking object, create a real * Blob. */ if (!blob) { MOZ_ASSERT(aFile.mFileInfo); const nsCOMPtr<nsIFile> file = FileInfo::GetFileForFileInfo(aFile.mFileInfo); if (!file) { return false; } const RefPtr<FileBlobImpl> impl = new FileBlobImpl(file); impl->SetFileId(aFile.mFileInfo->Id()); - blob = File::Create(nullptr, impl); - } - - // It can happen that this IDB is chrome code, so there is no parent, but - // still we want to set a correct parent for the new File object. - nsCOMPtr<nsISupports> parent; - if (NS_IsMainThread()) { - if (aDatabase && aDatabase->GetParentObject()) { - parent = aDatabase->GetParentObject(); - } else { - parent = xpc::CurrentNativeGlobal(aCx); + blob = File::Create(global, impl); + if (NS_WARN_IF(!blob)) { + return false; } - } else { - WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); - MOZ_ASSERT(workerPrivate); - - WorkerGlobalScope* globalScope = workerPrivate->GlobalScope(); - MOZ_ASSERT(globalScope); - - parent = do_QueryObject(globalScope); } - MOZ_ASSERT(parent); - if (aData.tag == SCTAG_DOM_BLOB) { blob->Impl()->SetLazyData(VoidString(), aData.type, aData.size, INT64_MAX); MOZ_ASSERT(!blob->IsFile()); // ActorsParent sends here a kind of half blob and half file wrapped into // a DOM File object. DOM File and DOM Blob are a WebIDL wrapper around a // BlobImpl object. SetLazyData() has just changed the BlobImpl to be a // Blob (see the previous assert), but 'blob' still has the WebIDL DOM // File wrapping. // Before exposing it to content, we must recreate a DOM Blob object. const RefPtr<Blob> exposedBlob = Blob::Create(blob->GetParentObject(), blob->Impl()); + if (NS_WARN_IF(!exposedBlob)) { + return false; + } + MOZ_ASSERT(exposedBlob); JS::Rooted<JS::Value> wrappedBlob(aCx); if (!ToJSValue(aCx, exposedBlob, &wrappedBlob)) { return false; } aResult.set(&wrappedBlob.toObject()); return true;
--- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -12,16 +12,17 @@ #include "GeckoProfiler.h" #include "MediaDecoder.h" #include "MediaEncoder.h" #include "MediaTrackGraphImpl.h" #include "VideoUtils.h" #include "mozilla/DOMEventTargetHelper.h" #include "mozilla/dom/AudioStreamTrack.h" #include "mozilla/dom/BlobEvent.h" +#include "mozilla/dom/EmptyBlobImpl.h" #include "mozilla/dom/File.h" #include "mozilla/dom/MediaRecorderErrorEvent.h" #include "mozilla/dom/MutableBlobStorage.h" #include "mozilla/dom/VideoStreamTrack.h" #include "mozilla/media/MediaUtils.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Preferences.h" #include "mozilla/StaticPtr.h" @@ -836,50 +837,47 @@ class MediaRecorder::Session : public Pr void MaybeCreateMutableBlobStorage() { if (!mMutableBlobStorage) { mMutableBlobStorage = new MutableBlobStorage( MutableBlobStorage::eCouldBeInTemporaryFile, nullptr, mMaxMemory); } } static const bool IsExclusive = false; - using BlobPromise = - MozPromise<nsMainThreadPtrHandle<Blob>, nsresult, IsExclusive>; + using BlobPromise = MozPromise<RefPtr<BlobImpl>, nsresult, IsExclusive>; class BlobStorer : public MutableBlobStorageCallback { MozPromiseHolder<BlobPromise> mHolder; virtual ~BlobStorer() = default; public: BlobStorer() = default; NS_INLINE_DECL_THREADSAFE_REFCOUNTING(BlobStorer, override) - void BlobStoreCompleted(MutableBlobStorage*, Blob* aBlob, + void BlobStoreCompleted(MutableBlobStorage*, BlobImpl* aBlobImpl, nsresult aRv) override { MOZ_ASSERT(NS_IsMainThread()); if (NS_FAILED(aRv)) { mHolder.Reject(aRv, __func__); - } else { - mHolder.Resolve(nsMainThreadPtrHandle<Blob>( - MakeAndAddRef<nsMainThreadPtrHolder<Blob>>( - "BlobStorer::ResolveBlob", aBlob)), - __func__); + return; } + + mHolder.Resolve(aBlobImpl, __func__); } RefPtr<BlobPromise> Promise() { return mHolder.Ensure(__func__); } }; protected: RefPtr<BlobPromise> GatherBlobImpl() { RefPtr<BlobStorer> storer = MakeAndAddRef<BlobStorer>(); MaybeCreateMutableBlobStorage(); - mMutableBlobStorage->GetBlobWhenReady( - mRecorder->GetOwner(), NS_ConvertUTF16toUTF8(mMimeType), storer); + mMutableBlobStorage->GetBlobImplWhenReady(NS_ConvertUTF16toUTF8(mMimeType), + storer); mMutableBlobStorage = nullptr; storer->Promise()->Then( mMainThread, __func__, [self = RefPtr<Session>(this), p = storer->Promise()] { if (self->mBlobPromise == p) { // Reset BlobPromise. self->mBlobPromise = nullptr; @@ -1106,62 +1104,62 @@ class MediaRecorder::Session : public Pr if (rv == NS_OK) { mRunningState = RunningState::Stopped; } else { mRunningState = Err(rv); } GatherBlob() - ->Then(mMainThread, __func__, - [this, self = RefPtr<Session>(this), rv, needsStartEvent]( - const BlobPromise::ResolveOrRejectValue& aResult) { - if (mRecorder->mSessions.LastElement() == this) { - // Set state to inactive, but only if the recorder is not - // controlled by another session already. - mRecorder->Inactivate(); - } + ->Then( + mMainThread, __func__, + [this, self = RefPtr<Session>(this), rv, needsStartEvent]( + const BlobPromise::ResolveOrRejectValue& aResult) { + if (mRecorder->mSessions.LastElement() == this) { + // Set state to inactive, but only if the recorder is not + // controlled by another session already. + mRecorder->Inactivate(); + } - if (needsStartEvent) { - mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("start")); - } + if (needsStartEvent) { + mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("start")); + } - // If there was an error, Fire the appropriate one - if (NS_FAILED(rv)) { - mRecorder->NotifyError(rv); - } + // If there was an error, Fire the appropriate one + if (NS_FAILED(rv)) { + mRecorder->NotifyError(rv); + } - // Fire a blob event named dataavailable - RefPtr<Blob> blob; - if (rv == NS_ERROR_DOM_SECURITY_ERR || aResult.IsReject()) { - // In case of SecurityError, the blob data must be discarded. - // We create a new empty one and throw the blob with its data - // away. - // In case we failed to gather blob data, we create an empty - // memory blob instead. - blob = Blob::CreateEmptyBlob(mRecorder->GetParentObject(), - mMimeType); - } else { - blob = aResult.ResolveValue(); - } - if (NS_FAILED(mRecorder->CreateAndDispatchBlobEvent(blob))) { - // Failed to dispatch blob event. That's unexpected. It's - // probably all right to fire an error event if we haven't - // already. - if (NS_SUCCEEDED(rv)) { - mRecorder->NotifyError(NS_ERROR_FAILURE); - } - } + // Fire a blob event named dataavailable + RefPtr<BlobImpl> blobImpl; + if (rv == NS_ERROR_DOM_SECURITY_ERR || aResult.IsReject()) { + // In case of SecurityError, the blob data must be discarded. + // We create a new empty one and throw the blob with its data + // away. + // In case we failed to gather blob data, we create an empty + // memory blob instead. + blobImpl = new EmptyBlobImpl(mMimeType); + } else { + blobImpl = aResult.ResolveValue(); + } + if (NS_FAILED(mRecorder->CreateAndDispatchBlobEvent(blobImpl))) { + // Failed to dispatch blob event. That's unexpected. It's + // probably all right to fire an error event if we haven't + // already. + if (NS_SUCCEEDED(rv)) { + mRecorder->NotifyError(NS_ERROR_FAILURE); + } + } - // Fire an event named stop - mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop")); + // Fire an event named stop + mRecorder->DispatchSimpleEvent(NS_LITERAL_STRING("stop")); - // And finally, Shutdown and destroy the Session - return Shutdown(); - }) + // And finally, Shutdown and destroy the Session + return Shutdown(); + }) ->Then(mMainThread, __func__, [this, self = RefPtr<Session>(this)] { GetShutdownBarrier()->RemoveBlocker(mShutdownBlocker); mShutdownBlocker = nullptr; }); } void MediaEncoderInitialized() { MOZ_ASSERT(mEncoderThread->IsCurrentThreadIn()); @@ -1897,23 +1895,33 @@ bool MediaRecorder::IsTypeSupported(Glob return MediaRecorder::IsTypeSupported(aMIMEType); } /* static */ bool MediaRecorder::IsTypeSupported(const nsAString& aMIMEType) { return IsTypeSupportedImpl(aMIMEType) == TypeSupport::Supported; } -nsresult MediaRecorder::CreateAndDispatchBlobEvent(Blob* aBlob) { +nsresult MediaRecorder::CreateAndDispatchBlobEvent(BlobImpl* aBlobImpl) { MOZ_ASSERT(NS_IsMainThread(), "Not running on main thread"); + if (!GetOwnerGlobal()) { + // This MediaRecorder has been disconnected in the meantime. + return NS_ERROR_FAILURE; + } + + RefPtr<Blob> blob = Blob::Create(GetOwnerGlobal(), aBlobImpl); + if (NS_WARN_IF(!blob)) { + return NS_ERROR_FAILURE; + } + BlobEventInit init; init.mBubbles = false; init.mCancelable = false; - init.mData = aBlob; + init.mData = blob; RefPtr<BlobEvent> event = BlobEvent::Constructor(this, NS_LITERAL_STRING("dataavailable"), init); event->SetTrusted(true); ErrorResult rv; DispatchEvent(*event, rv); return rv.StealNSResult(); }
--- a/dom/media/MediaRecorder.h +++ b/dom/media/MediaRecorder.h @@ -116,17 +116,17 @@ class MediaRecorder final : public DOMEv uint32_t AudioBitsPerSecond() const { return mAudioBitsPerSecond; } uint32_t VideoBitsPerSecond() const { return mVideoBitsPerSecond; } protected: virtual ~MediaRecorder(); MediaRecorder& operator=(const MediaRecorder& x) = delete; // Create dataavailable event with Blob data and it runs in main thread - nsresult CreateAndDispatchBlobEvent(Blob* aBlob); + nsresult CreateAndDispatchBlobEvent(BlobImpl* aBlobImpl); // Creating a simple event to notify UA simple event. void DispatchSimpleEvent(const nsAString& aStr); // Creating a error event with message. void NotifyError(nsresult aRv); MediaRecorder(const MediaRecorder& x) = delete; // prevent bad usage // Remove session pointer. void RemoveSession(Session* aSession);
--- a/dom/media/imagecapture/CaptureTask.cpp +++ b/dom/media/imagecapture/CaptureTask.cpp @@ -31,29 +31,33 @@ class CaptureTask::MediaTrackEventListen }; CaptureTask::CaptureTask(dom::ImageCapture* aImageCapture) : mImageCapture(aImageCapture), mEventListener(new MediaTrackEventListener(this)), mImageGrabbedOrTrackEnd(false), mPrincipalChanged(false) {} -nsresult CaptureTask::TaskComplete(already_AddRefed<dom::Blob> aBlob, +nsresult CaptureTask::TaskComplete(already_AddRefed<dom::BlobImpl> aBlobImpl, nsresult aRv) { MOZ_ASSERT(NS_IsMainThread()); DetachTrack(); nsresult rv; - RefPtr<dom::Blob> blob(aBlob); + RefPtr<dom::BlobImpl> blobImpl(aBlobImpl); // We have to set the parent because the blob has been generated with a valid // one. - if (blob) { - blob = dom::Blob::Create(mImageCapture->GetParentObject(), blob->Impl()); + RefPtr<dom::Blob> blob; + if (blobImpl) { + blob = dom::Blob::Create(mImageCapture->GetOwnerGlobal(), blobImpl); + if (NS_WARN_IF(!blob)) { + return NS_ERROR_FAILURE; + } } if (mPrincipalChanged) { aRv = NS_ERROR_DOM_SECURITY_ERR; IC_LOG("MediaStream principal should not change during TakePhoto()."); } if (NS_SUCCEEDED(aRv)) { @@ -99,19 +103,20 @@ void CaptureTask::NotifyRealtimeTrackDat MOZ_ASSERT(aMedia.GetType() == MediaSegment::VIDEO); const VideoSegment& video = static_cast<const VideoSegment&>(aMedia); // Callback for encoding complete, it calls on main thread. class EncodeComplete : public dom::EncodeCompleteCallback { public: explicit EncodeComplete(CaptureTask* aTask) : mTask(aTask) {} - nsresult ReceiveBlob(already_AddRefed<dom::Blob> aBlob) override { - RefPtr<dom::Blob> blob(aBlob); - mTask->TaskComplete(blob.forget(), NS_OK); + nsresult ReceiveBlobImpl( + already_AddRefed<dom::BlobImpl> aBlobImpl) override { + RefPtr<dom::BlobImpl> blobImpl(aBlobImpl); + mTask->TaskComplete(blobImpl.forget(), NS_OK); mTask = nullptr; return NS_OK; } protected: RefPtr<CaptureTask> mTask; };
--- a/dom/media/imagecapture/CaptureTask.h +++ b/dom/media/imagecapture/CaptureTask.h @@ -9,17 +9,17 @@ #include "MediaTrackGraph.h" #include "MediaTrackListener.h" #include "PrincipalChangeObserver.h" namespace mozilla { namespace dom { -class Blob; +class BlobImpl; class ImageCapture; class MediaStreamTrack; } // namespace dom /** * CaptureTask retrieves image from MediaTrack and encodes the image to jpeg in * ImageEncoder. The whole procedures start at AttachTrack(), it will add this * class into MediaTrack and retrieves an image in MediaTrackGraph thread. @@ -43,17 +43,18 @@ class CaptureTask : public DirectMediaTr // CaptureTask methods. // It is called when aBlob is ready to post back to script in company with // aRv == NS_OK. If aRv is not NS_OK, it will post an error event to script. // // Note: // this function should be called on main thread. - nsresult TaskComplete(already_AddRefed<dom::Blob> aBlob, nsresult aRv); + nsresult TaskComplete(already_AddRefed<dom::BlobImpl> aBlobImpl, + nsresult aRv); // Add listeners into MediaStreamTrack and PrincipalChangeObserver. // It should be on main thread only. void AttachTrack(); // Remove listeners from MediaStreamTrack and PrincipalChangeObserver. // It should be on main thread only. void DetachTrack();
--- a/dom/presentation/PresentationConnection.cpp +++ b/dom/presentation/PresentationConnection.cpp @@ -507,18 +507,20 @@ nsresult PresentationConnection::DoRecei } JSContext* cx = jsapi.cx(); JS::Rooted<JS::Value> jsData(cx); nsresult rv; if (aIsBinary) { if (mBinaryType == PresentationConnectionBinaryType::Blob) { RefPtr<Blob> blob = - Blob::CreateStringBlob(GetOwner(), aData, EmptyString()); - MOZ_ASSERT(blob); + Blob::CreateStringBlob(GetOwnerGlobal(), aData, EmptyString()); + if (NS_WARN_IF(!blob)) { + return NS_ERROR_FAILURE; + } if (!ToJSValue(cx, blob, &jsData)) { return NS_ERROR_FAILURE; } } else if (mBinaryType == PresentationConnectionBinaryType::Arraybuffer) { JS::Rooted<JSObject*> arrayBuf(cx); rv = nsContentUtils::CreateArrayBuffer(cx, aData, arrayBuf.address()); if (NS_WARN_IF(NS_FAILED(rv))) {
--- a/dom/serviceworkers/ServiceWorkerEvents.cpp +++ b/dom/serviceworkers/ServiceWorkerEvents.cpp @@ -1046,17 +1046,17 @@ nsresult ExtractBytesFromData( if (aDataInit.IsUSVString()) { return ExtractBytesFromUSVString(aDataInit.GetAsUSVString(), aBytes); } MOZ_ASSERT_UNREACHABLE("Unexpected push message data"); return NS_ERROR_FAILURE; } } // namespace -PushMessageData::PushMessageData(nsISupports* aOwner, +PushMessageData::PushMessageData(nsIGlobalObject* aOwner, nsTArray<uint8_t>&& aBytes) : mOwner(aOwner), mBytes(std::move(aBytes)) {} PushMessageData::~PushMessageData() {} NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(PushMessageData, mOwner) NS_IMPL_CYCLE_COLLECTING_ADDREF(PushMessageData) @@ -1144,17 +1144,17 @@ already_AddRefed<PushEvent> PushEvent::C e->SetComposed(aOptions.mComposed); if (aOptions.mData.WasPassed()) { nsTArray<uint8_t> bytes; nsresult rv = ExtractBytesFromData(aOptions.mData.Value(), bytes); if (NS_FAILED(rv)) { aRv.Throw(rv); return nullptr; } - e->mData = new PushMessageData(aOwner, std::move(bytes)); + e->mData = new PushMessageData(aOwner->GetOwnerGlobal(), std::move(bytes)); } return e.forget(); } NS_IMPL_ADDREF_INHERITED(PushEvent, ExtendableEvent) NS_IMPL_RELEASE_INHERITED(PushEvent, ExtendableEvent) NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(PushEvent)
--- a/dom/serviceworkers/ServiceWorkerEvents.h +++ b/dom/serviceworkers/ServiceWorkerEvents.h @@ -178,29 +178,29 @@ class FetchEvent final : public Extendab 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; } + nsIGlobalObject* 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); + PushMessageData(nsIGlobalObject* aOwner, nsTArray<uint8_t>&& aBytes); private: - nsCOMPtr<nsISupports> mOwner; + nsCOMPtr<nsIGlobalObject> mOwner; nsTArray<uint8_t> mBytes; nsString mDecodedText; ~PushMessageData(); nsresult EnsureDecodedText(); uint8_t* GetContentsCopy(); };
--- a/dom/websocket/WebSocket.cpp +++ b/dom/websocket/WebSocket.cpp @@ -1839,17 +1839,19 @@ nsresult WebSocket::CreateAndDispatchMes // Create appropriate JS object for message JS::Rooted<JS::Value> jsData(cx); if (aIsBinary) { if (mBinaryType == dom::BinaryType::Blob) { messageType = nsIWebSocketEventListener::TYPE_BLOB; RefPtr<Blob> blob = Blob::CreateStringBlob(GetOwnerGlobal(), aData, EmptyString()); - MOZ_ASSERT(blob); + if (NS_WARN_IF(!blob)) { + return NS_ERROR_FAILURE; + } if (!ToJSValue(cx, blob, &jsData)) { return NS_ERROR_FAILURE; } } else if (mBinaryType == dom::BinaryType::Arraybuffer) { messageType = nsIWebSocketEventListener::TYPE_ARRAYBUFFER;
--- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -1696,17 +1696,17 @@ XMLHttpRequestMainThread::OnDataAvailabl nsCOMPtr<nsIFile> localFile; nsCOMPtr<nsIURI> blobURI; GetBlobURIFromChannel(request, getter_AddRefs(blobURI)); if (blobURI) { RefPtr<BlobImpl> blobImpl; rv = NS_GetBlobForBlobURI(blobURI, getter_AddRefs(blobImpl)); if (NS_SUCCEEDED(rv)) { if (blobImpl) { - mResponseBlob = Blob::Create(GetOwner(), blobImpl); + mResponseBlob = Blob::Create(GetOwnerGlobal(), blobImpl); } if (!mResponseBlob) { rv = NS_ERROR_FILE_NOT_FOUND; } } } else { rv = GetLocalFileFromChannel(request, getter_AddRefs(localFile)); } @@ -2138,17 +2138,17 @@ XMLHttpRequestMainThread::OnStopRequest( contentType.Assign(NS_ConvertUTF16toUTF8(mOverrideMimeType)); } else { mChannel->GetContentType(contentType); } // mBlobStorage can be null if the channel is non-file non-cacheable // and if the response length is zero. MaybeCreateBlobStorage(); - mBlobStorage->GetBlobWhenReady(GetOwner(), contentType, this); + mBlobStorage->GetBlobImplWhenReady(contentType, this); waitingForBlobCreation = true; NS_ASSERTION(mResponseBody.IsEmpty(), "mResponseBody should be empty"); NS_ASSERTION(mResponseText.IsEmpty(), "mResponseText should be empty"); } else if (NS_SUCCEEDED(status) && !mIsMappedArrayBuffer && mResponseType == XMLHttpRequestResponseType::Arraybuffer) { // set the capacity down to the actual length, to realloc back // down to the actual size @@ -3578,25 +3578,25 @@ void XMLHttpRequestMainThread::MaybeCrea if (nsCOMPtr<nsIGlobalObject> global = GetOwnerGlobal()) { eventTarget = global->EventTargetFor(TaskCategory::Other); } mBlobStorage = new MutableBlobStorage(storageType, eventTarget); } void XMLHttpRequestMainThread::BlobStoreCompleted( - MutableBlobStorage* aBlobStorage, Blob* aBlob, nsresult aRv) { + MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl, nsresult aRv) { // Ok, the state is changed... if (mBlobStorage != aBlobStorage || NS_FAILED(aRv)) { return; } MOZ_ASSERT(mState != XMLHttpRequest_Binding::DONE); - mResponseBlob = aBlob; + mResponseBlob = Blob::Create(GetOwnerGlobal(), aBlobImpl); mBlobStorage = nullptr; ChangeStateToDone(mFlagSyncLooping); } NS_IMETHODIMP XMLHttpRequestMainThread::GetName(nsACString& aName) { aName.AssignLiteral("XMLHttpRequest");
--- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -428,17 +428,17 @@ class XMLHttpRequestMainThread final : p static void SetDontWarnAboutSyncXHR(bool aVal) { sDontWarnAboutSyncXHR = aVal; } static bool DontWarnAboutSyncXHR() { return sDontWarnAboutSyncXHR; } virtual void SetOriginAttributes( const mozilla::dom::OriginAttributesDictionary& aAttrs) override; - void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, Blob* aBlob, + void BlobStoreCompleted(MutableBlobStorage* aBlobStorage, BlobImpl* aBlobImpl, nsresult aResult) override; void LocalFileToBlobCompleted(Blob* aBlob); protected: nsresult DetectCharset(); nsresult AppendToResponseText(Span<const uint8_t> aBuffer, bool aLast = false);
--- a/editor/libeditor/HTMLEditSubActionHandler.cpp +++ b/editor/libeditor/HTMLEditSubActionHandler.cpp @@ -4965,16 +4965,110 @@ EditActionResult HTMLEditor::IndentAsSub nsresult rv = MaybeInsertPaddingBRElementForEmptyLastLineAtSelection(); NS_WARNING_ASSERTION( NS_SUCCEEDED(rv), "MaybeInsertPaddingBRElementForEmptyLastLineAtSelection() failed"); return result.SetResult(rv); } +// Helper for Handle[CSS|HTML]IndentAtSelectionInternal +nsresult HTMLEditor::IndentListChild(RefPtr<Element>* aCurList, + const EditorDOMPoint& aCurPoint, + OwningNonNull<nsINode>& aCurNode) { + MOZ_ASSERT(HTMLEditUtils::IsList(aCurPoint.GetContainer()), + "unexpected container"); + MOZ_ASSERT(IsTopLevelEditSubActionDataAvailable()); + + // some logic for putting list items into nested lists... + + // Check for whether we should join a list that follows aCurNode. + // We do this if the next element is a list, and the list is of the + // same type (li/ol) as aCurNode was a part it. + if (nsIContent* nextEditableSibling = + GetNextHTMLSibling(aCurNode, SkipWhitespace::Yes)) { + if (HTMLEditUtils::IsList(nextEditableSibling) && + aCurPoint.GetContainer()->NodeInfo()->NameAtom() == + nextEditableSibling->NodeInfo()->NameAtom() && + aCurPoint.GetContainer()->NodeInfo()->NamespaceID() == + nextEditableSibling->NodeInfo()->NamespaceID()) { + nsresult rv = + MoveNodeWithTransaction(MOZ_KnownLive(*aCurNode->AsContent()), + EditorDOMPoint(nextEditableSibling, 0)); + if (NS_WARN_IF(Destroyed())) { + return NS_ERROR_EDITOR_DESTROYED; + } + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "MoveNodeWithTransaction() failed"); + return rv; + } + } + + // Check for whether we should join a list that preceeds aCurNode. + // We do this if the previous element is a list, and the list is of + // the same type (li/ol) as aCurNode was a part of. + if (nsCOMPtr<nsIContent> previousEditableSibling = + GetPriorHTMLSibling(aCurNode, SkipWhitespace::Yes)) { + if (HTMLEditUtils::IsList(previousEditableSibling) && + aCurPoint.GetContainer()->NodeInfo()->NameAtom() == + previousEditableSibling->NodeInfo()->NameAtom() && + aCurPoint.GetContainer()->NodeInfo()->NamespaceID() == + previousEditableSibling->NodeInfo()->NamespaceID()) { + nsresult rv = + MoveNodeToEndWithTransaction(MOZ_KnownLive(*aCurNode->AsContent()), + *previousEditableSibling); + if (NS_WARN_IF(Destroyed())) { + return NS_ERROR_EDITOR_DESTROYED; + } + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "MoveNodeToEndWithTransaction() failed"); + return rv; + } + } + + // check to see if aCurList is still appropriate. Which it is if + // aCurNode is still right after it in the same list. + nsIContent* previousEditableSibling = + *aCurList ? GetPriorHTMLSibling(aCurNode, SkipWhitespace::Yes) : nullptr; + if (!*aCurList || + (previousEditableSibling && previousEditableSibling != *aCurList)) { + nsAtom* containerName = + aCurPoint.GetContainer()->NodeInfo()->NameAtom(); + // Create a new nested list of correct type. + SplitNodeResult splitNodeResult = + MaybeSplitAncestorsForInsertWithTransaction( + MOZ_KnownLive(*containerName), aCurPoint); + if (NS_WARN_IF(splitNodeResult.Failed())) { + return splitNodeResult.Rv(); + } + *aCurList = CreateNodeWithTransaction(MOZ_KnownLive(*containerName), + splitNodeResult.SplitPoint()); + if (NS_WARN_IF(Destroyed())) { + return NS_ERROR_EDITOR_DESTROYED; + } + if (NS_WARN_IF(!*aCurList)) { + return NS_ERROR_FAILURE; + } + // aCurList is now the correct thing to put aCurNode in + // remember our new block for postprocessing + TopLevelEditSubActionDataRef().mNewBlockElement = *aCurList; + } + // tuck the node into the end of the active list + RefPtr<nsINode> container = *aCurList; + nsresult rv = + MoveNodeToEndWithTransaction(MOZ_KnownLive(*aCurNode->AsContent()), + *container); + if (NS_WARN_IF(Destroyed())) { + return NS_ERROR_EDITOR_DESTROYED; + } + NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), + "MoveNodeToEndWithTransaction() failed"); + return rv; +} + EditActionResult HTMLEditor::HandleIndentAtSelection() { MOZ_ASSERT(IsEditActionDataAvailable()); nsresult rv = EnsureNoPaddingBRElementForEmptyEditor(); if (NS_WARN_IF(rv == NS_ERROR_EDITOR_DESTROYED)) { return EditActionResult(NS_ERROR_EDITOR_DESTROYED); } NS_WARNING_ASSERTION( @@ -5139,98 +5233,19 @@ nsresult HTMLEditor::HandleCSSIndentAtSe } // Ignore all non-editable nodes. Leave them be. // XXX We ignore non-editable nodes here, but not so in the above block. if (!IsEditable(curNode)) { continue; } - // some logic for putting list items into nested lists... if (HTMLEditUtils::IsList(atCurNode.GetContainer())) { - // Check for whether we should join a list that follows curNode. - // We do this if the next element is a list, and the list is of the - // same type (li/ol) as curNode was a part it. - // XXX We also check namespace of the element here, but we don't do - // that in other places. - if (nsIContent* nextEditableSibling = GetNextHTMLSibling(curNode)) { - if (HTMLEditUtils::IsList(nextEditableSibling) && - atCurNode.GetContainer()->NodeInfo()->NameAtom() == - nextEditableSibling->NodeInfo()->NameAtom() && - atCurNode.GetContainer()->NodeInfo()->NamespaceID() == - nextEditableSibling->NodeInfo()->NamespaceID()) { - nsresult rv = - MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()), - EditorDOMPoint(nextEditableSibling, 0)); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - continue; - } - } - - // Check for whether we should join a list that preceeds curNode. - // We do this if the previous element is a list, and the list is of - // the same type (li/ol) as curNode was a part of. - if (nsCOMPtr<nsIContent> previousEditableSibling = - GetPriorHTMLSibling(curNode)) { - if (HTMLEditUtils::IsList(previousEditableSibling) && - atCurNode.GetContainer()->NodeInfo()->NameAtom() == - previousEditableSibling->NodeInfo()->NameAtom() && - atCurNode.GetContainer()->NodeInfo()->NamespaceID() == - previousEditableSibling->NodeInfo()->NamespaceID()) { - nsresult rv = MoveNodeToEndWithTransaction( - MOZ_KnownLive(*curNode->AsContent()), *previousEditableSibling); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - continue; - } - } - - // check to see if curList is still appropriate. Which it is if - // curNode is still right after it in the same list. - nsIContent* previousEditableSibling = - curList ? GetPriorHTMLSibling(curNode) : nullptr; - if (!curList || - (previousEditableSibling && previousEditableSibling != curList)) { - nsAtom* containerName = - atCurNode.GetContainer()->NodeInfo()->NameAtom(); - // Create a new nested list of correct type. - SplitNodeResult splitNodeResult = - MaybeSplitAncestorsForInsertWithTransaction( - MOZ_KnownLive(*containerName), atCurNode); - if (NS_WARN_IF(splitNodeResult.Failed())) { - return splitNodeResult.Rv(); - } - curList = CreateNodeWithTransaction(MOZ_KnownLive(*containerName), - splitNodeResult.SplitPoint()); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(!curList)) { - return NS_ERROR_FAILURE; - } - // curList is now the correct thing to put curNode in - // remember our new block for postprocessing - TopLevelEditSubActionDataRef().mNewBlockElement = curList; - } - // tuck the node into the end of the active list - nsresult rv = MoveNodeToEndWithTransaction( - MOZ_KnownLive(*curNode->AsContent()), *curList); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(NS_FAILED(rv))) { + nsresult rv = IndentListChild(&curList, atCurNode, curNode); + if (NS_FAILED(rv)) { return rv; } continue; } // Not a list item. if (HTMLEditor::NodeIsBlockStatic(*curNode)) { @@ -5404,100 +5419,23 @@ nsresult HTMLEditor::HandleHTMLIndentAtS } // Ignore all non-editable nodes. Leave them be. // XXX We ignore non-editable nodes here, but not so in the above block. if (!IsEditable(curNode)) { continue; } - // some logic for putting list items into nested lists... if (HTMLEditUtils::IsList(atCurNode.GetContainer())) { - // Check for whether we should join a list that follows curNode. - // We do this if the next element is a list, and the list is of the - // same type (li/ol) as curNode was a part it. - if (nsIContent* nextEditableSibling = GetNextHTMLSibling(curNode)) { - if (HTMLEditUtils::IsList(nextEditableSibling) && - atCurNode.GetContainer()->NodeInfo()->NameAtom() == - nextEditableSibling->NodeInfo()->NameAtom() && - atCurNode.GetContainer()->NodeInfo()->NamespaceID() == - nextEditableSibling->NodeInfo()->NamespaceID()) { - rv = MoveNodeWithTransaction(MOZ_KnownLive(*curNode->AsContent()), - EditorDOMPoint(nextEditableSibling, 0)); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - continue; - } - } - - // Check for whether we should join a list that preceeds curNode. - // We do this if the previous element is a list, and the list is of - // the same type (li/ol) as curNode was a part of. - if (nsCOMPtr<nsIContent> previousEditableSibling = - GetPriorHTMLSibling(curNode)) { - if (HTMLEditUtils::IsList(previousEditableSibling) && - atCurNode.GetContainer()->NodeInfo()->NameAtom() == - previousEditableSibling->NodeInfo()->NameAtom() && - atCurNode.GetContainer()->NodeInfo()->NamespaceID() == - previousEditableSibling->NodeInfo()->NamespaceID()) { - rv = MoveNodeToEndWithTransaction( - MOZ_KnownLive(*curNode->AsContent()), *previousEditableSibling); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } - continue; - } - } - - // check to see if curList is still appropriate. Which it is if - // curNode is still right after it in the same list. - nsIContent* previousEditableSibling = - curList ? GetPriorHTMLSibling(curNode) : nullptr; - if (!curList || - (previousEditableSibling && previousEditableSibling != curList)) { - nsAtom* containerName = - atCurNode.GetContainer()->NodeInfo()->NameAtom(); - // Create a new nested list of correct type. - SplitNodeResult splitNodeResult = - MaybeSplitAncestorsForInsertWithTransaction( - MOZ_KnownLive(*containerName), atCurNode); - if (NS_WARN_IF(splitNodeResult.Failed())) { - return splitNodeResult.Rv(); - } - curList = CreateNodeWithTransaction(MOZ_KnownLive(*containerName), - splitNodeResult.SplitPoint()); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(!curList)) { - return NS_ERROR_FAILURE; - } - // curList is now the correct thing to put curNode in - // remember our new block for postprocessing - TopLevelEditSubActionDataRef().mNewBlockElement = curList; - } - // tuck the node into the end of the active list - rv = MoveNodeToEndWithTransaction(MOZ_KnownLive(*curNode->AsContent()), - *curList); - if (NS_WARN_IF(Destroyed())) { - return NS_ERROR_EDITOR_DESTROYED; - } - if (NS_WARN_IF(NS_FAILED(rv))) { + nsresult rv = IndentListChild(&curList, atCurNode, curNode); + if (NS_FAILED(rv)) { return rv; } // forget curQuote, if any curQuote = nullptr; - continue; } // Not a list item, use blockquote? // if we are inside a list item, we don't want to blockquote, we want // to sublist the list item. We may have several nodes listed in the // array of nodes to act on, that are in the same list item. Since
--- a/editor/libeditor/HTMLEditor.cpp +++ b/editor/libeditor/HTMLEditor.cpp @@ -3784,40 +3784,32 @@ nsresult HTMLEditor::RemoveBlockContaine // Now remove container nsresult rv = RemoveContainerWithTransaction(aElement); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } return NS_OK; } -/** - * GetPriorHTMLSibling() returns the previous editable sibling, if there is - * one within the parent. - */ -nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode) { +nsIContent* HTMLEditor::GetPriorHTMLSibling(nsINode* aNode, SkipWhitespace aSkipWS) { MOZ_ASSERT(aNode); nsIContent* node = aNode->GetPreviousSibling(); - while (node && !IsEditable(node)) { + while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) { node = node->GetPreviousSibling(); } return node; } -/** - * GetNextHTMLSibling() returns the next editable sibling, if there is - * one within the parent. - */ -nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode) { +nsIContent* HTMLEditor::GetNextHTMLSibling(nsINode* aNode, SkipWhitespace aSkipWS) { MOZ_ASSERT(aNode); nsIContent* node = aNode->GetNextSibling(); - while (node && !IsEditable(node)) { + while (node && (!IsEditable(node) || SkippableWhitespace(node, aSkipWS))) { node = node->GetNextSibling(); } return node; } nsIContent* HTMLEditor::GetPreviousHTMLElementOrTextInternal( nsINode& aNode, bool aNoBlockCrossing) {
--- a/editor/libeditor/HTMLEditor.h +++ b/editor/libeditor/HTMLEditor.h @@ -1010,19 +1010,36 @@ class HTMLEditor final : public TextEdit * with topmost split element. If this didn't * find inline elements to be split, Handled() * returns false. */ MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE SplitNodeResult SplitAncestorStyledInlineElementsAt(const EditorDOMPoint& aPointToSplit, nsAtom* aProperty, nsAtom* aAttribute); - nsIContent* GetPriorHTMLSibling(nsINode* aNode); - - nsIContent* GetNextHTMLSibling(nsINode* aNode); + /** + * GetPriorHTMLSibling() returns the previous editable sibling, if there is + * one within the parent, optionally skipping text nodes that are only + * whitespace. + */ + enum class SkipWhitespace { Yes, No }; + nsIContent* GetPriorHTMLSibling(nsINode* aNode, SkipWhitespace = SkipWhitespace::No); + + /** + * GetNextHTMLSibling() returns the next editable sibling, if there is + * one within the parent, optionally skipping text nodes that are only + * whitespace. + */ + nsIContent* GetNextHTMLSibling(nsINode* aNode, SkipWhitespace = SkipWhitespace::No); + + // Helper for GetPriorHTMLSibling/GetNextHTMLSibling. + static bool SkippableWhitespace(nsINode* aNode, SkipWhitespace aSkipWS) { + return aSkipWS == SkipWhitespace::Yes && + aNode->IsText() && aNode->AsText()->TextIsOnlyWhitespace(); + } /** * GetPreviousHTMLElementOrText*() methods are similar to * EditorBase::GetPreviousElementOrText*() but this won't return nodes * outside active editing host. */ nsIContent* GetPreviousHTMLElementOrText(nsINode& aNode) { return GetPreviousHTMLElementOrTextInternal(aNode, false); @@ -4306,16 +4323,22 @@ class HTMLEditor final : public TextEdit already_AddRefed<nsRange> GetChangedRangeForTopLevelEditSubAction() const { if (!mChangedRangeForTopLevelEditSubAction) { mChangedRangeForTopLevelEditSubAction = new nsRange(GetDocument()); } return do_AddRef(mChangedRangeForTopLevelEditSubAction); } protected: + // Helper for Handle[CSS|HTML]IndentAtSelectionInternal + MOZ_CAN_RUN_SCRIPT MOZ_MUST_USE nsresult + IndentListChild(RefPtr<Element>* aCurList, + const EditorDOMPoint& aCurPoint, + OwningNonNull<nsINode>& aCurNode); + RefPtr<TypeInState> mTypeInState; RefPtr<ComposerCommandsUpdater> mComposerCommandsUpdater; // Used by TopLevelEditSubActionData::mSelectedRange. mutable RefPtr<RangeItem> mSelectedRangeForTopLevelEditSubAction; // Used by TopLevelEditSubActionData::mChangedRange. mutable RefPtr<nsRange> mChangedRangeForTopLevelEditSubAction;
new file mode 100644 --- /dev/null +++ b/editor/reftests/exec-command-indent-ws-ref.html @@ -0,0 +1,61 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>Reference for bug </title> +<style> +html,body { + color:black; background-color:white; font:10px/1 monospace; padding:0; margin:0; +} + +li::before { content: " list-item counter:" counters(list-item,".") " "; } +ol,ul { border:1px solid; margin: 0; } +div > ul { counter-reset: list-item 7; } +</style> +</head> +<body> + +<div contenteditable> +<ol start=8> + <li>A</li> + <ol><li class="indent">B</li></ol> + <li>C</li> +</ol> +</div> + +<div contenteditable> +<ol start=8> + <li>A</li> + <ol><li class="indent">B</li></ol> + <li>C</li> +</ol> +</div> + +<div contenteditable> +<ul> + <li>A</li> + <ul><li class="indent">B</li></ul> + <li>C</li> +</ul> +</div> + +<div contenteditable> +<ul> + <li>A</li> + <ul><li class="indent">B</li></ul> + <li>C</li> +</ul> +</div> + +<!-- now the same as above without whitespace: --> + +<div contenteditable><ol start=8><li>A</li><ol><li class="indent">B</li></ol><li>C</li></ol></div> +<div contenteditable><ol start=8><li>A</li><ol><li class="indent">B</li></ol><li>C</li></ol></div> +<div contenteditable><ul><li>A</li><ul><li class="indent">B</li></ul><li>C</li></ul></div> +<div contenteditable><ul><li>A</li><ul><li class="indent">B</li></ul><li>C</li></ul></div> + +</body> +</html>
new file mode 100644 --- /dev/null +++ b/editor/reftests/exec-command-indent-ws.html @@ -0,0 +1,81 @@ +<!DOCTYPE HTML> +<!-- + Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ +--> +<html><head> + <meta charset="utf-8"> + <title>Testcase for bug </title> +<style> +html,body { + color:black; background-color:white; font:10px/1 monospace; padding:0; margin:0; +} + +li::before { content: " list-item counter:" counters(list-item,".") " "; } +ol,ul { border:1px solid; margin: 0; } +div > ul { counter-reset: list-item 7; } +</style> +</head> +<body> + +<div contenteditable> +<ol start=8> + <li>A</li> + <ol></ol> + <li class="indent">B</li> + <li>C</li> +</ol> +</div> + +<div contenteditable> +<ol start=8> + <li>A</li> + <li class="indent">B</li> + <ol></ol> + <li>C</li> +</ol> +</div> + +<div contenteditable> +<ul> + <li>A</li> + <ul></ul> + <li class="indent">B</li> + <li>C</li> +</ul> +</div> + +<div contenteditable> +<ul> + <li>A</li> + <li class="indent">B</li> + <ul></ul> + <li>C</li> +</ul> +</div> + +<!-- now the same as above without whitespace: --> + +<div contenteditable><ol start=8><li>A</li><ol></ol><li class="indent">B</li><li>C</li></ol></div> +<div contenteditable><ol start=8><li>A</li><li class="indent">B</li><ol></ol><li>C</li></ol></div> +<div contenteditable><ul><li>A</li><ul></ul><li class="indent">B</li><li>C</li></ul></div> +<div contenteditable><ul><li>A</li><li class="indent">B</li><ul></ul><li>C</li></ul></div> + +<script> +function test() { + [...document.querySelectorAll('.indent')].forEach(function(elm) { + var r = document.createRange(); + r.setStart(elm.firstChild,0) + r.setEnd(elm.firstChild,0) + window.getSelection().addRange(r); + document.execCommand("indent"); + window.getSelection().removeAllRanges(); + }); +} + +test(); +document.activeElement.blur(); +</script> + +</body> +</html>
--- a/editor/reftests/reftest.list +++ b/editor/reftests/reftest.list @@ -145,8 +145,9 @@ needs-focus == spellcheck-contenteditabl == 911201.html 911201-ref.html needs-focus == 969773.html 969773-ref.html fuzzy-if(skiaContent,0-1,0-220) == 997805.html 997805-ref.html fuzzy-if(skiaContent,0-1,0-220) skip-if(verify&&OSX) == 1088158.html 1088158-ref.html fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-1.html 1443902-1-ref.html fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-2.html 1443902-2-ref.html fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-3.html 1443902-3-ref.html fuzzy-if(Android,0-1,0-1) needs-focus == 1443902-4.html 1443902-4-ref.html +== exec-command-indent-ws.html exec-command-indent-ws-ref.html
--- a/ipc/glue/IdleSchedulerChild.cpp +++ b/ipc/glue/IdleSchedulerChild.cpp @@ -2,54 +2,54 @@ /* 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/ipc/IdleSchedulerChild.h" #include "mozilla/ipc/IdleSchedulerParent.h" #include "mozilla/Atomics.h" -#include "mozilla/PrioritizedEventQueue.h" +#include "mozilla/IdlePeriodState.h" #include "BackgroundChild.h" namespace mozilla { namespace ipc { static IdleSchedulerChild* sMainThreadIdleScheduler = nullptr; IdleSchedulerChild::~IdleSchedulerChild() { if (sMainThreadIdleScheduler == this) { sMainThreadIdleScheduler = nullptr; } - MOZ_ASSERT(!mEventQueue); + MOZ_ASSERT(!mIdlePeriodState); } -void IdleSchedulerChild::Init(PrioritizedEventQueue* aEventQueue) { - mEventQueue = aEventQueue; +void IdleSchedulerChild::Init(IdlePeriodState* aIdlePeriodState) { + mIdlePeriodState = aIdlePeriodState; RefPtr<IdleSchedulerChild> scheduler = this; auto resolve = [&](Tuple<mozilla::Maybe<SharedMemoryHandle>, uint32_t>&& aResult) { if (Get<0>(aResult)) { mActiveCounter.SetHandle(*Get<0>(aResult), false); mActiveCounter.Map(sizeof(int32_t)); mChildId = Get<1>(aResult); - if (mChildId && mEventQueue && mEventQueue->IsActive()) { + if (mChildId && mIdlePeriodState && mIdlePeriodState->IsActive()) { SetActive(); } } }; auto reject = [&](ResponseRejectReason) {}; SendInitForIdleUse(std::move(resolve), std::move(reject)); } IPCResult IdleSchedulerChild::RecvIdleTime(uint64_t aId, TimeDuration aBudget) { - if (mEventQueue) { - mEventQueue->SetIdleToken(aId, aBudget); + if (mIdlePeriodState) { + mIdlePeriodState->SetIdleToken(aId, aBudget); } return IPC_OK(); } void IdleSchedulerChild::SetActive() { if (mChildId && CanSend() && mActiveCounter.memory()) { ++(static_cast<Atomic<int32_t>*>( mActiveCounter.memory())[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]);
--- a/ipc/glue/IdleSchedulerChild.h +++ b/ipc/glue/IdleSchedulerChild.h @@ -9,50 +9,50 @@ #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/ipc/PIdleSchedulerChild.h" class nsIIdlePeriod; namespace mozilla { -class PrioritizedEventQueue; +class IdlePeriodState; namespace ipc { class BackgroundChildImpl; class IdleSchedulerChild final : public PIdleSchedulerChild { public: IdleSchedulerChild() = default; NS_INLINE_DECL_REFCOUNTING(IdleSchedulerChild) IPCResult RecvIdleTime(uint64_t aId, TimeDuration aBudget); - void Init(PrioritizedEventQueue* aEventQueue); + void Init(IdlePeriodState* aIdlePeriodState); - void Disconnect() { mEventQueue = nullptr; } + void Disconnect() { mIdlePeriodState = nullptr; } // See similar methods on PrioritizedEventQueue. void SetActive(); // Returns true if activity state dropped below cpu count. bool SetPaused(); static IdleSchedulerChild* GetMainThreadIdleScheduler(); private: ~IdleSchedulerChild(); friend class BackgroundChildImpl; // See IdleScheduleParent::sActiveChildCounter base::SharedMemory mActiveCounter; - PrioritizedEventQueue* mEventQueue = nullptr; + IdlePeriodState* mIdlePeriodState = nullptr; uint32_t mChildId = 0; }; } // namespace ipc } // namespace mozilla #endif // mozilla_ipc_IdleSchedulerChild_h__
--- a/js/public/Class.h +++ b/js/public/Class.h @@ -741,17 +741,17 @@ static const uint32_t JSCLASS_FOREGROUND // with the following flags. Failure to use JSCLASS_GLOBAL_FLAGS was // previously allowed, but is now an ES5 violation and thus unsupported. // // JSCLASS_GLOBAL_APPLICATION_SLOTS is the number of slots reserved at // the beginning of every global object's slots for use by the // application. static const uint32_t JSCLASS_GLOBAL_APPLICATION_SLOTS = 5; static const uint32_t JSCLASS_GLOBAL_SLOT_COUNT = - JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 26; + JSCLASS_GLOBAL_APPLICATION_SLOTS + JSProto_LIMIT * 2 + 25; static constexpr uint32_t JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(uint32_t n) { return JSCLASS_IS_GLOBAL | JSCLASS_HAS_RESERVED_SLOTS(JSCLASS_GLOBAL_SLOT_COUNT + n); } static constexpr uint32_t JSCLASS_GLOBAL_FLAGS = JSCLASS_GLOBAL_FLAGS_WITH_SLOTS(0);
--- a/js/src/debugger/DebugAPI-inl.h +++ b/js/src/debugger/DebugAPI-inl.h @@ -53,32 +53,32 @@ void DebugAPI::onNewGlobalObject(JSConte if (!cx->runtime()->onNewGlobalObjectWatchers().isEmpty()) { slowPathOnNewGlobalObject(cx, global); } } /* static */ void DebugAPI::notifyParticipatesInGC(GlobalObject* global, uint64_t majorGCNumber) { - GlobalObject::DebuggerVector* dbgs = global->getDebuggers(); - if (dbgs && !dbgs->empty()) { - slowPathNotifyParticipatesInGC(majorGCNumber, *dbgs); + Realm::DebuggerVector& dbgs = global->getDebuggers(); + if (!dbgs.empty()) { + slowPathNotifyParticipatesInGC(majorGCNumber, dbgs); } } /* static */ bool DebugAPI::onLogAllocationSite(JSContext* cx, JSObject* obj, HandleSavedFrame frame, mozilla::TimeStamp when) { - GlobalObject::DebuggerVector* dbgs = cx->global()->getDebuggers(); - if (!dbgs || dbgs->empty()) { + Realm::DebuggerVector& dbgs = cx->global()->getDebuggers(); + if (dbgs.empty()) { return true; } RootedObject hobj(cx, obj); - return slowPathOnLogAllocationSite(cx, hobj, frame, when, *dbgs); + return slowPathOnLogAllocationSite(cx, hobj, frame, when, dbgs); } /* static */ bool DebugAPI::onLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool ok) { MOZ_ASSERT_IF(frame.isInterpreterFrame(), frame.asInterpreterFrame() == cx->interpreterFrame()); MOZ_ASSERT_IF(frame.hasScript() && frame.script()->isDebuggee(),
--- a/js/src/debugger/DebugAPI.h +++ b/js/src/debugger/DebugAPI.h @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef debugger_DebugAPI_h #define debugger_DebugAPI_h #include "vm/GlobalObject.h" #include "vm/Interpreter.h" #include "vm/JSContext.h" +#include "vm/Realm.h" namespace js { // This file contains the API which SpiderMonkey should use to interact with any // active Debuggers. class AbstractGeneratorObject; class PromiseObject; @@ -332,43 +333,36 @@ class DebugAPI { HandleSavedFrame frame, mozilla::TimeStamp when); // Announce to the debugger that a global object is being collected by the // specified major GC. static inline void notifyParticipatesInGC(GlobalObject* global, uint64_t majorGCNumber); - // Allocate an object which holds a GlobalObject::DebuggerVector. - static JSObject* newGlobalDebuggersHolder(JSContext* cx); - - // Get the GlobalObject::DebuggerVector for an object allocated by - // newGlobalDebuggersObject. - static GlobalObject::DebuggerVector* getGlobalDebuggers(JSObject* holder); - /* * Get any instrumentation ID which has been associated with a script using * the specified debugger object. */ static bool getScriptInstrumentationId(JSContext* cx, HandleObject dbgObject, HandleScript script, MutableHandleValue rval); private: static bool stepModeEnabledSlow(JSScript* script); static bool hasBreakpointsAtSlow(JSScript* script, jsbytecode* pc); static void sweepBreakpointsSlow(JSFreeOp* fop, JSScript* script); static void slowPathOnNewScript(JSContext* cx, HandleScript script); static void slowPathOnNewGlobalObject(JSContext* cx, Handle<GlobalObject*> global); - static void slowPathNotifyParticipatesInGC( - uint64_t majorGCNumber, GlobalObject::DebuggerVector& dbgs); + static void slowPathNotifyParticipatesInGC(uint64_t majorGCNumber, + JS::Realm::DebuggerVector& dbgs); static MOZ_MUST_USE bool slowPathOnLogAllocationSite( JSContext* cx, HandleObject obj, HandleSavedFrame frame, - mozilla::TimeStamp when, GlobalObject::DebuggerVector& dbgs); + mozilla::TimeStamp when, JS::Realm::DebuggerVector& dbgs); static MOZ_MUST_USE bool slowPathOnLeaveFrame(JSContext* cx, AbstractFramePtr frame, jsbytecode* pc, bool ok); static MOZ_MUST_USE bool slowPathOnNewGenerator( JSContext* cx, AbstractFramePtr frame, Handle<AbstractGeneratorObject*> genObj); static MOZ_MUST_USE bool slowPathCheckNoExecute(JSContext* cx, HandleScript script);
--- a/js/src/debugger/Debugger.cpp +++ b/js/src/debugger/Debugger.cpp @@ -513,63 +513,16 @@ bool Debugger::hasMemory() const { DebuggerMemory& Debugger::memory() const { MOZ_ASSERT(hasMemory()); return object->getReservedSlot(JSSLOT_DEBUG_MEMORY_INSTANCE) .toObject() .as<DebuggerMemory>(); } -/*** DebuggerVectorHolder *****************************************************/ - -static void GlobalDebuggerVectorHolder_finalize(JSFreeOp* fop, JSObject* obj) { - MOZ_ASSERT(fop->maybeOnHelperThread()); - void* ptr = obj->as<NativeObject>().getPrivate(); - auto debuggers = static_cast<GlobalObject::DebuggerVector*>(ptr); - fop->delete_(obj, debuggers, MemoryUse::GlobalDebuggerVector); -} - -static const JSClassOps GlobalDebuggerVectorHolder_classOps = { - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - nullptr, - GlobalDebuggerVectorHolder_finalize}; - -static const JSClass GlobalDebuggerVectorHolder_class = { - "GlobalDebuggerVectorHolder", - JSCLASS_HAS_PRIVATE | JSCLASS_BACKGROUND_FINALIZE, - &GlobalDebuggerVectorHolder_classOps}; - -/* static */ -JSObject* DebugAPI::newGlobalDebuggersHolder(JSContext* cx) { - NativeObject* obj = NewNativeObjectWithGivenProto( - cx, &GlobalDebuggerVectorHolder_class, nullptr); - if (!obj) { - return nullptr; - } - - GlobalObject::DebuggerVector* debuggers = - cx->new_<GlobalObject::DebuggerVector>(cx->zone()); - if (!debuggers) { - return nullptr; - } - - InitObjectPrivate(obj, debuggers, MemoryUse::GlobalDebuggerVector); - return obj; -} - -/* static */ -GlobalObject::DebuggerVector* DebugAPI::getGlobalDebuggers(JSObject* holder) { - MOZ_ASSERT(holder->getClass() == &GlobalDebuggerVectorHolder_class); - return (GlobalObject::DebuggerVector*)holder->as<NativeObject>().getPrivate(); -} - /*** Debugger accessors *******************************************************/ bool Debugger::getFrame(JSContext* cx, const FrameIter& iter, MutableHandleValue vp) { RootedDebuggerFrame result(cx); if (!Debugger::getFrame(cx, iter, &result)) { return false; } @@ -660,23 +613,22 @@ bool Debugger::getFrame(JSContext* cx, c } static bool DebuggerExists( GlobalObject* global, const std::function<bool(Debugger* dbg)>& predicate) { // The GC analysis can't determine that the predicate can't GC, so let it know // explicitly. JS::AutoSuppressGCAnalysis nogc; - if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) { - for (auto p = debuggers->begin(); p != debuggers->end(); p++) { - // Callbacks should not create new references to the debugger, so don't - // use a barrier. This allows this method to be called during GC. - if (predicate(p->unbarrieredGet())) { - return true; - } + Realm::DebuggerVector& debuggers = global->getDebuggers(); + for (auto p = debuggers.begin(); p != debuggers.end(); p++) { + // Callbacks should not create new references to the debugger, so don't + // use a barrier. This allows this method to be called during GC. + if (predicate(p->unbarrieredGet())) { + return true; } } return false; } /* static */ bool Debugger::hasLiveHook(GlobalObject* global, Hook which) { return DebuggerExists(global, @@ -810,36 +762,34 @@ ResumeMode DebugAPI::slowPathOnResumeFra Rooted<AbstractGeneratorObject*> genObj( cx, GetGeneratorObjectForFrame(cx, frame)); MOZ_ASSERT(genObj); // For each debugger, if there is an existing Debugger.Frame object for the // resumed `frame`, update it with the new frame pointer and make sure the // frame is observable. - if (GlobalObject::DebuggerVector* debuggers = - frame.global()->getDebuggers()) { - for (Debugger* dbg : *debuggers) { - if (Debugger::GeneratorWeakMap::Ptr entry = - dbg->generatorFrames.lookup(genObj)) { - DebuggerFrame* frameObj = &entry->value()->as<DebuggerFrame>(); - MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj); - if (!dbg->frames.putNew(frame, frameObj)) { - ReportOutOfMemory(cx); - return ResumeMode::Throw; - } - - FrameIter iter(cx); - MOZ_ASSERT(iter.abstractFramePtr() == frame); - if (!frameObj->resume(iter)) { - return ResumeMode::Throw; - } - if (!Debugger::ensureExecutionObservabilityOfFrame(cx, frame)) { - return ResumeMode::Throw; - } + Realm::DebuggerVector& debuggers = frame.global()->getDebuggers(); + for (Debugger* dbg : debuggers) { + if (Debugger::GeneratorWeakMap::Ptr entry = + dbg->generatorFrames.lookup(genObj)) { + DebuggerFrame* frameObj = &entry->value()->as<DebuggerFrame>(); + MOZ_ASSERT(&frameObj->unwrappedGenerator() == genObj); + if (!dbg->frames.putNew(frame, frameObj)) { + ReportOutOfMemory(cx); + return ResumeMode::Throw; + } + + FrameIter iter(cx); + MOZ_ASSERT(iter.abstractFramePtr() == frame); + if (!frameObj->resume(iter)) { + return ResumeMode::Throw; + } + if (!Debugger::ensureExecutionObservabilityOfFrame(cx, frame)) { + return ResumeMode::Throw; } } } return slowPathOnEnterFrame(cx, frame); } /* static */ @@ -2287,23 +2237,22 @@ ResumeMode Debugger::dispatchHook(JSCont // Determine which debuggers will receive this event, and in what order. // Make a copy of the list, since the original is mutable and we will be // calling into arbitrary JS. // // Note: In the general case, 'triggered' contains references to objects in // different compartments--every compartment *except* this one. RootedValueVector triggered(cx); Handle<GlobalObject*> global = cx->global(); - if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) { - for (auto p = debuggers->begin(); p != debuggers->end(); p++) { - Debugger* dbg = *p; - if (hookIsEnabled(dbg)) { - if (!triggered.append(ObjectValue(*dbg->toJSObject()))) { - return ResumeMode::Terminate; - } + Realm::DebuggerVector& debuggers = global->getDebuggers(); + for (auto p = debuggers.begin(); p != debuggers.end(); p++) { + Debugger* dbg = *p; + if (hookIsEnabled(dbg)) { + if (!triggered.append(ObjectValue(*dbg->toJSObject()))) { + return ResumeMode::Terminate; } } } // Preserve the debuggee's microtask event queue while we run the hooks, so // the debugger's microtask checkpoints don't run from the debuggee's // microtasks, and vice versa. JS::AutoDebuggerJobQueueInterruption adjqi; @@ -2559,56 +2508,55 @@ ResumeMode DebugAPI::onSingleStep(JSCont // // The converse --- ensuring that we do receive traps when we should --- can // be done with unit tests. if (iter.hasScript()) { uint32_t liveStepperCount = 0; uint32_t suspendedStepperCount = 0; JSScript* trappingScript = iter.script(); GlobalObject* global = cx->global(); - if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) { - for (auto p = debuggers->begin(); p != debuggers->end(); p++) { - Debugger* dbg = *p; - for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty(); - r.popFront()) { - AbstractFramePtr frame = r.front().key(); - NativeObject* frameobj = r.front().value(); - if (frame.isWasmDebugFrame()) { - continue; - } - if (frame.script() == trappingScript && - !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT) - .isUndefined()) { - liveStepperCount++; - } + Realm::DebuggerVector& debuggers = global->getDebuggers(); + for (auto p = debuggers.begin(); p != debuggers.end(); p++) { + Debugger* dbg = *p; + for (Debugger::FrameMap::Range r = dbg->frames.all(); !r.empty(); + r.popFront()) { + AbstractFramePtr frame = r.front().key(); + NativeObject* frameobj = r.front().value(); + if (frame.isWasmDebugFrame()) { + continue; + } + if (frame.script() == trappingScript && + !frameobj->getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT) + .isUndefined()) { + liveStepperCount++; } - - // Also count hooks set on suspended generator frames. - for (Debugger::GeneratorWeakMap::Range r = dbg->generatorFrames.all(); - !r.empty(); r.popFront()) { - AbstractGeneratorObject& genObj = - r.front().key()->as<AbstractGeneratorObject>(); - DebuggerFrame& frameObj = r.front().value()->as<DebuggerFrame>(); - MOZ_ASSERT(&frameObj.unwrappedGenerator() == &genObj); - - // Live Debugger.Frames were already counted in dbg->frames loop. - if (frameObj.isLive()) { - continue; - } - - // If a frame isn't live, but it has an entry in generatorFrames, - // it had better be suspended. - MOZ_ASSERT(genObj.isSuspended()); - - if (!genObj.callee().isInterpretedLazy() && - genObj.callee().nonLazyScript() == trappingScript && - !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT) - .isUndefined()) { - suspendedStepperCount++; - } + } + + // Also count hooks set on suspended generator frames. + for (Debugger::GeneratorWeakMap::Range r = dbg->generatorFrames.all(); + !r.empty(); r.popFront()) { + AbstractGeneratorObject& genObj = + r.front().key()->as<AbstractGeneratorObject>(); + DebuggerFrame& frameObj = r.front().value()->as<DebuggerFrame>(); + MOZ_ASSERT(&frameObj.unwrappedGenerator() == &genObj); + + // Live Debugger.Frames were already counted in dbg->frames loop. + if (frameObj.isLive()) { + continue; + } + + // If a frame isn't live, but it has an entry in generatorFrames, + // it had better be suspended. + MOZ_ASSERT(genObj.isSuspended()); + + if (!genObj.callee().isInterpretedLazy() && + genObj.callee().nonLazyScript() == trappingScript && + !frameObj.getReservedSlot(DebuggerFrame::ONSTEP_HANDLER_SLOT) + .isUndefined()) { + suspendedStepperCount++; } } } MOZ_ASSERT(liveStepperCount + suspendedStepperCount == DebugScript::getStepperCount(trappingScript)); } #endif @@ -2747,47 +2695,46 @@ void DebugAPI::slowPathOnNewGlobalObject break; } } } MOZ_ASSERT(!cx->isExceptionPending()); } /* static */ -void DebugAPI::slowPathNotifyParticipatesInGC( - uint64_t majorGCNumber, GlobalObject::DebuggerVector& dbgs) { - for (GlobalObject::DebuggerVector::Range r = dbgs.all(); !r.empty(); - r.popFront()) { +void DebugAPI::slowPathNotifyParticipatesInGC(uint64_t majorGCNumber, + Realm::DebuggerVector& dbgs) { + for (Realm::DebuggerVector::Range r = dbgs.all(); !r.empty(); r.popFront()) { if (!r.front().unbarrieredGet()->debuggeeIsBeingCollected(majorGCNumber)) { #ifdef DEBUG fprintf(stderr, "OOM while notifying observing Debuggers of a GC: The " "onGarbageCollection\n" "hook will not be fired for this GC for some Debuggers!\n"); #endif return; } } } /* static */ Maybe<double> DebugAPI::allocationSamplingProbability(GlobalObject* global) { - GlobalObject::DebuggerVector* dbgs = global->getDebuggers(); - if (!dbgs || dbgs->empty()) { + Realm::DebuggerVector& dbgs = global->getDebuggers(); + if (dbgs.empty()) { return Nothing(); } - DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs->begin(); + DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs.begin(); double probability = 0; bool foundAnyDebuggers = false; - for (auto p = dbgs->begin(); p < dbgs->end(); p++) { + for (auto p = dbgs.begin(); p < dbgs.end(); p++) { // The set of debuggers had better not change while we're iterating, // such that the vector gets reallocated. - MOZ_ASSERT(dbgs->begin() == begin); + MOZ_ASSERT(dbgs.begin() == begin); // Use unbarrieredGet() to prevent triggering read barrier while collecting, // this is safe as long as dbgp does not escape. Debugger* dbgp = p->unbarrieredGet(); if (dbgp->trackingAllocationSites) { foundAnyDebuggers = true; probability = std::max(dbgp->allocationSamplingProbability, probability); } @@ -2795,17 +2742,17 @@ Maybe<double> DebugAPI::allocationSampli return foundAnyDebuggers ? Some(probability) : Nothing(); } /* static */ bool DebugAPI::slowPathOnLogAllocationSite(JSContext* cx, HandleObject obj, HandleSavedFrame frame, mozilla::TimeStamp when, - GlobalObject::DebuggerVector& dbgs) { + Realm::DebuggerVector& dbgs) { MOZ_ASSERT(!dbgs.empty()); mozilla::DebugOnly<WeakHeapPtr<Debugger*>*> begin = dbgs.begin(); // Root all the Debuggers while we're iterating over them; // appendAllocationSite calls Compartment::wrap, and thus can GC. // // SpiderMonkey protocol is generally for the caller to prove that it has // rooted the stuff it's asking you to operate on (i.e. by passing a @@ -3272,22 +3219,21 @@ bool Debugger::updateExecutionObservabil return true; } template <typename FrameFn> /* static */ void Debugger::forEachDebuggerFrame(AbstractFramePtr frame, FrameFn fn) { GlobalObject* global = frame.global(); - if (GlobalObject::DebuggerVector* debuggers = global->getDebuggers()) { - for (auto p = debuggers->begin(); p != debuggers->end(); p++) { - Debugger* dbg = *p; - if (FrameMap::Ptr entry = dbg->frames.lookup(frame)) { - fn(entry->value()); - } + Realm::DebuggerVector& debuggers = global->getDebuggers(); + for (auto p = debuggers.begin(); p != debuggers.end(); p++) { + Debugger* dbg = *p; + if (FrameMap::Ptr entry = dbg->frames.lookup(frame)) { + fn(entry->value()); } } } /* static */ bool Debugger::getDebuggerFrames(AbstractFramePtr frame, MutableHandle<DebuggerFrameVector> frames) { bool hadOOM = false; @@ -3496,24 +3442,23 @@ void Debugger::updateObservesAsmJSOnDebu bool Debugger::cannotTrackAllocations(const GlobalObject& global) { auto existingCallback = global.realm()->getAllocationMetadataBuilder(); return existingCallback && existingCallback != &SavedStacks::metadataBuilder; } /* static */ bool DebugAPI::isObservedByDebuggerTrackingAllocations( const GlobalObject& debuggee) { - if (auto* v = debuggee.getDebuggers()) { - for (auto p = v->begin(); p != v->end(); p++) { - // Use unbarrieredGet() to prevent triggering read barrier while - // collecting, this is safe as long as dbg does not escape. - Debugger* dbg = p->unbarrieredGet(); - if (dbg->trackingAllocationSites) { - return true; - } + auto& v = debuggee.getDebuggers(); + for (auto p = v.begin(); p != v.end(); p++) { + // Use unbarrieredGet() to prevent triggering read barrier while + // collecting, this is safe as long as dbg does not escape. + Debugger* dbg = p->unbarrieredGet(); + if (dbg->trackingAllocationSites) { + return true; } } return false; } /* static */ bool Debugger::addAllocationsTracking(JSContext* cx, @@ -3740,21 +3685,18 @@ bool DebugAPI::markIteratively(GCMarker* JSRuntime* rt = marker->runtime(); for (RealmsIter r(rt); !r.done(); r.next()) { if (r->isDebuggee()) { GlobalObject* global = r->unsafeUnbarrieredMaybeGlobal(); if (!IsMarkedUnbarriered(rt, &global)) { continue; } - // Every debuggee has at least one debugger, so in this case - // getDebuggers can't return nullptr. - const GlobalObject::DebuggerVector* debuggers = global->getDebuggers(); - MOZ_ASSERT(debuggers); - for (auto p = debuggers->begin(); p != debuggers->end(); p++) { + const Realm::DebuggerVector& debuggers = global->getDebuggers(); + for (auto p = debuggers.begin(); p != debuggers.end(); p++) { Debugger* dbg = p->unbarrieredGet(); // dbg is a Debugger with at least one debuggee. Check three things: // - dbg is actually in a compartment that is being marked // - it isn't already marked // - it actually has hooks that might be called GCPtrNativeObject& dbgobj = dbg->toJSObjectRef(); if (!dbgobj->zone()->isGCMarking()) { @@ -3915,21 +3857,21 @@ void DebugAPI::sweepAll(JSFreeOp* fop) { dbg = next; } } /* static */ void Debugger::detachAllDebuggersFromGlobal(JSFreeOp* fop, GlobalObject* global) { - const GlobalObject::DebuggerVector* debuggers = global->getDebuggers(); - MOZ_ASSERT(!debuggers->empty()); - while (!debuggers->empty()) { - debuggers->back()->removeDebuggeeGlobal(fop, global, nullptr, - Debugger::FromSweep::No); + const Realm::DebuggerVector& debuggers = global->getDebuggers(); + MOZ_ASSERT(!debuggers.empty()); + while (!debuggers.empty()) { + debuggers.back()->removeDebuggeeGlobal(fop, global, nullptr, + Debugger::FromSweep::No); } } static inline bool SweepZonesInSameGroup(Zone* a, Zone* b) { // Ensure two zones are swept in the same sweep group by adding an edge // between them in each direction. return a->addSweepGroupEdgeTo(b) && b->addSweepGroupEdgeTo(a); } @@ -4393,17 +4335,17 @@ bool Debugger::CallData::removeDebuggee( if (dbg->debuggees.has(global)) { dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, nullptr, FromSweep::No); // Only update the realm if there are no Debuggers left, as it's // expensive to check if no other Debugger has a live script or frame // hook on any of the current on-stack debuggee frames. - if (global->getDebuggers()->empty() && !obs.add(global->realm())) { + if (global->getDebuggers().empty() && !obs.add(global->realm())) { return false; } if (!updateExecutionObservability(cx, obs, NotObserving)) { return false; } } args.rval().setUndefined(); @@ -4414,17 +4356,17 @@ bool Debugger::CallData::removeAllDebugg ExecutionObservableRealms obs(cx); for (WeakGlobalObjectSet::Enum e(dbg->debuggees); !e.empty(); e.popFront()) { Rooted<GlobalObject*> global(cx, e.front()); dbg->removeDebuggeeGlobal(cx->runtime()->defaultFreeOp(), global, &e, FromSweep::No); // See note about adding to the observable set in removeDebuggee. - if (global->getDebuggers()->empty() && !obs.add(global->realm())) { + if (global->getDebuggers().empty() && !obs.add(global->realm())) { return false; } } if (!updateExecutionObservability(cx, obs, NotObserving)) { return false; } @@ -4612,18 +4554,18 @@ bool Debugger::addDebuggeeGlobal(JSConte if (realm == debuggeeRealm) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_DEBUG_LOOP); return false; } // Find all realms containing debuggers debugging realm's global object. // Add those realms to visited. if (realm->isDebuggee()) { - GlobalObject::DebuggerVector* v = realm->maybeGlobal()->getDebuggers(); - for (auto p = v->begin(); p != v->end(); p++) { + Realm::DebuggerVector& v = realm->getDebuggers(); + for (auto p = v.begin(); p != v.end(); p++) { Realm* next = (*p)->object->realm(); if (Find(visited, next) == visited.end() && !visited.append(next)) { return false; } } } } @@ -4637,26 +4579,22 @@ bool Debugger::addDebuggeeGlobal(JSConte // 5. Realm::isDebuggee()'s bit must be set. // // All five indications must be kept consistent. AutoRealm ar(cx, global); Zone* zone = global->zone(); // (1) - auto* globalDebuggers = GlobalObject::getOrCreateDebuggers(cx, global); - if (!globalDebuggers) { - return false; - } - if (!globalDebuggers->append(this)) { + auto& globalDebuggers = global->getDebuggers(); + if (!globalDebuggers.append(this)) { ReportOutOfMemory(cx); return false; } - auto globalDebuggersGuard = - MakeScopeExit([&] { globalDebuggers->popBack(); }); + auto globalDebuggersGuard = MakeScopeExit([&] { globalDebuggers.popBack(); }); // (2) if (!debuggees.put(global)) { ReportOutOfMemory(cx); return false; } auto debuggeesGuard = MakeScopeExit([&] { debuggees.remove(global); }); @@ -4724,24 +4662,24 @@ static T* findDebuggerInVector(Debugger* MOZ_ASSERT(p != vec->end()); return p; } // a WeakHeapPtr version for findDebuggerInVector // TODO: Bug 1515934 - findDebuggerInVector<T> triggers read barriers. template <typename AP> static WeakHeapPtr<Debugger*>* findDebuggerInVector( - Debugger* dbg, Vector<WeakHeapPtr<Debugger*>, 0, AP>* vec) { + Debugger* dbg, Vector<WeakHeapPtr<Debugger*>, 0, AP>& vec) { WeakHeapPtr<Debugger*>* p; - for (p = vec->begin(); p != vec->end(); p++) { + for (p = vec.begin(); p != vec.end(); p++) { if (p->unbarrieredGet() == dbg) { break; } } - MOZ_ASSERT(p != vec->end()); + MOZ_ASSERT(p != vec.end()); return p; } void Debugger::removeDebuggeeGlobal(JSFreeOp* fop, GlobalObject* global, WeakGlobalObjectSet::Enum* debugEnum, FromSweep fromSweep) { // The caller might have found global by enumerating this->debuggees; if // so, use HashSet::Enum::removeFront rather than HashSet::remove below, @@ -4784,28 +4722,28 @@ void Debugger::removeDebuggeeGlobal(JSFr auto& genObj = e.front().key()->as<AbstractGeneratorObject>(); auto& frameObj = e.front().value()->as<DebuggerFrame>(); if (genObj.isClosed() || &genObj.callee().global() == global) { frameObj.clearGenerator(fop, this, &e); } } } - auto* globalDebuggersVector = global->getDebuggers(); + auto& globalDebuggersVector = global->getDebuggers(); // The relation must be removed from up to three places: // globalDebuggersVector and debuggees for sure, and possibly the // compartment's debuggee set. // // The debuggee zone set is recomputed on demand. This avoids refcounting // and in practice we have relatively few debuggees that tend to all be in // the same zone. If after recomputing the debuggee zone set, this global's // zone is not in the set, then we must remove ourselves from the zone's // vector of observing debuggers. - globalDebuggersVector->erase( + globalDebuggersVector.erase( findDebuggerInVector(this, globalDebuggersVector)); if (debugEnum) { debugEnum->removeFront(); } else { debuggees.remove(global); } @@ -4833,17 +4771,17 @@ void Debugger::removeDebuggeeGlobal(JSFr MOZ_ASSERT_IF(debuggees.empty(), !firstBreakpoint()); // If we are tracking allocation sites, we need to remove the object // metadata callback from this global's realm. if (trackingAllocationSites) { Debugger::removeAllocationsTracking(*global); } - if (global->getDebuggers()->empty()) { + if (global->realm()->getDebuggers().empty()) { global->realm()->unsetIsDebuggee(); } else { global->realm()->updateDebuggerObservesAllExecution(); global->realm()->updateDebuggerObservesAsmJS(); global->realm()->updateDebuggerObservesCoverage(); } }
--- a/js/src/gc/GCEnum.h +++ b/js/src/gc/GCEnum.h @@ -132,17 +132,16 @@ enum class ZealMode { _(WasmModule) \ _(WasmTableTable) \ _(FileObjectFile) \ _(Debugger) \ _(DebuggerFrameGeneratorInfo) \ _(DebuggerFrameIterData) \ _(DebuggerOnStepHandler) \ _(DebuggerOnPopHandler) \ - _(GlobalDebuggerVector) \ _(RealmInstrumentation) #define JS_FOR_EACH_MEMORY_USE(_) \ JS_FOR_EACH_PUBLIC_MEMORY_USE(_) \ JS_FOR_EACH_INTERNAL_MEMORY_USE(_) enum class MemoryUse : uint8_t { #define DEFINE_MEMORY_USE(Name) Name,
--- a/js/src/gc/Zone.h +++ b/js/src/gc/Zone.h @@ -310,18 +310,16 @@ class Zone : public js::ZoneAllocator, p #endif void sweepAfterMinorGC(JSTracer* trc); void sweepBreakpoints(JSFreeOp* fop); void sweepUniqueIds(); void sweepWeakMaps(); void sweepCompartments(JSFreeOp* fop, bool keepAtleastOne, bool lastGC); - using DebuggerVector = js::Vector<js::Debugger*, 0, js::SystemAllocPolicy>; - private: js::jit::JitZone* createJitZone(JSContext* cx); bool isQueuedForBackgroundSweep() { return isOnList(); } // Side map for storing a unique ids for cells, independent of address. js::ZoneOrGCTaskData<js::gc::UniqueIdMap> uniqueIds_;
new file mode 100644 --- /dev/null +++ b/js/src/jit-test/tests/basic/bug1584027.js @@ -0,0 +1,3 @@ +// |jit-test| error:unsafe filename: (invalid UTF-8 filename) +setTestFilenameValidationCallback(); +evaluate("throw 2", {fileName: "\uDEFF"});
--- a/js/src/vm/GlobalObject.cpp +++ b/js/src/vm/GlobalObject.cpp @@ -853,41 +853,16 @@ bool js::DefinePropertiesAndFunctions(JS bool js::DefineToStringTag(JSContext* cx, HandleObject obj, JSAtom* tag) { RootedId toStringTagId(cx, SYMBOL_TO_JSID(cx->wellKnownSymbols().toStringTag)); RootedValue tagString(cx, StringValue(tag)); return DefineDataProperty(cx, obj, toStringTagId, tagString, JSPROP_READONLY); } -GlobalObject::DebuggerVector* GlobalObject::getDebuggers() const { - Value debuggers = getReservedSlot(DEBUGGERS); - if (debuggers.isUndefined()) { - return nullptr; - } - return DebugAPI::getGlobalDebuggers(&debuggers.toObject()); -} - -/* static */ GlobalObject::DebuggerVector* GlobalObject::getOrCreateDebuggers( - JSContext* cx, Handle<GlobalObject*> global) { - cx->check(global); - DebuggerVector* debuggers = global->getDebuggers(); - if (debuggers) { - return debuggers; - } - - JSObject* obj = DebugAPI::newGlobalDebuggersHolder(cx); - if (!obj) { - return nullptr; - } - - global->setReservedSlot(DEBUGGERS, ObjectValue(*obj)); - return global->getDebuggers(); -} - /* static */ NativeObject* GlobalObject::getOrCreateForOfPICObject( JSContext* cx, Handle<GlobalObject*> global) { cx->check(global); NativeObject* forOfPIC = global->getForOfPICObject(); if (forOfPIC) { return forOfPIC; }
--- a/js/src/vm/GlobalObject.h +++ b/js/src/vm/GlobalObject.h @@ -16,17 +16,16 @@ #include "vm/ArrayBufferObject.h" #include "vm/ErrorObject.h" #include "vm/JSFunction.h" #include "vm/Realm.h" #include "vm/Runtime.h" namespace js { -class Debugger; class TypedObjectModuleObject; class LexicalEnvironmentObject; class RegExpStatics; enum class ReferenceType; /* * Global object slots are reserved as follows: @@ -83,17 +82,16 @@ class GlobalObject : public NativeObject MAP_ITERATOR_PROTO, SET_ITERATOR_PROTO, MODULE_PROTO, IMPORT_ENTRY_PROTO, EXPORT_ENTRY_PROTO, REQUESTED_MODULE_PROTO, REGEXP_STATICS, RUNTIME_CODEGEN_ENABLED, - DEBUGGERS, INTRINSICS, FOR_OF_PIC_CHAIN, WINDOW_PROXY, GLOBAL_THIS_RESOLVED, INSTRUMENTATION, SOURCE_URLS, /* Total reserved-slot count for global objects. */ @@ -816,30 +814,19 @@ class GlobalObject : public NativeObject static bool initTypedObjectModule(JSContext* cx, Handle<GlobalObject*> global); static bool initStandardClasses(JSContext* cx, Handle<GlobalObject*> global); static bool initSelfHostingBuiltins(JSContext* cx, Handle<GlobalObject*> global, const JSFunctionSpec* builtins); - using DebuggerVector = Vector<WeakHeapPtr<Debugger*>, 0, ZoneAllocPolicy>; - - /* - * The collection of Debugger objects debugging this global. If this global - * is not a debuggee, this returns either nullptr or an empty vector. - */ - DebuggerVector* getDebuggers() const; - - /* - * The same, but create the empty vector if one does not already - * exist. Returns nullptr only on OOM. - */ - static DebuggerVector* getOrCreateDebuggers(JSContext* cx, - Handle<GlobalObject*> global); + Realm::DebuggerVector& getDebuggers() const { + return realm()->getDebuggers(); + } inline NativeObject* getForOfPICObject() { Value forOfPIC = getReservedSlot(FOR_OF_PIC_CHAIN); if (forOfPIC.isUndefined()) { return nullptr; } return &forOfPIC.toObject().as<NativeObject>(); }
--- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -12,16 +12,17 @@ #include "mozilla/DebugOnly.h" #include "mozilla/Maybe.h" #include "mozilla/MemoryReporting.h" #include "mozilla/PodOperations.h" #include "mozilla/ScopeExit.h" #include "mozilla/Sprintf.h" #include "mozilla/Unused.h" +#include "mozilla/Utf8.h" #include "mozilla/Vector.h" #include <algorithm> #include <new> #include <string.h> #include <type_traits> #include <utility> @@ -1720,18 +1721,24 @@ static MOZ_MUST_USE bool MaybeValidateFi if (!filename || options.skipFilenameValidation()) { return true; } if (gFilenameValidationCallback(filename, cx->realm()->isSystem())) { return true; } + const char* utf8Filename; + if (mozilla::IsUtf8(mozilla::MakeStringSpan(filename))) { + utf8Filename = filename; + } else { + utf8Filename = "(invalid UTF-8 filename)"; + } JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_UNSAFE_FILENAME, - filename); + utf8Filename); return false; } /* static */ bool ScriptSourceObject::initFromOptions( JSContext* cx, HandleScriptSourceObject source, const ReadOnlyCompileOptions& options) { cx->releaseCheck(source);
--- a/js/src/vm/Realm.cpp +++ b/js/src/vm/Realm.cpp @@ -8,16 +8,17 @@ #include "mozilla/MemoryReporting.h" #include <stddef.h> #include "jsfriendapi.h" #include "debugger/DebugAPI.h" +#include "debugger/Debugger.h" #include "gc/Policy.h" #include "gc/PublicIterators.h" #include "jit/JitOptions.h" #include "jit/JitRealm.h" #include "js/Date.h" #include "js/Proxy.h" #include "js/RootingAPI.h" #include "js/Wrapper.h" @@ -48,16 +49,17 @@ Realm::Realm(Compartment* comp, const JS : JS::shadow::Realm(comp), zone_(comp->zone()), runtime_(comp->runtimeFromMainThread()), creationOptions_(options.creationOptions()), behaviors_(options.behaviors()), objects_(zone_), varNames_(zone_), randomKeyGenerator_(runtime_->forkRandomKeyGenerator()), + debuggers_(zone_), wasm(runtime_) { MOZ_ASSERT_IF(creationOptions_.mergeable(), creationOptions_.invisibleToDebugger()); runtime_->numRealms++; } Realm::~Realm() {
--- a/js/src/vm/Realm.h +++ b/js/src/vm/Realm.h @@ -385,16 +385,23 @@ class JS::Realm : public JS::shadow::Rea // // An important invariant is that the JIT can only switch to a different // realm within the same compartment, so whenever that happens there must // always be a same-compartment realm with enterRealmDepthIgnoringJit_ > 0. // This lets us set Compartment::hasEnteredRealm without walking the // stack. unsigned enterRealmDepthIgnoringJit_ = 0; + public: + using DebuggerVector = + js::Vector<js::WeakHeapPtr<js::Debugger*>, 0, js::ZoneAllocPolicy>; + + private: + DebuggerVector debuggers_; + enum { IsDebuggee = 1 << 0, DebuggerObservesAllExecution = 1 << 1, DebuggerObservesAsmJS = 1 << 2, DebuggerObservesCoverage = 1 << 3, DebuggerNeedsDelazification = 1 << 4 }; static const unsigned DebuggerObservesMask = @@ -701,16 +708,18 @@ class JS::Realm : public JS::shadow::Rea // True if this realm's global is a debuggee of some Debugger // object. bool isDebuggee() const { return !!(debugModeBits_ & IsDebuggee); } void setIsDebuggee(); void unsetIsDebuggee(); + DebuggerVector& getDebuggers() { return debuggers_; }; + // True if this compartment's global is a debuggee of some Debugger // object with a live hook that observes all execution; e.g., // onEnterFrame. bool debuggerObservesAllExecution() const { static const unsigned Mask = IsDebuggee | DebuggerObservesAllExecution; return (debugModeBits_ & Mask) == Mask; } void updateDebuggerObservesAllExecution() {
--- a/js/xpconnect/src/ExportHelpers.cpp +++ b/js/xpconnect/src/ExportHelpers.cpp @@ -111,16 +111,20 @@ class MOZ_STACK_CLASS StackScopedCloneDa MOZ_ASSERT(global); // RefPtr<File> needs to go out of scope before toObjectOrNull() is called // because otherwise the static analysis thinks it can gc the JSObject via // the stack. JS::Rooted<JS::Value> val(aCx); { RefPtr<Blob> blob = Blob::Create(global, mBlobImpls[idx]); + if (NS_WARN_IF(!blob)) { + return nullptr; + } + if (!ToJSValue(aCx, blob, &val)) { return nullptr; } } return val.toObjectOrNull(); }
--- a/layout/forms/nsFileControlFrame.cpp +++ b/layout/forms/nsFileControlFrame.cpp @@ -326,17 +326,17 @@ static void AppendBlobImplAsDirectory(ns return; } nsPIDOMWindowInner* inner = aContent->OwnerDoc()->GetInnerWindow(); if (!inner || !inner->IsCurrentInnerWindow()) { return; } - RefPtr<Directory> directory = Directory::Create(inner, file); + RefPtr<Directory> directory = Directory::Create(inner->AsGlobal(), file); MOZ_ASSERT(directory); OwningFileOrDirectory* element = aArray.AppendElement(); element->SetAsDirectory() = directory; } /** * This is called when we receive a drop or a dragover.
--- a/widget/cocoa/nsTouchBar.mm +++ b/widget/cocoa/nsTouchBar.mm @@ -58,17 +58,17 @@ static const uint32_t kInputIconSize = 1 mTouchBarHelper = do_GetService(NS_TOUCHBARHELPER_CID); if (!mTouchBarHelper) { NS_ERROR("Unable to create Touch Bar Helper."); return nil; } self.delegate = self; self.mappedLayoutItems = [NSMutableDictionary dictionary]; - self.customizationAllowedItemIdentifiers = nil; + self.customizationAllowedItemIdentifiers = @[]; if (!aInputs) { // This customization identifier is how users' custom layouts are saved by macOS. // If this changes, all users' layouts would be reset to the default layout. self.customizationIdentifier = [BaseIdentifier stringByAppendingPathExtension:@"defaultbar"]; nsCOMPtr<nsIArray> allItems; nsresult rv = mTouchBarHelper->GetAllItems(getter_AddRefs(allItems)); @@ -121,17 +121,16 @@ static const uint32_t kInputIconSize = 1 - (void)dealloc { for (NSTouchBarItemIdentifier identifier in self.mappedLayoutItems) { NSTouchBarItem* item = [self itemForIdentifier:identifier]; if (!item) { continue; } if ([item isKindOfClass:[NSPopoverTouchBarItem class]]) { - [(NSPopoverTouchBarItem*)item setCollapsedRepresentation:nil]; [(NSPopoverTouchBarItem*)item setCollapsedRepresentationImage:nil]; [(nsTouchBar*)[(NSPopoverTouchBarItem*)item popoverTouchBar] release]; } else if ([[item view] isKindOfClass:[NSScrollView class]]) { [[(NSScrollView*)[item view] documentView] release]; [(NSScrollView*)[item view] release]; } [item release]; @@ -351,18 +350,16 @@ static const uint32_t kInputIconSize = 1 RefPtr<nsTouchBarInputIcon> icon = [aInput icon]; if (!icon) { icon = new nsTouchBarInputIcon([aInput document], nil, nil, aPopoverItem); [aInput setIcon:icon]; } icon->SetupIcon([aInput imageURI]); } else if ([aInput title]) { aPopoverItem.collapsedRepresentationLabel = [aInput title]; - } else { - aPopoverItem.collapsedRepresentation = nil; } // Special handling to show/hide the search popover if the Urlbar is focused. if ([[aInput nativeIdentifier] isEqualToString:SearchPopoverIdentifier]) { // We can reach this code during window shutdown. We only want to toggle // showPopover if we are in a normal running state. if (!mTouchBarHelper) { return;
--- a/widget/nsBaseFilePicker.cpp +++ b/widget/nsBaseFilePicker.cpp @@ -32,31 +32,37 @@ using namespace mozilla::dom; #define FILEPICKER_TITLES "chrome://global/locale/filepicker.properties" #define FILEPICKER_FILTERS "chrome://global/content/filepicker.properties" namespace { nsresult LocalFileToDirectoryOrBlob(nsPIDOMWindowInner* aWindow, bool aIsDirectory, nsIFile* aFile, nsISupports** aResult) { + MOZ_ASSERT(aWindow); + if (aIsDirectory) { #ifdef DEBUG bool isDir; aFile->IsDirectory(&isDir); MOZ_ASSERT(isDir); #endif - RefPtr<Directory> directory = Directory::Create(aWindow, aFile); + RefPtr<Directory> directory = Directory::Create(aWindow->AsGlobal(), aFile); MOZ_ASSERT(directory); directory.forget(aResult); return NS_OK; } - RefPtr<File> file = File::CreateFromFile(aWindow, aFile); + RefPtr<File> file = File::CreateFromFile(aWindow->AsGlobal(), aFile); + if (NS_WARN_IF(!file)) { + return NS_ERROR_FAILURE; + } + file.forget(aResult); return NS_OK; } } // anonymous namespace /** * A runnable to dispatch from the main thread to the main thread to display @@ -114,16 +120,20 @@ class nsBaseFilePickerEnumerator : publi return NS_OK; } nsCOMPtr<nsIFile> localFile = do_QueryInterface(tmp); if (!localFile) { return NS_ERROR_FAILURE; } + if (!mParent) { + return NS_ERROR_FAILURE; + } + return LocalFileToDirectoryOrBlob( mParent, mMode == nsIFilePicker::modeGetFolder, localFile, aResult); } NS_IMETHOD HasMoreElements(bool* aResult) override { return mIterator->HasMoreElements(aResult); } @@ -373,16 +383,20 @@ nsBaseFilePicker::GetDomFileOrDirectory( if (!localFile) { *aValue = nullptr; return NS_OK; } auto* innerParent = mParent ? mParent->GetCurrentInnerWindow() : nullptr; + if (!innerParent) { + return NS_ERROR_FAILURE; + } + return LocalFileToDirectoryOrBlob( innerParent, mMode == nsIFilePicker::modeGetFolder, localFile, aValue); } NS_IMETHODIMP nsBaseFilePicker::GetDomFileOrDirectoryEnumerator( nsISimpleEnumerator** aValue) { nsCOMPtr<nsISimpleEnumerator> iter;
--- a/widget/nsFilePickerProxy.cpp +++ b/widget/nsFilePickerProxy.cpp @@ -141,44 +141,50 @@ nsFilePickerProxy::Open(nsIFilePickerSho mFilters, mFilterNames, mRawFilters, displayDirectory, mDisplaySpecialDirectory, mOkButtonLabel, mCapture); return NS_OK; } mozilla::ipc::IPCResult nsFilePickerProxy::Recv__delete__( const MaybeInputData& aData, const int16_t& aResult) { + nsPIDOMWindowInner* inner = + mParent ? mParent->GetCurrentInnerWindow() : nullptr; + + if (NS_WARN_IF(!inner)) { + return IPC_OK(); + } + if (aData.type() == MaybeInputData::TInputBlobs) { const nsTArray<IPCBlob>& blobs = aData.get_InputBlobs().blobs(); for (uint32_t i = 0; i < blobs.Length(); ++i) { RefPtr<BlobImpl> blobImpl = IPCBlobUtils::Deserialize(blobs[i]); NS_ENSURE_TRUE(blobImpl, IPC_OK()); if (!blobImpl->IsFile()) { return IPC_OK(); } - nsPIDOMWindowInner* inner = - mParent ? mParent->GetCurrentInnerWindow() : nullptr; - RefPtr<File> file = File::Create(inner, blobImpl); - MOZ_ASSERT(file); + RefPtr<File> file = File::Create(inner->AsGlobal(), blobImpl); + if (NS_WARN_IF(!file)) { + return IPC_OK(); + } OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); element->SetAsFile() = file; } } else if (aData.type() == MaybeInputData::TInputDirectory) { nsCOMPtr<nsIFile> file; const nsAString& path(aData.get_InputDirectory().directoryPath()); nsresult rv = NS_NewLocalFile(path, true, getter_AddRefs(file)); if (NS_WARN_IF(NS_FAILED(rv))) { return IPC_OK(); } - RefPtr<Directory> directory = - Directory::Create(mParent->GetCurrentInnerWindow(), file); + RefPtr<Directory> directory = Directory::Create(inner->AsGlobal(), file); MOZ_ASSERT(directory); OwningFileOrDirectory* element = mFilesOrDirectories.AppendElement(); element->SetAsDirectory() = directory; } if (mCallback) { mCallback->Done(aResult);
copy from xpcom/threads/PrioritizedEventQueue.cpp copy to xpcom/threads/IdlePeriodState.cpp --- a/xpcom/threads/PrioritizedEventQueue.cpp +++ b/xpcom/threads/IdlePeriodState.cpp @@ -1,86 +1,111 @@ /* -*- 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 "PrioritizedEventQueue.h" -#include "mozilla/EventQueue.h" -#include "mozilla/ScopeExit.h" +#include "mozilla/IdlePeriodState.h" #include "mozilla/StaticPrefs_idle_period.h" -#include "mozilla/StaticPrefs_threads.h" #include "mozilla/ipc/IdleSchedulerChild.h" +#include "nsIIdlePeriod.h" #include "nsThreadManager.h" -#include "nsXPCOMPrivate.h" // for gXPCOMThreadsShutDown -#include "InputEventStatistics.h" - -using namespace mozilla; +#include "nsThreadUtils.h" +#include "nsXPCOM.h" +#include "nsXULAppAPI.h" static uint64_t sIdleRequestCounter = 0; -PrioritizedEventQueue::PrioritizedEventQueue( - already_AddRefed<nsIIdlePeriod> aIdlePeriod) - : mHighQueue(MakeUnique<EventQueue>(EventQueuePriority::High)), - mInputQueue(MakeUnique<EventQueue>(EventQueuePriority::Input)), - mMediumHighQueue(MakeUnique<EventQueue>(EventQueuePriority::MediumHigh)), - mNormalQueue(MakeUnique<EventQueue>(EventQueuePriority::Normal)), - mDeferredTimersQueue( - MakeUnique<EventQueue>(EventQueuePriority::DeferredTimers)), - mIdleQueue(MakeUnique<EventQueue>(EventQueuePriority::Idle)), - mIdlePeriod(aIdlePeriod) {} +namespace mozilla { -PrioritizedEventQueue::~PrioritizedEventQueue() { +IdlePeriodState::IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod) + : mIdlePeriod(aIdlePeriod) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); +} + +IdlePeriodState::~IdlePeriodState() { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); if (mIdleScheduler) { mIdleScheduler->Disconnect(); } } -void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, - EventQueuePriority aPriority, - const MutexAutoLock& aProofOfLock) { - // Double check the priority with a QI. - RefPtr<nsIRunnable> event(aEvent); - EventQueuePriority priority = aPriority; - - if (priority == EventQueuePriority::Input && - mInputQueueState == STATE_DISABLED) { - priority = EventQueuePriority::Normal; - } else if (priority == EventQueuePriority::MediumHigh && - !StaticPrefs::threads_medium_high_event_queue_enabled()) { - priority = EventQueuePriority::Normal; +size_t IdlePeriodState::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const { + size_t n = 0; + if (mIdlePeriod) { + n += aMallocSizeOf(mIdlePeriod); } - switch (priority) { - case EventQueuePriority::High: - mHighQueue->PutEvent(event.forget(), priority, aProofOfLock); - break; - case EventQueuePriority::Input: - mInputQueue->PutEvent(event.forget(), priority, aProofOfLock); - break; - case EventQueuePriority::MediumHigh: - mMediumHighQueue->PutEvent(event.forget(), priority, aProofOfLock); - break; - case EventQueuePriority::Normal: - mNormalQueue->PutEvent(event.forget(), priority, aProofOfLock); - break; - case EventQueuePriority::DeferredTimers: - mDeferredTimersQueue->PutEvent(event.forget(), priority, aProofOfLock); - break; - case EventQueuePriority::Idle: - mIdleQueue->PutEvent(event.forget(), priority, aProofOfLock); - break; - case EventQueuePriority::Count: - MOZ_CRASH("EventQueuePriority::Count isn't a valid priority"); - break; + return n; +} + +void IdlePeriodState::FlagNotIdle(Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); + + EnsureIsActive(); + if (mIdleToken && mIdleToken < TimeStamp::Now()) { + ClearIdleToken(aMutexToUnlock); } } -TimeStamp PrioritizedEventQueue::GetLocalIdleDeadline(bool& aShuttingDown) { +void IdlePeriodState::RanOutOfTasks(Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); + MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent); + EnsureIsPaused(aMutexToUnlock); + ClearIdleToken(aMutexToUnlock); +} + +TimeStamp IdlePeriodState::GetIdleDeadlineInternal(bool aIsPeek, + Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); + + bool shuttingDown; + TimeStamp localIdleDeadline = + GetLocalIdleDeadline(shuttingDown, aMutexToUnlock); + if (!localIdleDeadline) { + if (!aIsPeek) { + EnsureIsPaused(aMutexToUnlock); + ClearIdleToken(aMutexToUnlock); + } + return TimeStamp(); + } + + TimeStamp idleDeadline = + mHasPendingEventsPromisedIdleEvent || shuttingDown + ? localIdleDeadline + : GetIdleToken(localIdleDeadline, aMutexToUnlock); + if (!idleDeadline) { + if (!aIsPeek) { + EnsureIsPaused(aMutexToUnlock); + + // Don't call ClearIdleToken() here, since we may have a pending + // request already. + // RequestIdleToken can do all sorts of IPC stuff that might take mutexes. + MutexAutoUnlock unlock(aMutexToUnlock); + RequestIdleToken(localIdleDeadline); + } + return TimeStamp(); + } + + if (!aIsPeek) { + EnsureIsActive(); + } + return idleDeadline; +} + +TimeStamp IdlePeriodState::GetLocalIdleDeadline(bool& aShuttingDown, + Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); // If we are shutting down, we won't honor the idle period, and we will // always process idle runnables. This will ensure that the idle queue // gets exhausted at shutdown time to prevent intermittently leaking // some runnables inside that queue and even worse potentially leaving // some important cleanup work unfinished. if (gXPCOMThreadsShutDown || nsThreadManager::get().GetCurrentThread()->ShuttingDown()) { aShuttingDown = true; @@ -89,17 +114,17 @@ TimeStamp PrioritizedEventQueue::GetLoca aShuttingDown = false; TimeStamp idleDeadline; { // Releasing the lock temporarily since getting the idle period // might need to lock the timer thread. Unlocking here might make // us receive an event on the main queue, but we've committed to // run an idle event anyhow. - MutexAutoUnlock unlock(*mMutex); + MutexAutoUnlock unlock(aMutexToUnlock); mIdlePeriod->GetIdlePeriodHint(&idleDeadline); } // If HasPendingEvents() has been called and it has returned true because of // pending idle events, there is a risk that we may decide here that we aren't // idle and return null, in which case HasPendingEvents() has effectively // lied. Since we can't go back and fix the past, we have to adjust what we // do here and forcefully pick the idle queue task here. Note that this means @@ -117,320 +142,45 @@ TimeStamp PrioritizedEventQueue::GetLoca // If HasPendingEvents() has been called and it has returned true, but we're // no longer in the idle period, we must return a valid timestamp to pretend // that we are still in the idle period. return TimeStamp::Now(); } return idleDeadline; } -EventQueuePriority PrioritizedEventQueue::SelectQueue( - bool aUpdateState, const MutexAutoLock& aProofOfLock) { - size_t inputCount = mInputQueue->Count(aProofOfLock); - - if (aUpdateState && mInputQueueState == STATE_ENABLED && - mInputHandlingStartTime.IsNull() && inputCount > 0) { - mInputHandlingStartTime = - InputEventStatistics::Get().GetInputHandlingStartTime(inputCount); - } - - // We check the different queues in the following order. The conditions we use - // are meant to avoid starvation and to ensure that we don't process an event - // at the wrong time. - // - // HIGH: if mProcessHighPriorityQueue - // INPUT: if inputCount > 0 && TimeStamp::Now() > mInputHandlingStartTime - // MEDIUMHIGH: if medium high pending - // NORMAL: if normal pending - // - // If we still don't have an event, then we take events from the queues - // in the following order: - // - // HIGH - // INPUT - // DEFERREDTIMERS: if GetLocalIdleDeadline() - // IDLE: if GetLocalIdleDeadline() - // - // If we don't get an event in this pass, then we return null since no events - // are ready. - - // This variable determines which queue we will take an event from. - EventQueuePriority queue; - bool highPending = !mHighQueue->IsEmpty(aProofOfLock); - - if (mProcessHighPriorityQueue) { - queue = EventQueuePriority::High; - } else if (inputCount > 0 && (mInputQueueState == STATE_FLUSHING || - (mInputQueueState == STATE_ENABLED && - !mInputHandlingStartTime.IsNull() && - TimeStamp::Now() > mInputHandlingStartTime))) { - queue = EventQueuePriority::Input; - } else if (!mMediumHighQueue->IsEmpty(aProofOfLock)) { - MOZ_ASSERT( - mInputQueueState != STATE_FLUSHING, - "Shouldn't consume medium high event when flushing input events"); - queue = EventQueuePriority::MediumHigh; - } else if (!mNormalQueue->IsEmpty(aProofOfLock)) { - MOZ_ASSERT(mInputQueueState != STATE_FLUSHING, - "Shouldn't consume normal event when flushing input events"); - queue = EventQueuePriority::Normal; - } else if (highPending) { - queue = EventQueuePriority::High; - } else if (inputCount > 0 && mInputQueueState != STATE_SUSPEND) { - MOZ_ASSERT( - mInputQueueState != STATE_DISABLED, - "Shouldn't consume input events when the input queue is disabled"); - queue = EventQueuePriority::Input; - } else if (!mDeferredTimersQueue->IsEmpty(aProofOfLock)) { - // We may not actually return an idle event in this case. - queue = EventQueuePriority::DeferredTimers; - } else { - // We may not actually return an idle event in this case. - queue = EventQueuePriority::Idle; - } - - MOZ_ASSERT_IF( - queue == EventQueuePriority::Input, - mInputQueueState != STATE_DISABLED && mInputQueueState != STATE_SUSPEND); - - if (aUpdateState) { - mProcessHighPriorityQueue = highPending; - } - - return queue; -} - -already_AddRefed<nsIRunnable> PrioritizedEventQueue::GetEvent( - EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock) { -#ifndef RELEASE_OR_BETA - // Clear mNextIdleDeadline so that it is possible to determine that - // we're running an idle runnable in ProcessNextEvent. - *mNextIdleDeadline = TimeStamp(); -#endif - - EventQueuePriority queue = SelectQueue(true, aProofOfLock); - auto guard = MakeScopeExit([&] { - mHasPendingEventsPromisedIdleEvent = false; - if (queue != EventQueuePriority::Idle && - queue != EventQueuePriority::DeferredTimers) { - EnsureIsActive(); - if (mIdleToken && mIdleToken < TimeStamp::Now()) { - ClearIdleToken(); - } - } - }); - - if (aPriority) { - *aPriority = queue; - } - - if (queue == EventQueuePriority::High) { - nsCOMPtr<nsIRunnable> event = mHighQueue->GetEvent(aPriority, aProofOfLock); - MOZ_ASSERT(event); - mInputHandlingStartTime = TimeStamp(); - mProcessHighPriorityQueue = false; - return event.forget(); - } - - if (queue == EventQueuePriority::Input) { - nsCOMPtr<nsIRunnable> event = - mInputQueue->GetEvent(aPriority, aProofOfLock); - MOZ_ASSERT(event); - return event.forget(); - } - - if (queue == EventQueuePriority::MediumHigh) { - nsCOMPtr<nsIRunnable> event = - mMediumHighQueue->GetEvent(aPriority, aProofOfLock); - return event.forget(); - } - - if (queue == EventQueuePriority::Normal) { - nsCOMPtr<nsIRunnable> event = - mNormalQueue->GetEvent(aPriority, aProofOfLock); - return event.forget(); - } - - // If we get here, then all queues except deferredtimers and idle are empty. - MOZ_ASSERT(queue == EventQueuePriority::Idle || - queue == EventQueuePriority::DeferredTimers); - - if (mIdleQueue->IsEmpty(aProofOfLock) && - mDeferredTimersQueue->IsEmpty(aProofOfLock)) { - MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent); - EnsureIsPaused(); - ClearIdleToken(); - return nullptr; - } +TimeStamp IdlePeriodState::GetIdleToken(TimeStamp aLocalIdlePeriodHint, + Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); - bool shuttingDown; - TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown); - if (!localIdleDeadline) { - EnsureIsPaused(); - ClearIdleToken(); - return nullptr; - } - - TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown - ? localIdleDeadline - : GetIdleToken(localIdleDeadline); - if (!idleDeadline) { - EnsureIsPaused(); - - // Don't call ClearIdleToken() here, since we may have a pending - // request already. - MutexAutoUnlock unlock(*mMutex); - RequestIdleToken(localIdleDeadline); - return nullptr; - } - - nsCOMPtr<nsIRunnable> event = - mDeferredTimersQueue->GetEvent(aPriority, aProofOfLock); - if (!event) { - event = mIdleQueue->GetEvent(aPriority, aProofOfLock); - } - if (event) { - nsCOMPtr<nsIIdleRunnable> idleEvent = do_QueryInterface(event); - if (idleEvent) { - idleEvent->SetDeadline(idleDeadline); - } - -#ifndef RELEASE_OR_BETA - // Store the next idle deadline to be able to determine budget use - // in ProcessNextEvent. - *mNextIdleDeadline = idleDeadline; -#endif - } - - EnsureIsActive(); - return event.forget(); -} - -void PrioritizedEventQueue::DidRunEvent(const MutexAutoLock& aProofOfLock) { - if (IsEmpty(aProofOfLock)) { - if (IsActive()) { - SetPaused(); - } - ClearIdleToken(); - } -} - -bool PrioritizedEventQueue::IsEmpty(const MutexAutoLock& aProofOfLock) { - // Just check IsEmpty() on the sub-queues. Don't bother checking the idle - // deadline since that only determines whether an idle event is ready or not. - return mHighQueue->IsEmpty(aProofOfLock) && - mInputQueue->IsEmpty(aProofOfLock) && - mMediumHighQueue->IsEmpty(aProofOfLock) && - mNormalQueue->IsEmpty(aProofOfLock) && - mDeferredTimersQueue->IsEmpty(aProofOfLock) && - mIdleQueue->IsEmpty(aProofOfLock); -} - -bool PrioritizedEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) { - mHasPendingEventsPromisedIdleEvent = false; - - EventQueuePriority queue = SelectQueue(false, aProofOfLock); - - if (queue == EventQueuePriority::High) { - return mHighQueue->HasReadyEvent(aProofOfLock); - } else if (queue == EventQueuePriority::Input) { - return mInputQueue->HasReadyEvent(aProofOfLock); - } else if (queue == EventQueuePriority::MediumHigh) { - return mMediumHighQueue->HasReadyEvent(aProofOfLock); - } else if (queue == EventQueuePriority::Normal) { - return mNormalQueue->HasReadyEvent(aProofOfLock); - } - - MOZ_ASSERT(queue == EventQueuePriority::Idle || - queue == EventQueuePriority::DeferredTimers); - - // If we get here, then both the high and normal queues are empty. - - if (mDeferredTimersQueue->IsEmpty(aProofOfLock) && - mIdleQueue->IsEmpty(aProofOfLock)) { - return false; - } - - bool shuttingDown; - TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown); - if (localIdleDeadline) { - TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown - ? localIdleDeadline - : GetIdleToken(localIdleDeadline); - if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) || - mIdleQueue->HasReadyEvent(aProofOfLock))) { - mHasPendingEventsPromisedIdleEvent = true; - return true; - } - } - - return false; -} - -bool PrioritizedEventQueue::HasPendingHighPriorityEvents( - const MutexAutoLock& aProofOfLock) { - return !mHighQueue->IsEmpty(aProofOfLock); -} - -size_t PrioritizedEventQueue::Count(const MutexAutoLock& aProofOfLock) const { - MOZ_CRASH("unimplemented"); -} - -void PrioritizedEventQueue::EnableInputEventPrioritization( - const MutexAutoLock& aProofOfLock) { - MOZ_ASSERT(mInputQueueState == STATE_DISABLED); - mInputQueueState = STATE_ENABLED; - mInputHandlingStartTime = TimeStamp(); -} - -void PrioritizedEventQueue::FlushInputEventPrioritization( - const MutexAutoLock& aProofOfLock) { - MOZ_ASSERT(mInputQueueState == STATE_ENABLED || - mInputQueueState == STATE_SUSPEND); - mInputQueueState = - mInputQueueState == STATE_ENABLED ? STATE_FLUSHING : STATE_SUSPEND; -} - -void PrioritizedEventQueue::SuspendInputEventPrioritization( - const MutexAutoLock& aProofOfLock) { - MOZ_ASSERT(mInputQueueState == STATE_ENABLED || - mInputQueueState == STATE_FLUSHING); - mInputQueueState = STATE_SUSPEND; -} - -void PrioritizedEventQueue::ResumeInputEventPrioritization( - const MutexAutoLock& aProofOfLock) { - MOZ_ASSERT(mInputQueueState == STATE_SUSPEND); - mInputQueueState = STATE_ENABLED; -} - -mozilla::TimeStamp PrioritizedEventQueue::GetIdleToken( - TimeStamp aLocalIdlePeriodHint) { if (XRE_IsParentProcess()) { return aLocalIdlePeriodHint; } if (mIdleToken) { TimeStamp now = TimeStamp::Now(); if (mIdleToken < now) { - ClearIdleToken(); + ClearIdleToken(aMutexToUnlock); return mIdleToken; } return mIdleToken < aLocalIdlePeriodHint ? mIdleToken : aLocalIdlePeriodHint; } return TimeStamp(); } -void PrioritizedEventQueue::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) { +void IdlePeriodState::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); MOZ_ASSERT(!mActive); if (!mIdleSchedulerInitialized) { mIdleSchedulerInitialized = true; if (StaticPrefs::idle_period_cross_process_scheduling() && - XRE_IsContentProcess() && NS_IsMainThread() && + XRE_IsContentProcess() && // Disable when recording/replaying, as IdleSchedulerChild uses mutable // shared memory which needs special handling. !recordreplay::IsRecordingOrReplaying()) { // For now cross-process idle scheduler is supported only on the main // threads of the child processes. mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler(); if (mIdleScheduler) { mIdleScheduler->Init(this); @@ -445,44 +195,60 @@ void PrioritizedEventQueue::RequestIdleT } mIdleRequestId = ++sIdleRequestCounter; mIdleScheduler->SendRequestIdleTime(mIdleRequestId, aLocalIdlePeriodHint - now); } } -void PrioritizedEventQueue::SetIdleToken(uint64_t aId, TimeDuration aDuration) { +void IdlePeriodState::SetIdleToken(uint64_t aId, TimeDuration aDuration) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); if (mIdleRequestId == aId) { mIdleToken = TimeStamp::Now() + aDuration; } } -void PrioritizedEventQueue::SetActive() { +void IdlePeriodState::SetActive() { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); MOZ_ASSERT(!mActive); if (mIdleScheduler) { mIdleScheduler->SetActive(); } mActive = true; } -void PrioritizedEventQueue::SetPaused() { +void IdlePeriodState::SetPaused(Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); MOZ_ASSERT(mActive); if (mIdleScheduler && mIdleScheduler->SetPaused()) { - MutexAutoUnlock unlock(*mMutex); + MutexAutoUnlock unlock(aMutexToUnlock); // We may have gotten a free cpu core for running idle tasks. // We don't try to catch the case when there are prioritized processes // running. + + // This SendSchedule call is why we need to MutexAutoUnlock here, because + // IPC can do weird things with mutexes. mIdleScheduler->SendSchedule(); } mActive = false; } -void PrioritizedEventQueue::ClearIdleToken() { +void IdlePeriodState::ClearIdleToken(Mutex& aMutexToUnlock) { + MOZ_ASSERT(NS_IsMainThread(), + "Why are we touching idle state off the main thread?"); + if (mIdleRequestId) { if (mIdleScheduler) { - MutexAutoUnlock unlock(*mMutex); + // This SendIdleTimeUsed call is why we need to MutexAutoUnlock here, + // because IPC can do weird things with mutexes. + MutexAutoUnlock unlock(aMutexToUnlock); mIdleScheduler->SendIdleTimeUsed(mIdleRequestId); } mIdleRequestId = 0; mIdleToken = TimeStamp(); } } + +} // namespace mozilla
copy from xpcom/threads/PrioritizedEventQueue.h copy to xpcom/threads/IdlePeriodState.h --- a/xpcom/threads/PrioritizedEventQueue.h +++ b/xpcom/threads/IdlePeriodState.h @@ -1,207 +1,183 @@ /* -*- 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_PrioritizedEventQueue_h -#define mozilla_PrioritizedEventQueue_h +#ifndef mozilla_IdlePeriodState_h +#define mozilla_IdlePeriodState_h -#include "mozilla/AbstractEventQueue.h" -#include "mozilla/EventQueue.h" +/** + * A class for tracking the state of our idle period. This includes keeping + * track of both the state of our process-local idle period estimate and, for + * content processes, managing communication with the parent process for + * cross-pprocess idle detection. + */ + +#include "mozilla/MemoryReporting.h" +#include "mozilla/Mutex.h" +#include "mozilla/RefPtr.h" #include "mozilla/TimeStamp.h" -#include "mozilla/TypeTraits.h" -#include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" -#include "nsIIdlePeriod.h" -class nsIRunnable; +#include <stdint.h> + +class nsIIdlePeriod; namespace mozilla { namespace ipc { class IdleSchedulerChild; -} +} // namespace ipc + +class IdlePeriodState { + public: + explicit IdlePeriodState(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod); + + ~IdlePeriodState(); + + // Integration with memory reporting. + size_t SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const; -// This AbstractEventQueue implementation has one queue for each -// EventQueuePriority. The type of queue used for each priority is determined by -// the template parameter. -// -// When an event is pushed, its priority is determined by QIing the runnable to -// nsIRunnablePriority, or by falling back to the aPriority parameter if the QI -// fails. -// -// When an event is popped, a queue is selected based on heuristics that -// optimize for performance. Roughly, events are selected from the highest -// priority queue that is non-empty. However, there are a few exceptions: -// - We try to avoid processing too many high-priority events in a row so -// that the normal priority queue is not starved. When there are high- -// and normal-priority events available, we interleave popping from the -// normal and high queues. -// - We do not select events from the idle queue if the current idle period -// is almost over. -class PrioritizedEventQueue final : public AbstractEventQueue { - public: - static const bool SupportsPrioritization = true; + // Notification that whoever we are tracking idle state for has found a + // non-idle task to process. + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + void FlagNotIdle(Mutex& aMutexToUnlock); - explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod> aIdlePeriod); - - virtual ~PrioritizedEventQueue(); - - void PutEvent(already_AddRefed<nsIRunnable>&& aEvent, - EventQueuePriority aPriority, - const MutexAutoLock& aProofOfLock) final; - already_AddRefed<nsIRunnable> GetEvent( - EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock) final; - void DidRunEvent(const MutexAutoLock& aProofOfLock); + // Notification that whoever we are tracking idle state for has no more + // tasks (idle or not) to process. + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + void RanOutOfTasks(Mutex& aMutexToUnlock); - bool IsEmpty(const MutexAutoLock& aProofOfLock) final; - size_t Count(const MutexAutoLock& aProofOfLock) const final; - bool HasReadyEvent(const MutexAutoLock& aProofOfLock) final; - bool HasPendingHighPriorityEvents(const MutexAutoLock& aProofOfLock) final; + // Notification that whoever we are tracking idle state has idle tasks that + // they are considering ready to run and that we should keep claiming they are + // ready to run until they call ForgetPendingTaskGuarantee(). + void EnforcePendingTaskGuarantee() { + mHasPendingEventsPromisedIdleEvent = true; + } - // When checking the idle deadline, we need to drop whatever mutex protects - // this queue. This method allows that mutex to be stored so that we can drop - // it and reacquire it when checking the idle deadline. The mutex must live at - // least as long as the queue. - void SetMutexRef(Mutex& aMutex) { mMutex = &aMutex; } - -#ifndef RELEASE_OR_BETA - // nsThread.cpp sends telemetry containing the most recently computed idle - // deadline. We store a reference to a field in nsThread where this deadline - // will be stored so that it can be fetched quickly for telemetry. - void SetNextIdleDeadlineRef(TimeStamp& aDeadline) { - mNextIdleDeadline = &aDeadline; + // Notification that whoever we are tracking idle state for is done with our + // "we have an idle event ready to run" guarantee. When this happens, we can + // reset mHasPendingEventsPromisedIdleEvent to false, because we have + // fulfilled our contract. + void ForgetPendingTaskGuarantee() { + mHasPendingEventsPromisedIdleEvent = false; } -#endif - void EnableInputEventPrioritization(const MutexAutoLock& aProofOfLock) final; - void FlushInputEventPrioritization(const MutexAutoLock& aProofOfLock) final; - void SuspendInputEventPrioritization(const MutexAutoLock& aProofOfLock) final; - void ResumeInputEventPrioritization(const MutexAutoLock& aProofOfLock) final; - - size_t SizeOfExcludingThis( - mozilla::MallocSizeOf aMallocSizeOf) const override { - size_t n = 0; + // Get our current idle deadline so we can run an idle task with that + // deadline. This can return a null timestamp (which means we are not idle + // right now), and it can also queue up queries to our parent process, if + // we're a content process, to find out whether we're idle. This should only + // be called when there is an actual idle task that might run. + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + TimeStamp GetDeadlineForIdleTask(Mutex& aMutexToUnlock) { + return GetIdleDeadlineInternal(false, aMutexToUnlock); + } - n += mHighQueue->SizeOfIncludingThis(aMallocSizeOf); - n += mInputQueue->SizeOfIncludingThis(aMallocSizeOf); - n += mMediumHighQueue->SizeOfIncludingThis(aMallocSizeOf); - n += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf); - n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf); - n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf); - - if (mIdlePeriod) { - n += aMallocSizeOf(mIdlePeriod); - } - - return n; + // Peek our current idle deadline. This can return a null timestamp (which + // means we are not idle right now). This method does not have any + // side-effects on our state, apart from guaranteeing that if it returns + // non-null then GetDeadlineForIdleTask will return non-null until + // ForgetPendingTaskGuarantee() is called. + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + TimeStamp PeekIdleDeadline(Mutex& aMutexToUnlock) { + return GetIdleDeadlineInternal(true, aMutexToUnlock); } void SetIdleToken(uint64_t aId, TimeDuration aDuration); bool IsActive() { return mActive; } + protected: void EnsureIsActive() { if (!mActive) { SetActive(); } } - void EnsureIsPaused() { + void EnsureIsPaused(Mutex& aMutexToUnlock) { if (mActive) { - SetPaused(); + SetPaused(aMutexToUnlock); } } - private: - EventQueuePriority SelectQueue(bool aUpdateState, - const MutexAutoLock& aProofOfLock); + // Returns a null TimeStamp if we're not in the idle period. + TimeStamp GetLocalIdleDeadline(bool& aShuttingDown, Mutex& aMutexToUnlock); - // Returns a null TimeStamp if we're not in the idle period. - mozilla::TimeStamp GetLocalIdleDeadline(bool& aShuttingDown); + // Gets the idle token, which is the end time of the idle period. + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint, Mutex& aMutexToUnlock); + + // In case of child processes, requests idle time from the cross-process + // idle scheduler. + void RequestIdleToken(TimeStamp aLocalIdlePeriodHint); + + // Mark that we don't have idle time to use, nor are expecting to get an idle + // token from the idle scheduler. + void ClearIdleToken(Mutex& aMutexToUnlock); // SetActive should be called when the event queue is running any type of // tasks. void SetActive(); // SetPaused should be called once the event queue doesn't have more // tasks to process, or is waiting for the idle token. - void SetPaused(); - - // Gets the idle token, which is the end time of the idle period. - TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint); - - // In case of child processes, requests idle time from the cross-process - // idle scheduler. - void RequestIdleToken(TimeStamp aLocalIdlePeriodHint); - - // Returns true if the event queue either is waiting for an idle token - // from the idle scheduler or has one. - bool HasIdleRequest() { return mIdleRequestId != 0; } - - // Mark that the event queue doesn't have idle time to use, nor is expecting - // to get idle token from the idle scheduler. - void ClearIdleToken(); + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + void SetPaused(Mutex& aMutexToUnlock); - UniquePtr<EventQueue> mHighQueue; - UniquePtr<EventQueue> mInputQueue; - UniquePtr<EventQueue> mMediumHighQueue; - UniquePtr<EventQueue> mNormalQueue; - UniquePtr<EventQueue> mDeferredTimersQueue; - UniquePtr<EventQueue> mIdleQueue; - - // We need to drop the queue mutex when checking the idle deadline, so we keep - // a pointer to it here. - Mutex* mMutex = nullptr; + // Get or peek our idle deadline. When peeking, we generally don't change any + // of our internal state. When getting, we may request an idle token as + // needed. + // + // aMutexToUnlock is the mutex to unlock if we do anything that might lock + // other mutexes. + TimeStamp GetIdleDeadlineInternal(bool aIsPeek, Mutex& aMutexToUnlock); -#ifndef RELEASE_OR_BETA - // Pointer to a place where the most recently computed idle deadline is - // stored. - TimeStamp* mNextIdleDeadline = nullptr; -#endif + // Set to true if we have claimed we have a ready-to-run idle task when asked. + // In that case, we will ensure that we allow at least one task to run when + // someone tries to run a task, even if we have run out of idle period at that + // point. This ensures that we never fail to produce a task to run if we + // claim we have a task ready to run. + bool mHasPendingEventsPromisedIdleEvent = false; - // Try to process one high priority runnable after each normal - // priority runnable. This gives the processing model HTML spec has for - // 'Update the rendering' in the case only vsync messages are in the - // secondary queue and prevents starving the normal queue. - bool mProcessHighPriorityQueue = false; - - // mIdlePeriod keeps track of the current idle period. If at any - // time the main event queue is empty, calling + // mIdlePeriod keeps track of the current idle period. Calling // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when // the current idle period will end. nsCOMPtr<nsIIdlePeriod> mIdlePeriod; - // Set to true if HasPendingEvents() has been called and returned true because - // of a pending idle event. This is used to remember to return that idle - // event from GetIdleEvent() to ensure that HasPendingEvents() never lies. - bool mHasPendingEventsPromisedIdleEvent = false; - - TimeStamp mInputHandlingStartTime; - - enum InputEventQueueState { - STATE_DISABLED, - STATE_FLUSHING, - STATE_SUSPEND, - STATE_ENABLED - }; - InputEventQueueState mInputQueueState = STATE_DISABLED; - - // If non-null, tells the end time of the idle period. - // Idle period starts when we get idle token from the parent process and - // ends when either there are no runnables in the event queues or - // mIdleToken < TimeStamp::Now() + // If non-null, this timestamp represents the end time of the idle period. An + // idle period starts when we get the idle token from the parent process and + // ends when either there are no more things we want to run at idle priority + // or mIdleToken < TimeStamp::Now(), so we have reached our idle deadline. TimeStamp mIdleToken; // The id of the last idle request to the cross-process idle scheduler. uint64_t mIdleRequestId = 0; + // If we're in a content process, we use mIdleScheduler to communicate with + // the parent process for purposes of cross-process idle tracking. RefPtr<ipc::IdleSchedulerChild> mIdleScheduler; + + // mIdleSchedulerInitialized is true if our mIdleScheduler has been + // initialized. It may be null even after initialiazation, in various + // situations. bool mIdleSchedulerInitialized = false; - // mActive tells whether the event queue is running tasks. + // mActive is true when the PrioritizedEventQueue or TaskController we are + // associated with is running tasks. bool mActive = true; }; } // namespace mozilla -#endif // mozilla_PrioritizedEventQueue_h +#endif // mozilla_IdlePeriodState_h
--- a/xpcom/threads/PrioritizedEventQueue.cpp +++ b/xpcom/threads/PrioritizedEventQueue.cpp @@ -2,43 +2,36 @@ /* 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 "PrioritizedEventQueue.h" #include "mozilla/EventQueue.h" #include "mozilla/ScopeExit.h" -#include "mozilla/StaticPrefs_idle_period.h" #include "mozilla/StaticPrefs_threads.h" #include "mozilla/ipc/IdleSchedulerChild.h" #include "nsThreadManager.h" #include "nsXPCOMPrivate.h" // for gXPCOMThreadsShutDown #include "InputEventStatistics.h" using namespace mozilla; -static uint64_t sIdleRequestCounter = 0; - PrioritizedEventQueue::PrioritizedEventQueue( - already_AddRefed<nsIIdlePeriod> aIdlePeriod) + already_AddRefed<nsIIdlePeriod>&& aIdlePeriod) : mHighQueue(MakeUnique<EventQueue>(EventQueuePriority::High)), mInputQueue(MakeUnique<EventQueue>(EventQueuePriority::Input)), mMediumHighQueue(MakeUnique<EventQueue>(EventQueuePriority::MediumHigh)), mNormalQueue(MakeUnique<EventQueue>(EventQueuePriority::Normal)), mDeferredTimersQueue( MakeUnique<EventQueue>(EventQueuePriority::DeferredTimers)), mIdleQueue(MakeUnique<EventQueue>(EventQueuePriority::Idle)), - mIdlePeriod(aIdlePeriod) {} + mIdlePeriodState(std::move(aIdlePeriod)) {} -PrioritizedEventQueue::~PrioritizedEventQueue() { - if (mIdleScheduler) { - mIdleScheduler->Disconnect(); - } -} +PrioritizedEventQueue::~PrioritizedEventQueue() = default; void PrioritizedEventQueue::PutEvent(already_AddRefed<nsIRunnable>&& aEvent, EventQueuePriority aPriority, const MutexAutoLock& aProofOfLock) { // Double check the priority with a QI. RefPtr<nsIRunnable> event(aEvent); EventQueuePriority priority = aPriority; @@ -70,63 +63,16 @@ void PrioritizedEventQueue::PutEvent(alr mIdleQueue->PutEvent(event.forget(), priority, aProofOfLock); break; case EventQueuePriority::Count: MOZ_CRASH("EventQueuePriority::Count isn't a valid priority"); break; } } -TimeStamp PrioritizedEventQueue::GetLocalIdleDeadline(bool& aShuttingDown) { - // If we are shutting down, we won't honor the idle period, and we will - // always process idle runnables. This will ensure that the idle queue - // gets exhausted at shutdown time to prevent intermittently leaking - // some runnables inside that queue and even worse potentially leaving - // some important cleanup work unfinished. - if (gXPCOMThreadsShutDown || - nsThreadManager::get().GetCurrentThread()->ShuttingDown()) { - aShuttingDown = true; - return TimeStamp::Now(); - } - - aShuttingDown = false; - TimeStamp idleDeadline; - { - // Releasing the lock temporarily since getting the idle period - // might need to lock the timer thread. Unlocking here might make - // us receive an event on the main queue, but we've committed to - // run an idle event anyhow. - MutexAutoUnlock unlock(*mMutex); - mIdlePeriod->GetIdlePeriodHint(&idleDeadline); - } - - // If HasPendingEvents() has been called and it has returned true because of - // pending idle events, there is a risk that we may decide here that we aren't - // idle and return null, in which case HasPendingEvents() has effectively - // lied. Since we can't go back and fix the past, we have to adjust what we - // do here and forcefully pick the idle queue task here. Note that this means - // that we are choosing to run a task from the idle queue when we would - // normally decide that we aren't in an idle period, but this can only happen - // if we fall out of the idle period in between the call to HasPendingEvents() - // and here, which should hopefully be quite rare. We are effectively - // choosing to prioritize the sanity of our API semantics over the optimal - // scheduling. - if (!mHasPendingEventsPromisedIdleEvent && - (!idleDeadline || idleDeadline < TimeStamp::Now())) { - return TimeStamp(); - } - if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) { - // If HasPendingEvents() has been called and it has returned true, but we're - // no longer in the idle period, we must return a valid timestamp to pretend - // that we are still in the idle period. - return TimeStamp::Now(); - } - return idleDeadline; -} - EventQueuePriority PrioritizedEventQueue::SelectQueue( bool aUpdateState, const MutexAutoLock& aProofOfLock) { size_t inputCount = mInputQueue->Count(aProofOfLock); if (aUpdateState && mInputQueueState == STATE_ENABLED && mInputHandlingStartTime.IsNull() && inputCount > 0) { mInputHandlingStartTime = InputEventStatistics::Get().GetInputHandlingStartTime(inputCount); @@ -203,23 +149,20 @@ already_AddRefed<nsIRunnable> Prioritize #ifndef RELEASE_OR_BETA // Clear mNextIdleDeadline so that it is possible to determine that // we're running an idle runnable in ProcessNextEvent. *mNextIdleDeadline = TimeStamp(); #endif EventQueuePriority queue = SelectQueue(true, aProofOfLock); auto guard = MakeScopeExit([&] { - mHasPendingEventsPromisedIdleEvent = false; + mIdlePeriodState.ForgetPendingTaskGuarantee(); if (queue != EventQueuePriority::Idle && queue != EventQueuePriority::DeferredTimers) { - EnsureIsActive(); - if (mIdleToken && mIdleToken < TimeStamp::Now()) { - ClearIdleToken(); - } + mIdlePeriodState.FlagNotIdle(*mMutex); } }); if (aPriority) { *aPriority = queue; } if (queue == EventQueuePriority::High) { @@ -250,40 +193,22 @@ already_AddRefed<nsIRunnable> Prioritize } // If we get here, then all queues except deferredtimers and idle are empty. MOZ_ASSERT(queue == EventQueuePriority::Idle || queue == EventQueuePriority::DeferredTimers); if (mIdleQueue->IsEmpty(aProofOfLock) && mDeferredTimersQueue->IsEmpty(aProofOfLock)) { - MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent); - EnsureIsPaused(); - ClearIdleToken(); + mIdlePeriodState.RanOutOfTasks(*mMutex); return nullptr; } - bool shuttingDown; - TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown); - if (!localIdleDeadline) { - EnsureIsPaused(); - ClearIdleToken(); - return nullptr; - } - - TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown - ? localIdleDeadline - : GetIdleToken(localIdleDeadline); + TimeStamp idleDeadline = mIdlePeriodState.GetDeadlineForIdleTask(*mMutex); if (!idleDeadline) { - EnsureIsPaused(); - - // Don't call ClearIdleToken() here, since we may have a pending - // request already. - MutexAutoUnlock unlock(*mMutex); - RequestIdleToken(localIdleDeadline); return nullptr; } nsCOMPtr<nsIRunnable> event = mDeferredTimersQueue->GetEvent(aPriority, aProofOfLock); if (!event) { event = mIdleQueue->GetEvent(aPriority, aProofOfLock); } @@ -295,42 +220,39 @@ already_AddRefed<nsIRunnable> Prioritize #ifndef RELEASE_OR_BETA // Store the next idle deadline to be able to determine budget use // in ProcessNextEvent. *mNextIdleDeadline = idleDeadline; #endif } - EnsureIsActive(); return event.forget(); } void PrioritizedEventQueue::DidRunEvent(const MutexAutoLock& aProofOfLock) { if (IsEmpty(aProofOfLock)) { - if (IsActive()) { - SetPaused(); - } - ClearIdleToken(); + // Certainly no more idle tasks. + mIdlePeriodState.RanOutOfTasks(*mMutex); } } bool PrioritizedEventQueue::IsEmpty(const MutexAutoLock& aProofOfLock) { // Just check IsEmpty() on the sub-queues. Don't bother checking the idle // deadline since that only determines whether an idle event is ready or not. return mHighQueue->IsEmpty(aProofOfLock) && mInputQueue->IsEmpty(aProofOfLock) && mMediumHighQueue->IsEmpty(aProofOfLock) && mNormalQueue->IsEmpty(aProofOfLock) && mDeferredTimersQueue->IsEmpty(aProofOfLock) && mIdleQueue->IsEmpty(aProofOfLock); } bool PrioritizedEventQueue::HasReadyEvent(const MutexAutoLock& aProofOfLock) { - mHasPendingEventsPromisedIdleEvent = false; + mIdlePeriodState.ForgetPendingTaskGuarantee(); EventQueuePriority queue = SelectQueue(false, aProofOfLock); if (queue == EventQueuePriority::High) { return mHighQueue->HasReadyEvent(aProofOfLock); } else if (queue == EventQueuePriority::Input) { return mInputQueue->HasReadyEvent(aProofOfLock); } else if (queue == EventQueuePriority::MediumHigh) { @@ -344,27 +266,21 @@ bool PrioritizedEventQueue::HasReadyEven // If we get here, then both the high and normal queues are empty. if (mDeferredTimersQueue->IsEmpty(aProofOfLock) && mIdleQueue->IsEmpty(aProofOfLock)) { return false; } - bool shuttingDown; - TimeStamp localIdleDeadline = GetLocalIdleDeadline(shuttingDown); - if (localIdleDeadline) { - TimeStamp idleDeadline = mHasPendingEventsPromisedIdleEvent || shuttingDown - ? localIdleDeadline - : GetIdleToken(localIdleDeadline); - if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) || - mIdleQueue->HasReadyEvent(aProofOfLock))) { - mHasPendingEventsPromisedIdleEvent = true; - return true; - } + TimeStamp idleDeadline = mIdlePeriodState.PeekIdleDeadline(*mMutex); + if (idleDeadline && (mDeferredTimersQueue->HasReadyEvent(aProofOfLock) || + mIdleQueue->HasReadyEvent(aProofOfLock))) { + mIdlePeriodState.EnforcePendingTaskGuarantee(); + return true; } return false; } bool PrioritizedEventQueue::HasPendingHighPriorityEvents( const MutexAutoLock& aProofOfLock) { return !mHighQueue->IsEmpty(aProofOfLock); @@ -396,93 +312,8 @@ void PrioritizedEventQueue::SuspendInput mInputQueueState = STATE_SUSPEND; } void PrioritizedEventQueue::ResumeInputEventPrioritization( const MutexAutoLock& aProofOfLock) { MOZ_ASSERT(mInputQueueState == STATE_SUSPEND); mInputQueueState = STATE_ENABLED; } - -mozilla::TimeStamp PrioritizedEventQueue::GetIdleToken( - TimeStamp aLocalIdlePeriodHint) { - if (XRE_IsParentProcess()) { - return aLocalIdlePeriodHint; - } - if (mIdleToken) { - TimeStamp now = TimeStamp::Now(); - if (mIdleToken < now) { - ClearIdleToken(); - return mIdleToken; - } - return mIdleToken < aLocalIdlePeriodHint ? mIdleToken - : aLocalIdlePeriodHint; - } - return TimeStamp(); -} - -void PrioritizedEventQueue::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) { - MOZ_ASSERT(!mActive); - - if (!mIdleSchedulerInitialized) { - mIdleSchedulerInitialized = true; - if (StaticPrefs::idle_period_cross_process_scheduling() && - XRE_IsContentProcess() && NS_IsMainThread() && - // Disable when recording/replaying, as IdleSchedulerChild uses mutable - // shared memory which needs special handling. - !recordreplay::IsRecordingOrReplaying()) { - // For now cross-process idle scheduler is supported only on the main - // threads of the child processes. - mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler(); - if (mIdleScheduler) { - mIdleScheduler->Init(this); - } - } - } - - if (mIdleScheduler && !mIdleRequestId) { - TimeStamp now = TimeStamp::Now(); - if (aLocalIdlePeriodHint <= now) { - return; - } - - mIdleRequestId = ++sIdleRequestCounter; - mIdleScheduler->SendRequestIdleTime(mIdleRequestId, - aLocalIdlePeriodHint - now); - } -} - -void PrioritizedEventQueue::SetIdleToken(uint64_t aId, TimeDuration aDuration) { - if (mIdleRequestId == aId) { - mIdleToken = TimeStamp::Now() + aDuration; - } -} - -void PrioritizedEventQueue::SetActive() { - MOZ_ASSERT(!mActive); - if (mIdleScheduler) { - mIdleScheduler->SetActive(); - } - mActive = true; -} - -void PrioritizedEventQueue::SetPaused() { - MOZ_ASSERT(mActive); - if (mIdleScheduler && mIdleScheduler->SetPaused()) { - MutexAutoUnlock unlock(*mMutex); - // We may have gotten a free cpu core for running idle tasks. - // We don't try to catch the case when there are prioritized processes - // running. - mIdleScheduler->SendSchedule(); - } - mActive = false; -} - -void PrioritizedEventQueue::ClearIdleToken() { - if (mIdleRequestId) { - if (mIdleScheduler) { - MutexAutoUnlock unlock(*mMutex); - mIdleScheduler->SendIdleTimeUsed(mIdleRequestId); - } - mIdleRequestId = 0; - mIdleToken = TimeStamp(); - } -}
--- a/xpcom/threads/PrioritizedEventQueue.h +++ b/xpcom/threads/PrioritizedEventQueue.h @@ -4,22 +4,23 @@ * 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_PrioritizedEventQueue_h #define mozilla_PrioritizedEventQueue_h #include "mozilla/AbstractEventQueue.h" #include "mozilla/EventQueue.h" +#include "mozilla/IdlePeriodState.h" #include "mozilla/TimeStamp.h" #include "mozilla/TypeTraits.h" #include "mozilla/UniquePtr.h" #include "nsCOMPtr.h" -#include "nsIIdlePeriod.h" +class nsIIdlePeriod; class nsIRunnable; namespace mozilla { namespace ipc { class IdleSchedulerChild; } // This AbstractEventQueue implementation has one queue for each @@ -38,17 +39,17 @@ class IdleSchedulerChild; // and normal-priority events available, we interleave popping from the // normal and high queues. // - We do not select events from the idle queue if the current idle period // is almost over. class PrioritizedEventQueue final : public AbstractEventQueue { public: static const bool SupportsPrioritization = true; - explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod> aIdlePeriod); + explicit PrioritizedEventQueue(already_AddRefed<nsIIdlePeriod>&& aIdlePeriod); virtual ~PrioritizedEventQueue(); void PutEvent(already_AddRefed<nsIRunnable>&& aEvent, EventQueuePriority aPriority, const MutexAutoLock& aProofOfLock) final; already_AddRefed<nsIRunnable> GetEvent( EventQueuePriority* aPriority, const MutexAutoLock& aProofOfLock) final; @@ -85,68 +86,25 @@ class PrioritizedEventQueue final : publ n += mHighQueue->SizeOfIncludingThis(aMallocSizeOf); n += mInputQueue->SizeOfIncludingThis(aMallocSizeOf); n += mMediumHighQueue->SizeOfIncludingThis(aMallocSizeOf); n += mNormalQueue->SizeOfIncludingThis(aMallocSizeOf); n += mDeferredTimersQueue->SizeOfIncludingThis(aMallocSizeOf); n += mIdleQueue->SizeOfIncludingThis(aMallocSizeOf); - if (mIdlePeriod) { - n += aMallocSizeOf(mIdlePeriod); - } + n += mIdlePeriodState.SizeOfExcludingThis(aMallocSizeOf); return n; } - void SetIdleToken(uint64_t aId, TimeDuration aDuration); - - bool IsActive() { return mActive; } - - void EnsureIsActive() { - if (!mActive) { - SetActive(); - } - } - - void EnsureIsPaused() { - if (mActive) { - SetPaused(); - } - } - private: EventQueuePriority SelectQueue(bool aUpdateState, const MutexAutoLock& aProofOfLock); - // Returns a null TimeStamp if we're not in the idle period. - mozilla::TimeStamp GetLocalIdleDeadline(bool& aShuttingDown); - - // SetActive should be called when the event queue is running any type of - // tasks. - void SetActive(); - // SetPaused should be called once the event queue doesn't have more - // tasks to process, or is waiting for the idle token. - void SetPaused(); - - // Gets the idle token, which is the end time of the idle period. - TimeStamp GetIdleToken(TimeStamp aLocalIdlePeriodHint); - - // In case of child processes, requests idle time from the cross-process - // idle scheduler. - void RequestIdleToken(TimeStamp aLocalIdlePeriodHint); - - // Returns true if the event queue either is waiting for an idle token - // from the idle scheduler or has one. - bool HasIdleRequest() { return mIdleRequestId != 0; } - - // Mark that the event queue doesn't have idle time to use, nor is expecting - // to get idle token from the idle scheduler. - void ClearIdleToken(); - UniquePtr<EventQueue> mHighQueue; UniquePtr<EventQueue> mInputQueue; UniquePtr<EventQueue> mMediumHighQueue; UniquePtr<EventQueue> mNormalQueue; UniquePtr<EventQueue> mDeferredTimersQueue; UniquePtr<EventQueue> mIdleQueue; // We need to drop the queue mutex when checking the idle deadline, so we keep @@ -160,48 +118,25 @@ class PrioritizedEventQueue final : publ #endif // Try to process one high priority runnable after each normal // priority runnable. This gives the processing model HTML spec has for // 'Update the rendering' in the case only vsync messages are in the // secondary queue and prevents starving the normal queue. bool mProcessHighPriorityQueue = false; - // mIdlePeriod keeps track of the current idle period. If at any - // time the main event queue is empty, calling - // mIdlePeriod->GetIdlePeriodHint() will give an estimate of when - // the current idle period will end. - nsCOMPtr<nsIIdlePeriod> mIdlePeriod; - - // Set to true if HasPendingEvents() has been called and returned true because - // of a pending idle event. This is used to remember to return that idle - // event from GetIdleEvent() to ensure that HasPendingEvents() never lies. - bool mHasPendingEventsPromisedIdleEvent = false; - TimeStamp mInputHandlingStartTime; enum InputEventQueueState { STATE_DISABLED, STATE_FLUSHING, STATE_SUSPEND, STATE_ENABLED }; InputEventQueueState mInputQueueState = STATE_DISABLED; - // If non-null, tells the end time of the idle period. - // Idle period starts when we get idle token from the parent process and - // ends when either there are no runnables in the event queues or - // mIdleToken < TimeStamp::Now() - TimeStamp mIdleToken; - - // The id of the last idle request to the cross-process idle scheduler. - uint64_t mIdleRequestId = 0; - - RefPtr<ipc::IdleSchedulerChild> mIdleScheduler; - bool mIdleSchedulerInitialized = false; - - // mActive tells whether the event queue is running tasks. - bool mActive = true; + // Tracking of our idle state of various sorts. + IdlePeriodState mIdlePeriodState; }; } // namespace mozilla #endif // mozilla_PrioritizedEventQueue_h
--- a/xpcom/threads/moz.build +++ b/xpcom/threads/moz.build @@ -43,16 +43,17 @@ EXPORTS.mozilla += [ 'AbstractEventQueue.h', 'AbstractThread.h', 'BlockingResourceBase.h', 'CondVar.h', 'CPUUsageWatcher.h', 'DataMutex.h', 'DeadlockDetector.h', 'EventQueue.h', + 'IdlePeriodState.h', 'IdleTaskRunner.h', 'LazyIdleThread.h', 'MainThreadIdlePeriod.h', 'Monitor.h', 'MozPromise.h', 'MozPromiseInlines.h', 'Mutex.h', 'PerformanceCounter.h', @@ -81,16 +82,17 @@ SOURCES += [ 'ThreadDelay.cpp', ] UNIFIED_SOURCES += [ 'AbstractThread.cpp', 'BlockingResourceBase.cpp', 'CPUUsageWatcher.cpp', 'EventQueue.cpp', + 'IdlePeriodState.cpp', 'InputEventStatistics.cpp', 'LazyIdleThread.cpp', 'MainThreadIdlePeriod.cpp', 'nsEnvironment.cpp', 'nsMemoryPressure.cpp', 'nsProcessCommon.cpp', 'nsProxyRelease.cpp', 'nsThread.cpp',