diff --git a/dom/base/StructuredCloneHolder.cpp b/dom/base/StructuredCloneHolder.cpp index 27cb748c7..8a93a48ac 100644 --- a/dom/base/StructuredCloneHolder.cpp +++ b/dom/base/StructuredCloneHolder.cpp @@ -1075,9 +1075,9 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, return ReadFormData(aCx, aReader, aIndex, this); } - if (aTag == SCTAG_DOM_IMAGEBITMAP) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_IMAGEBITMAP && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { // Get the current global object. // This can be null. @@ -1087,7 +1087,9 @@ StructuredCloneHolder::CustomReadHandler(JSContext* aCx, parent, GetSurfaces(), aIndex); } - if (aTag == SCTAG_DOM_WASM) { + if (aTag == SCTAG_DOM_WASM && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { return ReadWasmModule(aCx, aIndex, this); } @@ -1202,9 +1204,9 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx, return true; } - if (aTag == SCTAG_DOM_CANVAS) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_CANVAS && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); OffscreenCanvasCloneData* data = static_cast(aContent); @@ -1222,9 +1224,9 @@ StructuredCloneHolder::CustomReadTransferHandler(JSContext* aCx, return true; } - if (aTag == SCTAG_DOM_IMAGEBITMAP) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_IMAGEBITMAP && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); ImageBitmapCloneData* data = static_cast(aContent); @@ -1328,9 +1330,9 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag, return; } - if (aTag == SCTAG_DOM_CANVAS) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_CANVAS && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); OffscreenCanvasCloneData* data = static_cast(aContent); @@ -1338,9 +1340,9 @@ StructuredCloneHolder::CustomFreeTransferHandler(uint32_t aTag, return; } - if (aTag == SCTAG_DOM_IMAGEBITMAP) { - MOZ_ASSERT(mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || - mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread); + if (aTag == SCTAG_DOM_IMAGEBITMAP && + (mStructuredCloneScope == StructuredCloneScope::SameProcessSameThread || + mStructuredCloneScope == StructuredCloneScope::SameProcessDifferentThread)) { MOZ_ASSERT(aContent); ImageBitmapCloneData* data = static_cast(aContent); diff --git a/dom/base/nsFrameMessageManager.cpp b/dom/base/nsFrameMessageManager.cpp index 7d8de8df7..071e61b4b 100644 --- a/dom/base/nsFrameMessageManager.cpp +++ b/dom/base/nsFrameMessageManager.cpp @@ -1065,6 +1065,7 @@ nsFrameMessageManager::ReceiveMessage(nsISupports* aTarget, if (aRetVal) { ErrorResult rv; StructuredCloneData* data = aRetVal->AppendElement(); + data->InitScope(JS::StructuredCloneScope::DifferentProcess); data->Write(cx, rval, rv); if (NS_WARN_IF(rv.Failed())) { aRetVal->RemoveElementAt(aRetVal->Length() - 1); diff --git a/dom/base/nsStructuredCloneContainer.cpp b/dom/base/nsStructuredCloneContainer.cpp index 8c2cdc091..ea2d38bc8 100644 --- a/dom/base/nsStructuredCloneContainer.cpp +++ b/dom/base/nsStructuredCloneContainer.cpp @@ -137,7 +137,7 @@ nsStructuredCloneContainer::GetDataAsBase64(nsAString &aOut) return NS_ERROR_FAILURE; } - auto iter = Data().Iter(); + auto iter = Data().Start(); size_t size = Data().Size(); nsAutoCString binaryData; binaryData.SetLength(size); diff --git a/dom/canvas/CanvasRenderingContext2D.cpp b/dom/canvas/CanvasRenderingContext2D.cpp index a503f2270..949d426dd 100644 --- a/dom/canvas/CanvasRenderingContext2D.cpp +++ b/dom/canvas/CanvasRenderingContext2D.cpp @@ -1914,8 +1914,6 @@ CanvasRenderingContext2D::GetHeight() const NS_IMETHODIMP CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight) { - ClearTarget(); - // Zero sized surfaces can cause problems. mZero = false; if (aHeight == 0) { @@ -1926,14 +1924,14 @@ CanvasRenderingContext2D::SetDimensions(int32_t aWidth, int32_t aHeight) aWidth = 1; mZero = true; } - mWidth = aWidth; - mHeight = aHeight; + + ClearTarget(aWidth, aHeight); return NS_OK; } void -CanvasRenderingContext2D::ClearTarget() +CanvasRenderingContext2D::ClearTarget(int32_t aWidth, int32_t aHeight) { Reset(); @@ -1941,6 +1939,12 @@ CanvasRenderingContext2D::ClearTarget() SetInitialState(); + // Update dimensions only if new (strictly positive) values were passed. + if (aWidth > 0 && aHeight > 0) { + mWidth = aWidth; + mHeight = aHeight; + } + // For vertical writing-mode, unless text-orientation is sideways, // we'll modify the initial value of textBaseline to 'middle'. RefPtr canvasStyle; diff --git a/dom/canvas/CanvasRenderingContext2D.h b/dom/canvas/CanvasRenderingContext2D.h index 10700bc3a..9bbf25767 100644 --- a/dom/canvas/CanvasRenderingContext2D.h +++ b/dom/canvas/CanvasRenderingContext2D.h @@ -673,8 +673,11 @@ protected: /** * Disposes an old target and prepares to lazily create a new target. + * + * Parameters are the new dimensions to be used, or if either is negative, + * existing dimensions will be left unchanged. */ - void ClearTarget(); + void ClearTarget(int32_t aWidth = -1, int32_t aHeight = -1); /* * Returns the target to the buffer provider. i.e. this will queue a frame for diff --git a/dom/filesystem/tests/test_webkitdirectory.html b/dom/filesystem/tests/test_webkitdirectory.html index 825f5e8fb..591619e45 100644 --- a/dom/filesystem/tests/test_webkitdirectory.html +++ b/dom/filesystem/tests/test_webkitdirectory.html @@ -152,6 +152,7 @@ function test_changeDataWhileWorking() { function test_setup() { SpecialPowers.pushPrefEnv({"set": [["dom.input.dirpicker", true], + ["dom.filesystem.pathcheck.disabled", true], ["dom.webkitBlink.dirPicker.enabled", true]]}, next); } diff --git a/dom/html/HTMLInputElement.cpp b/dom/html/HTMLInputElement.cpp index b1d82e3ee..c8a09c4e8 100644 --- a/dom/html/HTMLInputElement.cpp +++ b/dom/html/HTMLInputElement.cpp @@ -3597,7 +3597,8 @@ HTMLInputElement::Focus(ErrorResult& aError) nsNumberControlFrame* numberControlFrame = do_QueryFrame(GetPrimaryFrame()); if (numberControlFrame) { - HTMLInputElement* textControl = numberControlFrame->GetAnonTextControl(); + RefPtr textControl = + numberControlFrame->GetAnonTextControl(); if (textControl) { textControl->Focus(aError); return; diff --git a/dom/indexedDB/ActorsParent.cpp b/dom/indexedDB/ActorsParent.cpp index 1596132e9..210a4a5b5 100644 --- a/dom/indexedDB/ActorsParent.cpp +++ b/dom/indexedDB/ActorsParent.cpp @@ -8410,12 +8410,12 @@ class ObjectStoreAddOrPutRequestOp::SCInputStream final : public nsIInputStream { const JSStructuredCloneData& mData; - JSStructuredCloneData::IterImpl mIter; + JSStructuredCloneData::Iterator mIter; public: explicit SCInputStream(const JSStructuredCloneData& aData) : mData(aData) - , mIter(aData.Iter()) + , mIter(aData.Start()) { } private: @@ -19622,7 +19622,7 @@ UpgradeFileIdsFunction::OnFunctionCall(mozIStorageValueArray* aArguments, return NS_ERROR_UNEXPECTED; } - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); DatabaseOperationBase::GetStructuredCloneReadInfoFromValueArray(aArguments, 1, 0, @@ -19827,7 +19827,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromBlob( return NS_ERROR_FILE_CORRUPTED; } - if (!aInfo->mData.WriteBytes(uncompressedBuffer, uncompressed.Length())) { + if (!aInfo->mData.AppendBytes(uncompressedBuffer, uncompressed.Length())) { return NS_ERROR_OUT_OF_MEMORY; } @@ -19913,7 +19913,7 @@ DatabaseOperationBase::GetStructuredCloneReadInfoFromExternalBlob( break; } - if (NS_WARN_IF(!aInfo->mData.WriteBytes(buffer, numRead))) { + if (NS_WARN_IF(!aInfo->mData.AppendBytes(buffer, numRead))) { rv = NS_ERROR_OUT_OF_MEMORY; break; } @@ -25280,7 +25280,7 @@ UpdateIndexDataValuesFunction::OnFunctionCall(mozIStorageValueArray* aValues, } #endif - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); nsresult rv = GetStructuredCloneReadInfoFromValueArray(aValues, /* aDataIndex */ 3, @@ -26489,18 +26489,9 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) char keyPropBuffer[keyPropSize]; LittleEndian::writeUint64(keyPropBuffer, keyPropValue); - auto iter = cloneData.Iter(); - DebugOnly result = - iter.AdvanceAcrossSegments(cloneData, cloneInfo.offsetToKeyProp()); - MOZ_ASSERT(result); - - for (char index : keyPropBuffer) { - char* keyPropPointer = iter.Data(); - *keyPropPointer = index; - - result = iter.AdvanceAcrossSegments(cloneData, 1); - MOZ_ASSERT(result); - } + auto iter = cloneData.Start(); + MOZ_ALWAYS_TRUE(cloneData.Advance(iter, cloneInfo.offsetToKeyProp())); + MOZ_ALWAYS_TRUE(cloneData.UpdateBytes(iter, keyPropBuffer, keyPropSize)); } } @@ -26526,7 +26517,7 @@ ObjectStoreAddOrPutRequestOp::DoDatabaseWork(DatabaseConnection* aConnection) } else { nsCString flatCloneData; flatCloneData.SetLength(cloneDataSize); - auto iter = cloneData.Iter(); + auto iter = cloneData.Start(); cloneData.ReadBytes(iter, flatCloneData.BeginWriting(), cloneDataSize); // Compress the bytes before adding into the database. @@ -26783,7 +26774,7 @@ SCInputStream::ReadSegments(nsWriteSegmentFun aWriter, *_retval += count; aCount -= count; - mIter.Advance(mData, count); + mData.Advance(mIter, count); } return NS_OK; @@ -27972,7 +27963,7 @@ CursorOpBase::PopulateResponseFromStatement( switch (mCursor->mType) { case OpenCursorParams::TObjectStoreOpenCursorParams: { - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); rv = GetStructuredCloneReadInfoFromStatement(aStmt, 2, 1, @@ -28020,7 +28011,7 @@ CursorOpBase::PopulateResponseFromStatement( return rv; } - StructuredCloneReadInfo cloneInfo; + StructuredCloneReadInfo cloneInfo(JS::StructuredCloneScope::DifferentProcess); rv = GetStructuredCloneReadInfoFromStatement(aStmt, 4, 3, diff --git a/dom/indexedDB/IDBObjectStore.cpp b/dom/indexedDB/IDBObjectStore.cpp index c7a342872..ac5c8c61a 100644 --- a/dom/indexedDB/IDBObjectStore.cpp +++ b/dom/indexedDB/IDBObjectStore.cpp @@ -67,7 +67,7 @@ struct IDBObjectStore::StructuredCloneWriteInfo uint64_t mOffsetToKeyProp; explicit StructuredCloneWriteInfo(IDBDatabase* aDatabase) - : mCloneBuffer(JS::StructuredCloneScope::SameProcessSameThread, nullptr, + : mCloneBuffer(JS::StructuredCloneScope::DifferentProcessForIndexedDB, nullptr, nullptr) , mDatabase(aDatabase) , mOffsetToKeyProp(0) @@ -1225,7 +1225,7 @@ IDBObjectStore::DeserializeValue(JSContext* aCx, // FIXME: Consider to use StructuredCloneHolder here and in other // deserializing methods. if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, - JS::StructuredCloneScope::SameProcessSameThread, + JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, &callbacks, &aCloneReadInfo)) { return false; } @@ -1258,7 +1258,7 @@ IDBObjectStore::DeserializeIndexValue(JSContext* aCx, }; if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, - JS::StructuredCloneScope::SameProcessSameThread, + JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, &callbacks, &aCloneReadInfo)) { return false; } @@ -1294,7 +1294,7 @@ IDBObjectStore::DeserializeUpgradeValue(JSContext* aCx, }; if (!JS_ReadStructuredClone(aCx, aCloneReadInfo.mData, JS_STRUCTURED_CLONE_VERSION, - JS::StructuredCloneScope::SameProcessSameThread, + JS::StructuredCloneScope::DifferentProcessForIndexedDB, aValue, &callbacks, &aCloneReadInfo)) { return false; } diff --git a/dom/indexedDB/IndexedDatabase.h b/dom/indexedDB/IndexedDatabase.h index b0c4cb877..b3c6ab725 100644 --- a/dom/indexedDB/IndexedDatabase.h +++ b/dom/indexedDB/IndexedDatabase.h @@ -64,6 +64,10 @@ struct StructuredCloneReadInfo IDBDatabase* mDatabase; bool mHasPreprocessInfo; + // In IndexedDatabaseInlines.h + inline explicit + StructuredCloneReadInfo(JS::StructuredCloneScope aScope); + // In IndexedDatabaseInlines.h inline StructuredCloneReadInfo(); diff --git a/dom/indexedDB/IndexedDatabaseInlines.h b/dom/indexedDB/IndexedDatabaseInlines.h index 830c2f110..8c34a81dd 100644 --- a/dom/indexedDB/IndexedDatabaseInlines.h +++ b/dom/indexedDB/IndexedDatabaseInlines.h @@ -45,13 +45,20 @@ StructuredCloneFile::operator==(const StructuredCloneFile& aOther) const } inline -StructuredCloneReadInfo::StructuredCloneReadInfo() - : mDatabase(nullptr) +StructuredCloneReadInfo::StructuredCloneReadInfo(JS::StructuredCloneScope aScope) + : mData(aScope) + , mDatabase(nullptr) , mHasPreprocessInfo(false) { MOZ_COUNT_CTOR(StructuredCloneReadInfo); } +inline +StructuredCloneReadInfo::StructuredCloneReadInfo() + : StructuredCloneReadInfo(JS::StructuredCloneScope::DifferentProcessForIndexedDB) +{ +} + inline StructuredCloneReadInfo::StructuredCloneReadInfo( StructuredCloneReadInfo&& aCloneReadInfo) diff --git a/dom/ipc/ContentParent.cpp b/dom/ipc/ContentParent.cpp index be99005d0..c18f35130 100644 --- a/dom/ipc/ContentParent.cpp +++ b/dom/ipc/ContentParent.cpp @@ -4616,6 +4616,14 @@ ContentParent::RecvGetFilesRequest(const nsID& aUUID, { MOZ_ASSERT(!mGetFilesPendingRequests.GetWeak(aUUID)); + if (!mozilla::Preferences::GetBool("dom.filesystem.pathcheck.disabled", false)) { + RefPtr fss = FileSystemSecurity::Get(); + if (NS_WARN_IF(!fss || + !fss->ContentProcessHasAccessTo(ChildID(), aDirectoryPath))) { + return IPC_FAIL_NO_REASON(this); + } + } + ErrorResult rv; RefPtr helper = GetFilesHelperParent::Create(aUUID, aDirectoryPath, aRecursiveFlag, this, diff --git a/dom/ipc/StructuredCloneData.cpp b/dom/ipc/StructuredCloneData.cpp index 13ca39118..75f3635b0 100644 --- a/dom/ipc/StructuredCloneData.cpp +++ b/dom/ipc/StructuredCloneData.cpp @@ -90,7 +90,7 @@ StructuredCloneData::Write(JSContext* aCx, return; } - JSStructuredCloneData data; + JSStructuredCloneData data(mBuffer->scope()); mBuffer->abandon(); mBuffer->steal(&data); mBuffer = nullptr; @@ -197,10 +197,10 @@ BuildClonedMessageData(typename ParentManagerTraits::Conc ClonedMessageData& aClonedData) { SerializedStructuredCloneBuffer& buffer = aClonedData.data(); - auto iter = aData.Data().Iter(); + auto iter = aData.Data().Start(); size_t size = aData.Data().Size(); bool success; - buffer.data = aData.Data().Borrow(iter, size, &success); + buffer.data = aData.Data().Borrow(iter, size, &success); if (NS_WARN_IF(!success)) { return false; } @@ -443,7 +443,7 @@ StructuredCloneData::ReadIPCParams(const IPC::Message* aMsg, PickleIterator* aIter) { MOZ_ASSERT(!mInitialized); - JSStructuredCloneData data; + JSStructuredCloneData data(JS::StructuredCloneScope::DifferentProcess); if (!ReadParam(aMsg, aIter, &data)) { return false; } diff --git a/dom/ipc/StructuredCloneData.h b/dom/ipc/StructuredCloneData.h index 847f8bd20..ee2d21977 100644 --- a/dom/ipc/StructuredCloneData.h +++ b/dom/ipc/StructuredCloneData.h @@ -52,8 +52,8 @@ public: static already_AddRefed CreateFromExternalData(const char* aData, size_t aDataLength) { - JSStructuredCloneData buf; - buf.WriteBytes(aData, aDataLength); + JSStructuredCloneData buf(JS::StructuredCloneScope::DifferentProcess); + buf.AppendBytes(aData, aDataLength); RefPtr sharedData = new SharedJSAllocatedData(Move(buf)); return sharedData.forget(); @@ -62,12 +62,8 @@ public: static already_AddRefed CreateFromExternalData(const JSStructuredCloneData& aData) { - JSStructuredCloneData buf; - auto iter = aData.Iter(); - while (!iter.Done()) { - buf.WriteBytes(iter.Data(), iter.RemainingInSegment()); - iter.Advance(aData, iter.RemainingInSegment()); - } + JSStructuredCloneData buf(aData.scope()); + buf.Append(aData); RefPtr sharedData = new SharedJSAllocatedData(Move(buf)); return sharedData.forget(); @@ -229,10 +225,9 @@ public: // StructuredCloneData instance is destroyed before aData is destroyed. bool UseExternalData(const JSStructuredCloneData& aData) { - auto iter = aData.Iter(); + auto iter = aData.Start(); bool success = false; - mExternalData = - aData.Borrow(iter, aData.Size(), &success); + mExternalData = aData.Borrow(iter, aData.Size(), &success); mInitialized = true; return success; } @@ -261,6 +256,11 @@ public: return mSharedData ? mSharedData->Data() : mExternalData; } + void InitScope(JS::StructuredCloneScope aScope) + { + Data().initScope(aScope); + } + size_t DataLength() const { return mSharedData ? mSharedData->DataLength() : mExternalData.Size(); @@ -285,6 +285,7 @@ protected: : StructuredCloneHolder(StructuredCloneHolder::CloningSupported, aSupportsTransferring, StructuredCloneHolder::StructuredCloneScope::DifferentProcess) + , mExternalData(StructuredCloneHolder::StructuredCloneScope::DifferentProcess) , mInitialized(false) {} diff --git a/dom/media/MediaStreamTrack.cpp b/dom/media/MediaStreamTrack.cpp index 01f198a93..731ba34ef 100644 --- a/dom/media/MediaStreamTrack.cpp +++ b/dom/media/MediaStreamTrack.cpp @@ -168,11 +168,15 @@ MediaStreamTrack::Destroy() mPrincipalHandleListener->Forget(); mPrincipalHandleListener = nullptr; } - for (auto l : mTrackListeners) { - RemoveListener(l); + // Remove all listeners -- avoid iterating over the list we're removing from + const nsTArray> trackListeners(mTrackListeners); + for (auto listener : trackListeners) { + RemoveListener(listener); } - for (auto l : mDirectTrackListeners) { - RemoveDirectListener(l); + // Do the same as above for direct listeners + const nsTArray> directTrackListeners(mDirectTrackListeners); + for (auto listener : directTrackListeners) { + RemoveDirectListener(listener); } } diff --git a/dom/media/systemservices/CamerasChild.cpp b/dom/media/systemservices/CamerasChild.cpp index b95588129..62938a0f5 100644 --- a/dom/media/systemservices/CamerasChild.cpp +++ b/dom/media/systemservices/CamerasChild.cpp @@ -34,7 +34,9 @@ CamerasSingleton::CamerasSingleton() : mCamerasMutex("CamerasSingleton::mCamerasMutex"), mCameras(nullptr), mCamerasChildThread(nullptr), - mFakeDeviceChangeEventThread(nullptr) { + mFakeDeviceChangeEventThread(nullptr), + mInShutdown(false) +{ LOG(("CamerasSingleton: %p", this)); } @@ -284,6 +286,7 @@ CamerasChild::NumberOfCapabilities(CaptureEngine aCapEngine, LOG((__PRETTY_FUNCTION__)); LOG(("NumberOfCapabilities for %s", deviceUniqueIdUTF8)); nsCString unique_id(deviceUniqueIdUTF8); + RefPtr deathGrip = this; nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod (this, &CamerasChild::SendNumberOfCapabilities, aCapEngine, unique_id); @@ -320,6 +323,7 @@ int CamerasChild::EnsureInitialized(CaptureEngine aCapEngine) { LOG((__PRETTY_FUNCTION__)); + RefPtr deathGrip = this; nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod (this, &CamerasChild::SendEnsureInitialized, aCapEngine); @@ -335,6 +339,7 @@ CamerasChild::GetCaptureCapability(CaptureEngine aCapEngine, webrtc::VideoCaptureCapability& capability) { LOG(("GetCaptureCapability: %s %d", unique_idUTF8, capability_number)); + RefPtr deathGrip = this; nsCString unique_id(unique_idUTF8); nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod @@ -373,6 +378,7 @@ CamerasChild::GetCaptureDevice(CaptureEngine aCapEngine, bool* scary) { LOG((__PRETTY_FUNCTION__)); + RefPtr deathGrip = this; nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod (this, &CamerasChild::SendGetCaptureDevice, aCapEngine, list_number); @@ -412,6 +418,7 @@ CamerasChild::AllocateCaptureDevice(CaptureEngine aCapEngine, const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { LOG((__PRETTY_FUNCTION__)); + RefPtr deathGrip = this; nsCString unique_id(unique_idUTF8); nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod @@ -442,6 +449,7 @@ CamerasChild::ReleaseCaptureDevice(CaptureEngine aCapEngine, const int capture_id) { LOG((__PRETTY_FUNCTION__)); + RefPtr deathGrip = this; nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod (this, &CamerasChild::SendReleaseCaptureDevice, aCapEngine, capture_id); @@ -489,6 +497,7 @@ CamerasChild::StartCapture(CaptureEngine aCapEngine, webrtcCaps.rawType, webrtcCaps.codecType, webrtcCaps.interlaced); + RefPtr deathGrip = this; nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod (this, &CamerasChild::SendStartCapture, aCapEngine, capture_id, capCap); @@ -500,6 +509,7 @@ int CamerasChild::StopCapture(CaptureEngine aCapEngine, const int capture_id) { LOG((__PRETTY_FUNCTION__)); + RefPtr deathGrip = this; nsCOMPtr runnable = mozilla::NewNonOwningRunnableMethod (this, &CamerasChild::SendStopCapture, aCapEngine, capture_id); @@ -565,6 +575,7 @@ CamerasChild::ShutdownParent() // Delete the parent actor. // CamerasChild (this) will remain alive and is only deleted by the // IPC layer when SendAllDone returns. + RefPtr deathGrip = this; nsCOMPtr deleteRunnable = mozilla::NewNonOwningRunnableMethod(this, &CamerasChild::SendAllDone); CamerasSingleton::Thread()->Dispatch(deleteRunnable, NS_DISPATCH_NORMAL); @@ -687,7 +698,7 @@ CamerasChild::~CamerasChild() { LOG(("~CamerasChild: %p", this)); - { + if (!CamerasSingleton::InShutdown()) { OffTheBooksMutexAutoLock lock(CamerasSingleton::Mutex()); // In normal circumstances we've already shut down and the // following does nothing. But on fatal IPC errors we will diff --git a/dom/media/systemservices/CamerasChild.h b/dom/media/systemservices/CamerasChild.h index 56afdb011..3df51b066 100644 --- a/dom/media/systemservices/CamerasChild.h +++ b/dom/media/systemservices/CamerasChild.h @@ -97,6 +97,14 @@ public: return gTheInstance.get()->mFakeDeviceChangeEventThread; } + static bool InShutdown() { + return gTheInstance.get()->mInShutdown; + } + + static void StartShutdown() { + gTheInstance.get()->mInShutdown = true; + } + private: static Singleton gTheInstance; @@ -114,6 +122,7 @@ private: CamerasChild* mCameras; nsCOMPtr mCamerasChildThread; nsCOMPtr mFakeDeviceChangeEventThread; + Atomic mInShutdown; }; // Get a pointer to a CamerasChild object we can use to do IPC with. @@ -153,7 +162,7 @@ class CamerasChild final : public PCamerasChild public: // We are owned by the PBackground thread only. CamerasSingleton // takes a non-owning reference. - NS_INLINE_DECL_REFCOUNTING(CamerasChild) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CamerasChild) // IPC messages recevied, received on the PBackground thread // these are the actual callbacks with data diff --git a/dom/xbl/nsXBLBinding.cpp b/dom/xbl/nsXBLBinding.cpp index 423e94ca4..538eb11e5 100644 --- a/dom/xbl/nsXBLBinding.cpp +++ b/dom/xbl/nsXBLBinding.cpp @@ -1004,7 +1004,17 @@ nsXBLBinding::DoInitJSClass(JSContext *cx, NS_ENSURE_TRUE(xblScope, NS_ERROR_UNEXPECTED); JS::Rooted parent_proto(cx); - if (!JS_GetPrototype(cx, obj, &parent_proto)) { + { + JS::RootedObject wrapped(cx, obj); + JSAutoCompartment ac(cx, xblScope); + if (!JS_WrapObject(cx, &wrapped)) { + return NS_ERROR_FAILURE; + } + if (!JS_GetPrototype(cx, wrapped, &parent_proto)) { + return NS_ERROR_FAILURE; + } + } + if (!JS_WrapObject(cx, &parent_proto)) { return NS_ERROR_FAILURE; } diff --git a/gfx/layers/ImageDataSerializer.cpp b/gfx/layers/ImageDataSerializer.cpp index 08ed83bd9..db11e903c 100644 --- a/gfx/layers/ImageDataSerializer.cpp +++ b/gfx/layers/ImageDataSerializer.cpp @@ -83,6 +83,41 @@ ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize return ComputeYCbCrBufferSize(aYSize, aYSize.width, aCbCrSize, aCbCrSize.width); } +uint32_t +ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize, + uint32_t aYOffset, uint32_t aCbOffset, uint32_t aCrOffset) +{ + MOZ_ASSERT(aYSize.height >= 0 && aYSize.width >= 0); + + int32_t yStride = aYSize.width; + int32_t cbCrStride = aCbCrSize.width; + if (aYSize.height < 0 || aYSize.width < 0 || aCbCrSize.height < 0 || aCbCrSize.width < 0 || + !gfx::Factory::AllowedSurfaceSize(IntSize(yStride, aYSize.height)) || + !gfx::Factory::AllowedSurfaceSize(IntSize(cbCrStride, aCbCrSize.height))) { + return 0; + } + + uint32_t yLength = GetAlignedStride<4>(yStride, aYSize.height); + uint32_t cbCrLength = GetAlignedStride<4>(cbCrStride, aCbCrSize.height); + if (yLength == 0 || cbCrLength == 0) { + return 0; + } + + CheckedInt yEnd = aYOffset; + yEnd += yLength; + CheckedInt cbEnd = aCbOffset; + cbEnd += cbCrLength; + CheckedInt crEnd = aCrOffset; + crEnd += cbCrLength; + + if (!yEnd.isValid() || !cbEnd.isValid() || !crEnd.isValid() || + yEnd.value() > aCbOffset || cbEnd.value() > aCrOffset) { + return 0; + } + + return crEnd.value(); +} + uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize) { diff --git a/gfx/layers/ImageDataSerializer.h b/gfx/layers/ImageDataSerializer.h index 53a589d21..4b3194b0c 100644 --- a/gfx/layers/ImageDataSerializer.h +++ b/gfx/layers/ImageDataSerializer.h @@ -47,7 +47,11 @@ uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, int32_t aCbCrStride); uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, const gfx::IntSize& aCbCrSize); - +uint32_t ComputeYCbCrBufferSize(const gfx::IntSize& aYSize, + const gfx::IntSize& aCbCrSize, + uint32_t aYOffset, + uint32_t aCbOffset, + uint32_t aCrOffset); uint32_t ComputeYCbCrBufferSize(uint32_t aBufferSize); void ComputeYCbCrOffsets(int32_t yStride, int32_t yHeight, diff --git a/gfx/layers/composite/TextureHost.cpp b/gfx/layers/composite/TextureHost.cpp index f3b31ec17..888aeb8f3 100644 --- a/gfx/layers/composite/TextureHost.cpp +++ b/gfx/layers/composite/TextureHost.cpp @@ -259,7 +259,9 @@ CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc, case BufferDescriptor::TYCbCrDescriptor: { const YCbCrDescriptor& ycbcr = desc.get_YCbCrDescriptor(); reqSize = - ImageDataSerializer::ComputeYCbCrBufferSize(ycbcr.ySize(), ycbcr.cbCrSize()); + ImageDataSerializer::ComputeYCbCrBufferSize(ycbcr.ySize(), ycbcr.cbCrSize(), + ycbcr.yOffset(), ycbcr.cbOffset(), + ycbcr.crOffset()); break; } case BufferDescriptor::TRGBDescriptor: { @@ -272,7 +274,7 @@ CreateBackendIndependentTextureHost(const SurfaceDescriptor& aDesc, MOZ_CRASH("GFX: Bad descriptor"); } - if (bufSize < reqSize) { + if (reqSize == 0 || bufSize < reqSize) { NS_ERROR("A client process gave a shmem too small to fit for its descriptor!"); return nullptr; } diff --git a/gfx/skia/skia/src/core/SkMask.cpp b/gfx/skia/skia/src/core/SkMask.cpp index 111508074..b40b94974 100644 --- a/gfx/skia/skia/src/core/SkMask.cpp +++ b/gfx/skia/skia/src/core/SkMask.cpp @@ -43,7 +43,12 @@ uint8_t* SkMask::AllocImage(size_t size) { #ifdef TRACK_SKMASK_LIFETIME SkDebugf("SkMask::AllocImage %d\n", gCounter++); #endif - return (uint8_t*)sk_malloc_throw(SkAlign4(size)); + size_t aligned_size = std::numeric_limits::max(); + size_t adjustment = 3; + if (size + adjustment > size) { + aligned_size = (size + adjustment) & ~adjustment; + } + return static_cast(sk_malloc_throw(aligned_size)); } /** We explicitly use this allocator for SkBimap pixels, so that we can diff --git a/gfx/skia/skia/src/core/SkScan_Path.cpp b/gfx/skia/skia/src/core/SkScan_Path.cpp index 5b80492cf..d15d2d54b 100644 --- a/gfx/skia/skia/src/core/SkScan_Path.cpp +++ b/gfx/skia/skia/src/core/SkScan_Path.cpp @@ -591,16 +591,10 @@ static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) { return true; } -/** - * Variants of SkScalarRoundToInt, identical to SkDScalarRoundToInt except when the input fraction - * is 0.5. When SK_RASTERIZE_EVEN_ROUNDING is enabled, we must bias the result before rounding to - * account for potential FDot6 rounding edge-cases. - */ -#ifdef SK_RASTERIZE_EVEN_ROUNDING -static const double kRoundBias = 0.5 / SK_FDot6One; -#else -static const double kRoundBias = 0.0; -#endif +// Bias used for conservative rounding of float rects to int rects, to nudge the irects a little +// larger, so we don't "think" a path's bounds are inside a clip, when (due to numeric drift in +// the scan-converter) we might walk beyond the predicted limits. +static const double kConservativeRoundBias = 0.5 + 1.5 / SK_FDot6One; /** * Round the value down. This is used to round the top and left of a rectangle, @@ -608,8 +602,8 @@ static const double kRoundBias = 0.0; */ static inline int round_down_to_int(SkScalar x) { double xx = x; - xx -= 0.5 + kRoundBias; - return (int)ceil(xx); + xx -= kConservativeRoundBias; + return pin_double_to_int(ceil(xx)); } /** @@ -618,8 +612,8 @@ static inline int round_down_to_int(SkScalar x) { */ static inline int round_up_to_int(SkScalar x) { double xx = x; - xx += 0.5 + kRoundBias; - return (int)floor(xx); + xx += kConservativeRoundBias; + return pin_double_to_int(floor(xx)); } /** diff --git a/gfx/skia/skia/src/gpu/GrBufferAllocPool.cpp b/gfx/skia/skia/src/gpu/GrBufferAllocPool.cpp index e3f30b0c1..993e1c59d 100644 --- a/gfx/skia/skia/src/gpu/GrBufferAllocPool.cpp +++ b/gfx/skia/skia/src/gpu/GrBufferAllocPool.cpp @@ -14,6 +14,7 @@ #include "GrResourceProvider.h" #include "GrTypes.h" +#include "SkSafeMath.h" #include "SkTraceEvent.h" #ifdef SK_DEBUG @@ -335,7 +336,7 @@ void* GrVertexBufferAllocPool::makeSpace(size_t vertexSize, SkASSERT(startVertex); size_t offset = 0; // assign to suppress warning - void* ptr = INHERITED::makeSpace(vertexSize * vertexCount, + void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(vertexSize, vertexCount), vertexSize, buffer, &offset); @@ -360,7 +361,7 @@ void* GrIndexBufferAllocPool::makeSpace(int indexCount, SkASSERT(startIndex); size_t offset = 0; // assign to suppress warning - void* ptr = INHERITED::makeSpace(indexCount * sizeof(uint16_t), + void* ptr = INHERITED::makeSpace(SkSafeMath::Mul(indexCount, sizeof(uint16_t)), sizeof(uint16_t), buffer, &offset); diff --git a/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp b/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp index 9d73cf4f1..ec6c99c6e 100644 --- a/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp +++ b/gfx/skia/skia/src/gpu/batches/GrAAHairLinePathRenderer.cpp @@ -828,6 +828,13 @@ void AAHairlineBatch::onPrepareDraws(Target* target) const { int lineCount = lines.count() / 2; int conicCount = conics.count() / 3; + int quadAndConicCount = conicCount + quadCount; + + static constexpr int kMaxLines = SK_MaxS32 / kLineSegNumVertices; + static constexpr int kMaxQuadsAndConics = SK_MaxS32 / kQuadNumVertices; + if (lineCount > kMaxLines || quadAndConicCount > kMaxQuadsAndConics) { + return; + } // do lines first if (lineCount) { @@ -899,7 +906,7 @@ void AAHairlineBatch::onPrepareDraws(Target* target) const { ref_quads_index_buffer(target->resourceProvider())); size_t vertexStride = sizeof(BezierVertex); - int vertexCount = kQuadNumVertices * quadCount + kQuadNumVertices * conicCount; + int vertexCount = kQuadNumVertices * quadAndConicCount; void *vertices = target->makeVertexSpace(vertexStride, vertexCount, &vertexBuffer, &firstVertex); diff --git a/image/decoders/icon/nsIconURI.cpp b/image/decoders/icon/nsIconURI.cpp index 632a733fe..473ded218 100644 --- a/image/decoders/icon/nsIconURI.cpp +++ b/image/decoders/icon/nsIconURI.cpp @@ -688,7 +688,17 @@ nsMozIconURI::Deserialize(const URIParams& aParams) mContentType = params.contentType(); mFileName = params.fileName(); mStockIcon = params.stockIcon(); + + if (params.iconSize() < -1 || + params.iconSize() >= (int32_t) ArrayLength(kSizeStrings)) { + return false; + } mIconSize = params.iconSize(); + + if (params.iconState() < -1 || + params.iconState() >= (int32_t) ArrayLength(kStateStrings)) { + return false; + } mIconState = params.iconState(); return true; diff --git a/ipc/glue/IPCMessageUtils.h b/ipc/glue/IPCMessageUtils.h index f4720d8e8..9be5092a0 100644 --- a/ipc/glue/IPCMessageUtils.h +++ b/ipc/glue/IPCMessageUtils.h @@ -63,15 +63,18 @@ struct null_t { struct SerializedStructuredCloneBuffer final { + SerializedStructuredCloneBuffer() + : data(JS::StructuredCloneScope::Unassigned) + { + } + + SerializedStructuredCloneBuffer& operator=(const SerializedStructuredCloneBuffer& aOther) { data.Clear(); - auto iter = aOther.data.Iter(); - while (!iter.Done()) { - data.WriteBytes(iter.Data(), iter.RemainingInSegment()); - iter.Advance(aOther.data, iter.RemainingInSegment()); - } + data.initScope(aOther.data.scope()); + data.Append(aOther.data); return *this; } @@ -728,11 +731,9 @@ struct ParamTraits { MOZ_ASSERT(!(aParam.Size() % sizeof(uint64_t))); WriteParam(aMsg, aParam.Size()); - auto iter = aParam.Iter(); - while (!iter.Done()) { - aMsg->WriteBytes(iter.Data(), iter.RemainingInSegment(), sizeof(uint64_t)); - iter.Advance(aParam, iter.RemainingInSegment()); - } + aParam.ForEachDataChunk([&](const char* aData, size_t aSize) { + return aMsg->WriteBytes(aData, aSize, sizeof(uint64_t)); + }); } static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult) @@ -762,7 +763,7 @@ struct ParamTraits return false; } - *aResult = JSStructuredCloneData(Move(out)); + *aResult = JSStructuredCloneData(Move(out), JS::StructuredCloneScope::DifferentProcess); return true; } diff --git a/js/public/StructuredClone.h b/js/public/StructuredClone.h index 0e30bc3f2..4c7d90c70 100644 --- a/js/public/StructuredClone.h +++ b/js/public/StructuredClone.h @@ -9,6 +9,7 @@ #include "mozilla/Attributes.h" #include "mozilla/BufferList.h" +#include "mozilla/Move.h" #include @@ -30,7 +31,35 @@ namespace JS { enum class StructuredCloneScope : uint32_t { SameProcessSameThread, SameProcessDifferentThread, - DifferentProcess + + /** + * When writing, this means we're writing for an audience in a different + * process. Produce serialized data that can be sent to other processes, + * bitwise copied, or even stored as bytes in a database and read by later + * versions of Firefox years from now. The HTML5 spec refers to this as + * "ForStorage" as in StructuredSerializeForStorage, though we use + * DifferentProcess for IPC as well as storage. + * + * Transferable objects are limited to ArrayBuffers, whose contents are + * copied into the serialized data (rather than just writing a pointer). + */ + DifferentProcess, + + /** + * Handle a backwards-compatibility case with IndexedDB (bug 1434308): when + * reading, this means to treat legacy SameProcessSameThread data as if it + * were DifferentProcess. + * + * Do not use this for writing; use DifferentProcess instead. + */ + DifferentProcessForIndexedDB, + + /** + * Existing code wants to be able to create an uninitialized + * JSStructuredCloneData without knowing the scope, then populate it with + * data (at which point the scope *is* known.) + */ + Unassigned }; enum TransferableOwnership { @@ -90,6 +119,10 @@ class CloneDataPolicy } /* namespace JS */ +namespace js { +template struct BufferIterator; +} + /** * Read structured data from the reader r. This hook is used to read a value * previously serialized by a call to the WriteStructuredCloneOp hook. @@ -211,50 +244,153 @@ namespace js }; } -class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) : - public mozilla::BufferList -{ - typedef js::SystemAllocPolicy AllocPolicy; - typedef mozilla::BufferList BufferList; +/** + * JSStructuredCloneData represents structured clone data together with the + * information needed to read/write/transfer/free the records within it, in the + * form of a set of callbacks. + */ +class MOZ_NON_MEMMOVABLE JS_PUBLIC_API(JSStructuredCloneData) { + public: + using BufferList = mozilla::BufferList; + using Iterator = BufferList::IterImpl; - static const size_t kInitialSize = 0; - static const size_t kInitialCapacity = 4096; + private: static const size_t kStandardCapacity = 4096; + BufferList bufList_; + + // The (address space, thread) scope within which this clone is valid. Note + // that this must be either set during construction, or start out as + // Unassigned and transition once to something else. + JS::StructuredCloneScope scope_; + const JSStructuredCloneCallbacks* callbacks_; void* closure_; OwnTransferablePolicy ownTransferables_; js::SharedArrayRawBufferRefs refsHeld_; - void setOptionalCallbacks(const JSStructuredCloneCallbacks* callbacks, - void* closure, - OwnTransferablePolicy policy) { - callbacks_ = callbacks; - closure_ = closure; - ownTransferables_ = policy; - } - friend struct JSStructuredCloneWriter; friend class JS_PUBLIC_API(JSAutoStructuredCloneBuffer); + template friend struct js::BufferIterator; -public: - explicit JSStructuredCloneData(AllocPolicy aAP = AllocPolicy()) - : BufferList(kInitialSize, kInitialCapacity, kStandardCapacity, aAP) + public: + // The constructor must be infallible but SystemAllocPolicy is not, so both + // the initial size and initial capacity of the BufferList must be zero. + explicit JSStructuredCloneData(JS::StructuredCloneScope aScope) + : bufList_(0, 0, kStandardCapacity, js::SystemAllocPolicy()) + , scope_(aScope) + , callbacks_(nullptr) + , closure_(nullptr) + , ownTransferables_(OwnTransferablePolicy::NoTransferables) + {} + + // Steal the raw data from a BufferList. In this case, we don't know the + // scope and none of the callback info is assigned yet. + JSStructuredCloneData(BufferList&& buffers, JS::StructuredCloneScope aScope) + : bufList_(mozilla::Move(buffers)) + , scope_(aScope) , callbacks_(nullptr) , closure_(nullptr) , ownTransferables_(OwnTransferablePolicy::NoTransferables) {} MOZ_IMPLICIT JSStructuredCloneData(BufferList&& buffers) - : BufferList(Move(buffers)) - , callbacks_(nullptr) - , closure_(nullptr) - , ownTransferables_(OwnTransferablePolicy::NoTransferables) + : JSStructuredCloneData(mozilla::Move(buffers), JS::StructuredCloneScope::Unassigned) {} JSStructuredCloneData(JSStructuredCloneData&& other) = default; JSStructuredCloneData& operator=(JSStructuredCloneData&& other) = default; - ~JSStructuredCloneData(); + ~JSStructuredCloneData() { discardTransferables(); } - using BufferList::BufferList; + void setCallbacks(const JSStructuredCloneCallbacks* callbacks, + void* closure, + OwnTransferablePolicy policy) + { + callbacks_ = callbacks; + closure_ = closure; + ownTransferables_ = policy; + } + + JS::StructuredCloneScope scope() const { return scope_; } + + void initScope(JS::StructuredCloneScope aScope) { + MOZ_ASSERT(Size() == 0, "initScope() of nonempty JSStructuredCloneData"); + if (scope_ != JS::StructuredCloneScope::Unassigned) + MOZ_ASSERT(scope_ == aScope, "Cannot change scope after it has been initialized"); + scope_ = aScope; + } + + size_t Size() const { return bufList_.Size(); } + + const Iterator Start() const { return bufList_.Iter(); } + + bool Advance(Iterator& iter, size_t distance) const { + return iter.AdvanceAcrossSegments(bufList_, distance); + } + + bool ReadBytes(Iterator& iter, char* buffer, size_t size) const { + return bufList_.ReadBytes(iter, buffer, size); + } + + // Append new data to the end of the buffer. + bool AppendBytes(const char* data, size_t size) { + MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned); + return bufList_.WriteBytes(data, size); + } + + // Update data stored within the existing buffer. There must be at least + // 'size' bytes between the position of 'iter' and the end of the buffer. + bool UpdateBytes(Iterator& iter, const char* data, size_t size) const { + MOZ_ASSERT(scope_ != JS::StructuredCloneScope::Unassigned); + while (size > 0) { + size_t remaining = iter.RemainingInSegment(); + size_t nbytes = std::min(remaining, size); + memcpy(iter.Data(), data, nbytes); + data += nbytes; + size -= nbytes; + iter.Advance(bufList_, nbytes); + } + return true; + } + + void Clear() { + discardTransferables(); + bufList_.Clear(); + } + + // Return a new read-only JSStructuredCloneData that "borrows" the contents + // of |this|. Its lifetime should not exceed the donor's. This is only + // allowed for DifferentProcess clones, so finalization of the borrowing + // clone will do nothing. + JSStructuredCloneData Borrow(Iterator& iter, size_t size, bool* success) const + { + MOZ_ASSERT(scope_ == JS::StructuredCloneScope::DifferentProcess); + return JSStructuredCloneData(bufList_.Borrow(iter, size, success), + scope_); + } + + // Iterate over all contained data, one BufferList segment's worth at a + // time, and invoke the given FunctionToApply with the data pointer and + // size. The function should return a bool value, and this loop will exit + // with false if the function ever returns false. + template + bool ForEachDataChunk(FunctionToApply&& function) const { + Iterator iter = bufList_.Iter(); + while (!iter.Done()) { + if (!function(iter.Data(), iter.RemainingInSegment())) + return false; + iter.Advance(bufList_, iter.RemainingInSegment()); + } + return true; + } + + // Append the entire contents of other's bufList_ to our own. + bool Append(const JSStructuredCloneData& other) { + MOZ_ASSERT(scope_ == other.scope_); + return other.ForEachDataChunk([&](const char* data, size_t size) { + return AppendBytes(data, size); + }); + } + + void discardTransferables(); }; /** Note: if the *data contains transferable objects, it can be read only once. */ @@ -278,18 +414,29 @@ JS_PUBLIC_API(bool) JS_StructuredClone(JSContext* cx, JS::HandleValue v, JS::MutableHandleValue vp, const JSStructuredCloneCallbacks* optionalCallbacks, void* closure); -/** RAII sugar for JS_WriteStructuredClone. */ +/** + * The C-style API calls to read and write structured clones are fragile -- + * they rely on the caller to properly handle ownership of the clone data, and + * the handling of the input data as well as the interpretation of the contents + * of the clone buffer are dependent on the callbacks passed in. If you + * serialize and deserialize with different callbacks, the results are + * questionable. + * + * JSAutoStructuredCloneBuffer wraps things up in an RAII class for data + * management, and uses the same callbacks for both writing and reading + * (serializing and deserializing). + */ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { const JS::StructuredCloneScope scope_; JSStructuredCloneData data_; uint32_t version_; public: - JSAutoStructuredCloneBuffer(JS::StructuredCloneScope scope, + JSAutoStructuredCloneBuffer(JS::StructuredCloneScope aScope, const JSStructuredCloneCallbacks* callbacks, void* closure) - : scope_(scope), version_(JS_STRUCTURED_CLONE_VERSION) + : scope_(aScope), data_(aScope), version_(JS_STRUCTURED_CLONE_VERSION) { - data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); + data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); } JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other); @@ -300,12 +447,9 @@ class JS_PUBLIC_API(JSAutoStructuredCloneBuffer) { JSStructuredCloneData& data() { return data_; } bool empty() const { return !data_.Size(); } - void clear(const JSStructuredCloneCallbacks* optionalCallbacks=nullptr, void* closure=nullptr); + void clear(); - /** Copy some memory. It will be automatically freed by the destructor. */ - bool copy(JSContext* cx, const JSStructuredCloneData& data, - uint32_t version=JS_STRUCTURED_CLONE_VERSION, - const JSStructuredCloneCallbacks* callbacks=nullptr, void* closure=nullptr); + JS::StructuredCloneScope scope() const { return scope_; } /** * Adopt some memory. It will be automatically freed by the destructor. diff --git a/js/src/builtin/Array.js b/js/src/builtin/Array.js index 49379c7da..10edf335f 100644 --- a/js/src/builtin/Array.js +++ b/js/src/builtin/Array.js @@ -711,13 +711,14 @@ function CreateArrayIterator(obj, kind) { // http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%.next function ArrayIteratorNext() { // Step 1-3. - if (!IsObject(this) || !IsArrayIterator(this)) { + var obj; + if (!IsObject(this) || (obj = GuardToArrayIterator(this)) === null) { return callFunction(CallArrayIteratorMethodIfWrapped, this, "ArrayIteratorNext"); } // Step 4. - var a = UnsafeGetReservedSlot(this, ITERATOR_SLOT_TARGET); + var a = UnsafeGetReservedSlot(obj, ITERATOR_SLOT_TARGET); var result = { value: undefined, done: false }; // Step 5. @@ -728,10 +729,10 @@ function ArrayIteratorNext() { // Step 6. // The index might not be an integer, so we have to do a generic get here. - var index = UnsafeGetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + var index = UnsafeGetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX); // Step 7. - var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + var itemKind = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_ITEM_KIND); // Step 8-9. var len; @@ -746,13 +747,13 @@ function ArrayIteratorNext() { // Step 10. if (index >= len) { - UnsafeSetReservedSlot(this, ITERATOR_SLOT_TARGET, null); + UnsafeSetReservedSlot(obj, ITERATOR_SLOT_TARGET, null); result.done = true; return result; } // Step 11. - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + 1); + UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + 1); // Step 16. if (itemKind === ITEM_KIND_VALUE) { diff --git a/js/src/builtin/Map.js b/js/src/builtin/Map.js index c4b6465c1..f965ca10e 100644 --- a/js/src/builtin/Map.js +++ b/js/src/builtin/Map.js @@ -67,8 +67,8 @@ function MapIteratorNext() { var O = this; // Steps 2-3. - if (!IsObject(O) || !IsMapIterator(O)) - return callFunction(CallMapIteratorMethodIfWrapped, O, "MapIteratorNext"); + if (!IsObject(O) || (O = GuardToMapIterator(O)) === null) + return callFunction(CallMapIteratorMethodIfWrapped, this, "MapIteratorNext"); // Steps 4-5 (implemented in _GetNextMapEntryForIterator). // Steps 8-9 (omitted). @@ -87,7 +87,7 @@ function MapIteratorNext() { // Steps 10.b-c (omitted). // Step 6. - var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + var itemKind = UnsafeGetInt32FromReservedSlot(O, ITERATOR_SLOT_ITEM_KIND); var result; if (itemKind === ITEM_KIND_KEY) { diff --git a/js/src/builtin/Set.js b/js/src/builtin/Set.js index 9720e70a1..cc805887d 100644 --- a/js/src/builtin/Set.js +++ b/js/src/builtin/Set.js @@ -69,8 +69,8 @@ function SetIteratorNext() { var O = this; // Steps 2-3. - if (!IsObject(O) || !IsSetIterator(O)) - return callFunction(CallSetIteratorMethodIfWrapped, O, "SetIteratorNext"); + if (!IsObject(O) || (O = GuardToSetIterator(O)) === null) + return callFunction(CallSetIteratorMethodIfWrapped, this, "SetIteratorNext"); // Steps 4-5 (implemented in _GetNextSetEntryForIterator). // Steps 8-9 (omitted). @@ -88,7 +88,7 @@ function SetIteratorNext() { // Steps 10.b-c (omitted). // Step 6. - var itemKind = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_ITEM_KIND); + var itemKind = UnsafeGetInt32FromReservedSlot(O, ITERATOR_SLOT_ITEM_KIND); var result; if (itemKind === ITEM_KIND_VALUE) { diff --git a/js/src/builtin/String.js b/js/src/builtin/String.js index 623d8da1a..f395f8e97 100644 --- a/js/src/builtin/String.js +++ b/js/src/builtin/String.js @@ -536,16 +536,17 @@ function String_iterator() { } function StringIteratorNext() { - if (!IsObject(this) || !IsStringIterator(this)) { + var obj; + if (!IsObject(this) || (obj = GuardToStringIterator(this)) === null) { return callFunction(CallStringIteratorMethodIfWrapped, this, "StringIteratorNext"); } - var S = UnsafeGetStringFromReservedSlot(this, ITERATOR_SLOT_TARGET); + var S = UnsafeGetStringFromReservedSlot(obj, ITERATOR_SLOT_TARGET); // We know that JSString::MAX_LENGTH <= INT32_MAX (and assert this in // SelfHostring.cpp) so our current index can never be anything other than // an Int32Value. - var index = UnsafeGetInt32FromReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX); + var index = UnsafeGetInt32FromReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX); var size = S.length; var result = { value: undefined, done: false }; @@ -563,7 +564,7 @@ function StringIteratorNext() { } } - UnsafeSetReservedSlot(this, ITERATOR_SLOT_NEXT_INDEX, index + charCount); + UnsafeSetReservedSlot(obj, ITERATOR_SLOT_NEXT_INDEX, index + charCount); result.value = callFunction(String_substring, S, index, index + charCount); return result; diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index ba6d69a4c..afccfa53c 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -2060,7 +2060,7 @@ class CloneBufferObject : public NativeObject { Rooted obj(cx, Create(cx)); if (!obj) return nullptr; - auto data = js::MakeUnique(); + auto data = js::MakeUnique(buffer->scope()); if (!data) { ReportOutOfMemory(cx); return nullptr; @@ -2113,8 +2113,11 @@ class CloneBufferObject : public NativeObject { return false; size_t nbytes = JS_GetStringLength(args[0].toString()); MOZ_ASSERT(nbytes % sizeof(uint64_t) == 0); - auto buf = js::MakeUnique(nbytes, nbytes, nbytes); - js_memcpy(buf->Start(), str, nbytes); + auto buf = js::MakeUnique(JS::StructuredCloneScope::DifferentProcess); + if (!buf->AppendBytes(str, nbytes)) { + ReportOutOfMemory(cx); + return false; + } JS_free(cx, str); obj->setData(buf.release()); @@ -2158,7 +2161,7 @@ class CloneBufferObject : public NativeObject { ReportOutOfMemory(cx); return false; } - auto iter = obj->data()->Iter(); + auto iter = obj->data()->Start(); obj->data()->ReadBytes(iter, buffer.get(), size); JSString* str = JS_NewStringCopyN(cx, buffer.get(), size); if (!str) @@ -2216,6 +2219,8 @@ ParseCloneScope(JSContext* cx, HandleString str) scope.emplace(JS::StructuredCloneScope::SameProcessDifferentThread); else if (strcmp(scopeStr.ptr(), "DifferentProcess") == 0) scope.emplace(JS::StructuredCloneScope::DifferentProcess); + else if (strcmp(scopeStr.ptr(), "DifferentProcessForIndexedDB") == 0) + scope.emplace(JS::StructuredCloneScope::DifferentProcessForIndexedDB); return scope; } @@ -4346,19 +4351,22 @@ JS_FN_HELP("rejectPromise", RejectPromise, 2, 0, " clone buffer object. 'policy' may be an options hash. Valid keys:\n" " 'SharedArrayBuffer' - either 'allow' (the default) or 'deny'\n" " to specify whether SharedArrayBuffers may be serialized.\n" -"\n" -" 'scope' - SameProcessSameThread, SameProcessDifferentThread, or\n" -" DifferentProcess. Determines how some values will be serialized.\n" -" Clone buffers may only be deserialized with a compatible scope."), +" 'scope' - SameProcessSameThread, SameProcessDifferentThread,\n" +" DifferentProcess, or DifferentProcessForIndexedDB. Determines how some\n" +" values will be serialized. Clone buffers may only be deserialized with a\n" +" compatible scope. NOTE - For DifferentProcess/DifferentProcessForIndexedDB,\n" +" must also set SharedArrayBuffer:'deny' if data contains any shared memory\n" +" object."), JS_FN_HELP("deserialize", Deserialize, 1, 0, "deserialize(clonebuffer[, opts])", " Deserialize data generated by serialize. 'opts' is an options hash with one\n" " recognized key 'scope', which limits the clone buffers that are considered\n" " valid. Allowed values: 'SameProcessSameThread', 'SameProcessDifferentThread',\n" -" and 'DifferentProcess'. So for example, a DifferentProcess clone buffer\n" -" may be deserialized in any scope, but a SameProcessSameThread clone buffer\n" -" cannot be deserialized in a DifferentProcess scope."), +" 'DifferentProcess', and 'DifferentProcessForIndexedDB'. So for example, a\n" +" DifferentProcessForIndexedDB clone buffer may be deserialized in any scope, but\n" +" a SameProcessSameThread clone buffer cannot be deserialized in a\n" +" DifferentProcess scope."), JS_FN_HELP("detachArrayBuffer", DetachArrayBuffer, 1, 0, "detachArrayBuffer(buffer)", diff --git a/js/src/gc/GCRuntime.h b/js/src/gc/GCRuntime.h index 585e27d9c..a90df3203 100644 --- a/js/src/gc/GCRuntime.h +++ b/js/src/gc/GCRuntime.h @@ -73,7 +73,7 @@ class ChunkPool // Performs extra allocation off the main thread so that when memory is // required on the main thread it will already be available and waiting. -class BackgroundAllocTask : public GCParallelTask +class BackgroundAllocTask : public GCParallelTaskHelper { // Guarded by the GC lock. JSRuntime* runtime; @@ -85,12 +85,11 @@ class BackgroundAllocTask : public GCParallelTask BackgroundAllocTask(JSRuntime* rt, ChunkPool& pool); bool enabled() const { return enabled_; } - protected: - void run() override; + void run(); }; -// Search the provided Chunks for free arenas and decommit them. -class BackgroundDecommitTask : public GCParallelTask +// Search the provided Chunks for free arenas and recommit them. +class BackgroundDecommitTask : public GCParallelTaskHelper { public: using ChunkVector = mozilla::Vector; @@ -98,8 +97,7 @@ class BackgroundDecommitTask : public GCParallelTask explicit BackgroundDecommitTask(JSRuntime *rt) : runtime(rt) {} void setChunksToScan(ChunkVector &chunks); - protected: - void run() override; + void run(); private: JSRuntime* runtime; @@ -1171,8 +1169,10 @@ class GCRuntime /* * Concurrent sweep infrastructure. */ - void startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked); - void joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked); + void startTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked); + void joinTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked); /* * List head of arenas allocated during the sweep phase. diff --git a/js/src/gc/Nursery.cpp b/js/src/gc/Nursery.cpp index 61869ec5c..aa89155db 100644 --- a/js/src/gc/Nursery.cpp +++ b/js/src/gc/Nursery.cpp @@ -44,19 +44,19 @@ using mozilla::TimeStamp; static const uintptr_t CanaryMagicValue = 0xDEADB15D; -struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTask +struct js::Nursery::FreeMallocedBuffersTask : public GCParallelTaskHelper { explicit FreeMallocedBuffersTask(FreeOp* fop) : fop_(fop) {} bool init() { return buffers_.init(); } void transferBuffersToFree(MallocedBuffersSet& buffersToFree, const AutoLockHelperThreadState& lock); - ~FreeMallocedBuffersTask() override { join(); } + ~FreeMallocedBuffersTask() { join(); } + + void run(); private: FreeOp* fop_; MallocedBuffersSet buffers_; - - virtual void run() override; }; struct js::Nursery::SweepAction diff --git a/js/src/gc/Statistics.h b/js/src/gc/Statistics.h index 0bc93b463..5126ca541 100644 --- a/js/src/gc/Statistics.h +++ b/js/src/gc/Statistics.h @@ -22,9 +22,6 @@ using mozilla::Maybe; namespace js { - -class GCParallelTask; - namespace gcstats { enum Phase : uint8_t { diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 05e16d163..b5221044d 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -11738,6 +11738,32 @@ CodeGenerator::visitHasClass(LHasClass* ins) masm.cmpPtrSet(Assembler::Equal, output, ImmPtr(ins->mir()->getClass()), output); } +void +CodeGenerator::visitGuardToClass(LGuardToClass* ins) +{ + Register lhs = ToRegister(ins->lhs()); + Register output = ToRegister(ins->output()); + Register temp = ToRegister(ins->temp()); + + Label notEqual; + + masm.branchTestObjClass(Assembler::NotEqual, lhs, temp, ins->mir()->getClass(), ¬Equal); + masm.mov(lhs, output); + + if (ins->mir()->type() == MIRType::Object) { + // Can't return null-return here, so bail + bailoutFrom(¬Equal, ins->snapshot()); + } else { + Label done; + masm.jump(&done); + + masm.bind(¬Equal); + masm.mov(ImmPtr(0), output); + + masm.bind(&done); + } +} + void CodeGenerator::visitWasmParameter(LWasmParameter* lir) { diff --git a/js/src/jit/CodeGenerator.h b/js/src/jit/CodeGenerator.h index 6ddf384ac..d12b587bd 100644 --- a/js/src/jit/CodeGenerator.h +++ b/js/src/jit/CodeGenerator.h @@ -379,6 +379,7 @@ class CodeGenerator final : public CodeGeneratorSpecific void visitIsObject(LIsObject* lir); void visitIsObjectAndBranch(LIsObjectAndBranch* lir); void visitHasClass(LHasClass* lir); + void visitGuardToClass(LGuardToClass* lir); void visitWasmParameter(LWasmParameter* lir); void visitWasmParameterI64(LWasmParameterI64* lir); void visitWasmReturn(LWasmReturn* ret); diff --git a/js/src/jit/InlinableNatives.h b/js/src/jit/InlinableNatives.h index 15352c04f..23d3143a7 100644 --- a/js/src/jit/InlinableNatives.h +++ b/js/src/jit/InlinableNatives.h @@ -117,13 +117,15 @@ _(IntrinsicDefineDataProperty) \ _(IntrinsicObjectHasPrototype) \ \ - _(IntrinsicIsArrayIterator) \ - _(IntrinsicIsMapIterator) \ - _(IntrinsicIsSetIterator) \ - _(IntrinsicIsStringIterator) \ + _(IntrinsicGuardToArrayIterator) \ + _(IntrinsicGuardToMapIterator) \ + _(IntrinsicGuardToSetIterator) \ + _(IntrinsicGuardToStringIterator) \ \ + _(IntrinsicGuardToMapObject) \ _(IntrinsicGetNextMapEntryForIterator) \ \ + _(IntrinsicGuardToSetObject) \ _(IntrinsicGetNextSetEntryForIterator) \ \ _(IntrinsicArrayBufferByteLength) \ diff --git a/js/src/jit/IonBuilder.h b/js/src/jit/IonBuilder.h index 876804175..fe1bfa694 100644 --- a/js/src/jit/IonBuilder.h +++ b/js/src/jit/IonBuilder.h @@ -764,6 +764,7 @@ class IonBuilder const Class* clasp2 = nullptr, const Class* clasp3 = nullptr, const Class* clasp4 = nullptr); + InliningStatus inlineGuardToClass(CallInfo& callInfo, const Class* clasp); InliningResult inlineIsConstructing(CallInfo& callInfo); InliningResult inlineSubstringKernel(CallInfo& callInfo); InliningResult inlineObjectHasPrototype(CallInfo& callInfo); diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index 73bc16248..01d059e9f 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -4188,6 +4188,16 @@ LIRGenerator::visitHasClass(MHasClass* ins) define(new(alloc()) LHasClass(useRegister(ins->object())), ins); } +void +LIRGenerator::visitGuardToClass(MGuardToClass* ins) +{ + MOZ_ASSERT(ins->object()->type() == MIRType::Object); + MOZ_ASSERT(ins->type() == MIRType::ObjectOrNull|| ins->type() == MIRType::Object); + LGuardToClass* lir = new(alloc()) LGuardToClass(useRegister(ins->object()), temp()); + assignSnapshot(lir, Bailout_TypeBarrierO); + define(lir, ins); +} + void LIRGenerator::visitWasmAddOffset(MWasmAddOffset* ins) { diff --git a/js/src/jit/Lowering.h b/js/src/jit/Lowering.h index b2805cb7a..9b4095aec 100644 --- a/js/src/jit/Lowering.h +++ b/js/src/jit/Lowering.h @@ -289,6 +289,7 @@ class LIRGenerator : public LIRGeneratorSpecific void visitIsConstructor(MIsConstructor* ins); void visitIsObject(MIsObject* ins); void visitHasClass(MHasClass* ins); + void visitGuardToClass(MGuardToClass* ins); void visitWasmAddOffset(MWasmAddOffset* ins); void visitWasmBoundsCheck(MWasmBoundsCheck* ins); void visitWasmLoadGlobalVar(MWasmLoadGlobalVar* ins); diff --git a/js/src/jit/MCallOptimize.cpp b/js/src/jit/MCallOptimize.cpp index 571b1fb9b..efe69f824 100644 --- a/js/src/jit/MCallOptimize.cpp +++ b/js/src/jit/MCallOptimize.cpp @@ -280,24 +280,28 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target) return inlineIsConstructing(callInfo); case InlinableNative::IntrinsicSubstringKernel: return inlineSubstringKernel(callInfo); - case InlinableNative::IntrinsicIsArrayIterator: - return inlineHasClass(callInfo, &ArrayIteratorObject::class_); - case InlinableNative::IntrinsicIsMapIterator: - return inlineHasClass(callInfo, &MapIteratorObject::class_); - case InlinableNative::IntrinsicIsSetIterator: - return inlineHasClass(callInfo, &SetIteratorObject::class_); - case InlinableNative::IntrinsicIsStringIterator: - return inlineHasClass(callInfo, &StringIteratorObject::class_); + case InlinableNative::IntrinsicGuardToArrayIterator: + return inlineGuardToClass(callInfo, &ArrayIteratorObject::class_); + case InlinableNative::IntrinsicGuardToMapIterator: + return inlineGuardToClass(callInfo, &MapIteratorObject::class_); + case InlinableNative::IntrinsicGuardToSetIterator: + return inlineGuardToClass(callInfo, &SetIteratorObject::class_); + case InlinableNative::IntrinsicGuardToStringIterator: + return inlineGuardToClass(callInfo, &StringIteratorObject::class_); case InlinableNative::IntrinsicDefineDataProperty: return inlineDefineDataProperty(callInfo); case InlinableNative::IntrinsicObjectHasPrototype: return inlineObjectHasPrototype(callInfo); // Map intrinsics. + case InlinableNative::IntrinsicGuardToMapObject: + return inlineGuardToClass(callInfo, &MapObject::class_); case InlinableNative::IntrinsicGetNextMapEntryForIterator: return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Map); // Set intrinsics. + case InlinableNative::IntrinsicGuardToSetObject: + return inlineGuardToClass(callInfo, &SetObject::class_); case InlinableNative::IntrinsicGetNextSetEntryForIterator: return inlineGetNextEntryForIterator(callInfo, MGetNextEntryForIterator::Set); @@ -2191,6 +2195,37 @@ IonBuilder::inlineHasClass(CallInfo& callInfo, return InliningStatus_Inlined; } +IonBuilder::InliningStatus +IonBuilder::inlineGuardToClass(CallInfo& callInfo, const Class* clasp) +{ + MOZ_ASSERT(!callInfo.constructing()); + MOZ_ASSERT(callInfo.argc() == 1); + + if (callInfo.getArg(0)->type() != MIRType::Object) + return InliningStatus_NotInlined; + + if (getInlineReturnType() != MIRType::ObjectOrNull && + getInlineReturnType() != MIRType::Object) + { + return InliningStatus_NotInlined; + } + + TemporaryTypeSet* types = callInfo.getArg(0)->resultTypeSet(); + const Class* knownClass = types ? types->getKnownClass(constraints()) : nullptr; + + if (knownClass && knownClass == clasp) { + current->push(callInfo.getArg(0)); + } else { + MGuardToClass* guardToClass = MGuardToClass::New(alloc(), callInfo.getArg(0), + clasp, getInlineReturnType()); + current->add(guardToClass); + current->push(guardToClass); + } + + callInfo.setImplicitlyUsedUnchecked(); + return InliningStatus_Inlined; +} + IonBuilder::InliningResult IonBuilder::inlineGetNextEntryForIterator(CallInfo& callInfo, MGetNextEntryForIterator::Mode mode) { diff --git a/js/src/jit/MIR.h b/js/src/jit/MIR.h index f389e36a6..5a60dada4 100644 --- a/js/src/jit/MIR.h +++ b/js/src/jit/MIR.h @@ -13175,6 +13175,48 @@ class MHasClass } }; +class MGuardToClass + : public MUnaryInstruction, + public SingleObjectPolicy::Data +{ + const Class* class_; + + MGuardToClass(MDefinition* object, const Class* clasp, MIRType resultType) + : MUnaryInstruction(object) + , class_(clasp) + { + MOZ_ASSERT(object->type() == MIRType::Object || + (object->type() == MIRType::Value && object->mightBeType(MIRType::Object))); + MOZ_ASSERT(resultType == MIRType::Object || resultType == MIRType::ObjectOrNull); + setResultType(resultType); + setMovable(); + if (resultType == MIRType::Object) { + // We will bail out if the class type is incorrect, + // so we need to ensure we don't eliminate this instruction + setGuard(); + } + } + + public: + INSTRUCTION_HEADER(GuardToClass) + TRIVIAL_NEW_WRAPPERS + NAMED_OPERANDS((0, object)) + + const Class* getClass() const { + return class_; + } + AliasSet getAliasSet() const override { + return AliasSet::None(); + } + bool congruentTo(const MDefinition* ins) const override { + if (!ins->isGuardToClass()) + return false; + if (getClass() != ins->toGuardToClass()->getClass()) + return false; + return congruentIfOperandsEqual(ins); + } +}; + class MCheckReturn : public MBinaryInstruction, public BoxInputsPolicy::Data diff --git a/js/src/jit/MOpcodes.h b/js/src/jit/MOpcodes.h index bb2ab8190..fddc1e637 100644 --- a/js/src/jit/MOpcodes.h +++ b/js/src/jit/MOpcodes.h @@ -272,6 +272,7 @@ namespace jit { _(IsCallable) \ _(IsObject) \ _(HasClass) \ + _(GuardToClass) \ _(CopySign) \ _(Rotate) \ _(NewDerivedTypedObject) \ diff --git a/js/src/jit/shared/LIR-shared.h b/js/src/jit/shared/LIR-shared.h index 420672013..5e84c3f45 100644 --- a/js/src/jit/shared/LIR-shared.h +++ b/js/src/jit/shared/LIR-shared.h @@ -7891,6 +7891,29 @@ class LHasClass : public LInstructionHelper<1, 1, 0> } }; +class LGuardToClass : public LInstructionHelper<1, 1, 1> +{ + public: + LIR_HEADER(GuardToClass); + explicit LGuardToClass(const LAllocation& lhs, const LDefinition& temp) + { + setOperand(0, lhs); + setTemp(0, temp); + } + + const LAllocation* lhs() { + return getOperand(0); + } + + const LDefinition* temp() { + return getTemp(0); + } + + MGuardToClass* mir() const { + return mir_->toGuardToClass(); + } +}; + template class LWasmSelectBase : public LInstructionHelper { diff --git a/js/src/jit/shared/LOpcodes-shared.h b/js/src/jit/shared/LOpcodes-shared.h index 63731991a..cbc00787a 100644 --- a/js/src/jit/shared/LOpcodes-shared.h +++ b/js/src/jit/shared/LOpcodes-shared.h @@ -387,6 +387,7 @@ _(IsObject) \ _(IsObjectAndBranch) \ _(HasClass) \ + _(GuardToClass) \ _(RecompileCheck) \ _(MemoryBarrier) \ _(AssertRangeI) \ diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp index 615947096..a946bf56a 100644 --- a/js/src/jsgc.cpp +++ b/js/src/jsgc.cpp @@ -2142,7 +2142,7 @@ ArenasToUpdate::getArenasToUpdate(AutoLockHelperThreadState& lock, unsigned maxL return { begin, last->next }; } -struct UpdatePointersTask : public GCParallelTask +struct UpdatePointersTask : public GCParallelTaskHelper { // Maximum number of arenas to update in one block. #ifdef DEBUG @@ -2158,14 +2158,13 @@ struct UpdatePointersTask : public GCParallelTask arenas_.end = nullptr; } - ~UpdatePointersTask() override { join(); } + void run(); private: JSRuntime* rt_; ArenasToUpdate* source_; ArenaListSegment arenas_; - virtual void run() override; bool getArenasToUpdate(); void updateArenas(); }; @@ -2262,7 +2261,7 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s for (size_t i = 0; i < bgTaskCount && !bgArenas.done(); i++) { bgTasks[i].emplace(rt, &bgArenas, lock); startTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock); - tasksStarted = i; + tasksStarted++; } } @@ -2273,6 +2272,8 @@ GCRuntime::updateCellPointers(MovingTracer* trc, Zone* zone, AllocKinds kinds, s for (size_t i = 0; i < tasksStarted; i++) joinTask(*bgTasks[i], gcstats::PHASE_COMPACT_UPDATE_CELLS, lock); + for (size_t i = tasksStarted; i < MaxCellUpdateBackgroundTasks; i++) + MOZ_ASSERT(bgTasks[i].isNothing()); } } @@ -2971,7 +2972,6 @@ js::gc::BackgroundDecommitTask::run() AutoLockGC lock(runtime); for (Chunk* chunk : toDecommit) { - // The arena list is not doubly-linked, so we have to work in the free // list order and not in the natural order. while (chunk->info.numArenasFreeCommitted) { @@ -4383,7 +4383,8 @@ GCRuntime::endMarkingZoneGroup() marker.setMarkColorBlack(); } -class GCSweepTask : public GCParallelTask +template +class GCSweepTask : public GCParallelTaskHelper { GCSweepTask(const GCSweepTask&) = delete; @@ -4393,13 +4394,13 @@ class GCSweepTask : public GCParallelTask public: explicit GCSweepTask(JSRuntime* rt) : runtime(rt) {} GCSweepTask(GCSweepTask&& other) - : GCParallelTask(mozilla::Move(other)), + : GCParallelTaskHelper(mozilla::Move(other)), runtime(other.runtime) {} }; // Causes the given WeakCache to be swept when run. -class SweepWeakCacheTask : public GCSweepTask +class SweepWeakCacheTask : public GCSweepTask { JS::WeakCache& cache; @@ -4411,15 +4412,15 @@ class SweepWeakCacheTask : public GCSweepTask : GCSweepTask(mozilla::Move(other)), cache(other.cache) {} - void run() override { + void run() { cache.sweep(); } }; #define MAKE_GC_SWEEP_TASK(name) \ - class name : public GCSweepTask { \ - void run() override; \ + class name : public GCSweepTask { \ public: \ + void run(); \ explicit name (JSRuntime* rt) : GCSweepTask(rt) {} \ } MAKE_GC_SWEEP_TASK(SweepAtomsTask); @@ -4471,7 +4472,8 @@ SweepMiscTask::run() } void -GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked) +GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked) { if (!task.startWithLockHeld(locked)) { AutoUnlockHelperThreadState unlock(locked); @@ -4481,7 +4483,8 @@ GCRuntime::startTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperT } void -GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, AutoLockHelperThreadState& locked) +GCRuntime::joinTask(GCParallelTask& task, gcstats::Phase phase, + AutoLockHelperThreadState& locked) { gcstats::AutoPhase ap(stats, task, phase); task.joinWithLockHeld(locked); diff --git a/js/src/jsgc.h b/js/src/jsgc.h index 73c658dda..078bc27c6 100644 --- a/js/src/jsgc.h +++ b/js/src/jsgc.h @@ -12,6 +12,7 @@ #include "mozilla/Atomics.h" #include "mozilla/EnumeratedArray.h" #include "mozilla/MemoryReporting.h" +#include "mozilla/Move.h" #include "mozilla/TimeStamp.h" #include "mozilla/TypeTraits.h" @@ -936,10 +937,19 @@ class GCHelperState }; // A generic task used to dispatch work to the helper thread system. -// Users should derive from GCParallelTask add what data they need and -// override |run|. +// Users supply a function pointer to call. +// +// Note that we don't use virtual functions here because destructors can write +// the vtable pointer on entry, which can causes races if synchronization +// happens there. class GCParallelTask { + public: + using TaskFunc = void (*)(GCParallelTask*); + + private: + TaskFunc func_; + // The state of the parallel computation. enum TaskState { NotStarted, @@ -956,19 +966,24 @@ class GCParallelTask // A flag to signal a request for early completion of the off-thread task. mozilla::Atomic cancel_; - virtual void run() = 0; - public: - GCParallelTask() : state(NotStarted), duration_(0) {} + explicit GCParallelTask(TaskFunc func) + : func_(func), + state(NotStarted), + duration_(0), + cancel_(false) + {} + GCParallelTask(GCParallelTask&& other) - : state(other.state), + : func_(other.func_), + state(other.state), duration_(0), cancel_(false) {} // Derived classes must override this to ensure that join() gets called // before members get destructed. - virtual ~GCParallelTask(); + ~GCParallelTask(); // Time spent in the most recent invocation of this task. mozilla::TimeDuration duration() const { return duration_; } @@ -997,12 +1012,34 @@ class GCParallelTask bool isRunningWithLockHeld(const AutoLockHelperThreadState& locked) const; bool isRunning() const; + void runTask() { + func_(this); + } + // This should be friended to HelperThread, but cannot be because it // would introduce several circular dependencies. public: void runFromHelperThread(AutoLockHelperThreadState& locked); }; +// CRTP template to handle cast to derived type when calling run(). +template +class GCParallelTaskHelper : public GCParallelTask +{ + public: + GCParallelTaskHelper() + : GCParallelTask(&runTaskTyped) + {} + GCParallelTaskHelper(GCParallelTaskHelper&& other) + : GCParallelTask(mozilla::Move(other)) + {} + + private: + static void runTaskTyped(GCParallelTask* task) { + static_cast(task)->run(); + } +}; + typedef void (*IterateChunkCallback)(JSRuntime* rt, void* data, gc::Chunk* chunk); typedef void (*IterateZoneCallback)(JSRuntime* rt, void* data, JS::Zone* zone); typedef void (*IterateArenaCallback)(JSRuntime* rt, void* data, gc::Arena* arena, diff --git a/js/src/jsstr.h b/js/src/jsstr.h index 3b92aa21b..7e9621d4a 100644 --- a/js/src/jsstr.h +++ b/js/src/jsstr.h @@ -9,6 +9,7 @@ #include "mozilla/HashFunctions.h" #include "mozilla/PodOperations.h" +#include "mozilla/TextUtils.h" #include @@ -95,7 +96,7 @@ struct JSSubString { #define JS7_UNOCT(c) (JS7_UNDEC(c)) #define JS7_ISHEX(c) ((c) < 128 && isxdigit(c)) #define JS7_UNHEX(c) (unsigned)(JS7_ISDEC(c) ? (c) - '0' : 10 + tolower(c) - 'a') -#define JS7_ISLET(c) ((c) < 128 && isalpha(c)) +#define JS7_ISLET(c) (mozilla::IsAsciiAlpha(c)) extern size_t js_strlen(const char16_t* s); diff --git a/js/src/tests/js1_8_5/extensions/clone-errors.js b/js/src/tests/js1_8_5/extensions/clone-errors.js index f65578a06..d2ccea2e8 100644 --- a/js/src/tests/js1_8_5/extensions/clone-errors.js +++ b/js/src/tests/js1_8_5/extensions/clone-errors.js @@ -25,6 +25,7 @@ check({get x() { throw new Error("fail"); }}); // Mismatched scopes. for (let [write_scope, read_scope] of [['SameProcessSameThread', 'SameProcessDifferentThread'], ['SameProcessSameThread', 'DifferentProcess'], + ['SameProcessDifferentThread', 'DifferentProcessForIndexedDB'], ['SameProcessDifferentThread', 'DifferentProcess']]) { var ab = new ArrayBuffer(12); diff --git a/js/src/tests/js1_8_5/extensions/clone-transferables.js b/js/src/tests/js1_8_5/extensions/clone-transferables.js index 673684b95..9aad27208 100644 --- a/js/src/tests/js1_8_5/extensions/clone-transferables.js +++ b/js/src/tests/js1_8_5/extensions/clone-transferables.js @@ -3,11 +3,15 @@ // http://creativecommons.org/licenses/publicdomain/ function* buffer_options() { - for (var scope of ["SameProcessSameThread", "SameProcessDifferentThread", "DifferentProcess"]) { - for (var size of [0, 8, 16, 200, 1000, 4096, 8192, 65536]) { - yield { scope, size }; + for (var scope of ["SameProcessSameThread", + "SameProcessDifferentThread", + "DifferentProcess", + "DifferentProcessForIndexedDB"]) + { + for (var size of [0, 8, 16, 200, 1000, 4096, 8192, 65536]) { + yield { scope, size }; + } } - } } diff --git a/js/src/vm/HelperThreads.cpp b/js/src/vm/HelperThreads.cpp index 5c8d4ac02..b9f3b02c2 100644 --- a/js/src/vm/HelperThreads.cpp +++ b/js/src/vm/HelperThreads.cpp @@ -1137,7 +1137,7 @@ js::GCParallelTask::runFromMainThread(JSRuntime* rt) MOZ_ASSERT(state == NotStarted); MOZ_ASSERT(js::CurrentThreadCanAccessRuntime(rt)); mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now(); - run(); + runTask(); duration_ = mozilla::TimeStamp::Now() - timeStart; } @@ -1148,7 +1148,7 @@ js::GCParallelTask::runFromHelperThread(AutoLockHelperThreadState& locked) AutoUnlockHelperThreadState parallelSection(locked); gc::AutoSetThreadIsPerformingGC performingGC; mozilla::TimeStamp timeStart = mozilla::TimeStamp::Now(); - run(); + runTask(); duration_ = mozilla::TimeStamp::Now() - timeStart; } diff --git a/js/src/vm/SelfHosting.cpp b/js/src/vm/SelfHosting.cpp index 1e8af993c..4de2e0624 100644 --- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -189,6 +189,22 @@ intrinsic_IsInstanceOfBuiltin(JSContext* cx, unsigned argc, Value* vp) return true; } +template +static bool +intrinsic_GuardToBuiltin(JSContext* cx, unsigned argc, Value* vp) +{ + CallArgs args = CallArgsFromVp(argc, vp); + MOZ_ASSERT(args.length() == 1); + MOZ_ASSERT(args[0].isObject()); + + if (args[0].toObject().is()) { + args.rval().setObject(args[0].toObject()); + return true; + } + args.rval().setNull(); + return true; +} + /** * Self-hosting intrinsic returning the original constructor for a builtin * the name of which is the first and only argument. @@ -2322,18 +2338,18 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("_SetCanonicalName", intrinsic_SetCanonicalName, 2,0), - JS_INLINABLE_FN("IsArrayIterator", - intrinsic_IsInstanceOfBuiltin, 1,0, - IntrinsicIsArrayIterator), - JS_INLINABLE_FN("IsMapIterator", - intrinsic_IsInstanceOfBuiltin, 1,0, - IntrinsicIsMapIterator), - JS_INLINABLE_FN("IsSetIterator", - intrinsic_IsInstanceOfBuiltin, 1,0, - IntrinsicIsSetIterator), - JS_INLINABLE_FN("IsStringIterator", - intrinsic_IsInstanceOfBuiltin, 1,0, - IntrinsicIsStringIterator), + JS_INLINABLE_FN("GuardToArrayIterator", + intrinsic_GuardToBuiltin, 1,0, + IntrinsicGuardToArrayIterator), + JS_INLINABLE_FN("GuardToMapIterator", + intrinsic_GuardToBuiltin, 1,0, + IntrinsicGuardToMapIterator), + JS_INLINABLE_FN("GuardToSetIterator", + intrinsic_GuardToBuiltin, 1,0, + IntrinsicGuardToSetIterator), + JS_INLINABLE_FN("GuardToStringIterator", + intrinsic_GuardToBuiltin, 1,0, + IntrinsicGuardToStringIterator), JS_FN("_CreateMapIterationResultPair", intrinsic_CreateMapIterationResultPair, 0, 0), JS_INLINABLE_FN("_GetNextMapEntryForIterator", intrinsic_GetNextMapEntryForIterator, 2,0, @@ -2436,7 +2452,12 @@ static const JSFunctionSpec intrinsic_functions[] = { JS_FN("CallStarGeneratorMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), + JS_INLINABLE_FN("GuardToMapObject", intrinsic_GuardToBuiltin, 1, 0, + IntrinsicGuardToMapObject), JS_FN("IsWeakSet", intrinsic_IsInstanceOfBuiltin, 1,0), + + JS_INLINABLE_FN("GuardToSetObject", intrinsic_GuardToBuiltin, 1, 0, + IntrinsicGuardToSetObject), JS_FN("CallWeakSetMethodIfWrapped", CallNonGenericSelfhostedMethod>, 2, 0), diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 4ece91da7..434ba5aab 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -160,16 +160,16 @@ template struct BufferIterator { typedef mozilla::BufferList BufferList; - explicit BufferIterator(BufferList& buffer) + explicit BufferIterator(const BufferList& buffer) : mBuffer(buffer) , mIter(buffer.Iter()) { JS_STATIC_ASSERT(8 % sizeof(T) == 0); } - BufferIterator(const BufferIterator& other) - : mBuffer(other.mBuffer) - , mIter(other.mIter) + explicit BufferIterator(const JSStructuredCloneData& data) + : mBuffer(data.bufList_) + , mIter(data.Start()) { } @@ -228,7 +228,7 @@ struct BufferIterator { return mIter.HasRoomFor(sizeof(T)); } - BufferList& mBuffer; + const BufferList& mBuffer; typename BufferList::IterImpl mIter; }; @@ -295,13 +295,22 @@ SharedArrayRawBufferRefs::releaseAll() refs_.clear(); } +// SCOutput provides an interface to write raw data -- eg uint64_ts, doubles, +// arrays of bytes -- into a structured clone data output stream. It also knows +// how to free any transferable data within that stream. +// +// Note that it contains a full JSStructuredCloneData object, which holds the +// callbacks necessary to read/write/transfer/free the data. For the purpose of +// this class, only the freeTransfer callback is relevant; the rest of the callbacks +// are used by the higher-level JSStructuredCloneWriter interface. struct SCOutput { public: - using Iter = BufferIterator; + using Iter = BufferIterator; - explicit SCOutput(JSContext* cx); + SCOutput(JSContext* cx, JS::StructuredCloneScope scope); JSContext* context() const { return cx; } + JS::StructuredCloneScope scope() const { return buf.scope(); } bool write(uint64_t u); bool writePair(uint32_t tag, uint32_t data); @@ -314,22 +323,25 @@ struct SCOutput { template bool writeArray(const T* p, size_t nbytes); - bool extractBuffer(JSStructuredCloneData* data); - void discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure); + void setCallbacks(const JSStructuredCloneCallbacks* callbacks, + void* closure, + OwnTransferablePolicy policy) + { + buf.setCallbacks(callbacks, closure, policy); + } + void extractBuffer(JSStructuredCloneData* data) { *data = Move(buf); } + void discardTransferables(); uint64_t tell() const { return buf.Size(); } uint64_t count() const { return buf.Size() / sizeof(uint64_t); } - Iter iter() { - return BufferIterator(buf); - } + Iter iter() { return Iter(buf); } size_t offset(Iter dest) { return dest - iter(); } - private: JSContext* cx; - mozilla::BufferList buf; + JSStructuredCloneData buf; }; class SCInput { @@ -419,13 +431,6 @@ struct JSStructuredCloneReader { // be valid cross-process.) JS::StructuredCloneScope allowedScope; - // The scope the buffer was generated for (what sort of buffer it is.) The - // scope is not just a permissions thing; it also affects the storage - // format (eg a Transferred ArrayBuffer can be stored as a pointer for - // SameProcessSameThread but must have its contents in the clone buffer for - // DifferentProcess.) - JS::StructuredCloneScope storedScope; - // Stack of objects with properties remaining to be read. AutoValueVector objs; @@ -449,13 +454,15 @@ struct JSStructuredCloneWriter { const JSStructuredCloneCallbacks* cb, void* cbClosure, const Value& tVal) - : out(cx), scope(scope), objs(out.context()), + : out(cx, scope), objs(out.context()), counts(out.context()), entries(out.context()), - memory(out.context()), callbacks(cb), - closure(cbClosure), transferable(out.context(), tVal), + memory(out.context()), + transferable(out.context(), tVal), transferableObjects(out.context(), GCHashSet(cx)), cloneDataPolicy(cloneDataPolicy) - {} + { + out.setCallbacks(cb, cbClosure, OwnTransferablePolicy::NoTransferables); + } ~JSStructuredCloneWriter(); @@ -471,20 +478,10 @@ struct JSStructuredCloneWriter { SCOutput& output() { return out; } - bool extractBuffer(JSStructuredCloneData* data) { - bool success = out.extractBuffer(data); - if (success) { - // Move the SharedArrayRawBuf references here, SCOutput::extractBuffer - // moves the serialized data. - data->refsHeld_.takeOwnership(Move(refsHeld)); - data->setOptionalCallbacks(callbacks, closure, - OwnTransferablePolicy::OwnsTransferablesIfAny); - } - return success; + void extractBuffer(JSStructuredCloneData* newData) { + out.extractBuffer(newData); } - JS::StructuredCloneScope cloneScope() const { return scope; } - private: JSStructuredCloneWriter() = delete; JSStructuredCloneWriter(const JSStructuredCloneWriter&) = delete; @@ -515,9 +512,6 @@ struct JSStructuredCloneWriter { SCOutput out; - // The (address space, thread) scope within which this clone is valid. - JS::StructuredCloneScope scope; - // Vector of objects with properties remaining to be written. // // NB: These can span multiple compartments, so the compartment must be @@ -543,12 +537,6 @@ struct JSStructuredCloneWriter { SystemAllocPolicy>; Rooted memory; - // The user defined callbacks that will be used for cloning. - const JSStructuredCloneCallbacks* callbacks; - - // Any value passed to JS_WriteStructuredClone. - void* closure; - // Set of transferable objects RootedValue transferable; Rooted> transferableObjects; @@ -615,7 +603,12 @@ WriteStructuredClone(JSContext* cx, HandleValue v, JSStructuredCloneData* bufp, const Value& transferable) { JSStructuredCloneWriter w(cx, scope, cloneDataPolicy, cb, cbClosure, transferable); - return w.init() && w.write(v) && w.extractBuffer(bufp); + if (!w.init()) + return false; + if (!w.write(v)) + return false; + w.extractBuffer(bufp); + return true; } bool @@ -628,91 +621,15 @@ ReadStructuredClone(JSContext* cx, JSStructuredCloneData& data, return r.read(vp); } -// If the given buffer contains Transferables, free them. Note that custom -// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to -// delete their transferables. -template -static void -DiscardTransferables(mozilla::BufferList& buffer, - const JSStructuredCloneCallbacks* cb, void* cbClosure) -{ - auto point = BufferIterator(buffer); - if (point.done()) - return; // Empty buffer - - uint32_t tag, data; - MOZ_RELEASE_ASSERT(point.canPeek()); - SCInput::getPair(point.peek(), &tag, &data); - point.next(); - - if (tag == SCTAG_HEADER) { - if (point.done()) - return; - - MOZ_RELEASE_ASSERT(point.canPeek()); - SCInput::getPair(point.peek(), &tag, &data); - point.next(); - } - - if (tag != SCTAG_TRANSFER_MAP_HEADER) - return; - - if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED) - return; - - // freeTransfer should not GC - JS::AutoSuppressGCAnalysis nogc; - - if (point.done()) - return; - - uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek()); - point.next(); - while (numTransferables--) { - if (!point.canPeek()) - return; - - uint32_t ownership; - SCInput::getPair(point.peek(), &tag, &ownership); - point.next(); - MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY); - if (!point.canPeek()) - return; - - void* content; - SCInput::getPtr(point.peek(), &content); - point.next(); - if (!point.canPeek()) - return; - - uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek()); - point.next(); - - if (ownership < JS::SCTAG_TMO_FIRST_OWNED) - continue; - - if (ownership == JS::SCTAG_TMO_ALLOC_DATA) { - js_free(content); - } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) { - JS_ReleaseMappedArrayBufferContents(content, extraData); - } else if (cb && cb->freeTransfer) { - cb->freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, cbClosure); - } else { - MOZ_ASSERT(false, "unknown ownership"); - } - } -} - static bool StructuredCloneHasTransferObjects(const JSStructuredCloneData& data) { - auto iter = data.Iter(); - if (data.Size() < sizeof(uint64_t)) return false; uint64_t u; - data.ReadBytes(iter, reinterpret_cast(&u), sizeof(u)); + BufferIterator iter(data); + MOZ_ALWAYS_TRUE(iter.readBytes(reinterpret_cast(&u), sizeof(u))); uint32_t tag = uint32_t(u >> 32); return (tag == SCTAG_TRANSFER_MAP_HEADER); } @@ -723,7 +640,7 @@ SCInput::SCInput(JSContext* cx, JSStructuredCloneData& data) : cx(cx), point(data) { - static_assert(JSStructuredCloneData::kSegmentAlignment % 8 == 0, + static_assert(JSStructuredCloneData::BufferList::kSegmentAlignment % 8 == 0, "structured clone buffer reads should be aligned"); MOZ_ASSERT(data.Size() % 8 == 0); } @@ -885,9 +802,8 @@ SCInput::readPtr(void** p) return true; } -SCOutput::SCOutput(JSContext* cx) - : cx(cx) - , buf(0, 0, 4096, cx) +SCOutput::SCOutput(JSContext* cx, JS::StructuredCloneScope scope) + : cx(cx), buf(scope) { } @@ -895,7 +811,11 @@ bool SCOutput::write(uint64_t u) { uint64_t v = NativeEndian::swapToLittleEndian(u); - return buf.WriteBytes(reinterpret_cast(&v), sizeof(u)); + if (!buf.AppendBytes(reinterpret_cast(&v), sizeof(u))) { + ReportOutOfMemory(context()); + return false; + } + return true; } bool @@ -956,7 +876,7 @@ SCOutput::writeArray(const T* p, size_t nelems) for (size_t i = 0; i < nelems; i++) { T value = swapToLittleEndian(p[i]); - if (!buf.WriteBytes(reinterpret_cast(&value), sizeof(value))) + if (!buf.AppendBytes(reinterpret_cast(&value), sizeof(value))) return false; } @@ -965,7 +885,7 @@ SCOutput::writeArray(const T* p, size_t nelems) size_t padbytes = sizeof(uint64_t) * nwords - sizeof(T) * nelems; char zero = 0; for (size_t i = 0; i < padbytes; i++) { - if (!buf.WriteBytes(&zero, sizeof(zero))) + if (!buf.AppendBytes(&zero, sizeof(zero))) return false; } @@ -1000,34 +920,101 @@ SCOutput::writePtr(const void* p) return write(reinterpret_cast(p)); } -bool -SCOutput::extractBuffer(JSStructuredCloneData* data) -{ - bool success; - mozilla::BufferList out = - buf.MoveFallible(&success); - if (!success) { - ReportOutOfMemory(cx); - return false; - } - *data = JSStructuredCloneData(Move(out)); - return true; -} - void -SCOutput::discardTransferables(const JSStructuredCloneCallbacks* cb, void* cbClosure) +SCOutput::discardTransferables() { - DiscardTransferables(buf, cb, cbClosure); + buf.discardTransferables(); } } /* namespace js */ -JSStructuredCloneData::~JSStructuredCloneData() + +// If the buffer contains Transferables, free them. Note that custom +// Transferables will use the JSStructuredCloneCallbacks::freeTransfer() to +// delete their transferables. +void +JSStructuredCloneData::discardTransferables() { if (!Size()) return; - if (ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny) - DiscardTransferables(*this, callbacks_, closure_); + + if (ownTransferables_ != OwnTransferablePolicy::OwnsTransferablesIfAny) + return; + + // DifferentProcess clones cannot contain pointers, so nothing needs to be + // released. + if (scope_ == JS::StructuredCloneScope::DifferentProcess) + return; + + FreeTransferStructuredCloneOp freeTransfer = nullptr; + if (callbacks_) + freeTransfer = callbacks_->freeTransfer; + + auto point = BufferIterator(*this); + if (point.done()) + return; // Empty buffer + + uint32_t tag, data; + MOZ_RELEASE_ASSERT(point.canPeek()); + SCInput::getPair(point.peek(), &tag, &data); + point.next(); + + if (tag == SCTAG_HEADER) { + if (point.done()) + return; + + MOZ_RELEASE_ASSERT(point.canPeek()); + SCInput::getPair(point.peek(), &tag, &data); + point.next(); + } + + if (tag != SCTAG_TRANSFER_MAP_HEADER) + return; + + if (TransferableMapHeader(data) == SCTAG_TM_TRANSFERRED) + return; + + // freeTransfer should not GC + JS::AutoSuppressGCAnalysis nogc; + + if (point.done()) + return; + + uint64_t numTransferables = NativeEndian::swapFromLittleEndian(point.peek()); + point.next(); + while (numTransferables--) { + if (!point.canPeek()) + return; + + uint32_t ownership; + SCInput::getPair(point.peek(), &tag, &ownership); + point.next(); + MOZ_ASSERT(tag >= SCTAG_TRANSFER_MAP_PENDING_ENTRY); + if (!point.canPeek()) + return; + + void* content; + SCInput::getPtr(point.peek(), &content); + point.next(); + if (!point.canPeek()) + return; + + uint64_t extraData = NativeEndian::swapFromLittleEndian(point.peek()); + point.next(); + + if (ownership < JS::SCTAG_TMO_FIRST_OWNED) + continue; + + if (ownership == JS::SCTAG_TMO_ALLOC_DATA) { + js_free(content); + } else if (ownership == JS::SCTAG_TMO_MAPPED_DATA) { + JS_ReleaseMappedArrayBufferContents(content, extraData); + } else if (freeTransfer) { + freeTransfer(tag, JS::TransferableOwnership(ownership), content, extraData, closure_); + } else { + MOZ_ASSERT(false, "unknown ownership"); + } + } } JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); @@ -1035,9 +1022,8 @@ JS_STATIC_ASSERT(JSString::MAX_LENGTH < UINT32_MAX); JSStructuredCloneWriter::~JSStructuredCloneWriter() { // Free any transferable data left lying around in the buffer - if (out.count()) { - out.discardTransferables(callbacks, closure); - } + if (out.count()) + out.discardTransferables(); } bool @@ -1106,7 +1092,7 @@ JSStructuredCloneWriter::parseTransferable() bool JSStructuredCloneWriter::reportDataCloneError(uint32_t errorId) { - ReportDataCloneError(context(), callbacks, errorId); + ReportDataCloneError(context(), out.buf.callbacks_, errorId); return false; } @@ -1521,8 +1507,8 @@ JSStructuredCloneWriter::startWrite(HandleValue v) return traverseSavedFrame(obj); } - if (callbacks && callbacks->write) - return callbacks->write(context(), this, obj, closure); + if (out.buf.callbacks_ && out.buf.callbacks_->write) + return out.buf.callbacks_->write(context(), this, obj, out.buf.closure_); /* else fall through */ } @@ -1532,7 +1518,7 @@ JSStructuredCloneWriter::startWrite(HandleValue v) bool JSStructuredCloneWriter::writeHeader() { - return out.writePair(SCTAG_HEADER, (uint32_t)scope); + return out.writePair(SCTAG_HEADER, (uint32_t)output().scope()); } bool @@ -1590,6 +1576,7 @@ JSStructuredCloneWriter::transferOwnership() JSContext* cx = context(); RootedObject obj(cx); + JS::StructuredCloneScope scope = output().scope(); for (auto tr = transferableObjects.all(); !tr.empty(); tr.popFront()) { obj = tr.front(); @@ -1622,7 +1609,9 @@ JSStructuredCloneWriter::transferOwnership() return false; } - if (scope == JS::StructuredCloneScope::DifferentProcess) { + if (scope == JS::StructuredCloneScope::DifferentProcess || + scope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) + { // Write Transferred ArrayBuffers in DifferentProcess scope at // the end of the clone buffer, and store the offset within the // buffer to where the ArrayBuffer was written. Note that this @@ -1659,9 +1648,9 @@ JSStructuredCloneWriter::transferOwnership() extraData = nbytes; } } else { - if (!callbacks || !callbacks->writeTransfer) + if (!out.buf.callbacks_ || !out.buf.callbacks_->writeTransfer) return reportDataCloneError(JS_SCERR_TRANSFERABLE); - if (!callbacks->writeTransfer(cx, obj, closure, &tag, &ownership, &content, &extraData)) + if (!out.buf.callbacks_->writeTransfer(cx, obj, out.buf.closure_, &tag, &ownership, &content, &extraData)) return false; MOZ_ASSERT(tag > SCTAG_TRANSFER_MAP_PENDING_ENTRY); } @@ -2260,25 +2249,33 @@ JSStructuredCloneReader::readHeader() if (!in.getPair(&tag, &data)) return in.reportTruncated(); - if (tag != SCTAG_HEADER) { + JS::StructuredCloneScope storedScope; + if (tag == SCTAG_HEADER) { + MOZ_ALWAYS_TRUE(in.readPair(&tag, &data)); + storedScope = JS::StructuredCloneScope(data); + } else { // Old structured clone buffer. We must have read it from disk. - storedScope = JS::StructuredCloneScope::DifferentProcess; - return true; + storedScope = JS::StructuredCloneScope::DifferentProcessForIndexedDB; } - MOZ_ALWAYS_TRUE(in.readPair(&tag, &data)); - storedScope = JS::StructuredCloneScope(data); - - if (data != uint32_t(JS::StructuredCloneScope::SameProcessSameThread) && - data != uint32_t(JS::StructuredCloneScope::SameProcessDifferentThread) && - data != uint32_t(JS::StructuredCloneScope::DifferentProcess)) + if (storedScope < JS::StructuredCloneScope::SameProcessSameThread || + storedScope > JS::StructuredCloneScope::DifferentProcessForIndexedDB) { JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA, "invalid structured clone scope"); return false; } + + if (allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) { + // Bug 1434308 and bug 1458320 - the scopes stored in old IndexedDB + // clones are incorrect. Treat them as if they were DifferentProcess. + allowedScope = JS::StructuredCloneScope::DifferentProcess; + return true; + } + if (storedScope < allowedScope) { - JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, JSMSG_SC_BAD_SERIALIZED_DATA, + JS_ReportErrorNumberASCII(context(), GetErrorMessage, nullptr, + JSMSG_SC_BAD_SERIALIZED_DATA, "incompatible structured clone scope"); return false; } @@ -2322,10 +2319,14 @@ JSStructuredCloneReader::readTransferMap() return false; if (tag == SCTAG_TRANSFER_MAP_ARRAY_BUFFER) { - if (storedScope == JS::StructuredCloneScope::DifferentProcess) { + if (allowedScope == JS::StructuredCloneScope::DifferentProcess || + allowedScope == JS::StructuredCloneScope::DifferentProcessForIndexedDB) + { // Transferred ArrayBuffers in a DifferentProcess clone buffer - // are treated as if they weren't Transferred at all. - continue; + // are treated as if they weren't Transferred at all. We should + // only see SCTAG_TRANSFER_MAP_STORED_ARRAY_BUFFER. + ReportDataCloneError(cx, callbacks, JS_SCERR_TRANSFERABLE); + return false; } size_t nbytes = extraData; @@ -2659,7 +2660,7 @@ JS_StructuredClone(JSContext* cx, HandleValue value, MutableHandleValue vp, } JSAutoStructuredCloneBuffer::JSAutoStructuredCloneBuffer(JSAutoStructuredCloneBuffer&& other) - : scope_(other.scope_) + : scope_(other.scope()), data_(other.scope()) { data_.ownTransferables_ = other.data_.ownTransferables_; other.steal(&data_, &version_, &data_.callbacks_, &data_.closure_); @@ -2677,51 +2678,14 @@ JSAutoStructuredCloneBuffer::operator=(JSAutoStructuredCloneBuffer&& other) } void -JSAutoStructuredCloneBuffer::clear(const JSStructuredCloneCallbacks* optionalCallbacks, - void* optionalClosure) +JSAutoStructuredCloneBuffer::clear() { - if (!data_.Size()) - return; - - const JSStructuredCloneCallbacks* callbacks = - optionalCallbacks ? optionalCallbacks : data_.callbacks_; - void* closure = optionalClosure ? optionalClosure : data_.closure_; - - if (data_.ownTransferables_ == OwnTransferablePolicy::OwnsTransferablesIfAny) - DiscardTransferables(data_, callbacks, closure); + data_.discardTransferables(); data_.ownTransferables_ = OwnTransferablePolicy::NoTransferables; - data_.refsHeld_.releaseAll(); data_.Clear(); version_ = 0; } -bool -JSAutoStructuredCloneBuffer::copy(JSContext* cx, const JSStructuredCloneData& srcData, - uint32_t version, const JSStructuredCloneCallbacks* callbacks, - void* closure) -{ - // transferable objects cannot be copied - if (StructuredCloneHasTransferObjects(srcData)) - return false; - - clear(); - - auto iter = srcData.Iter(); - while (!iter.Done()) { - if (!data_.WriteBytes(iter.Data(), iter.RemainingInSegment())) - return false; - iter.Advance(srcData, iter.RemainingInSegment()); - } - - version_ = version; - - if (!data_.refsHeld_.acquireAll(cx, srcData.refsHeld_)) - return false; - - data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::NoTransferables); - return true; -} - void JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t version, const JSStructuredCloneCallbacks* callbacks, @@ -2730,7 +2694,7 @@ JSAutoStructuredCloneBuffer::adopt(JSStructuredCloneData&& data, uint32_t versio clear(); data_ = Move(data); version_ = version; - data_.setOptionalCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny); + data_.setCallbacks(callbacks, closure, OwnTransferablePolicy::OwnsTransferablesIfAny); } void @@ -2747,7 +2711,7 @@ JSAutoStructuredCloneBuffer::steal(JSStructuredCloneData* data, uint32_t* versio *data = Move(data_); version_ = 0; - data_.setOptionalCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables); + data_.setCallbacks(nullptr, nullptr, OwnTransferablePolicy::NoTransferables); } bool @@ -2861,5 +2825,5 @@ JS_ObjectNotWritten(JSStructuredCloneWriter* w, HandleObject obj) JS_PUBLIC_API(JS::StructuredCloneScope) JS_GetStructuredCloneScope(JSStructuredCloneWriter* w) { - return w->cloneScope(); + return w->output().scope(); } diff --git a/mfbt/TextUtils.h b/mfbt/TextUtils.h new file mode 100644 index 000000000..9494296ce --- /dev/null +++ b/mfbt/TextUtils.h @@ -0,0 +1,58 @@ +/* -*- 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/. */ + +/* Character/text operations. */ + +#ifndef mozilla_TextUtils_h +#define mozilla_TextUtils_h + +#include "mozilla/TypeTraits.h" + +namespace mozilla { + +namespace detail { + +template +class MakeUnsignedChar + : public MakeUnsigned +{}; + +template<> +class MakeUnsignedChar +{ +public: + using Type = char16_t; +}; + +template<> +class MakeUnsignedChar +{ +public: + using Type = char32_t; +}; + +} // namespace detail + +/** + * Returns true iff |aChar| matches [a-zA-Z]. + * + * This function is basically what you thought isalpha was, except its behavior + * doesn't depend on the user's current locale. + */ +template +constexpr bool +IsAsciiAlpha(Char aChar) +{ + using UnsignedChar = typename detail::MakeUnsignedChar::Type; + return ('a' <= static_cast(aChar) && + static_cast(aChar) <= 'z') || + ('A' <= static_cast(aChar) && + static_cast(aChar) <= 'Z'); +} + +} // namespace mozilla + +#endif /* mozilla_TextUtils_h */ diff --git a/mfbt/moz.build b/mfbt/moz.build index f3c996a0f..6d023704f 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -87,6 +87,7 @@ EXPORTS.mozilla = [ 'StaticAnalysisFunctions.h', 'TaggedAnonymousMemory.h', 'TemplateLib.h', + 'TextUtils.h', 'ThreadLocal.h', 'ToString.h', 'Tuple.h', diff --git a/mfbt/tests/TestTextUtils.cpp b/mfbt/tests/TestTextUtils.cpp new file mode 100644 index 000000000..db481c138 --- /dev/null +++ b/mfbt/tests/TestTextUtils.cpp @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "mozilla/Assertions.h" +#include "mozilla/TextUtils.h" + +using mozilla::IsAsciiAlpha; + +// char + +static_assert(!IsAsciiAlpha('@'), "'@' isn't ASCII alpha"); +static_assert('@' == 0x40, "'@' has value 0x40"); + +static_assert('A' == 0x41, "'A' has value 0x41"); +static_assert(IsAsciiAlpha('A'), "'A' is ASCII alpha"); +static_assert(IsAsciiAlpha('B'), "'B' is ASCII alpha"); +static_assert(IsAsciiAlpha('M'), "'M' is ASCII alpha"); +static_assert(IsAsciiAlpha('Y'), "'Y' is ASCII alpha"); +static_assert(IsAsciiAlpha('Z'), "'Z' is ASCII alpha"); + +static_assert('Z' == 0x5A, "'Z' has value 0x5A"); +static_assert('[' == 0x5B, "'[' has value 0x5B"); +static_assert(!IsAsciiAlpha('['), "'[' isn't ASCII alpha"); + +static_assert(!IsAsciiAlpha('`'), "'`' isn't ASCII alpha"); +static_assert('`' == 0x60, "'`' has value 0x60"); + +static_assert('a' == 0x61, "'a' has value 0x61"); +static_assert(IsAsciiAlpha('a'), "'a' is ASCII alpha"); +static_assert(IsAsciiAlpha('b'), "'b' is ASCII alpha"); +static_assert(IsAsciiAlpha('m'), "'m' is ASCII alpha"); +static_assert(IsAsciiAlpha('y'), "'y' is ASCII alpha"); +static_assert(IsAsciiAlpha('z'), "'z' is ASCII alpha"); + +static_assert('z' == 0x7A, "'z' has value 0x7A"); +static_assert('{' == 0x7B, "'{' has value 0x7B"); +static_assert(!IsAsciiAlpha('{'), "'{' isn't ASCII alpha"); + +// char16_t + +static_assert(!IsAsciiAlpha(u'@'), "u'@' isn't ASCII alpha"); +static_assert(u'@' == 0x40, "u'@' has value 0x40"); + +static_assert(u'A' == 0x41, "u'A' has value 0x41"); +static_assert(IsAsciiAlpha(u'A'), "u'A' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'B'), "u'B' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'M'), "u'M' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'Y'), "u'Y' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'Z'), "u'Z' is ASCII alpha"); + +static_assert(u'Z' == 0x5A, "u'Z' has value 0x5A"); +static_assert(u'[' == 0x5B, "u'[' has value 0x5B"); +static_assert(!IsAsciiAlpha(u'['), "u'[' isn't ASCII alpha"); + +static_assert(!IsAsciiAlpha(u'`'), "u'`' isn't ASCII alpha"); +static_assert(u'`' == 0x60, "u'`' has value 0x60"); + +static_assert(u'a' == 0x61, "u'a' has value 0x61"); +static_assert(IsAsciiAlpha(u'a'), "u'a' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'b'), "u'b' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'm'), "u'm' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'y'), "u'y' is ASCII alpha"); +static_assert(IsAsciiAlpha(u'z'), "u'z' is ASCII alpha"); + +static_assert(u'z' == 0x7A, "u'z' has value 0x7A"); +static_assert(u'{' == 0x7B, "u'{' has value 0x7B"); +static_assert(!IsAsciiAlpha(u'{'), "u'{' isn't ASCII alpha"); + +// char32_t + +static_assert(!IsAsciiAlpha(U'@'), "U'@' isn't ASCII alpha"); +static_assert(U'@' == 0x40, "U'@' has value 0x40"); + +static_assert(U'A' == 0x41, "U'A' has value 0x41"); +static_assert(IsAsciiAlpha(U'A'), "U'A' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'B'), "U'B' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'M'), "U'M' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'Y'), "U'Y' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'Z'), "U'Z' is ASCII alpha"); + +static_assert(U'Z' == 0x5A, "U'Z' has value 0x5A"); +static_assert(U'[' == 0x5B, "U'[' has value 0x5B"); +static_assert(!IsAsciiAlpha(U'['), "U'[' isn't ASCII alpha"); + +static_assert(!IsAsciiAlpha(U'`'), "U'`' isn't ASCII alpha"); +static_assert(U'`' == 0x60, "U'`' has value 0x60"); + +static_assert(U'a' == 0x61, "U'a' has value 0x61"); +static_assert(IsAsciiAlpha(U'a'), "U'a' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'b'), "U'b' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'm'), "U'm' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'y'), "U'y' is ASCII alpha"); +static_assert(IsAsciiAlpha(U'z'), "U'z' is ASCII alpha"); + +static_assert(U'z' == 0x7A, "U'z' has value 0x7A"); +static_assert(U'{' == 0x7B, "U'{' has value 0x7B"); +static_assert(!IsAsciiAlpha(U'{'), "U'{' isn't ASCII alpha"); + +int +main() +{ + return 0; +} diff --git a/mfbt/tests/moz.build b/mfbt/tests/moz.build index 62125ee31..2b4fe5930 100644 --- a/mfbt/tests/moz.build +++ b/mfbt/tests/moz.build @@ -42,6 +42,7 @@ CppUnitTests([ 'TestSHA1', 'TestSplayTree', 'TestTemplateLib', + 'TestTextUtils', 'TestTuple', 'TestTypedEnum', 'TestTypeTraits', diff --git a/uriloader/exthandler/win/nsMIMEInfoWin.cpp b/uriloader/exthandler/win/nsMIMEInfoWin.cpp index 2c7171c87..6ead8f4c3 100644 --- a/uriloader/exthandler/win/nsMIMEInfoWin.cpp +++ b/uriloader/exthandler/win/nsMIMEInfoWin.cpp @@ -243,8 +243,7 @@ nsMIMEInfoWin::LoadUriInternal(nsIURI * aURL) SHELLEXECUTEINFOW sinfo; memset(&sinfo, 0, sizeof(sinfo)); sinfo.cbSize = sizeof(sinfo); - sinfo.fMask = SEE_MASK_FLAG_DDEWAIT | - SEE_MASK_FLAG_NO_UI; + sinfo.fMask = SEE_MASK_FLAG_DDEWAIT; sinfo.hwnd = nullptr; sinfo.lpVerb = (LPWSTR)&cmdVerb; sinfo.nShow = SW_SHOWNORMAL; diff --git a/widget/GfxInfoBase.cpp b/widget/GfxInfoBase.cpp index a57080bb4..5efb73d74 100644 --- a/widget/GfxInfoBase.cpp +++ b/widget/GfxInfoBase.cpp @@ -68,11 +68,15 @@ public: delete GfxInfoBase::mFeatureStatus; GfxInfoBase::mFeatureStatus = nullptr; - for (uint32_t i = 0; i < DeviceFamilyMax; i++) + for (uint32_t i = 0; i < DeviceFamilyMax; i++) { delete GfxDriverInfo::mDeviceFamilies[i]; + GfxDriverInfo::mDeviceFamilies[i] = nullptr; + } - for (uint32_t i = 0; i < DeviceVendorMax; i++) + for (uint32_t i = 0; i < DeviceVendorMax; i++) { delete GfxDriverInfo::mDeviceVendors[i]; + GfxDriverInfo::mDeviceVendors[i] = nullptr; + } GfxInfoBase::mShutdownOccurred = true; diff --git a/widget/GfxInfoX11.cpp b/widget/GfxInfoX11.cpp index 48fc3dbb5..338dcac67 100644 --- a/widget/GfxInfoX11.cpp +++ b/widget/GfxInfoX11.cpp @@ -268,8 +268,6 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, OperatingSystem* aOS /* = nullptr */) { - GetData(); - NS_ENSURE_ARG_POINTER(aStatus); *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; aSuggestedDriverVersion.SetIsVoid(true); @@ -277,6 +275,12 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aOS) *aOS = os; + if (mShutdownOccurred) { + return NS_OK; + } + + GetData(); + if (mGLMajorVersion == 1) { // We're on OpenGL 1. In most cases that indicates really old hardware. // We better block them, rather than rely on them to fail gracefully, because they don't! diff --git a/widget/android/GfxInfo.cpp b/widget/android/GfxInfo.cpp index c2de6772c..5f9d1aba5 100644 --- a/widget/android/GfxInfo.cpp +++ b/widget/android/GfxInfo.cpp @@ -376,6 +376,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aOS) *aOS = os; + if (mShutdownOccurred) { + return NS_OK; + } + // OpenGL layers are never blacklisted on Android. // This early return is so we avoid potentially slow // GLStrings initialization on startup when we initialize GL layers. diff --git a/widget/cocoa/GfxInfo.mm b/widget/cocoa/GfxInfo.mm index c68ad7e12..a574517e8 100644 --- a/widget/cocoa/GfxInfo.mm +++ b/widget/cocoa/GfxInfo.mm @@ -307,6 +307,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aOS) *aOS = os; + if (mShutdownOccurred) { + return NS_OK; + } + // Don't evaluate special cases when we're evaluating the downloaded blocklist. if (!aDriverInfo.Length()) { if (aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) { diff --git a/widget/uikit/GfxInfo.cpp b/widget/uikit/GfxInfo.cpp index 2aea3b5ea..cabe993dd 100644 --- a/widget/uikit/GfxInfo.cpp +++ b/widget/uikit/GfxInfo.cpp @@ -175,6 +175,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aOS) *aOS = OperatingSystem::Ios; + if (mShutdownOccurred) { + return NS_OK; + } + // OpenGL layers are never blacklisted on iOS. // This early return is so we avoid potentially slow // GLStrings initialization on startup when we initialize GL layers. diff --git a/widget/windows/GfxInfo.cpp b/widget/windows/GfxInfo.cpp index ec2877225..2d3a63ff0 100644 --- a/widget/windows/GfxInfo.cpp +++ b/widget/windows/GfxInfo.cpp @@ -1160,6 +1160,10 @@ GfxInfo::GetFeatureStatusImpl(int32_t aFeature, if (aOS) *aOS = os; + if (mShutdownOccurred) { + return NS_OK; + } + // Don't evaluate special cases if we're checking the downloaded blocklist. if (!aDriverInfo.Length()) { nsAutoString adapterVendorID;