From e5cf1f3ee968ee490993835dda3822df3dd64ef3 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:47 +0000 Subject: [PATCH 01/65] Bug 1786465 - Convert OriginPrivateFileSystemChild to a standard actor; r=dom-storage-reviewers,jesup The send methods which need to be mocked in tests are declared in OriginPrivateFileSystemChild the same way as POriginPrivateFileSystemChild send methods, just without the Msg suffix and virtual. The relevant POriginPrivateFileSystemChild send methods are hidden in OriginPrivateFileSystemChild, so they can only be used internally for the forwarding. Differential Revision: https://phabricator.services.mozilla.com/D155957 --- .../FileSystemBackgroundRequestHandler.cpp | 2 +- dom/fs/child/FileSystemChildFactory.cpp | 102 +------------- dom/fs/child/OriginPrivateFileSystemChild.h | 128 ++++++++++++++---- dom/fs/include/fs/FileSystemChildFactory.h | 2 - .../parent/OriginPrivateFileSystemParent.cpp | 30 ++-- dom/fs/parent/OriginPrivateFileSystemParent.h | 30 ++-- dom/fs/shared/POriginPrivateFileSystem.ipdl | 14 +- dom/fs/test/gtest/FileSystemMocks.h | 12 +- ...TestFileSystemBackgroundRequestHandler.cpp | 18 +-- .../child/TestFileSystemRequestHandler.cpp | 24 +++- 10 files changed, 165 insertions(+), 197 deletions(-) diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp index 3f11d6a422d00..66d385b2e02e4 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp @@ -113,7 +113,7 @@ FileSystemBackgroundRequestHandler::CreateFileSystemManagerChild( &parentEndpoint, &childEndpoint)); RefPtr child = mChildFactory->Create(); - if (!childEndpoint.Bind(child->AsBindable())) { + if (!childEndpoint.Bind(child)) { return CreateFileSystemManagerChildPromise::CreateAndReject( NS_ERROR_FAILURE, __func__); } diff --git a/dom/fs/child/FileSystemChildFactory.cpp b/dom/fs/child/FileSystemChildFactory.cpp index d6085ac3379aa..3b50a7a6fcd4a 100644 --- a/dom/fs/child/FileSystemChildFactory.cpp +++ b/dom/fs/child/FileSystemChildFactory.cpp @@ -5,111 +5,15 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "fs/FileSystemChildFactory.h" -#include "mozilla/dom/POriginPrivateFileSystemChild.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" + #include "mozilla/RefPtr.h" -#include "nsISupportsImpl.h" +#include "mozilla/dom/OriginPrivateFileSystemChild.h" namespace mozilla::dom::fs { -namespace { - -using ResolveGetHandle = - mozilla::ipc::ResolveCallback; -using ResolveGetFile = - mozilla::ipc::ResolveCallback; -using ResolveResolve = - mozilla::ipc::ResolveCallback; -using ResolveGetEntries = - mozilla::ipc::ResolveCallback; -using ResolveRemoveEntry = - mozilla::ipc::ResolveCallback; -using DoReject = mozilla::ipc::RejectCallback; - -class OriginPrivateFileSystemChildImpl final - : public OriginPrivateFileSystemChild { - public: - class TOriginPrivateFileSystem final : public POriginPrivateFileSystemChild { - NS_INLINE_DECL_REFCOUNTING(TOriginPrivateFileSystem); - - protected: - ~TOriginPrivateFileSystem() = default; - }; - - OriginPrivateFileSystemChildImpl() : mImpl(new TOriginPrivateFileSystem()) {} - - NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(OriginPrivateFileSystemChildImpl, - Destroy(), override) - - void SendGetRootHandle(ResolveGetHandle&& aResolve, - DoReject&& aReject) override { - mImpl->SendGetRootHandle(std::forward(aResolve), - std::forward(aReject)); - } - - void SendGetDirectoryHandle(const fs::FileSystemGetHandleRequest& aRequest, - ResolveGetHandle&& aResolve, - DoReject&& aReject) override { - mImpl->SendGetDirectoryHandle(aRequest, - std::forward(aResolve), - std::forward(aReject)); - } - - void SendGetFileHandle(const fs::FileSystemGetHandleRequest& aRequest, - ResolveGetHandle&& aResolve, - DoReject&& aReject) override { - mImpl->SendGetFileHandle(aRequest, std::forward(aResolve), - std::forward(aReject)); - } - - void SendGetFile(const fs::FileSystemGetFileRequest& aRequest, - ResolveGetFile&& aResolve, DoReject&& aReject) override { - mImpl->SendGetFile(aRequest, std::forward(aResolve), - std::forward(aReject)); - } - - void SendResolve(const fs::FileSystemResolveRequest& aRequest, - ResolveResolve&& aResolve, DoReject&& aReject) override { - mImpl->SendResolve(aRequest, std::forward(aResolve), - std::forward(aReject)); - } - - void SendGetEntries(const fs::FileSystemGetEntriesRequest& aRequest, - ResolveGetEntries&& aResolve, - DoReject&& aReject) override { - mImpl->SendGetEntries(aRequest, std::forward(aResolve), - std::forward(aReject)); - } - - void SendRemoveEntry(const fs::FileSystemRemoveEntryRequest& aRequest, - ResolveRemoveEntry&& aResolve, - DoReject&& aReject) override { - mImpl->SendRemoveEntry(aRequest, std::forward(aResolve), - std::forward(aReject)); - } - - void Close() override { mImpl->Close(); } - - POriginPrivateFileSystemChild* AsBindable() override { return mImpl.get(); }; - - protected: - ~OriginPrivateFileSystemChildImpl() = default; - - void Destroy() { - if (mImpl->CanSend()) { - Close(); - } - delete this; - } - - const RefPtr mImpl; -}; // class OriginPrivateFileSystemChildImpl - -} // namespace - already_AddRefed FileSystemChildFactory::Create() const { - return MakeAndAddRef(); + return MakeAndAddRef(); } } // namespace mozilla::dom::fs diff --git a/dom/fs/child/OriginPrivateFileSystemChild.h b/dom/fs/child/OriginPrivateFileSystemChild.h index 43bc9b122f7ec..70cbf70c1cf1b 100644 --- a/dom/fs/child/OriginPrivateFileSystemChild.h +++ b/dom/fs/child/OriginPrivateFileSystemChild.h @@ -8,57 +8,127 @@ #define DOM_FS_CHILD_ORIGINPRIVATEFILESYSTEMCHILD_H_ #include "mozilla/dom/POriginPrivateFileSystemChild.h" -#include "mozilla/UniquePtr.h" #include "nsISupportsImpl.h" namespace mozilla::dom { -class OriginPrivateFileSystemChild { +// XXX This can be greatly simplified if bug 1786484 gets fixed. +// See: https://phabricator.services.mozilla.com/D155351 +// https://phabricator.services.mozilla.com/D155352 + +class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { public: - NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(OriginPrivateFileSystemChild, + Destroy()) virtual void SendGetRootHandle( - mozilla::ipc::ResolveCallback&& aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendGetRootHandleMsg( + std::forward< + mozilla::ipc::ResolveCallback>( + aResolve), + std::forward(aReject)); + } virtual void SendGetDirectoryHandle( - const fs::FileSystemGetHandleRequest& request, - mozilla::ipc::ResolveCallback&& aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; + const FileSystemGetHandleRequest& aRequest, + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendGetDirectoryHandleMsg( + aRequest, + std::forward< + mozilla::ipc::ResolveCallback>( + aResolve), + std::forward(aReject)); + } virtual void SendGetFileHandle( - const fs::FileSystemGetHandleRequest& request, - mozilla::ipc::ResolveCallback&& aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; + const FileSystemGetHandleRequest& aRequest, + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendGetFileHandleMsg( + aRequest, + std::forward< + mozilla::ipc::ResolveCallback>( + aResolve), + std::forward(aReject)); + } virtual void SendGetFile( - const fs::FileSystemGetFileRequest& request, - mozilla::ipc::ResolveCallback&& aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; + const FileSystemGetFileRequest& aRequest, + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendGetFileMsg( + aRequest, + std::forward>( + aResolve), + std::forward(aReject)); + } virtual void SendResolve( - const fs::FileSystemResolveRequest& request, - mozilla::ipc::ResolveCallback&& aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; + const FileSystemResolveRequest& aRequest, + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendResolveMsg( + aRequest, + std::forward>( + aResolve), + std::forward(aReject)); + } virtual void SendGetEntries( - const fs::FileSystemGetEntriesRequest& request, - mozilla::ipc::ResolveCallback&& - aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; + const FileSystemGetEntriesRequest& aRequest, + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendGetEntriesMsg( + aRequest, + std::forward< + mozilla::ipc::ResolveCallback>( + aResolve), + std::forward(aReject)); + } virtual void SendRemoveEntry( - const fs::FileSystemRemoveEntryRequest& request, - mozilla::ipc::ResolveCallback&& - aResolve, - mozilla::ipc::RejectCallback&& aReject) = 0; - - virtual void Close() = 0; - - virtual POriginPrivateFileSystemChild* AsBindable() = 0; + const FileSystemRemoveEntryRequest& aRequest, + mozilla::ipc::ResolveCallback&& aResolve, + mozilla::ipc::RejectCallback&& aReject) { + POriginPrivateFileSystemChild::SendRemoveEntryMsg( + aRequest, + std::forward< + mozilla::ipc::ResolveCallback>( + aResolve), + std::forward(aReject)); + } + + virtual void Shutdown() { + if (CanSend()) { + Close(); + } + } protected: virtual ~OriginPrivateFileSystemChild() = default; + + virtual void Destroy() { + Shutdown(); + delete this; + } + + private: + using POriginPrivateFileSystemChild::SendGetRootHandleMsg; + + using POriginPrivateFileSystemChild::SendGetDirectoryHandleMsg; + + using POriginPrivateFileSystemChild::SendGetFileHandleMsg; + + using POriginPrivateFileSystemChild::SendGetFileMsg; + + using POriginPrivateFileSystemChild::SendResolveMsg; + + using POriginPrivateFileSystemChild::SendGetEntriesMsg; + + using POriginPrivateFileSystemChild::SendRemoveEntryMsg; }; } // namespace mozilla::dom diff --git a/dom/fs/include/fs/FileSystemChildFactory.h b/dom/fs/include/fs/FileSystemChildFactory.h index cc92bd0297bb6..411b8b6e4e075 100644 --- a/dom/fs/include/fs/FileSystemChildFactory.h +++ b/dom/fs/include/fs/FileSystemChildFactory.h @@ -9,8 +9,6 @@ #include "mozilla/AlreadyAddRefed.h" -class nsIGlobalObject; - namespace mozilla { class ErrorResult; diff --git a/dom/fs/parent/OriginPrivateFileSystemParent.cpp b/dom/fs/parent/OriginPrivateFileSystemParent.cpp index 53031ebcc4741..6982a520126a5 100644 --- a/dom/fs/parent/OriginPrivateFileSystemParent.cpp +++ b/dom/fs/parent/OriginPrivateFileSystemParent.cpp @@ -26,57 +26,59 @@ OriginPrivateFileSystemParent::OriginPrivateFileSystemParent( TaskQueue* aTaskQueue, const EntryId& aRootEntry) : mTaskQueue(aTaskQueue), mData(), mRootEntry(aRootEntry) {} -IPCResult OriginPrivateFileSystemParent::RecvGetRootHandle( - GetRootHandleResolver&& aResolver) { +IPCResult OriginPrivateFileSystemParent::RecvGetRootHandleMsg( + GetRootHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(mRootEntry); aResolver(response); return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetDirectoryHandle( +IPCResult OriginPrivateFileSystemParent::RecvGetDirectoryHandleMsg( FileSystemGetHandleRequest&& /* aRequest */, - GetDirectoryHandleResolver&& aResolver) { + GetDirectoryHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetFileHandle( - FileSystemGetHandleRequest&& aRequest, GetFileHandleResolver&& aResolver) { +IPCResult OriginPrivateFileSystemParent::RecvGetFileHandleMsg( + FileSystemGetHandleRequest&& aRequest, + GetFileHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetFile( - FileSystemGetFileRequest&& aRequest, GetFileResolver&& aResolver) { +IPCResult OriginPrivateFileSystemParent::RecvGetFileMsg( + FileSystemGetFileRequest&& aRequest, GetFileMsgResolver&& aResolver) { FileSystemGetFileResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvResolve( - FileSystemResolveRequest&& aRequest, ResolveResolver&& aResolver) { +IPCResult OriginPrivateFileSystemParent::RecvResolveMsg( + FileSystemResolveRequest&& aRequest, ResolveMsgResolver&& aResolver) { FileSystemResolveResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetEntries( - FileSystemGetEntriesRequest&& aRequest, GetEntriesResolver&& aResolver) { +IPCResult OriginPrivateFileSystemParent::RecvGetEntriesMsg( + FileSystemGetEntriesRequest&& aRequest, GetEntriesMsgResolver&& aResolver) { FileSystemGetEntriesResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvRemoveEntry( - FileSystemRemoveEntryRequest&& aRequest, RemoveEntryResolver&& aResolver) { +IPCResult OriginPrivateFileSystemParent::RecvRemoveEntryMsg( + FileSystemRemoveEntryRequest&& aRequest, + RemoveEntryMsgResolver&& aResolver) { FileSystemRemoveEntryResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); diff --git a/dom/fs/parent/OriginPrivateFileSystemParent.h b/dom/fs/parent/OriginPrivateFileSystemParent.h index e6740720b35b2..6209479a36fbe 100644 --- a/dom/fs/parent/OriginPrivateFileSystemParent.h +++ b/dom/fs/parent/OriginPrivateFileSystemParent.h @@ -34,26 +34,30 @@ class OriginPrivateFileSystemParent : public POriginPrivateFileSystemParent { OriginPrivateFileSystemParent(TaskQueue* aTaskQueue, const EntryId& aRootEntry); - mozilla::ipc::IPCResult RecvGetRootHandle(GetRootHandleResolver&& aResolver); + mozilla::ipc::IPCResult RecvGetRootHandleMsg( + GetRootHandleMsgResolver&& aResolver); - mozilla::ipc::IPCResult RecvGetDirectoryHandle( + mozilla::ipc::IPCResult RecvGetDirectoryHandleMsg( FileSystemGetHandleRequest&& aRequest, - GetDirectoryHandleResolver&& aResolver); + GetDirectoryHandleMsgResolver&& aResolver); - mozilla::ipc::IPCResult RecvGetFileHandle( - FileSystemGetHandleRequest&& aRequest, GetFileHandleResolver&& aResolver); + mozilla::ipc::IPCResult RecvGetFileHandleMsg( + FileSystemGetHandleRequest&& aRequest, + GetFileHandleMsgResolver&& aResolver); - mozilla::ipc::IPCResult RecvGetFile(FileSystemGetFileRequest&& aRequest, - GetFileResolver&& aResolver); + mozilla::ipc::IPCResult RecvGetFileMsg(FileSystemGetFileRequest&& aRequest, + GetFileMsgResolver&& aResolver); - mozilla::ipc::IPCResult RecvResolve(FileSystemResolveRequest&& aRequest, - ResolveResolver&& aResolver); + mozilla::ipc::IPCResult RecvResolveMsg(FileSystemResolveRequest&& aRequest, + ResolveMsgResolver&& aResolver); - mozilla::ipc::IPCResult RecvGetEntries(FileSystemGetEntriesRequest&& aRequest, - GetEntriesResolver&& aResolver); + mozilla::ipc::IPCResult RecvGetEntriesMsg( + FileSystemGetEntriesRequest&& aRequest, + GetEntriesMsgResolver&& aResolver); - mozilla::ipc::IPCResult RecvRemoveEntry( - FileSystemRemoveEntryRequest&& aRequest, RemoveEntryResolver&& aResolver); + mozilla::ipc::IPCResult RecvRemoveEntryMsg( + FileSystemRemoveEntryRequest&& aRequest, + RemoveEntryMsgResolver&& aResolver); mozilla::ipc::IPCResult RecvCloseFile(FileSystemGetFileRequest&& aRequest); diff --git a/dom/fs/shared/POriginPrivateFileSystem.ipdl b/dom/fs/shared/POriginPrivateFileSystem.ipdl index 49291948d5b4d..63b116fb78784 100644 --- a/dom/fs/shared/POriginPrivateFileSystem.ipdl +++ b/dom/fs/shared/POriginPrivateFileSystem.ipdl @@ -194,7 +194,7 @@ async protocol POriginPrivateFileSystem /** * TODO: documentation */ - async GetRootHandle() + async GetRootHandleMsg() returns(FileSystemGetHandleResponse response); /** @@ -213,7 +213,7 @@ async protocol POriginPrivateFileSystem * * @returns error or entry handle */ - async GetDirectoryHandle(FileSystemGetHandleRequest request) + async GetDirectoryHandleMsg(FileSystemGetHandleRequest request) returns(FileSystemGetHandleResponse handle); /** @@ -232,7 +232,7 @@ async protocol POriginPrivateFileSystem * * @returns error or entry handle */ - async GetFileHandle(FileSystemGetHandleRequest request) + async GetFileHandleMsg(FileSystemGetHandleRequest request) returns(FileSystemGetHandleResponse handle); /** @@ -250,7 +250,7 @@ async protocol POriginPrivateFileSystem * * @returns error or file object */ - async GetFile(FileSystemGetFileRequest request) + async GetFileMsg(FileSystemGetFileRequest request) returns(FileSystemGetFileResponse response); /** @@ -273,7 +273,7 @@ async protocol POriginPrivateFileSystem * * @returns error or file system path */ - async Resolve(FileSystemResolveRequest request) + async ResolveMsg(FileSystemResolveRequest request) returns(FileSystemResolveResponse response); /** @@ -292,7 +292,7 @@ async protocol POriginPrivateFileSystem * * @returns error or iterator */ - async GetEntries(FileSystemGetEntriesRequest request) + async GetEntriesMsg(FileSystemGetEntriesRequest request) returns(FileSystemGetEntriesResponse entries); /** @@ -309,7 +309,7 @@ async protocol POriginPrivateFileSystem * * @returns error information */ - async RemoveEntry(FileSystemRemoveEntryRequest request) + async RemoveEntryMsg(FileSystemRemoveEntryRequest request) returns(FileSystemRemoveEntryResponse response); /** diff --git a/dom/fs/test/gtest/FileSystemMocks.h b/dom/fs/test/gtest/FileSystemMocks.h index db30a61f6b6a9..f9d661a743ae7 100644 --- a/dom/fs/test/gtest/FileSystemMocks.h +++ b/dom/fs/test/gtest/FileSystemMocks.h @@ -192,9 +192,6 @@ class TestPromiseListener : public PromiseNativeHandler, class TestOriginPrivateFileSystemChild : public OriginPrivateFileSystemChild { public: - NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(TestOriginPrivateFileSystemChild, - Destroy(), override) - MOCK_METHOD(void, SendGetRootHandle, (mozilla::ipc::ResolveCallback && aResolve, @@ -243,17 +240,10 @@ class TestOriginPrivateFileSystemChild : public OriginPrivateFileSystemChild { mozilla::ipc::RejectCallback&& aReject), (override)); - MOCK_METHOD(void, Close, (), (override)); - - MOCK_METHOD(POriginPrivateFileSystemChild*, AsBindable, (), (override)); + MOCK_METHOD(void, Shutdown, (), (override)); protected: virtual ~TestOriginPrivateFileSystemChild() = default; - - void Destroy() { - Close(); - delete this; - } }; class TestFileSystemChildFactory final : public FileSystemChildFactory { diff --git a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp index b48f35af5af86..64a426d6b06d7 100644 --- a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp @@ -12,8 +12,6 @@ #include "mozilla/dom/OriginPrivateFileSystemChild.h" #include "mozilla/dom/POriginPrivateFileSystem.h" -using ::testing::Invoke; - namespace mozilla::dom::fs::test { class TestFileSystemBackgroundRequestHandler : public ::testing::Test { @@ -32,21 +30,11 @@ class TestFileSystemBackgroundRequestHandler : public ::testing::Test { RefPtr mOPFSChild; }; -class TestOPFSChild : public POriginPrivateFileSystemChild { - public: - NS_INLINE_DECL_REFCOUNTING(TestOPFSChild) - - protected: - ~TestOPFSChild() = default; -}; - TEST_F(TestFileSystemBackgroundRequestHandler, isCreateFileSystemManagerChildSuccessful) { - EXPECT_CALL(*mOPFSChild, AsBindable()) - .WillOnce(Invoke([]() -> POriginPrivateFileSystemChild* { - return new TestOPFSChild(); - })); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); bool done = false; auto testable = GetFileSystemBackgroundRequestHandler(); diff --git a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp index ffde8e31c3724..71dc189239d30 100644 --- a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp @@ -71,7 +71,9 @@ TEST_F(TestFileSystemRequestHandler, isGetRootHandleSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mOPFSChild, SendGetRootHandle(_, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); @@ -91,7 +93,9 @@ TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mOPFSChild, SendGetDirectoryHandle(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); @@ -112,7 +116,9 @@ TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mOPFSChild, SendGetFileHandle(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); @@ -139,7 +145,9 @@ TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mOPFSChild, SendGetFile(_, _, _)).WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); @@ -166,7 +174,9 @@ TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) { EXPECT_CALL(*mOPFSChild, SendGetEntries(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); auto testable = GetFileSystemRequestHandler(); ArrayAppendable sink; @@ -185,7 +195,9 @@ TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mOPFSChild, SendRemoveEntry(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Close()).Times(1); + EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { + mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + }); auto testable = GetFileSystemRequestHandler(); RefPtr promise = GetDefaultPromise(); From 8b7b500fa95bf5a530f910b492b944877b2e9010 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:48 +0000 Subject: [PATCH 02/65] Bug 1786489 - QM: Add ToResult overload for bool values; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155366 --- dom/quota/ResultExtensions.h | 29 +++++++++++++++++++ dom/quota/test/gtest/TestResultExtensions.cpp | 17 +++++++++++ 2 files changed, 46 insertions(+) diff --git a/dom/quota/ResultExtensions.h b/dom/quota/ResultExtensions.h index 7bf735ff29760..fac6c4ea94af7 100644 --- a/dom/quota/ResultExtensions.h +++ b/dom/quota/ResultExtensions.h @@ -20,6 +20,35 @@ namespace mozilla { +// Allow bool errors to automatically convert to bool values, so MOZ_TRY/QM_TRY +// can be used in bool returning methods with Result results. +template <> +class [[nodiscard]] GenericErrorResult { + bool mErrorValue; + + template + friend class Result; + + public: + explicit GenericErrorResult(bool aErrorValue) : mErrorValue(aErrorValue) { + MOZ_ASSERT(!aErrorValue); + } + + GenericErrorResult(bool aErrorValue, const ErrorPropagationTag&) + : GenericErrorResult(aErrorValue) {} + + MOZ_IMPLICIT operator bool() const { return mErrorValue; } +}; + +// Allow MOZ_TRY/QM_TRY to handle `bool` values. +template +inline Result ToResult(bool aValue) { + if (aValue) { + return Ok(); + } + return Err(ResultTypeTraits::From(NS_ERROR_FAILURE)); +} + #ifdef QM_ERROR_STACKS_ENABLED // Allow QMResult errors to use existing stack id and to increase the frame id // during error propagation. diff --git a/dom/quota/test/gtest/TestResultExtensions.cpp b/dom/quota/test/gtest/TestResultExtensions.cpp index 7b3fe90af718d..137acc1423a8f 100644 --- a/dom/quota/test/gtest/TestResultExtensions.cpp +++ b/dom/quota/test/gtest/TestResultExtensions.cpp @@ -30,6 +30,23 @@ class TestClass { class DOM_Quota_ResultExtensions_ToResult : public DOM_Quota_Test {}; class DOM_Quota_ResultExtensions_GenericErrorResult : public DOM_Quota_Test {}; +TEST_F(DOM_Quota_ResultExtensions_ToResult, FromBool) { + // success + { + auto res = ToResult(true); + static_assert(std::is_same_v>); + EXPECT_TRUE(res.isOk()); + } + + // failure + { + auto res = ToResult(false); + static_assert(std::is_same_v>); + EXPECT_TRUE(res.isErr()); + EXPECT_EQ(res.unwrapErr(), NS_ERROR_FAILURE); + } +} + TEST_F(DOM_Quota_ResultExtensions_ToResult, FromQMResult_Failure) { // copy { From df54d955f84b128cb3970ab8c38c2b253b46f4e7 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:48 +0000 Subject: [PATCH 03/65] Bug 1786465 - Replace PBackgroundFileSystem protocol with a single async message; r=dom-storage-reviewers,jesup PBackgroundFileSystem currently contains only one async message and we don't plan to add more messages, so the one and only message can be defined directly on PBackground. Differential Revision: https://phabricator.services.mozilla.com/D155367 --- dom/fs/api/FileSystemManager.cpp | 64 +++++++++--- dom/fs/child/BackgroundFileSystemChild.h | 24 ----- .../FileSystemBackgroundRequestHandler.cpp | 99 +++---------------- .../FileSystemBackgroundRequestHandler.h | 16 ++- dom/fs/child/moz.build | 1 - dom/fs/parent/BackgroundFileSystemParent.h | 40 -------- ...cpp => FileSystemManagerParentFactory.cpp} | 29 +++--- .../parent/FileSystemManagerParentFactory.h | 39 ++++++++ dom/fs/parent/OriginPrivateFileSystemParent.h | 1 - dom/fs/parent/moz.build | 4 +- dom/fs/shared/PBackgroundFileSystem.ipdl | 30 ------ dom/fs/shared/moz.build | 1 - dom/fs/test/gtest/FileSystemMocks.cpp | 4 + dom/fs/test/gtest/FileSystemMocks.h | 3 + ...TestFileSystemBackgroundRequestHandler.cpp | 15 +-- ipc/glue/BackgroundParentImpl.cpp | 23 ++--- ipc/glue/BackgroundParentImpl.h | 9 +- ipc/glue/PBackground.ipdl | 13 ++- 18 files changed, 172 insertions(+), 243 deletions(-) delete mode 100644 dom/fs/child/BackgroundFileSystemChild.h delete mode 100644 dom/fs/parent/BackgroundFileSystemParent.h rename dom/fs/parent/{BackgroundFileSystemParent.cpp => FileSystemManagerParentFactory.cpp} (85%) create mode 100644 dom/fs/parent/FileSystemManagerParentFactory.h delete mode 100644 dom/fs/shared/PBackgroundFileSystem.ipdl diff --git a/dom/fs/api/FileSystemManager.cpp b/dom/fs/api/FileSystemManager.cpp index a1158a9f32c60..3ef2f3b6257e1 100644 --- a/dom/fs/api/FileSystemManager.cpp +++ b/dom/fs/api/FileSystemManager.cpp @@ -10,9 +10,45 @@ #include "fs/FileSystemRequestHandler.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/WorkerPrivate.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" +#include "mozilla/ipc/BackgroundUtils.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" +#include "nsIScriptObjectPrincipal.h" namespace mozilla::dom { +namespace { + +Result GetPrincipalInfo( + nsIGlobalObject* aGlobal) { + using mozilla::ipc::PrincipalInfo; + + if (NS_IsMainThread()) { + nsCOMPtr sop = do_QueryInterface(aGlobal); + QM_TRY(MOZ_TO_RESULT(sop)); + + nsCOMPtr principal = sop->GetEffectiveStoragePrincipal(); + QM_TRY(MOZ_TO_RESULT(principal)); + + PrincipalInfo principalInfo; + QM_TRY(MOZ_TO_RESULT(PrincipalToPrincipalInfo(principal, &principalInfo))); + + return std::move(principalInfo); + } + + WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); + QM_TRY(MOZ_TO_RESULT(workerPrivate)); + + const PrincipalInfo& principalInfo = + workerPrivate->GetEffectiveStoragePrincipalInfo(); + + return principalInfo; +} + +} // namespace + FileSystemManager::FileSystemManager(nsIGlobalObject* aGlobal) : mGlobal(aGlobal), mBackgroundRequestHandler(new FileSystemBackgroundRequestHandler()), @@ -28,6 +64,9 @@ NS_IMPL_CYCLE_COLLECTION(FileSystemManager, mGlobal); already_AddRefed FileSystemManager::GetDirectory(ErrorResult& aRv) { MOZ_ASSERT(mGlobal); + QM_TRY_INSPECT(const auto& principalInfo, GetPrincipalInfo(mGlobal), nullptr, + [&aRv](const nsresult rv) { aRv.Throw(rv); }); + RefPtr promise = Promise::Create(mGlobal, aRv); if (NS_WARN_IF(aRv.Failed())) { return nullptr; @@ -35,18 +74,19 @@ already_AddRefed FileSystemManager::GetDirectory(ErrorResult& aRv) { MOZ_ASSERT(promise); - mBackgroundRequestHandler->CreateFileSystemManagerChild(mGlobal)->Then( - GetCurrentSerialEventTarget(), __func__, - [self = RefPtr(this), - promise](const RefPtr& child) { - RefPtr actorHolder = - MakeAndAddRef(child); - self->mRequestHandler->GetRootHandle(actorHolder, promise); - }, - [promise](nsresult) { - promise->MaybeRejectWithUnknownError( - "Could not create the file system manager"); - }); + mBackgroundRequestHandler->CreateFileSystemManagerChild(principalInfo) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self = RefPtr(this), + promise](const RefPtr& child) { + RefPtr actorHolder = + MakeAndAddRef(child); + self->mRequestHandler->GetRootHandle(actorHolder, promise); + }, + [promise](nsresult) { + promise->MaybeRejectWithUnknownError( + "Could not create the file system manager"); + }); return promise.forget(); } diff --git a/dom/fs/child/BackgroundFileSystemChild.h b/dom/fs/child/BackgroundFileSystemChild.h deleted file mode 100644 index 63aa545d79e80..0000000000000 --- a/dom/fs/child/BackgroundFileSystemChild.h +++ /dev/null @@ -1,24 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef DOM_FS_CHILD_BACKGROUNDFILESYSTEMCHILD_H_ -#define DOM_FS_CHILD_BACKGROUNDFILESYSTEMCHILD_H_ - -#include "mozilla/dom/PBackgroundFileSystemChild.h" -#include "nsISupports.h" - -namespace mozilla::dom { - -class BackgroundFileSystemChild : public PBackgroundFileSystemChild { - NS_INLINE_DECL_REFCOUNTING(BackgroundFileSystemChild); - - protected: - virtual ~BackgroundFileSystemChild() = default; -}; - -} // namespace mozilla::dom - -#endif // DOM_FS_CHILD_BACKGROUNDFILESYSTEMCHILD_H_ diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp index 66d385b2e02e4..0a25bee43eac9 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp @@ -7,90 +7,14 @@ #include "FileSystemBackgroundRequestHandler.h" #include "fs/FileSystemChildFactory.h" -#include "mozilla/dom/BackgroundFileSystemChild.h" #include "mozilla/dom/OriginPrivateFileSystemChild.h" #include "mozilla/dom/POriginPrivateFileSystem.h" -#include "mozilla/dom/WorkerPrivate.h" #include "mozilla/ipc/BackgroundChild.h" -#include "mozilla/ipc/BackgroundUtils.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/ipc/PBackgroundChild.h" -#include "nsIScriptObjectPrincipal.h" namespace mozilla::dom { -namespace { - -// Not static: BackgroundFileSystemChild must be owned by calling thread -RefPtr CreateBackgroundFileSystemChild( - nsIGlobalObject* aGlobal) { - using mozilla::dom::BackgroundFileSystemChild; - using mozilla::ipc::BackgroundChild; - using mozilla::ipc::PBackgroundChild; - using mozilla::ipc::PrincipalInfo; - - // TODO: It would be nice to convert all error checks to QM_TRY some time - // later. - - // TODO: It would be cleaner if we were called with a PrincipalInfo, instead - // of an nsIGlobalObject, so we wouldn't have the dependency on the - // workers code. - - PBackgroundChild* backgroundChild = - BackgroundChild::GetOrCreateForCurrentThread(); - - if (NS_WARN_IF(!backgroundChild)) { - MOZ_ASSERT(false); - return nullptr; - } - - RefPtr result; - - if (NS_IsMainThread()) { - nsCOMPtr sop = do_QueryInterface(aGlobal); - if (!sop) { - return nullptr; - } - - nsCOMPtr principal = sop->GetEffectiveStoragePrincipal(); - if (!principal) { - return nullptr; - } - - auto principalInfo = MakeUnique(); - nsresult rv = PrincipalToPrincipalInfo(principal, principalInfo.get()); - if (NS_FAILED(rv)) { - return nullptr; - } - - auto* child = new BackgroundFileSystemChild(); - - result = static_cast( - backgroundChild->SendPBackgroundFileSystemConstructor(child, - *principalInfo)); - } else { - WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate(); - if (!workerPrivate) { - return nullptr; - } - - const PrincipalInfo& principalInfo = - workerPrivate->GetEffectiveStoragePrincipalInfo(); - - BackgroundFileSystemChild* child = new BackgroundFileSystemChild(); - - result = static_cast( - backgroundChild->SendPBackgroundFileSystemConstructor(child, - principalInfo)); - } - - MOZ_ASSERT(result); - - return result; -} - -} // namespace - FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler( fs::FileSystemChildFactory* aChildFactory) : mChildFactory(aChildFactory) {} @@ -103,8 +27,17 @@ FileSystemBackgroundRequestHandler::~FileSystemBackgroundRequestHandler() = RefPtr FileSystemBackgroundRequestHandler::CreateFileSystemManagerChild( - nsIGlobalObject* aGlobal) { + const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { + using mozilla::ipc::BackgroundChild; using mozilla::ipc::Endpoint; + using mozilla::ipc::PBackgroundChild; + + PBackgroundChild* backgroundChild = + BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!backgroundChild)) { + return CreateFileSystemManagerChildPromise::CreateAndReject( + NS_ERROR_FAILURE, __func__); + } // Create a new IPC connection Endpoint parentEndpoint; @@ -121,15 +54,9 @@ FileSystemBackgroundRequestHandler::CreateFileSystemManagerChild( // XXX do something (register with global?) so that we can Close() the actor // before the event queue starts to shut down. That will cancel all // outstanding async requests (returns lambdas with errors). - RefPtr backgroundFileSystemChild = - CreateBackgroundFileSystemChild(aGlobal); - if (!backgroundFileSystemChild) { - return CreateFileSystemManagerChildPromise::CreateAndReject( - NS_ERROR_FAILURE, __func__); - } - - return backgroundFileSystemChild - ->SendCreateFileSystemManagerParent(std::move(parentEndpoint)) + return backgroundChild + ->SendCreateFileSystemManagerParent(aPrincipalInfo, + std::move(parentEndpoint)) ->Then( GetCurrentSerialEventTarget(), __func__, [child](nsresult rv) { diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.h b/dom/fs/child/FileSystemBackgroundRequestHandler.h index b4ea8e6db7b4b..98539c5fb4725 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.h +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.h @@ -10,12 +10,16 @@ #include "mozilla/MozPromise.h" #include "mozilla/UniquePtr.h" -class nsIGlobalObject; - template class RefPtr; -namespace mozilla::dom { +namespace mozilla { + +namespace ipc { +class PrincipalInfo; +} // namespace ipc + +namespace dom { class OriginPrivateFileSystemChild; @@ -34,7 +38,8 @@ class FileSystemBackgroundRequestHandler { MozPromise, nsresult, false>; virtual RefPtr - CreateFileSystemManagerChild(nsIGlobalObject* aGlobal); + CreateFileSystemManagerChild( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo); virtual ~FileSystemBackgroundRequestHandler(); @@ -42,6 +47,7 @@ class FileSystemBackgroundRequestHandler { const UniquePtr mChildFactory; }; // class FileSystemBackgroundRequestHandler -} // namespace mozilla::dom +} // namespace dom +} // namespace mozilla #endif // DOM_FS_CHILD_FILESYSTEMBACKGROUNDREQUESTHANDLER_H_ diff --git a/dom/fs/child/moz.build b/dom/fs/child/moz.build index dd321fa496d43..c876d033588a6 100644 --- a/dom/fs/child/moz.build +++ b/dom/fs/child/moz.build @@ -5,7 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ - "BackgroundFileSystemChild.h", "OriginPrivateFileSystemChild.h", ] diff --git a/dom/fs/parent/BackgroundFileSystemParent.h b/dom/fs/parent/BackgroundFileSystemParent.h deleted file mode 100644 index 022ce733877ce..0000000000000 --- a/dom/fs/parent/BackgroundFileSystemParent.h +++ /dev/null @@ -1,40 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#ifndef DOM_FS_PARENT_BACKGROUNDFILESYSTEMPARENT_H_ -#define DOM_FS_PARENT_BACKGROUNDFILESYSTEMPARENT_H_ - -#include "mozilla/ipc/BackgroundUtils.h" -#include "mozilla/dom/PBackgroundFileSystemParent.h" -#include "mozilla/dom/POriginPrivateFileSystemParent.h" -#include "mozilla/ipc/PBackgroundSharedTypes.h" -#include "mozilla/TaskQueue.h" -#include "nsISupports.h" - -namespace mozilla::dom { - -class BackgroundFileSystemParent : public PBackgroundFileSystemParent { - public: - explicit BackgroundFileSystemParent( - const mozilla::ipc::PrincipalInfo& aPrincipalInfo) - : mPrincipalInfo(aPrincipalInfo) {} - - mozilla::ipc::IPCResult RecvCreateFileSystemManagerParent( - mozilla::ipc::Endpoint&& aParentEp, - CreateFileSystemManagerParentResolver&& aResolver); - - NS_INLINE_DECL_REFCOUNTING(BackgroundFileSystemParent) - - protected: - virtual ~BackgroundFileSystemParent() = default; - - private: - mozilla::ipc::PrincipalInfo mPrincipalInfo; -}; - -} // namespace mozilla::dom - -#endif // DOM_FS_PARENT_BACKGROUNDFILESYSTEMPARENT_H_ diff --git a/dom/fs/parent/BackgroundFileSystemParent.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp similarity index 85% rename from dom/fs/parent/BackgroundFileSystemParent.cpp rename to dom/fs/parent/FileSystemManagerParentFactory.cpp index c08afda11b363..f2943168e2533 100644 --- a/dom/fs/parent/BackgroundFileSystemParent.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -4,19 +4,18 @@ * 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 "BackgroundFileSystemParent.h" -#include "OriginPrivateFileSystemParent.h" +#include "FileSystemManagerParentFactory.h" -#include "nsNetCID.h" +#include "mozilla/StaticPrefs_dom.h" #include "mozilla/dom/FileSystemTypes.h" +#include "mozilla/dom/OriginPrivateFileSystemParent.h" #include "mozilla/dom/quota/QuotaCommon.h" -#include "mozilla/ipc/Endpoint.h" -#include "mozilla/ipc/FileDescriptorUtils.h" -#include "mozilla/Maybe.h" -#include "mozilla/MozPromise.h" #include "mozilla/dom/quota/QuotaManager.h" -#include "mozilla/StaticPrefs_dom.h" +#include "mozilla/ipc/Endpoint.h" +#include "nsIScriptObjectPrincipal.h" +#include "nsNetCID.h" #include "nsServiceManagerUtils.h" +#include "nsString.h" namespace mozilla { LazyLogModule gOPFSLog("OPFS"); @@ -49,18 +48,18 @@ FileSystemDataManager::CreateFileSystemDataManager( } // namespace fs::data -mozilla::ipc::IPCResult -BackgroundFileSystemParent::RecvCreateFileSystemManagerParent( - Endpoint&& aParentEp, - CreateFileSystemManagerParentResolver&& aResolver) { +mozilla::ipc::IPCResult CreateFileSystemManagerParent( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + mozilla::ipc::Endpoint&& aParentEndpoint, + std::function&& aResolver) { QM_TRY(OkIf(StaticPrefs::dom_fs_enabled()), IPC_OK(), [aResolver](const auto&) { aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR); }); - QM_TRY(OkIf(aParentEp.IsValid()), IPC_OK(), + QM_TRY(OkIf(aParentEndpoint.IsValid()), IPC_OK(), [aResolver](const auto&) { aResolver(NS_ERROR_INVALID_ARG); }); nsAutoCString origin = - quota::QuotaManager::GetOriginFromValidatedPrincipalInfo(mPrincipalInfo); + quota::QuotaManager::GetOriginFromValidatedPrincipalInfo(aPrincipalInfo); auto sendBackError = [aResolver](const auto& aRv) { aResolver(aRv); }; @@ -87,7 +86,7 @@ BackgroundFileSystemParent::RecvCreateFileSystemManagerParent( // just have the create be one-way, then send the actual request on the // new channel, but that's an extra IPC instead. InvokeAsync(taskqueue, __func__, - [origin, parentEp = std::move(aParentEp), aResolver, rootId, + [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, data = std::move(data), taskqueue, pbackground]() mutable { RefPtr parent = new OriginPrivateFileSystemParent(taskqueue, rootId); diff --git a/dom/fs/parent/FileSystemManagerParentFactory.h b/dom/fs/parent/FileSystemManagerParentFactory.h new file mode 100644 index 0000000000000..a13ceb38b7d9e --- /dev/null +++ b/dom/fs/parent/FileSystemManagerParentFactory.h @@ -0,0 +1,39 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_FS_PARENT_FILESYSTEMMANAGER_H_ +#define DOM_FS_PARENT_FILESYSTEMMANAGER_H_ + +#include + +enum class nsresult : uint32_t; + +namespace mozilla { + +namespace ipc { + +template +class Endpoint; + +class IPCResult; +class PrincipalInfo; + +} // namespace ipc + +namespace dom { + +class POriginPrivateFileSystemParent; + +mozilla::ipc::IPCResult CreateFileSystemManagerParent( + const mozilla::ipc::PrincipalInfo& aPrincipalInfo, + mozilla::ipc::Endpoint&& + aParentEndpoint, + std::function&& aResolver); + +} // namespace dom +} // namespace mozilla + +#endif // DOM_FS_PARENT_FILESYSTEMMANAGER_H_ diff --git a/dom/fs/parent/OriginPrivateFileSystemParent.h b/dom/fs/parent/OriginPrivateFileSystemParent.h index 6209479a36fbe..debe50082fe80 100644 --- a/dom/fs/parent/OriginPrivateFileSystemParent.h +++ b/dom/fs/parent/OriginPrivateFileSystemParent.h @@ -8,7 +8,6 @@ #define DOM_FS_PARENT_ORIGINPRIVATEFILESYSTEMPARENT_H_ #include "ErrorList.h" -#include "mozilla/dom/PBackgroundFileSystemParent.h" #include "mozilla/dom/POriginPrivateFileSystemParent.h" #include "mozilla/TaskQueue.h" #include "nsISupports.h" diff --git a/dom/fs/parent/moz.build b/dom/fs/parent/moz.build index 0b1526fa312ed..67059cf79b994 100644 --- a/dom/fs/parent/moz.build +++ b/dom/fs/parent/moz.build @@ -5,13 +5,13 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ - "BackgroundFileSystemParent.h", + "FileSystemManagerParentFactory.h", "OriginPrivateFileSystemParent.h", ] UNIFIED_SOURCES += [ - "BackgroundFileSystemParent.cpp", "FileSystemHashSource.cpp", + "FileSystemManagerParentFactory.cpp", "OriginPrivateFileSystemParent.cpp", ] diff --git a/dom/fs/shared/PBackgroundFileSystem.ipdl b/dom/fs/shared/PBackgroundFileSystem.ipdl deleted file mode 100644 index 6ed34dbff3db6..0000000000000 --- a/dom/fs/shared/PBackgroundFileSystem.ipdl +++ /dev/null @@ -1,30 +0,0 @@ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this file, - * You can obtain one at http://mozilla.org/MPL/2.0/. */ - -include protocol PBackground; -include protocol POriginPrivateFileSystem; - -namespace mozilla { -namespace dom { - -async protocol PBackgroundFileSystem -{ - manager PBackground; - - parent: - - /** - * This will set up a POriginPrivateFileSystem IPC connection - */ - async CreateFileSystemManagerParent( - Endpoint aParentEP) - returns(nsresult rv); - - child: - async __delete__(); -}; - -} // namespace dom -} // namespace mozilla - diff --git a/dom/fs/shared/moz.build b/dom/fs/shared/moz.build index 40e8d6a278635..f0e98d671c3d9 100644 --- a/dom/fs/shared/moz.build +++ b/dom/fs/shared/moz.build @@ -12,7 +12,6 @@ EXPORTS.mozilla.dom += [ FINAL_LIBRARY = "xul" IPDL_SOURCES += [ - "PBackgroundFileSystem.ipdl", "POriginPrivateFileSystem.ipdl", ] diff --git a/dom/fs/test/gtest/FileSystemMocks.cpp b/dom/fs/test/gtest/FileSystemMocks.cpp index 078711dafc3db..f3d6932313025 100644 --- a/dom/fs/test/gtest/FileSystemMocks.cpp +++ b/dom/fs/test/gtest/FileSystemMocks.cpp @@ -26,4 +26,8 @@ nsIGlobalObject* GetGlobal() { return global.get(); } +mozilla::ipc::PrincipalInfo GetPrincipalInfo() { + return mozilla::ipc::PrincipalInfo{mozilla::ipc::SystemPrincipalInfo{}}; +} + } // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/FileSystemMocks.h b/dom/fs/test/gtest/FileSystemMocks.h index f9d661a743ae7..38ff3fdbe48b1 100644 --- a/dom/fs/test/gtest/FileSystemMocks.h +++ b/dom/fs/test/gtest/FileSystemMocks.h @@ -23,6 +23,7 @@ #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseNativeHandler.h" #include "mozilla/dom/ScriptSettings.h" +#include "mozilla/ipc/PBackgroundSharedTypes.h" #include "mozilla/ErrorResult.h" #include "mozilla/ScopeExit.h" #include "mozilla/UniquePtr.h" @@ -41,6 +42,8 @@ namespace mozilla::dom::fs::test { nsIGlobalObject* GetGlobal(); +mozilla::ipc::PrincipalInfo GetPrincipalInfo(); + class MockFileSystemRequestHandler : public FileSystemRequestHandler { public: MOCK_METHOD(void, GetRootHandle, diff --git a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp index 64a426d6b06d7..4103163010f33 100644 --- a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp @@ -26,7 +26,7 @@ class TestFileSystemBackgroundRequestHandler : public ::testing::Test { new TestFileSystemChildFactory(mOPFSChild)); } - nsIGlobalObject* mGlobal = GetGlobal(); + mozilla::ipc::PrincipalInfo mPrincipalInfo = GetPrincipalInfo(); RefPtr mOPFSChild; }; @@ -38,12 +38,13 @@ TEST_F(TestFileSystemBackgroundRequestHandler, bool done = false; auto testable = GetFileSystemBackgroundRequestHandler(); - testable->CreateFileSystemManagerChild(mGlobal)->Then( - GetCurrentSerialEventTarget(), __func__, - [&done](const RefPtr& child) { - done = true; - }, - [&done](nsresult) { done = true; }); + testable->CreateFileSystemManagerChild(mPrincipalInfo) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [&done](const RefPtr& child) { + done = true; + }, + [&done](nsresult) { done = true; }); // MozPromise should be rejected SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [&done]() { return done; }); diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index 628cd19d625d9..46871eff53493 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -14,13 +14,13 @@ #include "mozilla/RDDProcessManager.h" #include "mozilla/ipc/UtilityProcessManager.h" #include "mozilla/RefPtr.h" -#include "mozilla/dom/BackgroundFileSystemParent.h" #include "mozilla/dom/BackgroundSessionStorageServiceParent.h" #include "mozilla/dom/ClientManagerActors.h" #include "mozilla/dom/ContentParent.h" #include "mozilla/dom/DOMTypes.h" #include "mozilla/dom/EndpointForReportParent.h" #include "mozilla/dom/FileCreatorParent.h" +#include "mozilla/dom/FileSystemManagerParentFactory.h" #include "mozilla/dom/FileSystemRequestParent.h" #include "mozilla/dom/GamepadEventChannelParent.h" #include "mozilla/dom/GamepadTestChannelParent.h" @@ -185,16 +185,6 @@ auto BackgroundParentImpl::AllocPBackgroundIDBFactoryParent( return AllocPBackgroundIDBFactoryParent(aLoggingInfo); } -auto BackgroundParentImpl::AllocPBackgroundFileSystemParent( - const PrincipalInfo& aPrincipalInfo) - -> already_AddRefed { - AssertIsInMainProcess(); - AssertIsOnBackgroundThread(); - - return MakeAndAddRef( - aPrincipalInfo); -} - mozilla::ipc::IPCResult BackgroundParentImpl::RecvPBackgroundIDBFactoryConstructor( PBackgroundIDBFactoryParent* aActor, const LoggingInfo& aLoggingInfo) { @@ -499,6 +489,17 @@ BackgroundParentImpl::AllocPBackgroundSessionStorageServiceParent() { return MakeAndAddRef(); } +mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateFileSystemManagerParent( + const PrincipalInfo& aPrincipalInfo, + Endpoint&& aParentEndpoint, + CreateFileSystemManagerParentResolver&& aResolver) { + AssertIsInMainProcess(); + AssertIsOnBackgroundThread(); + + return mozilla::dom::CreateFileSystemManagerParent( + aPrincipalInfo, std::move(aParentEndpoint), std::move(aResolver)); +} + already_AddRefed BackgroundParentImpl::AllocPIdleSchedulerParent() { AssertIsOnBackgroundThread(); diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h index 685d967a2dc1e..6de82dc556997 100644 --- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -28,10 +28,6 @@ class BackgroundParentImpl : public PBackgroundParent { bool DeallocPBackgroundTestParent(PBackgroundTestParent* aActor) override; - already_AddRefed - AllocPBackgroundFileSystemParent( - const PrincipalInfo& aPrincipalInfo) override; - already_AddRefed AllocPBackgroundIDBFactoryParent(const LoggingInfo& aLoggingInfo) override; @@ -136,6 +132,11 @@ class BackgroundParentImpl : public PBackgroundParent { already_AddRefed AllocPBackgroundSessionStorageServiceParent() override; + mozilla::ipc::IPCResult RecvCreateFileSystemManagerParent( + const PrincipalInfo& aPrincipalInfo, + Endpoint&& aParentEndpoint, + CreateFileSystemManagerParentResolver&& aResolver) override; + already_AddRefed AllocPIdleSchedulerParent() override; PTemporaryIPCBlobParent* AllocPTemporaryIPCBlobParent() override; diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl index 675a73bbd7ba0..60ae9905d63d9 100644 --- a/ipc/glue/PBackground.ipdl +++ b/ipc/glue/PBackground.ipdl @@ -3,7 +3,6 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ include protocol PBackgroundDataBridge; -include protocol PBackgroundFileSystem; include protocol PBackgroundIDBFactory; include protocol PBackgroundIndexedDBUtils; include protocol PBackgroundSDBConnection; @@ -22,6 +21,7 @@ include protocol PCacheStorage; include protocol PCacheStreamControl; include protocol PClientManager; include protocol PEndpointForReport; +include protocol POriginPrivateFileSystem; include protocol PFileSystemRequest; include protocol PGamepadEventChannel; include protocol PGamepadTestChannel; @@ -83,7 +83,6 @@ namespace ipc { sync protocol PBackground { manages PBackgroundDataBridge; - manages PBackgroundFileSystem; manages PBackgroundIDBFactory; manages PBackgroundIndexedDBUtils; manages PBackgroundSDBConnection; @@ -136,8 +135,6 @@ parent: // Only called at startup during mochitests to check the basic infrastructure. async PBackgroundTest(nsCString testArg); - async PBackgroundFileSystem(PrincipalInfo principalInfo); - async PBackgroundDataBridge(uint64_t channelID); async PBackgroundIDBFactory(LoggingInfo loggingInfo); @@ -190,6 +187,14 @@ parent: async PBackgroundStorage(nsString profilePath, uint32_t privateBrowsingId); + /** + * Finish the setup of a new POriginPrivateFileSystem top level protocol. + */ + async CreateFileSystemManagerParent( + PrincipalInfo principalInfo, + Endpoint aParentEndpoint) + returns(nsresult rv); + async PVsync(); async PCameras(); From 9dfb082c69ac111cd60eeebe67d18473099f26dd Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:48 +0000 Subject: [PATCH 04/65] Bug 1786465 - Rename POriginPrivateFileSystem to PFileSystemManager; r=dom-storage-reviewers,jesup The child actor will be held by FileSystemManager on the child side and the parent actor already holds FileSystemDataManager, so it makes sense to rename the protocol to PFileSystemManager. Differential Revision: https://phabricator.services.mozilla.com/D155368 --- dom/fs/api/FileSystemDirectoryHandle.cpp | 2 +- dom/fs/api/FileSystemFileHandle.cpp | 2 +- dom/fs/api/FileSystemHandle.cpp | 4 +- dom/fs/api/FileSystemHandle.h | 6 +-- dom/fs/api/FileSystemManager.cpp | 2 +- .../FileSystemBackgroundRequestHandler.cpp | 14 +++--- .../FileSystemBackgroundRequestHandler.h | 4 +- dom/fs/child/FileSystemChildFactory.cpp | 6 +-- ...SystemChild.h => FileSystemManagerChild.h} | 43 ++++++++--------- dom/fs/child/FileSystemRequestHandler.cpp | 2 +- dom/fs/child/moz.build | 2 +- dom/fs/include/fs/FileSystemChildFactory.h | 4 +- ...Parent.cpp => FileSystemManagerParent.cpp} | 32 ++++++------- ...stemParent.h => FileSystemManagerParent.h} | 17 ++++--- .../parent/FileSystemManagerParentFactory.cpp | 8 ++-- .../parent/FileSystemManagerParentFactory.h | 4 +- dom/fs/parent/moz.build | 4 +- dom/fs/shared/FileSystemActorHolder.h | 4 +- ...ileSystem.ipdl => PFileSystemManager.ipdl} | 2 +- dom/fs/shared/moz.build | 2 +- dom/fs/test/gtest/FileSystemMocks.h | 14 +++--- .../api/TestFileSystemDirectoryHandle.cpp | 2 +- ...TestFileSystemBackgroundRequestHandler.cpp | 18 ++++--- .../child/TestFileSystemRequestHandler.cpp | 48 ++++++++++--------- ipc/glue/BackgroundParentImpl.cpp | 2 +- ipc/glue/BackgroundParentImpl.h | 2 +- ipc/glue/PBackground.ipdl | 6 +-- 27 files changed, 127 insertions(+), 129 deletions(-) rename dom/fs/child/{OriginPrivateFileSystemChild.h => FileSystemManagerChild.h} (73%) rename dom/fs/parent/{OriginPrivateFileSystemParent.cpp => FileSystemManagerParent.cpp} (75%) rename dom/fs/parent/{OriginPrivateFileSystemParent.h => FileSystemManagerParent.h} (82%) rename dom/fs/shared/{POriginPrivateFileSystem.ipdl => PFileSystemManager.ipdl} (99%) diff --git a/dom/fs/api/FileSystemDirectoryHandle.cpp b/dom/fs/api/FileSystemDirectoryHandle.cpp index 4c42bbd2d716e..bbae7a817e512 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.cpp +++ b/dom/fs/api/FileSystemDirectoryHandle.cpp @@ -11,7 +11,7 @@ #include "mozilla/dom/FileSystemDirectoryHandleBinding.h" #include "mozilla/dom/FileSystemDirectoryIterator.h" #include "mozilla/dom/FileSystemHandleBinding.h" -#include "mozilla/dom/POriginPrivateFileSystem.h" +#include "mozilla/dom/PFileSystemManager.h" #include "mozilla/dom/Promise.h" namespace mozilla::dom { diff --git a/dom/fs/api/FileSystemFileHandle.cpp b/dom/fs/api/FileSystemFileHandle.cpp index fc828917bf26d..6c8429166329a 100644 --- a/dom/fs/api/FileSystemFileHandle.cpp +++ b/dom/fs/api/FileSystemFileHandle.cpp @@ -10,7 +10,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemFileHandleBinding.h" #include "mozilla/dom/FileSystemHandleBinding.h" -#include "mozilla/dom/POriginPrivateFileSystem.h" +#include "mozilla/dom/PFileSystemManager.h" #include "mozilla/dom/Promise.h" namespace mozilla::dom { diff --git a/dom/fs/api/FileSystemHandle.cpp b/dom/fs/api/FileSystemHandle.cpp index 7af24373f5aeb..14444e535fc05 100644 --- a/dom/fs/api/FileSystemHandle.cpp +++ b/dom/fs/api/FileSystemHandle.cpp @@ -9,7 +9,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemHandleBinding.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" +#include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/Promise.h" namespace mozilla::dom { @@ -60,7 +60,7 @@ already_AddRefed FileSystemHandle::IsSameEntry( return promise.forget(); } -OriginPrivateFileSystemChild* FileSystemHandle::Actor() const { +FileSystemManagerChild* FileSystemHandle::Actor() const { return mActor->Actor(); } diff --git a/dom/fs/api/FileSystemHandle.h b/dom/fs/api/FileSystemHandle.h index a2a5a10e3e743..db9e07f7f9311 100644 --- a/dom/fs/api/FileSystemHandle.h +++ b/dom/fs/api/FileSystemHandle.h @@ -8,7 +8,7 @@ #define DOM_FS_FILESYSTEMHANDLE_H_ #include "mozilla/dom/FileSystemActorHolder.h" -#include "mozilla/dom/POriginPrivateFileSystem.h" +#include "mozilla/dom/PFileSystemManager.h" #include "mozilla/Logging.h" #include "nsCOMPtr.h" #include "nsISupports.h" @@ -31,7 +31,7 @@ namespace dom { class DOMString; enum class FileSystemHandleKind : uint8_t; -class OriginPrivateFileSystemChild; +class FileSystemManagerChild; class Promise; namespace fs { @@ -62,7 +62,7 @@ class FileSystemHandle : public nsISupports, public nsWrapperCache { already_AddRefed IsSameEntry(FileSystemHandle& aOther, ErrorResult& aError) const; - OriginPrivateFileSystemChild* Actor() const; + FileSystemManagerChild* Actor() const; protected: virtual ~FileSystemHandle() = default; diff --git a/dom/fs/api/FileSystemManager.cpp b/dom/fs/api/FileSystemManager.cpp index 3ef2f3b6257e1..93b9a9686882a 100644 --- a/dom/fs/api/FileSystemManager.cpp +++ b/dom/fs/api/FileSystemManager.cpp @@ -78,7 +78,7 @@ already_AddRefed FileSystemManager::GetDirectory(ErrorResult& aRv) { ->Then( GetCurrentSerialEventTarget(), __func__, [self = RefPtr(this), - promise](const RefPtr& child) { + promise](const RefPtr& child) { RefPtr actorHolder = MakeAndAddRef(child); self->mRequestHandler->GetRootHandle(actorHolder, promise); diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp index 0a25bee43eac9..84216fbc7fd8a 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp @@ -7,8 +7,8 @@ #include "FileSystemBackgroundRequestHandler.h" #include "fs/FileSystemChildFactory.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" -#include "mozilla/dom/POriginPrivateFileSystem.h" +#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/PFileSystemManager.h" #include "mozilla/ipc/BackgroundChild.h" #include "mozilla/ipc/Endpoint.h" #include "mozilla/ipc/PBackgroundChild.h" @@ -40,12 +40,12 @@ FileSystemBackgroundRequestHandler::CreateFileSystemManagerChild( } // Create a new IPC connection - Endpoint parentEndpoint; - Endpoint childEndpoint; - MOZ_ALWAYS_SUCCEEDS(POriginPrivateFileSystem::CreateEndpoints( - &parentEndpoint, &childEndpoint)); + Endpoint parentEndpoint; + Endpoint childEndpoint; + MOZ_ALWAYS_SUCCEEDS( + PFileSystemManager::CreateEndpoints(&parentEndpoint, &childEndpoint)); - RefPtr child = mChildFactory->Create(); + RefPtr child = mChildFactory->Create(); if (!childEndpoint.Bind(child)) { return CreateFileSystemManagerChildPromise::CreateAndReject( NS_ERROR_FAILURE, __func__); diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.h b/dom/fs/child/FileSystemBackgroundRequestHandler.h index 98539c5fb4725..be23b496c5ba8 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.h +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.h @@ -21,7 +21,7 @@ class PrincipalInfo; namespace dom { -class OriginPrivateFileSystemChild; +class FileSystemManagerChild; namespace fs { class FileSystemChildFactory; @@ -35,7 +35,7 @@ class FileSystemBackgroundRequestHandler { FileSystemBackgroundRequestHandler(); using CreateFileSystemManagerChildPromise = - MozPromise, nsresult, false>; + MozPromise, nsresult, false>; virtual RefPtr CreateFileSystemManagerChild( diff --git a/dom/fs/child/FileSystemChildFactory.cpp b/dom/fs/child/FileSystemChildFactory.cpp index 3b50a7a6fcd4a..bf4c493dd812e 100644 --- a/dom/fs/child/FileSystemChildFactory.cpp +++ b/dom/fs/child/FileSystemChildFactory.cpp @@ -7,13 +7,13 @@ #include "fs/FileSystemChildFactory.h" #include "mozilla/RefPtr.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" +#include "mozilla/dom/FileSystemManagerChild.h" namespace mozilla::dom::fs { -already_AddRefed FileSystemChildFactory::Create() +already_AddRefed FileSystemChildFactory::Create() const { - return MakeAndAddRef(); + return MakeAndAddRef(); } } // namespace mozilla::dom::fs diff --git a/dom/fs/child/OriginPrivateFileSystemChild.h b/dom/fs/child/FileSystemManagerChild.h similarity index 73% rename from dom/fs/child/OriginPrivateFileSystemChild.h rename to dom/fs/child/FileSystemManagerChild.h index 70cbf70c1cf1b..756c70e7a263f 100644 --- a/dom/fs/child/OriginPrivateFileSystemChild.h +++ b/dom/fs/child/FileSystemManagerChild.h @@ -4,10 +4,10 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef DOM_FS_CHILD_ORIGINPRIVATEFILESYSTEMCHILD_H_ -#define DOM_FS_CHILD_ORIGINPRIVATEFILESYSTEMCHILD_H_ +#ifndef DOM_FS_CHILD_FILESYSTEMMANAGERCHILD_H_ +#define DOM_FS_CHILD_FILESYSTEMMANAGERCHILD_H_ -#include "mozilla/dom/POriginPrivateFileSystemChild.h" +#include "mozilla/dom/PFileSystemManagerChild.h" #include "nsISupportsImpl.h" namespace mozilla::dom { @@ -16,15 +16,14 @@ namespace mozilla::dom { // See: https://phabricator.services.mozilla.com/D155351 // https://phabricator.services.mozilla.com/D155352 -class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { +class FileSystemManagerChild : public PFileSystemManagerChild { public: - NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(OriginPrivateFileSystemChild, - Destroy()) + NS_INLINE_DECL_REFCOUNTING_WITH_DESTROY(FileSystemManagerChild, Destroy()) virtual void SendGetRootHandle( mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendGetRootHandleMsg( + PFileSystemManagerChild::SendGetRootHandleMsg( std::forward< mozilla::ipc::ResolveCallback>( aResolve), @@ -35,7 +34,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { const FileSystemGetHandleRequest& aRequest, mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendGetDirectoryHandleMsg( + PFileSystemManagerChild::SendGetDirectoryHandleMsg( aRequest, std::forward< mozilla::ipc::ResolveCallback>( @@ -47,7 +46,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { const FileSystemGetHandleRequest& aRequest, mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendGetFileHandleMsg( + PFileSystemManagerChild::SendGetFileHandleMsg( aRequest, std::forward< mozilla::ipc::ResolveCallback>( @@ -59,7 +58,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { const FileSystemGetFileRequest& aRequest, mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendGetFileMsg( + PFileSystemManagerChild::SendGetFileMsg( aRequest, std::forward>( aResolve), @@ -70,7 +69,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { const FileSystemResolveRequest& aRequest, mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendResolveMsg( + PFileSystemManagerChild::SendResolveMsg( aRequest, std::forward>( aResolve), @@ -81,7 +80,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { const FileSystemGetEntriesRequest& aRequest, mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendGetEntriesMsg( + PFileSystemManagerChild::SendGetEntriesMsg( aRequest, std::forward< mozilla::ipc::ResolveCallback>( @@ -93,7 +92,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { const FileSystemRemoveEntryRequest& aRequest, mozilla::ipc::ResolveCallback&& aResolve, mozilla::ipc::RejectCallback&& aReject) { - POriginPrivateFileSystemChild::SendRemoveEntryMsg( + PFileSystemManagerChild::SendRemoveEntryMsg( aRequest, std::forward< mozilla::ipc::ResolveCallback>( @@ -108,7 +107,7 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { } protected: - virtual ~OriginPrivateFileSystemChild() = default; + virtual ~FileSystemManagerChild() = default; virtual void Destroy() { Shutdown(); @@ -116,21 +115,21 @@ class OriginPrivateFileSystemChild : public POriginPrivateFileSystemChild { } private: - using POriginPrivateFileSystemChild::SendGetRootHandleMsg; + using PFileSystemManagerChild::SendGetRootHandleMsg; - using POriginPrivateFileSystemChild::SendGetDirectoryHandleMsg; + using PFileSystemManagerChild::SendGetDirectoryHandleMsg; - using POriginPrivateFileSystemChild::SendGetFileHandleMsg; + using PFileSystemManagerChild::SendGetFileHandleMsg; - using POriginPrivateFileSystemChild::SendGetFileMsg; + using PFileSystemManagerChild::SendGetFileMsg; - using POriginPrivateFileSystemChild::SendResolveMsg; + using PFileSystemManagerChild::SendResolveMsg; - using POriginPrivateFileSystemChild::SendGetEntriesMsg; + using PFileSystemManagerChild::SendGetEntriesMsg; - using POriginPrivateFileSystemChild::SendRemoveEntryMsg; + using PFileSystemManagerChild::SendRemoveEntryMsg; }; } // namespace mozilla::dom -#endif // DOM_FS_CHILD_ORIGINPRIVATEFILESYSTEMCHILD_H_ +#endif // DOM_FS_CHILD_FILESYSTEMMANAGERCHILD_H_ diff --git a/dom/fs/child/FileSystemRequestHandler.cpp b/dom/fs/child/FileSystemRequestHandler.cpp index dca5a4e2d368d..9d91661b2029b 100644 --- a/dom/fs/child/FileSystemRequestHandler.cpp +++ b/dom/fs/child/FileSystemRequestHandler.cpp @@ -11,7 +11,7 @@ #include "mozilla/dom/FileSystemDirectoryHandle.h" #include "mozilla/dom/FileSystemFileHandle.h" #include "mozilla/dom/FileSystemHandle.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" +#include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/quota/QuotaCommon.h" diff --git a/dom/fs/child/moz.build b/dom/fs/child/moz.build index c876d033588a6..b37beda7762b3 100644 --- a/dom/fs/child/moz.build +++ b/dom/fs/child/moz.build @@ -5,7 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ - "OriginPrivateFileSystemChild.h", + "FileSystemManagerChild.h", ] UNIFIED_SOURCES += [ diff --git a/dom/fs/include/fs/FileSystemChildFactory.h b/dom/fs/include/fs/FileSystemChildFactory.h index 411b8b6e4e075..7fe58d65e9149 100644 --- a/dom/fs/include/fs/FileSystemChildFactory.h +++ b/dom/fs/include/fs/FileSystemChildFactory.h @@ -13,7 +13,7 @@ namespace mozilla { class ErrorResult; namespace dom { -class OriginPrivateFileSystemChild; +class FileSystemManagerChild; } // namespace dom } // namespace mozilla @@ -21,7 +21,7 @@ namespace mozilla::dom::fs { class FileSystemChildFactory { public: - virtual already_AddRefed Create() const; + virtual already_AddRefed Create() const; virtual ~FileSystemChildFactory() = default; }; diff --git a/dom/fs/parent/OriginPrivateFileSystemParent.cpp b/dom/fs/parent/FileSystemManagerParent.cpp similarity index 75% rename from dom/fs/parent/OriginPrivateFileSystemParent.cpp rename to dom/fs/parent/FileSystemManagerParent.cpp index 6982a520126a5..d3682cb5c3267 100644 --- a/dom/fs/parent/OriginPrivateFileSystemParent.cpp +++ b/dom/fs/parent/FileSystemManagerParent.cpp @@ -4,7 +4,7 @@ * 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 "OriginPrivateFileSystemParent.h" +#include "FileSystemManagerParent.h" #include "nsNetCID.h" #include "mozilla/dom/FileSystemTypes.h" #include "mozilla/ipc/Endpoint.h" @@ -22,11 +22,11 @@ extern LazyLogModule gOPFSLog; namespace mozilla::dom { -OriginPrivateFileSystemParent::OriginPrivateFileSystemParent( - TaskQueue* aTaskQueue, const EntryId& aRootEntry) +FileSystemManagerParent::FileSystemManagerParent(TaskQueue* aTaskQueue, + const EntryId& aRootEntry) : mTaskQueue(aTaskQueue), mData(), mRootEntry(aRootEntry) {} -IPCResult OriginPrivateFileSystemParent::RecvGetRootHandleMsg( +IPCResult FileSystemManagerParent::RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(mRootEntry); aResolver(response); @@ -34,7 +34,7 @@ IPCResult OriginPrivateFileSystemParent::RecvGetRootHandleMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetDirectoryHandleMsg( +IPCResult FileSystemManagerParent::RecvGetDirectoryHandleMsg( FileSystemGetHandleRequest&& /* aRequest */, GetDirectoryHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); @@ -43,7 +43,7 @@ IPCResult OriginPrivateFileSystemParent::RecvGetDirectoryHandleMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetFileHandleMsg( +IPCResult FileSystemManagerParent::RecvGetFileHandleMsg( FileSystemGetHandleRequest&& aRequest, GetFileHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); @@ -52,7 +52,7 @@ IPCResult OriginPrivateFileSystemParent::RecvGetFileHandleMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetFileMsg( +IPCResult FileSystemManagerParent::RecvGetFileMsg( FileSystemGetFileRequest&& aRequest, GetFileMsgResolver&& aResolver) { FileSystemGetFileResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); @@ -60,7 +60,7 @@ IPCResult OriginPrivateFileSystemParent::RecvGetFileMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvResolveMsg( +IPCResult FileSystemManagerParent::RecvResolveMsg( FileSystemResolveRequest&& aRequest, ResolveMsgResolver&& aResolver) { FileSystemResolveResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); @@ -68,7 +68,7 @@ IPCResult OriginPrivateFileSystemParent::RecvResolveMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetEntriesMsg( +IPCResult FileSystemManagerParent::RecvGetEntriesMsg( FileSystemGetEntriesRequest&& aRequest, GetEntriesMsgResolver&& aResolver) { FileSystemGetEntriesResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); @@ -76,7 +76,7 @@ IPCResult OriginPrivateFileSystemParent::RecvGetEntriesMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvRemoveEntryMsg( +IPCResult FileSystemManagerParent::RecvRemoveEntryMsg( FileSystemRemoveEntryRequest&& aRequest, RemoveEntryMsgResolver&& aResolver) { FileSystemRemoveEntryResponse response(NS_ERROR_NOT_IMPLEMENTED); @@ -85,14 +85,14 @@ IPCResult OriginPrivateFileSystemParent::RecvRemoveEntryMsg( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvCloseFile( +IPCResult FileSystemManagerParent::RecvCloseFile( FileSystemGetFileRequest&& aRequest) { LOG(("Closing file")); // painful to print out the id return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetAccessHandle( +IPCResult FileSystemManagerParent::RecvGetAccessHandle( FileSystemGetFileRequest&& aRequest, GetAccessHandleResolver&& aResolver) { FileSystemGetAccessHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); @@ -100,7 +100,7 @@ IPCResult OriginPrivateFileSystemParent::RecvGetAccessHandle( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvGetWritable( +IPCResult FileSystemManagerParent::RecvGetWritable( FileSystemGetFileRequest&& aRequest, GetWritableResolver&& aResolver) { FileSystemGetAccessHandleResponse response(NS_ERROR_NOT_IMPLEMENTED); aResolver(response); @@ -108,15 +108,15 @@ IPCResult OriginPrivateFileSystemParent::RecvGetWritable( return IPC_OK(); } -IPCResult OriginPrivateFileSystemParent::RecvNeedQuota( +IPCResult FileSystemManagerParent::RecvNeedQuota( FileSystemQuotaRequest&& aRequest, NeedQuotaResolver&& aResolver) { aResolver(0u); return IPC_OK(); } -OriginPrivateFileSystemParent::~OriginPrivateFileSystemParent() { - LOG(("Destroying OPFS Parent %p", this)); +FileSystemManagerParent::~FileSystemManagerParent() { + LOG(("Destroying FileSystemManagerParent %p", this)); if (mTaskQueue) { mTaskQueue->BeginShutdown(); } diff --git a/dom/fs/parent/OriginPrivateFileSystemParent.h b/dom/fs/parent/FileSystemManagerParent.h similarity index 82% rename from dom/fs/parent/OriginPrivateFileSystemParent.h rename to dom/fs/parent/FileSystemManagerParent.h index debe50082fe80..6cdd756fbed48 100644 --- a/dom/fs/parent/OriginPrivateFileSystemParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -4,11 +4,11 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#ifndef DOM_FS_PARENT_ORIGINPRIVATEFILESYSTEMPARENT_H_ -#define DOM_FS_PARENT_ORIGINPRIVATEFILESYSTEMPARENT_H_ +#ifndef DOM_FS_PARENT_FILESYSTEMMANAGERPARENT_H_ +#define DOM_FS_PARENT_FILESYSTEMMANAGERPARENT_H_ #include "ErrorList.h" -#include "mozilla/dom/POriginPrivateFileSystemParent.h" +#include "mozilla/dom/PFileSystemManagerParent.h" #include "mozilla/TaskQueue.h" #include "nsISupports.h" @@ -28,10 +28,9 @@ namespace fs::data { class FileSystemDataManagerBase {}; } // namespace fs::data -class OriginPrivateFileSystemParent : public POriginPrivateFileSystemParent { +class FileSystemManagerParent : public PFileSystemManagerParent { public: - OriginPrivateFileSystemParent(TaskQueue* aTaskQueue, - const EntryId& aRootEntry); + FileSystemManagerParent(TaskQueue* aTaskQueue, const EntryId& aRootEntry); mozilla::ipc::IPCResult RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver); @@ -69,10 +68,10 @@ class OriginPrivateFileSystemParent : public POriginPrivateFileSystemParent { mozilla::ipc::IPCResult RecvNeedQuota(FileSystemQuotaRequest&& aRequest, NeedQuotaResolver&& aResolver); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(OriginPrivateFileSystemParent) + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemManagerParent) protected: - virtual ~OriginPrivateFileSystemParent(); + virtual ~FileSystemManagerParent(); private: RefPtr mTaskQueue; @@ -84,4 +83,4 @@ class OriginPrivateFileSystemParent : public POriginPrivateFileSystemParent { } // namespace mozilla::dom -#endif // DOM_FS_PARENT_ORIGINPRIVATEFILESYSTEMPARENT_H_ +#endif // DOM_FS_PARENT_FILESYSTEMMANAGERPARENT_H_ diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index f2943168e2533..5399e56ffed22 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -7,8 +7,8 @@ #include "FileSystemManagerParentFactory.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/dom/FileSystemManagerParent.h" #include "mozilla/dom/FileSystemTypes.h" -#include "mozilla/dom/OriginPrivateFileSystemParent.h" #include "mozilla/dom/quota/QuotaCommon.h" #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/ipc/Endpoint.h" @@ -50,7 +50,7 @@ FileSystemDataManager::CreateFileSystemDataManager( mozilla::ipc::IPCResult CreateFileSystemManagerParent( const mozilla::ipc::PrincipalInfo& aPrincipalInfo, - mozilla::ipc::Endpoint&& aParentEndpoint, + mozilla::ipc::Endpoint&& aParentEndpoint, std::function&& aResolver) { QM_TRY(OkIf(StaticPrefs::dom_fs_enabled()), IPC_OK(), [aResolver](const auto&) { aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR); }); @@ -88,8 +88,8 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( InvokeAsync(taskqueue, __func__, [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, data = std::move(data), taskqueue, pbackground]() mutable { - RefPtr parent = - new OriginPrivateFileSystemParent(taskqueue, rootId); + RefPtr parent = + new FileSystemManagerParent(taskqueue, rootId); if (!parentEp.Bind(parent)) { return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); diff --git a/dom/fs/parent/FileSystemManagerParentFactory.h b/dom/fs/parent/FileSystemManagerParentFactory.h index a13ceb38b7d9e..6d581ffa3fcb3 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.h +++ b/dom/fs/parent/FileSystemManagerParentFactory.h @@ -25,11 +25,11 @@ class PrincipalInfo; namespace dom { -class POriginPrivateFileSystemParent; +class PFileSystemManagerParent; mozilla::ipc::IPCResult CreateFileSystemManagerParent( const mozilla::ipc::PrincipalInfo& aPrincipalInfo, - mozilla::ipc::Endpoint&& + mozilla::ipc::Endpoint&& aParentEndpoint, std::function&& aResolver); diff --git a/dom/fs/parent/moz.build b/dom/fs/parent/moz.build index 67059cf79b994..c2867c67106e4 100644 --- a/dom/fs/parent/moz.build +++ b/dom/fs/parent/moz.build @@ -5,14 +5,14 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ + "FileSystemManagerParent.h", "FileSystemManagerParentFactory.h", - "OriginPrivateFileSystemParent.h", ] UNIFIED_SOURCES += [ "FileSystemHashSource.cpp", + "FileSystemManagerParent.cpp", "FileSystemManagerParentFactory.cpp", - "OriginPrivateFileSystemParent.cpp", ] FINAL_LIBRARY = "xul" diff --git a/dom/fs/shared/FileSystemActorHolder.h b/dom/fs/shared/FileSystemActorHolder.h index 086b28f9dc212..017dd59f45e18 100644 --- a/dom/fs/shared/FileSystemActorHolder.h +++ b/dom/fs/shared/FileSystemActorHolder.h @@ -4,12 +4,12 @@ * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ -#include "mozilla/dom/OriginPrivateFileSystemChild.h" +#include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/ipc/ToplevelActorHolder.h" namespace mozilla::dom { using FileSystemActorHolder = - mozilla::ipc::ToplevelActorHolder; + mozilla::ipc::ToplevelActorHolder; } // namespace mozilla::dom diff --git a/dom/fs/shared/POriginPrivateFileSystem.ipdl b/dom/fs/shared/PFileSystemManager.ipdl similarity index 99% rename from dom/fs/shared/POriginPrivateFileSystem.ipdl rename to dom/fs/shared/PFileSystemManager.ipdl index 63b116fb78784..52e371fbf6c5f 100644 --- a/dom/fs/shared/POriginPrivateFileSystem.ipdl +++ b/dom/fs/shared/PFileSystemManager.ipdl @@ -188,7 +188,7 @@ struct FileSystemQuotaRequest } // namespace fs -async protocol POriginPrivateFileSystem +async protocol PFileSystemManager { parent: /** diff --git a/dom/fs/shared/moz.build b/dom/fs/shared/moz.build index f0e98d671c3d9..c2d111df2e0e1 100644 --- a/dom/fs/shared/moz.build +++ b/dom/fs/shared/moz.build @@ -12,7 +12,7 @@ EXPORTS.mozilla.dom += [ FINAL_LIBRARY = "xul" IPDL_SOURCES += [ - "POriginPrivateFileSystem.ipdl", + "PFileSystemManager.ipdl", ] include("/ipc/chromium/chromium-config.mozbuild") diff --git a/dom/fs/test/gtest/FileSystemMocks.h b/dom/fs/test/gtest/FileSystemMocks.h index 38ff3fdbe48b1..e1686d3f9b86d 100644 --- a/dom/fs/test/gtest/FileSystemMocks.h +++ b/dom/fs/test/gtest/FileSystemMocks.h @@ -19,7 +19,7 @@ #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMExceptionBinding.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" +#include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/PromiseNativeHandler.h" #include "mozilla/dom/ScriptSettings.h" @@ -193,7 +193,7 @@ class TestPromiseListener : public PromiseNativeHandler, ErrorHandler mOnError; }; -class TestOriginPrivateFileSystemChild : public OriginPrivateFileSystemChild { +class TestFileSystemManagerChild : public FileSystemManagerChild { public: MOCK_METHOD(void, SendGetRootHandle, (mozilla::ipc::ResolveCallback && @@ -246,22 +246,22 @@ class TestOriginPrivateFileSystemChild : public OriginPrivateFileSystemChild { MOCK_METHOD(void, Shutdown, (), (override)); protected: - virtual ~TestOriginPrivateFileSystemChild() = default; + virtual ~TestFileSystemManagerChild() = default; }; class TestFileSystemChildFactory final : public FileSystemChildFactory { public: - explicit TestFileSystemChildFactory(TestOriginPrivateFileSystemChild* aChild) + explicit TestFileSystemChildFactory(TestFileSystemManagerChild* aChild) : mChild(aChild) {} - already_AddRefed Create() const override { - return RefPtr(mChild).forget(); + already_AddRefed Create() const override { + return RefPtr(mChild).forget(); } ~TestFileSystemChildFactory() = default; private: - TestOriginPrivateFileSystemChild* mChild; + TestFileSystemManagerChild* mChild; }; struct MockExpectMe { diff --git a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp index 1145ef9e52be1..78585ab351453 100644 --- a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp @@ -14,7 +14,7 @@ #include "mozilla/dom/FileSystemDirectoryIterator.h" #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemHandleBinding.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" +#include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/UniquePtr.h" #include "nsIGlobalObject.h" diff --git a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp index 4103163010f33..a010e748d1595 100644 --- a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp @@ -9,31 +9,31 @@ #include "gtest/gtest.h" #include "mozilla/SpinEventLoopUntil.h" #include "mozilla/UniquePtr.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" -#include "mozilla/dom/POriginPrivateFileSystem.h" +#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/PFileSystemManager.h" namespace mozilla::dom::fs::test { class TestFileSystemBackgroundRequestHandler : public ::testing::Test { protected: void SetUp() override { - mOPFSChild = MakeAndAddRef(); + mFileSystemManagerChild = MakeAndAddRef(); } UniquePtr GetFileSystemBackgroundRequestHandler() { return MakeUnique( - new TestFileSystemChildFactory(mOPFSChild)); + new TestFileSystemChildFactory(mFileSystemManagerChild)); } mozilla::ipc::PrincipalInfo mPrincipalInfo = GetPrincipalInfo(); - RefPtr mOPFSChild; + RefPtr mFileSystemManagerChild; }; TEST_F(TestFileSystemBackgroundRequestHandler, isCreateFileSystemManagerChildSuccessful) { - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); bool done = false; @@ -41,9 +41,7 @@ TEST_F(TestFileSystemBackgroundRequestHandler, testable->CreateFileSystemManagerChild(mPrincipalInfo) ->Then( GetCurrentSerialEventTarget(), __func__, - [&done](const RefPtr& child) { - done = true; - }, + [&done](const RefPtr& child) { done = true; }, [&done](nsresult) { done = true; }); // MozPromise should be rejected SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, diff --git a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp index 71dc189239d30..ba81294e4eba2 100644 --- a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp @@ -10,8 +10,8 @@ #include "fs/FileSystemRequestHandler.h" #include "mozilla/dom/IPCBlob.h" -#include "mozilla/dom/OriginPrivateFileSystemChild.h" -#include "mozilla/dom/POriginPrivateFileSystem.h" +#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/PFileSystemManager.h" #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/IPCCore.h" #include "mozilla/SpinEventLoopUntil.h" @@ -32,8 +32,9 @@ class TestFileSystemRequestHandler : public ::testing::Test { mChild = FileSystemChildMetadata("parent"_ns, u"ChildName"_ns); mEntry = FileSystemEntryMetadata("myid"_ns, u"EntryName"_ns); mName = u"testDir"_ns; - mOPFSChild = MakeAndAddRef(); - mActor = MakeAndAddRef(mOPFSChild.get()); + mFileSystemManagerChild = MakeAndAddRef(); + mActor = + MakeAndAddRef(mFileSystemManagerChild.get()); } void TearDown() override { mActor->RemoveActor(); } @@ -57,7 +58,7 @@ class TestFileSystemRequestHandler : public ::testing::Test { FileSystemChildMetadata mChild; FileSystemEntryMetadata mEntry; nsString mName; - RefPtr mOPFSChild; + RefPtr mFileSystemManagerChild; RefPtr mActor; }; @@ -69,10 +70,10 @@ TEST_F(TestFileSystemRequestHandler, isGetRootHandleSuccessful) { }; EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); - EXPECT_CALL(*mOPFSChild, SendGetRootHandle(_, _)) + EXPECT_CALL(*mFileSystemManagerChild, SendGetRootHandle(_, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); RefPtr promise = GetDefaultPromise(); @@ -91,10 +92,10 @@ TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) { }; EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); - EXPECT_CALL(*mOPFSChild, SendGetDirectoryHandle(_, _, _)) + EXPECT_CALL(*mFileSystemManagerChild, SendGetDirectoryHandle(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); RefPtr promise = GetDefaultPromise(); @@ -114,10 +115,10 @@ TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) { }; EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); - EXPECT_CALL(*mOPFSChild, SendGetFileHandle(_, _, _)) + EXPECT_CALL(*mFileSystemManagerChild, SendGetFileHandle(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); RefPtr promise = GetDefaultPromise(); @@ -144,9 +145,10 @@ TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) { }; EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); - EXPECT_CALL(*mOPFSChild, SendGetFile(_, _, _)).WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, SendGetFile(_, _, _)) + .WillOnce(Invoke(fakeResponse)); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); RefPtr promise = GetDefaultPromise(); @@ -172,10 +174,10 @@ TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) { RefPtr promise = Promise::Create(mGlobal, rv); promise->AppendNativeHandler(listener); - EXPECT_CALL(*mOPFSChild, SendGetEntries(_, _, _)) + EXPECT_CALL(*mFileSystemManagerChild, SendGetEntries(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); auto testable = GetFileSystemRequestHandler(); @@ -193,10 +195,10 @@ TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) { }; EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); - EXPECT_CALL(*mOPFSChild, SendRemoveEntry(_, _, _)) + EXPECT_CALL(*mFileSystemManagerChild, SendRemoveEntry(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mOPFSChild, Shutdown()).WillOnce([this]() { - mOPFSChild->OriginPrivateFileSystemChild::Shutdown(); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { + mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); }); auto testable = GetFileSystemRequestHandler(); diff --git a/ipc/glue/BackgroundParentImpl.cpp b/ipc/glue/BackgroundParentImpl.cpp index 46871eff53493..d230a85b248ca 100644 --- a/ipc/glue/BackgroundParentImpl.cpp +++ b/ipc/glue/BackgroundParentImpl.cpp @@ -491,7 +491,7 @@ BackgroundParentImpl::AllocPBackgroundSessionStorageServiceParent() { mozilla::ipc::IPCResult BackgroundParentImpl::RecvCreateFileSystemManagerParent( const PrincipalInfo& aPrincipalInfo, - Endpoint&& aParentEndpoint, + Endpoint&& aParentEndpoint, CreateFileSystemManagerParentResolver&& aResolver) { AssertIsInMainProcess(); AssertIsOnBackgroundThread(); diff --git a/ipc/glue/BackgroundParentImpl.h b/ipc/glue/BackgroundParentImpl.h index 6de82dc556997..2bad806f128cf 100644 --- a/ipc/glue/BackgroundParentImpl.h +++ b/ipc/glue/BackgroundParentImpl.h @@ -134,7 +134,7 @@ class BackgroundParentImpl : public PBackgroundParent { mozilla::ipc::IPCResult RecvCreateFileSystemManagerParent( const PrincipalInfo& aPrincipalInfo, - Endpoint&& aParentEndpoint, + Endpoint&& aParentEndpoint, CreateFileSystemManagerParentResolver&& aResolver) override; already_AddRefed AllocPIdleSchedulerParent() override; diff --git a/ipc/glue/PBackground.ipdl b/ipc/glue/PBackground.ipdl index 60ae9905d63d9..0cc202897553b 100644 --- a/ipc/glue/PBackground.ipdl +++ b/ipc/glue/PBackground.ipdl @@ -21,7 +21,7 @@ include protocol PCacheStorage; include protocol PCacheStreamControl; include protocol PClientManager; include protocol PEndpointForReport; -include protocol POriginPrivateFileSystem; +include protocol PFileSystemManager; include protocol PFileSystemRequest; include protocol PGamepadEventChannel; include protocol PGamepadTestChannel; @@ -188,11 +188,11 @@ parent: async PBackgroundStorage(nsString profilePath, uint32_t privateBrowsingId); /** - * Finish the setup of a new POriginPrivateFileSystem top level protocol. + * Finish the setup of a new PFileSystemManager top level protocol. */ async CreateFileSystemManagerParent( PrincipalInfo principalInfo, - Endpoint aParentEndpoint) + Endpoint aParentEndpoint) returns(nsresult rv); async PVsync(); From 1ca94ffc488c6eaf01d4abe528b89927d39aab35 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:49 +0000 Subject: [PATCH 05/65] Bug 1786501 - Add empty StorageManager::Shutdown method; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D155369 --- dom/quota/StorageManager.cpp | 2 ++ dom/quota/StorageManager.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp index d421b8b324fb1..5022de903672c 100644 --- a/dom/quota/StorageManager.cpp +++ b/dom/quota/StorageManager.cpp @@ -731,6 +731,8 @@ StorageManager::StorageManager(nsIGlobalObject* aGlobal) : mOwner(aGlobal) { StorageManager::~StorageManager() = default; +void StorageManager::Shutdown() {} + already_AddRefed StorageManager::Persisted(ErrorResult& aRv) { MOZ_ASSERT(mOwner); diff --git a/dom/quota/StorageManager.h b/dom/quota/StorageManager.h index d8b579905aee0..7e97da8da988d 100644 --- a/dom/quota/StorageManager.h +++ b/dom/quota/StorageManager.h @@ -36,6 +36,8 @@ class StorageManager final : public nsISupports, public nsWrapperCache { nsIGlobalObject* GetParentObject() const { return mOwner; } + void Shutdown(); + // WebIDL already_AddRefed Persisted(ErrorResult& aRv); From efcf3c3af28ed50097a6442f913fc0601edc49ee Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:49 +0000 Subject: [PATCH 06/65] Bug 1786501 - Call StorageManager::Shutdown from Navigator::Invalidate; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D155370 --- dom/base/Navigator.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dom/base/Navigator.cpp b/dom/base/Navigator.cpp index a829cf2a8b7b0..a1af8eec1b7ea 100644 --- a/dom/base/Navigator.cpp +++ b/dom/base/Navigator.cpp @@ -175,7 +175,10 @@ void Navigator::Invalidate() { mPermissions = nullptr; - mStorageManager = nullptr; + if (mStorageManager) { + mStorageManager->Shutdown(); + mStorageManager = nullptr; + } // If there is a page transition, make sure delete the geolocation object. if (mGeolocation) { From bccd6f030833c8ea816a8180f1fa412ce390537b Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:50 +0000 Subject: [PATCH 07/65] Bug 1786501 - Implement WorkerNavigator::Invalidate; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D155371 --- dom/workers/WorkerNavigator.cpp | 32 ++++++++++++++++++++++++++++---- dom/workers/WorkerNavigator.h | 2 ++ dom/workers/WorkerScope.cpp | 13 +++++++++++++ dom/workers/WorkerScope.h | 4 ++-- 4 files changed, 45 insertions(+), 6 deletions(-) diff --git a/dom/workers/WorkerNavigator.cpp b/dom/workers/WorkerNavigator.cpp index b4408cb22166b..49cd5bea69684 100644 --- a/dom/workers/WorkerNavigator.cpp +++ b/dom/workers/WorkerNavigator.cpp @@ -39,9 +39,21 @@ namespace mozilla::dom { using namespace workerinternals; -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WorkerNavigator, mStorageManager, - mConnection, mMediaCapabilities, mWebGpu, - mLocks); +NS_IMPL_CYCLE_COLLECTION_CLASS(WorkerNavigator) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WorkerNavigator) + tmp->Invalidate(); + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END + +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WorkerNavigator) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStorageManager) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mConnection) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMediaCapabilities) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWebGpu) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLocks) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END + +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(WorkerNavigator) NS_IMPL_CYCLE_COLLECTION_ROOT_NATIVE(WorkerNavigator, AddRef) NS_IMPL_CYCLE_COLLECTION_UNROOT_NATIVE(WorkerNavigator, Release) @@ -50,7 +62,7 @@ WorkerNavigator::WorkerNavigator(const NavigatorProperties& aProperties, bool aOnline) : mProperties(aProperties), mOnline(aOnline) {} -WorkerNavigator::~WorkerNavigator() = default; +WorkerNavigator::~WorkerNavigator() { Invalidate(); } /* static */ already_AddRefed WorkerNavigator::Create(bool aOnLine) { @@ -65,6 +77,18 @@ already_AddRefed WorkerNavigator::Create(bool aOnLine) { return navigator.forget(); } +void WorkerNavigator::Invalidate() { + mStorageManager = nullptr; + + mConnection = nullptr; + + mMediaCapabilities = nullptr; + + mWebGpu = nullptr; + + mLocks = nullptr; +} + JSObject* WorkerNavigator::WrapObject(JSContext* aCx, JS::Handle aGivenProto) { return WorkerNavigator_Binding::Wrap(aCx, this, aGivenProto); diff --git a/dom/workers/WorkerNavigator.h b/dom/workers/WorkerNavigator.h index b0c24b8f1f59a..8a67c6277ae8c 100644 --- a/dom/workers/WorkerNavigator.h +++ b/dom/workers/WorkerNavigator.h @@ -56,6 +56,8 @@ class WorkerNavigator final : public nsWrapperCache { static already_AddRefed Create(bool aOnLine); + void Invalidate(); + virtual JSObject* WrapObject(JSContext* aCx, JS::Handle aGivenProto) override; diff --git a/dom/workers/WorkerScope.cpp b/dom/workers/WorkerScope.cpp index e0f50ad59e9a7..55a62d4b18c1e 100644 --- a/dom/workers/WorkerScope.cpp +++ b/dom/workers/WorkerScope.cpp @@ -411,6 +411,19 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(WorkerGlobalScope, WorkerGlobalScope::~WorkerGlobalScope() = default; +void WorkerGlobalScope::NoteTerminating() { + if (IsDying()) { + return; + } + + StartDying(); + + if (mNavigator) { + mNavigator->Invalidate(); + mNavigator = nullptr; + } +} + Crypto* WorkerGlobalScope::GetCrypto(ErrorResult& aError) { AssertIsOnWorkerThread(); diff --git a/dom/workers/WorkerScope.h b/dom/workers/WorkerScope.h index 73c4e7c0f8fdd..0135fa6881791 100644 --- a/dom/workers/WorkerScope.h +++ b/dom/workers/WorkerScope.h @@ -156,8 +156,6 @@ class WorkerGlobalScopeBase : public DOMEventTargetHelper, uint64_t WindowID() const; - void NoteTerminating() { StartDying(); } - // Usually global scope dies earlier than the WorkerPrivate, but if we see // it leak at least we can tell it to not carry away a dead pointer. void NoteWorkerTerminated() { mWorkerPrivate = nullptr; } @@ -212,6 +210,8 @@ class WorkerGlobalScope : public WorkerGlobalScopeBase { using WorkerGlobalScopeBase::WorkerGlobalScopeBase; + void NoteTerminating(); + // nsIGlobalObject implementation RefPtr GetServiceWorkerRegistration( const ServiceWorkerRegistrationDescriptor& aDescriptor) const final; From 23d5acfc600f45056649d7127315992da0dee620 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:51 +0000 Subject: [PATCH 08/65] Bug 1786501 - Call StorageManager::Shutdown from WorkerNavigator::Invalidate; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D155372 --- dom/workers/WorkerNavigator.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dom/workers/WorkerNavigator.cpp b/dom/workers/WorkerNavigator.cpp index 49cd5bea69684..b45062359a0ec 100644 --- a/dom/workers/WorkerNavigator.cpp +++ b/dom/workers/WorkerNavigator.cpp @@ -78,7 +78,10 @@ already_AddRefed WorkerNavigator::Create(bool aOnLine) { } void WorkerNavigator::Invalidate() { - mStorageManager = nullptr; + if (mStorageManager) { + mStorageManager->Shutdown(); + mStorageManager = nullptr; + } mConnection = nullptr; From 34b9324920c5b6af101ac75b52ec64bef89bbfb1 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:51 +0000 Subject: [PATCH 09/65] Bug 1786501 - Expose StorageManager::Shutdown to chrome; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D155792 --- dom/quota/StorageManager.cpp | 4 ++-- dom/quota/StorageManager.h | 4 ++-- dom/webidl/StorageManager.webidl | 11 +++++++++++ 3 files changed, 15 insertions(+), 4 deletions(-) diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp index 5022de903672c..f18a9f779c7f8 100644 --- a/dom/quota/StorageManager.cpp +++ b/dom/quota/StorageManager.cpp @@ -731,8 +731,6 @@ StorageManager::StorageManager(nsIGlobalObject* aGlobal) : mOwner(aGlobal) { StorageManager::~StorageManager() = default; -void StorageManager::Shutdown() {} - already_AddRefed StorageManager::Persisted(ErrorResult& aRv) { MOZ_ASSERT(mOwner); @@ -767,6 +765,8 @@ already_AddRefed StorageManager::GetDirectory(ErrorResult& aRv) { return mFileSystemManager->GetDirectory(aRv); } +void StorageManager::Shutdown() {} + NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(StorageManager, mOwner, mFileSystemManager) diff --git a/dom/quota/StorageManager.h b/dom/quota/StorageManager.h index 7e97da8da988d..94d58b342ab52 100644 --- a/dom/quota/StorageManager.h +++ b/dom/quota/StorageManager.h @@ -36,8 +36,6 @@ class StorageManager final : public nsISupports, public nsWrapperCache { nsIGlobalObject* GetParentObject() const { return mOwner; } - void Shutdown(); - // WebIDL already_AddRefed Persisted(ErrorResult& aRv); @@ -47,6 +45,8 @@ class StorageManager final : public nsISupports, public nsWrapperCache { already_AddRefed GetDirectory(ErrorResult& aRv); + void Shutdown(); + NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(StorageManager) diff --git a/dom/webidl/StorageManager.webidl b/dom/webidl/StorageManager.webidl index 0ddbd5081c3b3..9fb832e6368e4 100644 --- a/dom/webidl/StorageManager.webidl +++ b/dom/webidl/StorageManager.webidl @@ -32,3 +32,14 @@ partial interface StorageManager { [Pref="dom.fs.enabled", NewObject] Promise getDirectory(); }; + +/** + * Testing methods that exist only for the benefit of automated glass-box + * testing. Will never be exposed to content at large and unlikely to be useful + * in a WebDriver context. + */ +[SecureContext] +partial interface StorageManager { + [ChromeOnly] + void shutdown(); +}; From b596af3819ecf3e4b2f9129f72e1bb2b56befcde Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:52 +0000 Subject: [PATCH 10/65] Bug 1786501 - Shutdown StorageManager when the test is done in _execute_test; r=smaug Differential Revision: https://phabricator.services.mozilla.com/D155793 --- testing/xpcshell/head.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/testing/xpcshell/head.js b/testing/xpcshell/head.js index c9024f09f8b95..ec2d63c806363 100644 --- a/testing/xpcshell/head.js +++ b/testing/xpcshell/head.js @@ -690,6 +690,13 @@ function _execute_test() { // Restore idle service to avoid leaks. _fakeIdleService.deactivate(); + if ( + globalThis.hasOwnProperty("storage") && + StorageManager.isInstance(globalThis.storage) + ) { + globalThis.storage.shutdown(); + } + if (_profileInitialized) { // Since we have a profile, we will notify profile shutdown topics at // the end of the current test, to ensure correct cleanup on shutdown. From e87e17d52134a7e62f07d8bd9c69f7b7ab8299c4 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:52 +0000 Subject: [PATCH 11/65] Bug 1786465 - Create only one instance of FileSystemManagerChild per nsIGlobalObject; r=dom-storage-reviewers,jesup This is the main lifecycle change. `FileSystemHandle` now holds `FileSystemManager` object instead of holding `FileSystemActorHolder` object. There's only one instance of `FileSystemManager` per `StorageManager` (which is created only once for nsIGlobalObject). `FileSystemActorHolder` objects were created for every StorageManager::GetDirectory call. Differential Revision: https://phabricator.services.mozilla.com/D155381 --- dom/fs/api/FileSystemDirectoryHandle.cpp | 9 +- dom/fs/api/FileSystemDirectoryHandle.h | 4 +- dom/fs/api/FileSystemFileHandle.cpp | 9 +- dom/fs/api/FileSystemFileHandle.h | 4 +- dom/fs/api/FileSystemHandle.cpp | 28 +++-- dom/fs/api/FileSystemHandle.h | 8 +- dom/fs/api/FileSystemManager.cpp | 27 +++-- dom/fs/api/FileSystemManager.h | 9 +- .../FileSystemBackgroundRequestHandler.cpp | 100 +++++++++++------- .../FileSystemBackgroundRequestHandler.h | 20 +++- dom/fs/child/FileSystemRequestHandler.cpp | 99 +++++++++-------- dom/fs/include/fs/FileSystemRequestHandler.h | 17 +-- dom/fs/shared/FileSystemActorHolder.h | 15 --- dom/fs/shared/moz.build | 1 - dom/fs/test/gtest/FileSystemMocks.cpp | 1 + dom/fs/test/gtest/FileSystemMocks.h | 17 ++- .../api/TestFileSystemDirectoryHandle.cpp | 36 +++---- .../gtest/api/TestFileSystemFileHandle.cpp | 25 ++--- .../test/gtest/api/TestFileSystemHandle.cpp | 29 +++-- ...TestFileSystemBackgroundRequestHandler.cpp | 8 +- .../child/TestFileSystemRequestHandler.cpp | 77 +++++++++----- 21 files changed, 304 insertions(+), 239 deletions(-) delete mode 100644 dom/fs/shared/FileSystemActorHolder.h diff --git a/dom/fs/api/FileSystemDirectoryHandle.cpp b/dom/fs/api/FileSystemDirectoryHandle.cpp index bbae7a817e512..d0522933753a6 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.cpp +++ b/dom/fs/api/FileSystemDirectoryHandle.cpp @@ -11,21 +11,22 @@ #include "mozilla/dom/FileSystemDirectoryHandleBinding.h" #include "mozilla/dom/FileSystemDirectoryIterator.h" #include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/PFileSystemManager.h" #include "mozilla/dom/Promise.h" namespace mozilla::dom { FileSystemDirectoryHandle::FileSystemDirectoryHandle( - nsIGlobalObject* aGlobal, RefPtr& aActor, + nsIGlobalObject* aGlobal, RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata, fs::FileSystemRequestHandler* aRequestHandler) - : FileSystemHandle(aGlobal, aActor, aMetadata, aRequestHandler) {} + : FileSystemHandle(aGlobal, aManager, aMetadata, aRequestHandler) {} FileSystemDirectoryHandle::FileSystemDirectoryHandle( - nsIGlobalObject* aGlobal, RefPtr& aActor, + nsIGlobalObject* aGlobal, RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata) - : FileSystemDirectoryHandle(aGlobal, aActor, aMetadata, + : FileSystemDirectoryHandle(aGlobal, aManager, aMetadata, new fs::FileSystemRequestHandler()) {} NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemDirectoryHandle, diff --git a/dom/fs/api/FileSystemDirectoryHandle.h b/dom/fs/api/FileSystemDirectoryHandle.h index 1d0134cb224cb..627c6df5baaec 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.h +++ b/dom/fs/api/FileSystemDirectoryHandle.h @@ -23,12 +23,12 @@ struct FileSystemRemoveOptions; class FileSystemDirectoryHandle final : public FileSystemHandle { public: FileSystemDirectoryHandle(nsIGlobalObject* aGlobal, - RefPtr& aActor, + RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata, fs::FileSystemRequestHandler* aRequestHandler); FileSystemDirectoryHandle(nsIGlobalObject* aGlobal, - RefPtr& aActor, + RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata); NS_DECL_ISUPPORTS_INHERITED diff --git a/dom/fs/api/FileSystemFileHandle.cpp b/dom/fs/api/FileSystemFileHandle.cpp index 6c8429166329a..3d228d0aa2367 100644 --- a/dom/fs/api/FileSystemFileHandle.cpp +++ b/dom/fs/api/FileSystemFileHandle.cpp @@ -10,6 +10,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemFileHandleBinding.h" #include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/PFileSystemManager.h" #include "mozilla/dom/Promise.h" @@ -20,15 +21,15 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHERITED_0(FileSystemFileHandle, NS_IMPL_CYCLE_COLLECTION_INHERITED(FileSystemFileHandle, FileSystemHandle) FileSystemFileHandle::FileSystemFileHandle( - nsIGlobalObject* aGlobal, RefPtr& aActor, + nsIGlobalObject* aGlobal, RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata, fs::FileSystemRequestHandler* aRequestHandler) - : FileSystemHandle(aGlobal, aActor, aMetadata, aRequestHandler) {} + : FileSystemHandle(aGlobal, aManager, aMetadata, aRequestHandler) {} FileSystemFileHandle::FileSystemFileHandle( - nsIGlobalObject* aGlobal, RefPtr& aActor, + nsIGlobalObject* aGlobal, RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata) - : FileSystemFileHandle(aGlobal, aActor, aMetadata, + : FileSystemFileHandle(aGlobal, aManager, aMetadata, new fs::FileSystemRequestHandler()) {} // WebIDL Boilerplate diff --git a/dom/fs/api/FileSystemFileHandle.h b/dom/fs/api/FileSystemFileHandle.h index 3cf6b1021dec7..614264dd8556e 100644 --- a/dom/fs/api/FileSystemFileHandle.h +++ b/dom/fs/api/FileSystemFileHandle.h @@ -20,12 +20,12 @@ struct FileSystemCreateWritableOptions; class FileSystemFileHandle final : public FileSystemHandle { public: FileSystemFileHandle(nsIGlobalObject* aGlobal, - RefPtr& aActor, + RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata, fs::FileSystemRequestHandler* aRequestHandler); FileSystemFileHandle(nsIGlobalObject* aGlobal, - RefPtr& aActor, + RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata); NS_DECL_ISUPPORTS_INHERITED diff --git a/dom/fs/api/FileSystemHandle.cpp b/dom/fs/api/FileSystemHandle.cpp index 14444e535fc05..aa98b2697e7a7 100644 --- a/dom/fs/api/FileSystemHandle.cpp +++ b/dom/fs/api/FileSystemHandle.cpp @@ -9,7 +9,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemHandleBinding.h" -#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/Promise.h" namespace mozilla::dom { @@ -18,16 +18,28 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemHandle) NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END -NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle); -NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle); -NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(FileSystemHandle, mGlobal); + +NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemHandle) +NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemHandle) + +NS_IMPL_CYCLE_COLLECTION_CLASS(FileSystemHandle) +NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(FileSystemHandle) + NS_IMPL_CYCLE_COLLECTION_UNLINK(mGlobal) + // Don't unlink mManager! + NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER +NS_IMPL_CYCLE_COLLECTION_UNLINK_END +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(FileSystemHandle) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mGlobal) + NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mManager) +NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END +NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(FileSystemHandle) FileSystemHandle::FileSystemHandle( - nsIGlobalObject* aGlobal, RefPtr& aActor, + nsIGlobalObject* aGlobal, RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata, fs::FileSystemRequestHandler* aRequestHandler) : mGlobal(aGlobal), - mActor(aActor), + mManager(aManager), mMetadata(aMetadata), mRequestHandler(aRequestHandler) { MOZ_ASSERT(!mMetadata.entryId().IsEmpty()); @@ -60,8 +72,4 @@ already_AddRefed FileSystemHandle::IsSameEntry( return promise.forget(); } -FileSystemManagerChild* FileSystemHandle::Actor() const { - return mActor->Actor(); -} - } // namespace mozilla::dom diff --git a/dom/fs/api/FileSystemHandle.h b/dom/fs/api/FileSystemHandle.h index db9e07f7f9311..9592126e4c674 100644 --- a/dom/fs/api/FileSystemHandle.h +++ b/dom/fs/api/FileSystemHandle.h @@ -7,7 +7,6 @@ #ifndef DOM_FS_FILESYSTEMHANDLE_H_ #define DOM_FS_FILESYSTEMHANDLE_H_ -#include "mozilla/dom/FileSystemActorHolder.h" #include "mozilla/dom/PFileSystemManager.h" #include "mozilla/Logging.h" #include "nsCOMPtr.h" @@ -31,6 +30,7 @@ namespace dom { class DOMString; enum class FileSystemHandleKind : uint8_t; +class FileSystemManager; class FileSystemManagerChild; class Promise; @@ -41,7 +41,7 @@ class FileSystemRequestHandler; class FileSystemHandle : public nsISupports, public nsWrapperCache { public: FileSystemHandle(nsIGlobalObject* aGlobal, - RefPtr& aActor, + RefPtr& aManager, const fs::FileSystemEntryMetadata& aMetadata, fs::FileSystemRequestHandler* aRequestHandler); @@ -62,14 +62,12 @@ class FileSystemHandle : public nsISupports, public nsWrapperCache { already_AddRefed IsSameEntry(FileSystemHandle& aOther, ErrorResult& aError) const; - FileSystemManagerChild* Actor() const; - protected: virtual ~FileSystemHandle() = default; nsCOMPtr mGlobal; - RefPtr mActor; + RefPtr mManager; const fs::FileSystemEntryMetadata mMetadata; diff --git a/dom/fs/api/FileSystemManager.cpp b/dom/fs/api/FileSystemManager.cpp index 93b9a9686882a..3bfa72aaca2c1 100644 --- a/dom/fs/api/FileSystemManager.cpp +++ b/dom/fs/api/FileSystemManager.cpp @@ -9,6 +9,7 @@ #include "FileSystemBackgroundRequestHandler.h" #include "fs/FileSystemRequestHandler.h" #include "mozilla/ErrorResult.h" +#include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/quota/QuotaCommon.h" @@ -49,11 +50,17 @@ Result GetPrincipalInfo( } // namespace -FileSystemManager::FileSystemManager(nsIGlobalObject* aGlobal) +FileSystemManager::FileSystemManager( + nsIGlobalObject* aGlobal, + RefPtr aBackgroundRequestHandler) : mGlobal(aGlobal), - mBackgroundRequestHandler(new FileSystemBackgroundRequestHandler()), + mBackgroundRequestHandler(std::move(aBackgroundRequestHandler)), mRequestHandler(new fs::FileSystemRequestHandler()) {} +FileSystemManager::FileSystemManager(nsIGlobalObject* aGlobal) + : FileSystemManager(aGlobal, + MakeRefPtr()) {} + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemManager) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END @@ -61,6 +68,10 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemManager); NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemManager); NS_IMPL_CYCLE_COLLECTION(FileSystemManager, mGlobal); +FileSystemManagerChild* FileSystemManager::Actor() const { + return mBackgroundRequestHandler->GetFileSystemManagerChild(); +} + already_AddRefed FileSystemManager::GetDirectory(ErrorResult& aRv) { MOZ_ASSERT(mGlobal); @@ -74,14 +85,16 @@ already_AddRefed FileSystemManager::GetDirectory(ErrorResult& aRv) { MOZ_ASSERT(promise); + if (Actor()) { + mRequestHandler->GetRootHandle(this, promise); + return promise.forget(); + } + mBackgroundRequestHandler->CreateFileSystemManagerChild(principalInfo) ->Then( GetCurrentSerialEventTarget(), __func__, - [self = RefPtr(this), - promise](const RefPtr& child) { - RefPtr actorHolder = - MakeAndAddRef(child); - self->mRequestHandler->GetRootHandle(actorHolder, promise); + [self = RefPtr(this), promise](bool) { + self->mRequestHandler->GetRootHandle(self, promise); }, [promise](nsresult) { promise->MaybeRejectWithUnknownError( diff --git a/dom/fs/api/FileSystemManager.h b/dom/fs/api/FileSystemManager.h index 2c3af58706cdd..cab5c6268b52e 100644 --- a/dom/fs/api/FileSystemManager.h +++ b/dom/fs/api/FileSystemManager.h @@ -20,6 +20,7 @@ class ErrorResult; namespace dom { +class FileSystemManagerChild; class FileSystemBackgroundRequestHandler; namespace fs { @@ -35,11 +36,17 @@ class FileSystemRequestHandler; // unlink phase. class FileSystemManager : public nsISupports { public: + FileSystemManager( + nsIGlobalObject* aGlobal, + RefPtr aBackgroundRequestHandler); + explicit FileSystemManager(nsIGlobalObject* aGlobal); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(FileSystemManager) + FileSystemManagerChild* Actor() const; + already_AddRefed GetDirectory(ErrorResult& aRv); private: @@ -47,7 +54,7 @@ class FileSystemManager : public nsISupports { nsCOMPtr mGlobal; - const UniquePtr mBackgroundRequestHandler; + const RefPtr mBackgroundRequestHandler; const UniquePtr mRequestHandler; }; diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp index 84216fbc7fd8a..3eea5ac9deb8c 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp @@ -17,7 +17,12 @@ namespace mozilla::dom { FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler( fs::FileSystemChildFactory* aChildFactory) - : mChildFactory(aChildFactory) {} + : mChildFactory(aChildFactory), mCreatingFileSystemManagerChild(false) {} + +FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler( + RefPtr aFileSystemManagerChild) + : mFileSystemManagerChild(std::move(aFileSystemManagerChild)), + mCreatingFileSystemManagerChild(false) {} FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler() : FileSystemBackgroundRequestHandler(new fs::FileSystemChildFactory()) {} @@ -25,53 +30,72 @@ FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler() FileSystemBackgroundRequestHandler::~FileSystemBackgroundRequestHandler() = default; -RefPtr +FileSystemManagerChild* +FileSystemBackgroundRequestHandler::GetFileSystemManagerChild() const { + return mFileSystemManagerChild; +} + +RefPtr FileSystemBackgroundRequestHandler::CreateFileSystemManagerChild( const mozilla::ipc::PrincipalInfo& aPrincipalInfo) { + MOZ_ASSERT(!mFileSystemManagerChild); + using mozilla::ipc::BackgroundChild; using mozilla::ipc::Endpoint; using mozilla::ipc::PBackgroundChild; - PBackgroundChild* backgroundChild = - BackgroundChild::GetOrCreateForCurrentThread(); - if (NS_WARN_IF(!backgroundChild)) { - return CreateFileSystemManagerChildPromise::CreateAndReject( - NS_ERROR_FAILURE, __func__); - } + if (!mCreatingFileSystemManagerChild) { + PBackgroundChild* backgroundChild = + BackgroundChild::GetOrCreateForCurrentThread(); + if (NS_WARN_IF(!backgroundChild)) { + return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + + // Create a new IPC connection + Endpoint parentEndpoint; + Endpoint childEndpoint; + MOZ_ALWAYS_SUCCEEDS( + PFileSystemManager::CreateEndpoints(&parentEndpoint, &childEndpoint)); + + RefPtr child = mChildFactory->Create(); + if (!childEndpoint.Bind(child)) { + return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + + mCreatingFileSystemManagerChild = true; + + // XXX do something (register with global?) so that we can Close() the actor + // before the event queue starts to shut down. That will cancel all + // outstanding async requests (returns lambdas with errors). + backgroundChild + ->SendCreateFileSystemManagerParent(aPrincipalInfo, + std::move(parentEndpoint)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [self = RefPtr(this), + child](nsresult rv) { + self->mCreatingFileSystemManagerChild = false; + + if (NS_FAILED(rv)) { + self->mCreateFileSystemManagerChildPromiseHolder.RejectIfExists( + rv, __func__); + } else { + self->mFileSystemManagerChild = child; - // Create a new IPC connection - Endpoint parentEndpoint; - Endpoint childEndpoint; - MOZ_ALWAYS_SUCCEEDS( - PFileSystemManager::CreateEndpoints(&parentEndpoint, &childEndpoint)); + self->mCreateFileSystemManagerChildPromiseHolder + .ResolveIfExists(true, __func__); + } + }, + [self = RefPtr(this)]( + const mozilla::ipc::ResponseRejectReason&) { + self->mCreatingFileSystemManagerChild = false; - RefPtr child = mChildFactory->Create(); - if (!childEndpoint.Bind(child)) { - return CreateFileSystemManagerChildPromise::CreateAndReject( - NS_ERROR_FAILURE, __func__); + self->mCreateFileSystemManagerChildPromiseHolder.RejectIfExists( + NS_ERROR_FAILURE, __func__); + }); } - // XXX do something (register with global?) so that we can Close() the actor - // before the event queue starts to shut down. That will cancel all - // outstanding async requests (returns lambdas with errors). - return backgroundChild - ->SendCreateFileSystemManagerParent(aPrincipalInfo, - std::move(parentEndpoint)) - ->Then( - GetCurrentSerialEventTarget(), __func__, - [child](nsresult rv) { - if (NS_FAILED(rv)) { - return CreateFileSystemManagerChildPromise::CreateAndReject( - rv, __func__); - } - - return CreateFileSystemManagerChildPromise::CreateAndResolve( - child, __func__); - }, - [](const mozilla::ipc::ResponseRejectReason&) { - return CreateFileSystemManagerChildPromise::CreateAndReject( - NS_ERROR_FAILURE, __func__); - }); + return mCreateFileSystemManagerChildPromiseHolder.Ensure(__func__); } } // namespace mozilla::dom diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.h b/dom/fs/child/FileSystemBackgroundRequestHandler.h index be23b496c5ba8..8eb730b3fd9dc 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.h +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.h @@ -9,6 +9,7 @@ #include "mozilla/MozPromise.h" #include "mozilla/UniquePtr.h" +#include "mozilla/dom/quota/ForwardDecls.h" template class RefPtr; @@ -32,19 +33,28 @@ class FileSystemBackgroundRequestHandler { explicit FileSystemBackgroundRequestHandler( fs::FileSystemChildFactory* aChildFactory); + explicit FileSystemBackgroundRequestHandler( + RefPtr aFileSystemManagerChild); + FileSystemBackgroundRequestHandler(); - using CreateFileSystemManagerChildPromise = - MozPromise, nsresult, false>; + NS_INLINE_DECL_REFCOUNTING(FileSystemBackgroundRequestHandler) - virtual RefPtr - CreateFileSystemManagerChild( + FileSystemManagerChild* GetFileSystemManagerChild() const; + + virtual RefPtr CreateFileSystemManagerChild( const mozilla::ipc::PrincipalInfo& aPrincipalInfo); + protected: virtual ~FileSystemBackgroundRequestHandler(); - protected: const UniquePtr mChildFactory; + + MozPromiseHolder mCreateFileSystemManagerChildPromiseHolder; + + RefPtr mFileSystemManagerChild; + + bool mCreatingFileSystemManagerChild; }; // class FileSystemBackgroundRequestHandler } // namespace dom diff --git a/dom/fs/child/FileSystemRequestHandler.cpp b/dom/fs/child/FileSystemRequestHandler.cpp index 9d91661b2029b..2d4c9749dd73c 100644 --- a/dom/fs/child/FileSystemRequestHandler.cpp +++ b/dom/fs/child/FileSystemRequestHandler.cpp @@ -11,6 +11,7 @@ #include "mozilla/dom/FileSystemDirectoryHandle.h" #include "mozilla/dom/FileSystemFileHandle.h" #include "mozilla/dom/FileSystemHandle.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/Promise.h" #include "mozilla/dom/quota/QuotaCommon.h" @@ -26,7 +27,7 @@ RefPtr MakeGetFileResult(nsIGlobalObject* aGlobal, const nsString& aName, const nsString& aType, int64_t aLastModifiedMilliSeconds, nsTArray&& aPath, IPCBlob&& /* aFile */, - RefPtr& aActor) { + RefPtr& aManager) { // TODO: Replace with a real implementation RefPtr result = File::CreateMemoryFileWithCustomLastModified( aGlobal, static_cast(new uint8_t[1]), sizeof(uint8_t), aName, @@ -35,21 +36,22 @@ RefPtr MakeGetFileResult(nsIGlobalObject* aGlobal, const nsString& aName, return result; } -void GetDirectoryContentsResponseHandler( - nsIGlobalObject* aGlobal, FileSystemDirectoryListing&& aResponse, - ArrayAppendable& /* aSink */, RefPtr& aActor) { +void GetDirectoryContentsResponseHandler(nsIGlobalObject* aGlobal, + FileSystemDirectoryListing&& aResponse, + ArrayAppendable& /* aSink */, + RefPtr& aManager) { // TODO: Add page size to FileSystemConstants, preallocate and handle overflow nsTArray> batch; for (const auto& it : aResponse.files()) { RefPtr handle = - new FileSystemFileHandle(aGlobal, aActor, it); + new FileSystemFileHandle(aGlobal, aManager, it); batch.AppendElement(handle); } for (const auto& it : aResponse.directories()) { RefPtr handle = - new FileSystemDirectoryHandle(aGlobal, aActor, it); + new FileSystemDirectoryHandle(aGlobal, aManager, it); batch.AppendElement(handle); } } @@ -57,9 +59,9 @@ void GetDirectoryContentsResponseHandler( RefPtr MakeResolution( nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, const RefPtr& /* aResolution */, - RefPtr& aActor) { + RefPtr& aManager) { RefPtr result = new FileSystemDirectoryHandle( - aGlobal, aActor, + aGlobal, aManager, FileSystemEntryMetadata(aResponse.get_EntryId(), kRootName)); return result; } @@ -67,9 +69,10 @@ RefPtr MakeResolution( RefPtr MakeResolution( nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, const RefPtr& /* aResolution */, - const Name& aName, RefPtr& aActor) { + const Name& aName, RefPtr& aManager) { RefPtr result = new FileSystemDirectoryHandle( - aGlobal, aActor, FileSystemEntryMetadata(aResponse.get_EntryId(), aName)); + aGlobal, aManager, + FileSystemEntryMetadata(aResponse.get_EntryId(), aName)); return result; } @@ -77,9 +80,10 @@ RefPtr MakeResolution( RefPtr MakeResolution( nsIGlobalObject* aGlobal, FileSystemGetHandleResponse&& aResponse, const RefPtr& /* aResolution */, const Name& aName, - RefPtr& aActor) { + RefPtr& aManager) { RefPtr result = new FileSystemFileHandle( - aGlobal, aActor, FileSystemEntryMetadata(aResponse.get_EntryId(), aName)); + aGlobal, aManager, + FileSystemEntryMetadata(aResponse.get_EntryId(), aName)); return result; } @@ -87,12 +91,12 @@ RefPtr MakeResolution(nsIGlobalObject* aGlobal, FileSystemGetFileResponse&& aResponse, const RefPtr& /* aResolution */, const Name& aName, - RefPtr& aActor) { + RefPtr& aManager) { auto& fileProperties = aResponse.get_FileSystemFileProperties(); return MakeGetFileResult(aGlobal, aName, fileProperties.type(), fileProperties.last_modified_ms(), std::move(fileProperties.path()), - std::move(fileProperties.file()), aActor); + std::move(fileProperties.file()), aManager); } template @@ -143,7 +147,7 @@ template <> void ResolveCallback(FileSystemGetEntriesResponse&& aResponse, // NOLINTNEXTLINE(performance-unnecessary-value-param) RefPtr aPromise, ArrayAppendable& aSink, - RefPtr& aActor) { + RefPtr& aManager) { // NOLINTEND(readability-inconsistent-declaration-parameter-name) MOZ_ASSERT(aPromise); QM_TRY(OkIf(Promise::PromiseState::Pending == aPromise->State()), QM_VOID); @@ -157,7 +161,7 @@ void ResolveCallback(FileSystemGetEntriesResponse&& aResponse, aPromise->GetParentObject(), std::forward( aResponse.get_FileSystemDirectoryListing()), - aSink, aActor); + aSink, aManager); // TODO: Remove this when sink is ready aPromise->MaybeReject(NS_ERROR_NOT_IMPLEMENTED); @@ -230,24 +234,26 @@ mozilla::ipc::RejectCallback GetRejectCallback( } // namespace void FileSystemRequestHandler::GetRootHandle( - RefPtr& aActor, + RefPtr + aManager, // NOLINT(performance-unnecessary-value-param) RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) MOZ_ASSERT(aPromise); auto&& onResolve = SelectResolveCallback>( - aPromise, aActor); + aPromise, aManager); auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aActor && aActor->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); - aActor->Actor()->SendGetRootHandle(std::move(onResolve), std::move(onReject)); + aManager->Actor()->SendGetRootHandle(std::move(onResolve), + std::move(onReject)); } void FileSystemRequestHandler::GetDirectoryHandle( - RefPtr& aActor, + RefPtr& aManager, const FileSystemChildMetadata& aDirectory, bool aCreate, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) MOZ_ASSERT(!aDirectory.parentId().IsEmpty()); @@ -257,19 +263,19 @@ void FileSystemRequestHandler::GetDirectoryHandle( auto&& onResolve = SelectResolveCallback>( - aPromise, aDirectory.childName(), aActor); + aPromise, aDirectory.childName(), aManager); auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aActor && aActor->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); - aActor->Actor()->SendGetDirectoryHandle(request, std::move(onResolve), - std::move(onReject)); + aManager->Actor()->SendGetDirectoryHandle(request, std::move(onResolve), + std::move(onReject)); } void FileSystemRequestHandler::GetFileHandle( - RefPtr& aActor, const FileSystemChildMetadata& aFile, + RefPtr& aManager, const FileSystemChildMetadata& aFile, bool aCreate, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) MOZ_ASSERT(!aFile.parentId().IsEmpty()); @@ -279,19 +285,19 @@ void FileSystemRequestHandler::GetFileHandle( auto&& onResolve = SelectResolveCallback>( - aPromise, aFile.childName(), aActor); + aPromise, aFile.childName(), aManager); auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aActor && aActor->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); - aActor->Actor()->SendGetFileHandle(request, std::move(onResolve), - std::move(onReject)); + aManager->Actor()->SendGetFileHandle(request, std::move(onResolve), + std::move(onReject)); } void FileSystemRequestHandler::GetFile( - RefPtr& aActor, const FileSystemEntryMetadata& aFile, + RefPtr& aManager, const FileSystemEntryMetadata& aFile, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) MOZ_ASSERT(!aFile.entryId().IsEmpty()); MOZ_ASSERT(aPromise); @@ -300,19 +306,19 @@ void FileSystemRequestHandler::GetFile( auto&& onResolve = SelectResolveCallback>( - aPromise, aFile.entryName(), aActor); + aPromise, aFile.entryName(), aManager); auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aActor && aActor->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); - aActor->Actor()->SendGetFile(request, std::move(onResolve), - std::move(onReject)); + aManager->Actor()->SendGetFile(request, std::move(onResolve), + std::move(onReject)); } void FileSystemRequestHandler::GetEntries( - RefPtr& aActor, const EntryId& aDirectory, + RefPtr& aManager, const EntryId& aDirectory, PageNumber aPage, RefPtr aPromise, // NOLINT(performance-unnecessary-value-param) ArrayAppendable& aSink) { @@ -322,27 +328,28 @@ void FileSystemRequestHandler::GetEntries( FileSystemGetEntriesRequest request(aDirectory, aPage); using TOverload = void (*)(FileSystemGetEntriesResponse&&, RefPtr, - ArrayAppendable&, RefPtr&); + ArrayAppendable&, RefPtr&); // We are not allowed to pass a promise to an external function in a lambda auto&& onResolve = static_cast>( // NOLINTNEXTLINE(modernize-avoid-bind) std::bind(static_cast(ResolveCallback), - std::placeholders::_1, aPromise, std::ref(aSink), aActor)); + std::placeholders::_1, aPromise, std::ref(aSink), + aManager)); auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aActor && aActor->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); - aActor->Actor()->SendGetEntries(request, std::move(onResolve), - std::move(onReject)); + aManager->Actor()->SendGetEntries(request, std::move(onResolve), + std::move(onReject)); } void FileSystemRequestHandler::RemoveEntry( - RefPtr& aActor, - const FileSystemChildMetadata& aEntry, bool aRecursive, + RefPtr& aManager, const FileSystemChildMetadata& aEntry, + bool aRecursive, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) MOZ_ASSERT(!aEntry.parentId().IsEmpty()); MOZ_ASSERT(aPromise); @@ -354,11 +361,11 @@ void FileSystemRequestHandler::RemoveEntry( auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aActor && aActor->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); - aActor->Actor()->SendRemoveEntry(request, std::move(onResolve), - std::move(onReject)); + aManager->Actor()->SendRemoveEntry(request, std::move(onResolve), + std::move(onReject)); } } // namespace mozilla::dom::fs diff --git a/dom/fs/include/fs/FileSystemRequestHandler.h b/dom/fs/include/fs/FileSystemRequestHandler.h index a819772b53437..0d53fcf7939ae 100644 --- a/dom/fs/include/fs/FileSystemRequestHandler.h +++ b/dom/fs/include/fs/FileSystemRequestHandler.h @@ -7,11 +7,14 @@ #ifndef DOM_FS_CHILD_FILESYSTEMREQUESTHANDLER_H_ #define DOM_FS_CHILD_FILESYSTEMREQUESTHANDLER_H_ -#include "mozilla/dom/FileSystemActorHolder.h" #include "mozilla/dom/FileSystemTypes.h" +template +class RefPtr; + namespace mozilla::dom { +class FileSystemManager; class Promise; namespace fs { @@ -22,26 +25,26 @@ class FileSystemEntryMetadata; class FileSystemRequestHandler { public: - virtual void GetRootHandle(RefPtr& aActor, + virtual void GetRootHandle(RefPtr aManager, RefPtr aPromise); - virtual void GetDirectoryHandle(RefPtr& aActor, + virtual void GetDirectoryHandle(RefPtr& aManager, const FileSystemChildMetadata& aDirectory, bool aCreate, RefPtr aPromise); - virtual void GetFileHandle(RefPtr& aActor, + virtual void GetFileHandle(RefPtr& aManager, const FileSystemChildMetadata& aFile, bool aCreate, RefPtr aPromise); - virtual void GetFile(RefPtr& aActor, + virtual void GetFile(RefPtr& aManager, const FileSystemEntryMetadata& aFile, RefPtr aPromise); - virtual void GetEntries(RefPtr& aActor, + virtual void GetEntries(RefPtr& aManager, const EntryId& aDirectory, PageNumber aPage, RefPtr aPromise, ArrayAppendable& aSink); - virtual void RemoveEntry(RefPtr& aActor, + virtual void RemoveEntry(RefPtr& aManager, const FileSystemChildMetadata& aEntry, bool aRecursive, RefPtr aPromise); diff --git a/dom/fs/shared/FileSystemActorHolder.h b/dom/fs/shared/FileSystemActorHolder.h deleted file mode 100644 index 017dd59f45e18..0000000000000 --- a/dom/fs/shared/FileSystemActorHolder.h +++ /dev/null @@ -1,15 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ -/* vim: set ts=8 sts=2 et sw=2 tw=80: */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -#include "mozilla/dom/FileSystemManagerChild.h" -#include "mozilla/ipc/ToplevelActorHolder.h" - -namespace mozilla::dom { - -using FileSystemActorHolder = - mozilla::ipc::ToplevelActorHolder; - -} // namespace mozilla::dom diff --git a/dom/fs/shared/moz.build b/dom/fs/shared/moz.build index c2d111df2e0e1..364b90904efe1 100644 --- a/dom/fs/shared/moz.build +++ b/dom/fs/shared/moz.build @@ -5,7 +5,6 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ - "FileSystemActorHolder.h", "FileSystemTypes.h", ] diff --git a/dom/fs/test/gtest/FileSystemMocks.cpp b/dom/fs/test/gtest/FileSystemMocks.cpp index f3d6932313025..9d18f9a2892fc 100644 --- a/dom/fs/test/gtest/FileSystemMocks.cpp +++ b/dom/fs/test/gtest/FileSystemMocks.cpp @@ -7,6 +7,7 @@ #include "FileSystemMocks.h" #include "jsapi.h" +#include "mozilla/dom/FileSystemManager.h" #include "nsISupports.h" #include "js/RootingAPI.h" diff --git a/dom/fs/test/gtest/FileSystemMocks.h b/dom/fs/test/gtest/FileSystemMocks.h index e1686d3f9b86d..1aedb5e0a99a4 100644 --- a/dom/fs/test/gtest/FileSystemMocks.h +++ b/dom/fs/test/gtest/FileSystemMocks.h @@ -47,35 +47,34 @@ mozilla::ipc::PrincipalInfo GetPrincipalInfo(); class MockFileSystemRequestHandler : public FileSystemRequestHandler { public: MOCK_METHOD(void, GetRootHandle, - (RefPtr & aActor, - RefPtr aPromise), + (RefPtr aManager, RefPtr aPromise), (override)); MOCK_METHOD(void, GetDirectoryHandle, - (RefPtr & aActor, + (RefPtr & aManager, const FileSystemChildMetadata& aDirectory, bool aCreate, RefPtr aPromise), (override)); MOCK_METHOD(void, GetFileHandle, - (RefPtr & aActor, + (RefPtr & aManager, const FileSystemChildMetadata& aFile, bool aCreate, RefPtr aPromise), (override)); MOCK_METHOD(void, GetFile, - (RefPtr & aActor, + (RefPtr & aManager, const FileSystemEntryMetadata& aFile, RefPtr aPromise), (override)); MOCK_METHOD(void, GetEntries, - (RefPtr & aActor, - const EntryId& aDirectory, PageNumber aPage, - RefPtr aPromise, ArrayAppendable& aSink), + (RefPtr & aManager, const EntryId& aDirectory, + PageNumber aPage, RefPtr aPromise, + ArrayAppendable& aSink), (override)); MOCK_METHOD(void, RemoveEntry, - (RefPtr & aActor, + (RefPtr & aManager, const FileSystemChildMetadata& aEntry, bool aRecursive, RefPtr aPromise), (override)); diff --git a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp index 78585ab351453..5c043391225e9 100644 --- a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp @@ -8,13 +8,12 @@ #include "FileSystemMocks.h" -#include "mozilla/dom/FileSystemActorHolder.h" #include "mozilla/dom/FileSystemDirectoryHandle.h" #include "mozilla/dom/FileSystemDirectoryHandleBinding.h" #include "mozilla/dom/FileSystemDirectoryIterator.h" #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemHandleBinding.h" -#include "mozilla/dom/FileSystemManagerChild.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/UniquePtr.h" #include "nsIGlobalObject.h" @@ -27,29 +26,26 @@ class TestFileSystemDirectoryHandle : public ::testing::Test { mRequestHandler = MakeUnique(); mMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns); mName = u"testDir"_ns; - mActor = MakeAndAddRef( - MakeUnique()->Create().take()); + mManager = MakeAndAddRef(mGlobal); } - void TearDown() override { mActor->RemoveActor(); } - nsIGlobalObject* mGlobal = GetGlobal(); UniquePtr mRequestHandler; FileSystemEntryMetadata mMetadata; nsString mName; - RefPtr mActor; + RefPtr mManager; }; TEST_F(TestFileSystemDirectoryHandle, constructDirectoryHandleRefPointer) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata); + MakeAndAddRef(mGlobal, mManager, mMetadata); ASSERT_TRUE(dirHandle); } TEST_F(TestFileSystemDirectoryHandle, areEntriesReturned) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -60,7 +56,7 @@ TEST_F(TestFileSystemDirectoryHandle, areEntriesReturned) { TEST_F(TestFileSystemDirectoryHandle, areKeysReturned) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -71,7 +67,7 @@ TEST_F(TestFileSystemDirectoryHandle, areKeysReturned) { TEST_F(TestFileSystemDirectoryHandle, areValuesReturned) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -82,7 +78,7 @@ TEST_F(TestFileSystemDirectoryHandle, areValuesReturned) { TEST_F(TestFileSystemDirectoryHandle, isHandleKindDirectory) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -92,7 +88,7 @@ TEST_F(TestFileSystemDirectoryHandle, isHandleKindDirectory) { TEST_F(TestFileSystemDirectoryHandle, isFileHandleReturned) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -107,7 +103,7 @@ TEST_F(TestFileSystemDirectoryHandle, isFileHandleReturned) { TEST_F(TestFileSystemDirectoryHandle, doesGetFileHandleFailOnNullGlobal) { mGlobal = nullptr; RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata); + MakeAndAddRef(mGlobal, mManager, mMetadata); ASSERT_TRUE(dirHandle); @@ -120,7 +116,7 @@ TEST_F(TestFileSystemDirectoryHandle, doesGetFileHandleFailOnNullGlobal) { TEST_F(TestFileSystemDirectoryHandle, isDirectoryHandleReturned) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -135,7 +131,7 @@ TEST_F(TestFileSystemDirectoryHandle, isDirectoryHandleReturned) { TEST_F(TestFileSystemDirectoryHandle, doesGetDirectoryHandleFailOnNullGlobal) { mGlobal = nullptr; RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata); + MakeAndAddRef(mGlobal, mManager, mMetadata); ASSERT_TRUE(dirHandle); @@ -148,7 +144,7 @@ TEST_F(TestFileSystemDirectoryHandle, doesGetDirectoryHandleFailOnNullGlobal) { TEST_F(TestFileSystemDirectoryHandle, isRemoveEntrySuccessful) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -163,7 +159,7 @@ TEST_F(TestFileSystemDirectoryHandle, isRemoveEntrySuccessful) { TEST_F(TestFileSystemDirectoryHandle, doesRemoveEntryFailOnNullGlobal) { mGlobal = nullptr; RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata); + MakeAndAddRef(mGlobal, mManager, mMetadata); ASSERT_TRUE(dirHandle); @@ -176,7 +172,7 @@ TEST_F(TestFileSystemDirectoryHandle, doesRemoveEntryFailOnNullGlobal) { TEST_F(TestFileSystemDirectoryHandle, isResolveSuccessful) { RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata, + MakeAndAddRef(mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(dirHandle); @@ -190,7 +186,7 @@ TEST_F(TestFileSystemDirectoryHandle, isResolveSuccessful) { TEST_F(TestFileSystemDirectoryHandle, doesResolveFailOnNullGlobal) { mGlobal = nullptr; RefPtr dirHandle = - MakeAndAddRef(mGlobal, mActor, mMetadata); + MakeAndAddRef(mGlobal, mManager, mMetadata); ASSERT_TRUE(dirHandle); diff --git a/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp index e0ef35afa0020..2038b6401005f 100644 --- a/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp @@ -10,11 +10,11 @@ #include "fs/FileSystemChildFactory.h" -#include "mozilla/dom/FileSystemActorHolder.h" #include "mozilla/dom/FileSystemFileHandle.h" #include "mozilla/dom/FileSystemFileHandleBinding.h" #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/Promise.h" #include "mozilla/UniquePtr.h" @@ -27,28 +27,25 @@ class TestFileSystemFileHandle : public ::testing::Test { void SetUp() override { mRequestHandler = MakeUnique(); mMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns); - mActor = MakeAndAddRef( - MakeUnique()->Create().take()); + mManager = MakeAndAddRef(mGlobal); } - void TearDown() override { mActor->RemoveActor(); } - nsIGlobalObject* mGlobal = GetGlobal(); UniquePtr mRequestHandler; FileSystemEntryMetadata mMetadata; - RefPtr mActor; + RefPtr mManager; }; TEST_F(TestFileSystemFileHandle, constructFileHandleRefPointer) { RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); } TEST_F(TestFileSystemFileHandle, isHandleKindFile) { RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); @@ -57,7 +54,7 @@ TEST_F(TestFileSystemFileHandle, isHandleKindFile) { TEST_F(TestFileSystemFileHandle, isFileReturned) { RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); @@ -70,7 +67,7 @@ TEST_F(TestFileSystemFileHandle, isFileReturned) { TEST_F(TestFileSystemFileHandle, doesGetFileFailOnNullGlobal) { mGlobal = nullptr; RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); @@ -82,7 +79,7 @@ TEST_F(TestFileSystemFileHandle, doesGetFileFailOnNullGlobal) { TEST_F(TestFileSystemFileHandle, isWritableReturned) { RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); @@ -96,7 +93,7 @@ TEST_F(TestFileSystemFileHandle, isWritableReturned) { TEST_F(TestFileSystemFileHandle, doesCreateWritableFailOnNullGlobal) { mGlobal = nullptr; RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); @@ -109,7 +106,7 @@ TEST_F(TestFileSystemFileHandle, doesCreateWritableFailOnNullGlobal) { TEST_F(TestFileSystemFileHandle, isSyncAccessHandleReturned) { RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); @@ -122,7 +119,7 @@ TEST_F(TestFileSystemFileHandle, isSyncAccessHandleReturned) { TEST_F(TestFileSystemFileHandle, doesCreateSyncAccessHandleFailOnNullGlobal) { mGlobal = nullptr; RefPtr fileHandle = MakeAndAddRef( - mGlobal, mActor, mMetadata, mRequestHandler.release()); + mGlobal, mManager, mMetadata, mRequestHandler.release()); ASSERT_TRUE(fileHandle); diff --git a/dom/fs/test/gtest/api/TestFileSystemHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemHandle.cpp index e7d2518eeb251..eac4a2184ddd7 100644 --- a/dom/fs/test/gtest/api/TestFileSystemHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemHandle.cpp @@ -10,12 +10,12 @@ #include "fs/FileSystemChildFactory.h" -#include "mozilla/dom/FileSystemActorHolder.h" #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemDirectoryHandle.h" #include "mozilla/dom/FileSystemFileHandle.h" #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemHandleBinding.h" +#include "mozilla/dom/FileSystemManager.h" #include "nsIGlobalObject.h" namespace mozilla::dom::fs::test { @@ -25,23 +25,20 @@ class TestFileSystemHandle : public ::testing::Test { void SetUp() override { mDirMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns); mFileMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns); - mActor = MakeAndAddRef( - MakeUnique()->Create().take()); + mManager = MakeAndAddRef(mGlobal); } - void TearDown() override { mActor->RemoveActor(); } - nsIGlobalObject* mGlobal = GetGlobal(); FileSystemEntryMetadata mDirMetadata; FileSystemEntryMetadata mFileMetadata; - RefPtr mActor; + RefPtr mManager; }; TEST_F(TestFileSystemHandle, createAndDestroyHandles) { RefPtr dirHandle = - new FileSystemDirectoryHandle(mGlobal, mActor, mDirMetadata); + new FileSystemDirectoryHandle(mGlobal, mManager, mDirMetadata); RefPtr fileHandle = - new FileSystemFileHandle(mGlobal, mActor, mFileMetadata); + new FileSystemFileHandle(mGlobal, mManager, mFileMetadata); EXPECT_TRUE(dirHandle); EXPECT_TRUE(fileHandle); @@ -49,9 +46,9 @@ TEST_F(TestFileSystemHandle, createAndDestroyHandles) { TEST_F(TestFileSystemHandle, areFileNamesAsExpected) { RefPtr dirHandle = - new FileSystemDirectoryHandle(mGlobal, mActor, mDirMetadata); + new FileSystemDirectoryHandle(mGlobal, mManager, mDirMetadata); RefPtr fileHandle = - new FileSystemFileHandle(mGlobal, mActor, mFileMetadata); + new FileSystemFileHandle(mGlobal, mManager, mFileMetadata); auto GetEntryName = [](const RefPtr& aHandle) { DOMString domName; @@ -71,16 +68,16 @@ TEST_F(TestFileSystemHandle, areFileNamesAsExpected) { TEST_F(TestFileSystemHandle, isParentObjectReturned) { ASSERT_TRUE(mGlobal); RefPtr dirHandle = - new FileSystemDirectoryHandle(mGlobal, mActor, mDirMetadata); + new FileSystemDirectoryHandle(mGlobal, mManager, mDirMetadata); ASSERT_EQ(mGlobal, dirHandle->GetParentObject()); } TEST_F(TestFileSystemHandle, areHandleKindsAsExpected) { RefPtr dirHandle = - new FileSystemDirectoryHandle(mGlobal, mActor, mDirMetadata); + new FileSystemDirectoryHandle(mGlobal, mManager, mDirMetadata); RefPtr fileHandle = - new FileSystemFileHandle(mGlobal, mActor, mFileMetadata); + new FileSystemFileHandle(mGlobal, mManager, mFileMetadata); EXPECT_EQ(FileSystemHandleKind::Directory, dirHandle->Kind()); EXPECT_EQ(FileSystemHandleKind::File, fileHandle->Kind()); @@ -88,9 +85,9 @@ TEST_F(TestFileSystemHandle, areHandleKindsAsExpected) { TEST_F(TestFileSystemHandle, isDifferentEntry) { RefPtr dirHandle = - new FileSystemDirectoryHandle(mGlobal, mActor, mDirMetadata); + new FileSystemDirectoryHandle(mGlobal, mManager, mDirMetadata); RefPtr fileHandle = - new FileSystemFileHandle(mGlobal, mActor, mFileMetadata); + new FileSystemFileHandle(mGlobal, mManager, mFileMetadata); IgnoredErrorResult rv; RefPtr promise = dirHandle->IsSameEntry(*fileHandle, rv); @@ -101,7 +98,7 @@ TEST_F(TestFileSystemHandle, isDifferentEntry) { TEST_F(TestFileSystemHandle, isSameEntry) { RefPtr fileHandle = - new FileSystemFileHandle(mGlobal, mActor, mFileMetadata); + new FileSystemFileHandle(mGlobal, mManager, mFileMetadata); IgnoredErrorResult rv; RefPtr promise = fileHandle->IsSameEntry(*fileHandle, rv); diff --git a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp index a010e748d1595..d9f8ae1ec0340 100644 --- a/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemBackgroundRequestHandler.cpp @@ -9,6 +9,7 @@ #include "gtest/gtest.h" #include "mozilla/SpinEventLoopUntil.h" #include "mozilla/UniquePtr.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/PFileSystemManager.h" @@ -20,9 +21,9 @@ class TestFileSystemBackgroundRequestHandler : public ::testing::Test { mFileSystemManagerChild = MakeAndAddRef(); } - UniquePtr + RefPtr GetFileSystemBackgroundRequestHandler() { - return MakeUnique( + return MakeRefPtr( new TestFileSystemChildFactory(mFileSystemManagerChild)); } @@ -41,8 +42,7 @@ TEST_F(TestFileSystemBackgroundRequestHandler, testable->CreateFileSystemManagerChild(mPrincipalInfo) ->Then( GetCurrentSerialEventTarget(), __func__, - [&done](const RefPtr& child) { done = true; }, - [&done](nsresult) { done = true; }); + [&done](bool) { done = true; }, [&done](nsresult) { done = true; }); // MozPromise should be rejected SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [&done]() { return done; }); diff --git a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp index ba81294e4eba2..3664ca1840532 100644 --- a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp @@ -6,10 +6,12 @@ #include "gtest/gtest.h" +#include "FileSystemBackgroundRequestHandler.h" #include "FileSystemMocks.h" #include "fs/FileSystemRequestHandler.h" #include "mozilla/dom/IPCBlob.h" +#include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/PFileSystemManager.h" #include "mozilla/ipc/FileDescriptorUtils.h" @@ -33,12 +35,11 @@ class TestFileSystemRequestHandler : public ::testing::Test { mEntry = FileSystemEntryMetadata("myid"_ns, u"EntryName"_ns); mName = u"testDir"_ns; mFileSystemManagerChild = MakeAndAddRef(); - mActor = - MakeAndAddRef(mFileSystemManagerChild.get()); + mManager = MakeAndAddRef( + mGlobal, MakeRefPtr( + mFileSystemManagerChild)); } - void TearDown() override { mActor->RemoveActor(); } - already_AddRefed GetDefaultPromise() { IgnoredErrorResult rv; RefPtr result = Promise::Create(mGlobal, rv); @@ -59,7 +60,7 @@ class TestFileSystemRequestHandler : public ::testing::Test { FileSystemEntryMetadata mEntry; nsString mName; RefPtr mFileSystemManagerChild; - RefPtr mActor; + RefPtr mManager; }; TEST_F(TestFileSystemRequestHandler, isGetRootHandleSuccessful) { @@ -72,13 +73,16 @@ TEST_F(TestFileSystemRequestHandler, isGetRootHandleSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mFileSystemManagerChild, SendGetRootHandle(_, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { - mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); - }); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()) + .WillOnce([fileSystemManagerChild = + static_cast(mFileSystemManagerChild.get())]() { + static_cast(fileSystemManagerChild) + ->FileSystemManagerChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); - testable->GetRootHandle(mActor, promise); + testable->GetRootHandle(mManager, promise); SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [this]() { return mListener->IsDone(); }); } @@ -94,13 +98,16 @@ TEST_F(TestFileSystemRequestHandler, isGetDirectoryHandleSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mFileSystemManagerChild, SendGetDirectoryHandle(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { - mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); - }); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()) + .WillOnce([fileSystemManagerChild = + static_cast(mFileSystemManagerChild.get())]() { + static_cast(fileSystemManagerChild) + ->FileSystemManagerChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); - testable->GetDirectoryHandle(mActor, mChild, + testable->GetDirectoryHandle(mManager, mChild, /* create */ true, promise); SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [this]() { return mListener->IsDone(); }); @@ -117,13 +124,16 @@ TEST_F(TestFileSystemRequestHandler, isGetFileHandleSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mFileSystemManagerChild, SendGetFileHandle(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { - mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); - }); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()) + .WillOnce([fileSystemManagerChild = + static_cast(mFileSystemManagerChild.get())]() { + static_cast(fileSystemManagerChild) + ->FileSystemManagerChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); - testable->GetFileHandle(mActor, mChild, /* create */ true, promise); + testable->GetFileHandle(mManager, mChild, /* create */ true, promise); SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [this]() { return mListener->IsDone(); }); } @@ -147,13 +157,16 @@ TEST_F(TestFileSystemRequestHandler, isGetFileSuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mFileSystemManagerChild, SendGetFile(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { - mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); - }); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()) + .WillOnce([fileSystemManagerChild = + static_cast(mFileSystemManagerChild.get())]() { + static_cast(fileSystemManagerChild) + ->FileSystemManagerChild::Shutdown(); + }); RefPtr promise = GetDefaultPromise(); auto testable = GetFileSystemRequestHandler(); - testable->GetFile(mActor, mEntry, promise); + testable->GetFile(mManager, mEntry, promise); SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [this]() { return mListener->IsDone(); }); } @@ -176,13 +189,16 @@ TEST_F(TestFileSystemRequestHandler, isGetEntriesSuccessful) { EXPECT_CALL(*mFileSystemManagerChild, SendGetEntries(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { - mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); - }); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()) + .WillOnce([fileSystemManagerChild = + static_cast(mFileSystemManagerChild.get())]() { + static_cast(fileSystemManagerChild) + ->FileSystemManagerChild::Shutdown(); + }); auto testable = GetFileSystemRequestHandler(); ArrayAppendable sink; - testable->GetEntries(mActor, mEntry.entryId(), /* page */ 0, promise, sink); + testable->GetEntries(mManager, mEntry.entryId(), /* page */ 0, promise, sink); SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [listener]() { return listener->IsDone(); }); } @@ -197,13 +213,16 @@ TEST_F(TestFileSystemRequestHandler, isRemoveEntrySuccessful) { EXPECT_CALL(mListener->GetSuccessHandler(), InvokeMe()); EXPECT_CALL(*mFileSystemManagerChild, SendRemoveEntry(_, _, _)) .WillOnce(Invoke(fakeResponse)); - EXPECT_CALL(*mFileSystemManagerChild, Shutdown()).WillOnce([this]() { - mFileSystemManagerChild->FileSystemManagerChild::Shutdown(); - }); + EXPECT_CALL(*mFileSystemManagerChild, Shutdown()) + .WillOnce([fileSystemManagerChild = + static_cast(mFileSystemManagerChild.get())]() { + static_cast(fileSystemManagerChild) + ->FileSystemManagerChild::Shutdown(); + }); auto testable = GetFileSystemRequestHandler(); RefPtr promise = GetDefaultPromise(); - testable->RemoveEntry(mActor, mChild, /* recursive */ true, promise); + testable->RemoveEntry(mManager, mChild, /* recursive */ true, promise); SpinEventLoopUntil("Promise is fulfilled or timeout"_ns, [this]() { return mListener->IsDone(); }); } From 6dfa4bdac10e64ae411aa57aceff3d442bcc460b Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:53 +0000 Subject: [PATCH 12/65] Bug 1786465 - Expect not null FileSystemManager in FileSystemRequestHandler methods; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155382 --- dom/fs/child/FileSystemRequestHandler.cpp | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/dom/fs/child/FileSystemRequestHandler.cpp b/dom/fs/child/FileSystemRequestHandler.cpp index 2d4c9749dd73c..ca6220ddd1790 100644 --- a/dom/fs/child/FileSystemRequestHandler.cpp +++ b/dom/fs/child/FileSystemRequestHandler.cpp @@ -237,6 +237,7 @@ void FileSystemRequestHandler::GetRootHandle( RefPtr aManager, // NOLINT(performance-unnecessary-value-param) RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aManager); MOZ_ASSERT(aPromise); auto&& onResolve = SelectResolveCallbackActor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); aManager->Actor()->SendGetRootHandle(std::move(onResolve), @@ -256,6 +257,7 @@ void FileSystemRequestHandler::GetDirectoryHandle( RefPtr& aManager, const FileSystemChildMetadata& aDirectory, bool aCreate, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aManager); MOZ_ASSERT(!aDirectory.parentId().IsEmpty()); MOZ_ASSERT(aPromise); @@ -267,7 +269,7 @@ void FileSystemRequestHandler::GetDirectoryHandle( auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); aManager->Actor()->SendGetDirectoryHandle(request, std::move(onResolve), @@ -278,6 +280,7 @@ void FileSystemRequestHandler::GetFileHandle( RefPtr& aManager, const FileSystemChildMetadata& aFile, bool aCreate, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aManager); MOZ_ASSERT(!aFile.parentId().IsEmpty()); MOZ_ASSERT(aPromise); @@ -289,7 +292,7 @@ void FileSystemRequestHandler::GetFileHandle( auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); aManager->Actor()->SendGetFileHandle(request, std::move(onResolve), @@ -299,6 +302,7 @@ void FileSystemRequestHandler::GetFileHandle( void FileSystemRequestHandler::GetFile( RefPtr& aManager, const FileSystemEntryMetadata& aFile, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aManager); MOZ_ASSERT(!aFile.entryId().IsEmpty()); MOZ_ASSERT(aPromise); @@ -310,7 +314,7 @@ void FileSystemRequestHandler::GetFile( auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); aManager->Actor()->SendGetFile(request, std::move(onResolve), @@ -322,6 +326,7 @@ void FileSystemRequestHandler::GetEntries( PageNumber aPage, RefPtr aPromise, // NOLINT(performance-unnecessary-value-param) ArrayAppendable& aSink) { + MOZ_ASSERT(aManager); MOZ_ASSERT(!aDirectory.IsEmpty()); MOZ_ASSERT(aPromise); @@ -340,7 +345,7 @@ void FileSystemRequestHandler::GetEntries( auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); aManager->Actor()->SendGetEntries(request, std::move(onResolve), @@ -351,6 +356,7 @@ void FileSystemRequestHandler::RemoveEntry( RefPtr& aManager, const FileSystemChildMetadata& aEntry, bool aRecursive, RefPtr aPromise) { // NOLINT(performance-unnecessary-value-param) + MOZ_ASSERT(aManager); MOZ_ASSERT(!aEntry.parentId().IsEmpty()); MOZ_ASSERT(aPromise); @@ -361,7 +367,7 @@ void FileSystemRequestHandler::RemoveEntry( auto&& onReject = GetRejectCallback(aPromise); - QM_TRY(OkIf(aManager && aManager->Actor()), QM_VOID, [aPromise](const auto&) { + QM_TRY(OkIf(aManager->Actor()), QM_VOID, [aPromise](const auto&) { aPromise->MaybeRejectWithUnknownError("Invalid actor"); }); aManager->Actor()->SendRemoveEntry(request, std::move(onResolve), From d7bfb3b3dc433865dc0110b87b0820bd72498884 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:53 +0000 Subject: [PATCH 13/65] Bug 1786465 - Shutdown FileSystemManager when StorageManager is shutting down; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155383 --- dom/fs/api/FileSystemManager.cpp | 2 ++ dom/fs/api/FileSystemManager.h | 2 ++ dom/fs/child/FileSystemBackgroundRequestHandler.cpp | 7 +++++++ dom/fs/child/FileSystemBackgroundRequestHandler.h | 2 ++ dom/quota/StorageManager.cpp | 7 ++++++- 5 files changed, 19 insertions(+), 1 deletion(-) diff --git a/dom/fs/api/FileSystemManager.cpp b/dom/fs/api/FileSystemManager.cpp index 3bfa72aaca2c1..a70f7873e2ee4 100644 --- a/dom/fs/api/FileSystemManager.cpp +++ b/dom/fs/api/FileSystemManager.cpp @@ -68,6 +68,8 @@ NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemManager); NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemManager); NS_IMPL_CYCLE_COLLECTION(FileSystemManager, mGlobal); +void FileSystemManager::Shutdown() { mBackgroundRequestHandler->Shutdown(); } + FileSystemManagerChild* FileSystemManager::Actor() const { return mBackgroundRequestHandler->GetFileSystemManagerChild(); } diff --git a/dom/fs/api/FileSystemManager.h b/dom/fs/api/FileSystemManager.h index cab5c6268b52e..7effa19924d2c 100644 --- a/dom/fs/api/FileSystemManager.h +++ b/dom/fs/api/FileSystemManager.h @@ -45,6 +45,8 @@ class FileSystemManager : public nsISupports { NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(FileSystemManager) + void Shutdown(); + FileSystemManagerChild* Actor() const; already_AddRefed GetDirectory(ErrorResult& aRv); diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp index 3eea5ac9deb8c..5ca9a40dfa3fb 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.cpp +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.cpp @@ -30,6 +30,13 @@ FileSystemBackgroundRequestHandler::FileSystemBackgroundRequestHandler() FileSystemBackgroundRequestHandler::~FileSystemBackgroundRequestHandler() = default; +void FileSystemBackgroundRequestHandler::Shutdown() { + if (mFileSystemManagerChild) { + mFileSystemManagerChild->Shutdown(); + mFileSystemManagerChild = nullptr; + } +} + FileSystemManagerChild* FileSystemBackgroundRequestHandler::GetFileSystemManagerChild() const { return mFileSystemManagerChild; diff --git a/dom/fs/child/FileSystemBackgroundRequestHandler.h b/dom/fs/child/FileSystemBackgroundRequestHandler.h index 8eb730b3fd9dc..a8721d5cba896 100644 --- a/dom/fs/child/FileSystemBackgroundRequestHandler.h +++ b/dom/fs/child/FileSystemBackgroundRequestHandler.h @@ -40,6 +40,8 @@ class FileSystemBackgroundRequestHandler { NS_INLINE_DECL_REFCOUNTING(FileSystemBackgroundRequestHandler) + void Shutdown(); + FileSystemManagerChild* GetFileSystemManagerChild() const; virtual RefPtr CreateFileSystemManagerChild( diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp index f18a9f779c7f8..33fe256a785b7 100644 --- a/dom/quota/StorageManager.cpp +++ b/dom/quota/StorageManager.cpp @@ -765,7 +765,12 @@ already_AddRefed StorageManager::GetDirectory(ErrorResult& aRv) { return mFileSystemManager->GetDirectory(aRv); } -void StorageManager::Shutdown() {} +void StorageManager::Shutdown() { + if (mFileSystemManager) { + mFileSystemManager->Shutdown(); + mFileSystemManager = nullptr; + } +} NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(StorageManager, mOwner, mFileSystemManager) From e13c3eb055495bb3f804aec4c031423d1dcc7548 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:53 +0000 Subject: [PATCH 14/65] Bug 1786465 - Add a StorageManager back reference to FileSystemManager; r=dom-storage-reviewers,jesup This will become useful when FileSystemManager gets invalidated during origin clearing. In that case, we need to throw away existing FileSystemManager and create it again later. Differential Revision: https://phabricator.services.mozilla.com/D155384 --- dom/fs/api/FileSystemDirectoryHandle.cpp | 1 + dom/fs/api/FileSystemManager.cpp | 13 +++++++++---- dom/fs/api/FileSystemManager.h | 10 +++++++--- .../gtest/api/TestFileSystemDirectoryHandle.cpp | 3 ++- dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp | 4 ++-- dom/fs/test/gtest/api/TestFileSystemHandle.cpp | 3 ++- .../gtest/child/TestFileSystemRequestHandler.cpp | 6 ++++-- dom/quota/StorageManager.cpp | 2 +- 8 files changed, 28 insertions(+), 14 deletions(-) diff --git a/dom/fs/api/FileSystemDirectoryHandle.cpp b/dom/fs/api/FileSystemDirectoryHandle.cpp index d0522933753a6..1b5b36bfc646d 100644 --- a/dom/fs/api/FileSystemDirectoryHandle.cpp +++ b/dom/fs/api/FileSystemDirectoryHandle.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/PFileSystemManager.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/StorageManager.h" namespace mozilla::dom { diff --git a/dom/fs/api/FileSystemManager.cpp b/dom/fs/api/FileSystemManager.cpp index a70f7873e2ee4..63fe3358ee173 100644 --- a/dom/fs/api/FileSystemManager.cpp +++ b/dom/fs/api/FileSystemManager.cpp @@ -11,6 +11,7 @@ #include "mozilla/ErrorResult.h" #include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/Promise.h" +#include "mozilla/dom/StorageManager.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/quota/QuotaCommon.h" #include "mozilla/dom/quota/ResultExtensions.h" @@ -51,22 +52,26 @@ Result GetPrincipalInfo( } // namespace FileSystemManager::FileSystemManager( - nsIGlobalObject* aGlobal, + nsIGlobalObject* aGlobal, RefPtr aStorageManager, RefPtr aBackgroundRequestHandler) : mGlobal(aGlobal), + mStorageManager(std::move(aStorageManager)), mBackgroundRequestHandler(std::move(aBackgroundRequestHandler)), mRequestHandler(new fs::FileSystemRequestHandler()) {} -FileSystemManager::FileSystemManager(nsIGlobalObject* aGlobal) - : FileSystemManager(aGlobal, +FileSystemManager::FileSystemManager(nsIGlobalObject* aGlobal, + RefPtr aStorageManager) + : FileSystemManager(aGlobal, std::move(aStorageManager), MakeRefPtr()) {} +FileSystemManager::~FileSystemManager() = default; + NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(FileSystemManager) NS_INTERFACE_MAP_ENTRY(nsISupports) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTING_ADDREF(FileSystemManager); NS_IMPL_CYCLE_COLLECTING_RELEASE(FileSystemManager); -NS_IMPL_CYCLE_COLLECTION(FileSystemManager, mGlobal); +NS_IMPL_CYCLE_COLLECTION(FileSystemManager, mGlobal, mStorageManager); void FileSystemManager::Shutdown() { mBackgroundRequestHandler->Shutdown(); } diff --git a/dom/fs/api/FileSystemManager.h b/dom/fs/api/FileSystemManager.h index 7effa19924d2c..1286102f5c282 100644 --- a/dom/fs/api/FileSystemManager.h +++ b/dom/fs/api/FileSystemManager.h @@ -22,6 +22,7 @@ namespace dom { class FileSystemManagerChild; class FileSystemBackgroundRequestHandler; +class StorageManager; namespace fs { class FileSystemRequestHandler; @@ -37,10 +38,11 @@ class FileSystemRequestHandler; class FileSystemManager : public nsISupports { public: FileSystemManager( - nsIGlobalObject* aGlobal, + nsIGlobalObject* aGlobal, RefPtr aStorageManager, RefPtr aBackgroundRequestHandler); - explicit FileSystemManager(nsIGlobalObject* aGlobal); + FileSystemManager(nsIGlobalObject* aGlobal, + RefPtr aStorageManager); NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_CYCLE_COLLECTION_CLASS(FileSystemManager) @@ -52,10 +54,12 @@ class FileSystemManager : public nsISupports { already_AddRefed GetDirectory(ErrorResult& aRv); private: - virtual ~FileSystemManager() = default; + virtual ~FileSystemManager(); nsCOMPtr mGlobal; + RefPtr mStorageManager; + const RefPtr mBackgroundRequestHandler; const UniquePtr mRequestHandler; }; diff --git a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp index 5c043391225e9..e4a79eb6ef62c 100644 --- a/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemDirectoryHandle.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemHandleBinding.h" #include "mozilla/dom/FileSystemManager.h" +#include "mozilla/dom/StorageManager.h" #include "mozilla/UniquePtr.h" #include "nsIGlobalObject.h" @@ -26,7 +27,7 @@ class TestFileSystemDirectoryHandle : public ::testing::Test { mRequestHandler = MakeUnique(); mMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns); mName = u"testDir"_ns; - mManager = MakeAndAddRef(mGlobal); + mManager = MakeAndAddRef(mGlobal, nullptr); } nsIGlobalObject* mGlobal = GetGlobal(); diff --git a/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp index 2038b6401005f..15b7946d7d9ea 100644 --- a/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemFileHandle.cpp @@ -16,7 +16,7 @@ #include "mozilla/dom/FileSystemHandleBinding.h" #include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/Promise.h" - +#include "mozilla/dom/StorageManager.h" #include "mozilla/UniquePtr.h" #include "nsIGlobalObject.h" @@ -27,7 +27,7 @@ class TestFileSystemFileHandle : public ::testing::Test { void SetUp() override { mRequestHandler = MakeUnique(); mMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns); - mManager = MakeAndAddRef(mGlobal); + mManager = MakeAndAddRef(mGlobal, nullptr); } nsIGlobalObject* mGlobal = GetGlobal(); diff --git a/dom/fs/test/gtest/api/TestFileSystemHandle.cpp b/dom/fs/test/gtest/api/TestFileSystemHandle.cpp index eac4a2184ddd7..4c1d339f83c92 100644 --- a/dom/fs/test/gtest/api/TestFileSystemHandle.cpp +++ b/dom/fs/test/gtest/api/TestFileSystemHandle.cpp @@ -16,6 +16,7 @@ #include "mozilla/dom/FileSystemHandle.h" #include "mozilla/dom/FileSystemHandleBinding.h" #include "mozilla/dom/FileSystemManager.h" +#include "mozilla/dom/StorageManager.h" #include "nsIGlobalObject.h" namespace mozilla::dom::fs::test { @@ -25,7 +26,7 @@ class TestFileSystemHandle : public ::testing::Test { void SetUp() override { mDirMetadata = FileSystemEntryMetadata("dir"_ns, u"Directory"_ns); mFileMetadata = FileSystemEntryMetadata("file"_ns, u"File"_ns); - mManager = MakeAndAddRef(mGlobal); + mManager = MakeAndAddRef(mGlobal, nullptr); } nsIGlobalObject* mGlobal = GetGlobal(); diff --git a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp index 3664ca1840532..d948599289c1a 100644 --- a/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp +++ b/dom/fs/test/gtest/child/TestFileSystemRequestHandler.cpp @@ -14,6 +14,7 @@ #include "mozilla/dom/FileSystemManager.h" #include "mozilla/dom/FileSystemManagerChild.h" #include "mozilla/dom/PFileSystemManager.h" +#include "mozilla/dom/StorageManager.h" #include "mozilla/ipc/FileDescriptorUtils.h" #include "mozilla/ipc/IPCCore.h" #include "mozilla/SpinEventLoopUntil.h" @@ -36,8 +37,9 @@ class TestFileSystemRequestHandler : public ::testing::Test { mName = u"testDir"_ns; mFileSystemManagerChild = MakeAndAddRef(); mManager = MakeAndAddRef( - mGlobal, MakeRefPtr( - mFileSystemManagerChild)); + mGlobal, nullptr, + MakeRefPtr( + mFileSystemManagerChild)); } already_AddRefed GetDefaultPromise() { diff --git a/dom/quota/StorageManager.cpp b/dom/quota/StorageManager.cpp index 33fe256a785b7..0d2423a590481 100644 --- a/dom/quota/StorageManager.cpp +++ b/dom/quota/StorageManager.cpp @@ -759,7 +759,7 @@ already_AddRefed StorageManager::GetDirectory(ErrorResult& aRv) { if (!mFileSystemManager) { MOZ_ASSERT(mOwner); - mFileSystemManager = MakeRefPtr(mOwner); + mFileSystemManager = MakeRefPtr(mOwner, this); } return mFileSystemManager->GetDirectory(aRv); From 4e80e418b5057bffda30346ab0a9a86e442da3bb Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:54 +0000 Subject: [PATCH 15/65] Bug 1786465 - Move FileSystemDataManager to own source files; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155550 --- dom/fs/parent/FileSystemManagerParent.cpp | 3 +- dom/fs/parent/FileSystemManagerParent.h | 5 +-- .../parent/FileSystemManagerParentFactory.cpp | 43 +++++++------------ .../datamodel/FileSystemDataManager.cpp | 19 ++++++++ .../parent/datamodel/FileSystemDataManager.h | 29 +++++++++++++ dom/fs/parent/datamodel/moz.build | 22 ++++++++++ dom/fs/parent/moz.build | 4 ++ 7 files changed, 93 insertions(+), 32 deletions(-) create mode 100644 dom/fs/parent/datamodel/FileSystemDataManager.cpp create mode 100644 dom/fs/parent/datamodel/FileSystemDataManager.h create mode 100644 dom/fs/parent/datamodel/moz.build diff --git a/dom/fs/parent/FileSystemManagerParent.cpp b/dom/fs/parent/FileSystemManagerParent.cpp index d3682cb5c3267..f134ef82149eb 100644 --- a/dom/fs/parent/FileSystemManagerParent.cpp +++ b/dom/fs/parent/FileSystemManagerParent.cpp @@ -6,6 +6,7 @@ #include "FileSystemManagerParent.h" #include "nsNetCID.h" +#include "mozilla/dom/FileSystemDataManager.h" #include "mozilla/dom/FileSystemTypes.h" #include "mozilla/ipc/Endpoint.h" @@ -24,7 +25,7 @@ namespace mozilla::dom { FileSystemManagerParent::FileSystemManagerParent(TaskQueue* aTaskQueue, const EntryId& aRootEntry) - : mTaskQueue(aTaskQueue), mData(), mRootEntry(aRootEntry) {} + : mTaskQueue(aTaskQueue), mRootEntry(aRootEntry) {} IPCResult FileSystemManagerParent::RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver) { diff --git a/dom/fs/parent/FileSystemManagerParent.h b/dom/fs/parent/FileSystemManagerParent.h index 6cdd756fbed48..a4cfea8fcb257 100644 --- a/dom/fs/parent/FileSystemManagerParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -24,8 +24,7 @@ extern LazyLogModule gOPFSLog; namespace mozilla::dom { namespace fs::data { -// TODO: Replace dummy with real FileSystemDataManager -class FileSystemDataManagerBase {}; +class FileSystemDataManager; } // namespace fs::data class FileSystemManagerParent : public PFileSystemManagerParent { @@ -76,7 +75,7 @@ class FileSystemManagerParent : public PFileSystemManagerParent { private: RefPtr mTaskQueue; - UniquePtr mData; + UniquePtr mDataManager; const EntryId mRootEntry; }; diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index 5399e56ffed22..eb0c28a2750e8 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -7,6 +7,7 @@ #include "FileSystemManagerParentFactory.h" #include "mozilla/StaticPrefs_dom.h" +#include "mozilla/dom/FileSystemDataManager.h" #include "mozilla/dom/FileSystemManagerParent.h" #include "mozilla/dom/FileSystemTypes.h" #include "mozilla/dom/quota/QuotaCommon.h" @@ -32,20 +33,6 @@ namespace fs::data { EntryId GetRootHandle(const Origin& aOrigin) { return "not implemented"_ns; } -// TODO: Replace with real FileSystemDataManager -class FileSystemDataManager : public FileSystemDataManagerBase { - public: - using result_t = Result; - static FileSystemDataManager::result_t CreateFileSystemDataManager( - const fs::Origin& aOrigin); -}; - -FileSystemDataManager::result_t -FileSystemDataManager::CreateFileSystemDataManager( - const fs::Origin& /*aOrigin*/) { - return nullptr; -} - } // namespace fs::data mozilla::ipc::IPCResult CreateFileSystemManagerParent( @@ -67,10 +54,10 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( // This opens the quota manager, which has to be done on PBackground QM_TRY_UNWRAP( - fs::data::FileSystemDataManager * dataRaw, + fs::data::FileSystemDataManager * dataManagerRaw, fs::data::FileSystemDataManager::CreateFileSystemDataManager(origin), IPC_OK(), sendBackError); - UniquePtr data(dataRaw); + UniquePtr dataManager(dataManagerRaw); nsCOMPtr pbackground = NS_GetCurrentThread(); @@ -85,18 +72,18 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( // We'll have to thread-hop back to this thread to respond. We could // just have the create be one-way, then send the actual request on the // new channel, but that's an extra IPC instead. - InvokeAsync(taskqueue, __func__, - [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, - data = std::move(data), taskqueue, pbackground]() mutable { - RefPtr parent = - new FileSystemManagerParent(taskqueue, rootId); - if (!parentEp.Bind(parent)) { - return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, - __func__); - } - // Send response back to pbackground to send to child - return BoolPromise::CreateAndResolve(true, __func__); - }) + InvokeAsync( + taskqueue, __func__, + [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, + dataManager = std::move(dataManager), taskqueue, pbackground]() mutable { + RefPtr parent = + new FileSystemManagerParent(taskqueue, rootId); + if (!parentEp.Bind(parent)) { + return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + // Send response back to pbackground to send to child + return BoolPromise::CreateAndResolve(true, __func__); + }) ->Then(GetCurrentSerialEventTarget(), __func__, [aResolver](const BoolPromise::ResolveOrRejectValue& aValue) { if (aValue.IsReject()) { diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp new file mode 100644 index 0000000000000..112e17113fdaa --- /dev/null +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -0,0 +1,19 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "FileSystemDataManager.h" + +#include "mozilla/Result.h" + +namespace mozilla::dom::fs::data { + +FileSystemDataManager::result_t +FileSystemDataManager::CreateFileSystemDataManager( + const fs::Origin& /*aOrigin*/) { + return nullptr; +} + +} // namespace mozilla::dom::fs::data diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h new file mode 100644 index 0000000000000..aeaabe5dd30db --- /dev/null +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -0,0 +1,29 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ +#define DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ + +#include "mozilla/dom/FileSystemTypes.h" + +namespace mozilla { + +template +class Result; + +namespace dom::fs::data { + +class FileSystemDataManager { + public: + using result_t = Result; + static FileSystemDataManager::result_t CreateFileSystemDataManager( + const fs::Origin& aOrigin); +}; + +} // namespace dom::fs::data +} // namespace mozilla + +#endif // DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ diff --git a/dom/fs/parent/datamodel/moz.build b/dom/fs/parent/datamodel/moz.build new file mode 100644 index 0000000000000..964bfeec7b846 --- /dev/null +++ b/dom/fs/parent/datamodel/moz.build @@ -0,0 +1,22 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +EXPORTS.mozilla.dom += [ + "FileSystemDataManager.h", +] + +UNIFIED_SOURCES += [ + "FileSystemDataManager.cpp", +] + +LOCAL_INCLUDES += [ + "/dom/fs/include", + "/dom/fs/parent", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul" diff --git a/dom/fs/parent/moz.build b/dom/fs/parent/moz.build index c2867c67106e4..62dc3f37b9acc 100644 --- a/dom/fs/parent/moz.build +++ b/dom/fs/parent/moz.build @@ -4,6 +4,10 @@ # 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/. +DIRS += [ + "datamodel", +] + EXPORTS.mozilla.dom += [ "FileSystemManagerParent.h", "FileSystemManagerParentFactory.h", From 5f483e76b8f9b86463f53941dd1d604ab509d323 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:54 +0000 Subject: [PATCH 16/65] Bug 1786465 - Convert FileSystemDataManager to be ref counted; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155551 --- dom/fs/parent/FileSystemManagerParent.h | 2 +- dom/fs/parent/FileSystemManagerParentFactory.cpp | 6 ++++-- dom/fs/parent/datamodel/FileSystemDataManager.cpp | 2 +- dom/fs/parent/datamodel/FileSystemDataManager.h | 8 +++++++- 4 files changed, 13 insertions(+), 5 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParent.h b/dom/fs/parent/FileSystemManagerParent.h index a4cfea8fcb257..a37fa16cb3a7b 100644 --- a/dom/fs/parent/FileSystemManagerParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -75,7 +75,7 @@ class FileSystemManagerParent : public PFileSystemManagerParent { private: RefPtr mTaskQueue; - UniquePtr mDataManager; + RefPtr mDataManager; const EntryId mRootEntry; }; diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index eb0c28a2750e8..4a783012b4397 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -54,10 +54,9 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( // This opens the quota manager, which has to be done on PBackground QM_TRY_UNWRAP( - fs::data::FileSystemDataManager * dataManagerRaw, + RefPtr dataManager, fs::data::FileSystemDataManager::CreateFileSystemDataManager(origin), IPC_OK(), sendBackError); - UniquePtr dataManager(dataManagerRaw); nsCOMPtr pbackground = NS_GetCurrentThread(); @@ -76,6 +75,9 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( taskqueue, __func__, [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, dataManager = std::move(dataManager), taskqueue, pbackground]() mutable { + NS_ProxyRelease("ReleaseFileSystemDataManager", pbackground, + dataManager.forget()); + RefPtr parent = new FileSystemManagerParent(taskqueue, rootId); if (!parentEp.Bind(parent)) { diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 112e17113fdaa..03b77bc42b671 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -13,7 +13,7 @@ namespace mozilla::dom::fs::data { FileSystemDataManager::result_t FileSystemDataManager::CreateFileSystemDataManager( const fs::Origin& /*aOrigin*/) { - return nullptr; + return RefPtr(); } } // namespace mozilla::dom::fs::data diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index aeaabe5dd30db..cc1a39ade7cee 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -8,6 +8,7 @@ #define DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ #include "mozilla/dom/FileSystemTypes.h" +#include "nsISupportsUtils.h" namespace mozilla { @@ -18,9 +19,14 @@ namespace dom::fs::data { class FileSystemDataManager { public: - using result_t = Result; + using result_t = Result, nsresult>; static FileSystemDataManager::result_t CreateFileSystemDataManager( const fs::Origin& aOrigin); + + NS_INLINE_DECL_REFCOUNTING(FileSystemDataManager) + + protected: + ~FileSystemDataManager() = default; }; } // namespace dom::fs::data From f2f4261753129a83899c59cd3d9417172afe57b2 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:55 +0000 Subject: [PATCH 17/65] Bug 1786465 - Start using FileSystemDataManager in FileSystemManagerParent; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155552 --- dom/fs/parent/FileSystemManagerParent.cpp | 15 ++++++++++++--- dom/fs/parent/FileSystemManagerParent.h | 4 +++- dom/fs/parent/FileSystemManagerParentFactory.cpp | 7 ++----- dom/fs/parent/datamodel/FileSystemDataManager.cpp | 6 +++++- dom/fs/parent/datamodel/FileSystemDataManager.h | 10 ++++++++++ 5 files changed, 32 insertions(+), 10 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParent.cpp b/dom/fs/parent/FileSystemManagerParent.cpp index f134ef82149eb..d7b35461b191a 100644 --- a/dom/fs/parent/FileSystemManagerParent.cpp +++ b/dom/fs/parent/FileSystemManagerParent.cpp @@ -23,9 +23,12 @@ extern LazyLogModule gOPFSLog; namespace mozilla::dom { -FileSystemManagerParent::FileSystemManagerParent(TaskQueue* aTaskQueue, - const EntryId& aRootEntry) - : mTaskQueue(aTaskQueue), mRootEntry(aRootEntry) {} +FileSystemManagerParent::FileSystemManagerParent( + TaskQueue* aTaskQueue, RefPtr aDataManager, + const EntryId& aRootEntry) + : mTaskQueue(aTaskQueue), + mDataManager(std::move(aDataManager)), + mRootEntry(aRootEntry) {} IPCResult FileSystemManagerParent::RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver) { @@ -121,6 +124,12 @@ FileSystemManagerParent::~FileSystemManagerParent() { if (mTaskQueue) { mTaskQueue->BeginShutdown(); } + + nsCOMPtr target = + mDataManager->MutableBackgroundTargetPtr(); + + NS_ProxyRelease("ReleaseFileSystemDataManager", target, + mDataManager.forget()); } } // namespace mozilla::dom diff --git a/dom/fs/parent/FileSystemManagerParent.h b/dom/fs/parent/FileSystemManagerParent.h index a37fa16cb3a7b..9f08de4f8739b 100644 --- a/dom/fs/parent/FileSystemManagerParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -29,7 +29,9 @@ class FileSystemDataManager; class FileSystemManagerParent : public PFileSystemManagerParent { public: - FileSystemManagerParent(TaskQueue* aTaskQueue, const EntryId& aRootEntry); + FileSystemManagerParent(TaskQueue* aTaskQueue, + RefPtr aDataManager, + const EntryId& aRootEntry); mozilla::ipc::IPCResult RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver); diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index 4a783012b4397..c8aa45a9e1ac0 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -75,11 +75,8 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( taskqueue, __func__, [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, dataManager = std::move(dataManager), taskqueue, pbackground]() mutable { - NS_ProxyRelease("ReleaseFileSystemDataManager", pbackground, - dataManager.forget()); - - RefPtr parent = - new FileSystemManagerParent(taskqueue, rootId); + RefPtr parent = new FileSystemManagerParent( + taskqueue, std::move(dataManager), rootId); if (!parentEp.Bind(parent)) { return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 03b77bc42b671..0d3b88f488e74 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -7,13 +7,17 @@ #include "FileSystemDataManager.h" #include "mozilla/Result.h" +#include "nsThreadUtils.h" namespace mozilla::dom::fs::data { +FileSystemDataManager::FileSystemDataManager() + : mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())) {} + FileSystemDataManager::result_t FileSystemDataManager::CreateFileSystemDataManager( const fs::Origin& /*aOrigin*/) { - return RefPtr(); + return MakeRefPtr(); } } // namespace mozilla::dom::fs::data diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index cc1a39ade7cee..719918de8c785 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -7,7 +7,9 @@ #ifndef DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ #define DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ +#include "mozilla/NotNull.h" #include "mozilla/dom/FileSystemTypes.h" +#include "nsCOMPtr.h" #include "nsISupportsUtils.h" namespace mozilla { @@ -19,14 +21,22 @@ namespace dom::fs::data { class FileSystemDataManager { public: + FileSystemDataManager(); + using result_t = Result, nsresult>; static FileSystemDataManager::result_t CreateFileSystemDataManager( const fs::Origin& aOrigin); NS_INLINE_DECL_REFCOUNTING(FileSystemDataManager) + nsISerialEventTarget* MutableBackgroundTargetPtr() const { + return mBackgroundTarget.get(); + } + protected: ~FileSystemDataManager() = default; + + const NotNull> mBackgroundTarget; }; } // namespace dom::fs::data From b13211a1b7732a8fcdbd1440bd6958b72d94c80f Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:55 +0000 Subject: [PATCH 18/65] Bug 1786465 - Create only one FileSystemDataManager per origin; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155553 --- .../parent/FileSystemManagerParentFactory.cpp | 2 +- .../datamodel/FileSystemDataManager.cpp | 90 +++++++++++++++++-- .../parent/datamodel/FileSystemDataManager.h | 14 +-- 3 files changed, 95 insertions(+), 11 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index c8aa45a9e1ac0..ec407128ca1c9 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -55,7 +55,7 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( // This opens the quota manager, which has to be done on PBackground QM_TRY_UNWRAP( RefPtr dataManager, - fs::data::FileSystemDataManager::CreateFileSystemDataManager(origin), + fs::data::FileSystemDataManager::GetOrCreateFileSystemDataManager(origin), IPC_OK(), sendBackError); nsCOMPtr pbackground = NS_GetCurrentThread(); diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 0d3b88f488e74..0aeb4b5c270b3 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -7,17 +7,97 @@ #include "FileSystemDataManager.h" #include "mozilla/Result.h" +#include "mozilla/StaticPtr.h" +#include "nsBaseHashtable.h" +#include "nsHashKeys.h" #include "nsThreadUtils.h" namespace mozilla::dom::fs::data { -FileSystemDataManager::FileSystemDataManager() - : mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())) {} +namespace { + +// nsCStringHashKey with disabled memmove +class nsCStringHashKeyDM : public nsCStringHashKey { + public: + explicit nsCStringHashKeyDM(const nsCStringHashKey::KeyTypePointer aKey) + : nsCStringHashKey(aKey) {} + enum { ALLOW_MEMMOVE = false }; +}; + +// When CheckedUnsafePtr's checking is enabled, it's necessary to ensure that +// the hashtable uses the copy constructor instead of memmove for moving entries +// since memmove will break CheckedUnsafePtr in a memory-corrupting way. +using FileSystemDataManagerHashKey = + std::conditional::type; + +// Raw (but checked when the diagnostic assert is enabled) references as we +// don't want to keep FileSystemDataManager objects alive forever. When a +// FileSystemDataManager is destroyed it calls RemoveFileSystemDataManager +// to clear itself. +using FileSystemDataManagerHashtable = + nsBaseHashtable>, + MovingNotNull>>; + +StaticAutoPtr gDataManagers; + +RefPtr GetFileSystemDataManager(const Origin& aOrigin) { + if (gDataManagers) { + auto maybeDataManager = gDataManagers->MaybeGet(aOrigin); + if (maybeDataManager) { + RefPtr result( + std::move(*maybeDataManager).unwrapBasePtr()); + return result; + } + } + + return nullptr; +} + +void AddFileSystemDataManager( + const Origin& aOrigin, const RefPtr& aDataManager) { + if (!gDataManagers) { + gDataManagers = new FileSystemDataManagerHashtable(); + } + + MOZ_ASSERT(!gDataManagers->Contains(aOrigin)); + gDataManagers->InsertOrUpdate(aOrigin, + WrapMovingNotNullUnchecked(aDataManager)); +} + +void RemoveFileSystemDataManager(const Origin& aOrigin) { + MOZ_ASSERT(gDataManagers); + const DebugOnly removed = gDataManagers->Remove(aOrigin); + MOZ_ASSERT(removed); + + if (!gDataManagers->Count()) { + gDataManagers = nullptr; + } +} + +} // namespace + +FileSystemDataManager::FileSystemDataManager(const Origin& aOrigin) + : mOrigin(aOrigin), + mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())) {} + +FileSystemDataManager::~FileSystemDataManager() { + RemoveFileSystemDataManager(mOrigin); +} FileSystemDataManager::result_t -FileSystemDataManager::CreateFileSystemDataManager( - const fs::Origin& /*aOrigin*/) { - return MakeRefPtr(); +FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { + if (RefPtr dataManager = + GetFileSystemDataManager(aOrigin)) { + return dataManager; + } + + auto dataManager = MakeRefPtr(aOrigin); + + AddFileSystemDataManager(aOrigin, dataManager); + + return dataManager; } } // namespace mozilla::dom::fs::data diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index 719918de8c785..6e6f10b582515 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -9,8 +9,10 @@ #include "mozilla/NotNull.h" #include "mozilla/dom/FileSystemTypes.h" +#include "mozilla/dom/quota/CheckedUnsafePtr.h" #include "nsCOMPtr.h" #include "nsISupportsUtils.h" +#include "nsString.h" namespace mozilla { @@ -19,13 +21,14 @@ class Result; namespace dom::fs::data { -class FileSystemDataManager { +class FileSystemDataManager + : public SupportsCheckedUnsafePtr> { public: - FileSystemDataManager(); + explicit FileSystemDataManager(const Origin& aOrigin); using result_t = Result, nsresult>; - static FileSystemDataManager::result_t CreateFileSystemDataManager( - const fs::Origin& aOrigin); + static FileSystemDataManager::result_t GetOrCreateFileSystemDataManager( + const Origin& aOrigin); NS_INLINE_DECL_REFCOUNTING(FileSystemDataManager) @@ -34,8 +37,9 @@ class FileSystemDataManager { } protected: - ~FileSystemDataManager() = default; + ~FileSystemDataManager(); + const Origin mOrigin; const NotNull> mBackgroundTarget; }; From 1d8065b5b45e53bfef64460af72f77eb6138665a Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:55 +0000 Subject: [PATCH 19/65] Bug 1786465 - Move TaskQueue ownership from FileSystemManagerParent to FileSystemDataManager; r=dom-storage-reviewers,jesup After this change, we will create only one task queue per origin. Differential Revision: https://phabricator.services.mozilla.com/D155554 --- dom/fs/parent/FileSystemManagerParent.cpp | 9 ++--- dom/fs/parent/FileSystemManagerParent.h | 6 +--- .../parent/FileSystemManagerParentFactory.cpp | 34 +++++++------------ .../datamodel/FileSystemDataManager.cpp | 25 ++++++++++++-- .../parent/datamodel/FileSystemDataManager.h | 9 ++++- 5 files changed, 45 insertions(+), 38 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParent.cpp b/dom/fs/parent/FileSystemManagerParent.cpp index d7b35461b191a..7b13cf4b6bc5a 100644 --- a/dom/fs/parent/FileSystemManagerParent.cpp +++ b/dom/fs/parent/FileSystemManagerParent.cpp @@ -24,11 +24,9 @@ extern LazyLogModule gOPFSLog; namespace mozilla::dom { FileSystemManagerParent::FileSystemManagerParent( - TaskQueue* aTaskQueue, RefPtr aDataManager, + RefPtr aDataManager, const EntryId& aRootEntry) - : mTaskQueue(aTaskQueue), - mDataManager(std::move(aDataManager)), - mRootEntry(aRootEntry) {} + : mDataManager(std::move(aDataManager)), mRootEntry(aRootEntry) {} IPCResult FileSystemManagerParent::RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver) { @@ -121,9 +119,6 @@ IPCResult FileSystemManagerParent::RecvNeedQuota( FileSystemManagerParent::~FileSystemManagerParent() { LOG(("Destroying FileSystemManagerParent %p", this)); - if (mTaskQueue) { - mTaskQueue->BeginShutdown(); - } nsCOMPtr target = mDataManager->MutableBackgroundTargetPtr(); diff --git a/dom/fs/parent/FileSystemManagerParent.h b/dom/fs/parent/FileSystemManagerParent.h index 9f08de4f8739b..6f82261998744 100644 --- a/dom/fs/parent/FileSystemManagerParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -9,7 +9,6 @@ #include "ErrorList.h" #include "mozilla/dom/PFileSystemManagerParent.h" -#include "mozilla/TaskQueue.h" #include "nsISupports.h" namespace mozilla { @@ -29,8 +28,7 @@ class FileSystemDataManager; class FileSystemManagerParent : public PFileSystemManagerParent { public: - FileSystemManagerParent(TaskQueue* aTaskQueue, - RefPtr aDataManager, + FileSystemManagerParent(RefPtr aDataManager, const EntryId& aRootEntry); mozilla::ipc::IPCResult RecvGetRootHandleMsg( @@ -75,8 +73,6 @@ class FileSystemManagerParent : public PFileSystemManagerParent { virtual ~FileSystemManagerParent(); private: - RefPtr mTaskQueue; - RefPtr mDataManager; const EntryId mRootEntry; diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index ec407128ca1c9..3b12f11b0fa55 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -14,8 +14,6 @@ #include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/ipc/Endpoint.h" #include "nsIScriptObjectPrincipal.h" -#include "nsNetCID.h" -#include "nsServiceManagerUtils.h" #include "nsString.h" namespace mozilla { @@ -60,29 +58,21 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( nsCOMPtr pbackground = NS_GetCurrentThread(); - nsCOMPtr target = - do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID); - MOZ_ASSERT(target); - - nsCString name("OPFS "); - name += origin; - RefPtr taskqueue = - TaskQueue::Create(target.forget(), PromiseFlatCString(name).get()); // We'll have to thread-hop back to this thread to respond. We could // just have the create be one-way, then send the actual request on the // new channel, but that's an extra IPC instead. - InvokeAsync( - taskqueue, __func__, - [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, - dataManager = std::move(dataManager), taskqueue, pbackground]() mutable { - RefPtr parent = new FileSystemManagerParent( - taskqueue, std::move(dataManager), rootId); - if (!parentEp.Bind(parent)) { - return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); - } - // Send response back to pbackground to send to child - return BoolPromise::CreateAndResolve(true, __func__); - }) + InvokeAsync(dataManager->MutableIOTargetPtr(), __func__, + [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, + dataManager = dataManager, pbackground]() mutable { + RefPtr parent = + new FileSystemManagerParent(std::move(dataManager), rootId); + if (!parentEp.Bind(parent)) { + return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, + __func__); + } + // Send response back to pbackground to send to child + return BoolPromise::CreateAndResolve(true, __func__); + }) ->Then(GetCurrentSerialEventTarget(), __func__, [aResolver](const BoolPromise::ResolveOrRejectValue& aValue) { if (aValue.IsReject()) { diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 0aeb4b5c270b3..30b995e5d5c27 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -8,8 +8,12 @@ #include "mozilla/Result.h" #include "mozilla/StaticPtr.h" +#include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/ResultExtensions.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" +#include "nsNetCID.h" +#include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" namespace mozilla::dom::fs::data { @@ -78,11 +82,15 @@ void RemoveFileSystemDataManager(const Origin& aOrigin) { } // namespace -FileSystemDataManager::FileSystemDataManager(const Origin& aOrigin) +FileSystemDataManager::FileSystemDataManager( + const Origin& aOrigin, MovingNotNull> aIOTaskQueue) : mOrigin(aOrigin), - mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())) {} + mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())), + mIOTaskQueue(std::move(aIOTaskQueue)) {} FileSystemDataManager::~FileSystemDataManager() { + mIOTaskQueue->BeginShutdown(); + RemoveFileSystemDataManager(mOrigin); } @@ -93,7 +101,18 @@ FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { return dataManager; } - auto dataManager = MakeRefPtr(aOrigin); + QM_TRY_UNWRAP(auto streamTransportService, + MOZ_TO_RESULT_GET_TYPED(nsCOMPtr, + MOZ_SELECT_OVERLOAD(do_GetService), + NS_STREAMTRANSPORTSERVICE_CONTRACTID)); + + nsCString taskQueueName("OPFS "_ns + aOrigin); + + RefPtr ioTaskQueue = + TaskQueue::Create(streamTransportService.forget(), taskQueueName.get()); + + auto dataManager = MakeRefPtr( + aOrigin, WrapMovingNotNull(ioTaskQueue)); AddFileSystemDataManager(aOrigin, dataManager); diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index 6e6f10b582515..db61d599f481c 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -8,6 +8,7 @@ #define DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ #include "mozilla/NotNull.h" +#include "mozilla/TaskQueue.h" #include "mozilla/dom/FileSystemTypes.h" #include "mozilla/dom/quota/CheckedUnsafePtr.h" #include "nsCOMPtr.h" @@ -24,7 +25,8 @@ namespace dom::fs::data { class FileSystemDataManager : public SupportsCheckedUnsafePtr> { public: - explicit FileSystemDataManager(const Origin& aOrigin); + FileSystemDataManager(const Origin& aOrigin, + MovingNotNull> aIOTaskQueue); using result_t = Result, nsresult>; static FileSystemDataManager::result_t GetOrCreateFileSystemDataManager( @@ -36,11 +38,16 @@ class FileSystemDataManager return mBackgroundTarget.get(); } + nsISerialEventTarget* MutableIOTargetPtr() const { + return mIOTaskQueue.get(); + } + protected: ~FileSystemDataManager(); const Origin mOrigin; const NotNull> mBackgroundTarget; + const NotNull> mIOTaskQueue; }; } // namespace dom::fs::data From 21d24415eb0f59100521de1e218c79d87dc8a71f Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:56 +0000 Subject: [PATCH 20/65] Bug 1786465 - Removed unused __delete__ message from PFileSystemManager protocol; r=dom-storage-reviewers,jesup The message has no use in top level protocols. Differential Revision: https://phabricator.services.mozilla.com/D155555 --- dom/fs/shared/PFileSystemManager.ipdl | 7 ------- 1 file changed, 7 deletions(-) diff --git a/dom/fs/shared/PFileSystemManager.ipdl b/dom/fs/shared/PFileSystemManager.ipdl index 52e371fbf6c5f..514cf31406b86 100644 --- a/dom/fs/shared/PFileSystemManager.ipdl +++ b/dom/fs/shared/PFileSystemManager.ipdl @@ -325,13 +325,6 @@ async protocol PFileSystemManager */ async NeedQuota(FileSystemQuotaRequest aRequest) returns (uint64_t aQuotaGranted); - - child: - - /** - * Mandatory IPC managed lifecycle request. - */ - async __delete__(); }; } // namespace dom From 5bd17a34d8e2d8dd04089418d445535324107bb5 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:57 +0000 Subject: [PATCH 21/65] Bug 1786465 - Clean up CreateFileSystemManagerParent; r=dom-storage-reviewers,jesup This patch mostly removes: - some unused lambda captures - redundant variables like `pbackground` - named lambda functions which are used only once - obsolete comments Differential Revision: https://phabricator.services.mozilla.com/D155556 --- .../parent/FileSystemManagerParentFactory.cpp | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index 3b12f11b0fa55..daced0d2f0baf 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -46,31 +46,24 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( nsAutoCString origin = quota::QuotaManager::GetOriginFromValidatedPrincipalInfo(aPrincipalInfo); - auto sendBackError = [aResolver](const auto& aRv) { aResolver(aRv); }; - - fs::EntryId rootId = fs::data::GetRootHandle(origin); - - // This opens the quota manager, which has to be done on PBackground + // This creates the file system data manager, which has to be done on + // PBackground QM_TRY_UNWRAP( RefPtr dataManager, fs::data::FileSystemDataManager::GetOrCreateFileSystemDataManager(origin), - IPC_OK(), sendBackError); + IPC_OK(), [aResolver](const auto& aRv) { aResolver(aRv); }); - nsCOMPtr pbackground = NS_GetCurrentThread(); + fs::EntryId rootId = fs::data::GetRootHandle(origin); - // We'll have to thread-hop back to this thread to respond. We could - // just have the create be one-way, then send the actual request on the - // new channel, but that's an extra IPC instead. InvokeAsync(dataManager->MutableIOTargetPtr(), __func__, - [origin, parentEp = std::move(aParentEndpoint), aResolver, rootId, - dataManager = dataManager, pbackground]() mutable { + [dataManager = dataManager, rootId, + parentEndpoint = std::move(aParentEndpoint)]() mutable { RefPtr parent = new FileSystemManagerParent(std::move(dataManager), rootId); - if (!parentEp.Bind(parent)) { + if (!parentEndpoint.Bind(parent)) { return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, __func__); } - // Send response back to pbackground to send to child return BoolPromise::CreateAndResolve(true, __func__); }) ->Then(GetCurrentSerialEventTarget(), __func__, From cda54f0226ca0cb49adcd6bc1f6ba36fd9178914 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:57 +0000 Subject: [PATCH 22/65] Bug 1786465 - Introduce Registered class template; r=dom-storage-reviewers,jesup See the documentation in FileSystemHelpers.h Differential Revision: https://phabricator.services.mozilla.com/D155785 --- dom/fs/shared/FileSystemHelpers.h | 139 ++++++++++++++ dom/fs/shared/moz.build | 1 + dom/fs/test/gtest/moz.build | 1 + .../gtest/shared/TestFileSystemHelpers.cpp | 179 ++++++++++++++++++ dom/fs/test/gtest/shared/moz.build | 16 ++ 5 files changed, 336 insertions(+) create mode 100644 dom/fs/shared/FileSystemHelpers.h create mode 100644 dom/fs/test/gtest/shared/TestFileSystemHelpers.cpp create mode 100644 dom/fs/test/gtest/shared/moz.build diff --git a/dom/fs/shared/FileSystemHelpers.h b/dom/fs/shared/FileSystemHelpers.h new file mode 100644 index 0000000000000..d83fdd4599a1e --- /dev/null +++ b/dom/fs/shared/FileSystemHelpers.h @@ -0,0 +1,139 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_FS_SHARED_FILESYSTEMHELPERS_H_ +#define DOM_FS_SHARED_FILESYSTEMHELPERS_H_ + +namespace mozilla::dom::fs { + +// XXX Consider moving this class template to MFBT. + +// A wrapper class template on top of the RefPtr. The RefPtr provides us the +// automatic reference counting of objects with AddRef() and Release() methods. +// `Registered` provides automatic registration counting of objects with +// Register() and Unregister() methods. Registration counting works similarly +// as reference counting, but objects are not deleted when the number of +// registrations drops to zero (that's managed by reference counting). Instead, +// an object can trigger an asynchronous close operation which still needs to +// hold and use the referenced object. Example: +// +// using BoolPromise = MozPromise; +// +// class MyObject { +// public: +// NS_INLINE_DECL_REFCOUNTING(MyObject) +// +// void Register() { +// mRegCnt++; +// } +// +// void Unregister() { +// mRegCnt--; +// if (mRegCnt == 0) { +// BeginClose(); +// } +// } +// +// private: +// RefPtr BeginClose() { +// return InvokeAsync(mIOTaskQueue, __func__, +// []() { +// return BoolPromise::CreateAndResolve(true, __func__); +// }) +// ->Then(GetCurrentSerialEventTarget(), __func__, +// [self = RefPtr(this)]( +// const BoolPromise::ResolveOrRejectValue&) { +// return self->mIOTaskQueue->BeginShutdown(); +// }) +// ->Then(GetCurrentSerialEventTarget(), __func__, +// [self = RefPtr(this)]( +// const ShutdownPromise::ResolveOrRejectValue&) { +// return BoolPromise::CreateAndResolve(true, __func__); +// }); +// } +// +// RefPtr mIOTaskQueue; +// uint32_t mRegCnt = 0; +// }; + +template +class Registered { + private: + RefPtr mObject; + + public: + ~Registered() { + if (mObject) { + mObject->Unregister(); + } + } + + Registered() {} + + Registered(const Registered& aOther) : mObject(aOther.mObject) { + mObject->Register(); + } + + Registered(Registered&& aOther) = default; + + MOZ_IMPLICIT Registered(RefPtr aObject) : mObject(std::move(aObject)) { + if (mObject) { + mObject->Register(); + } + } + + Registered& operator=(decltype(nullptr)) { + RefPtr oldObject = std::move(mObject); + mObject = nullptr; + if (oldObject) { + oldObject->Unregister(); + } + return *this; + } + + Registered& operator=(const Registered& aRhs) { + if (aRhs.mObject) { + aRhs.mObject->Register(); + } + RefPtr oldObject = std::move(mObject); + mObject = aRhs.mObject; + if (oldObject) { + oldObject->Unregister(); + } + return *this; + } + + Registered& operator=(Registered&& aRhs) { + RefPtr oldObject = std::move(mObject); + mObject = std::move(aRhs.mObject); + aRhs.mObject = nullptr; + if (oldObject) { + oldObject->Unregister(); + } + return *this; + } + + const RefPtr& inspect() const { return mObject; } + + RefPtr unwrap() { + RefPtr oldObject = std::move(mObject); + mObject = nullptr; + if (oldObject) { + oldObject->Unregister(); + } + return std::move(oldObject); + } + + T* get() const { return mObject; } + + operator T*() const& { return get(); } + + T* operator->() const { return get(); } +}; + +} // namespace mozilla::dom::fs + +#endif // DOM_FS_SHARED_FILESYSTEMHELPERS_H_ diff --git a/dom/fs/shared/moz.build b/dom/fs/shared/moz.build index 364b90904efe1..a67168f69af28 100644 --- a/dom/fs/shared/moz.build +++ b/dom/fs/shared/moz.build @@ -5,6 +5,7 @@ # file, You can obtain one at http://mozilla.org/MPL/2.0/. EXPORTS.mozilla.dom += [ + "FileSystemHelpers.h", "FileSystemTypes.h", ] diff --git a/dom/fs/test/gtest/moz.build b/dom/fs/test/gtest/moz.build index 93f6c30d4aaf7..ab32cfccba4c9 100644 --- a/dom/fs/test/gtest/moz.build +++ b/dom/fs/test/gtest/moz.build @@ -8,6 +8,7 @@ TEST_DIRS += [ "api", "child", "parent", + "shared", ] UNIFIED_SOURCES = [ diff --git a/dom/fs/test/gtest/shared/TestFileSystemHelpers.cpp b/dom/fs/test/gtest/shared/TestFileSystemHelpers.cpp new file mode 100644 index 0000000000000..ed380266347cd --- /dev/null +++ b/dom/fs/test/gtest/shared/TestFileSystemHelpers.cpp @@ -0,0 +1,179 @@ +/* -*- 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 "gtest/gtest.h" +#include "mozilla/RefPtr.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/FileSystemHelpers.h" + +namespace mozilla::dom::fs { + +namespace { + +class TestObject { + public: + explicit TestObject(uint32_t aExpectedAddRefCnt = 0, + uint32_t aExpectedAddRegCnt = 0) + : mExpectedAddRefCnt(aExpectedAddRefCnt), + mExpectedAddRegCnt(aExpectedAddRegCnt), + mAddRefCnt(0), + mAddRegCnt(0), + mRefCnt(0), + mRegCnt(0), + mClosed(false) {} + + uint32_t AddRef() { + mRefCnt++; + mAddRefCnt++; + return mRefCnt; + } + + uint32_t Release() { + EXPECT_TRUE(mRefCnt > 0); + mRefCnt--; + if (mRefCnt == 0) { + delete this; + return 0; + } + return mRefCnt; + } + + void Register() { + EXPECT_FALSE(mClosed); + mRegCnt++; + mAddRegCnt++; + } + + void Unregister() { + EXPECT_FALSE(mClosed); + EXPECT_TRUE(mRegCnt > 0); + mRegCnt--; + if (mRegCnt == 0) { + mClosed = true; + } + } + + void Foo() const {} + + private: + ~TestObject() { + if (mExpectedAddRefCnt > 0) { + EXPECT_EQ(mAddRefCnt, mExpectedAddRefCnt); + } + if (mExpectedAddRegCnt > 0) { + EXPECT_EQ(mAddRegCnt, mExpectedAddRegCnt); + } + } + + uint32_t mExpectedAddRefCnt; + uint32_t mExpectedAddRegCnt; + uint32_t mAddRefCnt; + uint32_t mAddRegCnt; + uint32_t mRefCnt; + uint32_t mRegCnt; + bool mClosed; +}; + +} // namespace + +TEST(TestFileSystemHelpers_Registered, Construct_Default) +{ + { Registered testObject; } +} + +TEST(TestFileSystemHelpers_Registered, Construct_Copy) +{ + { + Registered testObject1(MakeRefPtr(2, 2)); + Registered testObject2(testObject1); + testObject2 = nullptr; + } +} + +TEST(TestFileSystemHelpers_Registered, Construct_Move) +{ + { + Registered testObject1(MakeRefPtr(1, 1)); + Registered testObject2(std::move(testObject1)); + } +} + +TEST(TestFileSystemHelpers_Registered, Construct_FromRefPtr) +{ + { Registered testObject(MakeRefPtr(1, 1)); } +} + +TEST(TestFileSystemHelpers_Registered, Operator_Assign_FromNullPtr) +{ + { + Registered testObject; + testObject = nullptr; + } +} + +TEST(TestFileSystemHelpers_Registered, Operator_Assign_Copy) +{ + { + Registered testObject1(MakeRefPtr(2, 2)); + Registered testObject2; + testObject2 = testObject1; + } +} + +TEST(TestFileSystemHelpers_Registered, Operator_Assign_Move) +{ + { + Registered testObject1(MakeRefPtr(1, 1)); + Registered testObject2; + testObject2 = std::move(testObject1); + } +} + +TEST(TestFileSystemHelpers_Registered, Method_Inspect) +{ + { + Registered testObject1(MakeRefPtr(1, 1)); + const RefPtr& testObject2 = testObject1.inspect(); + Unused << testObject2; + } +} + +TEST(TestFileSystemHelpers_Registered, Method_Unwrap) +{ + { + Registered testObject1(MakeRefPtr(1, 1)); + RefPtr testObject2 = testObject1.unwrap(); + Unused << testObject2; + } +} + +TEST(TestFileSystemHelpers_Registered, Method_Get) +{ + { + Registered testObject1(MakeRefPtr(1, 1)); + TestObject* testObject2 = testObject1.get(); + Unused << testObject2; + } +} + +TEST(TestFileSystemHelpers_Registered, Operator_Conversion_ToRawPtr) +{ + { + Registered testObject1(MakeRefPtr(1, 1)); + TestObject* testObject2 = testObject1; + Unused << testObject2; + } +} + +TEST(TestFileSystemHelpers_Registered, Operator_Dereference_ToRawPtr) +{ + { + Registered testObject(MakeRefPtr(1, 1)); + testObject->Foo(); + } +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/test/gtest/shared/moz.build b/dom/fs/test/gtest/shared/moz.build new file mode 100644 index 0000000000000..e8feae8c57da4 --- /dev/null +++ b/dom/fs/test/gtest/shared/moz.build @@ -0,0 +1,16 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES = [ + "TestFileSystemHelpers.cpp", +] + +FINAL_LIBRARY = "xul-gtest" + +LOCAL_INCLUDES += [ + "/dom/fs/shared", + "/dom/fs/test/gtest", +] From c71997db22df908340906b7d8942a86038f9aaee Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:57 +0000 Subject: [PATCH 23/65] Bug 1786465 - Implement registration counting of FileSystemDataManager; r=dom-storage-reviewers,jesup FileSystemDataManager can be now closed when it's still alive (not in the destructor). Differential Revision: https://phabricator.services.mozilla.com/D155786 --- dom/fs/parent/FileSystemManagerParent.cpp | 36 ++++++++++-- dom/fs/parent/FileSystemManagerParent.h | 11 +++- .../parent/FileSystemManagerParentFactory.cpp | 39 ++++++++----- .../datamodel/FileSystemDataManager.cpp | 57 +++++++++++++++++-- .../parent/datamodel/FileSystemDataManager.h | 30 +++++++++- 5 files changed, 144 insertions(+), 29 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParent.cpp b/dom/fs/parent/FileSystemManagerParent.cpp index 7b13cf4b6bc5a..c588c5a47446e 100644 --- a/dom/fs/parent/FileSystemManagerParent.cpp +++ b/dom/fs/parent/FileSystemManagerParent.cpp @@ -8,6 +8,7 @@ #include "nsNetCID.h" #include "mozilla/dom/FileSystemDataManager.h" #include "mozilla/dom/FileSystemTypes.h" +#include "mozilla/dom/quota/ForwardDecls.h" #include "mozilla/ipc/Endpoint.h" using IPCResult = mozilla::ipc::IPCResult; @@ -28,6 +29,10 @@ FileSystemManagerParent::FileSystemManagerParent( const EntryId& aRootEntry) : mDataManager(std::move(aDataManager)), mRootEntry(aRootEntry) {} +FileSystemManagerParent::~FileSystemManagerParent() { + LOG(("Destroying FileSystemManagerParent %p", this)); +} + IPCResult FileSystemManagerParent::RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver) { FileSystemGetHandleResponse response(mRootEntry); @@ -117,14 +122,33 @@ IPCResult FileSystemManagerParent::RecvNeedQuota( return IPC_OK(); } -FileSystemManagerParent::~FileSystemManagerParent() { - LOG(("Destroying FileSystemManagerParent %p", this)); +void FileSystemManagerParent::OnChannelClose() { + if (!mAllowedToClose) { + AllowToClose(); + } + + PFileSystemManagerParent::OnChannelClose(); +} + +void FileSystemManagerParent::OnChannelError() { + if (!mAllowedToClose) { + AllowToClose(); + } + + PFileSystemManagerParent::OnChannelError(); +} + +void FileSystemManagerParent::AllowToClose() { + mAllowedToClose.Flip(); + + InvokeAsync(mDataManager->MutableBackgroundTargetPtr(), __func__, + [self = RefPtr(this)]() { + self->mDataManager->UnregisterActor(WrapNotNull(self)); - nsCOMPtr target = - mDataManager->MutableBackgroundTargetPtr(); + self->mDataManager = nullptr; - NS_ProxyRelease("ReleaseFileSystemDataManager", target, - mDataManager.forget()); + return BoolPromise::CreateAndResolve(true, __func__); + }); } } // namespace mozilla::dom diff --git a/dom/fs/parent/FileSystemManagerParent.h b/dom/fs/parent/FileSystemManagerParent.h index 6f82261998744..1076916a8d6d8 100644 --- a/dom/fs/parent/FileSystemManagerParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -8,6 +8,7 @@ #define DOM_FS_PARENT_FILESYSTEMMANAGERPARENT_H_ #include "ErrorList.h" +#include "mozilla/dom/FlippedOnce.h" #include "mozilla/dom/PFileSystemManagerParent.h" #include "nsISupports.h" @@ -31,6 +32,8 @@ class FileSystemManagerParent : public PFileSystemManagerParent { FileSystemManagerParent(RefPtr aDataManager, const EntryId& aRootEntry); + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemManagerParent, override) + mozilla::ipc::IPCResult RecvGetRootHandleMsg( GetRootHandleMsgResolver&& aResolver); @@ -67,15 +70,21 @@ class FileSystemManagerParent : public PFileSystemManagerParent { mozilla::ipc::IPCResult RecvNeedQuota(FileSystemQuotaRequest&& aRequest, NeedQuotaResolver&& aResolver); - NS_INLINE_DECL_THREADSAFE_REFCOUNTING(FileSystemManagerParent) + void OnChannelClose() override; + + void OnChannelError() override; protected: virtual ~FileSystemManagerParent(); + void AllowToClose(); + private: RefPtr mDataManager; const EntryId mRootEntry; + + FlippedOnce mAllowedToClose; }; } // namespace mozilla::dom diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index daced0d2f0baf..23f58f8836c43 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -37,6 +37,9 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( const mozilla::ipc::PrincipalInfo& aPrincipalInfo, mozilla::ipc::Endpoint&& aParentEndpoint, std::function&& aResolver) { + using CreateActorPromise = + MozPromise, nsresult, true>; + QM_TRY(OkIf(StaticPrefs::dom_fs_enabled()), IPC_OK(), [aResolver](const auto&) { aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR); }); @@ -49,28 +52,38 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( // This creates the file system data manager, which has to be done on // PBackground QM_TRY_UNWRAP( - RefPtr dataManager, + fs::Registered dataManager, fs::data::FileSystemDataManager::GetOrCreateFileSystemDataManager(origin), IPC_OK(), [aResolver](const auto& aRv) { aResolver(aRv); }); fs::EntryId rootId = fs::data::GetRootHandle(origin); - InvokeAsync(dataManager->MutableIOTargetPtr(), __func__, - [dataManager = dataManager, rootId, - parentEndpoint = std::move(aParentEndpoint)]() mutable { - RefPtr parent = - new FileSystemManagerParent(std::move(dataManager), rootId); - if (!parentEndpoint.Bind(parent)) { - return BoolPromise::CreateAndReject(NS_ERROR_FAILURE, - __func__); - } - return BoolPromise::CreateAndResolve(true, __func__); - }) + InvokeAsync( + dataManager->MutableIOTargetPtr(), __func__, + [dataManager = RefPtr(dataManager), + rootId, parentEndpoint = std::move(aParentEndpoint)]() mutable { + RefPtr parent = + new FileSystemManagerParent(std::move(dataManager), rootId); + + if (!parentEndpoint.Bind(parent)) { + return CreateActorPromise::CreateAndReject(NS_ERROR_FAILURE, + __func__); + } + + return CreateActorPromise::CreateAndResolve(std::move(parent), + __func__); + }) ->Then(GetCurrentSerialEventTarget(), __func__, - [aResolver](const BoolPromise::ResolveOrRejectValue& aValue) { + [dataManager = dataManager, + aResolver](CreateActorPromise::ResolveOrRejectValue&& aValue) { if (aValue.IsReject()) { aResolver(aValue.RejectValue()); } else { + RefPtr parent = + std::move(aValue.ResolveValue()); + + dataManager->RegisterActor(WrapNotNull(parent)); + aResolver(NS_OK); } }); diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 30b995e5d5c27..78510775131ce 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -86,19 +86,19 @@ FileSystemDataManager::FileSystemDataManager( const Origin& aOrigin, MovingNotNull> aIOTaskQueue) : mOrigin(aOrigin), mBackgroundTarget(WrapNotNull(GetCurrentSerialEventTarget())), - mIOTaskQueue(std::move(aIOTaskQueue)) {} + mIOTaskQueue(std::move(aIOTaskQueue)), + mRegCount(0), + mState(State::Initial) {} FileSystemDataManager::~FileSystemDataManager() { - mIOTaskQueue->BeginShutdown(); - - RemoveFileSystemDataManager(mOrigin); + MOZ_ASSERT(mState == State::Closed); } FileSystemDataManager::result_t FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { if (RefPtr dataManager = GetFileSystemDataManager(aOrigin)) { - return dataManager; + return Registered(std::move(dataManager)); } QM_TRY_UNWRAP(auto streamTransportService, @@ -116,7 +116,52 @@ FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { AddFileSystemDataManager(aOrigin, dataManager); - return dataManager; + return Registered(std::move(dataManager)); +} + +void FileSystemDataManager::Register() { mRegCount++; } + +void FileSystemDataManager::Unregister() { + MOZ_ASSERT(mRegCount > 0); + + mRegCount--; + + if (IsInactive()) { + Close(); + } +} + +void FileSystemDataManager::RegisterActor( + NotNull aActor) { + MOZ_ASSERT(!mActors.Contains(aActor)); + + mActors.Insert(aActor); +} + +void FileSystemDataManager::UnregisterActor( + NotNull aActor) { + MOZ_ASSERT(mActors.Contains(aActor)); + + mActors.Remove(aActor); + + if (IsInactive()) { + Close(); + } +} + +bool FileSystemDataManager::IsInactive() const { + return !mRegCount && !mActors.Count(); +} + +void FileSystemDataManager::Close() { + MOZ_ASSERT(mState == State::Initial); + MOZ_ASSERT(IsInactive()); + + mState = State::Closed; + + mIOTaskQueue->BeginShutdown(); + + RemoveFileSystemDataManager(mOrigin); } } // namespace mozilla::dom::fs::data diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index db61d599f481c..5d72ae9f300c5 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -10,25 +10,33 @@ #include "mozilla/NotNull.h" #include "mozilla/TaskQueue.h" #include "mozilla/dom/FileSystemTypes.h" +#include "mozilla/dom/FileSystemHelpers.h" #include "mozilla/dom/quota/CheckedUnsafePtr.h" #include "nsCOMPtr.h" #include "nsISupportsUtils.h" #include "nsString.h" +#include "nsTHashSet.h" namespace mozilla { template class Result; -namespace dom::fs::data { +namespace dom { + +class FileSystemManagerParent; + +namespace fs::data { class FileSystemDataManager : public SupportsCheckedUnsafePtr> { public: + enum struct State : uint8_t { Initial = 0, Closed }; + FileSystemDataManager(const Origin& aOrigin, MovingNotNull> aIOTaskQueue); - using result_t = Result, nsresult>; + using result_t = Result, nsresult>; static FileSystemDataManager::result_t GetOrCreateFileSystemDataManager( const Origin& aOrigin); @@ -42,15 +50,31 @@ class FileSystemDataManager return mIOTaskQueue.get(); } + void Register(); + + void Unregister(); + + void RegisterActor(NotNull aActor); + + void UnregisterActor(NotNull aActor); + protected: ~FileSystemDataManager(); + bool IsInactive() const; + + void Close(); + + nsTHashSet mActors; const Origin mOrigin; const NotNull> mBackgroundTarget; const NotNull> mIOTaskQueue; + uint32_t mRegCount; + State mState; }; -} // namespace dom::fs::data +} // namespace fs::data +} // namespace dom } // namespace mozilla #endif // DOM_FS_PARENT_DATAMODEL_FILESYSTEMDATAMANAGER_H_ From b4bb12b466270d51595a23a9296aa80ce148f164 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:58 +0000 Subject: [PATCH 24/65] Bug 1786465 - Add support for asynchronous closing of FileSystemDataManager; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155787 --- .../datamodel/FileSystemDataManager.cpp | 35 ++++++++++++++--- .../parent/datamodel/FileSystemDataManager.h | 8 +++- .../datamodel/TestFileSystemDataManager.cpp | 39 +++++++++++++++++++ dom/fs/test/gtest/parent/datamodel/moz.build | 18 +++++++++ dom/fs/test/gtest/parent/moz.build | 2 + 5 files changed, 94 insertions(+), 8 deletions(-) create mode 100644 dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp create mode 100644 dom/fs/test/gtest/parent/datamodel/moz.build diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 78510775131ce..30b42b98ae16d 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -98,6 +98,8 @@ FileSystemDataManager::result_t FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { if (RefPtr dataManager = GetFileSystemDataManager(aOrigin)) { + // XXX Handle the case when the manager is asynchronouslly closing! + return Registered(std::move(dataManager)); } @@ -127,7 +129,7 @@ void FileSystemDataManager::Unregister() { mRegCount--; if (IsInactive()) { - Close(); + BeginClose(); } } @@ -145,23 +147,44 @@ void FileSystemDataManager::UnregisterActor( mActors.Remove(aActor); if (IsInactive()) { - Close(); + BeginClose(); } } +RefPtr FileSystemDataManager::OnClose() { + MOZ_ASSERT(mState == State::Closing); + + return mClosePromiseHolder.Ensure(__func__); +} + bool FileSystemDataManager::IsInactive() const { return !mRegCount && !mActors.Count(); } -void FileSystemDataManager::Close() { +RefPtr FileSystemDataManager::BeginClose() { MOZ_ASSERT(mState == State::Initial); MOZ_ASSERT(IsInactive()); - mState = State::Closed; + mState = State::Closing; + + InvokeAsync(MutableIOTargetPtr(), __func__, + []() { return BoolPromise::CreateAndResolve(true, __func__); }) + ->Then(MutableBackgroundTargetPtr(), __func__, + [self = RefPtr(this)]( + const BoolPromise::ResolveOrRejectValue&) { + return self->mIOTaskQueue->BeginShutdown(); + }) + ->Then(MutableBackgroundTargetPtr(), __func__, + [self = RefPtr(this)]( + const ShutdownPromise::ResolveOrRejectValue&) { + RemoveFileSystemDataManager(self->mOrigin); + + self->mState = State::Closed; - mIOTaskQueue->BeginShutdown(); + self->mClosePromiseHolder.ResolveIfExists(true, __func__); + }); - RemoveFileSystemDataManager(mOrigin); + return OnClose(); } } // namespace mozilla::dom::fs::data diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index 5d72ae9f300c5..2ddc424cdff93 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -12,6 +12,7 @@ #include "mozilla/dom/FileSystemTypes.h" #include "mozilla/dom/FileSystemHelpers.h" #include "mozilla/dom/quota/CheckedUnsafePtr.h" +#include "mozilla/dom/quota/ForwardDecls.h" #include "nsCOMPtr.h" #include "nsISupportsUtils.h" #include "nsString.h" @@ -31,7 +32,7 @@ namespace fs::data { class FileSystemDataManager : public SupportsCheckedUnsafePtr> { public: - enum struct State : uint8_t { Initial = 0, Closed }; + enum struct State : uint8_t { Initial = 0, Closing, Closed }; FileSystemDataManager(const Origin& aOrigin, MovingNotNull> aIOTaskQueue); @@ -58,17 +59,20 @@ class FileSystemDataManager void UnregisterActor(NotNull aActor); + RefPtr OnClose(); + protected: ~FileSystemDataManager(); bool IsInactive() const; - void Close(); + RefPtr BeginClose(); nsTHashSet mActors; const Origin mOrigin; const NotNull> mBackgroundTarget; const NotNull> mIOTaskQueue; + MozPromiseHolder mClosePromiseHolder; uint32_t mRegCount; State mState; }; diff --git a/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp b/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp new file mode 100644 index 0000000000000..23d6a80298136 --- /dev/null +++ b/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp @@ -0,0 +1,39 @@ +/* -*- 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 "FileSystemDataManager.h" +#include "gtest/gtest.h" +#include "mozilla/SpinEventLoopUntil.h" +#include "TestHelpers.h" + +namespace mozilla::dom::fs::test { + +namespace { + +constexpr auto kTestOriginName = "http:://example.com"_ns; + +} // namespace + +TEST(TestFileSystemDataManager, GetOrCreateFileSystemDataManager) +{ + TEST_TRY_UNWRAP(Registered registeredDataManager, + data::FileSystemDataManager::GetOrCreateFileSystemDataManager( + Origin(kTestOriginName))); + + RefPtr dataManager = registeredDataManager.get(); + + registeredDataManager = nullptr; + + bool done = false; + + dataManager->OnClose()->Then( + GetCurrentSerialEventTarget(), __func__, + [&done](const BoolPromise::ResolveOrRejectValue&) { done = true; }); + + SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; }); +} + +} // namespace mozilla::dom::fs::test diff --git a/dom/fs/test/gtest/parent/datamodel/moz.build b/dom/fs/test/gtest/parent/datamodel/moz.build new file mode 100644 index 0000000000000..f2b8f20fb9ada --- /dev/null +++ b/dom/fs/test/gtest/parent/datamodel/moz.build @@ -0,0 +1,18 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, you can obtain one at http://mozilla.org/MPL/2.0/. + +UNIFIED_SOURCES = [ + "TestFileSystemDataManager.cpp", +] + +include("/ipc/chromium/chromium-config.mozbuild") + +FINAL_LIBRARY = "xul-gtest" + +LOCAL_INCLUDES += [ + "/dom/fs/parent/datamodel", + "/dom/fs/test/gtest", +] diff --git a/dom/fs/test/gtest/parent/moz.build b/dom/fs/test/gtest/parent/moz.build index 9539adbe2919b..ab08656af8d8b 100644 --- a/dom/fs/test/gtest/parent/moz.build +++ b/dom/fs/test/gtest/parent/moz.build @@ -4,6 +4,8 @@ # 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/. +TEST_DIRS += ["datamodel"] + UNIFIED_SOURCES = [ "TestFileSystemHashSource.cpp", ] From ec7c60f5b32d1e839c96ee61e1192bb409ae210e Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:58 +0000 Subject: [PATCH 25/65] Bug 1786465 - Add support for asynchronous creation of FileSystemDataManager; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155788 --- .../parent/FileSystemManagerParentFactory.cpp | 77 ++++++++++--------- .../datamodel/FileSystemDataManager.cpp | 11 ++- .../parent/datamodel/FileSystemDataManager.h | 5 +- .../datamodel/TestFileSystemDataManager.cpp | 30 +++++--- 4 files changed, 69 insertions(+), 54 deletions(-) diff --git a/dom/fs/parent/FileSystemManagerParentFactory.cpp b/dom/fs/parent/FileSystemManagerParentFactory.cpp index 23f58f8836c43..6bfd32444c4da 100644 --- a/dom/fs/parent/FileSystemManagerParentFactory.cpp +++ b/dom/fs/parent/FileSystemManagerParentFactory.cpp @@ -51,42 +51,47 @@ mozilla::ipc::IPCResult CreateFileSystemManagerParent( // This creates the file system data manager, which has to be done on // PBackground - QM_TRY_UNWRAP( - fs::Registered dataManager, - fs::data::FileSystemDataManager::GetOrCreateFileSystemDataManager(origin), - IPC_OK(), [aResolver](const auto& aRv) { aResolver(aRv); }); - - fs::EntryId rootId = fs::data::GetRootHandle(origin); - - InvokeAsync( - dataManager->MutableIOTargetPtr(), __func__, - [dataManager = RefPtr(dataManager), - rootId, parentEndpoint = std::move(aParentEndpoint)]() mutable { - RefPtr parent = - new FileSystemManagerParent(std::move(dataManager), rootId); - - if (!parentEndpoint.Bind(parent)) { - return CreateActorPromise::CreateAndReject(NS_ERROR_FAILURE, - __func__); - } - - return CreateActorPromise::CreateAndResolve(std::move(parent), - __func__); - }) - ->Then(GetCurrentSerialEventTarget(), __func__, - [dataManager = dataManager, - aResolver](CreateActorPromise::ResolveOrRejectValue&& aValue) { - if (aValue.IsReject()) { - aResolver(aValue.RejectValue()); - } else { - RefPtr parent = - std::move(aValue.ResolveValue()); - - dataManager->RegisterActor(WrapNotNull(parent)); - - aResolver(NS_OK); - } - }); + fs::data::FileSystemDataManager::GetOrCreateFileSystemDataManager(origin) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [origin, parentEndpoint = std::move(aParentEndpoint), + aResolver](const fs::Registered& + dataManager) mutable { + fs::EntryId rootId = fs::data::GetRootHandle(origin); + + InvokeAsync( + dataManager->MutableIOTargetPtr(), __func__, + [dataManager = + RefPtr(dataManager), + rootId, parentEndpoint = std::move(parentEndpoint)]() mutable { + RefPtr parent = + new FileSystemManagerParent(std::move(dataManager), + rootId); + + if (!parentEndpoint.Bind(parent)) { + return CreateActorPromise::CreateAndReject(NS_ERROR_FAILURE, + __func__); + } + + return CreateActorPromise::CreateAndResolve(std::move(parent), + __func__); + }) + ->Then(GetCurrentSerialEventTarget(), __func__, + [dataManager = dataManager, aResolver]( + CreateActorPromise::ResolveOrRejectValue&& aValue) { + if (aValue.IsReject()) { + aResolver(aValue.RejectValue()); + } else { + RefPtr parent = + std::move(aValue.ResolveValue()); + + dataManager->RegisterActor(WrapNotNull(parent)); + + aResolver(NS_OK); + } + }); + }, + [aResolver](nsresult aRejectValue) { aResolver(aRejectValue); }); return IPC_OK(); } diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 30b42b98ae16d..77ecbff836fea 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -94,19 +94,21 @@ FileSystemDataManager::~FileSystemDataManager() { MOZ_ASSERT(mState == State::Closed); } -FileSystemDataManager::result_t +RefPtr FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { if (RefPtr dataManager = GetFileSystemDataManager(aOrigin)) { // XXX Handle the case when the manager is asynchronouslly closing! - return Registered(std::move(dataManager)); + return CreatePromise::CreateAndResolve( + Registered(std::move(dataManager)), __func__); } QM_TRY_UNWRAP(auto streamTransportService, MOZ_TO_RESULT_GET_TYPED(nsCOMPtr, MOZ_SELECT_OVERLOAD(do_GetService), - NS_STREAMTRANSPORTSERVICE_CONTRACTID)); + NS_STREAMTRANSPORTSERVICE_CONTRACTID), + CreatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__)); nsCString taskQueueName("OPFS "_ns + aOrigin); @@ -118,7 +120,8 @@ FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { AddFileSystemDataManager(aOrigin, dataManager); - return Registered(std::move(dataManager)); + return CreatePromise::CreateAndResolve( + Registered(std::move(dataManager)), __func__); } void FileSystemDataManager::Register() { mRegCount++; } diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index 2ddc424cdff93..c1fa6dfc410b3 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -37,8 +37,9 @@ class FileSystemDataManager FileSystemDataManager(const Origin& aOrigin, MovingNotNull> aIOTaskQueue); - using result_t = Result, nsresult>; - static FileSystemDataManager::result_t GetOrCreateFileSystemDataManager( + using CreatePromise = MozPromise, nsresult, + /* IsExclusive */ true>; + static RefPtr GetOrCreateFileSystemDataManager( const Origin& aOrigin); NS_INLINE_DECL_REFCOUNTING(FileSystemDataManager) diff --git a/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp b/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp index 23d6a80298136..7c144a664242d 100644 --- a/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp +++ b/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp @@ -7,7 +7,6 @@ #include "FileSystemDataManager.h" #include "gtest/gtest.h" #include "mozilla/SpinEventLoopUntil.h" -#include "TestHelpers.h" namespace mozilla::dom::fs::test { @@ -19,19 +18,26 @@ constexpr auto kTestOriginName = "http:://example.com"_ns; TEST(TestFileSystemDataManager, GetOrCreateFileSystemDataManager) { - TEST_TRY_UNWRAP(Registered registeredDataManager, - data::FileSystemDataManager::GetOrCreateFileSystemDataManager( - Origin(kTestOriginName))); - - RefPtr dataManager = registeredDataManager.get(); - - registeredDataManager = nullptr; - bool done = false; - dataManager->OnClose()->Then( - GetCurrentSerialEventTarget(), __func__, - [&done](const BoolPromise::ResolveOrRejectValue&) { done = true; }); + data::FileSystemDataManager::GetOrCreateFileSystemDataManager( + Origin(kTestOriginName)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [](Registered registeredDataManager) { + RefPtr dataManager = + registeredDataManager.get(); + + registeredDataManager = nullptr; + + return dataManager->OnClose(); + }, + [](nsresult rejectValue) { + return BoolPromise::CreateAndReject(rejectValue, __func__); + }) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [&done](const BoolPromise::ResolveOrRejectValue&) { done = true; }); SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; }); } From 2579845fe323fbee823987303caffbae29c54a80 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:59 +0000 Subject: [PATCH 26/65] Bug 1786465 - Add support for asynchronous opening of FileSystemDataManager; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155789 --- .../datamodel/FileSystemDataManager.cpp | 53 +++++++++++++++++-- .../parent/datamodel/FileSystemDataManager.h | 7 ++- 2 files changed, 56 insertions(+), 4 deletions(-) diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 77ecbff836fea..20f1cfb37036d 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -13,6 +13,7 @@ #include "nsBaseHashtable.h" #include "nsHashKeys.h" #include "nsNetCID.h" +#include "nsProxyRelease.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" @@ -98,6 +99,8 @@ RefPtr FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { if (RefPtr dataManager = GetFileSystemDataManager(aOrigin)) { + // XXX Handle the case when the manager is asynchronouslly opening! + // XXX Handle the case when the manager is asynchronouslly closing! return CreatePromise::CreateAndResolve( @@ -120,8 +123,12 @@ FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { AddFileSystemDataManager(aOrigin, dataManager); - return CreatePromise::CreateAndResolve( - Registered(std::move(dataManager)), __func__); + return dataManager->BeginOpen()->Then( + GetCurrentSerialEventTarget(), __func__, + [dataManager = Registered(dataManager)]( + const BoolPromise::ResolveOrRejectValue&) { + return CreatePromise::CreateAndResolve(dataManager, __func__); + }); } void FileSystemDataManager::Register() { mRegCount++; } @@ -154,6 +161,12 @@ void FileSystemDataManager::UnregisterActor( } } +RefPtr FileSystemDataManager::OnOpen() { + MOZ_ASSERT(mState == State::Opening); + + return mOpenPromiseHolder.Ensure(__func__); +} + RefPtr FileSystemDataManager::OnClose() { MOZ_ASSERT(mState == State::Closing); @@ -164,8 +177,42 @@ bool FileSystemDataManager::IsInactive() const { return !mRegCount && !mActors.Count(); } -RefPtr FileSystemDataManager::BeginClose() { +RefPtr FileSystemDataManager::BeginOpen() { MOZ_ASSERT(mState == State::Initial); + + mState = State::Opening; + + InvokeAsync(MutableIOTargetPtr(), __func__, + [self = RefPtr(this)]() mutable { + nsCOMPtr target = + self->MutableBackgroundTargetPtr(); + + NS_ProxyRelease("ReleaseFileSystemDataManager", target, + self.forget()); + + return BoolPromise::CreateAndResolve(true, __func__); + }) + ->Then(GetCurrentSerialEventTarget(), __func__, + [self = RefPtr(this)]( + const BoolPromise::ResolveOrRejectValue& value) { + if (value.IsReject()) { + self->mState = State::Initial; + + self->mOpenPromiseHolder.RejectIfExists(value.RejectValue(), + __func__); + + } else { + self->mState = State::Open; + + self->mOpenPromiseHolder.ResolveIfExists(true, __func__); + } + }); + + return OnOpen(); +} + +RefPtr FileSystemDataManager::BeginClose() { + MOZ_ASSERT(mState == State::Open); MOZ_ASSERT(IsInactive()); mState = State::Closing; diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index c1fa6dfc410b3..706598b6e13ff 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -32,7 +32,7 @@ namespace fs::data { class FileSystemDataManager : public SupportsCheckedUnsafePtr> { public: - enum struct State : uint8_t { Initial = 0, Closing, Closed }; + enum struct State : uint8_t { Initial = 0, Opening, Open, Closing, Closed }; FileSystemDataManager(const Origin& aOrigin, MovingNotNull> aIOTaskQueue); @@ -60,6 +60,8 @@ class FileSystemDataManager void UnregisterActor(NotNull aActor); + RefPtr OnOpen(); + RefPtr OnClose(); protected: @@ -67,12 +69,15 @@ class FileSystemDataManager bool IsInactive() const; + RefPtr BeginOpen(); + RefPtr BeginClose(); nsTHashSet mActors; const Origin mOrigin; const NotNull> mBackgroundTarget; const NotNull> mIOTaskQueue; + MozPromiseHolder mOpenPromiseHolder; MozPromiseHolder mClosePromiseHolder; uint32_t mRegCount; State mState; From d314153bf65dbda77e3c65fd1baf64d8dc35b1ef Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:34:59 +0000 Subject: [PATCH 27/65] Bug 1786465 - Handle pending open/close operations in GetOrCreateFileSystemDataManager; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155790 --- .../datamodel/FileSystemDataManager.cpp | 27 +++- .../parent/datamodel/FileSystemDataManager.h | 6 + .../datamodel/TestFileSystemDataManager.cpp | 119 ++++++++++++++++++ 3 files changed, 150 insertions(+), 2 deletions(-) diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 20f1cfb37036d..29f385ee9cf57 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -99,9 +99,32 @@ RefPtr FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { if (RefPtr dataManager = GetFileSystemDataManager(aOrigin)) { - // XXX Handle the case when the manager is asynchronouslly opening! + if (dataManager->IsOpening()) { + // We have to wait for the open to be finished before resolving the + // promise. The manager can't close itself in the meantime because we + // add a new registration in the lambda capture list. + return dataManager->OnOpen()->Then( + GetCurrentSerialEventTarget(), __func__, + [dataManager = Registered(dataManager)]( + const BoolPromise::ResolveOrRejectValue&) { + return CreatePromise::CreateAndResolve(dataManager, __func__); + }); + } - // XXX Handle the case when the manager is asynchronouslly closing! + if (dataManager->IsClosing()) { + // First, we need to wait for the close to be finished. After that the + // manager is closed and it can't be opened again. The only option is + // to create a new manager and open it. However, all this stuff is + // asynchronous, so it can happen that something else called + // `GetOrCreateFileSystemManager` in the meantime. For that reason, we + // shouldn't try to create a new manager and open it here, a "recursive" + // call to `GetOrCreateFileSystemManager` will handle any new situation. + return dataManager->OnClose()->Then( + GetCurrentSerialEventTarget(), __func__, + [aOrigin](const BoolPromise::ResolveOrRejectValue&) { + return GetOrCreateFileSystemDataManager(aOrigin); + }); + } return CreatePromise::CreateAndResolve( Registered(std::move(dataManager)), __func__); diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index 706598b6e13ff..cd234f40c8877 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -60,6 +60,8 @@ class FileSystemDataManager void UnregisterActor(NotNull aActor); + bool IsOpen() const { return mState == State::Open; } + RefPtr OnOpen(); RefPtr OnClose(); @@ -69,6 +71,10 @@ class FileSystemDataManager bool IsInactive() const; + bool IsOpening() const { return mState == State::Opening; } + + bool IsClosing() const { return mState == State::Closing; } + RefPtr BeginOpen(); RefPtr BeginClose(); diff --git a/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp b/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp index 7c144a664242d..ceadef2ddce98 100644 --- a/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp +++ b/dom/fs/test/gtest/parent/datamodel/TestFileSystemDataManager.cpp @@ -42,4 +42,123 @@ TEST(TestFileSystemDataManager, GetOrCreateFileSystemDataManager) SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; }); } +TEST(TestFileSystemDataManager, GetOrCreateFileSystemDataManager_PendingOpen) +{ + Registered rdm1; + + Registered rdm2; + + { + bool done1 = false; + + data::FileSystemDataManager::GetOrCreateFileSystemDataManager( + Origin(kTestOriginName)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [&rdm1, &done1]( + Registered registeredDataManager) { + ASSERT_TRUE(registeredDataManager->IsOpen()); + + rdm1 = std::move(registeredDataManager); + + done1 = true; + }, + [&done1](nsresult rejectValue) { done1 = true; }); + + bool done2 = false; + + data::FileSystemDataManager::GetOrCreateFileSystemDataManager( + Origin(kTestOriginName)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [&rdm2, &done2]( + Registered registeredDataManager) { + ASSERT_TRUE(registeredDataManager->IsOpen()); + + rdm2 = std::move(registeredDataManager); + + done2 = true; + }, + [&done2](nsresult rejectValue) { done2 = true; }); + + SpinEventLoopUntil("Promise is fulfilled"_ns, + [&done1, &done2]() { return done1 && done2; }); + } + + RefPtr dm1 = rdm1.unwrap(); + + RefPtr dm2 = rdm2.unwrap(); + + { + bool done1 = false; + + dm1->OnClose()->Then( + GetCurrentSerialEventTarget(), __func__, + [&done1](const BoolPromise::ResolveOrRejectValue&) { done1 = true; }); + + bool done2 = false; + + dm2->OnClose()->Then( + GetCurrentSerialEventTarget(), __func__, + [&done2](const BoolPromise::ResolveOrRejectValue&) { done2 = true; }); + + SpinEventLoopUntil("Promise is fulfilled"_ns, + [&done1, &done2]() { return done1 && done2; }); + } +} + +TEST(TestFileSystemDataManager, GetOrCreateFileSystemDataManager_PendingClose) +{ + Registered rdm; + + { + bool done = false; + + data::FileSystemDataManager::GetOrCreateFileSystemDataManager( + Origin(kTestOriginName)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [&rdm, &done]( + Registered registeredDataManager) { + ASSERT_TRUE(registeredDataManager->IsOpen()); + + rdm = std::move(registeredDataManager); + + done = true; + }, + [&done](nsresult rejectValue) { done = true; }); + + SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; }); + } + + RefPtr dm = rdm.unwrap(); + + Unused << dm; + + { + bool done = false; + + data::FileSystemDataManager::GetOrCreateFileSystemDataManager( + Origin(kTestOriginName)) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [](Registered registeredDataManager) { + RefPtr dataManager = + registeredDataManager.get(); + + registeredDataManager = nullptr; + + return dataManager->OnClose(); + }, + [](nsresult rejectValue) { + return BoolPromise::CreateAndReject(rejectValue, __func__); + }) + ->Then( + GetCurrentSerialEventTarget(), __func__, + [&done](const BoolPromise::ResolveOrRejectValue&) { done = true; }); + + SpinEventLoopUntil("Promise is fulfilled"_ns, [&done]() { return done; }); + } +} + } // namespace mozilla::dom::fs::test From d2d118c60b2be018f697fdf7088ab845ba503507 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:35:00 +0000 Subject: [PATCH 28/65] Bug 1761098 - Add a new quota client. r=dom-storage-reviewers,janv The quota client supports only minimal functionality for now. Differential Revision: https://phabricator.services.mozilla.com/D141945 --- dom/fs/parent/FileSystemQuotaClient.cpp | 161 ++++++++++++++++++++++++ dom/fs/parent/FileSystemQuotaClient.h | 26 ++++ dom/fs/parent/moz.build | 2 + dom/quota/ActorsParent.cpp | 16 ++- dom/quota/Client.cpp | 27 ++++ dom/quota/Client.h | 2 + 6 files changed, 228 insertions(+), 6 deletions(-) create mode 100644 dom/fs/parent/FileSystemQuotaClient.cpp create mode 100644 dom/fs/parent/FileSystemQuotaClient.h diff --git a/dom/fs/parent/FileSystemQuotaClient.cpp b/dom/fs/parent/FileSystemQuotaClient.cpp new file mode 100644 index 0000000000000..57458e31b7d35 --- /dev/null +++ b/dom/fs/parent/FileSystemQuotaClient.cpp @@ -0,0 +1,161 @@ +/* -*- 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 "FileSystemQuotaClient.h" + +#include "mozilla/dom/quota/Client.h" +#include "mozilla/dom/quota/UsageInfo.h" +#include "mozilla/ipc/BackgroundParent.h" + +namespace mozilla::dom::fs { + +namespace { + +class QuotaClient final : public mozilla::dom::quota::Client { + public: + QuotaClient(); + + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(mozilla::dom::fs::QuotaClient, override) + + Type GetType() override; + + Result InitOrigin( + quota::PersistenceType aPersistenceType, + const quota::OriginMetadata& aOriginMetadata, + const AtomicBool& aCanceled) override; + + nsresult InitOriginWithoutTracking( + quota::PersistenceType aPersistenceType, + const quota::OriginMetadata& aOriginMetadata, + const AtomicBool& aCanceled) override; + + Result GetUsageForOrigin( + quota::PersistenceType aPersistenceType, + const quota::OriginMetadata& aOriginMetadata, + const AtomicBool& aCanceled) override; + + void OnOriginClearCompleted(quota::PersistenceType aPersistenceType, + const nsACString& aOrigin) override; + + void ReleaseIOThreadObjects() override; + + void AbortOperationsForLocks( + const DirectoryLockIdTable& aDirectoryLockIds) override; + + void AbortOperationsForProcess(ContentParentId aContentParentId) override; + + void AbortAllOperations() override; + + void StartIdleMaintenance() override; + + void StopIdleMaintenance() override; + + private: + ~QuotaClient() = default; + + void InitiateShutdown() override; + bool IsShutdownCompleted() const override; + nsCString GetShutdownStatus() const override; + void ForceKillActors() override; + void FinalizeShutdown() override; +}; + +} // namespace + +QuotaClient::QuotaClient() { ::mozilla::ipc::AssertIsOnBackgroundThread(); } + +mozilla::dom::quota::Client::Type QuotaClient::GetType() { + return quota::Client::Type::FILESYSTEM; +} + +Result QuotaClient::InitOrigin( + quota::PersistenceType aPersistenceType, + const quota::OriginMetadata& aOriginMetadata, const AtomicBool& aCanceled) { + quota::AssertIsOnIOThread(); + + return quota::UsageInfo{}; +} + +nsresult QuotaClient::InitOriginWithoutTracking( + quota::PersistenceType aPersistenceType, + const quota::OriginMetadata& aOriginMetadata, const AtomicBool& aCanceled) { + quota::AssertIsOnIOThread(); + + return NS_OK; +} + +Result QuotaClient::GetUsageForOrigin( + quota::PersistenceType aPersistenceType, + const quota::OriginMetadata& aOriginMetadata, const AtomicBool& aCanceled) { + quota::AssertIsOnIOThread(); + + return quota::UsageInfo{}; +} + +void QuotaClient::OnOriginClearCompleted( + quota::PersistenceType aPersistenceType, const nsACString& aOrigin) { + quota::AssertIsOnIOThread(); +} + +void QuotaClient::ReleaseIOThreadObjects() { quota::AssertIsOnIOThread(); } + +void QuotaClient::AbortOperationsForLocks( + const DirectoryLockIdTable& aDirectoryLockIds) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +void QuotaClient::AbortOperationsForProcess(ContentParentId aContentParentId) { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +void QuotaClient::AbortAllOperations() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +void QuotaClient::StartIdleMaintenance() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +void QuotaClient::StopIdleMaintenance() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +void QuotaClient::InitiateShutdown() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); +} + +bool QuotaClient::IsShutdownCompleted() const { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + return true; +} + +nsCString QuotaClient::GetShutdownStatus() const { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + return "Not implemented"_ns; +} + +void QuotaClient::ForceKillActors() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + // Hopefully not needed. +} + +void QuotaClient::FinalizeShutdown() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + // Empty for now. +} + +already_AddRefed CreateQuotaClient() { + ::mozilla::ipc::AssertIsOnBackgroundThread(); + + RefPtr client = new fs::QuotaClient(); + return client.forget(); +} + +} // namespace mozilla::dom::fs diff --git a/dom/fs/parent/FileSystemQuotaClient.h b/dom/fs/parent/FileSystemQuotaClient.h new file mode 100644 index 0000000000000..cf8f1cf8628ca --- /dev/null +++ b/dom/fs/parent/FileSystemQuotaClient.h @@ -0,0 +1,26 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=8 sts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef DOM_FS_PARENT_FILESYSTEMQUOTACLIENT_H_ +#define DOM_FS_PARENT_FILESYSTEMQUOTACLIENT_H_ + +template +struct already_AddRefed; + +namespace mozilla::dom { + +namespace quota { +class Client; +} // namespace quota + +namespace fs { + +already_AddRefed CreateQuotaClient(); + +} // namespace fs +} // namespace mozilla::dom + +#endif // DOM_FS_PARENT_FILESYSTEMQUOTACLIENT_H_ diff --git a/dom/fs/parent/moz.build b/dom/fs/parent/moz.build index 62dc3f37b9acc..e19e2318a4412 100644 --- a/dom/fs/parent/moz.build +++ b/dom/fs/parent/moz.build @@ -11,12 +11,14 @@ DIRS += [ EXPORTS.mozilla.dom += [ "FileSystemManagerParent.h", "FileSystemManagerParentFactory.h", + "FileSystemQuotaClient.h", ] UNIFIED_SOURCES += [ "FileSystemHashSource.cpp", "FileSystemManagerParent.cpp", "FileSystemManagerParentFactory.cpp", + "FileSystemQuotaClient.cpp", ] FINAL_LIBRARY = "xul" diff --git a/dom/quota/ActorsParent.cpp b/dom/quota/ActorsParent.cpp index d56f458aa9f18..ca28d4be50d24 100644 --- a/dom/quota/ActorsParent.cpp +++ b/dom/quota/ActorsParent.cpp @@ -70,6 +70,7 @@ #include "mozilla/UniquePtr.h" #include "mozilla/Unused.h" #include "mozilla/Variant.h" +#include "mozilla/dom/FileSystemQuotaClient.h" #include "mozilla/dom/FlippedOnce.h" #include "mozilla/dom/LocalStorageCommon.h" #include "mozilla/dom/StorageActivityService.h" @@ -3713,7 +3714,8 @@ nsresult QuotaManager::Init() { "QuotaManager IO")); static_assert(Client::IDB == 0 && Client::DOMCACHE == 1 && Client::SDB == 2 && - Client::LS == 3 && Client::TYPE_MAX == 4, + Client::FILESYSTEM == 3 && Client::LS == 4 && + Client::TYPE_MAX == 5, "Fix the registration!"); // Register clients. @@ -3721,6 +3723,7 @@ nsresult QuotaManager::Init() { clients.AppendElement(indexedDB::CreateQuotaClient()); clients.AppendElement(cache::CreateQuotaClient()); clients.AppendElement(simpledb::CreateQuotaClient()); + clients.AppendElement(fs::CreateQuotaClient()); if (NextGenLocalStorageEnabled()) { clients.AppendElement(localstorage::CreateQuotaClient()); } else { @@ -3732,11 +3735,12 @@ nsresult QuotaManager::Init() { MOZ_ASSERT(mClients->Capacity() == Client::TYPE_MAX, "Should be using an auto array with correct capacity!"); - mAllClientTypes.init(ClientTypesArray{Client::Type::IDB, - Client::Type::DOMCACHE, - Client::Type::SDB, Client::Type::LS}); - mAllClientTypesExceptLS.init(ClientTypesArray{ - Client::Type::IDB, Client::Type::DOMCACHE, Client::Type::SDB}); + mAllClientTypes.init(ClientTypesArray{ + Client::Type::IDB, Client::Type::DOMCACHE, Client::Type::SDB, + Client::Type::FILESYSTEM, Client::Type::LS}); + mAllClientTypesExceptLS.init( + ClientTypesArray{Client::Type::IDB, Client::Type::DOMCACHE, + Client::Type::SDB, Client::Type::FILESYSTEM}); return NS_OK; } diff --git a/dom/quota/Client.cpp b/dom/quota/Client.cpp index 757c013ac05c7..3fb5759431c59 100644 --- a/dom/quota/Client.cpp +++ b/dom/quota/Client.cpp @@ -21,6 +21,7 @@ namespace { const char kIDBPrefix = 'I'; const char kDOMCachePrefix = 'C'; const char kSDBPrefix = 'S'; +const char kFILESYSTEMPrefix = 'F'; const char kLSPrefix = 'L'; template @@ -77,6 +78,23 @@ struct ClientTypeTraits { static bool From(char aData) { return aData == kSDBPrefix; } }; +template <> +struct ClientTypeTraits { + template + static void To(T& aData) { + aData.AssignLiteral(FILESYSTEM_DIRECTORY_NAME); + } + + static void To(char& aData) { aData = kFILESYSTEMPrefix; } + + template + static bool From(const T& aData) { + return aData.EqualsLiteral(FILESYSTEM_DIRECTORY_NAME); + } + + static bool From(char aData) { return aData == kFILESYSTEMPrefix; } +}; + template <> struct ClientTypeTraits { template @@ -109,6 +127,10 @@ bool TypeTo_impl(Client::Type aType, T& aData) { ClientTypeTraits::To(aData); return true; + case Client::FILESYSTEM: + ClientTypeTraits::To(aData); + return true; + case Client::LS: if (CachedNextGenLocalStorageEnabled()) { ClientTypeTraits::To(aData); @@ -141,6 +163,11 @@ bool TypeFrom_impl(const T& aData, Client::Type& aType) { return true; } + if (ClientTypeTraits::From(aData)) { + aType = Client::FILESYSTEM; + return true; + } + if (CachedNextGenLocalStorageEnabled() && ClientTypeTraits::From(aData)) { aType = Client::LS; diff --git a/dom/quota/Client.h b/dom/quota/Client.h index 528ee1cf14e36..03ce07f3dbb43 100644 --- a/dom/quota/Client.h +++ b/dom/quota/Client.h @@ -25,6 +25,7 @@ class nsIFile; #define IDB_DIRECTORY_NAME "idb" #define DOMCACHE_DIRECTORY_NAME "cache" #define SDB_DIRECTORY_NAME "sdb" +#define FILESYSTEM_DIRECTORY_NAME "fs" #define LS_DIRECTORY_NAME "ls" // Deprecated @@ -54,6 +55,7 @@ class Client { // APPCACHE, DOMCACHE, SDB, + FILESYSTEM, LS, TYPE_MAX }; From fb2911b16a5de4f22275c9869cb9a4b9133a17eb Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:35:00 +0000 Subject: [PATCH 29/65] Bug 1761098 - Real implementation of InitiateShutdown and IsShutdownCompleted; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D155791 --- dom/fs/parent/FileSystemManagerParent.cpp | 12 ++++++ dom/fs/parent/FileSystemManagerParent.h | 3 ++ dom/fs/parent/FileSystemQuotaClient.cpp | 5 ++- .../datamodel/FileSystemDataManager.cpp | 40 +++++++++++++++++++ .../parent/datamodel/FileSystemDataManager.h | 6 +++ 5 files changed, 65 insertions(+), 1 deletion(-) diff --git a/dom/fs/parent/FileSystemManagerParent.cpp b/dom/fs/parent/FileSystemManagerParent.cpp index c588c5a47446e..c554ebdac3f92 100644 --- a/dom/fs/parent/FileSystemManagerParent.cpp +++ b/dom/fs/parent/FileSystemManagerParent.cpp @@ -122,6 +122,18 @@ IPCResult FileSystemManagerParent::RecvNeedQuota( return IPC_OK(); } +void FileSystemManagerParent::RequestAllowToClose() { + if (mRequestedAllowToClose) { + return; + } + + mRequestedAllowToClose.Flip(); + + // XXX Send a message to the child and wait for a response before closing! + + Close(); +} + void FileSystemManagerParent::OnChannelClose() { if (!mAllowedToClose) { AllowToClose(); diff --git a/dom/fs/parent/FileSystemManagerParent.h b/dom/fs/parent/FileSystemManagerParent.h index 1076916a8d6d8..b7b3e568f2875 100644 --- a/dom/fs/parent/FileSystemManagerParent.h +++ b/dom/fs/parent/FileSystemManagerParent.h @@ -70,6 +70,8 @@ class FileSystemManagerParent : public PFileSystemManagerParent { mozilla::ipc::IPCResult RecvNeedQuota(FileSystemQuotaRequest&& aRequest, NeedQuotaResolver&& aResolver); + void RequestAllowToClose(); + void OnChannelClose() override; void OnChannelError() override; @@ -84,6 +86,7 @@ class FileSystemManagerParent : public PFileSystemManagerParent { const EntryId mRootEntry; + FlippedOnce mRequestedAllowToClose; FlippedOnce mAllowedToClose; }; diff --git a/dom/fs/parent/FileSystemQuotaClient.cpp b/dom/fs/parent/FileSystemQuotaClient.cpp index 57458e31b7d35..a195fb1cfa197 100644 --- a/dom/fs/parent/FileSystemQuotaClient.cpp +++ b/dom/fs/parent/FileSystemQuotaClient.cpp @@ -6,6 +6,7 @@ #include "FileSystemQuotaClient.h" +#include "mozilla/dom/FileSystemDataManager.h" #include "mozilla/dom/quota/Client.h" #include "mozilla/dom/quota/UsageInfo.h" #include "mozilla/ipc/BackgroundParent.h" @@ -125,12 +126,14 @@ void QuotaClient::StopIdleMaintenance() { void QuotaClient::InitiateShutdown() { ::mozilla::ipc::AssertIsOnBackgroundThread(); + + data::FileSystemDataManager::InitiateShutdown(); } bool QuotaClient::IsShutdownCompleted() const { ::mozilla::ipc::AssertIsOnBackgroundThread(); - return true; + return data::FileSystemDataManager::IsShutdownCompleted(); } nsCString QuotaClient::GetShutdownStatus() const { diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 29f385ee9cf57..679c540de7b2f 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -8,7 +8,9 @@ #include "mozilla/Result.h" #include "mozilla/StaticPtr.h" +#include "mozilla/dom/FileSystemManagerParent.h" #include "mozilla/dom/quota/QuotaCommon.h" +#include "mozilla/dom/quota/QuotaManager.h" #include "mozilla/dom/quota/ResultExtensions.h" #include "nsBaseHashtable.h" #include "nsHashKeys.h" @@ -62,6 +64,8 @@ RefPtr GetFileSystemDataManager(const Origin& aOrigin) { void AddFileSystemDataManager( const Origin& aOrigin, const RefPtr& aDataManager) { + MOZ_ASSERT(!quota::QuotaManager::IsShuttingDown()); + if (!gDataManagers) { gDataManagers = new FileSystemDataManagerHashtable(); } @@ -97,6 +101,10 @@ FileSystemDataManager::~FileSystemDataManager() { RefPtr FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { + if (quota::QuotaManager::IsShuttingDown()) { + return CreatePromise::CreateAndReject(NS_ERROR_FAILURE, __func__); + } + if (RefPtr dataManager = GetFileSystemDataManager(aOrigin)) { if (dataManager->IsOpening()) { @@ -154,6 +162,32 @@ FileSystemDataManager::GetOrCreateFileSystemDataManager(const Origin& aOrigin) { }); } +// static +void FileSystemDataManager::InitiateShutdown() { + if (!gDataManagers) { + return; + } + + for (const auto& dataManager : gDataManagers->Values()) { + InvokeAsync(dataManager->MutableIOTargetPtr(), __func__, + [dataManager = RefPtr( + dataManager.get())]() mutable { + dataManager->RequestAllowToClose(); + + nsCOMPtr target = + dataManager->MutableBackgroundTargetPtr(); + + NS_ProxyRelease("ReleaseFileSystemDataManager", target, + dataManager.forget()); + + return BoolPromise::CreateAndResolve(true, __func__); + }); + } +} + +// static +bool FileSystemDataManager::IsShutdownCompleted() { return !gDataManagers; } + void FileSystemDataManager::Register() { mRegCount++; } void FileSystemDataManager::Unregister() { @@ -200,6 +234,12 @@ bool FileSystemDataManager::IsInactive() const { return !mRegCount && !mActors.Count(); } +void FileSystemDataManager::RequestAllowToClose() { + for (const auto& actor : mActors) { + actor->RequestAllowToClose(); + } +} + RefPtr FileSystemDataManager::BeginOpen() { MOZ_ASSERT(mState == State::Initial); diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.h b/dom/fs/parent/datamodel/FileSystemDataManager.h index cd234f40c8877..fdd6265e19c01 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.h +++ b/dom/fs/parent/datamodel/FileSystemDataManager.h @@ -42,6 +42,10 @@ class FileSystemDataManager static RefPtr GetOrCreateFileSystemDataManager( const Origin& aOrigin); + static void InitiateShutdown(); + + static bool IsShutdownCompleted(); + NS_INLINE_DECL_REFCOUNTING(FileSystemDataManager) nsISerialEventTarget* MutableBackgroundTargetPtr() const { @@ -75,6 +79,8 @@ class FileSystemDataManager bool IsClosing() const { return mState == State::Closing; } + void RequestAllowToClose(); + RefPtr BeginOpen(); RefPtr BeginClose(); From f553f0049ae385efd957b0bafada6d174777eb91 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:35:00 +0000 Subject: [PATCH 30/65] Bug 1772394 - Add a TODO comment about storage initialization; r=janv,dom-storage-reviewers,jari Some of the original changes introduced in this patch have been moved to D155551, D155552 and D155788. Differential Revision: https://phabricator.services.mozilla.com/D152795 --- dom/fs/parent/datamodel/FileSystemDataManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dom/fs/parent/datamodel/FileSystemDataManager.cpp b/dom/fs/parent/datamodel/FileSystemDataManager.cpp index 679c540de7b2f..5d89237e1e1a2 100644 --- a/dom/fs/parent/datamodel/FileSystemDataManager.cpp +++ b/dom/fs/parent/datamodel/FileSystemDataManager.cpp @@ -245,6 +245,8 @@ RefPtr FileSystemDataManager::BeginOpen() { mState = State::Opening; + // XXX Add storage initialization to the chain. + InvokeAsync(MutableIOTargetPtr(), __func__, [self = RefPtr(this)]() mutable { nsCOMPtr target = From 33f43b7b10525c02dba8f27dea8cc4b4da78e9a4 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 09:35:01 +0000 Subject: [PATCH 31/65] Bug 1787839 - QM_TRY: Add support for custom return value functions with error argument; r=dom-storage-reviewers,asuth Differential Revision: https://phabricator.services.mozilla.com/D155869 --- dom/quota/QuotaCommon.h | 109 ++++++++++++----------- dom/quota/test/gtest/TestQuotaCommon.cpp | 102 +++++++++++++++++++++ 2 files changed, 159 insertions(+), 52 deletions(-) diff --git a/dom/quota/QuotaCommon.h b/dom/quota/QuotaCommon.h index 7ff07c0a99a96..a88ee18f859b0 100644 --- a/dom/quota/QuotaCommon.h +++ b/dom/quota/QuotaCommon.h @@ -49,6 +49,8 @@ template class NotNull; } +#define MOZ_ARGS_AFTER_3(a1, a2, a3, ...) __VA_ARGS__ + #define MOZ_ADD_ARGS2(...) , ##__VA_ARGS__ #define MOZ_ADD_ARGS(...) MOZ_ADD_ARGS2(__VA_ARGS__) @@ -483,19 +485,19 @@ class NotNull; #endif // Handles the case when QM_VOID is passed as a custom return value. -#define QM_HANDLE_CUSTOM_RET_VAL_HELPER0(func, expr) +#define QM_HANDLE_CUSTOM_RET_VAL_HELPER0(func, expr, error) -#define QM_HANDLE_CUSTOM_RET_VAL_HELPER1(func, expr, customRetVal) \ - mozilla::dom::quota::HandleCustomRetVal(func, #expr, customRetVal) +#define QM_HANDLE_CUSTOM_RET_VAL_HELPER1(func, expr, error, customRetVal) \ + mozilla::dom::quota::HandleCustomRetVal(func, #expr, error, customRetVal) #define QM_HANDLE_CUSTOM_RET_VAL_GLUE(a, b) a b #define QM_HANDLE_CUSTOM_RET_VAL(...) \ QM_HANDLE_CUSTOM_RET_VAL_GLUE( \ MOZ_PASTE_PREFIX_AND_ARG_COUNT(QM_HANDLE_CUSTOM_RET_VAL_HELPER, \ - MOZ_ARGS_AFTER_2(__VA_ARGS__)), \ - (MOZ_ARG_1(__VA_ARGS__), \ - MOZ_ARG_2(__VA_ARGS__) MOZ_ADD_ARGS(MOZ_ARGS_AFTER_2(__VA_ARGS__)))) + MOZ_ARGS_AFTER_3(__VA_ARGS__)), \ + (MOZ_ARG_1(__VA_ARGS__), MOZ_ARG_2(__VA_ARGS__), \ + MOZ_ARG_3(__VA_ARGS__) MOZ_ADD_ARGS(MOZ_ARGS_AFTER_3(__VA_ARGS__)))) // QM_TRY_PROPAGATE_ERR, QM_TRY_CUSTOM_RET_VAL, // QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP and QM_TRY_GLUE macros are implementation @@ -513,30 +515,30 @@ class NotNull; // Handles the three arguments case when a custom return value needs to be // returned -#define QM_TRY_CUSTOM_RET_VAL(tryResult, expr, customRetVal) \ - auto tryResult = (expr); \ - static_assert(std::is_empty_v); \ - if (MOZ_UNLIKELY(tryResult.isErr())) { \ - auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \ - mozilla::dom::quota::QM_HANDLE_ERROR( \ - expr, tryTempError, mozilla::dom::quota::Severity::Error); \ - constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ - return QM_HANDLE_CUSTOM_RET_VAL(func, expr, customRetVal); \ +#define QM_TRY_CUSTOM_RET_VAL(tryResult, expr, customRetVal) \ + auto tryResult = (expr); \ + static_assert(std::is_empty_v); \ + if (MOZ_UNLIKELY(tryResult.isErr())) { \ + auto tryTempError MOZ_MAYBE_UNUSED = tryResult.unwrapErr(); \ + mozilla::dom::quota::QM_HANDLE_ERROR( \ + expr, tryTempError, mozilla::dom::quota::Severity::Error); \ + constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ + return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \ } // Handles the four arguments case when a cleanup function needs to be called // before a custom return value is returned -#define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, customRetVal, \ - cleanup) \ - auto tryResult = (expr); \ - static_assert(std::is_empty_v); \ - if (MOZ_UNLIKELY(tryResult.isErr())) { \ - auto tryTempError = tryResult.unwrapErr(); \ - mozilla::dom::quota::QM_HANDLE_ERROR( \ - expr, tryTempError, mozilla::dom::quota::Severity::Error); \ - cleanup(tryTempError); \ - constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ - return QM_HANDLE_CUSTOM_RET_VAL(func, expr, customRetVal); \ +#define QM_TRY_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, customRetVal, \ + cleanup) \ + auto tryResult = (expr); \ + static_assert(std::is_empty_v); \ + if (MOZ_UNLIKELY(tryResult.isErr())) { \ + auto tryTempError = tryResult.unwrapErr(); \ + mozilla::dom::quota::QM_HANDLE_ERROR( \ + expr, tryTempError, mozilla::dom::quota::Severity::Error); \ + cleanup(tryTempError); \ + constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ + return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \ } // Chooses the final implementation macro for given argument count. @@ -591,23 +593,23 @@ class NotNull; mozilla::dom::quota::QM_HANDLE_ERROR( \ expr, tryTempError, mozilla::dom::quota::Severity::Error); \ constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ - return QM_HANDLE_CUSTOM_RET_VAL(func, expr, customRetVal); \ + return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \ } \ MOZ_REMOVE_PAREN(target) = tryResult.accessFunction(); // Handles the six arguments case when a cleanup function needs to be called // before a custom return value is returned -#define QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP( \ - tryResult, accessFunction, target, expr, customRetVal, cleanup) \ - auto tryResult = (expr); \ - if (MOZ_UNLIKELY(tryResult.isErr())) { \ - auto tryTempError = tryResult.unwrapErr(); \ - mozilla::dom::quota::QM_HANDLE_ERROR( \ - expr, tryTempError, mozilla::dom::quota::Severity::Error); \ - cleanup(tryTempError); \ - constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ - return QM_HANDLE_CUSTOM_RET_VAL(func, expr, customRetVal); \ - } \ +#define QM_TRY_ASSIGN_CUSTOM_RET_VAL_WITH_CLEANUP( \ + tryResult, accessFunction, target, expr, customRetVal, cleanup) \ + auto tryResult = (expr); \ + if (MOZ_UNLIKELY(tryResult.isErr())) { \ + auto tryTempError = tryResult.unwrapErr(); \ + mozilla::dom::quota::QM_HANDLE_ERROR( \ + expr, tryTempError, mozilla::dom::quota::Severity::Error); \ + cleanup(tryTempError); \ + constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ + return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \ + } \ MOZ_REMOVE_PAREN(target) = tryResult.accessFunction(); // Chooses the final implementation macro for given argument count. @@ -676,23 +678,23 @@ class NotNull; mozilla::dom::quota::QM_HANDLE_ERROR( \ expr, tryResult.inspectErr(), mozilla::dom::quota::Severity::Error); \ constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ - return QM_HANDLE_CUSTOM_RET_VAL(func, expr, customRetVal); \ + return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \ } \ return tryResult.unwrap(); // Handles the four arguments case when a cleanup function needs to be called // before a custom return value is returned -#define QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, \ - customRetVal, cleanup) \ - auto tryResult = (expr); \ - if (MOZ_UNLIKELY(tryResult.isErr())) { \ - auto tryTempError = tryResult.unwrapErr(); \ - mozilla::dom::quota::QM_HANDLE_ERROR( \ - expr, tryTempError, mozilla::dom::quota::Severity::Error); \ - cleanup(tryTempError); \ - constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ - return QM_HANDLE_CUSTOM_RET_VAL(func, expr, customRetVal); \ - } \ +#define QM_TRY_RETURN_CUSTOM_RET_VAL_WITH_CLEANUP(tryResult, expr, \ + customRetVal, cleanup) \ + auto tryResult = (expr); \ + if (MOZ_UNLIKELY(tryResult.isErr())) { \ + auto tryTempError = tryResult.unwrapErr(); \ + mozilla::dom::quota::QM_HANDLE_ERROR( \ + expr, tryTempError, mozilla::dom::quota::Severity::Error); \ + cleanup(tryTempError); \ + constexpr const auto& func MOZ_MAYBE_UNUSED = __func__; \ + return QM_HANDLE_CUSTOM_RET_VAL(func, expr, tryTempError, customRetVal); \ + } \ return tryResult.unwrap(); // Chooses the final implementation macro for given argument count. @@ -1422,12 +1424,15 @@ Nothing HandleErrorWithCleanupReturnNothing(const char* aExpr, const T& aRv, return Nothing(); } -template -auto HandleCustomRetVal(const char* aFunc, const char* aExpr, +template +auto HandleCustomRetVal(const char* aFunc, const char* aExpr, const T& aRv, CustomRetVal&& aCustomRetVal) { if constexpr (std::is_invocable::value) { return aCustomRetVal(aFunc, aExpr); + } else if constexpr (std::is_invocable::value) { + return aCustomRetVal(aFunc, aRv); } else { return std::forward(aCustomRetVal); } diff --git a/dom/quota/test/gtest/TestQuotaCommon.cpp b/dom/quota/test/gtest/TestQuotaCommon.cpp index 7a3947b47a17f..6f676453d52a2 100644 --- a/dom/quota/test/gtest/TestQuotaCommon.cpp +++ b/dom/quota/test/gtest/TestQuotaCommon.cpp @@ -196,6 +196,22 @@ TEST(QuotaCommon_Try, Success_NoErr_DiagnosticAssertUnreachable) # endif #endif +TEST(QuotaCommon_Try, Success_CustomErr_CustomLambda) +{ + bool tryDidNotReturn = false; + + nsresult rv = [&tryDidNotReturn]() -> nsresult { + QM_TRY(MOZ_TO_RESULT(NS_OK), [](const char*, nsresult aRv) { return aRv; }); + + tryDidNotReturn = true; + + return NS_OK; + }(); + + EXPECT_TRUE(tryDidNotReturn); + EXPECT_EQ(rv, NS_OK); +} + TEST(QuotaCommon_Try, Success_WithCleanup) { bool tryCleanupRan = false; @@ -247,6 +263,23 @@ TEST(QuotaCommon_Try, Failure_CustomErr) EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); } +TEST(QuotaCommon_Try, Failure_CustomErr_CustomLambda) +{ + bool tryDidNotReturn = false; + + nsresult rv = [&tryDidNotReturn]() -> nsresult { + QM_TRY(MOZ_TO_RESULT(NS_ERROR_FAILURE), + [](const char* aFunc, nsresult) { return NS_ERROR_UNEXPECTED; }); + + tryDidNotReturn = true; + + return NS_OK; + }(); + + EXPECT_FALSE(tryDidNotReturn); + EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); +} + TEST(QuotaCommon_Try, Failure_NoErr) { bool tryDidNotReturn = false; @@ -537,6 +570,24 @@ TEST(QuotaCommon_TryInspect, Success_NoErr_AssertUnreachable) } #endif +TEST(QuotaCommon_TryInspect, Success_CustomErr_CustomLambda) +{ + bool tryInspectDidNotReturn = false; + + nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { + QM_TRY_INSPECT(const auto& x, (Result{42}), + [](const char*, nsresult aRv) { return aRv; }); + EXPECT_EQ(x, 42); + + tryInspectDidNotReturn = true; + + return NS_OK; + }(); + + EXPECT_TRUE(tryInspectDidNotReturn); + EXPECT_EQ(rv, NS_OK); +} + TEST(QuotaCommon_TryInspect, Success_WithCleanup) { bool tryInspectCleanupRan = false; @@ -595,6 +646,25 @@ TEST(QuotaCommon_TryInspect, Failure_CustomErr) EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); } +TEST(QuotaCommon_TryInspect, Failure_CustomErr_CustomLambda) +{ + bool tryInspectDidNotReturn = false; + + nsresult rv = [&tryInspectDidNotReturn]() -> nsresult { + QM_TRY_INSPECT(const auto& x, + (Result{Err(NS_ERROR_FAILURE)}), + [](const char*, nsresult) { return NS_ERROR_UNEXPECTED; }); + Unused << x; + + tryInspectDidNotReturn = true; + + return NS_OK; + }(); + + EXPECT_FALSE(tryInspectDidNotReturn); + EXPECT_EQ(rv, NS_ERROR_UNEXPECTED); +} + TEST(QuotaCommon_TryInspect, Failure_NoErr) { bool tryInspectDidNotReturn = false; @@ -938,6 +1008,21 @@ TEST(QuotaCommon_TryReturn, Success_CustomErr_AssertUnreachable) } #endif +TEST(QuotaCommon_TryReturn, Success_CustomErr_CustomLambda) +{ + bool tryReturnDidNotReturn = false; + + auto res = [&tryReturnDidNotReturn]() -> Result { + QM_TRY_RETURN(MOZ_TO_RESULT(NS_OK), + [](const char*, nsresult aRv) { return Err(aRv); }); + + tryReturnDidNotReturn = true; + }(); + + EXPECT_FALSE(tryReturnDidNotReturn); + EXPECT_TRUE(res.isOk()); +} + TEST(QuotaCommon_TryReturn, Success_WithCleanup) { bool tryReturnCleanupRan = false; @@ -1004,6 +1089,23 @@ TEST(QuotaCommon_TryReturn, Failure_CustomErr) EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); } +TEST(QuotaCommon_TryReturn, Failure_CustomErr_CustomLambda) +{ + bool tryReturnDidNotReturn = false; + + auto res = [&tryReturnDidNotReturn]() -> Result { + QM_TRY_RETURN( + (Result{Err(NS_ERROR_FAILURE)}), + [](const char*, nsresult) { return Err(NS_ERROR_UNEXPECTED); }); + + tryReturnDidNotReturn = true; + }(); + + EXPECT_FALSE(tryReturnDidNotReturn); + EXPECT_TRUE(res.isErr()); + EXPECT_EQ(res.unwrapErr(), NS_ERROR_UNEXPECTED); +} + TEST(QuotaCommon_TryReturn, Failure_WithCleanup) { bool tryReturnCleanupRan = false; From 71655f76a1d092c3454427c7d96d73115d34ae77 Mon Sep 17 00:00:00 2001 From: Paul Zuehlcke Date: Wed, 31 Aug 2022 09:44:27 +0000 Subject: [PATCH 32/65] Bug 1785870 - Add a test pref for importing cookie banner handling rules. r=timhuang Differential Revision: https://phabricator.services.mozilla.com/D155896 --- modules/libpref/init/all.js | 6 +++ .../cookiebanners/CookieBannerListService.jsm | 51 ++++++++++++++++++- 2 files changed, 55 insertions(+), 2 deletions(-) diff --git a/modules/libpref/init/all.js b/modules/libpref/init/all.js index 25535566b4043..265e1f8ea50e5 100644 --- a/modules/libpref/init/all.js +++ b/modules/libpref/init/all.js @@ -4434,3 +4434,9 @@ pref("cookiebanners.listService.logLevel", "Error"); // Contorls the log level for Cookie Banner Auto Clicking. pref("cookiebanners.bannerClicking.logLevel", "Error"); + +// Array of test rules for cookie banner handling as a JSON string. They will be +// inserted in addition to regular rules and may override them when setting the +// same domain. Every array item should be a valid CookieBannerRule. See +// CookieBannerRule.schema.json. +pref("cookiebanners.listService.testRules", "[]"); diff --git a/toolkit/components/cookiebanners/CookieBannerListService.jsm b/toolkit/components/cookiebanners/CookieBannerListService.jsm index 8d7c2e80e1d16..d18a30495babc 100644 --- a/toolkit/components/cookiebanners/CookieBannerListService.jsm +++ b/toolkit/components/cookiebanners/CookieBannerListService.jsm @@ -23,6 +23,9 @@ XPCOMUtils.defineLazyPreferenceGetter( "cookiebanners.cookieInjector.defaultExpiryRelative" ); +const PREF_TEST_RULES = "cookiebanners.listService.testRules"; +XPCOMUtils.defineLazyPreferenceGetter(lazy, "testRulesPref", PREF_TEST_RULES); + // Name of the RemoteSettings collection containing the rules. const COLLECTION_NAME = "cookie-banner-rules-list"; @@ -65,6 +68,8 @@ class CookieBannerListService { this.#rs.on("sync", this.#onSyncCallback); } + Services.prefs.addObserver(PREF_TEST_RULES, this); + return this.importAllRules(); } @@ -73,6 +78,7 @@ class CookieBannerListService { let rules = await this.#rs.get(); this.#importRules(rules); + this.#importTestRules(); } shutdown() { @@ -83,6 +89,8 @@ class CookieBannerListService { this.#rs.off("sync", this.#onSyncCallback); this.#onSyncCallback = null; } + + Services.prefs.removeObserver(PREF_TEST_RULES, this); } /** @@ -92,13 +100,29 @@ class CookieBannerListService { log("onSync", { created, updated, deleted }); // Remove deleted rules. - this.removeRules(deleted); + this.#removeRules(deleted); // Import new rules and override updated rules. this.#importRules(created.concat(updated.map(u => u.new))); + + // Re-import test rules in case they need to overwrite existing rules or a test rule was deleted above. + this.#importTestRules(); + } + + observe(subject, topic, prefName) { + if (prefName != PREF_TEST_RULES) { + return; + } + + // When the test rules update we need to clear all rules and import them + // again. This is required because we don't have a mechanism for deleting specific + // test rules. + // Passing `doImport = false` since we trigger the import ourselves. + Services.cookieBanners.resetRules(false); + this.importAllRules(); } - removeRules(rules = []) { + #removeRules(rules = []) { log("removeRules", rules); rules @@ -125,6 +149,29 @@ class CookieBannerListService { }); } + #importTestRules() { + log("importTestRules"); + + if (!Services.prefs.prefHasUserValue(PREF_TEST_RULES)) { + log("Skip importing test rules: Pref has default value."); + return; + } + + let testRules; + try { + testRules = JSON.parse(lazy.testRulesPref); + } catch (error) { + log("Failed to parse test rules JSON string.", error); + Cu.reportError( + `Failed to parse test rules JSON string. Make sure ${PREF_TEST_RULES} contains valid JSON. ${error?.name}` + ); + + return; + } + + this.#importRules(testRules); + } + #importCookieRule(rule, cookies) { // Skip rules that don't have cookies. if (!cookies) { From b3d833c951c5873cc92ab4bd27c0c98699ac82c1 Mon Sep 17 00:00:00 2001 From: Paul Zuehlcke Date: Wed, 31 Aug 2022 09:44:27 +0000 Subject: [PATCH 33/65] Bug 1785870 - Validate cookie banner handling test rules. r=timhuang Depends on D155896 Differential Revision: https://phabricator.services.mozilla.com/D155897 --- .../cookiebanners/CookieBannerListService.jsm | 65 ++++++++++++++++--- toolkit/components/cookiebanners/jar.mn | 6 ++ toolkit/components/cookiebanners/moz.build | 2 + .../components/cookiebanners/schema/README | 3 +- 4 files changed, 66 insertions(+), 10 deletions(-) create mode 100644 toolkit/components/cookiebanners/jar.mn diff --git a/toolkit/components/cookiebanners/CookieBannerListService.jsm b/toolkit/components/cookiebanners/CookieBannerListService.jsm index d18a30495babc..b415d7ac443c5 100644 --- a/toolkit/components/cookiebanners/CookieBannerListService.jsm +++ b/toolkit/components/cookiebanners/CookieBannerListService.jsm @@ -11,11 +11,10 @@ const { XPCOMUtils } = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ); -ChromeUtils.defineModuleGetter( - lazy, - "RemoteSettings", - "resource://services-settings/remote-settings.js" -); +XPCOMUtils.defineLazyModuleGetters(lazy, { + RemoteSettings: "resource://services-settings/remote-settings.js", + JsonSchema: "resource://gre/modules/JsonSchema.jsm", +}); XPCOMUtils.defineLazyPreferenceGetter( lazy, @@ -25,7 +24,6 @@ XPCOMUtils.defineLazyPreferenceGetter( const PREF_TEST_RULES = "cookiebanners.listService.testRules"; XPCOMUtils.defineLazyPreferenceGetter(lazy, "testRulesPref", PREF_TEST_RULES); - // Name of the RemoteSettings collection containing the rules. const COLLECTION_NAME = "cookie-banner-rules-list"; @@ -41,6 +39,19 @@ function log(...args) { logConsole.log(...args); } +// Lazy getter for the JSON schema of cookie banner rules. It is used for +// validation of rules defined by pref. +XPCOMUtils.defineLazyGetter(lazy, "CookieBannerRuleSchema", async () => { + let response = await fetch( + "chrome://global/content/cookiebanners/CookieBannerRule.schema.json" + ); + if (!response.ok) { + log("Fetch for CookieBannerRuleSchema failed", response); + throw new Error("Failed to fetch CookieBannerRuleSchema."); + } + return response.json(); +}); + /** * See nsICookieBannerListService */ @@ -78,7 +89,7 @@ class CookieBannerListService { let rules = await this.#rs.get(); this.#importRules(rules); - this.#importTestRules(); + return this.#importTestRules(); } shutdown() { @@ -149,7 +160,7 @@ class CookieBannerListService { }); } - #importTestRules() { + async #importTestRules() { log("importTestRules"); if (!Services.prefs.prefHasUserValue(PREF_TEST_RULES)) { @@ -157,6 +168,7 @@ class CookieBannerListService { return; } + // Parse array of rules from pref value string as JSON. let testRules; try { testRules = JSON.parse(lazy.testRulesPref); @@ -165,11 +177,46 @@ class CookieBannerListService { Cu.reportError( `Failed to parse test rules JSON string. Make sure ${PREF_TEST_RULES} contains valid JSON. ${error?.name}` ); + return; + } + // Ensure we have an array we can iterate over and not an object. + if (!Array.isArray(testRules)) { + Cu.reportError("Failed to parse test rules JSON String: Not an array."); return; } - this.#importRules(testRules); + // Validate individual array elements (rules) via the schema defined in + // CookieBannerRule.schema.json. + // Allow extra properties. They will be discarded. + let schema = await lazy.CookieBannerRuleSchema; + let validator = new lazy.JsonSchema.Validator(schema); + let validatedTestRules = []; + + let i = 0; + for (let rule of testRules) { + let { valid, errors } = validator.validate(rule); + + if (!valid) { + Cu.reportError( + `Skipping invalid test rule at index ${i}. Errors: ${JSON.stringify( + errors, + null, + 2 + )}` + ); + log("Test rule validation error", rule, errors); + + i += 1; + continue; + } + + // Only import rules if they are valid. + validatedTestRules.push(rule); + i += 1; + } + + this.#importRules(validatedTestRules); } #importCookieRule(rule, cookies) { diff --git a/toolkit/components/cookiebanners/jar.mn b/toolkit/components/cookiebanners/jar.mn new file mode 100644 index 0000000000000..ab0184022b622 --- /dev/null +++ b/toolkit/components/cookiebanners/jar.mn @@ -0,0 +1,6 @@ +# 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/. + +toolkit.jar: + content/global/cookiebanners/CookieBannerRule.schema.json (schema/CookieBannerRule.schema.json) diff --git a/toolkit/components/cookiebanners/moz.build b/toolkit/components/cookiebanners/moz.build index 5f5fe8d8bc0ab..10612d8092c5b 100644 --- a/toolkit/components/cookiebanners/moz.build +++ b/toolkit/components/cookiebanners/moz.build @@ -7,6 +7,8 @@ with Files("**"): BUG_COMPONENT = ("Core", "Privacy: Anti-Tracking") +JAR_MANIFESTS += ["jar.mn"] + XPIDL_SOURCES += [ "nsIClickRule.idl", "nsICookieBannerListService.idl", diff --git a/toolkit/components/cookiebanners/schema/README b/toolkit/components/cookiebanners/schema/README index 6bef7145c9998..74bae192656d6 100644 --- a/toolkit/components/cookiebanners/schema/README +++ b/toolkit/components/cookiebanners/schema/README @@ -1,2 +1,3 @@ This directory contains the JSON schemas used for the Remote Settings collection -which stores cookie banner rules. It is not included in the build. +which stores cookie banner rules. It is used locally to validate test rules +imported via pref. From 6c1207ec2d40bd05aea0c5b0b14fc038c9ad48b4 Mon Sep 17 00:00:00 2001 From: Paul Zuehlcke Date: Wed, 31 Aug 2022 09:44:28 +0000 Subject: [PATCH 34/65] Bug 1785870 - Tests for cookie banner rule test pref. r=timhuang Depends on D155897 Differential Revision: https://phabricator.services.mozilla.com/D156001 --- .../cookiebanners/CookieBannerListService.jsm | 15 ++- .../nsICookieBannerListService.idl | 6 + .../test/unit/test_cookiebannerlistservice.js | 119 +++++++++++++++++- 3 files changed, 130 insertions(+), 10 deletions(-) diff --git a/toolkit/components/cookiebanners/CookieBannerListService.jsm b/toolkit/components/cookiebanners/CookieBannerListService.jsm index b415d7ac443c5..cf31deaa4998b 100644 --- a/toolkit/components/cookiebanners/CookieBannerListService.jsm +++ b/toolkit/components/cookiebanners/CookieBannerListService.jsm @@ -69,19 +69,24 @@ class CookieBannerListService { this.#rs = lazy.RemoteSettings(COLLECTION_NAME); } - init() { + async init() { log("init"); + await this.importAllRules(); + + // Register listener to import rules when test pref changes. + Services.prefs.addObserver(PREF_TEST_RULES, this); + // Register callback for collection changes. // Only register if not already registered. if (!this.#onSyncCallback) { this.#onSyncCallback = this.onSync.bind(this); this.#rs.on("sync", this.#onSyncCallback); } + } - Services.prefs.addObserver(PREF_TEST_RULES, this); - - return this.importAllRules(); + initForTest() { + return this.init(); } async importAllRules() { @@ -89,7 +94,7 @@ class CookieBannerListService { let rules = await this.#rs.get(); this.#importRules(rules); - return this.#importTestRules(); + await this.#importTestRules(); } shutdown() { diff --git a/toolkit/components/cookiebanners/nsICookieBannerListService.idl b/toolkit/components/cookiebanners/nsICookieBannerListService.idl index 67308a9c2c6e9..6ad0d749c8a83 100644 --- a/toolkit/components/cookiebanners/nsICookieBannerListService.idl +++ b/toolkit/components/cookiebanners/nsICookieBannerListService.idl @@ -15,6 +15,12 @@ interface nsICookieBannerListService : nsISupports { */ void init(); + /** + * Same as init but returns a promise which resolves once init is done. Used + * for testing when we need to wait for all rules to be imported. + */ + Promise initForTest(); + /** * Shutdown the service. This disables any rule updates. */ diff --git a/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js b/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js index 9b0cd7ce30b43..a27a041a11d91 100644 --- a/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js +++ b/toolkit/components/cookiebanners/test/unit/test_cookiebannerlistservice.js @@ -12,11 +12,15 @@ let { sinon } = ChromeUtils.import("resource://testing-common/Sinon.jsm"); // Name of the RemoteSettings collection containing the rules. const COLLECTION_NAME = "cookie-banner-rules-list"; +// Name of pref used to import test rules. +const PREF_TEST_RULES = "cookiebanners.listService.testRules"; + let rulesInserted = []; let rulesRemoved = []; let insertCallback = null; const RULE_A_ORIGINAL = { + id: "example.com", click: { optOut: "#fooOut", presence: "#foobar", @@ -35,6 +39,7 @@ const RULE_A_ORIGINAL = { }; const RULE_B = { + id: "example.org", click: { optOut: "#fooOutB", presence: "#foobarB", @@ -52,6 +57,7 @@ const RULE_B = { }; const RULE_C = { + id: "example.net", click: { optOut: "#fooOutC", presence: "#foobarC", @@ -70,6 +76,7 @@ const RULE_C = { }; const RULE_A_UPDATED = { + id: "example.com", click: { optOut: "#fooOut", optIn: "#barIn", @@ -91,10 +98,24 @@ const RULE_A_UPDATED = { }, }; +const INVALID_RULE_CLICK = { + id: "foobar.com", + domain: "foobar.com", + click: { + presence: 1, + optIn: "#foo", + }, +}; + +const INVALID_RULE_EMPTY = {}; + // Testing with RemoteSettings requires a profile. do_get_profile(); add_setup(async () => { + // Enable debug logging. + Services.prefs.setStringPref("cookiebanners.listService.logLevel", "Debug"); + // Stub some nsICookieBannerService methods for easy testing. let removeRuleForDomain = sinon.stub().callsFake(domain => { rulesRemoved.push(domain); @@ -106,11 +127,12 @@ add_setup(async () => { }); let oldCookieBanners = Services.cookieBanners; - Services.cookieBanners = { insertRule, removeRuleForDomain }; + Services.cookieBanners = { insertRule, removeRuleForDomain, resetRules() {} }; // Remove stubs on test end. registerCleanupFunction(() => { Services.cookieBanners = oldCookieBanners; + Services.prefs.clearUserPref("cookiebanners.listService.logLevel"); }); }); @@ -156,7 +178,7 @@ add_task(async function test_initial_import() { let cookieBannerListService = Cc[ "@mozilla.org/cookie-banner-list-service;1" ].getService(Ci.nsICookieBannerListService); - cookieBannerListService.init(); + await cookieBannerListService.initForTest(); await insertPromise; @@ -206,10 +228,11 @@ add_task(async function test_initial_import() { // Cleanup cookieBannerListService.shutdown(); - db.clear(); - await db.importChanges({}, Date.now()); rulesInserted = []; rulesRemoved = []; + + db.clear(); + await db.importChanges({}, Date.now()); }); /** @@ -221,7 +244,7 @@ add_task(async function test_remotesettings_sync() { let cookieBannerListService = Cc[ "@mozilla.org/cookie-banner-list-service;1" ].getService(Ci.nsICookieBannerListService); - cookieBannerListService.init(); + await cookieBannerListService.initForTest(); const payload = { current: [RULE_A_ORIGINAL, RULE_C], @@ -276,4 +299,90 @@ add_task(async function test_remotesettings_sync() { cookieBannerListService.shutdown(); rulesInserted = []; rulesRemoved = []; + + let { db } = RemoteSettings(COLLECTION_NAME); + db.clear(); + await db.importChanges({}, Date.now()); +}); + +/** + * Tests the cookie banner rule test pref. + */ +add_task(async function test_rule_test_pref() { + Services.prefs.setStringPref( + PREF_TEST_RULES, + JSON.stringify([RULE_A_ORIGINAL, RULE_B]) + ); + + Assert.equal(rulesInserted.length, 0, "No inserted rules initially."); + Assert.equal(rulesRemoved.length, 0, "No removed rules initially."); + + let insertPromise = waitForInsert(() => rulesInserted.length >= 2); + + // Initialize the cookie banner list service so it imports test rules and listens for pref changes. + let cookieBannerListService = Cc[ + "@mozilla.org/cookie-banner-list-service;1" + ].getService(Ci.nsICookieBannerListService); + await cookieBannerListService.initForTest(); + + info("Wait for rules to be inserted"); + await insertPromise; + + Assert.equal(rulesInserted.length, 2, "Should have inserted two rules."); + Assert.equal(rulesRemoved.length, 0, "Should not have removed any rules."); + + Assert.ok( + rulesInserted.some(rule => rule.domain == RULE_A_ORIGINAL.domain), + "Should have inserted RULE_A" + ); + Assert.ok( + rulesInserted.some(rule => rule.domain == RULE_B.domain), + "Should have inserted RULE_B" + ); + + rulesInserted = []; + rulesRemoved = []; + + let insertPromise2 = waitForInsert(() => rulesInserted.length >= 3); + + info( + "Updating test rules via pref. The list service should detect the pref change." + ); + // This includes some invalid rules, they should be skipped. + Services.prefs.setStringPref( + PREF_TEST_RULES, + JSON.stringify([ + RULE_A_ORIGINAL, + RULE_B, + INVALID_RULE_EMPTY, + RULE_C, + INVALID_RULE_CLICK, + ]) + ); + + info("Wait for rules to be inserted"); + await insertPromise2; + + Assert.equal(rulesInserted.length, 3, "Should have inserted three rules."); + Assert.equal(rulesRemoved.length, 0, "Should not have removed any rules."); + + Assert.ok( + rulesInserted.some(rule => rule.domain == RULE_A_ORIGINAL.domain), + "Should have inserted RULE_A" + ); + Assert.ok( + rulesInserted.some(rule => rule.domain == RULE_B.domain), + "Should have inserted RULE_B" + ); + Assert.ok( + rulesInserted.some(rule => rule.domain == RULE_C.domain), + "Should have inserted RULE_C" + ); + + // Cleanup + cookieBannerListService.shutdown(); + rulesInserted = []; + rulesRemoved = []; + + Services.prefs.clearUserPref(PREF_TEST_RULES); }); From 77a54eb6f8cf26909d618820b2f3fd3ca3ba4520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 09:53:24 +0000 Subject: [PATCH 35/65] Bug 1788039: Avoid unnecessary stack operations for post-increment/decrement. r=arai Pass through `ValueUsage` to determine when the expression result is unused. When the result isn't used, we can then avoid having to emit dup/pop byte codes. Differential Revision: https://phabricator.services.mozilla.com/D155963 --- js/src/frontend/BytecodeEmitter.cpp | 30 +++++++++++++++------------- js/src/frontend/BytecodeEmitter.h | 12 ++++++----- js/src/frontend/ElemOpEmitter.cpp | 6 +++--- js/src/frontend/ElemOpEmitter.h | 3 ++- js/src/frontend/NameOpEmitter.cpp | 16 ++++++++------- js/src/frontend/NameOpEmitter.h | 3 ++- js/src/frontend/PrivateOpEmitter.cpp | 6 +++--- js/src/frontend/PrivateOpEmitter.h | 3 ++- js/src/frontend/PropOpEmitter.cpp | 7 ++++--- js/src/frontend/PropOpEmitter.h | 4 +++- js/src/frontend/ValueUsage.h | 6 ++++-- 11 files changed, 55 insertions(+), 41 deletions(-) diff --git a/js/src/frontend/BytecodeEmitter.cpp b/js/src/frontend/BytecodeEmitter.cpp index 43c8443f2cc8a..8aa682c0e5f93 100644 --- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -1684,7 +1684,7 @@ bool BytecodeEmitter::emitPropLHS(PropertyAccess* prop) { return true; } -bool BytecodeEmitter::emitPropIncDec(UnaryNode* incDec) { +bool BytecodeEmitter::emitPropIncDec(UnaryNode* incDec, ValueUsage valueUsage) { PropertyAccess* prop = &incDec->kid()->as(); bool isSuper = prop->isSuper(); ParseNodeKind kind = incDec->getKind(); @@ -1713,7 +1713,7 @@ bool BytecodeEmitter::emitPropIncDec(UnaryNode* incDec) { return false; } } - if (!poe.emitIncDec(prop->key().atom())) { + if (!poe.emitIncDec(prop->key().atom(), valueUsage)) { // [stack] RESULT return false; } @@ -1721,7 +1721,7 @@ bool BytecodeEmitter::emitPropIncDec(UnaryNode* incDec) { return true; } -bool BytecodeEmitter::emitNameIncDec(UnaryNode* incDec) { +bool BytecodeEmitter::emitNameIncDec(UnaryNode* incDec, ValueUsage valueUsage) { MOZ_ASSERT(incDec->kid()->isKind(ParseNodeKind::Name)); ParseNodeKind kind = incDec->getKind(); @@ -1734,7 +1734,7 @@ bool BytecodeEmitter::emitNameIncDec(UnaryNode* incDec) { : kind == ParseNodeKind::PostDecrementExpr ? NameOpEmitter::Kind::PostDecrement : NameOpEmitter::Kind::PreDecrement); - if (!noe.emitIncDec()) { + if (!noe.emitIncDec(valueUsage)) { return false; } @@ -1829,7 +1829,7 @@ static PrivateOpEmitter::Kind PrivateConvertIncDecKind(ParseNodeKind kind) { } } -bool BytecodeEmitter::emitElemIncDec(UnaryNode* incDec) { +bool BytecodeEmitter::emitElemIncDec(UnaryNode* incDec, ValueUsage valueUsage) { PropertyByValue* elemExpr = &incDec->kid()->as(); bool isSuper = elemExpr->isSuper(); MOZ_ASSERT(!elemExpr->key().isKind(ParseNodeKind::PrivateName)); @@ -1844,7 +1844,7 @@ bool BytecodeEmitter::emitElemIncDec(UnaryNode* incDec) { // [stack] OBJ KEY return false; } - if (!eoe.emitIncDec()) { + if (!eoe.emitIncDec(valueUsage)) { // [stack] RESULT return false; } @@ -1874,7 +1874,8 @@ bool BytecodeEmitter::emitCallIncDec(UnaryNode* incDec) { return emit2(JSOp::ThrowMsg, uint8_t(ThrowMsgKind::AssignToCall)); } -bool BytecodeEmitter::emitPrivateIncDec(UnaryNode* incDec) { +bool BytecodeEmitter::emitPrivateIncDec(UnaryNode* incDec, + ValueUsage valueUsage) { PrivateMemberAccess* privateExpr = &incDec->kid()->as(); ParseNodeKind kind = incDec->getKind(); PrivateOpEmitter xoe(this, PrivateConvertIncDecKind(kind), @@ -1887,7 +1888,7 @@ bool BytecodeEmitter::emitPrivateIncDec(UnaryNode* incDec) { // [stack] OBJ NAME return false; } - if (!xoe.emitIncDec()) { + if (!xoe.emitIncDec(valueUsage)) { // [stack] RESULT return false; } @@ -8570,18 +8571,19 @@ bool BytecodeEmitter::emitSequenceExpr( // Using MOZ_NEVER_INLINE in here is a workaround for llvm.org/pr14047. See // the comment on emitSwitch. -MOZ_NEVER_INLINE bool BytecodeEmitter::emitIncOrDec(UnaryNode* incDec) { +MOZ_NEVER_INLINE bool BytecodeEmitter::emitIncOrDec(UnaryNode* incDec, + ValueUsage valueUsage) { switch (incDec->kid()->getKind()) { case ParseNodeKind::DotExpr: - return emitPropIncDec(incDec); + return emitPropIncDec(incDec, valueUsage); case ParseNodeKind::ElemExpr: - return emitElemIncDec(incDec); + return emitElemIncDec(incDec, valueUsage); case ParseNodeKind::PrivateMemberExpr: - return emitPrivateIncDec(incDec); + return emitPrivateIncDec(incDec, valueUsage); case ParseNodeKind::CallExpr: return emitCallIncDec(incDec); default: - return emitNameIncDec(incDec); + return emitNameIncDec(incDec, valueUsage); } } @@ -11245,7 +11247,7 @@ bool BytecodeEmitter::emitTree( case ParseNodeKind::PreDecrementExpr: case ParseNodeKind::PostIncrementExpr: case ParseNodeKind::PostDecrementExpr: - if (!emitIncOrDec(&pn->as())) { + if (!emitIncOrDec(&pn->as(), valueUsage)) { return false; } break; diff --git a/js/src/frontend/BytecodeEmitter.h b/js/src/frontend/BytecodeEmitter.h index 114a59ed72306..cea76b6335670 100644 --- a/js/src/frontend/BytecodeEmitter.h +++ b/js/src/frontend/BytecodeEmitter.h @@ -692,7 +692,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter { const NameLocation& loc, ValueIsOnStack isOnStack); - [[nodiscard]] bool emitNameIncDec(UnaryNode* incDec); + [[nodiscard]] bool emitNameIncDec(UnaryNode* incDec, ValueUsage valueUsage); [[nodiscard]] bool emitDeclarationList(ListNode* declList); [[nodiscard]] bool emitSingleDeclaration(ListNode* declList, NameNode* decl, @@ -733,7 +733,7 @@ struct MOZ_STACK_CLASS BytecodeEmitter { [[nodiscard]] bool emitPushResumeKind(GeneratorResumeKind kind); [[nodiscard]] bool emitPropLHS(PropertyAccess* prop); - [[nodiscard]] bool emitPropIncDec(UnaryNode* incDec); + [[nodiscard]] bool emitPropIncDec(UnaryNode* incDec, ValueUsage valueUsage); [[nodiscard]] bool emitComputedPropertyName(UnaryNode* computedPropName); @@ -751,10 +751,11 @@ struct MOZ_STACK_CLASS BytecodeEmitter { ElemOpEmitter& eoe); [[nodiscard]] bool emitElemOpBase(JSOp op); - [[nodiscard]] bool emitElemIncDec(UnaryNode* incDec); + [[nodiscard]] bool emitElemIncDec(UnaryNode* incDec, ValueUsage valueUsage); [[nodiscard]] bool emitObjAndPrivateName(PrivateMemberAccess* elem, ElemOpEmitter& eoe); - [[nodiscard]] bool emitPrivateIncDec(UnaryNode* incDec); + [[nodiscard]] bool emitPrivateIncDec(UnaryNode* incDec, + ValueUsage valueUsage); [[nodiscard]] bool emitCatch(BinaryNode* catchClause); [[nodiscard]] bool emitIf(TernaryNode* ifNode); @@ -915,7 +916,8 @@ struct MOZ_STACK_CLASS BytecodeEmitter { [[nodiscard]] bool emitSequenceExpr( ListNode* node, ValueUsage valueUsage = ValueUsage::WantValue); - [[nodiscard]] MOZ_NEVER_INLINE bool emitIncOrDec(UnaryNode* incDec); + [[nodiscard]] MOZ_NEVER_INLINE bool emitIncOrDec(UnaryNode* incDec, + ValueUsage valueUsage); [[nodiscard]] bool emitConditionalExpression( ConditionalExpression& conditional, diff --git a/js/src/frontend/ElemOpEmitter.cpp b/js/src/frontend/ElemOpEmitter.cpp index 7fdcf89990f08..0d563e466da92 100644 --- a/js/src/frontend/ElemOpEmitter.cpp +++ b/js/src/frontend/ElemOpEmitter.cpp @@ -203,7 +203,7 @@ bool ElemOpEmitter::emitAssignment() { return true; } -bool ElemOpEmitter::emitIncDec() { +bool ElemOpEmitter::emitIncDec(ValueUsage valueUsage) { MOZ_ASSERT(state_ == State::Key); MOZ_ASSERT(isIncDec()); @@ -219,7 +219,7 @@ bool ElemOpEmitter::emitIncDec() { // [stack] ... N return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { // [stack] OBJ KEY SUPERBASE? N if (!bce_->emit1(JSOp::Dup)) { // [stack] ... N N @@ -243,7 +243,7 @@ bool ElemOpEmitter::emitIncDec() { // [stack] N? N+1 return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { if (!bce_->emit1(JSOp::Pop)) { // [stack] N return false; diff --git a/js/src/frontend/ElemOpEmitter.h b/js/src/frontend/ElemOpEmitter.h index 3f664d54034d6..cf528bf3125f1 100644 --- a/js/src/frontend/ElemOpEmitter.h +++ b/js/src/frontend/ElemOpEmitter.h @@ -13,6 +13,7 @@ namespace js { namespace frontend { struct BytecodeEmitter; +enum class ValueUsage; // Class for emitting bytecode for element operation. // @@ -257,7 +258,7 @@ class MOZ_STACK_CLASS ElemOpEmitter { [[nodiscard]] bool emitAssignment(); - [[nodiscard]] bool emitIncDec(); + [[nodiscard]] bool emitIncDec(ValueUsage valueUsage); }; } /* namespace frontend */ diff --git a/js/src/frontend/NameOpEmitter.cpp b/js/src/frontend/NameOpEmitter.cpp index 109f9a0ea25de..074aaabf6d865 100644 --- a/js/src/frontend/NameOpEmitter.cpp +++ b/js/src/frontend/NameOpEmitter.cpp @@ -11,6 +11,7 @@ #include "frontend/ParserAtom.h" // ParserAtom #include "frontend/SharedContext.h" #include "frontend/TDZCheckCache.h" +#include "frontend/ValueUsage.h" #include "vm/Opcodes.h" using namespace js; @@ -412,7 +413,7 @@ bool NameOpEmitter::emitAssignment() { return true; } -bool NameOpEmitter::emitIncDec() { +bool NameOpEmitter::emitIncDec(ValueUsage valueUsage) { MOZ_ASSERT(state_ == State::Start); JSOp incOp = isInc() ? JSOp::Inc : JSOp::Dec; @@ -424,9 +425,9 @@ bool NameOpEmitter::emitIncDec() { // [stack] ENV? N return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { if (!bce_->emit1(JSOp::Dup)) { - // [stack] ENV? N? N + // [stack] ENV? N N return false; } } @@ -434,13 +435,14 @@ bool NameOpEmitter::emitIncDec() { // [stack] ENV? N? N+1 return false; } - if (isPostIncDec() && emittedBindOp()) { + if (isPostIncDec() && emittedBindOp() && + valueUsage == ValueUsage::WantValue) { if (!bce_->emit2(JSOp::Pick, 2)) { - // [stack] N? N+1 ENV? + // [stack] N N+1 ENV return false; } if (!bce_->emit1(JSOp::Swap)) { - // [stack] N? ENV? N+1 + // [stack] N ENV N+1 return false; } } @@ -448,7 +450,7 @@ bool NameOpEmitter::emitIncDec() { // [stack] N? N+1 return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { if (!bce_->emit1(JSOp::Pop)) { // [stack] N return false; diff --git a/js/src/frontend/NameOpEmitter.h b/js/src/frontend/NameOpEmitter.h index 06026b472a14c..62d632a321b08 100644 --- a/js/src/frontend/NameOpEmitter.h +++ b/js/src/frontend/NameOpEmitter.h @@ -17,6 +17,7 @@ namespace js { namespace frontend { struct BytecodeEmitter; +enum class ValueUsage; // Class for emitting bytecode for name operation. // @@ -172,7 +173,7 @@ class MOZ_STACK_CLASS NameOpEmitter { [[nodiscard]] bool emitGet(); [[nodiscard]] bool prepareForRhs(); [[nodiscard]] bool emitAssignment(); - [[nodiscard]] bool emitIncDec(); + [[nodiscard]] bool emitIncDec(ValueUsage valueUsage); }; } /* namespace frontend */ diff --git a/js/src/frontend/PrivateOpEmitter.cpp b/js/src/frontend/PrivateOpEmitter.cpp index 98211383ee8e8..9e617b209ed8a 100644 --- a/js/src/frontend/PrivateOpEmitter.cpp +++ b/js/src/frontend/PrivateOpEmitter.cpp @@ -263,7 +263,7 @@ bool PrivateOpEmitter::emitAssignment() { return true; } -bool PrivateOpEmitter::emitIncDec() { +bool PrivateOpEmitter::emitIncDec(ValueUsage valueUsage) { MOZ_ASSERT(state_ == State::Reference); MOZ_ASSERT(isIncDec()); // [stack] OBJ NAME @@ -286,7 +286,7 @@ bool PrivateOpEmitter::emitIncDec() { // [stack] OBJ NAME N return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { // [stack] OBJ NAME N if (!bce_->emit1(JSOp::Dup)) { // [stack] OBJ NAME N N @@ -320,7 +320,7 @@ bool PrivateOpEmitter::emitIncDec() { } } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { if (!bce_->emit1(JSOp::Pop)) { // [stack] N return false; diff --git a/js/src/frontend/PrivateOpEmitter.h b/js/src/frontend/PrivateOpEmitter.h index f9d314bbd7ebf..558541b05c36b 100644 --- a/js/src/frontend/PrivateOpEmitter.h +++ b/js/src/frontend/PrivateOpEmitter.h @@ -19,6 +19,7 @@ namespace js { namespace frontend { struct BytecodeEmitter; +enum class ValueUsage; // Class for emitting bytecode for operations on private members of objects. // @@ -219,7 +220,7 @@ class MOZ_STACK_CLASS PrivateOpEmitter { [[nodiscard]] bool emitGet(); [[nodiscard]] bool emitGetForCallOrNew(); [[nodiscard]] bool emitAssignment(); - [[nodiscard]] bool emitIncDec(); + [[nodiscard]] bool emitIncDec(ValueUsage valueUsage); [[nodiscard]] size_t numReferenceSlots() { return 2; } }; diff --git a/js/src/frontend/PropOpEmitter.cpp b/js/src/frontend/PropOpEmitter.cpp index 9368cece9df37..ff08bd85804c3 100644 --- a/js/src/frontend/PropOpEmitter.cpp +++ b/js/src/frontend/PropOpEmitter.cpp @@ -189,7 +189,8 @@ bool PropOpEmitter::emitAssignment(TaggedParserAtomIndex prop) { return true; } -bool PropOpEmitter::emitIncDec(TaggedParserAtomIndex prop) { +bool PropOpEmitter::emitIncDec(TaggedParserAtomIndex prop, + ValueUsage valueUsage) { MOZ_ASSERT(state_ == State::Obj); MOZ_ASSERT(isIncDec()); @@ -205,7 +206,7 @@ bool PropOpEmitter::emitIncDec(TaggedParserAtomIndex prop) { // [stack] ... N return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { // [stack] OBJ SUPERBASE? N if (!bce_->emit1(JSOp::Dup)) { // [stack] .. N N @@ -229,7 +230,7 @@ bool PropOpEmitter::emitIncDec(TaggedParserAtomIndex prop) { // [stack] N? N+1 return false; } - if (isPostIncDec()) { + if (isPostIncDec() && valueUsage == ValueUsage::WantValue) { if (!bce_->emit1(JSOp::Pop)) { // [stack] N return false; diff --git a/js/src/frontend/PropOpEmitter.h b/js/src/frontend/PropOpEmitter.h index dc39c165b6b9b..76402025ea0ec 100644 --- a/js/src/frontend/PropOpEmitter.h +++ b/js/src/frontend/PropOpEmitter.h @@ -16,6 +16,7 @@ namespace frontend { struct BytecodeEmitter; class TaggedParserAtomIndex; +enum class ValueUsage; // Class for emitting bytecode for property operation. // @@ -243,7 +244,8 @@ class MOZ_STACK_CLASS PropOpEmitter { // `prop` can be nullptr for CompoundAssignment. [[nodiscard]] bool emitAssignment(TaggedParserAtomIndex prop); - [[nodiscard]] bool emitIncDec(TaggedParserAtomIndex prop); + [[nodiscard]] bool emitIncDec(TaggedParserAtomIndex prop, + ValueUsage valueUsage); }; } /* namespace frontend */ diff --git a/js/src/frontend/ValueUsage.h b/js/src/frontend/ValueUsage.h index 98e0ab273aa1f..562272c5f8a01 100644 --- a/js/src/frontend/ValueUsage.h +++ b/js/src/frontend/ValueUsage.h @@ -10,10 +10,12 @@ namespace js { namespace frontend { -// Used to control whether JSOp::CallIgnoresRv is emitted for function calls. +// Used to control whether the expression result is used. This enables various +// optimizations, for example it allows to emit JSOp::CallIgnoresRv for function +// calls. enum class ValueUsage { // Assume the value of the current expression may be used. This is always - // correct but prohibits JSOp::CallIgnoresRv. + // correct but prohibits optimizations like JSOp::CallIgnoresRv. WantValue, // Pass this when emitting an expression if the expression's value is From afc1a8350f72148bf14b82513afcd117e4fa7e73 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 09:53:27 +0000 Subject: [PATCH 36/65] Bug 1787546: Handle custom added "cause" property in structured clone of Error objects. r=sfink Differential Revision: https://phabricator.services.mozilla.com/D155835 --- .../jit-test/tests/structured-clone/errors.js | 21 +++++++++++++++++++ js/src/vm/StructuredClone.cpp | 11 +++++++++- 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/js/src/jit-test/tests/structured-clone/errors.js b/js/src/jit-test/tests/structured-clone/errors.js index 7c58b2095a351..56dfc05e3f10d 100644 --- a/js/src/jit-test/tests/structured-clone/errors.js +++ b/js/src/jit-test/tests/structured-clone/errors.js @@ -58,6 +58,27 @@ for (let constructor of constructors) { assertEq(cloned.stack, error.stack); assertEq(cloned.stack === undefined, false); + // |cause| property, manually added after construction. + error = new constructor("hello"); + error.cause = new Error("foobar"); + assertDeepEq(Object.getOwnPropertyDescriptor(error, "cause"), { + value: error.cause, + writable: true, + enumerable: true, + configurable: true, + }); + cloned = roundtrip(error); + assertDeepEq(Object.getOwnPropertyDescriptor(cloned, "cause"), { + value: cloned.cause, + writable: true, + enumerable: false, // Non-enumerable in the cloned object! + configurable: true, + }); + assertEq(cloned.hasOwnProperty('message'), true); + assertEq(cloned instanceof constructor, true); + assertEq(cloned.stack, error.stack); + assertEq(cloned.stack === undefined, false); + // Subclassing error = new (class MyError extends constructor {}); cloned = roundtrip(error); diff --git a/js/src/vm/StructuredClone.cpp b/js/src/vm/StructuredClone.cpp index 337fb61087fc0..559fd5dad73ea 100644 --- a/js/src/vm/StructuredClone.cpp +++ b/js/src/vm/StructuredClone.cpp @@ -1917,7 +1917,16 @@ bool JSStructuredCloneWriter::traverseError(HandleObject obj) { // Non-standard: Serialize |cause|. Because this property // might be missing we also write "hasCause" later. - Rooted> cause(cx, unwrapped->getCause()); + RootedId causeId(cx, NameToId(cx->names().cause)); + Rooted> causeDesc(cx); + if (!GetOwnPropertyDescriptor(cx, obj, causeId, &causeDesc)) { + return false; + } + + Rooted> cause(cx); + if (causeDesc.isSome() && causeDesc->isDataDescriptor()) { + cause = mozilla::Some(causeDesc->value()); + } if (!cx->compartment()->wrap(cx, &cause)) { return false; } From 7e916cece1a6f2d40a72ca2d9b9dc301f3a36c22 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 Aug 2022 09:59:32 +0000 Subject: [PATCH 37/65] Bug 1785018 - Avoid data race when calling GetParent for nsLocalFileUnix r=necko-reviewers,kershaw We avoid the race by cloning the file before calling GetParent, to avoid writing to mCacheDirectory's buffer on the main thread during shutdown, when called from SyncRemoveAllCacheFiles. Additionally, we dispatch OnDelayedStartupFinished to the IO thread to avoid accessing mCacheDirectory on the main thread. Differential Revision: https://phabricator.services.mozilla.com/D155798 --- netwerk/cache2/CacheFileIOManager.cpp | 28 +++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/netwerk/cache2/CacheFileIOManager.cpp b/netwerk/cache2/CacheFileIOManager.cpp index e16e49890036a..6f5287296f527 100644 --- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -1345,17 +1345,25 @@ nsresult CacheFileIOManager::OnProfile() { // static nsresult CacheFileIOManager::OnDelayedStartupFinished() { - if (NS_WARN_IF(!gInstance)) { - return NS_ERROR_NOT_AVAILABLE; + if (!StaticPrefs::network_cache_shutdown_purge_in_background_task()) { + return NS_OK; } - if (StaticPrefs::network_cache_shutdown_purge_in_background_task()) { - // TODO: run on another thread: Check if there are any old cache dirs. - // Report telemetry. - gInstance->DispatchPurgeTask(""_ns, "0"_ns, kPurgeExtension); + RefPtr ioMan = gInstance; + nsCOMPtr target = IOTarget(); + if (NS_WARN_IF(!ioMan || !target)) { + return NS_ERROR_NOT_AVAILABLE; } - return NS_OK; + return target->Dispatch( + NS_NewRunnableFunction("CacheFileIOManager::OnDelayedStartupFinished", + [ioMan = RefPtr{ioMan}] { + // TODO: Check if there are any old cache dirs. + // Report telemetry. + gInstance->DispatchPurgeTask(""_ns, "0"_ns, + kPurgeExtension); + }), + nsIEventTarget::DISPATCH_NORMAL); } // static @@ -4051,8 +4059,12 @@ nsresult CacheFileIOManager::DispatchPurgeTask( return NS_ERROR_NOT_IMPLEMENTED; #endif + nsCOMPtr cacheDir; + rv = mCacheDirectory->Clone(getter_AddRefs(cacheDir)); + NS_ENSURE_SUCCESS(rv, rv); + nsCOMPtr profileDir; - rv = mCacheDirectory->GetParent(getter_AddRefs(profileDir)); + rv = cacheDir->GetParent(getter_AddRefs(profileDir)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr lf; From 9523a4e42bed7f35090816e995eb4faa1782bec2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 10:38:00 +0000 Subject: [PATCH 38/65] Bug 1564347 - Part 2: Add AllocateThinOrFatInlineString. r=jandem Move inline string allocation into a separate function. Differential Revision: https://phabricator.services.mozilla.com/D155388 --- js/src/jit/CodeGenerator.cpp | 70 +++++++++++++++++++++++------------- 1 file changed, 46 insertions(+), 24 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 9994141814216..ff01e7fd054c1 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -10386,21 +10386,25 @@ static void CopyStringCharsMaybeInflate(MacroAssembler& masm, Register input, masm.bind(&done); } -static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, - Register output, Register temp1, Register temp2, - Register temp3, - gc::InitialHeap initialStringHeap, - Label* failure, CharEncoding encoding) { - JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", - (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); - - // State: result length in temp2. +static void AllocateThinOrFatInlineString(MacroAssembler& masm, Register output, + Register length, Register temp, + gc::InitialHeap initialStringHeap, + Label* failure, + CharEncoding encoding) { +#ifdef DEBUG + size_t maxInlineLength; + if (encoding == CharEncoding::Latin1) { + maxInlineLength = JSFatInlineString::MAX_LENGTH_LATIN1; + } else { + maxInlineLength = JSFatInlineString::MAX_LENGTH_TWO_BYTE; + } - // Ensure both strings are linear. - masm.branchIfRope(lhs, failure); - masm.branchIfRope(rhs, failure); + Label ok; + masm.branch32(Assembler::BelowOrEqual, length, Imm32(maxInlineLength), &ok); + masm.assumeUnreachable("string length too large to be allocated as inline"); + masm.bind(&ok); +#endif - // Allocate a JSThinInlineString or JSFatInlineString. size_t maxThinInlineLength; if (encoding == CharEncoding::Latin1) { maxThinInlineLength = JSThinInlineString::MAX_LENGTH_LATIN1; @@ -10409,13 +10413,13 @@ static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, } Label isFat, allocDone; - masm.branch32(Assembler::Above, temp2, Imm32(maxThinInlineLength), &isFat); + masm.branch32(Assembler::Above, length, Imm32(maxThinInlineLength), &isFat); { uint32_t flags = JSString::INIT_THIN_INLINE_FLAGS; if (encoding == CharEncoding::Latin1) { flags |= JSString::LATIN1_CHARS_BIT; } - masm.newGCString(output, temp1, initialStringHeap, failure); + masm.newGCString(output, temp, initialStringHeap, failure); masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); masm.jump(&allocDone); } @@ -10425,13 +10429,32 @@ static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, if (encoding == CharEncoding::Latin1) { flags |= JSString::LATIN1_CHARS_BIT; } - masm.newGCFatInlineString(output, temp1, initialStringHeap, failure); + masm.newGCFatInlineString(output, temp, initialStringHeap, failure); masm.store32(Imm32(flags), Address(output, JSString::offsetOfFlags())); } masm.bind(&allocDone); // Store length. - masm.store32(temp2, Address(output, JSString::offsetOfLength())); + masm.store32(length, Address(output, JSString::offsetOfLength())); +} + +static void ConcatInlineString(MacroAssembler& masm, Register lhs, Register rhs, + Register output, Register temp1, Register temp2, + Register temp3, + gc::InitialHeap initialStringHeap, + Label* failure, CharEncoding encoding) { + JitSpew(JitSpew_Codegen, "# Emitting ConcatInlineString (encoding=%s)", + (encoding == CharEncoding::Latin1 ? "Latin-1" : "Two-Byte")); + + // State: result length in temp2. + + // Ensure both strings are linear. + masm.branchIfRope(lhs, failure); + masm.branchIfRope(rhs, failure); + + // Allocate a JSThinInlineString or JSFatInlineString. + AllocateThinOrFatInlineString(masm, output, temp2, temp1, initialStringHeap, + failure, encoding); // Load chars pointer in temp2. masm.loadInlineStringCharsForStore(output, temp2); @@ -10638,9 +10661,9 @@ JitCode* JitRealm::generateStringConcatStub(JSContext* cx) { masm.add32(temp1, temp2); - // Check if we can use a JSFatInlineString. The result is a Latin1 string if + // Check if we can use a JSInlineString. The result is a Latin1 string if // lhs and rhs are both Latin1, so we AND the flags. - Label isFatInlineTwoByte, isFatInlineLatin1; + Label isInlineTwoByte, isInlineLatin1; masm.load32(Address(lhs, JSString::offsetOfFlags()), temp1); masm.and32(Address(rhs, JSString::offsetOfFlags()), temp1); @@ -10650,14 +10673,13 @@ JitCode* JitRealm::generateStringConcatStub(JSContext* cx) { { masm.branch32(Assembler::BelowOrEqual, temp2, Imm32(JSFatInlineString::MAX_LENGTH_TWO_BYTE), - &isFatInlineTwoByte); + &isInlineTwoByte); masm.jump(¬Inline); } masm.bind(&isLatin1); { masm.branch32(Assembler::BelowOrEqual, temp2, - Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), - &isFatInlineLatin1); + Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), &isInlineLatin1); } masm.bind(¬Inline); @@ -10691,11 +10713,11 @@ JitCode* JitRealm::generateStringConcatStub(JSContext* cx) { masm.mov(lhs, output); masm.ret(); - masm.bind(&isFatInlineTwoByte); + masm.bind(&isInlineTwoByte); ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, initialStringHeap, &failure, CharEncoding::TwoByte); - masm.bind(&isFatInlineLatin1); + masm.bind(&isInlineLatin1); ConcatInlineString(masm, lhs, rhs, output, temp1, temp2, temp3, initialStringHeap, &failure, CharEncoding::Latin1); From fe86ad544c3f26f443573b77dc7713e29c2da4e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 10:38:01 +0000 Subject: [PATCH 39/65] Bug 1564347 - Part 3: Inline String.prototype.toLowerCase for small Latin-1 strings. r=jandem Differential Revision: https://phabricator.services.mozilla.com/D37377 --- .../tests/warp/string-tolowercase-latin1.js | 76 +++++++++ js/src/jit/CodeGenerator.cpp | 161 +++++++++++++++++- js/src/jit/LIROps.yaml | 15 +- js/src/jit/Lowering.cpp | 24 ++- js/src/jit/arm64/Lowering-arm64.cpp | 2 + js/src/jit/arm64/Lowering-arm64.h | 1 + js/src/jit/x86/Lowering-x86.cpp | 2 +- 7 files changed, 267 insertions(+), 14 deletions(-) create mode 100644 js/src/jit-test/tests/warp/string-tolowercase-latin1.js diff --git a/js/src/jit-test/tests/warp/string-tolowercase-latin1.js b/js/src/jit-test/tests/warp/string-tolowercase-latin1.js new file mode 100644 index 0000000000000..3ef6151eb57ed --- /dev/null +++ b/js/src/jit-test/tests/warp/string-tolowercase-latin1.js @@ -0,0 +1,76 @@ +// Test inline lower case conversion of ASCII / Latin-1 strings. + +function* characters(...ranges) { + for (let [start, end] of ranges) { + for (let i = start; i <= end; ++i) { + yield i; + } + } +} + +const ascii_upper = [...characters( + [0x41, 0x5A], // A..Z + [0x30, 0x39], // 0..9 +)]; + +const ascii_lower = [...characters( + [0x61, 0x7A], // a..z + [0x30, 0x39], // 0..9 +)]; + +const latin1_upper = [...characters( + [0xC0, 0xDE], // À..Þ + [0x30, 0x39], // 0..9 +)]; + +const latin1_lower = [...characters( + [0xDF, 0xFF], // ß..ÿ +)]; + +for (let upper of [ascii_upper, latin1_upper]) { + let s = String.fromCodePoint(...upper); + assertEq(isLatin1(s), true); + assertEq(s, s.toUpperCase()); +} + +for (let lower of [ascii_lower, latin1_lower]) { + let s = String.fromCodePoint(...lower); + assertEq(isLatin1(s), true); + assertEq(s, s.toLowerCase()); +} + +for (let i = 0; i <= 32; ++i) { + let strings = [ascii_upper, ascii_lower, latin1_upper, latin1_lower].flatMap(codePoints => [ + String.fromCodePoint(...codePoints.slice(0, i)), + + // Trailing ASCII upper case character. + String.fromCodePoint(...codePoints.slice(0, i)) + "A", + + // Trailing ASCII lower case character. + String.fromCodePoint(...codePoints.slice(0, i)) + "a", + + // Trailing Latin-1 upper case character. + String.fromCodePoint(...codePoints.slice(0, i)) + "À", + + // Trailing Latin-1 lower case character. + String.fromCodePoint(...codePoints.slice(0, i)) + "ß", + ]).flatMap(x => [ + x, + newRope(x, ""), + newString(x, {twoByte: true}), + ]); + + const expected = strings.map(x => { + // Prevent Warp compilation when computing the expected results. + with ({}) ; + return x.toLowerCase(); + }); + + for (let i = 0; i < 1000; ++i) { + let idx = i % strings.length; + let str = strings[idx]; + + let actual = str.toLowerCase(); + if (actual !== expected[idx]) throw new Error(); + } +} diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index ff01e7fd054c1..90be7fee51ac0 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -11139,15 +11139,164 @@ void CodeGenerator::visitStringEndsWithInline(LStringEndsWithInline* lir) { masm.bind(ool->rejoin()); } -void CodeGenerator::visitStringConvertCase(LStringConvertCase* lir) { - pushArg(ToRegister(lir->string())); +void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) { + Register string = ToRegister(lir->string()); + Register output = ToRegister(lir->output()); + Register temp0 = ToRegister(lir->temp0()); + Register temp1 = ToRegister(lir->temp1()); + Register temp2 = ToRegister(lir->temp2()); + + // On x86 there are not enough registers. In that case reuse the string + // register as a temporary. + Register temp3 = + lir->temp3()->isBogusTemp() ? string : ToRegister(lir->temp3()); + Register temp4 = ToRegister(lir->temp4()); using Fn = JSString* (*)(JSContext*, HandleString); - if (lir->mir()->mode() == MStringConvertCase::LowerCase) { - callVM(lir); - } else { - callVM(lir); + OutOfLineCode* ool = oolCallVM( + lir, ArgList(string), StoreRegisterTo(output)); + + // Take the slow path if the string isn't a linear Latin-1 string. + Imm32 linearLatin1Bits(JSString::LINEAR_BIT | JSString::LATIN1_CHARS_BIT); + Register flags = temp0; + masm.load32(Address(string, JSString::offsetOfFlags()), flags); + masm.and32(linearLatin1Bits, flags); + masm.branch32(Assembler::NotEqual, flags, linearLatin1Bits, ool->entry()); + + Register length = temp0; + masm.loadStringLength(string, length); + + // Return the input if it's the empty string. + Label notEmptyString; + masm.branch32(Assembler::NotEqual, length, Imm32(0), ¬EmptyString); + { + masm.movePtr(string, output); + masm.jump(ool->rejoin()); + } + masm.bind(¬EmptyString); + + Register inputChars = temp1; + masm.loadStringChars(string, inputChars, CharEncoding::Latin1); + + Register toLowerCaseTable = temp2; + masm.movePtr(ImmPtr(unicode::latin1ToLowerCaseTable), toLowerCaseTable); + + // Single element strings can be directly retrieved from static strings cache. + Label notSingleElementString; + masm.branch32(Assembler::NotEqual, length, Imm32(1), ¬SingleElementString); + { + Register current = temp4; + + masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); + masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), + current); + masm.movePtr(ImmPtr(&gen->runtime->staticStrings().unitStaticTable), + output); + masm.loadPtr(BaseIndex(output, current, ScalePointer), output); + + masm.jump(ool->rejoin()); } + masm.bind(¬SingleElementString); + + // Use the OOL-path when the string is too long. This prevents scanning long + // strings which have upper case characters only near the end a second time in + // the VM. + constexpr int32_t MaxInlineLength = 64; + masm.branch32(Assembler::Above, length, Imm32(MaxInlineLength), ool->entry()); + + { + // Check if there are any characters which need to be converted. + // + // This extra loop gives a small performance improvement for strings which + // are already lower cased and lets us avoid calling into the runtime for + // non-inline, all lower case strings. But more importantly it avoids + // repeated inline allocation failures: + // |AllocateThinOrFatInlineString| below takes the OOL-path and calls the + // |js::StringToLowerCase| runtime function when the result string can't be + // allocated inline. And |js::StringToLowerCase| directly returns the input + // string when no characters need to be converted. That means it won't + // trigger GC to clear up the free nursery space, so the next toLowerCase() + // call will again fail to inline allocate the result string. + Label hasUpper; + { + if (temp3 == string) { + masm.push(string); + } + + Register checkInputChars = temp3; + masm.movePtr(inputChars, checkInputChars); + + Register current = temp4; + Register currentLowercase = output; + + Label start; + masm.bind(&start); + masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1); + masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), + currentLowercase); + masm.branch32(Assembler::NotEqual, current, currentLowercase, &hasUpper); + masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars); + masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); + + if (temp3 == string) { + masm.pop(string); + } + + // Input is already in lower case. + masm.movePtr(string, output); + masm.jump(ool->rejoin()); + } + masm.bind(&hasUpper); + + if (temp3 == string) { + masm.pop(string); + } + + // |length| was clobbered above, reload. + masm.loadStringLength(string, length); + + // Call into the runtime when we can't create an inline string. + masm.branch32(Assembler::Above, length, + Imm32(JSFatInlineString::MAX_LENGTH_LATIN1), ool->entry()); + + AllocateThinOrFatInlineString(masm, output, length, temp4, + initialStringHeap(), ool->entry(), + CharEncoding::Latin1); + + if (temp3 == string) { + masm.push(string); + } + + Register outputChars = temp3; + masm.loadInlineStringCharsForStore(output, outputChars); + + { + Register current = temp4; + + Label start; + masm.bind(&start); + masm.loadChar(Address(inputChars, 0), current, CharEncoding::Latin1); + masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), + current); + masm.storeChar(current, Address(outputChars, 0), CharEncoding::Latin1); + masm.addPtr(Imm32(sizeof(Latin1Char)), inputChars); + masm.addPtr(Imm32(sizeof(Latin1Char)), outputChars); + masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); + } + + if (temp3 == string) { + masm.pop(string); + } + } + + masm.bind(ool->rejoin()); +} + +void CodeGenerator::visitStringToUpperCase(LStringToUpperCase* lir) { + pushArg(ToRegister(lir->string())); + + using Fn = JSString* (*)(JSContext*, HandleString); + callVM(lir); } void CodeGenerator::visitStringSplit(LStringSplit* lir) { diff --git a/js/src/jit/LIROps.yaml b/js/src/jit/LIROps.yaml index c51f56307c33a..bd3473a1bd1cc 100644 --- a/js/src/jit/LIROps.yaml +++ b/js/src/jit/LIROps.yaml @@ -1145,13 +1145,22 @@ searchString: JSLinearString* num_temps: 1 -# Calls the ToLowerCase or ToUpperCase case conversion function. -- name: StringConvertCase +# Calls the ToLowerCase case conversion function. Inlines the case conversion +# when possible. +- name: StringToLowerCase + result_type: WordSized + operands: + string: WordSized + num_temps: 5 + mir_op: StringConvertCase + +# Calls the ToUpperCase case conversion function. +- name: StringToUpperCase result_type: WordSized operands: string: WordSized call_instruction: true - mir_op: true + mir_op: StringConvertCase - name: StringSplit result_type: WordSized diff --git a/js/src/jit/Lowering.cpp b/js/src/jit/Lowering.cpp index e46e27edac141..caabd964989e0 100644 --- a/js/src/jit/Lowering.cpp +++ b/js/src/jit/Lowering.cpp @@ -2366,10 +2366,26 @@ void LIRGenerator::visitStringEndsWith(MStringEndsWith* ins) { void LIRGenerator::visitStringConvertCase(MStringConvertCase* ins) { MOZ_ASSERT(ins->string()->type() == MIRType::String); - auto* lir = - new (alloc()) LStringConvertCase(useRegisterAtStart(ins->string())); - defineReturn(lir, ins); - assignSafepoint(lir, ins); + if (ins->mode() == MStringConvertCase::LowerCase) { +#ifdef JS_CODEGEN_X86 + // Due to lack of registers on x86, we reuse the string register as + // temporary. As a result we only need four temporary registers and take a + // bogus temporary as the fifth argument. + LDefinition temp4 = LDefinition::BogusTemp(); +#else + LDefinition temp4 = temp(); +#endif + auto* lir = new (alloc()) + LStringToLowerCase(useRegister(ins->string()), temp(), temp(), temp(), + temp4, tempByteOpRegister()); + define(lir, ins); + assignSafepoint(lir, ins); + } else { + auto* lir = + new (alloc()) LStringToUpperCase(useRegisterAtStart(ins->string())); + defineReturn(lir, ins); + assignSafepoint(lir, ins); + } } void LIRGenerator::visitStart(MStart* start) {} diff --git a/js/src/jit/arm64/Lowering-arm64.cpp b/js/src/jit/arm64/Lowering-arm64.cpp index 605ce06c28e24..3df5153bb1b39 100644 --- a/js/src/jit/arm64/Lowering-arm64.cpp +++ b/js/src/jit/arm64/Lowering-arm64.cpp @@ -39,6 +39,8 @@ LAllocation LIRGeneratorARM64::useByteOpRegisterOrNonDoubleConstant( return useRegisterOrNonDoubleConstant(mir); } +LDefinition LIRGeneratorARM64::tempByteOpRegister() { return temp(); } + LDefinition LIRGeneratorARM64::tempToUnbox() { return temp(); } void LIRGenerator::visitBox(MBox* box) { diff --git a/js/src/jit/arm64/Lowering-arm64.h b/js/src/jit/arm64/Lowering-arm64.h index 3032bc7c63310..4ab52dd464711 100644 --- a/js/src/jit/arm64/Lowering-arm64.h +++ b/js/src/jit/arm64/Lowering-arm64.h @@ -24,6 +24,7 @@ class LIRGeneratorARM64 : public LIRGeneratorShared { LAllocation useByteOpRegister(MDefinition* mir); LAllocation useByteOpRegisterAtStart(MDefinition* mir); LAllocation useByteOpRegisterOrNonDoubleConstant(MDefinition* mir); + LDefinition tempByteOpRegister(); LDefinition tempToUnbox(); diff --git a/js/src/jit/x86/Lowering-x86.cpp b/js/src/jit/x86/Lowering-x86.cpp index a23c0fb4dec4d..968b5baf14449 100644 --- a/js/src/jit/x86/Lowering-x86.cpp +++ b/js/src/jit/x86/Lowering-x86.cpp @@ -753,7 +753,7 @@ void LIRGeneratorX86::lowerBigIntMod(MBigIntMod* ins) { void LIRGenerator::visitSubstr(MSubstr* ins) { // Due to lack of registers on x86, we reuse the string register as // temporary. As a result we only need two temporary registers and take a - // bugos temporary as fifth argument. + // bogus temporary as fifth argument. LSubstr* lir = new (alloc()) LSubstr(useRegister(ins->string()), useRegister(ins->begin()), useRegister(ins->length()), temp(), LDefinition::BogusTemp(), From e9f9b34eac00ec26a06de904a8a9acd53873ba6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 10:38:01 +0000 Subject: [PATCH 40/65] Bug 1564347 - Part 4: Add MacroAssembler::branch8(BaseIndex, Register). r=jandem Differential Revision: https://phabricator.services.mozilla.com/D155607 --- js/src/jit/MacroAssembler.h | 7 +++ js/src/jit/arm/MacroAssembler-arm-inl.h | 63 +++++++++++++++++++ js/src/jit/arm64/MacroAssembler-arm64-inl.h | 30 +++++++++ .../jit/loong64/MacroAssembler-loong64-inl.h | 29 +++++++++ .../MacroAssembler-mips-shared-inl.h | 30 +++++++++ js/src/jit/wasm32/MacroAssembler-wasm32-inl.h | 5 ++ js/src/jit/x86-shared/Assembler-x86-shared.h | 19 ++++++ .../jit/x86-shared/BaseAssembler-x86-shared.h | 22 +++++++ js/src/jit/x86-shared/Encoding-x86-shared.h | 2 + .../MacroAssembler-x86-shared-inl.h | 6 ++ .../x86-shared/MacroAssembler-x86-shared.h | 1 + 11 files changed, 214 insertions(+) diff --git a/js/src/jit/MacroAssembler.h b/js/src/jit/MacroAssembler.h index df8d27a60ed19..2de8606453015 100644 --- a/js/src/jit/MacroAssembler.h +++ b/js/src/jit/MacroAssembler.h @@ -1407,6 +1407,13 @@ class MacroAssembler : public MacroAssemblerSpecific { inline void branch8(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH; + // Compares the byte in |lhs| against |rhs| using a 8-bit comparison on + // x86/x64 or a 32-bit comparison (all other platforms). The caller should + // ensure |rhs| is a zero- resp. sign-extended byte value for cross-platform + // compatible code. + inline void branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) PER_SHARED_ARCH; + inline void branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) PER_SHARED_ARCH; diff --git a/js/src/jit/arm/MacroAssembler-arm-inl.h b/js/src/jit/arm/MacroAssembler-arm-inl.h index 5d7d998154e74..2580a147df007 100644 --- a/js/src/jit/arm/MacroAssembler-arm-inl.h +++ b/js/src/jit/arm/MacroAssembler-arm-inl.h @@ -1196,6 +1196,69 @@ void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, ma_b(label, cond); } +void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) { + ScratchRegisterScope scratch(*this); + SecondScratchRegisterScope scratch2(*this); + + // Inlined calls to load8{Zero,Sign}Extend() and branch32() to acquire + // exclusive access to scratch registers. + + bool isSigned; + switch (cond) { + case Assembler::Equal: + case Assembler::NotEqual: + case Assembler::Above: + case Assembler::AboveOrEqual: + case Assembler::Below: + case Assembler::BelowOrEqual: + isSigned = false; + break; + + case Assembler::GreaterThan: + case Assembler::GreaterThanOrEqual: + case Assembler::LessThan: + case Assembler::LessThanOrEqual: + isSigned = true; + break; + + default: + MOZ_CRASH("unexpected condition"); + } + + if (isSigned) { + Register index = lhs.index; + + // ARMv7 does not have LSL on an index register with an extended load. + if (lhs.scale != TimesOne) { + ma_lsl(Imm32::ShiftOf(lhs.scale), index, scratch); + index = scratch; + } + + if (lhs.offset != 0) { + if (index != scratch) { + ma_mov(index, scratch); + index = scratch; + } + ma_add(Imm32(lhs.offset), index, scratch2); + } + ma_ldrsb(EDtrAddr(lhs.base, EDtrOffReg(index)), scratch); + } else { + Register base = lhs.base; + uint32_t scale = Imm32::ShiftOf(lhs.scale).value; + + if (lhs.offset == 0) { + ma_ldrb(DTRAddr(base, DtrRegImmShift(lhs.index, LSL, scale)), scratch); + } else { + ma_add(base, Imm32(lhs.offset), scratch, scratch2); + ma_ldrb(DTRAddr(scratch, DtrRegImmShift(lhs.index, LSL, scale)), scratch); + } + } + + ma_cmp(scratch, rhs); + ma_b(label, cond); +} + void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { ScratchRegisterScope scratch(*this); diff --git a/js/src/jit/arm64/MacroAssembler-arm64-inl.h b/js/src/jit/arm64/MacroAssembler-arm64-inl.h index 59a1f1dfb4048..5044cafc39252 100644 --- a/js/src/jit/arm64/MacroAssembler-arm64-inl.h +++ b/js/src/jit/arm64/MacroAssembler-arm64-inl.h @@ -963,6 +963,36 @@ void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, } } +void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) { + vixl::UseScratchRegisterScope temps(this); + Register scratch = temps.AcquireX().asUnsized(); + MOZ_ASSERT(scratch != lhs.base); + + switch (cond) { + case Assembler::Equal: + case Assembler::NotEqual: + case Assembler::Above: + case Assembler::AboveOrEqual: + case Assembler::Below: + case Assembler::BelowOrEqual: + load8ZeroExtend(lhs, scratch); + branch32(cond, scratch, rhs, label); + break; + + case Assembler::GreaterThan: + case Assembler::GreaterThanOrEqual: + case Assembler::LessThan: + case Assembler::LessThanOrEqual: + load8SignExtend(lhs, scratch); + branch32(cond, scratch, rhs, label); + break; + + default: + MOZ_CRASH("unexpected condition"); + } +} + void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { vixl::UseScratchRegisterScope temps(this); diff --git a/js/src/jit/loong64/MacroAssembler-loong64-inl.h b/js/src/jit/loong64/MacroAssembler-loong64-inl.h index bb1b98b8fa4ee..04932e1048722 100644 --- a/js/src/jit/loong64/MacroAssembler-loong64-inl.h +++ b/js/src/jit/loong64/MacroAssembler-loong64-inl.h @@ -896,6 +896,35 @@ void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, } } +void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) { + SecondScratchRegisterScope scratch2(*this); + MOZ_ASSERT(scratch2 != lhs.base); + + switch (cond) { + case Assembler::Equal: + case Assembler::NotEqual: + case Assembler::Above: + case Assembler::AboveOrEqual: + case Assembler::Below: + case Assembler::BelowOrEqual: + load8ZeroExtend(lhs, scratch2); + branch32(cond, scratch2, rhs, label); + break; + + case Assembler::GreaterThan: + case Assembler::GreaterThanOrEqual: + case Assembler::LessThan: + case Assembler::LessThanOrEqual: + load8SignExtend(lhs, scratch2); + branch32(cond, scratch2, rhs, label); + break; + + default: + MOZ_CRASH("unexpected condition"); + } +} + void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { SecondScratchRegisterScope scratch2(*this); diff --git a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h index 653900c9f1ce9..c40a710228ddd 100644 --- a/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h +++ b/js/src/jit/mips-shared/MacroAssembler-mips-shared-inl.h @@ -477,6 +477,36 @@ void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, } } +void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) { + SecondScratchRegisterScope scratch2(*this); + MOZ_ASSERT(scratch2 != lhs.base); + + switch (cond) { + case Assembler::Equal: + case Assembler::NotEqual: + case Assembler::Above: + case Assembler::AboveOrEqual: + case Assembler::Below: + case Assembler::BelowOrEqual: + load8ZeroExtend(lhs, scratch2); + branch32(cond, scratch2, rhs, label); + break; + + case Assembler::GreaterThan: + case Assembler::GreaterThanOrEqual: + case Assembler::LessThan: + case Assembler::LessThanOrEqual: + load8SignExtend(lhs, scratch2); + branch32(cond, scratch2, rhs, label); + break; + + default: + MOZ_CRASH("unexpected condition"); + } +} + + void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { SecondScratchRegisterScope scratch2(*this); diff --git a/js/src/jit/wasm32/MacroAssembler-wasm32-inl.h b/js/src/jit/wasm32/MacroAssembler-wasm32-inl.h index 80092dab5540f..3aca58598bb4f 100644 --- a/js/src/jit/wasm32/MacroAssembler-wasm32-inl.h +++ b/js/src/jit/wasm32/MacroAssembler-wasm32-inl.h @@ -633,6 +633,11 @@ void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, MOZ_CRASH(); } +void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) { + MOZ_CRASH(); +} + void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { MOZ_CRASH(); diff --git a/js/src/jit/x86-shared/Assembler-x86-shared.h b/js/src/jit/x86-shared/Assembler-x86-shared.h index 3eee35080ac50..a87a5f246b3a9 100644 --- a/js/src/jit/x86-shared/Assembler-x86-shared.h +++ b/js/src/jit/x86-shared/Assembler-x86-shared.h @@ -1269,6 +1269,25 @@ class AssemblerX86Shared : public AssemblerShared { MOZ_CRASH("unexpected operand kind"); } } + void cmpb(Register rhs, const Operand& lhs) { + switch (lhs.kind()) { + case Operand::REG: + masm.cmpb_rr(rhs.encoding(), lhs.reg()); + break; + case Operand::MEM_REG_DISP: + masm.cmpb_rm(rhs.encoding(), lhs.disp(), lhs.base()); + break; + case Operand::MEM_SCALE: + masm.cmpb_rm(rhs.encoding(), lhs.disp(), lhs.base(), lhs.index(), + lhs.scale()); + break; + case Operand::MEM_ADDRESS32: + masm.cmpb_rm(rhs.encoding(), lhs.address()); + break; + default: + MOZ_CRASH("unexpected operand kind"); + } + } void cmpb(Imm32 rhs, const Operand& lhs) { switch (lhs.kind()) { case Operand::REG: diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 16f224dee6714..067a36adf7a0b 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -1830,6 +1830,28 @@ class BaseAssembler : public GenericAssembler { } } + void cmpb_rr(RegisterID rhs, RegisterID lhs) { + spew("cmpb %s, %s", GPReg8Name(rhs), GPReg8Name(lhs)); + m_formatter.oneByteOp(OP_CMP_GbEb, rhs, lhs); + } + + void cmpb_rm(RegisterID rhs, int32_t offset, RegisterID base) { + spew("cmpb %s, " MEM_ob, GPReg8Name(rhs), ADDR_ob(offset, base)); + m_formatter.oneByteOp(OP_CMP_EbGb, offset, base, rhs); + } + + void cmpb_rm(RegisterID rhs, int32_t offset, RegisterID base, + RegisterID index, int scale) { + spew("cmpb %s, " MEM_obs, GPReg8Name(rhs), + ADDR_obs(offset, base, index, scale)); + m_formatter.oneByteOp(OP_CMP_EbGb, offset, base, index, scale, rhs); + } + + void cmpb_rm(RegisterID rhs, const void* addr) { + spew("cmpb %s, %p", GPReg8Name(rhs), addr); + m_formatter.oneByteOp(OP_CMP_EbGb, addr, rhs); + } + void cmpb_ir(int32_t rhs, RegisterID lhs) { if (rhs == 0) { testb_rr(lhs, lhs); diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index c69f68e7a943f..a9e1f5f131265 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -84,7 +84,9 @@ enum OneByteOpcodeID { OP_XOR_EvGv = 0x31, OP_XOR_GvEv = 0x33, OP_XOR_EAXIv = 0x35, + OP_CMP_EbGb = 0x38, OP_CMP_EvGv = 0x39, + OP_CMP_GbEb = 0x3A, OP_CMP_GvEv = 0x3B, OP_CMP_EAXIv = 0x3D, #ifdef JS_CODEGEN_X64 diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h index 6acef397dff6e..4f71299c7441a 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared-inl.h @@ -466,6 +466,12 @@ void MacroAssembler::branch8(Condition cond, const Address& lhs, Imm32 rhs, j(cond, label); } +void MacroAssembler::branch8(Condition cond, const BaseIndex& lhs, Register rhs, + Label* label) { + cmp8(Operand(lhs), rhs); + j(cond, label); +} + void MacroAssembler::branch16(Condition cond, const Address& lhs, Imm32 rhs, Label* label) { cmp16(lhs, rhs); diff --git a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h index a29e64c6669d6..26c92adf098f3 100644 --- a/js/src/jit/x86-shared/MacroAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/MacroAssembler-x86-shared.h @@ -154,6 +154,7 @@ class MacroAssemblerX86Shared : public Assembler { void cmp8(const Address& lhs, Imm32 rhs) { cmp8(Operand(lhs), rhs); } void cmp8(const Operand& lhs, Imm32 rhs) { cmpb(rhs, lhs); } + void cmp8(const Operand& lhs, Register rhs) { cmpb(rhs, lhs); } void atomic_inc32(const Operand& addr) { lock_incl(addr); } void atomic_dec32(const Operand& addr) { lock_decl(addr); } From 42bb21f29e84928bd0d8f3868d20d23a89f96978 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 10:38:01 +0000 Subject: [PATCH 41/65] Bug 1564347 - Part 5: Use branch8 in CodeGenerator::visitStringToLowerCase(). r=jandem Differential Revision: https://phabricator.services.mozilla.com/D155608 --- js/src/jit/CodeGenerator.cpp | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index 90be7fee51ac0..5fb3a90dda9f3 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -11219,39 +11219,26 @@ void CodeGenerator::visitStringToLowerCase(LStringToLowerCase* lir) { // call will again fail to inline allocate the result string. Label hasUpper; { - if (temp3 == string) { - masm.push(string); - } - - Register checkInputChars = temp3; + Register checkInputChars = output; masm.movePtr(inputChars, checkInputChars); Register current = temp4; - Register currentLowercase = output; Label start; masm.bind(&start); masm.loadChar(Address(checkInputChars, 0), current, CharEncoding::Latin1); - masm.load8ZeroExtend(BaseIndex(toLowerCaseTable, current, TimesOne), - currentLowercase); - masm.branch32(Assembler::NotEqual, current, currentLowercase, &hasUpper); + masm.branch8(Assembler::NotEqual, + BaseIndex(toLowerCaseTable, current, TimesOne), current, + &hasUpper); masm.addPtr(Imm32(sizeof(Latin1Char)), checkInputChars); masm.branchSub32(Assembler::NonZero, Imm32(1), length, &start); - if (temp3 == string) { - masm.pop(string); - } - // Input is already in lower case. masm.movePtr(string, output); masm.jump(ool->rejoin()); } masm.bind(&hasUpper); - if (temp3 == string) { - masm.pop(string); - } - // |length| was clobbered above, reload. masm.loadStringLength(string, length); From 8de7524e6f5c9a5429e7109b801f76993724e63e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Bargull?= Date: Wed, 31 Aug 2022 10:38:02 +0000 Subject: [PATCH 42/65] Bug 1564347 - Part 6: Use eax encoding for cmpb when possible. r=jandem I noticed this was missing when I added the `cmpb` methods in part 4. Differential Revision: https://phabricator.services.mozilla.com/D155609 --- js/src/jit/x86-shared/BaseAssembler-x86-shared.h | 6 +++++- js/src/jit/x86-shared/Encoding-x86-shared.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h index 067a36adf7a0b..ea122fb3c5b59 100644 --- a/js/src/jit/x86-shared/BaseAssembler-x86-shared.h +++ b/js/src/jit/x86-shared/BaseAssembler-x86-shared.h @@ -1859,7 +1859,11 @@ class BaseAssembler : public GenericAssembler { } spew("cmpb $0x%x, %s", uint32_t(rhs), GPReg8Name(lhs)); - m_formatter.oneByteOp(OP_GROUP1_EbIb, lhs, GROUP1_OP_CMP); + if (lhs == rax) { + m_formatter.oneByteOp(OP_CMP_EAXIb); + } else { + m_formatter.oneByteOp(OP_GROUP1_EbIb, lhs, GROUP1_OP_CMP); + } m_formatter.immediate8(rhs); } diff --git a/js/src/jit/x86-shared/Encoding-x86-shared.h b/js/src/jit/x86-shared/Encoding-x86-shared.h index a9e1f5f131265..faee83dc74d32 100644 --- a/js/src/jit/x86-shared/Encoding-x86-shared.h +++ b/js/src/jit/x86-shared/Encoding-x86-shared.h @@ -88,6 +88,7 @@ enum OneByteOpcodeID { OP_CMP_EvGv = 0x39, OP_CMP_GbEb = 0x3A, OP_CMP_GvEv = 0x3B, + OP_CMP_EAXIb = 0x3C, OP_CMP_EAXIv = 0x3D, #ifdef JS_CODEGEN_X64 PRE_REX = 0x40, From bcba3514b3abbc3fa07c9ebe44841d75c8512488 Mon Sep 17 00:00:00 2001 From: Paul Bone Date: Wed, 31 Aug 2022 10:39:03 +0000 Subject: [PATCH 43/65] Bug 1779138 - Disable GatherTotalMemory on MacOS r=florian In order to remove uses of ResidentUnique on MacOS this patch disables GatherTotalMemory in its entirety (rather than provide inaccurate telemetry). I will follow up with a patch that uses task_info phys_footprint to re-enable it. Differential Revision: https://phabricator.services.mozilla.com/D156048 --- xpcom/base/MemoryTelemetry.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/xpcom/base/MemoryTelemetry.cpp b/xpcom/base/MemoryTelemetry.cpp index bdbf4cdd1312f..75b47d4d887b2 100644 --- a/xpcom/base/MemoryTelemetry.cpp +++ b/xpcom/base/MemoryTelemetry.cpp @@ -216,9 +216,11 @@ nsresult MemoryTelemetry::GatherReports( // If we're running in the parent process, collect data from all processes for // the MEMORY_TOTAL histogram. +#ifndef XP_MACOSX if (XRE_IsParentProcess() && !mGatheringTotalMemory) { GatherTotalMemory(); } +#endif if (!Telemetry::CanRecordReleaseData()) { return NS_OK; From f5961514b758a5be864ccd91423fa6dc93c11655 Mon Sep 17 00:00:00 2001 From: Jan Varga Date: Wed, 31 Aug 2022 10:43:56 +0000 Subject: [PATCH 44/65] Bug 1786465 - A follow-up fix to allow copy elision; r=dom-storage-reviewers,jesup Differential Revision: https://phabricator.services.mozilla.com/D156065 --- dom/fs/shared/FileSystemHelpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dom/fs/shared/FileSystemHelpers.h b/dom/fs/shared/FileSystemHelpers.h index d83fdd4599a1e..76de07e0615fa 100644 --- a/dom/fs/shared/FileSystemHelpers.h +++ b/dom/fs/shared/FileSystemHelpers.h @@ -124,7 +124,7 @@ class Registered { if (oldObject) { oldObject->Unregister(); } - return std::move(oldObject); + return oldObject; } T* get() const { return mObject; } From 85cf1b2eed5f15b760ecd4fead409151c7ade741 Mon Sep 17 00:00:00 2001 From: Daniel Holbert Date: Wed, 31 Aug 2022 11:16:32 +0000 Subject: [PATCH 45/65] Bug 1787871 - fix line clamping in the title of the Firefox View tab pickup hero tab card, r=sclements Differential Revision: https://phabricator.services.mozilla.com/D155964 --- browser/components/firefoxview/firefoxview.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/firefoxview/firefoxview.css b/browser/components/firefoxview/firefoxview.css index e5ee7b4618cfa..6cda58fd54e60 100644 --- a/browser/components/firefoxview/firefoxview.css +++ b/browser/components/firefoxview/firefoxview.css @@ -759,7 +759,7 @@ button.close { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; - height: 2.5em; + align-self: start; } .synced-tab-li-device { From 25837aaf2ef6628b961b6ac0b4986ac4a8af7115 Mon Sep 17 00:00:00 2001 From: Manuel Bucher Date: Wed, 31 Aug 2022 11:19:14 +0000 Subject: [PATCH 46/65] Bug 1787729 - Move NS_HasRootDomain, CheckForBrokenChromeURL, IsCoepCredentiallessEnabled to mozilla::net namespace r=necko-reviewers,kershaw Differential Revision: https://phabricator.services.mozilla.com/D155992 --- dom/base/nsDOMWindowUtils.cpp | 2 +- dom/media/gmp/GMPServiceParent.cpp | 2 +- netwerk/base/nsNetUtil.cpp | 10 +++++----- netwerk/base/nsNetUtil.h | 9 +++++---- netwerk/cache2/CacheFileIOManager.cpp | 2 +- netwerk/cache2/CacheStorageService.cpp | 2 +- netwerk/dns/nsEffectiveTLDService.cpp | 2 +- 7 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dom/base/nsDOMWindowUtils.cpp b/dom/base/nsDOMWindowUtils.cpp index e98d328bdd265..e4c0663e06e48 100644 --- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -4697,7 +4697,7 @@ nsDOMWindowUtils::IsCoepCredentialless(bool* aResult) { return NS_ERROR_FAILURE; } - *aResult = IsCoepCredentiallessEnabled( + *aResult = net::IsCoepCredentiallessEnabled( doc->Trials().IsEnabled(OriginTrial::CoepCredentialless)); return NS_OK; } diff --git a/dom/media/gmp/GMPServiceParent.cpp b/dom/media/gmp/GMPServiceParent.cpp index 9bd21ad5b9d8c..996c25e263416 100644 --- a/dom/media/gmp/GMPServiceParent.cpp +++ b/dom/media/gmp/GMPServiceParent.cpp @@ -1423,7 +1423,7 @@ bool MatchBaseDomain(nsIFile* aPath, const nsACString& aBaseDomain) { return false; } bool success; - rv = NS_HasRootDomain(originHostname, aBaseDomain, &success); + rv = net::HasRootDomain(originHostname, aBaseDomain, &success); if (NS_SUCCEEDED(rv) && success) { return true; } diff --git a/netwerk/base/nsNetUtil.cpp b/netwerk/base/nsNetUtil.cpp index e30499029a7f7..55dfef1e96ee1 100644 --- a/netwerk/base/nsNetUtil.cpp +++ b/netwerk/base/nsNetUtil.cpp @@ -3802,11 +3802,8 @@ void WarnIgnoredPreload(const mozilla::dom::Document& aDoc, nsIURI& aURI) { "PreloadIgnoredInvalidAttr", params); } -} // namespace net -} // namespace mozilla - -nsresult NS_HasRootDomain(const nsACString& aInput, const nsACString& aHost, - bool* aResult) { +nsresult HasRootDomain(const nsACString& aInput, const nsACString& aHost, + bool* aResult) { if (NS_WARN_IF(!aResult)) { return NS_ERROR_FAILURE; } @@ -3909,3 +3906,6 @@ bool IsCoepCredentiallessEnabled(bool aIsOriginTrialCoepCredentiallessEnabled) { browser_tabs_remote_coep_credentialless_DoNotUseDirectly() || aIsOriginTrialCoepCredentiallessEnabled; } + +} // namespace net +} // namespace mozilla diff --git a/netwerk/base/nsNetUtil.h b/netwerk/base/nsNetUtil.h index e5c3c2176c1e6..c1c397b39c9c7 100644 --- a/netwerk/base/nsNetUtil.h +++ b/netwerk/base/nsNetUtil.h @@ -1054,8 +1054,6 @@ bool CheckPreloadAttrs(const nsAttrValue& aAs, const nsAString& aType, const nsAString& aMedia, mozilla::dom::Document* aDocument); void WarnIgnoredPreload(const mozilla::dom::Document&, nsIURI&); -} // namespace net -} // namespace mozilla /** * Returns true if the |aInput| in is part of the root domain of |aHost|. @@ -1066,11 +1064,14 @@ void WarnIgnoredPreload(const mozilla::dom::Document&, nsIURI&); * @param aInput The host to be analyzed. * @param aHost The host to compare to. */ -nsresult NS_HasRootDomain(const nsACString& aInput, const nsACString& aHost, - bool* aResult); +nsresult HasRootDomain(const nsACString& aInput, const nsACString& aHost, + bool* aResult); void CheckForBrokenChromeURL(nsILoadInfo* aLoadInfo, nsIURI* aURI); bool IsCoepCredentiallessEnabled(bool aIsOriginTrialCoepCredentiallessEnabled); +} // namespace net +} // namespace mozilla + #endif // !nsNetUtil_h__ diff --git a/netwerk/cache2/CacheFileIOManager.cpp b/netwerk/cache2/CacheFileIOManager.cpp index 6f5287296f527..0c0ab48fadeaf 100644 --- a/netwerk/cache2/CacheFileIOManager.cpp +++ b/netwerk/cache2/CacheFileIOManager.cpp @@ -3167,7 +3167,7 @@ nsresult CacheFileIOManager::EvictByContextInternal( // Clear entry if the host belongs to the given base domain. bool hasRootDomain = false; - rv = NS_HasRootDomain(host, baseDomain, &hasRootDomain); + rv = HasRootDomain(host, baseDomain, &hasRootDomain); if (NS_WARN_IF(NS_FAILED(rv))) { return false; } diff --git a/netwerk/cache2/CacheStorageService.cpp b/netwerk/cache2/CacheStorageService.cpp index 7d1425eec8a5e..cb34b64b27ba9 100644 --- a/netwerk/cache2/CacheStorageService.cpp +++ b/netwerk/cache2/CacheStorageService.cpp @@ -789,7 +789,7 @@ NS_IMETHODIMP CacheStorageService::ClearBaseDomain( } bool hasRootDomain = false; - rv = NS_HasRootDomain(host, cBaseDomain, &hasRootDomain); + rv = HasRootDomain(host, cBaseDomain, &hasRootDomain); if (NS_WARN_IF(NS_FAILED(rv))) { continue; } diff --git a/netwerk/dns/nsEffectiveTLDService.cpp b/netwerk/dns/nsEffectiveTLDService.cpp index f113a61ee8624..408c925279f6b 100644 --- a/netwerk/dns/nsEffectiveTLDService.cpp +++ b/netwerk/dns/nsEffectiveTLDService.cpp @@ -512,5 +512,5 @@ nsresult nsEffectiveTLDService::NormalizeHostname(nsCString& aHostname) { NS_IMETHODIMP nsEffectiveTLDService::HasRootDomain(const nsACString& aInput, const nsACString& aHost, bool* aResult) { - return NS_HasRootDomain(aInput, aHost, aResult); + return net::HasRootDomain(aInput, aHost, aResult); } From 403b0c1b5819f315f591106e8421096ed342f0f3 Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Wed, 31 Aug 2022 11:24:31 +0000 Subject: [PATCH 47/65] Bug 1787706 - Increase the blob tile size to 512. r=jrmuizel It improves rasterization and upload times by a lot in almost all of the test cases I can find. The only drawback is that our invalidation granulatiry is the tile so invalidation gets coarser as we increase the tile size. 512 is a bit special because it is the limit above which a different texture upload path is taken, so there will be more risk of performance side effects if/when we decide to make tiles even larger. Differential Revision: https://phabricator.services.mozilla.com/D155822 --- gfx/tests/reftest/reftest.list | 2 +- gfx/thebes/gfxPlatform.cpp | 2 +- layout/reftests/bugs/reftest.list | 2 +- layout/reftests/svg/filters/reftest.list | 2 +- layout/reftests/text-stroke/reftest.list | 2 +- modules/libpref/init/StaticPrefList.yaml | 2 +- .../meta/svg/painting/reftests/marker-path-021.svg.ini | 3 +++ 7 files changed, 9 insertions(+), 6 deletions(-) create mode 100644 testing/web-platform/meta/svg/painting/reftests/marker-path-021.svg.ini diff --git a/gfx/tests/reftest/reftest.list b/gfx/tests/reftest/reftest.list index 57b30342fc4a8..4fdd2068a9833 100644 --- a/gfx/tests/reftest/reftest.list +++ b/gfx/tests/reftest/reftest.list @@ -19,7 +19,7 @@ fuzzy(0-11,0-4) fails-if(useDrawSnapshot) == 1474722.html 1474722-ref.html fails-if(useDrawSnapshot) == 1501195.html 1501195-ref.html == 1519754.html 1519754-ref.html skip-if(!asyncPan) == 1524261.html 1524261-ref.html -fuzzy-if(!useDrawSnapshot,14-14,44-95) == 1524353.html 1524353-ref.html +fuzzy-if(!useDrawSnapshot,14-14,44-300) == 1524353.html 1524353-ref.html == 1523776.html 1523776-ref.html == bug1523410-translate-scale-snap.html bug1523410-translate-scale-snap-ref.html == 1523080.html 1523080-ref.html diff --git a/gfx/thebes/gfxPlatform.cpp b/gfx/thebes/gfxPlatform.cpp index a10aad915c635..b63249fa6ebf6 100644 --- a/gfx/thebes/gfxPlatform.cpp +++ b/gfx/thebes/gfxPlatform.cpp @@ -590,7 +590,7 @@ static void WebRenderBatchingPrefChangeCallback(const char* aPrefName, void*) { static void WebRenderBlobTileSizePrefChangeCallback(const char* aPrefName, void*) { uint32_t tileSize = Preferences::GetUint( - StaticPrefs::GetPrefName_gfx_webrender_blob_tile_size(), 256); + StaticPrefs::GetPrefName_gfx_webrender_blob_tile_size(), 512); gfx::gfxVars::SetWebRenderBlobTileSize(tileSize); } diff --git a/layout/reftests/bugs/reftest.list b/layout/reftests/bugs/reftest.list index 41894431af9db..60fafeb4a6064 100644 --- a/layout/reftests/bugs/reftest.list +++ b/layout/reftests/bugs/reftest.list @@ -2108,7 +2108,7 @@ skip-if(Android) == 1727172-1.xhtml 1727172-1-ref.html == 1726663-1.html 1726663-1-ref.html == 1727016-1.html 1727016-1-ref.html != 1730314-1.html 1730314-1-ref.html -fuzzy(0-3,0-3) fuzzy-if(Android,0-3,0-1901) fuzzy-if(winWidget,0-154,0-118) == 1738700-1.html 1738700-1-ref.html +fuzzy(0-3,0-3) fuzzy-if(Android,0-3,0-1901) fuzzy-if(winWidget,0-154,0-130) == 1738700-1.html 1738700-1-ref.html skip-if(Android||!browserIsFission) fuzzy(255-255,171000-171000) HTTP == 1743533-1.html 1743533-1-ref.html # do not decrease the fuzz! this is a not equal test, the high fuzz minimum is to ensure they differ by enough. == 1743560-1.html 1743560-1-ref.html == 1743851-1.html 1743851-1-ref.html diff --git a/layout/reftests/svg/filters/reftest.list b/layout/reftests/svg/filters/reftest.list index 46fa5daea7928..ab1b024e29adc 100644 --- a/layout/reftests/svg/filters/reftest.list +++ b/layout/reftests/svg/filters/reftest.list @@ -107,7 +107,7 @@ fuzzy(0-1,0-400) == feDisplacementMap-alpha-01.svg pass.svg fuzzy(0-2,0-500) == feDisplacementMap-colour-01.svg feDisplacementMap-colour-01-ref.svg == feDisplacementMap-scale-01.svg pass.svg -fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-2,0-25) fuzzy-if(!useDrawSnapshot,55-98,14033-16360) == feDropShadow-01.svg feDropShadow-01-ref.svg +fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu),0-2,0-25) fuzzy-if(!useDrawSnapshot,10-98,14033-16360) == feDropShadow-01.svg feDropShadow-01-ref.svg == feFlood-color-01.svg pass.svg diff --git a/layout/reftests/text-stroke/reftest.list b/layout/reftests/text-stroke/reftest.list index 356e49e35c59f..5eed07484a6b7 100644 --- a/layout/reftests/text-stroke/reftest.list +++ b/layout/reftests/text-stroke/reftest.list @@ -4,6 +4,6 @@ fuzzy(0-64,0-776) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-80,0-2822) == webkit-text-stroke-property-001.html webkit-text-stroke-property-001-ref.html fuzzy(0-4,0-27) fuzzy-if(geckoview,0-4,0-1476) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-125,0-3725) == webkit-text-stroke-property-002.html webkit-text-stroke-property-002-ref.html fuzzy(0-64,0-528) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-74,0-2596) == webkit-text-stroke-property-003.html webkit-text-stroke-property-003-ref.html -fuzzy(0-64,0-575) fuzzy-if(geckoview&&device,0-64,0-599) fuzzy-if(geckoview&&emulator,96-96,58-58) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-85,0-2147) == webkit-text-stroke-property-004.html webkit-text-stroke-property-004-ref.html +fuzzy(0-64,0-575) fuzzy-if(geckoview&&device,0-64,0-599) fuzzy-if(geckoview&&emulator,32-96,24-58) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-85,0-2147) == webkit-text-stroke-property-004.html webkit-text-stroke-property-004-ref.html fuzzy(0-64,0-860) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-80,0-2822) == webkit-text-stroke-property-005.html webkit-text-stroke-property-005-ref.html fuzzy(0-71,0-10) fuzzy-if(/^Windows\x20NT\x2010\.0/.test(http.oscpu)&&(/^aarch64-msvc/.test(xulRuntime.XPCOMABI)||(winWidget&&!layersGPUAccelerated)),0-48,0-351) == webkit-text-stroke-property-006.html webkit-text-stroke-property-006-ref.html diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml index 987f60ae04c46..79ab2d5bb008d 100644 --- a/modules/libpref/init/StaticPrefList.yaml +++ b/modules/libpref/init/StaticPrefList.yaml @@ -6085,7 +6085,7 @@ - name: gfx.webrender.blob-tile-size type: uint32_t - value: 256 + value: 512 mirror: always - name: gfx.webrender.batched-upload-threshold diff --git a/testing/web-platform/meta/svg/painting/reftests/marker-path-021.svg.ini b/testing/web-platform/meta/svg/painting/reftests/marker-path-021.svg.ini new file mode 100644 index 0000000000000..86c84ac974f06 --- /dev/null +++ b/testing/web-platform/meta/svg/painting/reftests/marker-path-021.svg.ini @@ -0,0 +1,3 @@ +[marker-path-021.svg] + fuzzy: + maxDifference=0-1;totalPixels=0-1 From acff43bccca6be48f3e3a4be54bf79f3dfd056fa Mon Sep 17 00:00:00 2001 From: criss Date: Wed, 31 Aug 2022 15:08:39 +0300 Subject: [PATCH 48/65] Backed out changeset 49f976dc75d5 (bug 1779138) for causing Xpcshell failures on test_TelemetrySession.js. CLOSED TREE --- xpcom/base/MemoryTelemetry.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/xpcom/base/MemoryTelemetry.cpp b/xpcom/base/MemoryTelemetry.cpp index 75b47d4d887b2..bdbf4cdd1312f 100644 --- a/xpcom/base/MemoryTelemetry.cpp +++ b/xpcom/base/MemoryTelemetry.cpp @@ -216,11 +216,9 @@ nsresult MemoryTelemetry::GatherReports( // If we're running in the parent process, collect data from all processes for // the MEMORY_TOTAL histogram. -#ifndef XP_MACOSX if (XRE_IsParentProcess() && !mGatheringTotalMemory) { GatherTotalMemory(); } -#endif if (!Telemetry::CanRecordReleaseData()) { return NS_OK; From 3074bcadc673a47d1e3ad7db8773e426e2931d9f Mon Sep 17 00:00:00 2001 From: Nicolas Chevobbe Date: Wed, 31 Aug 2022 11:40:15 +0000 Subject: [PATCH 49/65] Bug 1787389 - [devtools] Fix ESLint consistent-return failures in devtools/client/debugger/src/actions. r=bomsy. Differential Revision: https://phabricator.services.mozilla.com/D155824 --- devtools/.eslintrc.js | 15 ------- .../debugger/src/actions/breakpoints/index.js | 4 +- .../src/actions/breakpoints/modify.js | 12 +++--- .../src/actions/breakpoints/syncBreakpoint.js | 4 +- .../debugger/src/actions/expressions.js | 6 ++- .../debugger/src/actions/pause/commands.js | 39 +++++++++++-------- .../src/actions/pause/highlightCalls.js | 12 +++--- .../src/actions/pause/inlinePreview.js | 10 ++--- .../debugger/src/actions/pause/mapScopes.js | 3 +- .../debugger/src/actions/pause/selectFrame.js | 3 +- .../src/actions/pause/tests/pause.spec.js | 2 + .../client/debugger/src/actions/preview.js | 4 +- .../src/actions/sources/newSources.js | 5 ++- .../debugger/src/actions/sources/select.js | 5 ++- devtools/client/debugger/src/actions/ui.js | 2 +- .../debugger/src/client/firefox/commands.js | 39 ++++++++++++------- 16 files changed, 88 insertions(+), 77 deletions(-) diff --git a/devtools/.eslintrc.js b/devtools/.eslintrc.js index 30dda1c9c7da7..a5d9f6d4184d8 100644 --- a/devtools/.eslintrc.js +++ b/devtools/.eslintrc.js @@ -33,21 +33,6 @@ module.exports = { }, { files: [ - "client/debugger/src/actions/breakpoints/index.js", - "client/debugger/src/actions/breakpoints/modify.js", - "client/debugger/src/actions/breakpoints/syncBreakpoint.js", - "client/debugger/src/actions/expressions.js", - "client/debugger/src/actions/pause/commands.js", - "client/debugger/src/actions/pause/highlightCalls.js", - "client/debugger/src/actions/pause/inlinePreview.js", - "client/debugger/src/actions/pause/mapScopes.js", - "client/debugger/src/actions/pause/selectFrame.js", - "client/debugger/src/actions/pause/tests/pause.spec.js", - "client/debugger/src/actions/preview.js", - "client/debugger/src/actions/sources/newSources.js", - "client/debugger/src/actions/sources/select.js", - "client/debugger/src/actions/ui.js", - "client/debugger/src/client/firefox/commands.js", "client/debugger/src/components/App.js", "client/debugger/src/components/Editor/Breakpoint.js", "client/debugger/src/components/Editor/ColumnBreakpoint.js", diff --git a/devtools/client/debugger/src/actions/breakpoints/index.js b/devtools/client/debugger/src/actions/breakpoints/index.js index d6821831871a9..ddef5bb0dbb7f 100644 --- a/devtools/client/debugger/src/actions/breakpoints/index.js +++ b/devtools/client/debugger/src/actions/breakpoints/index.js @@ -215,7 +215,7 @@ export function toggleBreakpointAtLine(cx, line) { const selectedSource = getSelectedSource(state); if (!selectedSource) { - return; + return null; } const bp = getBreakpointAtLocation(state, { line, column: undefined }); @@ -243,7 +243,7 @@ export function addBreakpointAtLine( const source = getSelectedSource(state); if (!source) { - return; + return null; } const breakpointLocation = { sourceId: source.id, diff --git a/devtools/client/debugger/src/actions/breakpoints/modify.js b/devtools/client/debugger/src/actions/breakpoints/modify.js index 77f4587be50b7..68f54ecf185f0 100644 --- a/devtools/client/debugger/src/actions/breakpoints/modify.js +++ b/devtools/client/debugger/src/actions/breakpoints/modify.js @@ -83,7 +83,7 @@ export function enableBreakpoint(cx, initialBreakpoint) { const { dispatch, getState, client } = thunkArgs; const breakpoint = getBreakpoint(getState(), initialBreakpoint.location); if (!breakpoint || !breakpoint.disabled) { - return; + return null; } dispatch(setSkipPausing(false)); @@ -121,7 +121,7 @@ export function addBreakpoint( // No position is found if the `initialLocation` is on a non-breakable line or // the line no longer exists. if (!position) { - return; + return null; } const { location, generatedLocation } = position; @@ -130,7 +130,7 @@ export function addBreakpoint( const generatedSource = getLocationSource(getState(), generatedLocation); if (!source || !generatedSource) { - return; + return null; } const originalContent = getSourceContent(getState(), source.id); @@ -160,7 +160,7 @@ export function addBreakpoint( }); if (shouldCancel()) { - return; + return null; } dispatch(setSkipPausing(false)); @@ -189,7 +189,7 @@ export function removeBreakpoint(cx, initialBreakpoint) { const breakpoint = getBreakpoint(getState(), initialBreakpoint.location); if (!breakpoint) { - return; + return null; } dispatch(setSkipPausing(false)); @@ -270,7 +270,7 @@ export function disableBreakpoint(cx, initialBreakpoint) { return ({ dispatch, getState, client }) => { const breakpoint = getBreakpoint(getState(), initialBreakpoint.location); if (!breakpoint || breakpoint.disabled) { - return; + return null; } dispatch(setSkipPausing(false)); diff --git a/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js b/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js index c0a2febce342b..0b4b1198ec107 100644 --- a/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js +++ b/devtools/client/debugger/src/actions/breakpoints/syncBreakpoint.js @@ -56,7 +56,7 @@ export function syncBreakpoint(cx, sourceId, pendingBreakpoint) { const generatedSource = getSource(getState(), generatedSourceId); if (!source || !generatedSource) { - return; + return null; } const { location, generatedLocation } = pendingBreakpoint; @@ -110,7 +110,7 @@ export function syncBreakpoint(cx, sourceId, pendingBreakpoint) { removeBreakpointAtGeneratedLocation(cx, sourceGeneratedLocation) ); } - return; + return null; } const isSameLocation = comparePosition( diff --git a/devtools/client/debugger/src/actions/expressions.js b/devtools/client/debugger/src/actions/expressions.js index a548e8c5522d2..6a30303b5d0db 100644 --- a/devtools/client/debugger/src/actions/expressions.js +++ b/devtools/client/debugger/src/actions/expressions.js @@ -30,7 +30,7 @@ import { features } from "../utils/prefs"; export function addExpression(cx, input) { return async ({ dispatch, getState, evaluationsParser }) => { if (!input) { - return; + return null; } const expressionError = await evaluationsParser.hasSyntaxError(input); @@ -46,6 +46,8 @@ export function addExpression(cx, input) { if (newExpression) { return dispatch(evaluateExpression(cx, newExpression)); } + + return null; }; } @@ -126,7 +128,7 @@ function evaluateExpression(cx, expression) { return async function({ dispatch, getState, client, sourceMaps }) { if (!expression.input) { console.warn("Expressions should not be empty"); - return; + return null; } let { input } = expression; diff --git a/devtools/client/debugger/src/actions/pause/commands.js b/devtools/client/debugger/src/actions/pause/commands.js index 9eddf6a3e9803..7766b75bf07ec 100644 --- a/devtools/client/debugger/src/actions/pause/commands.js +++ b/devtools/client/debugger/src/actions/pause/commands.js @@ -61,7 +61,7 @@ export function selectThread(cx, thread) { export function command(type) { return async ({ dispatch, getState, client }) => { if (!type) { - return; + return null; } // For now, all commands are by default against the currently selected thread const thread = getCurrentThread(getState()); @@ -85,9 +85,10 @@ export function command(type) { */ export function stepIn() { return ({ dispatch, getState }) => { - if (getIsCurrentThreadPaused(getState())) { - return dispatch(command("stepIn")); + if (!getIsCurrentThreadPaused(getState())) { + return null; } + return dispatch(command("stepIn")); }; } @@ -99,9 +100,10 @@ export function stepIn() { */ export function stepOver() { return ({ dispatch, getState }) => { - if (getIsCurrentThreadPaused(getState())) { - return dispatch(command("stepOver")); + if (!getIsCurrentThreadPaused(getState())) { + return null; } + return dispatch(command("stepOver")); }; } @@ -113,9 +115,10 @@ export function stepOver() { */ export function stepOut() { return ({ dispatch, getState }) => { - if (getIsCurrentThreadPaused(getState())) { - return dispatch(command("stepOut")); + if (!getIsCurrentThreadPaused(getState())) { + return null; } + return dispatch(command("stepOut")); }; } @@ -127,10 +130,11 @@ export function stepOut() { */ export function resume() { return ({ dispatch, getState }) => { - if (getIsCurrentThreadPaused(getState())) { - recordEvent("continue"); - return dispatch(command("resume")); + if (!getIsCurrentThreadPaused(getState())) { + return null; } + recordEvent("continue"); + return dispatch(command("resume")); }; } @@ -141,13 +145,14 @@ export function resume() { */ export function restart(cx, frame) { return async ({ dispatch, getState, client }) => { - if (getIsCurrentThreadPaused(getState())) { - return dispatch({ - type: "COMMAND", - command: "restart", - thread: cx.thread, - [PROMISE]: client.restart(cx.thread, frame.id), - }); + if (!getIsCurrentThreadPaused(getState())) { + return null; } + return dispatch({ + type: "COMMAND", + command: "restart", + thread: cx.thread, + [PROMISE]: client.restart(cx.thread, frame.id), + }); }; } diff --git a/devtools/client/debugger/src/actions/pause/highlightCalls.js b/devtools/client/debugger/src/actions/pause/highlightCalls.js index 7cf26ded0752b..69a0178106daf 100644 --- a/devtools/client/debugger/src/actions/pause/highlightCalls.js +++ b/devtools/client/debugger/src/actions/pause/highlightCalls.js @@ -27,7 +27,7 @@ function inHouseContainsPosition(a, b) { export function highlightCalls(cx) { return async function({ dispatch, getState, parser, client }) { if (!cx) { - return; + return null; } const frame = await getSelectedFrame( @@ -36,29 +36,29 @@ export function highlightCalls(cx) { ); if (!frame) { - return; + return null; } const { thread } = cx; const originalAstScopes = await parser.getScopes(frame.location); if (!originalAstScopes) { - return; + return null; } const source = getLocationSource(getState(), frame.location); if (!source) { - return; + return null; } const symbols = getSymbols(getState(), source); if (!symbols) { - return; + return null; } if (!symbols.callExpressions) { - return; + return null; } const localAstScope = originalAstScopes[0]; diff --git a/devtools/client/debugger/src/actions/pause/inlinePreview.js b/devtools/client/debugger/src/actions/pause/inlinePreview.js index 854cafd6aa422..90284f6b5a013 100644 --- a/devtools/client/debugger/src/actions/pause/inlinePreview.js +++ b/devtools/client/debugger/src/actions/pause/inlinePreview.js @@ -27,14 +27,14 @@ function getLocalScopeLevels(originalAstScopes) { export function generateInlinePreview(cx, frame) { return async function({ dispatch, getState, parser, client }) { if (!frame || !features.inlinePreview) { - return; + return null; } const { thread } = cx; // Avoid regenerating inline previews when we already have preview data if (getInlinePreviews(getState(), thread, frame.id)) { - return; + return null; } const originalFrameScopes = getOriginalFrameScope( @@ -53,20 +53,20 @@ export function generateInlinePreview(cx, frame) { let scopes = originalFrameScopes?.scope || generatedFrameScopes?.scope; if (!scopes || !scopes.bindings) { - return; + return null; } // It's important to use selectedLocation, because we don't know // if we'll be viewing the original or generated frame location const selectedLocation = getSelectedLocation(getState()); if (!selectedLocation) { - return; + return null; } const originalAstScopes = await parser.getScopes(selectedLocation); validateThreadContext(getState(), cx); if (!originalAstScopes) { - return; + return null; } const allPreviews = []; diff --git a/devtools/client/debugger/src/actions/pause/mapScopes.js b/devtools/client/debugger/src/actions/pause/mapScopes.js index 78afe32fa988c..bb4bc31bba35f 100644 --- a/devtools/client/debugger/src/actions/pause/mapScopes.js +++ b/devtools/client/debugger/src/actions/pause/mapScopes.js @@ -84,7 +84,8 @@ export async function buildOriginalScopes( export function toggleMapScopes() { return async function({ dispatch, getState, client, sourceMaps }) { if (isMapScopesEnabled(getState())) { - return dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: false }); + dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: false }); + return; } dispatch({ type: "TOGGLE_MAP_SCOPES", mapScopes: true }); diff --git a/devtools/client/debugger/src/actions/pause/selectFrame.js b/devtools/client/debugger/src/actions/pause/selectFrame.js index b3879679f7e0a..50203a4da5b64 100644 --- a/devtools/client/debugger/src/actions/pause/selectFrame.js +++ b/devtools/client/debugger/src/actions/pause/selectFrame.js @@ -18,7 +18,8 @@ export function selectFrame(cx, frame) { // Frames that aren't on-stack do not support evalling and may not // have live inspectable scopes, so we do not allow selecting them. if (frame.state !== "on-stack") { - return dispatch(selectLocation(cx, frame.location)); + dispatch(selectLocation(cx, frame.location)); + return; } dispatch({ diff --git a/devtools/client/debugger/src/actions/pause/tests/pause.spec.js b/devtools/client/debugger/src/actions/pause/tests/pause.spec.js index 9464d747c51f8..c9b0115faf7b3 100644 --- a/devtools/client/debugger/src/actions/pause/tests/pause.spec.js +++ b/devtools/client/debugger/src/actions/pause/tests/pause.spec.js @@ -64,6 +64,8 @@ const mockCommandClient = { contentType: "text/rust", }); } + + return resolve(); }); }, getSourceActorBreakpointPositions: async () => ({}), diff --git a/devtools/client/debugger/src/actions/preview.js b/devtools/client/debugger/src/actions/preview.js index ceb874c990c62..15925508dd606 100644 --- a/devtools/client/debugger/src/actions/preview.js +++ b/devtools/client/debugger/src/actions/preview.js @@ -25,7 +25,7 @@ import { getMappedExpression } from "./expressions"; function findExpressionMatch(state, codeMirror, tokenPos) { const source = getSelectedSource(state); if (!source) { - return; + return null; } const symbols = getSymbols(state, source); @@ -167,7 +167,7 @@ export function clearPreview(cx) { return ({ dispatch, getState, client }) => { const currentSelection = getPreview(getState()); if (!currentSelection) { - return; + return null; } return dispatch({ diff --git a/devtools/client/debugger/src/actions/sources/newSources.js b/devtools/client/debugger/src/actions/sources/newSources.js index d0dcea77b536f..c78ea4ae4a8ee 100644 --- a/devtools/client/debugger/src/actions/sources/newSources.js +++ b/devtools/client/debugger/src/actions/sources/newSources.js @@ -65,6 +65,8 @@ function loadSourceMaps(cx, sources) { throw error; } } + + return []; }; } @@ -136,7 +138,8 @@ function checkSelectedSource(cx, sourceId) { if (rawPendingUrl === source.url) { if (isPrettyURL(pendingUrl)) { const prettySource = await dispatch(togglePrettyPrint(cx, source.id)); - return dispatch(checkPendingBreakpoints(cx, prettySource.id)); + dispatch(checkPendingBreakpoints(cx, prettySource.id)); + return; } await dispatch( diff --git a/devtools/client/debugger/src/actions/sources/select.js b/devtools/client/debugger/src/actions/sources/select.js index 5037b7302902f..e827709362fab 100644 --- a/devtools/client/debugger/src/actions/sources/select.js +++ b/devtools/client/debugger/src/actions/sources/select.js @@ -131,7 +131,8 @@ export function selectLocation(cx, location, { keepContext = true } = {}) { if (!source) { // If there is no source we deselect the current selected source - return dispatch(clearSelectedLocation(cx)); + dispatch(clearSelectedLocation(cx)); + return; } const activeSearch = getActiveSearch(getState()); @@ -228,7 +229,7 @@ export function selectSpecificLocation(cx, location) { export function jumpToMappedLocation(cx, location) { return async function({ dispatch, getState, client, sourceMaps }) { if (!client) { - return; + return null; } // Map to either an original or a generated source location diff --git a/devtools/client/debugger/src/actions/ui.js b/devtools/client/debugger/src/actions/ui.js index b6f49a91fb1de..a8830cd26b1f6 100644 --- a/devtools/client/debugger/src/actions/ui.js +++ b/devtools/client/debugger/src/actions/ui.js @@ -165,7 +165,7 @@ export function clearHighlightLineRange() { export function openConditionalPanel(location, log = false) { if (!location) { - return; + return null; } return { diff --git a/devtools/client/debugger/src/client/firefox/commands.js b/devtools/client/debugger/src/client/firefox/commands.js index c9684e5b43253..79d114750867c 100644 --- a/devtools/client/debugger/src/client/firefox/commands.js +++ b/devtools/client/debugger/src/client/firefox/commands.js @@ -50,13 +50,15 @@ async function loadObjectProperties(root, threadActorID) { function releaseActor(actor) { if (!actor) { - return; + return Promise.resolve(); } const objFront = commands.client.getFrontByID(actor); - if (objFront) { - return objFront.release().catch(() => {}); + if (!objFront) { + return Promise.resolve(); } + + return objFront.release().catch(() => {}); } function lookupTarget(thread) { @@ -133,7 +135,8 @@ async function setXHRBreakpoint(path, method) { const hasWatcherSupport = commands.targetCommand.hasTargetWatcherSupport(); if (!hasWatcherSupport) { // Without watcher support, forward setXHRBreakpoint to all threads. - return forEachThread(thread => thread.setXHRBreakpoint(path, method)); + await forEachThread(thread => thread.setXHRBreakpoint(path, method)); + return; } const breakpointsFront = await commands.targetCommand.watcherFront.getBreakpointListActor(); await breakpointsFront.setXHRBreakpoint(path, method); @@ -143,7 +146,8 @@ async function removeXHRBreakpoint(path, method) { const hasWatcherSupport = commands.targetCommand.hasTargetWatcherSupport(); if (!hasWatcherSupport) { // Without watcher support, forward removeXHRBreakpoint to all threads. - return forEachThread(thread => thread.removeXHRBreakpoint(path, method)); + await forEachThread(thread => thread.removeXHRBreakpoint(path, method)); + return; } const breakpointsFront = await commands.targetCommand.watcherFront.getBreakpointListActor(); await breakpointsFront.removeXHRBreakpoint(path, method); @@ -155,18 +159,20 @@ export function toggleJavaScriptEnabled(enabled) { }); } -function addWatchpoint(object, property, label, watchpointType) { - if (currentTarget().getTrait("watchpoints")) { - const objectFront = createObjectFront(object); - return objectFront.addWatchpoint(property, label, watchpointType); +async function addWatchpoint(object, property, label, watchpointType) { + if (!currentTarget().getTrait("watchpoints")) { + return; } + const objectFront = createObjectFront(object); + await objectFront.addWatchpoint(property, label, watchpointType); } async function removeWatchpoint(object, property) { - if (currentTarget().getTrait("watchpoints")) { - const objectFront = createObjectFront(object); - await objectFront.removeWatchpoint(property); + if (!currentTarget().getTrait("watchpoints")) { + return; } + const objectFront = createObjectFront(object); + await objectFront.removeWatchpoint(property); } function hasBreakpoint(location) { @@ -183,7 +189,7 @@ async function setBreakpoint(location, options) { breakpoint && JSON.stringify(breakpoint.options) == JSON.stringify(options) ) { - return; + return null; } breakpoints[makePendingLocationId(location)] = { location, options }; @@ -216,6 +222,8 @@ async function setBreakpoint(location, options) { ) { return thread.setBreakpoint(location, serverOptions); } + + return Promise.resolve(); }); } @@ -240,6 +248,8 @@ async function removeBreakpoint(location) { ) { return thread.removeBreakpoint(location); } + + return Promise.resolve(); }); } @@ -358,7 +368,8 @@ async function setSkipPausing(shouldSkip) { async function setEventListenerBreakpoints(ids) { const hasWatcherSupport = commands.targetCommand.hasTargetWatcherSupport(); if (!hasWatcherSupport) { - return forEachThread(thread => thread.setActiveEventBreakpoints(ids)); + await forEachThread(thread => thread.setActiveEventBreakpoints(ids)); + return; } const breakpointListFront = await commands.targetCommand.watcherFront.getBreakpointListActor(); await breakpointListFront.setActiveEventBreakpoints(ids); From 945807a7a248b09b9792b8dcadcdb74bdfdf7ca4 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 Aug 2022 11:56:51 +0000 Subject: [PATCH 50/65] Bug 1755660 - Test redirected HTTPS requests through proxy r=necko-reviewers,dragana The patch also changes test_server.js to await server shutdown when necessary. Differential Revision: https://phabricator.services.mozilla.com/D155334 --- netwerk/test/unit/test_servers.js | 82 ++++++++++++++++++++++++++++--- 1 file changed, 76 insertions(+), 6 deletions(-) diff --git a/netwerk/test/unit/test_servers.js b/netwerk/test/unit/test_servers.js index 131f204db8740..1dd5b8da3fb9e 100644 --- a/netwerk/test/unit/test_servers.js +++ b/netwerk/test/unit/test_servers.js @@ -71,6 +71,8 @@ add_task(async function test_http() { equal(req.status, Cr.NS_OK); equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "http/1.1"); + + await server.stop(); }); add_task(async function test_https() { @@ -101,6 +103,8 @@ add_task(async function test_https() { equal(req.status, Cr.NS_OK); equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "http/1.1"); + + await server.stop(); }); add_task(async function test_http2() { @@ -131,6 +135,8 @@ add_task(async function test_http2() { equal(req.status, Cr.NS_OK); equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); equal(req.QueryInterface(Ci.nsIHttpChannel).protocolVersion, "h2"); + + await server.stop(); }); add_task(async function test_http1_proxy() { @@ -141,8 +147,8 @@ add_task(async function test_http1_proxy() { let proxy = new NodeHTTPProxyServer(); await proxy.start(); - registerCleanupFunction(() => { - proxy.stop(); + registerCleanupFunction(async () => { + await proxy.stop(); }); let chan = makeChan(`http://localhost:${proxy.port()}/test`); @@ -181,6 +187,8 @@ add_task(async function test_http1_proxy() { ); } ); + + await proxy.stop(); }); add_task(async function test_https_proxy() { @@ -191,8 +199,8 @@ add_task(async function test_https_proxy() { let proxy = new NodeHTTPSProxyServer(); await proxy.start(); - registerCleanupFunction(() => { - proxy.stop(); + registerCleanupFunction(async () => { + await proxy.stop(); }); let chan = makeChan(`https://localhost:${proxy.port()}/test`); @@ -227,6 +235,8 @@ add_task(async function test_https_proxy() { equal(buff, server.constructor.name); } ); + + await proxy.stop(); }); add_task(async function test_http2_proxy() { @@ -237,8 +247,8 @@ add_task(async function test_http2_proxy() { let proxy = new NodeHTTP2ProxyServer(); await proxy.start(); - registerCleanupFunction(() => { - proxy.stop(); + registerCleanupFunction(async () => { + await proxy.stop(); }); let chan = makeChan(`https://localhost:${proxy.port()}/test`); @@ -273,4 +283,64 @@ add_task(async function test_http2_proxy() { equal(buff, server.constructor.name); } ); + + await proxy.stop(); +}); + +add_task(async function test_proxy_with_redirects() { + let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( + Ci.nsIX509CertDB + ); + addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); + + let proxies = [ + NodeHTTPProxyServer, + NodeHTTPSProxyServer, + NodeHTTP2ProxyServer, + ]; + for (let p of proxies) { + let proxy = new p(); + await proxy.start(); + registerCleanupFunction(async () => { + await proxy.stop(); + }); + + await with_node_servers( + [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server], + async server => { + info(`Testing ${p.name} with ${server.constructor.name}`); + await server.execute( + `global.server_name = "${server.constructor.name}";` + ); + await server.registerPathHandler("/redirect", (req, resp) => { + resp.writeHead(302, { + Location: "/test", + }); + resp.end(global.server_name); + }); + await server.registerPathHandler("/test", (req, resp) => { + resp.writeHead(200); + resp.end(global.server_name); + }); + + let chan = makeChan(`${server.origin()}/redirect`); + let { req, buff } = await new Promise(resolve => { + chan.asyncOpen( + new ChannelListener( + (req, buff) => resolve({ req, buff }), + null, + CL_ALLOW_UNKNOWN_CL + ) + ); + }); + equal(req.status, Cr.NS_OK); + equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); + equal(buff, server.constructor.name); + req.QueryInterface(Ci.nsIProxiedChannel); + ok(!!req.proxyInfo); + notEqual(req.proxyInfo.type, "direct"); + } + ); + await proxy.stop(); + } }); From f6a797d5c412bf24fc1ff768f83bd222c867e168 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 Aug 2022 11:56:51 +0000 Subject: [PATCH 51/65] Bug 1755660 - Allow node proxies to handle POST requests r=necko-reviewers,dragana We move from http.get to http.request to be able to pass the method to the proxied request. We alsoread the data from the stream and pipe it to the new request. Differential Revision: https://phabricator.services.mozilla.com/D155390 --- netwerk/test/unit/head_servers.js | 104 ++++++++++++++++++++---------- 1 file changed, 70 insertions(+), 34 deletions(-) diff --git a/netwerk/test/unit/head_servers.js b/netwerk/test/unit/head_servers.js index 21e7e331bc476..132f5ef2b54f4 100644 --- a/netwerk/test/unit/head_servers.js +++ b/netwerk/test/unit/head_servers.js @@ -219,25 +219,41 @@ class BaseProxyCode { return; } + let url = new URL(req.url); const http = require("http"); - http - .get(req.url, { method: req.method }, proxyresp => { - res.writeHead( - proxyresp.statusCode, - proxyresp.statusMessage, - proxyresp.headers - ); - let rawData = ""; - proxyresp.on("data", chunk => { - rawData += chunk; - }); - proxyresp.on("end", () => { - res.end(rawData); - }); - }) + let preq = http + .request( + { + method: req.method, + path: url.pathname, + port: url.port, + host: url.hostname, + protocol: url.protocol, + }, + proxyresp => { + res.writeHead( + proxyresp.statusCode, + proxyresp.statusMessage, + proxyresp.headers + ); + let rawData = ""; + proxyresp.on("data", chunk => { + rawData += chunk; + }); + proxyresp.on("end", () => { + res.end(rawData); + }); + } + ) .on("error", e => { console.log(`sock err: ${e}`); }); + if (req.method != "POST") { + preq.end(); + } else { + req.on("data", chunk => preq.write(chunk)); + req.on("end", () => preq.end()); + } } static onConnect(req, clientSocket, head) { @@ -433,27 +449,47 @@ class HTTP2ProxyCode { global.proxy.on("stream", (stream, headers) => { if (headers[":scheme"] === "http") { const http = require("http"); - let url = `${headers[":scheme"]}://${headers[":authority"]}${headers[":path"]}`; - http - .get(url, { method: headers[":method"] }, proxyresp => { - let headers = Object.assign({}, proxyresp.headers); - // Filter out some prohibited headers. - ["connection", "transfer-encoding", "keep-alive"].forEach(prop => { - delete headers[prop]; - }); - stream.respond( - Object.assign({ ":status": proxyresp.statusCode }, headers) - ); - proxyresp.on("data", chunk => { - stream.write(chunk); - }); - proxyresp.on("end", () => { - stream.end(); - }); - }) + let url = new URL( + `${headers[":scheme"]}://${headers[":authority"]}${headers[":path"]}` + ); + let req = http + .request( + { + method: headers[":method"], + path: headers[":path"], + port: url.port, + host: url.hostname, + protocol: url.protocol, + }, + proxyresp => { + let headers = Object.assign({}, proxyresp.headers); + // Filter out some prohibited headers. + ["connection", "transfer-encoding", "keep-alive"].forEach( + prop => { + delete headers[prop]; + } + ); + stream.respond( + Object.assign({ ":status": proxyresp.statusCode }, headers) + ); + proxyresp.on("data", chunk => { + stream.write(chunk); + }); + proxyresp.on("end", () => { + stream.end(); + }); + } + ) .on("error", e => { console.log(`sock err: ${e}`); }); + + if (headers[":method"] != "POST") { + req.end(); + } else { + stream.on("data", chunk => req.write(chunk)); + stream.on("end", () => req.end()); + } return; } if (headers[":method"] !== "CONNECT") { @@ -492,7 +528,7 @@ class HTTP2ProxyCode { socket.on("close", () => { stream.close(); }); - stream.on("close", () => { + stream.on("end", () => { socket.end(); }); stream.on("aborted", () => { From 7c7eb2b453bfe90b97f48c0b7053decf0f044948 Mon Sep 17 00:00:00 2001 From: Valentin Gosu Date: Wed, 31 Aug 2022 11:56:52 +0000 Subject: [PATCH 52/65] Bug 1755660 - Add proxy test for POST request with a slow writer r=necko-reviewers,dragana Differential Revision: https://phabricator.services.mozilla.com/D155335 --- netwerk/test/unit/test_proxy-slow-upload.js | 104 ++++++++++++++++++++ netwerk/test/unit/xpcshell.ini | 1 + 2 files changed, 105 insertions(+) create mode 100644 netwerk/test/unit/test_proxy-slow-upload.js diff --git a/netwerk/test/unit/test_proxy-slow-upload.js b/netwerk/test/unit/test_proxy-slow-upload.js new file mode 100644 index 0000000000000..db0bd39c043d8 --- /dev/null +++ b/netwerk/test/unit/test_proxy-slow-upload.js @@ -0,0 +1,104 @@ +/* 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/. */ + +"use strict"; + +/* import-globals-from head_cache.js */ +/* import-globals-from head_cookies.js */ +/* import-globals-from head_channels.js */ +/* import-globals-from head_servers.js */ + +const SIZE = 4096; +const CONTENT = "x".repeat(SIZE); + +add_task(async function test_slow_upload() { + let certdb = Cc["@mozilla.org/security/x509certdb;1"].getService( + Ci.nsIX509CertDB + ); + addCertFromFile(certdb, "http2-ca.pem", "CTu,u,u"); + + let proxies = [ + NodeHTTPProxyServer, + NodeHTTPSProxyServer, + NodeHTTP2ProxyServer, + ]; + for (let p of proxies) { + let proxy = new p(); + await proxy.start(); + registerCleanupFunction(async () => { + await proxy.stop(); + }); + + await with_node_servers( + [NodeHTTPServer, NodeHTTPSServer, NodeHTTP2Server], + async server => { + info(`Testing ${p.name} with ${server.constructor.name}`); + await server.execute( + `global.server_name = "${server.constructor.name}";` + ); + await server.registerPathHandler("/test", (req, resp) => { + let content = ""; + req.on("data", data => { + global.data_count = (global.data_count || 0) + 1; + content += data; + }); + req.on("end", () => { + resp.writeHead(200); + resp.end(content); + }); + }); + + let sstream = Cc[ + "@mozilla.org/io/string-input-stream;1" + ].createInstance(Ci.nsIStringInputStream); + sstream.data = CONTENT; + + let mime = Cc[ + "@mozilla.org/network/mime-input-stream;1" + ].createInstance(Ci.nsIMIMEInputStream); + mime.addHeader("Content-Type", "multipart/form-data; boundary=zzzzz"); + mime.setData(sstream); + + let tq = Cc["@mozilla.org/network/throttlequeue;1"].createInstance( + Ci.nsIInputChannelThrottleQueue + ); + // Make sure the request takes more than one read. + tq.init(100 + SIZE / 2, 100 + SIZE / 2); + + let chan = NetUtil.newChannel({ + uri: `${server.origin()}/test`, + loadUsingSystemPrincipal: true, + }).QueryInterface(Ci.nsIHttpChannel); + + let tic = chan.QueryInterface(Ci.nsIThrottledInputChannel); + tic.throttleQueue = tq; + + chan + .QueryInterface(Ci.nsIUploadChannel) + .setUploadStream(mime, "", mime.available()); + chan.requestMethod = "POST"; + + let { req, buff } = await new Promise(resolve => { + chan.asyncOpen( + new ChannelListener( + (req, buff) => resolve({ req, buff }), + null, + CL_ALLOW_UNKNOWN_CL + ) + ); + }); + equal(req.status, Cr.NS_OK); + equal(req.QueryInterface(Ci.nsIHttpChannel).responseStatus, 200); + ok(buff == CONTENT, "Content must match"); + ok(!!req.QueryInterface(Ci.nsIProxiedChannel).proxyInfo); + greater( + await server.execute(`global.data_count`), + 1, + "Content should have been streamed to the server in several chunks" + ); + } + ); + await proxy.stop(); + } +}); diff --git a/netwerk/test/unit/xpcshell.ini b/netwerk/test/unit/xpcshell.ini index 1abea4b6e0b96..c29ed83c40dfa 100644 --- a/netwerk/test/unit/xpcshell.ini +++ b/netwerk/test/unit/xpcshell.ini @@ -634,6 +634,7 @@ run-sequentially = node server exceptions dont replay well skip-if = os == "android" # MultiInstanceLock doesn't work on Android [test_http2-proxy-failing.js] run-sequentially = node server exceptions dont replay well +[test_proxy-slow-upload.js] [test_websocket_server.js] run-sequentially = node server exceptions dont replay well [test_h2proxy_connection_limit.js] From 83c7733c7ce104c173ea5dd1d383bc12e96e5983 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 31 Aug 2022 12:39:19 +0000 Subject: [PATCH 53/65] Bug 1786147 - Move line-clamp out of mako and do some adjacent clean-up. r=boris No behavior change, but simplifies the following patch. Differential Revision: https://phabricator.services.mozilla.com/D155180 --- layout/generic/nsFlexContainerFrame.cpp | 2 +- layout/style/ServoBindings.toml | 1 + layout/style/nsStyleStruct.cpp | 6 +-- layout/style/nsStyleStruct.h | 2 +- servo/components/style/macros.rs | 40 -------------- servo/components/style/properties/data.py | 2 +- .../components/style/properties/gecko.mako.rs | 28 +--------- .../style/properties/helpers.mako.rs | 4 -- .../style/properties/longhands/box.mako.rs | 9 ++-- servo/components/style/values/animated/mod.rs | 1 + servo/components/style/values/computed/box.rs | 23 ++++++-- servo/components/style/values/computed/mod.rs | 6 +-- servo/components/style/values/generics/box.rs | 54 ++++++++++++++++++- servo/components/style/values/mod.rs | 3 -- .../components/style/values/specified/box.rs | 23 ++++++-- .../components/style/values/specified/mod.rs | 7 +-- servo/ports/geckolib/cbindgen.toml | 1 + 17 files changed, 108 insertions(+), 104 deletions(-) diff --git a/layout/generic/nsFlexContainerFrame.cpp b/layout/generic/nsFlexContainerFrame.cpp index 503c65a7f7afc..38e256b74d107 100644 --- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -5865,5 +5865,5 @@ uint32_t nsFlexContainerFrame::GetLineClampValue() const { return 0; } - return StyleDisplay()->mLineClamp; + return StyleDisplay()->mWebkitLineClamp; } diff --git a/layout/style/ServoBindings.toml b/layout/style/ServoBindings.toml index 9a8d6ba9b16c2..7104c23377c34 100644 --- a/layout/style/ServoBindings.toml +++ b/layout/style/ServoBindings.toml @@ -438,6 +438,7 @@ cbindgen-types = [ { gecko = "StyleTextJustify", servo = "crate::values::computed::TextJustify" }, { gecko = "StyleMozControlCharacterVisibility", servo = "crate::values::computed::text::MozControlCharacterVisibility" }, { gecko = "StyleLineBreak", servo = "crate::values::computed::LineBreak" }, + { gecko = "StyleLineClamp", servo = "crate::values::computed::LineClamp" }, { gecko = "StyleUserSelect", servo = "crate::values::computed::UserSelect" }, { gecko = "StyleBreakBetween", servo = "crate::values::computed::BreakBetween" }, { gecko = "StyleBreakWithin", servo = "crate::values::computed::BreakWithin" }, diff --git a/layout/style/nsStyleStruct.cpp b/layout/style/nsStyleStruct.cpp index 3e3b480babc8d..6d50e4f71c36d 100644 --- a/layout/style/nsStyleStruct.cpp +++ b/layout/style/nsStyleStruct.cpp @@ -2248,7 +2248,7 @@ nsStyleDisplay::nsStyleDisplay(const Document& aDocument) mPerspectiveOrigin(Position::FromPercentage(0.5f)), mVerticalAlign( StyleVerticalAlign::Keyword(StyleVerticalAlignKeyword::Baseline)), - mLineClamp(0), + mWebkitLineClamp(0), mShapeMargin(LengthPercentage::Zero()), mShapeOutside(StyleShapeOutside::None()) { MOZ_COUNT_CTOR(nsStyleDisplay); @@ -2301,7 +2301,7 @@ nsStyleDisplay::nsStyleDisplay(const nsStyleDisplay& aSource) mChildPerspective(aSource.mChildPerspective), mPerspectiveOrigin(aSource.mPerspectiveOrigin), mVerticalAlign(aSource.mVerticalAlign), - mLineClamp(aSource.mLineClamp), + mWebkitLineClamp(aSource.mWebkitLineClamp), mShapeImageThreshold(aSource.mShapeImageThreshold), mShapeMargin(aSource.mShapeMargin), mShapeOutside(aSource.mShapeOutside) { @@ -2527,7 +2527,7 @@ nsChangeHint nsStyleDisplay::CalcDifference( } } - if (mLineClamp != aNewData.mLineClamp) { + if (mWebkitLineClamp != aNewData.mWebkitLineClamp) { hint |= NS_STYLE_HINT_REFLOW; } diff --git a/layout/style/nsStyleStruct.h b/layout/style/nsStyleStruct.h index a9f024e664f66..709e795033df8 100644 --- a/layout/style/nsStyleStruct.h +++ b/layout/style/nsStyleStruct.h @@ -1316,7 +1316,7 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay { mozilla::StyleVerticalAlign mVerticalAlign; - uint32_t mLineClamp; + mozilla::StyleLineClamp mWebkitLineClamp; // The threshold used for extracting a shape from shape-outside: . float mShapeImageThreshold = 0.0f; diff --git a/servo/components/style/macros.rs b/servo/components/style/macros.rs index 3084d33dd3d78..5f3a1ea463bbd 100644 --- a/servo/components/style/macros.rs +++ b/servo/components/style/macros.rs @@ -65,46 +65,6 @@ macro_rules! try_match_ident_ignore_ascii_case { }} } -macro_rules! define_keyword_type { - ($name:ident, $css:expr) => { - #[allow(missing_docs)] - #[derive( - Animate, - Clone, - ComputeSquaredDistance, - Copy, - MallocSizeOf, - PartialEq, - SpecifiedValueInfo, - ToAnimatedValue, - ToAnimatedZero, - ToComputedValue, - ToCss, - ToResolvedValue, - ToShmem, - )] - pub struct $name; - - impl fmt::Debug for $name { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str($css) - } - } - - impl $crate::parser::Parse for $name { - fn parse<'i, 't>( - _context: &$crate::parser::ParserContext, - input: &mut ::cssparser::Parser<'i, 't>, - ) -> Result<$name, ::style_traits::ParseError<'i>> { - input - .expect_ident_matching($css) - .map(|_| $name) - .map_err(|e| e.into()) - } - } - }; -} - #[cfg(feature = "servo")] macro_rules! local_name { ($s:tt) => { diff --git a/servo/components/style/properties/data.py b/servo/components/style/properties/data.py index 8024504a4fb94..3e7e1f511b775 100644 --- a/servo/components/style/properties/data.py +++ b/servo/components/style/properties/data.py @@ -485,6 +485,7 @@ def specified_is_copy(self): "JustifyItems", "JustifySelf", "LineBreak", + "LineClamp", "MasonryAutoFlow", "MozForceBrokenImageIcon", "text::MozControlCharacterVisibility", @@ -502,7 +503,6 @@ def specified_is_copy(self): "OverflowWrap", "OverscrollBehavior", "Percentage", - "PositiveIntegerOrNone", "PrintColorAdjust", "Resize", "RubyPosition", diff --git a/servo/components/style/properties/gecko.mako.rs b/servo/components/style/properties/gecko.mako.rs index e74f1ef9990a5..7bb4571cb2058 100644 --- a/servo/components/style/properties/gecko.mako.rs +++ b/servo/components/style/properties/gecko.mako.rs @@ -38,7 +38,7 @@ use crate::selector_parser::PseudoElement; use servo_arc::{Arc, RawOffsetArc, UniqueArc}; use std::mem::{forget, MaybeUninit}; use std::{cmp, ops, ptr}; -use crate::values::{self, CustomIdent, Either, KeyframesName, None_}; +use crate::values::{self, CustomIdent, KeyframesName}; use crate::values::computed::{Percentage, TransitionProperty}; use crate::values::computed::BorderStyle; use crate::values::computed::font::FontSize; @@ -1127,9 +1127,7 @@ fn static_assert() { ${impl_copy_animation_value(ident, gecko_ffi_name)} -<% skip_box_longhands= """display - clear - -webkit-line-clamp""" %> +<% skip_box_longhands= """display clear""" %> <%self:impl_trait style_struct_name="Box" skip_longhands="${skip_box_longhands}"> #[inline] pub fn set_display(&mut self, v: longhands::display::computed_value::T) { @@ -1169,28 +1167,6 @@ fn static_assert() { gecko_inexhaustive=True, ) %> ${impl_keyword('clear', 'mBreakType', clear_keyword)} - - #[allow(non_snake_case)] - pub fn set__webkit_line_clamp(&mut self, v: longhands::_webkit_line_clamp::computed_value::T) { - self.gecko.mLineClamp = match v { - Either::First(n) => n.0 as u32, - Either::Second(None_) => 0, - }; - } - - ${impl_simple_copy('_webkit_line_clamp', 'mLineClamp')} - - #[allow(non_snake_case)] - pub fn clone__webkit_line_clamp(&self) -> longhands::_webkit_line_clamp::computed_value::T { - match self.gecko.mLineClamp { - 0 => Either::Second(None_), - n => { - debug_assert!(n <= std::i32::MAX as u32); - Either::First((n as i32).into()) - } - } - } - <%def name="simple_image_array_property(name, shorthand, field_name)"> diff --git a/servo/components/style/properties/helpers.mako.rs b/servo/components/style/properties/helpers.mako.rs index 91be2d6011bc9..5e3c4165cb485 100644 --- a/servo/components/style/properties/helpers.mako.rs +++ b/servo/components/style/properties/helpers.mako.rs @@ -119,8 +119,6 @@ use crate::values::computed::{Context, ToComputedValue}; #[allow(unused_imports)] use crate::values::{computed, specified}; - #[allow(unused_imports)] - use crate::values::{Auto, Either, None_}; ${caller.body()} } @@ -414,8 +412,6 @@ #[allow(unused_imports)] use crate::properties::{UnparsedValue, ShorthandId}; #[allow(unused_imports)] - use crate::values::{Auto, Either, None_}; - #[allow(unused_imports)] use crate::error_reporting::ParseErrorReporter; #[allow(unused_imports)] use crate::properties::longhands; diff --git a/servo/components/style/properties/longhands/box.mako.rs b/servo/components/style/properties/longhands/box.mako.rs index 78f732c942d6f..f6fdbd1d3d8fa 100644 --- a/servo/components/style/properties/longhands/box.mako.rs +++ b/servo/components/style/properties/longhands/box.mako.rs @@ -543,16 +543,13 @@ ${helpers.predefined_type( spec="https://compat.spec.whatwg.org/#touch-action", )} -// Note that we only implement -webkit-line-clamp as a single, longhand -// property for now, but the spec defines line-clamp as a shorthand for separate -// max-lines, block-ellipsis, and continue properties. ${helpers.predefined_type( "-webkit-line-clamp", - "PositiveIntegerOrNone", - "Either::Second(None_)", + "LineClamp", + "computed::LineClamp::none()", engines="gecko", gecko_pref="layout.css.webkit-line-clamp.enabled", - animation_value_type="Integer", + animation_value_type="ComputedValue", spec="https://drafts.csswg.org/css-overflow-3/#line-clamp", )} diff --git a/servo/components/style/values/animated/mod.rs b/servo/components/style/values/animated/mod.rs index 226c01a9a497e..b36946c492668 100644 --- a/servo/components/style/values/animated/mod.rs +++ b/servo/components/style/values/animated/mod.rs @@ -378,6 +378,7 @@ trivial_to_animated_value!(ComputedAngle); trivial_to_animated_value!(ComputedUrl); trivial_to_animated_value!(bool); trivial_to_animated_value!(f32); +trivial_to_animated_value!(i32); // Note: This implementation is for ToAnimatedValue of ShapeSource. // // SVGPathData uses Box<[T]>. If we want to derive ToAnimatedValue for all the diff --git a/servo/components/style/values/computed/box.rs b/servo/components/style/values/computed/box.rs index 28d704b015c2b..2758289295be2 100644 --- a/servo/components/style/values/computed/box.rs +++ b/servo/components/style/values/computed/box.rs @@ -4,12 +4,11 @@ //! Computed types for box properties. +use crate::values::animated::{Animate, Procedure}; use crate::values::computed::length::{LengthPercentage, NonNegativeLength}; -use crate::values::computed::{Context, Number, ToComputedValue}; +use crate::values::computed::{Context, Integer, Number, ToComputedValue}; use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; -use crate::values::generics::box_::Perspective as GenericPerspective; -use crate::values::generics::box_::VerticalAlign as GenericVerticalAlign; -use crate::values::generics::box_::ContainIntrinsicSize as GenericContainIntrinsicSize; +use crate::values::generics::box_::{GenericLineClamp, GenericPerspective, GenericVerticalAlign, GenericContainIntrinsicSize}; use crate::values::specified::box_ as specified; pub use crate::values::specified::box_::{ @@ -30,6 +29,22 @@ pub type AnimationIterationCount = GenericAnimationIterationCount; /// A computed value for the `contain-intrinsic-size` property. pub type ContainIntrinsicSize = GenericContainIntrinsicSize; +/// A computed value for the `line-clamp` property. +pub type LineClamp = GenericLineClamp; + +impl Animate for LineClamp { + #[inline] + fn animate(&self, other: &Self, procedure: Procedure) -> Result { + if self.is_none() != other.is_none() { + return Err(()); + } + if self.is_none() { + return Ok(Self::none()) + } + Ok(Self(self.0.animate(&other.0, procedure)?.max(1))) + } +} + impl AnimationIterationCount { /// Returns the value `1.0`. #[inline] diff --git a/servo/components/style/values/computed/mod.rs b/servo/components/style/values/computed/mod.rs index 11ee74b09fbb1..b6352df71db18 100644 --- a/servo/components/style/values/computed/mod.rs +++ b/servo/components/style/values/computed/mod.rs @@ -45,7 +45,7 @@ pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; pub use self::border::{BorderImageSlice, BorderImageWidth}; pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain, ContainerName, ContainerType}; pub use self::box_::{Appearance, BreakBetween, BreakWithin, Clear, ContentVisibility, ContainIntrinsicSize, Float}; -pub use self::box_::{Display, Overflow, OverflowAnchor, TransitionProperty}; +pub use self::box_::{Display, LineClamp, Overflow, OverflowAnchor, TransitionProperty}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter}; pub use self::box_::{ScrollAxis, ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop}; pub use self::box_::{ScrollSnapStrictness, ScrollSnapType, ScrollTimelineName}; @@ -99,7 +99,6 @@ pub use self::ui::{Cursor, MozForceBrokenImageIcon, UserSelect}; pub use super::specified::TextTransform; pub use super::specified::ViewportVariant; pub use super::specified::{BorderStyle, TextDecorationLine}; -pub use super::{Auto, Either, None_}; pub use app_units::Au; #[cfg(feature = "gecko")] @@ -839,9 +838,6 @@ impl From for PositiveInteger { } } -/// A computed positive `` value or `none`. -pub type PositiveIntegerOrNone = Either; - /// rect(...) | auto pub type ClipRect = generics::GenericClipRect; diff --git a/servo/components/style/values/generics/box.rs b/servo/components/style/values/generics/box.rs index b64852e645a51..073d2edbe652c 100644 --- a/servo/components/style/values/generics/box.rs +++ b/servo/components/style/values/generics/box.rs @@ -107,7 +107,6 @@ pub enum GenericContainIntrinsicSize { pub use self::GenericContainIntrinsicSize as ContainIntrinsicSize; impl ToCss for ContainIntrinsicSize { - fn to_css(&self, dest: &mut CssWriter) -> fmt::Result where W: Write, @@ -123,6 +122,55 @@ impl ToCss for ContainIntrinsicSize { } } +/// Note that we only implement -webkit-line-clamp as a single, longhand +/// property for now, but the spec defines line-clamp as a shorthand for +/// separate max-lines, block-ellipsis, and continue properties. +/// +/// https://drafts.csswg.org/css-overflow-3/#line-clamp +#[derive( + Clone, + ComputeSquaredDistance, + Copy, + Debug, + MallocSizeOf, + PartialEq, + SpecifiedValueInfo, + ToComputedValue, + ToAnimatedValue, + ToAnimatedZero, + ToResolvedValue, + ToShmem, +)] +#[repr(transparent)] +#[value_info(other_values = "auto")] +pub struct GenericLineClamp(pub I); + +pub use self::GenericLineClamp as LineClamp; + +impl LineClamp { + /// Returns the `none` value. + pub fn none() -> Self { + Self(crate::Zero::zero()) + } + + /// Returns whether we're the `none` value. + pub fn is_none(&self) -> bool { + self.0.is_zero() + } +} + +impl ToCss for LineClamp { + fn to_css(&self, dest: &mut CssWriter) -> fmt::Result + where + W: Write, + { + if self.is_none() { + return dest.write_str("none"); + } + self.0.to_css(dest) + } +} + /// https://drafts.csswg.org/css-animations/#animation-iteration-count #[derive( Clone, @@ -135,13 +183,15 @@ impl ToCss for ContainIntrinsicSize { ToResolvedValue, ToShmem, )] -pub enum AnimationIterationCount { +pub enum GenericAnimationIterationCount { /// A `` value. Number(Number), /// The `infinite` keyword. Infinite, } +pub use self::GenericAnimationIterationCount as AnimationIterationCount; + /// A generic value for the `perspective` property. #[derive( Animate, diff --git a/servo/components/style/values/mod.rs b/servo/components/style/values/mod.rs index ebe56b41a3c49..1574a0b50509b 100644 --- a/servo/components/style/values/mod.rs +++ b/servo/components/style/values/mod.rs @@ -48,9 +48,6 @@ pub fn normalize(v: CSSFloat) -> CSSFloat { /// A CSS integer value. pub type CSSInteger = i32; -define_keyword_type!(None_, "none"); -define_keyword_type!(Auto, "auto"); - /// Serialize an identifier which is represented as an atom. #[cfg(feature = "gecko")] pub fn serialize_atom_identifier(ident: &Atom, dest: &mut W) -> fmt::Result diff --git a/servo/components/style/values/specified/box.rs b/servo/components/style/values/specified/box.rs index 38b001830fd9a..f271faffaf556 100644 --- a/servo/components/style/values/specified/box.rs +++ b/servo/components/style/values/specified/box.rs @@ -8,11 +8,10 @@ use crate::custom_properties::Name as CustomPropertyName; use crate::parser::{Parse, ParserContext}; use crate::properties::{LonghandId, PropertyDeclarationId}; use crate::properties::{PropertyId, ShorthandId}; -use crate::values::generics::box_::AnimationIterationCount as GenericAnimationIterationCount; -use crate::values::generics::box_::Perspective as GenericPerspective; +use crate::values::generics::box_::{GenericAnimationIterationCount, GenericPerspective, GenericLineClamp}; use crate::values::generics::box_::{GenericContainIntrinsicSize, GenericVerticalAlign, VerticalAlignKeyword}; use crate::values::specified::length::{LengthPercentage, NonNegativeLength}; -use crate::values::specified::{AllowQuirks, Number}; +use crate::values::specified::{AllowQuirks, Number, Integer}; use crate::values::{CustomIdent, KeyframesName, TimelineName}; use crate::Atom; use cssparser::Parser; @@ -646,6 +645,9 @@ impl SpecifiedValueInfo for Display { /// A specified value for the `contain-intrinsic-size` property. pub type ContainIntrinsicSize = GenericContainIntrinsicSize; +/// A specified value for the `line-clamp` property. +pub type LineClamp = GenericLineClamp; + /// A specified value for the `vertical-align` property. pub type VerticalAlign = GenericVerticalAlign; @@ -1471,6 +1473,21 @@ impl Parse for ContainIntrinsicSize { } } +impl Parse for LineClamp { + /// none | + fn parse<'i, 't>( + context: &ParserContext, + input: &mut Parser<'i, 't>, + ) -> Result> { + if let Ok(i) = input.try_parse(|i| crate::values::specified::PositiveInteger::parse(context, i)) + { + return Ok(Self(i.0)) + } + input.expect_ident_matching("none")?; + Ok(Self::none()) + } +} + /// https://drafts.csswg.org/css-contain-2/#content-visibility #[cfg_attr(feature = "servo", derive(Deserialize, Serialize))] #[derive( diff --git a/servo/components/style/values/specified/mod.rs b/servo/components/style/values/specified/mod.rs index fbb0a42d94fe0..fbd9c52934b67 100644 --- a/servo/components/style/values/specified/mod.rs +++ b/servo/components/style/values/specified/mod.rs @@ -13,7 +13,7 @@ use super::generics::grid::{GridLine as GenericGridLine, TrackBreadth as Generic use super::generics::grid::{TrackList as GenericTrackList, TrackSize as GenericTrackSize}; use super::generics::transform::IsParallelTo; use super::generics::{self, GreaterThanOrEqualToOne, NonNegative}; -use super::{CSSFloat, CSSInteger, Either, None_}; +use super::{CSSFloat, CSSInteger}; use crate::context::QuirksMode; use crate::parser::{Parse, ParserContext}; use crate::values::serialize_atom_identifier; @@ -38,7 +38,7 @@ pub use self::border::{BorderImageRepeat, BorderImageSideWidth}; pub use self::border::{BorderRadius, BorderSideWidth, BorderSpacing, BorderStyle}; pub use self::box_::{AnimationIterationCount, AnimationName, AnimationTimeline, Contain, Display}; pub use self::box_::{Appearance, BreakBetween, BreakWithin, ContainerName, ContainerType}; -pub use self::box_::{Clear, ContentVisibility, ContainIntrinsicSize, Float, Overflow, OverflowAnchor}; +pub use self::box_::{Clear, ContentVisibility, ContainIntrinsicSize, LineClamp, Float, Overflow, OverflowAnchor}; pub use self::box_::{OverflowClipBox, OverscrollBehavior, Perspective, Resize, ScrollbarGutter}; pub use self::box_::{ScrollAxis, ScrollSnapAlign, ScrollSnapAxis, ScrollSnapStop}; pub use self::box_::{ScrollSnapStrictness, ScrollSnapType, ScrollTimelineName}; @@ -708,9 +708,6 @@ impl Parse for PositiveInteger { } } -/// A specified positive `` value or `none`. -pub type PositiveIntegerOrNone = Either; - /// The specified value of a grid `` pub type TrackBreadth = GenericTrackBreadth; diff --git a/servo/ports/geckolib/cbindgen.toml b/servo/ports/geckolib/cbindgen.toml index 93bb9ab0c6d1a..af42bcecb7b64 100644 --- a/servo/ports/geckolib/cbindgen.toml +++ b/servo/ports/geckolib/cbindgen.toml @@ -148,6 +148,7 @@ include = [ "ZIndex", "TransformOrigin", "LineBreak", + "LineClamp", "WordBreak", "Contain", "ContainerType", From 3b00b2f6db9ef984c0cf71fb964ca2768f2f2896 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20Cobos=20=C3=81lvarez?= Date: Wed, 31 Aug 2022 15:42:46 +0200 Subject: [PATCH 54/65] Bug 1786147 - Fix typo to avoid devtools property-db failures. MANUAL PUSH: Orange fix CLOSED TREE --- servo/components/style/values/generics/box.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/servo/components/style/values/generics/box.rs b/servo/components/style/values/generics/box.rs index 073d2edbe652c..b8ade8a467ad3 100644 --- a/servo/components/style/values/generics/box.rs +++ b/servo/components/style/values/generics/box.rs @@ -142,7 +142,7 @@ impl ToCss for ContainIntrinsicSize { ToShmem, )] #[repr(transparent)] -#[value_info(other_values = "auto")] +#[value_info(other_values = "none")] pub struct GenericLineClamp(pub I); pub use self::GenericLineClamp as LineClamp; From f31cd07e4a274dfa68a3a85293ce7714e6da0973 Mon Sep 17 00:00:00 2001 From: Tooru Fujisawa Date: Wed, 31 Aug 2022 13:22:53 +0000 Subject: [PATCH 55/65] Bug 1771831 - Make it possible to reorder tabs in "List all tabs" list by drag-and-drop. r=NeilDeakin Also the tab list item can be dragged to the tab bar to reorder, or moved to other window, or detached. Differential Revision: https://phabricator.services.mozilla.com/D147709 --- .../content/browser-allTabsMenu.inc.xhtml | 3 + browser/base/content/browser-allTabsMenu.js | 2 + browser/base/content/browser.css | 16 ++ browser/base/content/tabbrowser-tabs.js | 23 +- .../test/performance/browser_tabdetach.js | 2 + browser/base/content/test/tabs/browser.ini | 1 + .../test/tabs/browser_tab_manager_drag.js | 226 ++++++++++++++++++ browser/base/content/test/tabs/head.js | 14 +- browser/modules/TabsList.jsm | 203 +++++++++++++++- 9 files changed, 480 insertions(+), 10 deletions(-) create mode 100644 browser/base/content/test/tabs/browser_tab_manager_drag.js diff --git a/browser/base/content/browser-allTabsMenu.inc.xhtml b/browser/base/content/browser-allTabsMenu.inc.xhtml index 24b38eece4a86..5f9aa3660bcd9 100644 --- a/browser/base/content/browser-allTabsMenu.inc.xhtml +++ b/browser/base/content/browser-allTabsMenu.inc.xhtml @@ -21,6 +21,9 @@ oncommand="PanelUI.showSubView('allTabsMenu-hiddenTabsView', this);" data-l10n-id="all-tabs-menu-hidden-tabs"/> + + + diff --git a/browser/base/content/browser-allTabsMenu.js b/browser/base/content/browser-allTabsMenu.js index bc1c07b5f7268..22e13f9e2e2ba 100644 --- a/browser/base/content/browser-allTabsMenu.js +++ b/browser/base/content/browser-allTabsMenu.js @@ -16,6 +16,7 @@ var gTabsPanel = { allTabsButton: "alltabs-button", allTabsView: "allTabsMenu-allTabsView", allTabsViewTabs: "allTabsMenu-allTabsViewTabs", + dropIndicator: "allTabsMenu-dropIndicator", containerTabsView: "allTabsMenu-containerTabsView", hiddenTabsButton: "allTabsMenu-hiddenTabsButton", hiddenTabsView: "allTabsMenu-hiddenTabsView", @@ -56,6 +57,7 @@ var gTabsPanel = { containerNode: this.allTabsViewTabs, filterFn: tab => !tab.hidden && (!tab.pinned || (showPinnedTabs && tab.pinned)), + dropIndicator: this.dropIndicator, }); this.allTabsView.addEventListener("ViewShowing", e => { diff --git a/browser/base/content/browser.css b/browser/base/content/browser.css index aca4f3c2becb2..aa6961d4d7647 100644 --- a/browser/base/content/browser.css +++ b/browser/base/content/browser.css @@ -511,6 +511,22 @@ toolbarpaletteitem[place="palette"] > #personal-bookmarks > #bookmarks-toolbar-b position: absolute; } +#allTabsMenu-dropIndicatorHolder { + display: block; + position: relative; +} + +#allTabsMenu-dropIndicator { + background: url(chrome://browser/skin/tabbrowser/tab-drag-indicator.svg) no-repeat center; + display: block; + position: absolute; + transform: rotate(-90deg); + width: 12px; + height: 29px; + left: 8px; + top: 0; +} + #nav-bar-customization-target > #personal-bookmarks, toolbar:not(#TabsToolbar) > #wrapper-personal-bookmarks, toolbar:not(#TabsToolbar) > #personal-bookmarks { diff --git a/browser/base/content/tabbrowser-tabs.js b/browser/base/content/tabbrowser-tabs.js index 1c301c550db3d..85ed1ca73c5e4 100644 --- a/browser/base/content/tabbrowser-tabs.js +++ b/browser/base/content/tabbrowser-tabs.js @@ -409,6 +409,10 @@ return; } + this.startTabDrag(event, tab); + } + + startTabDrag(event, tab, { fromTabList = false } = {}) { let selectedTabs = gBrowser.selectedTabs; let otherSelectedTabs = selectedTabs.filter( selectedTab => selectedTab != tab @@ -529,13 +533,14 @@ movingTabs: (tab.multiselected ? gBrowser.selectedTabs : [tab]).filter( t => t.pinned == tab.pinned ), + fromTabList, }; event.stopPropagation(); } on_dragover(event) { - var effects = this._getDropEffectForTabDrag(event); + var effects = this.getDropEffectForTabDrag(event); var ind = this._tabDropIndicator; if (effects == "" || effects == "none") { @@ -571,7 +576,8 @@ let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0); if ( (effects == "move" || effects == "copy") && - this == draggedTab.container + this == draggedTab.container && + !draggedTab._dragData.fromTabList ) { ind.hidden = true; @@ -692,9 +698,14 @@ newTranslateX -= tabWidth; } - let dropIndex = - "animDropIndex" in draggedTab._dragData && - draggedTab._dragData.animDropIndex; + let dropIndex; + if (draggedTab._dragData.fromTabList) { + dropIndex = this._getDropIndex(event, false); + } else { + dropIndex = + "animDropIndex" in draggedTab._dragData && + draggedTab._dragData.animDropIndex; + } let incrementDropIndex = true; if (dropIndex && dropIndex > movingTabs[0]._tPos) { dropIndex--; @@ -1982,7 +1993,7 @@ return tabs.length; } - _getDropEffectForTabDrag(event) { + getDropEffectForTabDrag(event) { var dt = event.dataTransfer; let isMovingTabs = dt.mozItemCount > 0; diff --git a/browser/base/content/test/performance/browser_tabdetach.js b/browser/base/content/test/performance/browser_tabdetach.js index 7468a98729b26..1908f879f1166 100644 --- a/browser/base/content/test/performance/browser_tabdetach.js +++ b/browser/base/content/test/performance/browser_tabdetach.js @@ -13,6 +13,7 @@ const EXPECTED_REFLOWS = [ { stack: [ "clientX@chrome://browser/content/tabbrowser-tabs.js", + "startTabDrag@chrome://browser/content/tabbrowser-tabs.js", "on_dragstart@chrome://browser/content/tabbrowser-tabs.js", "handleEvent@chrome://browser/content/tabbrowser-tabs.js", "synthesizeMouseAtPoint@chrome://mochikit/content/tests/SimpleTest/EventUtils.js", @@ -24,6 +25,7 @@ const EXPECTED_REFLOWS = [ { stack: [ + "startTabDrag@chrome://browser/content/tabbrowser-tabs.js", "on_dragstart@chrome://browser/content/tabbrowser-tabs.js", "handleEvent@chrome://browser/content/tabbrowser-tabs.js", "synthesizeMouseAtPoint@chrome://mochikit/content/tests/SimpleTest/EventUtils.js", diff --git a/browser/base/content/test/tabs/browser.ini b/browser/base/content/test/tabs/browser.ini index 66831c88b8ecd..9564c3c0989d1 100644 --- a/browser/base/content/test/tabs/browser.ini +++ b/browser/base/content/test/tabs/browser.ini @@ -148,3 +148,4 @@ skip-if = (verify && os == 'mac') [browser_bfcache_exemption_about_pages.js] skip-if = !fission [browser_tab_tooltips.js] +[browser_tab_manager_drag.js] \ No newline at end of file diff --git a/browser/base/content/test/tabs/browser_tab_manager_drag.js b/browser/base/content/test/tabs/browser_tab_manager_drag.js new file mode 100644 index 0000000000000..6adab77874704 --- /dev/null +++ b/browser/base/content/test/tabs/browser_tab_manager_drag.js @@ -0,0 +1,226 @@ +/** + * Test reordering the tabs in the Tab Manager, moving the tab between the + * Tab Manager and tab bar. + */ + +"use strict"; + +const URL1 = "data:text/plain,tab1"; +const URL2 = "data:text/plain,tab2"; +const URL3 = "data:text/plain,tab3"; +const URL4 = "data:text/plain,tab4"; +const URL5 = "data:text/plain,tab5"; + +function assertOrder(order, expected, message) { + is( + JSON.stringify(order), + JSON.stringify(expected), + `The order of the tabs ${message}` + ); +} + +function toIndex(url) { + const m = url.match(/^data:text\/plain,tab(\d)/); + if (m) { + return parseInt(m[1]); + } + return 0; +} + +function getOrderOfList(list) { + return [...list.querySelectorAll("toolbaritem")].map(row => { + const url = row.firstElementChild.tab.linkedBrowser.currentURI.spec; + return toIndex(url); + }); +} + +function getOrderOfTabs(tabs) { + return tabs.map(tab => { + const url = tab.linkedBrowser.currentURI.spec; + return toIndex(url); + }); +} + +async function testWithNewWindow(func) { + Services.prefs.setBoolPref("browser.tabs.tabmanager.enabled", true); + + const newWindow = await BrowserTestUtils.openNewWindowWithFlushedCacheForMozSupports(); + + await Promise.all([ + addTabTo(newWindow.gBrowser, URL1), + addTabTo(newWindow.gBrowser, URL2), + addTabTo(newWindow.gBrowser, URL3), + addTabTo(newWindow.gBrowser, URL4), + addTabTo(newWindow.gBrowser, URL5), + ]); + + newWindow.gTabsPanel.init(); + + const button = newWindow.document.getElementById("alltabs-button"); + + const allTabsView = newWindow.document.getElementById( + "allTabsMenu-allTabsView" + ); + const allTabsPopupShownPromise = BrowserTestUtils.waitForEvent( + allTabsView, + "ViewShown" + ); + button.click(); + await allTabsPopupShownPromise; + + await func(newWindow); + + await BrowserTestUtils.closeWindow(newWindow); + + Services.prefs.clearUserPref("browser.tabs.tabmanager.enabled"); +} + +add_task(async function test_reorder() { + await testWithNewWindow(async function(newWindow) { + const list = newWindow.document.getElementById( + "allTabsMenu-allTabsViewTabs" + ); + + assertOrder(getOrderOfList(list), [0, 1, 2, 3, 4, 5], "before reorder"); + + let rows; + rows = list.querySelectorAll("toolbaritem"); + EventUtils.synthesizeDrop( + rows[3], + rows[1], + null, + "move", + newWindow, + newWindow, + { clientX: 0, clientY: 0 } + ); + + assertOrder(getOrderOfList(list), [0, 3, 1, 2, 4, 5], "after moving up"); + + rows = list.querySelectorAll("toolbaritem"); + EventUtils.synthesizeDrop( + rows[1], + rows[5], + null, + "move", + newWindow, + newWindow, + { clientX: 0, clientY: 0 } + ); + + assertOrder(getOrderOfList(list), [0, 1, 2, 4, 3, 5], "after moving down"); + + rows = list.querySelectorAll("toolbaritem"); + EventUtils.synthesizeDrop( + rows[4], + rows[3], + null, + "move", + newWindow, + newWindow, + { clientX: 0, clientY: 0 } + ); + + assertOrder( + getOrderOfList(list), + [0, 1, 2, 3, 4, 5], + "after moving up again" + ); + }); +}); + +function tabOf(row) { + return row.firstElementChild.tab; +} + +add_task(async function test_move_to_tab_bar() { + await testWithNewWindow(async function(newWindow) { + const list = newWindow.document.getElementById( + "allTabsMenu-allTabsViewTabs" + ); + + assertOrder(getOrderOfList(list), [0, 1, 2, 3, 4, 5], "before reorder"); + + let rows; + rows = list.querySelectorAll("toolbaritem"); + EventUtils.synthesizeDrop( + rows[3], + tabOf(rows[1]), + null, + "move", + newWindow, + newWindow, + { clientX: 0, clientY: 0 } + ); + + assertOrder( + getOrderOfList(list), + [0, 3, 1, 2, 4, 5], + "after moving up with tab bar" + ); + + rows = list.querySelectorAll("toolbaritem"); + EventUtils.synthesizeDrop( + rows[1], + tabOf(rows[4]), + null, + "move", + newWindow, + newWindow, + { clientX: 0, clientY: 0 } + ); + + assertOrder( + getOrderOfList(list), + [0, 1, 2, 3, 4, 5], + "after moving down with tab bar" + ); + }); +}); + +add_task(async function test_move_to_different_tab_bar() { + const newWindow2 = await BrowserTestUtils.openNewWindowWithFlushedCacheForMozSupports(); + + await testWithNewWindow(async function(newWindow) { + const list = newWindow.document.getElementById( + "allTabsMenu-allTabsViewTabs" + ); + + assertOrder( + getOrderOfList(list), + [0, 1, 2, 3, 4, 5], + "before reorder in newWindow" + ); + assertOrder( + getOrderOfTabs(newWindow2.gBrowser.tabs), + [0], + "before reorder in newWindow2" + ); + + let rows; + rows = list.querySelectorAll("toolbaritem"); + EventUtils.synthesizeDrop( + rows[3], + newWindow2.gBrowser.tabs[0], + null, + "move", + newWindow, + newWindow2, + { clientX: 0, clientY: 0 } + ); + + assertOrder( + getOrderOfList(list), + [0, 1, 2, 4, 5], + "after moving to other window in newWindow" + ); + + assertOrder( + getOrderOfTabs(newWindow2.gBrowser.tabs), + [3, 0], + "after moving to other window in newWindow2" + ); + }); + + await BrowserTestUtils.closeWindow(newWindow2); +}); diff --git a/browser/base/content/test/tabs/head.js b/browser/base/content/test/tabs/head.js index 5d139e2b34b04..fc85bd5a16cc0 100644 --- a/browser/base/content/test/tabs/head.js +++ b/browser/base/content/test/tabs/head.js @@ -26,10 +26,18 @@ function triggerClickOn(target, options) { return promise; } -async function addTab(url = "http://mochi.test:8888/", params = {}) { +async function addTab(url = "http://mochi.test:8888/", params) { + return addTabTo(gBrowser, url, params); +} + +async function addTabTo( + targetBrowser, + url = "http://mochi.test:8888/", + params = {} +) { params.skipAnimation = true; - const tab = BrowserTestUtils.addTab(gBrowser, url, params); - const browser = gBrowser.getBrowserForTab(tab); + const tab = BrowserTestUtils.addTab(targetBrowser, url, params); + const browser = targetBrowser.getBrowserForTab(tab); await BrowserTestUtils.browserLoaded(browser); return tab; } diff --git a/browser/modules/TabsList.jsm b/browser/modules/TabsList.jsm index 21eba048d94ac..ad16fa8904aee 100644 --- a/browser/modules/TabsList.jsm +++ b/browser/modules/TabsList.jsm @@ -14,6 +14,8 @@ ChromeUtils.defineModuleGetter( var EXPORTED_SYMBOLS = ["TabsPanel"]; +const TAB_DROP_TYPE = "application/x-moz-tabbrowser-tab"; + function setAttributes(element, attrs) { for (let [name, value] of Object.entries(attrs)) { if (value) { @@ -25,11 +27,23 @@ function setAttributes(element, attrs) { } class TabsListBase { - constructor({ className, filterFn, insertBefore, containerNode }) { + constructor({ + className, + filterFn, + insertBefore, + containerNode, + dropIndicator = null, + }) { this.className = className; this.filterFn = filterFn; this.insertBefore = insertBefore; this.containerNode = containerNode; + this.dropIndicator = dropIndicator; + + if (this.dropIndicator) { + this.dropTargetRow = null; + this.dropTargetDirection = 0; + } this.doc = containerNode.ownerDocument; this.gBrowser = this.doc.defaultView.gBrowser; @@ -60,6 +74,21 @@ class TabsListBase { case "command": this._selectTab(event.target.tab); break; + case "dragstart": + this._onDragStart(event); + break; + case "dragover": + this._onDragOver(event); + break; + case "dragleave": + this._onDragLeave(event); + break; + case "dragend": + this._onDragEnd(event); + break; + case "drop": + this._onDrop(event); + break; } } @@ -104,10 +133,19 @@ class TabsListBase { _setupListeners() { this.listenersRegistered = true; + this.gBrowser.tabContainer.addEventListener("TabAttrModified", this); this.gBrowser.tabContainer.addEventListener("TabClose", this); this.gBrowser.tabContainer.addEventListener("TabMove", this); this.gBrowser.tabContainer.addEventListener("TabPinned", this); + + if (this.dropIndicator) { + this.containerNode.addEventListener("dragstart", this); + this.containerNode.addEventListener("dragover", this); + this.containerNode.addEventListener("dragleave", this); + this.containerNode.addEventListener("dragend", this); + this.containerNode.addEventListener("drop", this); + } } _cleanupListeners() { @@ -115,6 +153,15 @@ class TabsListBase { this.gBrowser.tabContainer.removeEventListener("TabClose", this); this.gBrowser.tabContainer.removeEventListener("TabMove", this); this.gBrowser.tabContainer.removeEventListener("TabPinned", this); + + if (this.dropIndicator) { + this.containerNode.removeEventListener("dragstart", this); + this.containerNode.removeEventListener("dragover", this); + this.containerNode.removeEventListener("dragleave", this); + this.containerNode.removeEventListener("dragend", this); + this.containerNode.removeEventListener("drop", this); + } + this.listenersRegistered = false; } @@ -317,4 +364,158 @@ class TabsPanel extends TabsListBase { } } } + + _onDragStart(event) { + const row = this._getDragTargetRow(event); + if (!row) { + return; + } + + this.gBrowser.tabContainer.startTabDrag(event, row.firstElementChild.tab, { + fromTabList: true, + }); + } + + _getDragTargetRow(event) { + let row = event.target; + while (row && row.localName !== "toolbaritem") { + row = row.parentNode; + } + return row; + } + + _isMovingTabs(event) { + var effects = this.gBrowser.tabContainer.getDropEffectForTabDrag(event); + return effects == "move"; + } + + _onDragOver(event) { + if (!this._isMovingTabs(event)) { + return; + } + + if (!this._updateDropTarget(event)) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + } + + _getRowIndex(row) { + return Array.prototype.indexOf.call(this.containerNode.children, row); + } + + _onDrop(event) { + if (!this._isMovingTabs(event)) { + return; + } + + if (!this._updateDropTarget(event)) { + return; + } + + event.preventDefault(); + event.stopPropagation(); + + let draggedTab = event.dataTransfer.mozGetDataAt(TAB_DROP_TYPE, 0); + + if (draggedTab === this.dropTargetRow.firstElementChild.tab) { + this._cleanupDragDetails(); + return; + } + + const targetTab = this.dropTargetRow.firstElementChild.tab; + + // NOTE: Given the list is opened only when the window is focused, + // we don't have to check `draggedTab.container`. + + let pos; + if (draggedTab._tPos < targetTab._tPos) { + pos = targetTab._tPos + this.dropTargetDirection; + } else { + pos = targetTab._tPos + this.dropTargetDirection + 1; + } + this.gBrowser.moveTabTo(draggedTab, pos); + + this._cleanupDragDetails(); + } + + _onDragLeave(event) { + if (!this._isMovingTabs(event)) { + return; + } + + if (event.target !== this.containerNode) { + return; + } + + this._clearDropTarget(); + } + + _onDragEnd(event) { + if (!this._isMovingTabs(event)) { + return; + } + + this._cleanupDragDetails(); + } + + _updateDropTarget(event) { + const row = this._getDragTargetRow(event); + if (!row) { + return false; + } + + const rect = row.getBoundingClientRect(); + const index = this._getRowIndex(row); + if (index === -1) { + return false; + } + + const threshold = rect.height * 0.5; + if (event.clientY < rect.top + threshold) { + this._setDropTarget(row, -1); + } else { + this._setDropTarget(row, 0); + } + + return true; + } + + _setDropTarget(row, direction) { + this._clearDropTarget(); + + this.dropTargetRow = row; + this.dropTargetDirection = direction; + + let offset = this.dropIndicator.parentNode.getBoundingClientRect().top; + let top; + if (this.dropTargetDirection === -1) { + if (this.dropTargetRow.previousSibling) { + const rect = this.dropTargetRow.previousSibling.getBoundingClientRect(); + top = rect.top + rect.height; + } else { + const rect = this.dropTargetRow.getBoundingClientRect(); + top = rect.top; + } + } else { + const rect = this.dropTargetRow.getBoundingClientRect(); + top = rect.top + rect.height; + } + + this.dropIndicator.style.top = `${top - offset - 12}px`; + this.dropIndicator.collapsed = false; + } + + _clearDropTarget() { + if (this.dropTargetRow) { + this.dropTargetRow = null; + } + this.dropIndicator.collapsed = true; + } + + _cleanupDragDetails() { + this._clearDropTarget(); + } } From 4425a477662b7254187e78b2a6528fe1a5071161 Mon Sep 17 00:00:00 2001 From: Tom Schuster Date: Wed, 31 Aug 2022 13:35:31 +0000 Subject: [PATCH 56/65] Bug 1782910 - Sanitizer: Implement the element matches an element name. r=emilio This is still missing the part that normalizes lower-cased svg/mathml names. Differential Revision: https://phabricator.services.mozilla.com/D154654 --- dom/base/nsTreeSanitizer.cpp | 134 +++++++++++------- dom/base/nsTreeSanitizer.h | 51 +++++-- .../sanitizer-names.https.tentative.html.ini | 63 +------- .../sanitizer-names.https.tentative.html | 46 +++--- 4 files changed, 152 insertions(+), 142 deletions(-) diff --git a/dom/base/nsTreeSanitizer.cpp b/dom/base/nsTreeSanitizer.cpp index 3c29d1abf0fc9..1343e8ae50e2c 100644 --- a/dom/base/nsTreeSanitizer.cpp +++ b/dom/base/nsTreeSanitizer.cpp @@ -1590,8 +1590,8 @@ bool nsTreeSanitizer::MustFlattenForSanitizerAPI(int32_t aNamespace, // Step 6. If element matches any name in config["blockElements"]: Return // block. - // TODO(bug 1782910): "matches" is not really contains! - if (mBlockElements && mBlockElements->Contains(aLocal)) { + if (mBlockElements && + MatchesElementName(*mBlockElements, aNamespace, aLocal)) { return true; } @@ -1601,19 +1601,20 @@ bool nsTreeSanitizer::MustFlattenForSanitizerAPI(int32_t aNamespace, if (mAllowElements) { // Step 9. If element does not match any name in allow list: // Return block. - // TODO(bug 1782910): matches - if (!mAllowElements->Contains(aLocal)) { + if (!MatchesElementName(*mAllowElements, aNamespace, aLocal)) { return true; } - } else { // Step 8.2. Otherwise: Set allow list to the default configuration's // element allow list. // Step 9. If element does not match any name in allow list: // Return block. - // TODO(bug 1782910): matches - if (!sDefaultConfigurationElementAllowlist->Contains(aLocal)) { + + // The default configuration only contains HTML elements, so we can + // reject everything else. + if (aNamespace != kNameSpaceID_XHTML || + !sDefaultConfigurationElementAllowlist->Contains(aLocal)) { return true; } } @@ -1763,8 +1764,7 @@ bool nsTreeSanitizer::MustPruneForSanitizerAPI(int32_t aNamespace, } // Step 5. If element matches any name in config["dropElements"]: Return drop. - // TODO(bug 1782910): "matches" is not really contains! - if (mDropElements && mDropElements->Contains(aLocal)) { + if (mDropElements && MatchesElementName(*mDropElements, aNamespace, aLocal)) { return true; } @@ -1964,34 +1964,41 @@ void nsTreeSanitizer::SanitizeAttributes(mozilla::dom::Element* aElement, } } +// https://wicg.github.io/sanitizer-api/#element-matches-an-element-name +bool nsTreeSanitizer::MatchesElementName(ElementNameSet& aNames, + int32_t aNamespace, + nsAtom* aLocalName) { + return aNames.Contains(ElementName(aNamespace, aLocalName)); +} + // https://wicg.github.io/sanitizer-api/#attribute-match-list bool nsTreeSanitizer::MatchesAttributeMatchList( ElementToAttributeSetTable& aMatchList, Element& aElement, int32_t aAttrNamespace, nsAtom* aAttrLocalName) { // Step 1. If attribute’s local name does not match the attribute match list // list’s key and if the key is not "*": Return false. - DynamicAtomsTable* elements; + ElementNameSet* names; if (auto lookup = aMatchList.Lookup(aAttrLocalName)) { - elements = lookup->get(); + names = lookup->get(); } else if (auto lookup = aMatchList.Lookup(nsGkAtoms::_asterisk)) { - elements = lookup->get(); + names = lookup->get(); } else { return false; } // Step 2. Let element be the attribute’s Element. // Step 3. Let element name be element’s local name. - nsAtom* elemName = aElement.NodeInfo()->NameAtom(); - // Step 4. If element is a in either the SVG or MathML namespaces (i.e., it’s // a foreign element), then prefix element name with the appropriate namespace // designator plus a whitespace character. - // TODO(bug 1784040) Namespace handling. + int32_t namespaceID = aElement.NodeInfo()->NamespaceID(); + RefPtr nameAtom = aElement.NodeInfo()->NameAtom(); + ElementName elemName(namespaceID, nameAtom); // Step 5. If list’s value does not contain element name and value is not // ["*"]: Return false. - if (!elements->Contains(elemName) && - !elements->Contains(nsGkAtoms::_asterisk)) { + if (!names->Contains(elemName) && + !names->Contains(ElementName(kNameSpaceID_XHTML, nsGkAtoms::_asterisk))) { return false; } @@ -2452,6 +2459,50 @@ void nsTreeSanitizer::ReleaseStatics() { NS_IF_RELEASE(sNullPrincipal); } +UniquePtr nsTreeSanitizer::ConvertElementNames( + const Sequence& aNames) { + auto names = MakeUnique(aNames.Length()); + + // https://wicg.github.io/sanitizer-api/#normalize-element-name + for (const nsString& name : aNames) { + // Step 1. Let tokens be the result of strictly splitting name on the + // delimiter ":" (U+003A). + int32_t index = name.FindChar(':'); + + // Step 2. If tokens’ size is 1, then return tokens[0]. + if (index == kNotFound) { + RefPtr nameAtom = NS_AtomizeMainThread(name); + ElementName elemName(kNameSpaceID_XHTML, std::move(nameAtom)); + names->Insert(elemName); + continue; + } + + // Step 3. If tokens’ size is 2 and tokens[0] is either "svg" or "math", + // then: + if (name.FindChar(':', index + 1) == kNotFound) { + auto prefix = Substring(name, 0, index); + // Step 3.1. Adjust tokens[1] as described in the "any other start tag" + // branch of the rules for parsing tokens in foreign content subchapter in + // the HTML parsing spec. Step 3.2 Return the concatenation of the list + // «|tokens|[0],":" (U+003A),|tokens|[1]». + // TODO + RefPtr nameAtom = + NS_AtomizeMainThread(Substring(name, index + 1)); + if (prefix.EqualsLiteral("svg")) { + ElementName elemName(kNameSpaceID_SVG, std::move(nameAtom)); + names->Insert(elemName); + } else if (prefix.EqualsLiteral("math")) { + ElementName elemName(kNameSpaceID_MathML, std::move(nameAtom)); + names->Insert(elemName); + } + } + + // Step 4. Return null. + // Nothing is inserted and name is skipped. + } + return names; +} + void nsTreeSanitizer::WithWebSanitizerOptions( nsIGlobalObject* aGlobal, const mozilla::dom::SanitizerConfig& aOptions) { if (StaticPrefs::dom_security_sanitizer_logging()) { @@ -2478,46 +2529,25 @@ void nsTreeSanitizer::WithWebSanitizerOptions( } if (aOptions.mAllowElements.WasPassed()) { - const Sequence& allowedElements = aOptions.mAllowElements.Value(); - mAllowElements = MakeUnique(allowedElements.Length()); - for (const nsString& elem : allowedElements) { - RefPtr elAsAtom = NS_AtomizeMainThread(elem); - mAllowElements->Insert(elAsAtom); - } + mAllowElements = ConvertElementNames(aOptions.mAllowElements.Value()); } if (aOptions.mBlockElements.WasPassed()) { - const Sequence& blockedElements = aOptions.mBlockElements.Value(); - mBlockElements = MakeUnique(blockedElements.Length()); - for (const nsString& elem : blockedElements) { - RefPtr elAsAtom = NS_AtomizeMainThread(elem); - mBlockElements->Insert(elAsAtom); - } + mBlockElements = ConvertElementNames(aOptions.mBlockElements.Value()); } if (aOptions.mDropElements.WasPassed()) { - const Sequence& dropElements = aOptions.mDropElements.Value(); - mDropElements = MakeUnique(dropElements.Length()); - for (const nsString& elem : dropElements) { - RefPtr elAsAtom = NS_AtomizeMainThread(elem); - mDropElements->Insert(elAsAtom); - } + mDropElements = ConvertElementNames(aOptions.mDropElements.Value()); } if (aOptions.mAllowAttributes.WasPassed()) { const Record>& allowedAttributes = aOptions.mAllowAttributes.Value(); mAllowedAttributes = MakeUnique(); - nsAutoString name; - for (const auto& entries : allowedAttributes.Entries()) { - UniquePtr elems = - MakeUnique(allowedAttributes.Entries().Length()); - for (const auto& elem : entries.mValue) { - RefPtr elAsAtom = NS_AtomizeMainThread(elem); - elems->Insert(elAsAtom); - } - RefPtr attrAtom = NS_AtomizeMainThread(entries.mKey); - mAllowedAttributes->InsertOrUpdate(attrAtom, std::move(elems)); + for (const auto& entry : allowedAttributes.Entries()) { + RefPtr attrAtom = NS_AtomizeMainThread(entry.mKey); + UniquePtr elements = ConvertElementNames(entry.mValue); + mAllowedAttributes->InsertOrUpdate(attrAtom, std::move(elements)); } } @@ -2525,16 +2555,10 @@ void nsTreeSanitizer::WithWebSanitizerOptions( const Record>& droppedAttributes = aOptions.mDropAttributes.Value(); mDroppedAttributes = MakeUnique(); - nsAutoString name; - for (const auto& entries : droppedAttributes.Entries()) { - UniquePtr elems = - MakeUnique(droppedAttributes.Entries().Length()); - for (const auto& elem : entries.mValue) { - RefPtr elAsAtom = NS_AtomizeMainThread(elem); - elems->Insert(elAsAtom); - } - RefPtr attrAtom = NS_AtomizeMainThread(entries.mKey); - mDroppedAttributes->InsertOrUpdate(attrAtom, std::move(elems)); + for (const auto& entry : droppedAttributes.Entries()) { + RefPtr attrAtom = NS_AtomizeMainThread(entry.mKey); + UniquePtr elements = ConvertElementNames(entry.mValue); + mDroppedAttributes->InsertOrUpdate(attrAtom, std::move(elements)); } } } diff --git a/dom/base/nsTreeSanitizer.h b/dom/base/nsTreeSanitizer.h index 8c747c05ffa15..a1361ac6b1664 100644 --- a/dom/base/nsTreeSanitizer.h +++ b/dom/base/nsTreeSanitizer.h @@ -12,6 +12,7 @@ #include "nsTArray.h" #include "nsTHashSet.h" #include "mozilla/UniquePtr.h" +#include "mozilla/dom/NameSpaceConstants.h" #include "mozilla/dom/SanitizerBinding.h" class nsIContent; @@ -129,17 +130,44 @@ class nsTreeSanitizer { return aAtom->IsStatic() && GetEntry(aAtom->AsStatic()); } }; - // Use this table for user-defined lists - class DynamicAtomsTable : public nsTHashSet> { + + // The name of an element combined with its namespace. + class ElementName : public PLDHashEntryHdr { public: - explicit DynamicAtomsTable(uint32_t aLength) - : nsTHashSet>(aLength) {} + using KeyType = const ElementName&; + using KeyTypePointer = const ElementName*; + + explicit ElementName(KeyTypePointer aKey) + : mNamespaceID(aKey->mNamespaceID), mLocalName(aKey->mLocalName) {} + ElementName(int32_t aNamespaceID, RefPtr aLocalName) + : mNamespaceID(aNamespaceID), mLocalName(std::move(aLocalName)) {} + ElementName(ElementName&&) = default; + ~ElementName() = default; + + bool KeyEquals(KeyTypePointer aKey) const { + return mNamespaceID == aKey->mNamespaceID && + mLocalName == aKey->mLocalName; + } - bool Contains(nsAtom* aAtom) { return GetEntry(aAtom); } + static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; } + static PLDHashNumber HashKey(KeyTypePointer aKey) { + if (!aKey) { + return 0; + } + + return mozilla::HashGeneric(aKey->mNamespaceID, aKey->mLocalName.get()); + } + + enum { ALLOW_MEMMOVE = true }; + + private: + int32_t mNamespaceID = kNameSpaceID_None; + RefPtr mLocalName; }; + using ElementNameSet = nsTHashSet; using ElementToAttributeSetTable = - nsTHashMap, mozilla::UniquePtr>; + nsTHashMap, mozilla::UniquePtr>; void SanitizeChildren(nsINode* aRoot); @@ -249,11 +277,16 @@ class nsTreeSanitizer { */ static void RemoveAllAttributesFromDescendants(mozilla::dom::Element*); + static bool MatchesElementName(ElementNameSet& aNames, int32_t aNamespace, + nsAtom* aLocalName); static bool MatchesAttributeMatchList(ElementToAttributeSetTable& aMatchList, mozilla::dom::Element& aElement, int32_t aAttrNamespace, nsAtom* aAttrLocalName); + static mozilla::UniquePtr ConvertElementNames( + const mozilla::dom::Sequence& aNames); + /** * Log a Console Service message to indicate we removed something. * If you pass an element and/or attribute, their information will @@ -338,13 +371,13 @@ class nsTreeSanitizer { bool mAllowUnknownMarkup = false; // An allow-list of elements to keep. - mozilla::UniquePtr mAllowElements; + mozilla::UniquePtr mAllowElements; // A deny-list of elements to block. (aka flatten) - mozilla::UniquePtr mBlockElements; + mozilla::UniquePtr mBlockElements; // A deny-list of elements to drop. (aka prune) - mozilla::UniquePtr mDropElements; + mozilla::UniquePtr mDropElements; // An allow-list of attributes to keep. mozilla::UniquePtr mAllowedAttributes; diff --git a/testing/web-platform/meta/sanitizer-api/sanitizer-names.https.tentative.html.ini b/testing/web-platform/meta/sanitizer-api/sanitizer-names.https.tentative.html.ini index 5831e7aff29c4..fc25bade4bb1d 100644 --- a/testing/web-platform/meta/sanitizer-api/sanitizer-names.https.tentative.html.ini +++ b/testing/web-platform/meta/sanitizer-api/sanitizer-names.https.tentative.html.ini @@ -14,79 +14,22 @@ [Attribute names in config item: dropAttributes] expected: FAIL - [Namespaced elements #0: allowElements: ["p"\]] - expected: FAIL - - [Namespaced elements #1: allowElements: ["svg"\]] - expected: FAIL - - [Namespaced elements #2: allowElements: ["svg:svg"\]] - expected: FAIL - - [Namespaced elements #3: allowElements: ["math"\]] - expected: FAIL - - [Namespaced elements #4: allowElements: ["svg:math"\]] - expected: FAIL - - [Namespaced elements #5: allowElements: ["math:math"\]] - expected: FAIL - - [Namespaced elements #6: allowElements: ["potato:math"\]] - expected: FAIL - - [Namespaced elements #7: allowElements: ["potato:math"\]] - expected: FAIL - - [Namespaced attributes #0: allowAttributes: {"style": ["*"\]}] - expected: FAIL - - [Namespaced attributes #1: allowAttributes: {"href": ["*"\]}] - expected: FAIL - [Namespaced attributes #2: allowAttributes: {"xlink:href": ["*"\]}] expected: FAIL - [Namespaced attributes #3: allowAttributes: {"potato:href": ["*"\]}] - expected: FAIL - - [Namespaced attributes #4: allowAttributes: {"xlink:href": ["*"\]}] - expected: FAIL - - [Namespaced attributes #5: allowAttributes: {"href": ["*"\]}] - expected: FAIL - - [Mixed-case element names #0: "svg:feBlend"] - expected: FAIL - - [Mixed-case element names #0: "svg:feblend"] - expected: FAIL - - [Mixed-case element names #0: "SVG:FEBLEND"] + [Lower-case element names #0: "svg:feblend"] expected: FAIL [Mixed case element names #0: "svg:feBlend" is preserved in config.] expected: FAIL - [Mixed-case element names #1: "svg:feColorMatrix"] - expected: FAIL - - [Mixed-case element names #1: "svg:fecolormatrix"] - expected: FAIL - - [Mixed-case element names #1: "SVG:FECOLORMATRIX"] + [Lower-case element names #1: "svg:fecolormatrix"] expected: FAIL [Mixed case element names #1: "svg:feColorMatrix" is preserved in config.] expected: FAIL - [Mixed-case element names #2: "svg:textPath"] - expected: FAIL - - [Mixed-case element names #2: "svg:textpath"] - expected: FAIL - - [Mixed-case element names #2: "SVG:TEXTPATH"] + [Lower-case element names #2: "svg:textpath"] expected: FAIL [Mixed case element names #2: "svg:textPath" is preserved in config.] diff --git a/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.tentative.html b/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.tentative.html index 635b7975e65ec..e963761faacc4 100644 --- a/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.tentative.html +++ b/testing/web-platform/tests/sanitizer-api/sanitizer-names.https.tentative.html @@ -65,9 +65,12 @@ [ "potato:math", "Hello", "Hello" ], ].forEach(([elem, probe, expected], index) => { test(t => { - const sanitizer = new Sanitizer({allowElements: [elem]}); - assert_equals(sanitizer.sanitizeFor("template", probe).innerHTML, - expected ?? probe); + const sanitizer = new Sanitizer({allowElements: [elem], + // TODO(https://github.com/WICG/sanitizer-api/issues/167) + allowUnknownMarkup: true}); + const template = document.createElement("template"); + template.setHTML(probe, {sanitizer}); + assert_equals(template.innerHTML, expected ?? probe); }, `Namespaced elements #${index}: allowElements: ["${elem}"]`); }); @@ -81,39 +84,46 @@ [ "href", "

", "

" ], ].forEach(([attr, probe, expected], index) => { test(t => { - const sanitizer = new Sanitizer({allowAttributes: {[attr]: ["*"]}}); - assert_equals(sanitizer.sanitizeFor("template", probe).innerHTML, - expected ?? probe); + const sanitizer = new Sanitizer({allowAttributes: {[attr]: ["*"]}, + // TODO(https://github.com/WICG/sanitizer-api/issues/167) + allowUnknownMarkup: true}); + const template = document.createElement("template"); + template.setHTML(probe, {sanitizer}); + assert_equals(template.innerHTML, expected ?? probe); }, `Namespaced attributes #${index}: allowAttributes: {"${attr}": ["*"]}`); }); // Most element and attribute names are lower-cased, but "foreign content" // like SVG and MathML have some mixed-cased names. [ - [ "svg:feBlend", "" ], - [ "svg:feColorMatrix", "" ], - [ "svg:textPath", "" ], + [ "feBlend", "" ], + [ "feColorMatrix", "" ], + [ "textPath", "" ], ].forEach(([elem, probe], index) => { const sanitize = (elem, probe) => { - return new Sanitizer({allowElements: ["svg:svg", elem]}). - sanitizeFor("template", `${probe}${probe}`, {sanitizer}); + return template.content.firstElementChild.innerHTML; }; test(t => { assert_equals(sanitize(elem, probe), probe); - }, `Mixed-case element names #${index}: "${elem}"`); + }, `Mixed-case element names #${index}: "svg:${elem}"`); test(t => { - assert_not_equals(sanitize(elem.toLowerCase(), probe), probe); - }, `Mixed-case element names #${index}: "${elem.toLowerCase()}"`); + // Lowercase element names should be normalized to mixed-case. + assert_equals(sanitize(elem.toLowerCase(), probe), probe); + }, `Lower-case element names #${index}: "svg:${elem.toLowerCase()}"`); test(t => { assert_not_equals(sanitize(elem.toUpperCase(), probe), probe); - }, `Mixed-case element names #${index}: "${elem.toUpperCase()}"`); + }, `Upper-case element names #${index}: "svg:${elem.toUpperCase()}"`); test(t => { - const elems = [elem]; + const elems = ["svg:" + elem]; assert_array_equals( new Sanitizer({allowElements: elems}).getConfiguration().allowElements, elems); - }, `Mixed case element names #${index}: "${elem}" is preserved in config.`); + }, `Mixed case element names #${index}: "svg:${elem}" is preserved in config.`); }); From d25798f6e6d148e05c8d8d1a6cfe24a8df61a8a5 Mon Sep 17 00:00:00 2001 From: Sylvestre Ledru Date: Wed, 31 Aug 2022 13:51:30 +0000 Subject: [PATCH 57/65] Bug 1785147 - fix a typo in the doc r=julienw DONTBUILD Differential Revision: https://phabricator.services.mozilla.com/D156075 --- docs/performance/index.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/performance/index.md b/docs/performance/index.md index 31fcbbff82bab..f6363bcd48fd1 100644 --- a/docs/performance/index.md +++ b/docs/performance/index.md @@ -1,6 +1,6 @@ # Performance -This page explains how optimize the performance the Firefox code base +This page explains how to optimize the performance of the Firefox code base. The [test documentation](/testing/perfdocs/index.rst) explains how to test for performance in Firefox. From 2afa39dd24d964efa3091d851566389d8bfaa188 Mon Sep 17 00:00:00 2001 From: andrej Date: Wed, 31 Aug 2022 14:06:26 +0000 Subject: [PATCH 58/65] Bug 1786092 - Resolve a few miscalleanous WPT bugs. r=sparky,perftest-reviewers What are we doing: - Resolving a few bugs/user requests Issues being addressed: - Resolved issue where if the WPT_key.txt file is not available locally it does not affect running ./mach perftest-test - Added section to WPT where we display the amount of tests we have remaining - Altered the request_with_timeout function, to better handle requests Differential Revision: https://phabricator.services.mozilla.com/D155268 --- .../mozperftest/test/webpagetest.py | 30 ++++++++++++++----- .../mozperftest/tests/data/WPT_fakekey.txt | 0 .../mozperftest/tests/test_webpagetest.py | 23 ++++++++++++-- 3 files changed, 43 insertions(+), 10 deletions(-) create mode 100644 python/mozperftest/mozperftest/tests/data/WPT_fakekey.txt diff --git a/python/mozperftest/mozperftest/test/webpagetest.py b/python/mozperftest/mozperftest/test/webpagetest.py index a2b615b3f1a90..efd45c1a56f76 100644 --- a/python/mozperftest/mozperftest/test/webpagetest.py +++ b/python/mozperftest/mozperftest/test/webpagetest.py @@ -28,6 +28,7 @@ ] ACCEPTED_STATISTICS = ["average", "median", "standardDeviation"] +WPT_KEY_FILE = "WPT_key.txt" class WPTTimeOutError(Exception): @@ -169,10 +170,10 @@ def __init__(self, env, mach_cmd): if utils.ON_TRY: self.WPT_key = utils.get_tc_secret(wpt=True)["wpt_key"] else: - self.WPT_key = pathlib.Path(HERE, "WPT_key.txt").open().read() + self.WPT_key = pathlib.Path(HERE, WPT_KEY_FILE).open().read() self.statistic_types = ["average", "median", "standardDeviation"] self.timeout_limit = 21600 - self.wait_between_requests = 5 + self.wait_between_requests = 180 def run(self, metadata): options = metadata.script["options"] @@ -181,6 +182,7 @@ def run(self, metadata): self.wpt_browser_metrics = options["browser_metrics"] self.pre_run_error_checks(options["test_parameters"], test_list) self.create_and_run_wpt_threaded_tests(test_list, metadata) + self.test_runs_left_this_month() return metadata def pre_run_error_checks(self, options, test_list): @@ -239,19 +241,25 @@ def location_queue(self, location): ) def request_with_timeout(self, url): - results_of_test = {} + requested_results = requests.get(url) + results_of_request = json.loads(requested_results.text) start = time.time() while ( - results_of_test.get("statusCode") != 200 + requested_results.status_code == 200 and time.time() - start < self.timeout_limit + and ( + "statusCode" in results_of_request.keys() + and results_of_request["statusCode"] != 200 + ) ): - results_of_test = json.loads(requests.get(url).text) + requested_results = requests.get(url) + results_of_request = json.loads(requested_results.text) time.sleep(self.wait_between_requests) - if results_of_test.get("statusCode") != 200: + if time.time() - start > self.timeout_limit: raise WPTTimeOutError( f"{url} test timed out after {self.timeout_limit} seconds" ) - return results_of_test + return results_of_request def check_urls_are_valid(self, test_list): for url in test_list: @@ -376,3 +384,11 @@ def extract_desired_values_from_wpt_run(self, wpt_run): except Exception: self.error("Issue found with processing browser/WPT version") return desired_values + + def test_runs_left_this_month(self): + tests_left_this_month = self.request_with_timeout( + f"https://www.webpagetest.org/testBalance.php?k={self.WPT_key}&f=json" + ) + self.info( + f"There are {tests_left_this_month['data']['remaining']} tests remaining" + ) diff --git a/python/mozperftest/mozperftest/tests/data/WPT_fakekey.txt b/python/mozperftest/mozperftest/tests/data/WPT_fakekey.txt new file mode 100644 index 0000000000000..e69de29bb2d1d diff --git a/python/mozperftest/mozperftest/tests/test_webpagetest.py b/python/mozperftest/mozperftest/tests/test_webpagetest.py index d8fd51c429b25..4628f99cfe0d1 100644 --- a/python/mozperftest/mozperftest/tests/test_webpagetest.py +++ b/python/mozperftest/mozperftest/tests/test_webpagetest.py @@ -82,19 +82,21 @@ def init_placeholder_wpt_data(fvonly=False, invalid_results=False): return placeholder_data -def init_mocked_request(status_code, **kwargs): +def init_mocked_request(status_code, WPT_test_status_code=200, **kwargs): mock_data = { "data": { "ec2-us-east-1": {"PendingTests": {"Queued": 3}, "Label": "California"}, "jsonUrl": "mock_test.com", "summary": "Just a pageload test", "url": "testurl.ca", + "remaining": 2000, }, - "statusCode": status_code, + "statusCode": WPT_test_status_code, } for key, value in kwargs.items(): mock_data["data"][key] = value mock_request = requests.Response() + mock_request.status_code = status_code mock_request._content = json.dumps(mock_data).encode("utf-8") return mock_request @@ -107,6 +109,8 @@ def init_mocked_request(status_code, **kwargs): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("requests.get", return_value=init_mocked_request(200)) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_no_issues_mocked_results(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) test = webpagetest.WebPageTest(env, mach_cmd) @@ -120,6 +124,8 @@ def test_webpagetest_no_issues_mocked_results(*mocked): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("requests.get", return_value=init_mocked_request(200)) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_invalid_browser(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) metadata.script["options"]["test_parameters"]["browser"] = "Invalid Browser" @@ -132,6 +138,8 @@ def test_webpagetest_test_invalid_browser(*mocked): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("requests.get", return_value=init_mocked_request(200)) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_invalid_connection(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) test = webpagetest.WebPageTest(env, mach_cmd) @@ -144,6 +152,8 @@ def test_webpagetest_test_invalid_connection(*mocked): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("requests.get", return_value=init_mocked_request(200)) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_invalid_url(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) test = webpagetest.WebPageTest(env, mach_cmd) @@ -156,6 +166,8 @@ def test_webpagetest_test_invalid_url(*mocked): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("requests.get", return_value=init_mocked_request(200)) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_invalid_statistic(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) test = webpagetest.WebPageTest(env, mach_cmd) @@ -171,6 +183,7 @@ def test_webpagetest_test_invalid_statistic(*mocked): "mozperftest.test.webpagetest.WebPageTest.request_with_timeout", return_value={"data": {}}, ) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_invalid_location(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) test = webpagetest.WebPageTest(env, mach_cmd) @@ -179,11 +192,12 @@ def test_webpagetest_test_invalid_location(*mocked): test.run(metadata) -@mock.patch("requests.get", return_value=init_mocked_request(400)) +@mock.patch("requests.get", return_value=init_mocked_request(200, 101)) @mock.patch("mozperftest.utils.get_tc_secret", return_value={"wpt_key": "fake_key"}) @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_timeout(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) test = webpagetest.WebPageTest(env, mach_cmd) @@ -204,6 +218,7 @@ def test_webpagetest_test_timeout(*mocked): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_wrong_browserlocation(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) metadata.script["options"]["test_list"] = ["google.ca"] @@ -221,6 +236,8 @@ def test_webpagetest_test_wrong_browserlocation(*mocked): @mock.patch( "mozperftest.test.webpagetest.WebPageTest.location_queue", return_value=None ) +@mock.patch("requests.get", return_value=init_mocked_request(200)) +@mock.patch("mozperftest.test.webpagetest.WPT_KEY_FILE", "tests/data/WPT_fakekey.txt") def test_webpagetest_test_metric_not_found(*mocked): mach_cmd, metadata, env = running_env(tests=[str(EXAMPLE_WPT_TEST)]) metadata.script["options"]["test_list"] = ["google.ca"] From d8cc4c76c919322996e7774799def391d914fb57 Mon Sep 17 00:00:00 2001 From: Jon Coppeard Date: Wed, 31 Aug 2022 14:18:26 +0000 Subject: [PATCH 59/65] Bug 1787926 - Don't add async dependency on modules that have finished evaluating r=yulia The problem is that we're waiting for an import to finish evaluation after it already has. I think this a problem with the spec, related to the change from AsyncEvaluating to AsyncEvaluation here: https://github.com/tc39/proposal-top-level-await/commit/c2375dbd6ecb0d5e9a415f6a812fc0974a2935b7#diff-181371b08d71216599b0acccbaabd03c306da6de142ea6275c2135810999805aL588-R589 Previously we wouldn't add an async dependency when the required module's AsyncEvaluating field was false, which was the case after evaluation had finished. Now this is never set to false so we always add the dependency here and there's nothing to trigger evaluation of the importing module. Differential Revision: https://phabricator.services.mozilla.com/D155968 --- js/src/jit-test/tests/modules/bug-1787926.js | 16 ++++++++++++++++ js/src/vm/Modules.cpp | 3 ++- 2 files changed, 18 insertions(+), 1 deletion(-) create mode 100644 js/src/jit-test/tests/modules/bug-1787926.js diff --git a/js/src/jit-test/tests/modules/bug-1787926.js b/js/src/jit-test/tests/modules/bug-1787926.js new file mode 100644 index 0000000000000..fb710143599ee --- /dev/null +++ b/js/src/jit-test/tests/modules/bug-1787926.js @@ -0,0 +1,16 @@ +let m = registerModule('m', parseModule(`import {} from "s";`)); +let l = registerModule('l', parseModule(`import {} from "s";`)); +let s = registerModule('s', parseModule(`await 0;`)); + +let state = "init"; + +moduleLink(m); +moduleEvaluate(m).then(() => { state = "loaded"; }); +drainJobQueue(); + +assertEq(state, "loaded"); + +import("l").then(() => { state = "complete"; }); +drainJobQueue(); + +assertEq(state, "complete"); diff --git a/js/src/vm/Modules.cpp b/js/src/vm/Modules.cpp index ae31e4d471a4c..f0da34e5b4cda 100644 --- a/js/src/vm/Modules.cpp +++ b/js/src/vm/Modules.cpp @@ -1465,7 +1465,8 @@ static bool InnerModuleEvaluation(JSContext* cx, Handle module, } // Step 11.d.v. If requiredModule.[[AsyncEvaluation]] is true, then: - if (requiredModule->isAsyncEvaluating()) { + if (requiredModule->isAsyncEvaluating() && + requiredModule->status() != ModuleStatus::Evaluated) { // Step 11.d.v.2. Append module to requiredModule.[[AsyncParentModules]]. if (!ModuleObject::appendAsyncParentModule(cx, requiredModule, module)) { return false; From 3577ed2643583f3da5b242c2c37fe7c3c50b67f7 Mon Sep 17 00:00:00 2001 From: Makoto Kato Date: Wed, 31 Aug 2022 14:31:14 +0000 Subject: [PATCH 60/65] Bug 1786449 - Revert overscroll support part of bug 1724480. r=geckoview-reviewers,calu To support overscroll drawing on old Andorid (9 or early), we use the reflection to access `Paint` object. But this is removed by bug 1724480. So I would like to revert overscroll part for Android 9 or early. I tested on old Galaxy S7 (Android 9). Differential Revision: https://phabricator.services.mozilla.com/D155716 --- .../geckoview/OverscrollEdgeEffect.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java index f6e941ee97ac6..1b48a5930cc0a 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java @@ -8,6 +8,9 @@ import android.content.Context; import android.graphics.BlendMode; import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.os.Build; import android.widget.EdgeEffect; @@ -43,6 +46,25 @@ public final class OverscrollEdgeEffect { private void setBlendMode(final EdgeEffect edgeEffect) { if (Build.VERSION.SDK_INT < 29) { // setBlendMode is only supported on SDK_INT >= 29 and above. + + if (sPaintField == null) { + try { + sPaintField = EdgeEffect.class.getDeclaredField("mPaint"); + sPaintField.setAccessible(true); + } catch (final NoSuchFieldException e) { + // Cannot get the field, nothing we can do here + return; + } + } + + try { + final Paint paint = (Paint) sPaintField.get(edgeEffect); + final PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.SRC); + paint.setXfermode(mode); + } catch (final IllegalAccessException ex) { + // Nothing we can do + } + return; } From 9dac4ed4190861f0a316d0d4dac1d1f904adcba2 Mon Sep 17 00:00:00 2001 From: Meg Viar Date: Wed, 31 Aug 2022 16:09:26 +0000 Subject: [PATCH 61/65] Bug 1786647 - Split up Feature Callout messages and fetch from ASRouter on page load, focus, and screen advance r=Mardak Differential Revision: https://phabricator.services.mozilla.com/D155524 --- .../components/firefoxview/featureCallout.mjs | 209 +--------- .../tests/browser/browser_feature_callout.js | 16 +- .../schemas/MessagingExperiment.schema.json | 7 +- .../OnboardingMessage/Spotlight.schema.json | 3 +- .../newtab/lib/FeatureCalloutMessages.jsm | 388 ++++++++++++++++++ .../newtab/lib/OnboardingMessageProvider.jsm | 9 +- .../components/newtab/test/xpcshell/head.js | 5 + .../TriggerActionSchemas.json | 23 ++ .../schemas/TriggerActionSchemas/index.md | 4 + 9 files changed, 465 insertions(+), 199 deletions(-) create mode 100644 browser/components/newtab/lib/FeatureCalloutMessages.jsm diff --git a/browser/components/firefoxview/featureCallout.mjs b/browser/components/firefoxview/featureCallout.mjs index 819c0581c6185..4e5754c7b73bc 100644 --- a/browser/components/firefoxview/featureCallout.mjs +++ b/browser/components/firefoxview/featureCallout.mjs @@ -12,8 +12,13 @@ const lazy = {}; XPCOMUtils.defineLazyModuleGetters(lazy, { AboutWelcomeParent: "resource:///actors/AboutWelcomeParent.jsm", + ASRouter: "resource://activity-stream/lib/ASRouter.jsm", }); +// When expanding the use of Feature Callout +// to new about: pages, make `progressPref` a +// configurable field on callout messages and +// use it to determine which pref to observe XPCOMUtils.defineLazyPreferenceGetter( lazy, "featureTourProgress", @@ -36,7 +41,7 @@ async function _handlePrefChange() { container?.classList.add("hidden"); // wait for fade out transition setTimeout(async () => { - _loadConfig(lazy.featureTourProgress.message); + await _loadConfig(); container?.remove(); await _renderCallout(); }, TRANSITION_MS); @@ -78,170 +83,6 @@ let READY = false; const TRANSITION_MS = 500; const CONTAINER_ID = "root"; -const MESSAGES = [ - { - id: "FIREFOX_VIEW_FEATURE_TOUR", - template: "multistage", - backdrop: "transparent", - transitions: false, - disableHistoryUpdates: true, - screens: [ - { - id: "FEATURE_CALLOUT_1", - parent_selector: "#tab-pickup-container", - content: { - position: "callout", - arrow_position: "top", - title: { - string_id: "callout-firefox-view-tab-pickup-title", - }, - subtitle: { - string_id: "callout-firefox-view-tab-pickup-subtitle", - }, - logo: { - imageURL: "chrome://browser/content/callout-tab-pickup.svg", - darkModeImageURL: - "chrome://browser/content/callout-tab-pickup-dark.svg", - height: "128px", - }, - primary_button: { - label: { - string_id: "callout-primary-advance-button-label", - }, - action: { - type: "SET_PREF", - data: { - pref: { - name: "browser.firefox-view.feature-tour", - value: JSON.stringify({ - message: "FIREFOX_VIEW_FEATURE_TOUR", - screen: "FEATURE_CALLOUT_2", - complete: false, - }), - }, - }, - }, - }, - dismiss_button: { - action: { - type: "SET_PREF", - data: { - pref: { - name: "browser.firefox-view.feature-tour", - value: JSON.stringify({ - message: "FIREFOX_VIEW_FEATURE_TOUR", - screen: "FEATURE_CALLOUT_1", - complete: true, - }), - }, - }, - }, - }, - }, - }, - { - id: "FEATURE_CALLOUT_2", - parent_selector: "#recently-closed-tabs-container", - content: { - position: "callout", - arrow_position: "bottom", - title: { - string_id: "callout-firefox-view-recently-closed-title", - }, - subtitle: { - string_id: "callout-firefox-view-recently-closed-subtitle", - }, - primary_button: { - label: { - string_id: "callout-primary-advance-button-label", - }, - action: { - type: "SET_PREF", - data: { - pref: { - name: "browser.firefox-view.feature-tour", - value: JSON.stringify({ - message: "FIREFOX_VIEW_FEATURE_TOUR", - screen: "FEATURE_CALLOUT_3", - complete: false, - }), - }, - }, - }, - }, - dismiss_button: { - action: { - type: "SET_PREF", - data: { - pref: { - name: "browser.firefox-view.feature-tour", - value: JSON.stringify({ - message: "FIREFOX_VIEW_FEATURE_TOUR", - screen: "FEATURE_CALLOUT_2", - complete: true, - }), - }, - }, - }, - }, - }, - }, - { - id: "FEATURE_CALLOUT_3", - parent_selector: "#colorways.content-container", - content: { - position: "callout", - arrow_position: "end", - title: { - string_id: "callout-firefox-view-colorways-title", - }, - subtitle: { - string_id: "callout-firefox-view-colorways-subtitle", - }, - logo: { - imageURL: "chrome://browser/content/callout-colorways.svg", - darkModeImageURL: - "chrome://browser/content/callout-colorways-dark.svg", - height: "128px", - }, - primary_button: { - label: { - string_id: "callout-primary-complete-button-label", - }, - action: { - type: "SET_PREF", - data: { - pref: { - name: "browser.firefox-view.feature-tour", - value: JSON.stringify({ - message: "FIREFOX_VIEW_FEATURE_TOUR", - screen: "", - complete: true, - }), - }, - }, - }, - }, - dismiss_button: { - action: { - type: "SET_PREF", - data: { - pref: { - name: "browser.firefox-view.feature-tour", - value: JSON.stringify({ - message: "FIREFOX_VIEW_FEATURE_TOUR", - screen: "FEATURE_CALLOUT_3", - complete: true, - }), - }, - }, - }, - }, - }, - }, - ], - }, -]; function _createContainer() { let parent = document.querySelector(CURRENT_SCREEN?.parent_selector); @@ -511,31 +352,15 @@ function _observeRender(container) { RENDER_OBSERVER?.observe(container, { childList: true }); } -function _loadConfig(messageId) { - // If the parent element a screen describes doesn't exist, remove screen - // and ensure last screen displays the final primary CTA - // (for example, when there are no active colorways in about:firefoxview) - function _getRelevantScreens(screens) { - const finalCTA = screens[screens.length - 1].content.primary_button; - screens = screens.filter((s, i) => { - return document.querySelector(s.parent_selector); - }); - if (screens.length) { - screens[screens.length - 1].content.primary_button = finalCTA; - } - return screens; - } - - let content = MESSAGES.find(m => m.id === messageId); - const screenId = lazy.featureTourProgress.screen; - let screenIndex; - if (content?.screens?.length && screenId) { - content.screens = _getRelevantScreens(content.screens); - screenIndex = content.screens.findIndex(s => s.id === screenId); - content.startScreen = screenIndex; - } - CURRENT_SCREEN = content?.screens?.[screenIndex || 0]; - CONFIG = content; +async function _loadConfig() { + await lazy.ASRouter.waitForInitialized; + let result = await lazy.ASRouter.sendTriggerMessage({ + // triggerId and triggerContext + id: "featureCalloutCheck", + context: { source: document.location.pathname.toLowerCase() }, + }); + CONFIG = result.message.content; + CURRENT_SCREEN = CONFIG?.screens?.[CONFIG?.startScreen || 0]; } async function _renderCallout() { @@ -555,9 +380,9 @@ async function showFeatureCallout(messageId) { return; } - _loadConfig(messageId); + await _loadConfig(); - if (!CONFIG) { + if (!CONFIG?.screens?.length) { return; } diff --git a/browser/components/firefoxview/tests/browser/browser_feature_callout.js b/browser/components/firefoxview/tests/browser/browser_feature_callout.js index 20c8f69ae91d9..ecb4c64f6f7c0 100644 --- a/browser/components/firefoxview/tests/browser/browser_feature_callout.js +++ b/browser/components/firefoxview/tests/browser/browser_feature_callout.js @@ -1,5 +1,12 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ */ + +"use strict"; + +const { BuiltInThemes } = ChromeUtils.import( + "resource:///modules/BuiltInThemes.jsm" +); + const calloutId = "root"; const calloutSelector = `#${calloutId}.featureCallout`; const primaryButtonSelector = `#${calloutId} .primary`; @@ -327,6 +334,10 @@ add_task(async function feature_callout_only_highlights_existing_elements() { ], }); + const sandbox = sinon.createSandbox(); + // Return no active colorways + sandbox.stub(BuiltInThemes, "findActiveColorwayCollection").returns(false); + await BrowserTestUtils.withNewTab( { gBrowser, @@ -335,9 +346,6 @@ add_task(async function feature_callout_only_highlights_existing_elements() { async browser => { const { document } = browser.contentWindow; await waitForCalloutScreen(document, ".FEATURE_CALLOUT_1"); - - // Remove parent element for third screen in tour - document.querySelector("#colorways.content-container").remove(); // Advance to second screen await clickPrimaryButton(document); await waitForCalloutScreen(document, ".FEATURE_CALLOUT_2"); @@ -357,6 +365,8 @@ add_task(async function feature_callout_only_highlights_existing_elements() { !document.querySelector(`${calloutSelector}:not(.hidden)`), "Feature Callout screen does not render if its parent element does not exist" ); + + sandbox.restore(); } ); }); diff --git a/browser/components/newtab/content-src/asrouter/schemas/MessagingExperiment.schema.json b/browser/components/newtab/content-src/asrouter/schemas/MessagingExperiment.schema.json index c3af6af6c1515..9d16bdc3a5e87 100644 --- a/browser/components/newtab/content-src/asrouter/schemas/MessagingExperiment.schema.json +++ b/browser/components/newtab/content-src/asrouter/schemas/MessagingExperiment.schema.json @@ -117,7 +117,8 @@ "template": { "type": "string", "enum": [ - "spotlight" + "spotlight", + "feature_callout" ] } }, @@ -1258,7 +1259,8 @@ }, "template": { "type": "string", - "const": "spotlight" + "description": "Specify whether the surface is shown as a Spotlight modal or an in-surface Feature Callout dialog", + "enum": ["spotlight", "feature_callout"] } }, "additionalProperties": true, @@ -1576,6 +1578,7 @@ "pb_newtab", "protections_panel", "spotlight", + "feature_callout", "toast_notification", "toolbar_badge", "update_action", diff --git a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/Spotlight.schema.json b/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/Spotlight.schema.json index f4aae00d5a3c9..bb79e46f7f956 100644 --- a/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/Spotlight.schema.json +++ b/browser/components/newtab/content-src/asrouter/templates/OnboardingMessage/Spotlight.schema.json @@ -161,7 +161,8 @@ }, "template": { "type": "string", - "const": "spotlight" + "description": "Specify whether the surface is shown as a Spotlight modal or an in-surface Feature Callout dialog", + "enum": ["spotlight", "feature_callout"] } }, "additionalProperties": true, diff --git a/browser/components/newtab/lib/FeatureCalloutMessages.jsm b/browser/components/newtab/lib/FeatureCalloutMessages.jsm new file mode 100644 index 0000000000000..3f79da2d23a2f --- /dev/null +++ b/browser/components/newtab/lib/FeatureCalloutMessages.jsm @@ -0,0 +1,388 @@ +/* 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/. */ +"use strict"; + +// Eventually, make this a messaging system +// provider instead of adding these message +// into OnboardingMessageProvider.jsm +const FIREFOX_VIEW_PREF = "browser.firefox-view.feature-tour"; +// Empty screens are included as placeholders to ensure step +// indicator shows the correct number of total steps in the tour +const EMPTY_SCREEN = { content: {} }; +// Generate a JEXL targeting string based on the current screen +// id found in a given Feature Callout tour progress preference +const matchCurrentScreenTargeting = (prefName, screenId) => { + return `'${prefName}' | preferenceValue | regExpMatch('(?<=screen\"\:)"(.*)(?=",)')[1] == '${screenId}'`; +}; + +const MESSAGES = () => [ + // about:firefoxview messages + { + id: "FIREFOX_VIEW_FEATURE_TOUR_1", + template: "feature_callout", + content: { + id: "FIREFOX_VIEW_FEATURE_TOUR", + template: "multistage", + backdrop: "transparent", + transitions: false, + disableHistoryUpdates: true, + screens: [ + { + id: "FEATURE_CALLOUT_1", + parent_selector: "#tabpickup-steps", + content: { + position: "callout", + arrow_position: "top", + title: { + string_id: "callout-firefox-view-tab-pickup-title", + }, + subtitle: { + string_id: "callout-firefox-view-tab-pickup-subtitle", + }, + logo: { + imageURL: "chrome://browser/content/callout-tab-pickup.svg", + darkModeImageURL: + "chrome://browser/content/callout-tab-pickup-dark.svg", + height: "128px", + }, + primary_button: { + label: { + string_id: "callout-primary-advance-button-label", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "FEATURE_CALLOUT_2", + complete: false, + }), + }, + }, + }, + }, + dismiss_button: { + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "", + complete: true, + }), + }, + }, + }, + }, + }, + }, + EMPTY_SCREEN, + EMPTY_SCREEN, + ], + }, + priority: 1, + targeting: `source == "firefoxview" && colorwaysActive && ${matchCurrentScreenTargeting( + FIREFOX_VIEW_PREF, + "FEATURE_CALLOUT_1" + )}`, + trigger: { id: "featureCalloutCheck" }, + }, + { + id: "FIREFOX_VIEW_FEATURE_TOUR_1_NO_CWS", + template: "feature_callout", + content: { + id: "FIREFOX_VIEW_FEATURE_TOUR", + template: "multistage", + backdrop: "transparent", + transitions: false, + disableHistoryUpdates: true, + screens: [ + { + id: "FEATURE_CALLOUT_1", + parent_selector: "#tabpickup-steps", + content: { + position: "callout", + arrow_position: "top", + title: { + string_id: "callout-firefox-view-tab-pickup-title", + }, + subtitle: { + string_id: "callout-firefox-view-tab-pickup-subtitle", + }, + logo: { + imageURL: "chrome://browser/content/callout-tab-pickup.svg", + darkModeImageURL: + "chrome://browser/content/callout-tab-pickup-dark.svg", + height: "128px", + }, + primary_button: { + label: { + string_id: "callout-primary-advance-button-label", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "FEATURE_CALLOUT_2", + complete: false, + }), + }, + }, + }, + }, + dismiss_button: { + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "", + complete: true, + }), + }, + }, + }, + }, + }, + }, + EMPTY_SCREEN, + ], + }, + priority: 1, + targeting: `source == "firefoxview" && !colorwaysActive && ${matchCurrentScreenTargeting( + FIREFOX_VIEW_PREF, + "FEATURE_CALLOUT_1" + )}`, + trigger: { id: "featureCalloutCheck" }, + }, + { + id: "FIREFOX_VIEW_FEATURE_TOUR_2", + template: "feature_callout", + content: { + id: "FIREFOX_VIEW_FEATURE_TOUR", + startScreen: 1, + template: "multistage", + backdrop: "transparent", + transitions: false, + disableHistoryUpdates: true, + screens: [ + EMPTY_SCREEN, + { + id: "FEATURE_CALLOUT_2", + parent_selector: "#recently-closed-tabs-container", + content: { + position: "callout", + arrow_position: "bottom", + title: { + string_id: "callout-firefox-view-recently-closed-title", + }, + subtitle: { + string_id: "callout-firefox-view-recently-closed-subtitle", + }, + primary_button: { + label: { + string_id: "callout-primary-advance-button-label", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "FEATURE_CALLOUT_3", + complete: false, + }), + }, + }, + }, + }, + dismiss_button: { + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "", + complete: true, + }), + }, + }, + }, + }, + }, + }, + EMPTY_SCREEN, + ], + }, + priority: 1, + targeting: `source == "firefoxview" && colorwaysActive && ${matchCurrentScreenTargeting( + FIREFOX_VIEW_PREF, + "FEATURE_CALLOUT_2" + )}`, + trigger: { id: "featureCalloutCheck" }, + }, + { + id: "FIREFOX_VIEW_FEATURE_TOUR_2_NO_CWS", + template: "feature_callout", + content: { + id: "FIREFOX_VIEW_FEATURE_TOUR", + startScreen: 1, + template: "multistage", + backdrop: "transparent", + transitions: false, + disableHistoryUpdates: true, + screens: [ + EMPTY_SCREEN, + { + id: "FEATURE_CALLOUT_2", + parent_selector: "#recently-closed-tabs-container", + content: { + position: "callout", + arrow_position: "bottom", + title: { + string_id: "callout-firefox-view-recently-closed-title", + }, + subtitle: { + string_id: "callout-firefox-view-recently-closed-subtitle", + }, + primary_button: { + label: { + string_id: "callout-primary-complete-button-label", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "FEATURE_CALLOUT_3", + complete: false, + }), + }, + }, + }, + }, + dismiss_button: { + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "", + complete: true, + }), + }, + }, + }, + }, + }, + }, + ], + }, + priority: 1, + targeting: `source == "firefoxview" && !colorwaysActive && ${matchCurrentScreenTargeting( + FIREFOX_VIEW_PREF, + "FEATURE_CALLOUT_2" + )}`, + trigger: { id: "featureCalloutCheck" }, + }, + { + id: "FIREFOX_VIEW_FEATURE_TOUR_3", + template: "feature_callout", + content: { + id: "FIREFOX_VIEW_FEATURE_TOUR", + startScreen: 2, + template: "multistage", + backdrop: "transparent", + transitions: false, + disableHistoryUpdates: true, + screens: [ + EMPTY_SCREEN, + EMPTY_SCREEN, + { + id: "FEATURE_CALLOUT_3", + parent_selector: "#colorways.content-container", + content: { + position: "callout", + arrow_position: "end", + title: { + string_id: "callout-firefox-view-colorways-title", + }, + subtitle: { + string_id: "callout-firefox-view-colorways-subtitle", + }, + logo: { + imageURL: "chrome://browser/content/callout-colorways.svg", + darkModeImageURL: + "chrome://browser/content/callout-colorways-dark.svg", + height: "128px", + }, + primary_button: { + label: { + string_id: "callout-primary-complete-button-label", + }, + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "", + complete: true, + }), + }, + }, + }, + }, + dismiss_button: { + action: { + type: "SET_PREF", + data: { + pref: { + name: FIREFOX_VIEW_PREF, + value: JSON.stringify({ + message: "FIREFOX_VIEW_FEATURE_TOUR", + screen: "", + complete: true, + }), + }, + }, + }, + }, + }, + }, + ], + }, + priority: 1, + targeting: `source == "firefoxview" && colorwaysActive && ${matchCurrentScreenTargeting( + FIREFOX_VIEW_PREF, + "FEATURE_CALLOUT_3" + )}`, + trigger: { id: "featureCalloutCheck" }, + }, +]; + +const FeatureCalloutMessages = { + getMessages() { + return MESSAGES(); + }, +}; + +const EXPORTED_SYMBOLS = ["FeatureCalloutMessages"]; diff --git a/browser/components/newtab/lib/OnboardingMessageProvider.jsm b/browser/components/newtab/lib/OnboardingMessageProvider.jsm index 0efa79ab879ba..3f844c09a15ca 100644 --- a/browser/components/newtab/lib/OnboardingMessageProvider.jsm +++ b/browser/components/newtab/lib/OnboardingMessageProvider.jsm @@ -7,6 +7,9 @@ const { XPCOMUtils } = ChromeUtils.importESModule( "resource://gre/modules/XPCOMUtils.sys.mjs" ); +const { FeatureCalloutMessages } = ChromeUtils.import( + "resource://activity-stream/lib/FeatureCalloutMessages.jsm" +); const lazy = {}; @@ -39,7 +42,7 @@ const L10N = new Localization([ const HOMEPAGE_PREF = "browser.startup.homepage"; const NEWTAB_PREF = "browser.newtabpage.enabled"; -const ONBOARDING_MESSAGES = () => [ +const BASE_MESSAGES = () => [ { id: "FXA_ACCOUNTS_BADGE", template: "toolbar_badge", @@ -881,6 +884,10 @@ const ONBOARDING_MESSAGES = () => [ }, ]; +// Eventually, move Feature Callout messages to their own provider +const ONBOARDING_MESSAGES = () => + BASE_MESSAGES().concat(FeatureCalloutMessages.getMessages()); + const OnboardingMessageProvider = { async getExtraAttributes() { const [header, button_label] = await L10N.formatMessages([ diff --git a/browser/components/newtab/test/xpcshell/head.js b/browser/components/newtab/test/xpcshell/head.js index 0ca988fb69561..d43cdf44ec847 100644 --- a/browser/components/newtab/test/xpcshell/head.js +++ b/browser/components/newtab/test/xpcshell/head.js @@ -94,6 +94,11 @@ async function makeValidators() { "resource://testing-common/WhatsNewMessage.schema.json", { common: true } ), + feature_callout: await schemaValidatorFor( + // For now, Feature Callout and Spotlight share a common schema + "resource://testing-common/Spotlight.schema.json", + { common: true } + ), }; messageValidators.milestone_message = messageValidators.cfr_doorhanger; diff --git a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json index f2937597b798d..0ac108ed2648f 100644 --- a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json +++ b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/TriggerActionSchemas.json @@ -216,6 +216,29 @@ }, "additionalProperties": false, "required": ["id", "params"] + }, + { + "type": "object", + "properties": { + "id": { + "type": "string", + "enum": ["featureCalloutCheck"] + }, + "context": { + "type": "object", + "properties": { + "source": { + "type": "string", + "enum": ["firefoxview"], + "description": "Which about page is the source of the trigger" + } + }, + "additionalProperties": false + } + }, + "additionalProperties": false, + "required": ["id"], + "description": "Happens when navigating to about:firefoxview or other about pages with Feature Callout tours enabled" } ] } diff --git a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md index d90c9f0da2539..ec8132d128daf 100644 --- a/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md +++ b/toolkit/components/messaging-system/schemas/TriggerActionSchemas/index.md @@ -130,3 +130,7 @@ Watch for changes on any number of preferences. Runs when a pref is added, remov params: ["pref name"] } ``` + +### `featureCalloutCheck` + +Happens when navigating to about:firefoxview or other about pages with Feature Callout tours enabled From 9dc15db244e2f541d8bfaaaa80b792c44975da58 Mon Sep 17 00:00:00 2001 From: Ryan VanderMeulen Date: Wed, 31 Aug 2022 16:10:45 +0000 Subject: [PATCH 62/65] Bug 1784990 - Update lz4 to 1.9.4. r=glandium Differential Revision: https://phabricator.services.mozilla.com/D154770 --- mfbt/lz4/LICENSE | 2 +- mfbt/lz4/README.md | 58 +++- mfbt/lz4/README.mozilla | 18 + mfbt/lz4/lz4.c | 617 +++++++++++++++++++++++----------- mfbt/lz4/lz4.h | 160 ++++++--- mfbt/lz4/lz4file.c | 311 +++++++++++++++++ mfbt/lz4/lz4file.h | 93 ++++++ mfbt/lz4/lz4frame.c | 663 +++++++++++++++++++++++-------------- mfbt/lz4/lz4frame.h | 175 +++++++--- mfbt/lz4/lz4frame_static.h | 2 +- mfbt/lz4/lz4hc.c | 236 ++++++------- mfbt/lz4/lz4hc.h | 28 +- mfbt/moz.build | 1 + 13 files changed, 1687 insertions(+), 677 deletions(-) create mode 100644 mfbt/lz4/README.mozilla create mode 100644 mfbt/lz4/lz4file.c create mode 100644 mfbt/lz4/lz4file.h diff --git a/mfbt/lz4/LICENSE b/mfbt/lz4/LICENSE index 74c2cdd7d50b4..488491695a6b6 100644 --- a/mfbt/lz4/LICENSE +++ b/mfbt/lz4/LICENSE @@ -1,5 +1,5 @@ LZ4 Library -Copyright (c) 2011-2016, Yann Collet +Copyright (c) 2011-2020, Yann Collet All rights reserved. Redistribution and use in source and binary forms, with or without modification, diff --git a/mfbt/lz4/README.md b/mfbt/lz4/README.md index e2af868ff4ad3..08d1cef2bf8ad 100644 --- a/mfbt/lz4/README.md +++ b/mfbt/lz4/README.md @@ -2,16 +2,20 @@ LZ4 - Library Files ================================ The `/lib` directory contains many files, but depending on project's objectives, -not all of them are necessary. +not all of them are required. +Limited systems may want to reduce the nb of source files to include +as a way to reduce binary size and dependencies. -#### Minimal LZ4 build +Capabilities are added at the "level" granularity, detailed below. + +#### Level 1 : Minimal LZ4 build The minimum required is **`lz4.c`** and **`lz4.h`**, which provides the fast compression and decompression algorithms. They generate and decode data using the [LZ4 block format]. -#### High Compression variant +#### Level 2 : High Compression variant For more compression ratio at the cost of compression speed, the High Compression variant called **lz4hc** is available. @@ -20,7 +24,7 @@ This variant also compresses data using the [LZ4 block format], and depends on regular `lib/lz4.*` source files. -#### Frame support, for interoperability +#### Level 3 : Frame support, for interoperability In order to produce compressed data compatible with `lz4` command line utility, it's necessary to use the [official interoperable frame format]. @@ -28,14 +32,29 @@ This format is generated and decoded automatically by the **lz4frame** library. Its public API is described in `lib/lz4frame.h`. In order to work properly, lz4frame needs all other modules present in `/lib`, including, lz4 and lz4hc, and also **xxhash**. -So it's necessary to include all `*.c` and `*.h` files present in `/lib`. +So it's necessary to also include `xxhash.c` and `xxhash.h`. + + +#### Level 4 : File compression operations + +As a helper around file operations, +the library has been recently extended with `lz4file.c` and `lz4file.h` +(still considered experimental at the time of this writing). +These helpers allow opening, reading, writing, and closing files +using transparent LZ4 compression / decompression. +As a consequence, using `lz4file` adds a dependency on ``. + +`lz4file` relies on `lz4frame` in order to produce compressed data +conformant to the [LZ4 Frame format] specification. +Consequently, to enable this capability, +it's necessary to include all `*.c` and `*.h` files from `lib/` directory. #### Advanced / Experimental API Definitions which are not guaranteed to remain stable in future versions, are protected behind macros, such as `LZ4_STATIC_LINKING_ONLY`. -As the name strongly implies, these definitions should only be invoked +As the name suggests, these definitions should only be invoked in the context of static linking ***only***. Otherwise, dependent application may fail on API or ABI break in the future. The associated symbols are also not exposed by the dynamic library by default. @@ -58,7 +77,7 @@ The following build macro can be selected to adjust source code behavior at comp Set to 65535 by default, which is the maximum value supported by lz4 format. Reducing maximum distance will reduce opportunities for LZ4 to find matches, hence will produce a worse compression ratio. - However, a smaller max distance can allow compatibility with specific decoders using limited memory budget. + Setting a smaller max distance could allow compatibility with specific decoders with limited memory budget. This build macro only influences the compressed output of the compressor. - `LZ4_DISABLE_DEPRECATE_WARNINGS` : invoking a deprecated function will make the compiler generate a warning. @@ -69,15 +88,11 @@ The following build macro can be selected to adjust source code behavior at comp This build macro offers another project-specific method by defining `LZ4_DISABLE_DEPRECATE_WARNINGS` before including the LZ4 header files. -- `LZ4_USER_MEMORY_FUNCTIONS` : replace calls to 's `malloc`, `calloc` and `free` - by user-defined functions, which must be called `LZ4_malloc()`, `LZ4_calloc()` and `LZ4_free()`. - User functions must be available at link time. - - `LZ4_FORCE_SW_BITCOUNT` : by default, the compression algorithm tries to determine lengths by using bitcount instructions, generally implemented as fast single instructions in many cpus. In case the target cpus doesn't support it, or compiler intrinsic doesn't work, or feature bad performance, it's possible to use an optimized software path instead. - This is achieved by setting this build macros . + This is achieved by setting this build macros. In most cases, it's not expected to be necessary, but it can be legitimately considered for less common platforms. @@ -85,6 +100,22 @@ The following build macro can be selected to adjust source code behavior at comp passed as argument to become a compression state is suitably aligned. This test can be disabled if it proves flaky, by setting this value to 0. +- `LZ4_USER_MEMORY_FUNCTIONS` : replace calls to ``'s `malloc()`, `calloc()` and `free()` + by user-defined functions, which must be named `LZ4_malloc()`, `LZ4_calloc()` and `LZ4_free()`. + User functions must be available at link time. + +- `LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION` : + Remove support of dynamic memory allocation. + For more details, see description of this macro in `lib/lz4.c`. + +- `LZ4_FREESTANDING` : by setting this build macro to 1, + LZ4/HC removes dependencies on the C standard library, + including allocation functions and `memmove()`, `memcpy()`, and `memset()`. + This build macro is designed to help use LZ4/HC in restricted environments + (embedded, bootloader, etc). + For more details, see description of this macro in `lib/lz4.h`. + + #### Amalgamation @@ -101,7 +132,7 @@ All `*.h` files present in `/lib` remain necessary to compile `lz4_all.c`. DLL can be created using MinGW+MSYS with the `make liblz4` command. This command creates `dll\liblz4.dll` and the import library `dll\liblz4.lib`. -To override the `dlltool` command when cross-compiling on Linux, just set the `DLLTOOL` variable. Example of cross compilation on Linux with mingw-w64 64 bits: +To override the `dlltool` command when cross-compiling on Linux, just set the `DLLTOOL` variable. Example of cross compilation on Linux with mingw-w64 64 bits: ``` make BUILD_STATIC=no CC=x86_64-w64-mingw32-gcc DLLTOOL=x86_64-w64-mingw32-dlltool OS=Windows_NT ``` @@ -127,6 +158,7 @@ Other files present in the directory are not source code. They are : - `README.md` : this file [official interoperable frame format]: ../doc/lz4_Frame_format.md +[LZ4 Frame format]: ../doc/lz4_Frame_format.md [LZ4 block format]: ../doc/lz4_Block_format.md diff --git a/mfbt/lz4/README.mozilla b/mfbt/lz4/README.mozilla new file mode 100644 index 0000000000000..3974a20090001 --- /dev/null +++ b/mfbt/lz4/README.mozilla @@ -0,0 +1,18 @@ +This directory contains the LZ4 source from the upstream repo: +https://github.com/lz4/lz4/ + +Current version: 1.9.4 [5ff839680134437dbf4678f3d0c7b371d84f4964] + +Our in-tree copy of LZ4 does not depend on any generated files from the +upstream build system, only the lz4*.{c,h} files found in the lib +sub-directory. Therefore, it should be sufficient to simply overwrite +the in-tree files with the updated ones from upstream. + +If the collection of source files changes, manual updates to moz.build may be +needed as we don't use the upstream makefiles. + +Note that we do NOT use the copy of xxhash.{c,h} from the LZ4 repo. We +instead use the newer release from that project's upstream repo: +https://github.com/Cyan4973/xxHash + +Current version: 0.8.1 [35b0373c697b5f160d3db26b1cbb45a0d5ba788c] diff --git a/mfbt/lz4/lz4.c b/mfbt/lz4/lz4.c index 9f5e9bfa0839f..654bfdf32f96a 100644 --- a/mfbt/lz4/lz4.c +++ b/mfbt/lz4/lz4.c @@ -1,6 +1,6 @@ /* LZ4 - Fast LZ compression algorithm - Copyright (C) 2011-present, Yann Collet. + Copyright (C) 2011-2020, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -124,6 +124,7 @@ #if defined(_MSC_VER) && (_MSC_VER >= 1400) /* Visual Studio 2005+ */ # include /* only present in VS2005+ */ # pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 6237) /* disable: C6237: conditional expression is always 0 */ #endif /* _MSC_VER */ #ifndef LZ4_FORCE_INLINE @@ -187,7 +188,27 @@ /*-************************************ * Memory routines **************************************/ -#ifdef LZ4_USER_MEMORY_FUNCTIONS + +/*! LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION : + * Disable relatively high-level LZ4/HC functions that use dynamic memory + * allocation functions (malloc(), calloc(), free()). + * + * Note that this is a compile-time switch. And since it disables + * public/stable LZ4 v1 API functions, we don't recommend using this + * symbol to generate a library for distribution. + * + * The following public functions are removed when this symbol is defined. + * - lz4 : LZ4_createStream, LZ4_freeStream, + * LZ4_createStreamDecode, LZ4_freeStreamDecode, LZ4_create (deprecated) + * - lz4hc : LZ4_createStreamHC, LZ4_freeStreamHC, + * LZ4_createHC (deprecated), LZ4_freeHC (deprecated) + * - lz4frame, lz4file : All LZ4F_* functions + */ +#if defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) +# define ALLOC(s) lz4_error_memory_allocation_is_disabled +# define ALLOC_AND_ZERO(s) lz4_error_memory_allocation_is_disabled +# define FREEMEM(p) lz4_error_memory_allocation_is_disabled +#elif defined(LZ4_USER_MEMORY_FUNCTIONS) /* memory management functions can be customized by user project. * Below functions must exist somewhere in the Project * and be available at link time */ @@ -204,8 +225,13 @@ void LZ4_free(void* p); # define FREEMEM(p) free(p) #endif -#include /* memset, memcpy */ -#define MEM_INIT(p,v,s) memset((p),(v),(s)) +#if ! LZ4_FREESTANDING +# include /* memset, memcpy */ +#endif +#if !defined(LZ4_memset) +# define LZ4_memset(p,v,s) memset((p),(v),(s)) +#endif +#define MEM_INIT(p,v,s) LZ4_memset((p),(v),(s)) /*-************************************ @@ -316,10 +342,20 @@ typedef enum { * memcpy() as if it were standard compliant, so it can inline it in freestanding * environments. This is needed when decompressing the Linux Kernel, for example. */ -#if defined(__GNUC__) && (__GNUC__ >= 4) -#define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) -#else -#define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +#if !defined(LZ4_memcpy) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memcpy(dst, src, size) __builtin_memcpy(dst, src, size) +# else +# define LZ4_memcpy(dst, src, size) memcpy(dst, src, size) +# endif +#endif + +#if !defined(LZ4_memmove) +# if defined(__GNUC__) && (__GNUC__ >= 4) +# define LZ4_memmove __builtin_memmove +# else +# define LZ4_memmove memmove +# endif #endif static unsigned LZ4_isLittleEndian(void) @@ -343,14 +379,14 @@ static void LZ4_write32(void* memPtr, U32 value) { *(U32*)memPtr = value; } /* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ /* currently only defined for gcc and icc */ -typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) unalign; +typedef union { U16 u16; U32 u32; reg_t uArch; } __attribute__((packed)) LZ4_unalign; -static U16 LZ4_read16(const void* ptr) { return ((const unalign*)ptr)->u16; } -static U32 LZ4_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } -static reg_t LZ4_read_ARCH(const void* ptr) { return ((const unalign*)ptr)->uArch; } +static U16 LZ4_read16(const void* ptr) { return ((const LZ4_unalign*)ptr)->u16; } +static U32 LZ4_read32(const void* ptr) { return ((const LZ4_unalign*)ptr)->u32; } +static reg_t LZ4_read_ARCH(const void* ptr) { return ((const LZ4_unalign*)ptr)->uArch; } -static void LZ4_write16(void* memPtr, U16 value) { ((unalign*)memPtr)->u16 = value; } -static void LZ4_write32(void* memPtr, U32 value) { ((unalign*)memPtr)->u32 = value; } +static void LZ4_write16(void* memPtr, U16 value) { ((LZ4_unalign*)memPtr)->u16 = value; } +static void LZ4_write32(void* memPtr, U32 value) { ((LZ4_unalign*)memPtr)->u32 = value; } #else /* safe and portable access using memcpy() */ @@ -421,10 +457,12 @@ static const int dec64table[8] = {0, 0, 0, -1, -4, 1, 2, 3}; #ifndef LZ4_FAST_DEC_LOOP # if defined __i386__ || defined _M_IX86 || defined __x86_64__ || defined _M_X64 # define LZ4_FAST_DEC_LOOP 1 +# elif defined(__aarch64__) && defined(__APPLE__) +# define LZ4_FAST_DEC_LOOP 1 # elif defined(__aarch64__) && !defined(__clang__) - /* On aarch64, we disable this optimization for clang because on certain - * mobile chipsets, performance is reduced with clang. For information - * refer to https://github.com/lz4/lz4/pull/707 */ + /* On non-Apple aarch64, we disable this optimization for clang because + * on certain mobile chipsets, performance is reduced with clang. For + * more information refer to https://github.com/lz4/lz4/pull/707 */ # define LZ4_FAST_DEC_LOOP 1 # else # define LZ4_FAST_DEC_LOOP 0 @@ -486,7 +524,14 @@ LZ4_memcpy_using_offset(BYTE* dstPtr, const BYTE* srcPtr, BYTE* dstEnd, const si case 2: LZ4_memcpy(v, srcPtr, 2); LZ4_memcpy(&v[2], srcPtr, 2); +#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ +# pragma warning(push) +# pragma warning(disable : 6385) /* warning C6385: Reading invalid data from 'v'. */ +#endif LZ4_memcpy(&v[4], v, 4); +#if defined(_MSC_VER) && (_MSC_VER <= 1933) /* MSVC 2022 ver 17.3 or earlier */ +# pragma warning(pop) +#endif break; case 4: LZ4_memcpy(v, srcPtr, 4); @@ -515,9 +560,20 @@ static unsigned LZ4_NbCommonBytes (reg_t val) assert(val != 0); if (LZ4_isLittleEndian()) { if (sizeof(val) == 8) { -# if defined(_MSC_VER) && (_MSC_VER >= 1800) && defined(_M_AMD64) && !defined(LZ4_FORCE_SW_BITCOUNT) +# if defined(_MSC_VER) && (_MSC_VER >= 1800) && (defined(_M_AMD64) && !defined(_M_ARM64EC)) && !defined(LZ4_FORCE_SW_BITCOUNT) +/*-************************************************************************************************* +* ARM64EC is a Microsoft-designed ARM64 ABI compatible with AMD64 applications on ARM64 Windows 11. +* The ARM64EC ABI does not support AVX/AVX2/AVX512 instructions, nor their relevant intrinsics +* including _tzcnt_u64. Therefore, we need to neuter the _tzcnt_u64 code path for ARM64EC. +****************************************************************************************************/ +# if defined(__clang__) && (__clang_major__ < 10) + /* Avoid undefined clang-cl intrinsics issue. + * See https://github.com/lz4/lz4/pull/1017 for details. */ + return (unsigned)__builtin_ia32_tzcnt_u64(val) >> 3; +# else /* x64 CPUS without BMI support interpret `TZCNT` as `REP BSF` */ return (unsigned)_tzcnt_u64(val) >> 3; +# endif # elif defined(_MSC_VER) && defined(_WIN64) && !defined(LZ4_FORCE_SW_BITCOUNT) unsigned long r = 0; _BitScanForward64(&r, (U64)val); @@ -652,10 +708,10 @@ typedef enum { clearedTable = 0, byPtr, byU32, byU16 } tableType_t; * - usingExtDict : Like withPrefix64k, but the preceding content is somewhere * else in memory, starting at ctx->dictionary with length * ctx->dictSize. - * - usingDictCtx : Like usingExtDict, but everything concerning the preceding - * content is in a separate context, pointed to by - * ctx->dictCtx. ctx->dictionary, ctx->dictSize, and table - * entries in the current context that refer to positions + * - usingDictCtx : Everything concerning the preceding content is + * in a separate context, pointed to by ctx->dictCtx. + * ctx->dictionary, ctx->dictSize, and table entries + * in the current context that refer to positions * preceding the beginning of the current compression are * ignored. Instead, ctx->dictCtx->dictionary and ctx->dictCtx * ->dictSize describe the location and size of the preceding @@ -672,12 +728,12 @@ typedef enum { noDictIssue = 0, dictSmall } dictIssue_directive; int LZ4_versionNumber (void) { return LZ4_VERSION_NUMBER; } const char* LZ4_versionString(void) { return LZ4_VERSION_STRING; } int LZ4_compressBound(int isize) { return LZ4_COMPRESSBOUND(isize); } -int LZ4_sizeofState(void) { return LZ4_STREAMSIZE; } +int LZ4_sizeofState(void) { return sizeof(LZ4_stream_t); } -/*-************************************ -* Internal Definitions used in Tests -**************************************/ +/*-**************************************** +* Internal Definitions, used only in Tests +*******************************************/ #if defined (__cplusplus) extern "C" { #endif @@ -687,7 +743,9 @@ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, int compressedSize, int maxOutputSize, const void* dictStart, size_t dictSize); - +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize); #if defined (__cplusplus) } #endif @@ -827,9 +885,10 @@ LZ4_prepareTable(LZ4_stream_t_internal* const cctx, } } - /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, is faster - * than compressing without a gap. However, compressing with - * currentOffset == 0 is faster still, so we preserve that case. + /* Adding a gap, so all previous entries are > LZ4_DISTANCE_MAX back, + * is faster than compressing without a gap. + * However, compressing with currentOffset == 0 is faster still, + * so we preserve that case. */ if (cctx->currentOffset != 0 && tableType == byU32) { DEBUGLOG(5, "LZ4_prepareTable: adding 64KB to currentOffset"); @@ -853,7 +912,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( const char* const source, char* const dest, const int inputSize, - int *inputConsumed, /* only written when outputDirective == fillOutput */ + int* inputConsumed, /* only written when outputDirective == fillOutput */ const int maxOutputSize, const limitedOutput_directive outputDirective, const tableType_t tableType, @@ -885,7 +944,8 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( /* the dictCtx currentOffset is indexed on the start of the dictionary, * while a dictionary in the current context precedes the currentOffset */ - const BYTE* dictBase = !dictionary ? NULL : (dictDirective == usingDictCtx) ? + const BYTE* dictBase = (dictionary == NULL) ? NULL : + (dictDirective == usingDictCtx) ? dictionary + dictSize - dictCtx->currentOffset : dictionary + dictSize - startIndex; @@ -981,10 +1041,11 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( match = base + matchIndex; lowLimit = (const BYTE*)source; } - } else if (dictDirective==usingExtDict) { + } else if (dictDirective == usingExtDict) { if (matchIndex < startIndex) { DEBUGLOG(7, "extDict candidate: matchIndex=%5u < startIndex=%5u", matchIndex, startIndex); assert(startIndex - matchIndex >= MINMATCH); + assert(dictBase); match = dictBase + matchIndex; lowLimit = dictionary; } else { @@ -1048,7 +1109,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( _next_match: /* at this stage, the following variables must be correctly set : * - ip : at start of LZ operation - * - match : at start of previous pattern occurence; can be within current prefix, or within extDict + * - match : at start of previous pattern occurrence; can be within current prefix, or within extDict * - offset : if maybe_ext_memSegment==1 (constant) * - lowLimit : must be == dictionary to mean "match is within extDict"; must be == source otherwise * - token and *token : position to write 4-bits for match length; higher 4-bits for literal length supposed already written @@ -1173,6 +1234,7 @@ LZ4_FORCE_INLINE int LZ4_compress_generic_validated( } } else if (dictDirective==usingExtDict) { if (matchIndex < startIndex) { + assert(dictBase); match = dictBase + matchIndex; lowLimit = dictionary; /* required for match length counter */ } else { @@ -1355,7 +1417,7 @@ int LZ4_compress_fast(const char* source, char* dest, int inputSize, int maxOutp { int result; #if (LZ4_HEAPMODE) - LZ4_stream_t* ctxPtr = ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ + LZ4_stream_t* ctxPtr = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); /* malloc-calloc always properly aligned */ if (ctxPtr == NULL) return 0; #else LZ4_stream_t ctx; @@ -1420,15 +1482,17 @@ int LZ4_compress_destSize(const char* src, char* dst, int* srcSizePtr, int targe * Streaming functions ********************************/ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_stream_t* LZ4_createStream(void) { LZ4_stream_t* const lz4s = (LZ4_stream_t*)ALLOC(sizeof(LZ4_stream_t)); - LZ4_STATIC_ASSERT(LZ4_STREAMSIZE >= sizeof(LZ4_stream_t_internal)); /* A compilation error here means LZ4_STREAMSIZE is not large enough */ + LZ4_STATIC_ASSERT(sizeof(LZ4_stream_t) >= sizeof(LZ4_stream_t_internal)); DEBUGLOG(4, "LZ4_createStream %p", lz4s); if (lz4s == NULL) return NULL; LZ4_initStream(lz4s, sizeof(*lz4s)); return lz4s; } +#endif static size_t LZ4_stream_t_alignment(void) { @@ -1462,6 +1526,7 @@ void LZ4_resetStream_fast(LZ4_stream_t* ctx) { LZ4_prepareTable(&(ctx->internal_donotuse), 0, byU32); } +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) int LZ4_freeStream (LZ4_stream_t* LZ4_stream) { if (!LZ4_stream) return 0; /* support free on NULL */ @@ -1469,6 +1534,7 @@ int LZ4_freeStream (LZ4_stream_t* LZ4_stream) FREEMEM(LZ4_stream); return (0); } +#endif #define HASH_UNIT sizeof(reg_t) @@ -1514,8 +1580,9 @@ int LZ4_loadDict (LZ4_stream_t* LZ4_dict, const char* dictionary, int dictSize) return (int)dict->dictSize; } -void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) { - const LZ4_stream_t_internal* dictCtx = dictionaryStream == NULL ? NULL : +void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream) +{ + const LZ4_stream_t_internal* dictCtx = (dictionaryStream == NULL) ? NULL : &(dictionaryStream->internal_donotuse); DEBUGLOG(4, "LZ4_attach_dictionary (%p, %p, size %u)", @@ -1568,36 +1635,40 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, int acceleration) { const tableType_t tableType = byU32; - LZ4_stream_t_internal* streamPtr = &LZ4_stream->internal_donotuse; - const BYTE* dictEnd = streamPtr->dictionary + streamPtr->dictSize; + LZ4_stream_t_internal* const streamPtr = &LZ4_stream->internal_donotuse; + const char* dictEnd = streamPtr->dictSize ? (const char*)streamPtr->dictionary + streamPtr->dictSize : NULL; - DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i)", inputSize); + DEBUGLOG(5, "LZ4_compress_fast_continue (inputSize=%i, dictSize=%u)", inputSize, streamPtr->dictSize); - LZ4_renormDictT(streamPtr, inputSize); /* avoid index overflow */ + LZ4_renormDictT(streamPtr, inputSize); /* fix index overflow */ if (acceleration < 1) acceleration = LZ4_ACCELERATION_DEFAULT; if (acceleration > LZ4_ACCELERATION_MAX) acceleration = LZ4_ACCELERATION_MAX; /* invalidate tiny dictionaries */ - if ( (streamPtr->dictSize-1 < 4-1) /* intentional underflow */ - && (dictEnd != (const BYTE*)source) ) { + if ( (streamPtr->dictSize < 4) /* tiny dictionary : not enough for a hash */ + && (dictEnd != source) /* prefix mode */ + && (inputSize > 0) /* tolerance : don't lose history, in case next invocation would use prefix mode */ + && (streamPtr->dictCtx == NULL) /* usingDictCtx */ + ) { DEBUGLOG(5, "LZ4_compress_fast_continue: dictSize(%u) at addr:%p is too small", streamPtr->dictSize, streamPtr->dictionary); + /* remove dictionary existence from history, to employ faster prefix mode */ streamPtr->dictSize = 0; streamPtr->dictionary = (const BYTE*)source; - dictEnd = (const BYTE*)source; + dictEnd = source; } /* Check overlapping input/dictionary space */ - { const BYTE* sourceEnd = (const BYTE*) source + inputSize; - if ((sourceEnd > streamPtr->dictionary) && (sourceEnd < dictEnd)) { + { const char* const sourceEnd = source + inputSize; + if ((sourceEnd > (const char*)streamPtr->dictionary) && (sourceEnd < dictEnd)) { streamPtr->dictSize = (U32)(dictEnd - sourceEnd); if (streamPtr->dictSize > 64 KB) streamPtr->dictSize = 64 KB; if (streamPtr->dictSize < 4) streamPtr->dictSize = 0; - streamPtr->dictionary = dictEnd - streamPtr->dictSize; + streamPtr->dictionary = (const BYTE*)dictEnd - streamPtr->dictSize; } } /* prefix mode : source data follows dictionary */ - if (dictEnd == (const BYTE*)source) { + if (dictEnd == source) { if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) return LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, withPrefix64k, dictSmall, acceleration); else @@ -1623,7 +1694,7 @@ int LZ4_compress_fast_continue (LZ4_stream_t* LZ4_stream, } else { result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingDictCtx, noDictIssue, acceleration); } - } else { + } else { /* small data <= 4 KB */ if ((streamPtr->dictSize < 64 KB) && (streamPtr->dictSize < streamPtr->currentOffset)) { result = LZ4_compress_generic(streamPtr, source, dest, inputSize, NULL, maxOutputSize, limitedOutput, tableType, usingExtDict, dictSmall, acceleration); } else { @@ -1661,21 +1732,25 @@ int LZ4_compress_forceExtDict (LZ4_stream_t* LZ4_dict, const char* source, char* /*! LZ4_saveDict() : * If previously compressed data block is not guaranteed to remain available at its memory location, * save it into a safer place (char* safeBuffer). - * Note : you don't need to call LZ4_loadDict() afterwards, - * dictionary is immediately usable, you can therefore call LZ4_compress_fast_continue(). - * Return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. + * Note : no need to call LZ4_loadDict() afterwards, dictionary is immediately usable, + * one can therefore call LZ4_compress_fast_continue() right after. + * @return : saved dictionary size in bytes (necessarily <= dictSize), or 0 if error. */ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) { LZ4_stream_t_internal* const dict = &LZ4_dict->internal_donotuse; - const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + + DEBUGLOG(5, "LZ4_saveDict : dictSize=%i, safeBuffer=%p", dictSize, safeBuffer); if ((U32)dictSize > 64 KB) { dictSize = 64 KB; } /* useless to define a dictionary > 64 KB */ if ((U32)dictSize > dict->dictSize) { dictSize = (int)dict->dictSize; } if (safeBuffer == NULL) assert(dictSize == 0); - if (dictSize > 0) - memmove(safeBuffer, previousDictEnd - dictSize, dictSize); + if (dictSize > 0) { + const BYTE* const previousDictEnd = dict->dictionary + dict->dictSize; + assert(dict->dictionary); + LZ4_memmove(safeBuffer, previousDictEnd - dictSize, (size_t)dictSize); + } dict->dictionary = (const BYTE*)safeBuffer; dict->dictSize = (U32)dictSize; @@ -1689,39 +1764,163 @@ int LZ4_saveDict (LZ4_stream_t* LZ4_dict, char* safeBuffer, int dictSize) * Decompression functions ********************************/ -typedef enum { endOnOutputSize = 0, endOnInputSize = 1 } endCondition_directive; typedef enum { decode_full_block = 0, partial_decode = 1 } earlyEnd_directive; #undef MIN #define MIN(a,b) ( (a) < (b) ? (a) : (b) ) + +/* variant for decompress_unsafe() + * does not know end of input + * presumes input is well formed + * note : will consume at least one byte */ +size_t read_long_length_no_check(const BYTE** pp) +{ + size_t b, l = 0; + do { b = **pp; (*pp)++; l += b; } while (b==255); + DEBUGLOG(6, "read_long_length_no_check: +length=%zu using %zu input bytes", l, l/255 + 1) + return l; +} + +/* core decoder variant for LZ4_decompress_fast*() + * for legacy support only : these entry points are deprecated. + * - Presumes input is correctly formed (no defense vs malformed inputs) + * - Does not know input size (presume input buffer is "large enough") + * - Decompress a full block (only) + * @return : nb of bytes read from input. + * Note : this variant is not optimized for speed, just for maintenance. + * the goal is to remove support of decompress_fast*() variants by v2.0 +**/ +LZ4_FORCE_INLINE int +LZ4_decompress_unsafe_generic( + const BYTE* const istart, + BYTE* const ostart, + int decompressedSize, + + size_t prefixSize, + const BYTE* const dictStart, /* only if dict==usingExtDict */ + const size_t dictSize /* note: =0 if dictStart==NULL */ + ) +{ + const BYTE* ip = istart; + BYTE* op = (BYTE*)ostart; + BYTE* const oend = ostart + decompressedSize; + const BYTE* const prefixStart = ostart - prefixSize; + + DEBUGLOG(5, "LZ4_decompress_unsafe_generic"); + if (dictStart == NULL) assert(dictSize == 0); + + while (1) { + /* start new sequence */ + unsigned token = *ip++; + + /* literals */ + { size_t ll = token >> ML_BITS; + if (ll==15) { + /* long literal length */ + ll += read_long_length_no_check(&ip); + } + if ((size_t)(oend-op) < ll) return -1; /* output buffer overflow */ + LZ4_memmove(op, ip, ll); /* support in-place decompression */ + op += ll; + ip += ll; + if ((size_t)(oend-op) < MFLIMIT) { + if (op==oend) break; /* end of block */ + DEBUGLOG(5, "invalid: literals end at distance %zi from end of block", oend-op); + /* incorrect end of block : + * last match must start at least MFLIMIT==12 bytes before end of output block */ + return -1; + } } + + /* match */ + { size_t ml = token & 15; + size_t const offset = LZ4_readLE16(ip); + ip+=2; + + if (ml==15) { + /* long literal length */ + ml += read_long_length_no_check(&ip); + } + ml += MINMATCH; + + if ((size_t)(oend-op) < ml) return -1; /* output buffer overflow */ + + { const BYTE* match = op - offset; + + /* out of range */ + if (offset > (size_t)(op - prefixStart) + dictSize) { + DEBUGLOG(6, "offset out of range"); + return -1; + } + + /* check special case : extDict */ + if (offset > (size_t)(op - prefixStart)) { + /* extDict scenario */ + const BYTE* const dictEnd = dictStart + dictSize; + const BYTE* extMatch = dictEnd - (offset - (size_t)(op-prefixStart)); + size_t const extml = (size_t)(dictEnd - extMatch); + if (extml > ml) { + /* match entirely within extDict */ + LZ4_memmove(op, extMatch, ml); + op += ml; + ml = 0; + } else { + /* match split between extDict & prefix */ + LZ4_memmove(op, extMatch, extml); + op += extml; + ml -= extml; + } + match = prefixStart; + } + + /* match copy - slow variant, supporting overlap copy */ + { size_t u; + for (u=0; u= lencheck. - * loop_check - check ip >= lencheck in body of loop. Returns loop_error if so. - * initial_check - check ip >= lencheck before start of loop. Returns initial_error if so. - * error (output) - error code. Should be set to 0 before call. - */ -typedef enum { loop_error = -2, initial_error = -1, ok = 0 } variable_length_error; -LZ4_FORCE_INLINE unsigned -read_variable_length(const BYTE**ip, const BYTE* lencheck, - int loop_check, int initial_check, - variable_length_error* error) -{ - U32 length = 0; - U32 s; - if (initial_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = initial_error; - return length; + * @ip : input pointer + * @ilimit : position after which if length is not decoded, the input is necessarily corrupted. + * @initial_check - check ip >= ipmax before start of loop. Returns initial_error if so. + * @error (output) - error code. Must be set to 0 before call. +**/ +typedef size_t Rvl_t; +static const Rvl_t rvl_error = (Rvl_t)(-1); +LZ4_FORCE_INLINE Rvl_t +read_variable_length(const BYTE** ip, const BYTE* ilimit, + int initial_check) +{ + Rvl_t s, length = 0; + assert(ip != NULL); + assert(*ip != NULL); + assert(ilimit != NULL); + if (initial_check && unlikely((*ip) >= ilimit)) { /* read limit reached */ + return rvl_error; } do { s = **ip; (*ip)++; length += s; - if (loop_check && unlikely((*ip) >= lencheck)) { /* overflow detection */ - *error = loop_error; - return length; + if (unlikely((*ip) > ilimit)) { /* read limit reached */ + return rvl_error; + } + /* accumulator overflow detection (32-bit mode only) */ + if ((sizeof(length)<8) && unlikely(length > ((Rvl_t)(-1)/2)) ) { + return rvl_error; } } while (s==255); @@ -1741,7 +1940,6 @@ LZ4_decompress_generic( int srcSize, int outputSize, /* If endOnInput==endOnInputSize, this value is `dstCapacity` */ - endCondition_directive endOnInput, /* endOnOutputSize, endOnInputSize */ earlyEnd_directive partialDecoding, /* full, partial */ dict_directive dict, /* noDict, withPrefix64k, usingExtDict */ const BYTE* const lowPrefix, /* always <= dst, == dst when no prefix */ @@ -1749,7 +1947,7 @@ LZ4_decompress_generic( const size_t dictSize /* note : = 0 if noDict */ ) { - if (src == NULL) { return -1; } + if ((src == NULL) || (outputSize < 0)) { return -1; } { const BYTE* ip = (const BYTE*) src; const BYTE* const iend = ip + srcSize; @@ -1760,13 +1958,12 @@ LZ4_decompress_generic( const BYTE* const dictEnd = (dictStart == NULL) ? NULL : dictStart + dictSize; - const int safeDecode = (endOnInput==endOnInputSize); - const int checkOffset = ((safeDecode) && (dictSize < (int)(64 KB))); + const int checkOffset = (dictSize < (int)(64 KB)); /* Set up the "end" pointers for the shortcut. */ - const BYTE* const shortiend = iend - (endOnInput ? 14 : 8) /*maxLL*/ - 2 /*offset*/; - const BYTE* const shortoend = oend - (endOnInput ? 14 : 8) /*maxLL*/ - 18 /*maxML*/; + const BYTE* const shortiend = iend - 14 /*maxLL*/ - 2 /*offset*/; + const BYTE* const shortoend = oend - 14 /*maxLL*/ - 18 /*maxML*/; const BYTE* match; size_t offset; @@ -1778,83 +1975,70 @@ LZ4_decompress_generic( /* Special cases */ assert(lowPrefix <= op); - if ((endOnInput) && (unlikely(outputSize==0))) { + if (unlikely(outputSize==0)) { /* Empty output buffer */ if (partialDecoding) return 0; return ((srcSize==1) && (*ip==0)) ? 0 : -1; } - if ((!endOnInput) && (unlikely(outputSize==0))) { return (*ip==0 ? 1 : -1); } - if ((endOnInput) && unlikely(srcSize==0)) { return -1; } + if (unlikely(srcSize==0)) { return -1; } - /* Currently the fast loop shows a regression on qualcomm arm chips. */ + /* LZ4_FAST_DEC_LOOP: + * designed for modern OoO performance cpus, + * where copying reliably 32-bytes is preferable to an unpredictable branch. + * note : fast loop may show a regression for some client arm chips. */ #if LZ4_FAST_DEC_LOOP if ((oend - op) < FASTLOOP_SAFE_DISTANCE) { DEBUGLOG(6, "skip fast decode loop"); goto safe_decode; } - /* Fast loop : decode sequences as long as output < iend-FASTLOOP_SAFE_DISTANCE */ + /* Fast loop : decode sequences as long as output < oend-FASTLOOP_SAFE_DISTANCE */ while (1) { /* Main fastloop assertion: We can always wildcopy FASTLOOP_SAFE_DISTANCE */ assert(oend - op >= FASTLOOP_SAFE_DISTANCE); - if (endOnInput) { assert(ip < iend); } + assert(ip < iend); token = *ip++; length = token >> ML_BITS; /* literal length */ - assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ - /* decode literal length */ if (length == RUN_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); - if (error == initial_error) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ /* copy literals */ cpy = op+length; LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if (endOnInput) { /* LZ4_decompress_safe() */ - if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } - LZ4_wildCopy32(op, ip, cpy); - } else { /* LZ4_decompress_fast() */ - if (cpy>oend-8) { goto safe_literal_copy; } - LZ4_wildCopy8(op, ip, cpy); /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : - * it doesn't know input length, and only relies on end-of-block properties */ - } + if ((cpy>oend-32) || (ip+length>iend-32)) { goto safe_literal_copy; } + LZ4_wildCopy32(op, ip, cpy); ip += length; op = cpy; } else { cpy = op+length; - if (endOnInput) { /* LZ4_decompress_safe() */ - DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); - /* We don't need to check oend, since we check it once for each loop below */ - if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } - /* Literals can only be 14, but hope compilers optimize if we copy by a register size */ - LZ4_memcpy(op, ip, 16); - } else { /* LZ4_decompress_fast() */ - /* LZ4_decompress_fast() cannot copy more than 8 bytes at a time : - * it doesn't know input length, and relies on end-of-block properties */ - LZ4_memcpy(op, ip, 8); - if (length > 8) { LZ4_memcpy(op+8, ip+8, 8); } - } + DEBUGLOG(7, "copy %u bytes in a 16-bytes stripe", (unsigned)length); + /* We don't need to check oend, since we check it once for each loop below */ + if (ip > iend-(16 + 1/*max lit + offset + nextToken*/)) { goto safe_literal_copy; } + /* Literals can only be <= 14, but hope compilers optimize better when copy by a register size */ + LZ4_memcpy(op, ip, 16); ip += length; op = cpy; } /* get offset */ offset = LZ4_readLE16(ip); ip+=2; match = op - offset; - assert(match <= op); + assert(match <= op); /* overflow check */ /* get matchlength */ length = token & ML_MASK; if (length == ML_MASK) { - variable_length_error error = ok; - if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ - length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); - if (error != ok) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; length += MINMATCH; + if (unlikely((uptrval)(op)+length<(uptrval)op)) { goto _output_error; } /* overflow detection */ + if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ if (op + length >= oend - FASTLOOP_SAFE_DISTANCE) { goto safe_match_copy; } @@ -1864,7 +2048,7 @@ LZ4_decompress_generic( goto safe_match_copy; } - /* Fastpath check: Avoids a branch in LZ4_wildCopy32 if true */ + /* Fastpath check: skip LZ4_wildCopy32 when true */ if ((dict == withPrefix64k) || (match >= lowPrefix)) { if (offset >= 8) { assert(match >= lowPrefix); @@ -1881,6 +2065,7 @@ LZ4_decompress_generic( if (checkOffset && (unlikely(match + dictSize < lowPrefix))) { goto _output_error; } /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) { DEBUGLOG(7, "partialDecoding: dictionary match, close to dstEnd"); @@ -1891,7 +2076,7 @@ LZ4_decompress_generic( if (length <= (size_t)(lowPrefix-match)) { /* match fits entirely within external dictionary : just copy */ - memmove(op, dictEnd - (lowPrefix-match), length); + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); op += length; } else { /* match stretches into both external dictionary and current block */ @@ -1927,11 +2112,10 @@ LZ4_decompress_generic( /* Main Loop : decode remaining sequences where output < FASTLOOP_SAFE_DISTANCE */ while (1) { + assert(ip < iend); token = *ip++; length = token >> ML_BITS; /* literal length */ - assert(!endOnInput || ip <= iend); /* ip < iend before the increment */ - /* A two-stage shortcut for the most common case: * 1) If the literal length is 0..14, and there is enough space, * enter the shortcut and copy 16 bytes on behalf of the literals @@ -1941,11 +2125,11 @@ LZ4_decompress_generic( * those 18 bytes earlier, upon entering the shortcut (in other words, * there is a combined check for both stages). */ - if ( (endOnInput ? length != RUN_MASK : length <= 8) + if ( (length != RUN_MASK) /* strictly "less than" on input, to re-enter the loop with at least one byte */ - && likely((endOnInput ? ip < shortiend : 1) & (op <= shortoend)) ) { + && likely((ip < shortiend) & (op <= shortoend)) ) { /* Copy the literals */ - LZ4_memcpy(op, ip, endOnInput ? 16 : 8); + LZ4_memcpy(op, ip, 16); op += length; ip += length; /* The second stage: prepare for match copying, decode full info. @@ -1975,11 +2159,11 @@ LZ4_decompress_generic( /* decode literal length */ if (length == RUN_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend-RUN_MASK, (int)endOnInput, (int)endOnInput, &error); - if (error == initial_error) { goto _output_error; } - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ - if ((safeDecode) && unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ + size_t const addl = read_variable_length(&ip, iend-RUN_MASK, 1); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)(op))) { goto _output_error; } /* overflow detection */ + if (unlikely((uptrval)(ip)+length<(uptrval)(ip))) { goto _output_error; } /* overflow detection */ } /* copy literals */ @@ -1988,9 +2172,7 @@ LZ4_decompress_generic( safe_literal_copy: #endif LZ4_STATIC_ASSERT(MFLIMIT >= WILDCOPYLENGTH); - if ( ((endOnInput) && ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) ) - || ((!endOnInput) && (cpy>oend-WILDCOPYLENGTH)) ) - { + if ((cpy>oend-MFLIMIT) || (ip+length>iend-(2+1+LASTLITERALS))) { /* We've either hit the input parsing restriction or the output parsing restriction. * In the normal scenario, decoding a full block, it must be the last sequence, * otherwise it's an error (invalid input or dimensions). @@ -2000,7 +2182,6 @@ LZ4_decompress_generic( /* Since we are partial decoding we may be in this block because of the output parsing * restriction, which is not valid since the output buffer is allowed to be undersized. */ - assert(endOnInput); DEBUGLOG(7, "partialDecoding: copying literals, close to input or output end") DEBUGLOG(7, "partialDecoding: literal length = %u", (unsigned)length); DEBUGLOG(7, "partialDecoding: remaining space in dstBuffer : %i", (int)(oend - op)); @@ -2021,21 +2202,17 @@ LZ4_decompress_generic( length = (size_t)(oend-op); } } else { - /* We must be on the last sequence because of the parsing limitations so check - * that we exactly regenerate the original size (must be exact when !endOnInput). - */ - if ((!endOnInput) && (cpy != oend)) { goto _output_error; } /* We must be on the last sequence (or invalid) because of the parsing limitations * so check that we exactly consume the input and don't overrun the output buffer. */ - if ((endOnInput) && ((ip+length != iend) || (cpy > oend))) { + if ((ip+length != iend) || (cpy > oend)) { DEBUGLOG(6, "should have been last run of literals") DEBUGLOG(6, "ip(%p) + length(%i) = %p != iend (%p)", ip, (int)length, ip+length, iend); DEBUGLOG(6, "or cpy(%p) > oend(%p)", cpy, oend); goto _output_error; } } - memmove(op, ip, length); /* supports overlapping memory regions; only matters for in-place decompression scenarios */ + LZ4_memmove(op, ip, length); /* supports overlapping memory regions, for in-place decompression scenarios */ ip += length; op += length; /* Necessarily EOF when !partialDecoding. @@ -2047,7 +2224,7 @@ LZ4_decompress_generic( break; } } else { - LZ4_wildCopy8(op, ip, cpy); /* may overwrite up to WILDCOPYLENGTH beyond cpy */ + LZ4_wildCopy8(op, ip, cpy); /* can overwrite up to 8 bytes beyond cpy */ ip += length; op = cpy; } @@ -2060,10 +2237,10 @@ LZ4_decompress_generic( _copy_match: if (length == ML_MASK) { - variable_length_error error = ok; - length += read_variable_length(&ip, iend - LASTLITERALS + 1, (int)endOnInput, 0, &error); - if (error != ok) goto _output_error; - if ((safeDecode) && unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ + size_t const addl = read_variable_length(&ip, iend - LASTLITERALS + 1, 0); + if (addl == rvl_error) { goto _output_error; } + length += addl; + if (unlikely((uptrval)(op)+length<(uptrval)op)) goto _output_error; /* overflow detection */ } length += MINMATCH; @@ -2073,6 +2250,7 @@ LZ4_decompress_generic( if ((checkOffset) && (unlikely(match + dictSize < lowPrefix))) goto _output_error; /* Error : offset outside buffers */ /* match starting within external dictionary */ if ((dict==usingExtDict) && (match < lowPrefix)) { + assert(dictEnd != NULL); if (unlikely(op+length > oend-LASTLITERALS)) { if (partialDecoding) length = MIN(length, (size_t)(oend-op)); else goto _output_error; /* doesn't respect parsing restriction */ @@ -2080,7 +2258,7 @@ LZ4_decompress_generic( if (length <= (size_t)(lowPrefix-match)) { /* match fits entirely within external dictionary : just copy */ - memmove(op, dictEnd - (lowPrefix-match), length); + LZ4_memmove(op, dictEnd - (lowPrefix-match), length); op += length; } else { /* match stretches into both external dictionary and current block */ @@ -2151,12 +2329,8 @@ LZ4_decompress_generic( } /* end of decoding */ - if (endOnInput) { - DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); - return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ - } else { - return (int) (((const char*)ip)-src); /* Nb of input bytes read */ - } + DEBUGLOG(5, "decoded %i bytes", (int) (((char*)op)-dst)); + return (int) (((char*)op)-dst); /* Nb of output bytes decoded */ /* Overflow error detected */ _output_error: @@ -2171,7 +2345,7 @@ LZ4_FORCE_O2 int LZ4_decompress_safe(const char* source, char* dest, int compressedSize, int maxDecompressedSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxDecompressedSize, - endOnInputSize, decode_full_block, noDict, + decode_full_block, noDict, (BYTE*)dest, NULL, 0); } @@ -2180,16 +2354,17 @@ int LZ4_decompress_safe_partial(const char* src, char* dst, int compressedSize, { dstCapacity = MIN(targetOutputSize, dstCapacity); return LZ4_decompress_generic(src, dst, compressedSize, dstCapacity, - endOnInputSize, partial_decode, + partial_decode, noDict, (BYTE*)dst, NULL, 0); } LZ4_FORCE_O2 int LZ4_decompress_fast(const char* source, char* dest, int originalSize) { - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, withPrefix64k, - (BYTE*)dest - 64 KB, NULL, 0); + DEBUGLOG(5, "LZ4_decompress_fast"); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, NULL, 0); } /*===== Instantiate a few more decoding cases, used more than once. =====*/ @@ -2198,16 +2373,25 @@ LZ4_FORCE_O2 /* Exported, an obsolete API function. */ int LZ4_decompress_safe_withPrefix64k(const char* source, char* dest, int compressedSize, int maxOutputSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, withPrefix64k, + decode_full_block, withPrefix64k, + (BYTE*)dest - 64 KB, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withPrefix64k(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, withPrefix64k, (BYTE*)dest - 64 KB, NULL, 0); } /* Another obsolete API function, paired with the previous one. */ int LZ4_decompress_fast_withPrefix64k(const char* source, char* dest, int originalSize) { - /* LZ4_decompress_fast doesn't validate match offsets, - * and thus serves well with any prefixed dictionary. */ - return LZ4_decompress_fast(source, dest, originalSize); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 64 KB, NULL, 0); } LZ4_FORCE_O2 @@ -2215,7 +2399,17 @@ static int LZ4_decompress_safe_withSmallPrefix(const char* source, char* dest, i size_t prefixSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, noDict, + decode_full_block, noDict, + (BYTE*)dest-prefixSize, NULL, 0); +} + +LZ4_FORCE_O2 +static int LZ4_decompress_safe_partial_withSmallPrefix(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, + size_t prefixSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, noDict, (BYTE*)dest-prefixSize, NULL, 0); } @@ -2225,7 +2419,18 @@ int LZ4_decompress_safe_forceExtDict(const char* source, char* dest, const void* dictStart, size_t dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, usingExtDict, + decode_full_block, usingExtDict, + (BYTE*)dest, (const BYTE*)dictStart, dictSize); +} + +LZ4_FORCE_O2 +int LZ4_decompress_safe_partial_forceExtDict(const char* source, char* dest, + int compressedSize, int targetOutputSize, int dstCapacity, + const void* dictStart, size_t dictSize) +{ + dstCapacity = MIN(targetOutputSize, dstCapacity); + return LZ4_decompress_generic(source, dest, compressedSize, dstCapacity, + partial_decode, usingExtDict, (BYTE*)dest, (const BYTE*)dictStart, dictSize); } @@ -2233,9 +2438,9 @@ LZ4_FORCE_O2 static int LZ4_decompress_fast_extDict(const char* source, char* dest, int originalSize, const void* dictStart, size_t dictSize) { - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, usingExtDict, - (BYTE*)dest, (const BYTE*)dictStart, dictSize); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + 0, (const BYTE*)dictStart, dictSize); } /* The "double dictionary" mode, for use with e.g. ring buffers: the first part @@ -2247,26 +2452,17 @@ int LZ4_decompress_safe_doubleDict(const char* source, char* dest, int compresse size_t prefixSize, const void* dictStart, size_t dictSize) { return LZ4_decompress_generic(source, dest, compressedSize, maxOutputSize, - endOnInputSize, decode_full_block, usingExtDict, - (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); -} - -LZ4_FORCE_INLINE -int LZ4_decompress_fast_doubleDict(const char* source, char* dest, int originalSize, - size_t prefixSize, const void* dictStart, size_t dictSize) -{ - return LZ4_decompress_generic(source, dest, 0, originalSize, - endOnOutputSize, decode_full_block, usingExtDict, + decode_full_block, usingExtDict, (BYTE*)dest-prefixSize, (const BYTE*)dictStart, dictSize); } /*===== streaming decompression functions =====*/ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_streamDecode_t* LZ4_createStreamDecode(void) { - LZ4_streamDecode_t* lz4s = (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); - LZ4_STATIC_ASSERT(LZ4_STREAMDECODESIZE >= sizeof(LZ4_streamDecode_t_internal)); /* A compilation error here means LZ4_STREAMDECODESIZE is not large enough */ - return lz4s; + LZ4_STATIC_ASSERT(sizeof(LZ4_streamDecode_t) >= sizeof(LZ4_streamDecode_t_internal)); + return (LZ4_streamDecode_t*) ALLOC_AND_ZERO(sizeof(LZ4_streamDecode_t)); } int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) @@ -2275,6 +2471,7 @@ int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) FREEMEM(LZ4_stream); return 0; } +#endif /*! LZ4_setStreamDecode() : * Use this function to instruct where to find the dictionary. @@ -2285,8 +2482,13 @@ int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream) int LZ4_setStreamDecode (LZ4_streamDecode_t* LZ4_streamDecode, const char* dictionary, int dictSize) { LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; - lz4sd->prefixSize = (size_t) dictSize; - lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + lz4sd->prefixSize = (size_t)dictSize; + if (dictSize) { + assert(dictionary != NULL); + lz4sd->prefixEnd = (const BYTE*) dictionary + dictSize; + } else { + lz4sd->prefixEnd = (const BYTE*) dictionary; + } lz4sd->externalDict = NULL; lz4sd->extDictSize = 0; return 1; @@ -2358,29 +2560,35 @@ int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const ch return result; } -LZ4_FORCE_O2 -int LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* source, char* dest, int originalSize) +LZ4_FORCE_O2 int +LZ4_decompress_fast_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* source, char* dest, int originalSize) { - LZ4_streamDecode_t_internal* lz4sd = &LZ4_streamDecode->internal_donotuse; + LZ4_streamDecode_t_internal* const lz4sd = + (assert(LZ4_streamDecode!=NULL), &LZ4_streamDecode->internal_donotuse); int result; + + DEBUGLOG(5, "LZ4_decompress_fast_continue (toDecodeSize=%i)", originalSize); assert(originalSize >= 0); if (lz4sd->prefixSize == 0) { + DEBUGLOG(5, "first invocation : no prefix nor extDict"); assert(lz4sd->extDictSize == 0); result = LZ4_decompress_fast(source, dest, originalSize); if (result <= 0) return result; lz4sd->prefixSize = (size_t)originalSize; lz4sd->prefixEnd = (BYTE*)dest + originalSize; } else if (lz4sd->prefixEnd == (BYTE*)dest) { - if (lz4sd->prefixSize >= 64 KB - 1 || lz4sd->extDictSize == 0) - result = LZ4_decompress_fast(source, dest, originalSize); - else - result = LZ4_decompress_fast_doubleDict(source, dest, originalSize, - lz4sd->prefixSize, lz4sd->externalDict, lz4sd->extDictSize); + DEBUGLOG(5, "continue using existing prefix"); + result = LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + lz4sd->prefixSize, + lz4sd->externalDict, lz4sd->extDictSize); if (result <= 0) return result; lz4sd->prefixSize += (size_t)originalSize; lz4sd->prefixEnd += originalSize; } else { + DEBUGLOG(5, "prefix becomes extDict"); lz4sd->extDictSize = lz4sd->prefixSize; lz4sd->externalDict = lz4sd->prefixEnd - lz4sd->extDictSize; result = LZ4_decompress_fast_extDict(source, dest, originalSize, @@ -2416,10 +2624,27 @@ int LZ4_decompress_safe_usingDict(const char* source, char* dest, int compressed return LZ4_decompress_safe_forceExtDict(source, dest, compressedSize, maxOutputSize, dictStart, (size_t)dictSize); } +int LZ4_decompress_safe_partial_usingDict(const char* source, char* dest, int compressedSize, int targetOutputSize, int dstCapacity, const char* dictStart, int dictSize) +{ + if (dictSize==0) + return LZ4_decompress_safe_partial(source, dest, compressedSize, targetOutputSize, dstCapacity); + if (dictStart+dictSize == dest) { + if (dictSize >= 64 KB - 1) { + return LZ4_decompress_safe_partial_withPrefix64k(source, dest, compressedSize, targetOutputSize, dstCapacity); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_withSmallPrefix(source, dest, compressedSize, targetOutputSize, dstCapacity, (size_t)dictSize); + } + assert(dictSize >= 0); + return LZ4_decompress_safe_partial_forceExtDict(source, dest, compressedSize, targetOutputSize, dstCapacity, dictStart, (size_t)dictSize); +} + int LZ4_decompress_fast_usingDict(const char* source, char* dest, int originalSize, const char* dictStart, int dictSize) { if (dictSize==0 || dictStart+dictSize == dest) - return LZ4_decompress_fast(source, dest, originalSize); + return LZ4_decompress_unsafe_generic( + (const BYTE*)source, (BYTE*)dest, originalSize, + (size_t)dictSize, NULL, 0); assert(dictSize >= 0); return LZ4_decompress_fast_extDict(source, dest, originalSize, dictStart, (size_t)dictSize); } @@ -2471,7 +2696,7 @@ int LZ4_uncompress_unknownOutputSize (const char* source, char* dest, int isize, /* Obsolete Streaming functions */ -int LZ4_sizeofStreamState(void) { return LZ4_STREAMSIZE; } +int LZ4_sizeofStreamState(void) { return sizeof(LZ4_stream_t); } int LZ4_resetStreamState(void* state, char* inputBuffer) { @@ -2480,11 +2705,13 @@ int LZ4_resetStreamState(void* state, char* inputBuffer) return 0; } +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) void* LZ4_create (char* inputBuffer) { (void)inputBuffer; return LZ4_createStream(); } +#endif char* LZ4_slideInputBuffer (void* state) { diff --git a/mfbt/lz4/lz4.h b/mfbt/lz4/lz4.h index 7ab1e483a9f53..491c6087c4177 100644 --- a/mfbt/lz4/lz4.h +++ b/mfbt/lz4/lz4.h @@ -1,7 +1,7 @@ /* * LZ4 - Fast LZ compression algorithm * Header File - * Copyright (C) 2011-present, Yann Collet. + * Copyright (C) 2011-2020, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -97,36 +97,77 @@ extern "C" { # define LZ4LIB_API LZ4LIB_VISIBILITY #endif +/*! LZ4_FREESTANDING : + * When this macro is set to 1, it enables "freestanding mode" that is + * suitable for typical freestanding environment which doesn't support + * standard C library. + * + * - LZ4_FREESTANDING is a compile-time switch. + * - It requires the following macros to be defined: + * LZ4_memcpy, LZ4_memmove, LZ4_memset. + * - It only enables LZ4/HC functions which don't use heap. + * All LZ4F_* functions are not supported. + * - See tests/freestanding.c to check its basic setup. + */ +#if defined(LZ4_FREESTANDING) && (LZ4_FREESTANDING == 1) +# define LZ4_HEAPMODE 0 +# define LZ4HC_HEAPMODE 0 +# define LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION 1 +# if !defined(LZ4_memcpy) +# error "LZ4_FREESTANDING requires macro 'LZ4_memcpy'." +# endif +# if !defined(LZ4_memset) +# error "LZ4_FREESTANDING requires macro 'LZ4_memset'." +# endif +# if !defined(LZ4_memmove) +# error "LZ4_FREESTANDING requires macro 'LZ4_memmove'." +# endif +#elif ! defined(LZ4_FREESTANDING) +# define LZ4_FREESTANDING 0 +#endif + + /*------ Version ------*/ #define LZ4_VERSION_MAJOR 1 /* for breaking interface changes */ #define LZ4_VERSION_MINOR 9 /* for new (non-breaking) interface capabilities */ -#define LZ4_VERSION_RELEASE 3 /* for tweaks, bug-fixes, or development */ +#define LZ4_VERSION_RELEASE 4 /* for tweaks, bug-fixes, or development */ #define LZ4_VERSION_NUMBER (LZ4_VERSION_MAJOR *100*100 + LZ4_VERSION_MINOR *100 + LZ4_VERSION_RELEASE) #define LZ4_LIB_VERSION LZ4_VERSION_MAJOR.LZ4_VERSION_MINOR.LZ4_VERSION_RELEASE #define LZ4_QUOTE(str) #str #define LZ4_EXPAND_AND_QUOTE(str) LZ4_QUOTE(str) -#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) +#define LZ4_VERSION_STRING LZ4_EXPAND_AND_QUOTE(LZ4_LIB_VERSION) /* requires v1.7.3+ */ -LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version */ -LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version */ +LZ4LIB_API int LZ4_versionNumber (void); /**< library version number; useful to check dll version; requires v1.3.0+ */ +LZ4LIB_API const char* LZ4_versionString (void); /**< library version string; useful to check dll version; requires v1.7.5+ */ /*-************************************ * Tuning parameter **************************************/ +#define LZ4_MEMORY_USAGE_MIN 10 +#define LZ4_MEMORY_USAGE_DEFAULT 14 +#define LZ4_MEMORY_USAGE_MAX 20 + /*! * LZ4_MEMORY_USAGE : - * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; etc.) - * Increasing memory usage improves compression ratio. - * Reduced memory usage may improve speed, thanks to better cache locality. + * Memory usage formula : N->2^N Bytes (examples : 10 -> 1KB; 12 -> 4KB ; 16 -> 64KB; 20 -> 1MB; ) + * Increasing memory usage improves compression ratio, at the cost of speed. + * Reduced memory usage may improve speed at the cost of ratio, thanks to better cache locality. * Default value is 14, for 16KB, which nicely fits into Intel x86 L1 cache */ #ifndef LZ4_MEMORY_USAGE -# define LZ4_MEMORY_USAGE 14 +# define LZ4_MEMORY_USAGE LZ4_MEMORY_USAGE_DEFAULT #endif +#if (LZ4_MEMORY_USAGE < LZ4_MEMORY_USAGE_MIN) +# error "LZ4_MEMORY_USAGE is too small !" +#endif + +#if (LZ4_MEMORY_USAGE > LZ4_MEMORY_USAGE_MAX) +# error "LZ4_MEMORY_USAGE is too large !" +#endif /*-************************************ * Simple Functions @@ -270,8 +311,25 @@ LZ4LIB_API int LZ4_decompress_safe_partial (const char* src, char* dst, int srcS ***********************************************/ typedef union LZ4_stream_u LZ4_stream_t; /* incomplete type (defined later) */ +/** + Note about RC_INVOKED + + - RC_INVOKED is predefined symbol of rc.exe (the resource compiler which is part of MSVC/Visual Studio). + https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros + + - Since rc.exe is a legacy compiler, it truncates long symbol (> 30 chars) + and reports warning "RC4011: identifier truncated". + + - To eliminate the warning, we surround long preprocessor symbol with + "#if !defined(RC_INVOKED) ... #endif" block that means + "skip this block when rc.exe is trying to read it". +*/ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4LIB_API LZ4_stream_t* LZ4_createStream(void); LZ4LIB_API int LZ4_freeStream (LZ4_stream_t* streamPtr); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif /*! LZ4_resetStream_fast() : v1.9.0+ * Use this to prepare an LZ4_stream_t for a new chain of dependent blocks @@ -355,8 +413,12 @@ typedef union LZ4_streamDecode_u LZ4_streamDecode_t; /* tracking context */ * creation / destruction of streaming decompression tracking context. * A tracking context can be re-used multiple times. */ +#if !defined(RC_INVOKED) /* https://docs.microsoft.com/en-us/windows/win32/menurc/predefined-macros */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4LIB_API LZ4_streamDecode_t* LZ4_createStreamDecode(void); LZ4LIB_API int LZ4_freeStreamDecode (LZ4_streamDecode_t* LZ4_stream); +#endif /* !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) */ +#endif /*! LZ4_setStreamDecode() : * An LZ4_streamDecode_t context can be allocated once and re-used multiple times. @@ -406,7 +468,10 @@ LZ4LIB_API int LZ4_decoderRingBufferSize(int maxBlockSize); * save the last 64KB of decoded data into a safe buffer where it can't be modified during decompression, * then indicate where this data is saved using LZ4_setStreamDecode(), before decompressing next block. */ -LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, const char* src, char* dst, int srcSize, int dstCapacity); +LZ4LIB_API int +LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecode, + const char* src, char* dst, + int srcSize, int dstCapacity); /*! LZ4_decompress_*_usingDict() : @@ -417,7 +482,16 @@ LZ4LIB_API int LZ4_decompress_safe_continue (LZ4_streamDecode_t* LZ4_streamDecod * Performance tip : Decompression speed can be substantially increased * when dst == dictStart + dictSize. */ -LZ4LIB_API int LZ4_decompress_safe_usingDict (const char* src, char* dst, int srcSize, int dstCapcity, const char* dictStart, int dictSize); +LZ4LIB_API int +LZ4_decompress_safe_usingDict(const char* src, char* dst, + int srcSize, int dstCapacity, + const char* dictStart, int dictSize); + +LZ4LIB_API int +LZ4_decompress_safe_partial_usingDict(const char* src, char* dst, + int compressedSize, + int targetOutputSize, int maxOutputSize, + const char* dictStart, int dictSize); #endif /* LZ4_H_2983827168210 */ @@ -496,13 +570,15 @@ LZ4LIB_STATIC_API int LZ4_compress_fast_extState_fastReset (void* state, const c * stream (and source buffer) must remain in-place / accessible / unchanged * through the completion of the first compression call on the stream. */ -LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const LZ4_stream_t* dictionaryStream); +LZ4LIB_STATIC_API void +LZ4_attach_dictionary(LZ4_stream_t* workingStream, + const LZ4_stream_t* dictionaryStream); /*! In-place compression and decompression * * It's possible to have input and output sharing the same buffer, - * for highly contrained memory environments. + * for highly constrained memory environments. * In both cases, it requires input to lay at the end of the buffer, * and decompression to start at beginning of the buffer. * Buffer size must feature some margin, hence be larger than final size. @@ -592,38 +668,26 @@ LZ4LIB_STATIC_API void LZ4_attach_dictionary(LZ4_stream_t* workingStream, const typedef unsigned int LZ4_u32; #endif +/*! LZ4_stream_t : + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_stream_t object. +**/ + typedef struct LZ4_stream_t_internal LZ4_stream_t_internal; struct LZ4_stream_t_internal { LZ4_u32 hashTable[LZ4_HASH_SIZE_U32]; - LZ4_u32 currentOffset; - LZ4_u32 tableType; const LZ4_byte* dictionary; const LZ4_stream_t_internal* dictCtx; + LZ4_u32 currentOffset; + LZ4_u32 tableType; LZ4_u32 dictSize; + /* Implicit padding to ensure structure is aligned */ }; -typedef struct { - const LZ4_byte* externalDict; - size_t extDictSize; - const LZ4_byte* prefixEnd; - size_t prefixSize; -} LZ4_streamDecode_t_internal; - - -/*! LZ4_stream_t : - * Do not use below internal definitions directly ! - * Declare or allocate an LZ4_stream_t instead. - * LZ4_stream_t can also be created using LZ4_createStream(), which is recommended. - * The structure definition can be convenient for static allocation - * (on stack, or as part of larger structure). - * Init this structure with LZ4_initStream() before first use. - * note : only use this definition in association with static linking ! - * this definition is not API/ABI safe, and may change in future versions. - */ -#define LZ4_STREAMSIZE 16416 /* static size, for inter-version compatibility */ -#define LZ4_STREAMSIZE_VOIDP (LZ4_STREAMSIZE / sizeof(void*)) +#define LZ4_STREAM_MINSIZE ((1UL << LZ4_MEMORY_USAGE) + 32) /* static size, for inter-version compatibility */ union LZ4_stream_u { - void* table[LZ4_STREAMSIZE_VOIDP]; + char minStateSize[LZ4_STREAM_MINSIZE]; LZ4_stream_t_internal internal_donotuse; }; /* previously typedef'd to LZ4_stream_t */ @@ -641,21 +705,25 @@ union LZ4_stream_u { * In which case, the function will @return NULL. * Note2: An LZ4_stream_t structure guarantees correct alignment and size. * Note3: Before v1.9.0, use LZ4_resetStream() instead - */ +**/ LZ4LIB_API LZ4_stream_t* LZ4_initStream (void* buffer, size_t size); /*! LZ4_streamDecode_t : - * information structure to track an LZ4 stream during decompression. - * init this structure using LZ4_setStreamDecode() before first use. - * note : only use in association with static linking ! - * this definition is not API/ABI safe, - * and may change in a future version ! - */ -#define LZ4_STREAMDECODESIZE_U64 (4 + ((sizeof(void*)==16) ? 2 : 0) /*AS-400*/ ) -#define LZ4_STREAMDECODESIZE (LZ4_STREAMDECODESIZE_U64 * sizeof(unsigned long long)) + * Never ever use below internal definitions directly ! + * These definitions are not API/ABI safe, and may change in future versions. + * If you need static allocation, declare or allocate an LZ4_streamDecode_t object. +**/ +typedef struct { + const LZ4_byte* externalDict; + const LZ4_byte* prefixEnd; + size_t extDictSize; + size_t prefixSize; +} LZ4_streamDecode_t_internal; + +#define LZ4_STREAMDECODE_MINSIZE 32 union LZ4_streamDecode_u { - unsigned long long table[LZ4_STREAMDECODESIZE_U64]; + char minStateSize[LZ4_STREAMDECODE_MINSIZE]; LZ4_streamDecode_t_internal internal_donotuse; } ; /* previously typedef'd to LZ4_streamDecode_t */ diff --git a/mfbt/lz4/lz4file.c b/mfbt/lz4/lz4file.c new file mode 100644 index 0000000000000..eaf9b1704d2bb --- /dev/null +++ b/mfbt/lz4/lz4file.c @@ -0,0 +1,311 @@ +/* + * LZ4 file library + * Copyright (C) 2022, Xiaomi Inc. + * + * BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * - Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * - Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * You can contact the author at : + * - LZ4 homepage : http://www.lz4.org + * - LZ4 source repository : https://github.com/lz4/lz4 + */ +#include +#include +#include "lz4.h" +#include "lz4file.h" + +struct LZ4_readFile_s { + LZ4F_dctx* dctxPtr; + FILE* fp; + LZ4_byte* srcBuf; + size_t srcBufNext; + size_t srcBufSize; + size_t srcBufMaxSize; +}; + +struct LZ4_writeFile_s { + LZ4F_cctx* cctxPtr; + FILE* fp; + LZ4_byte* dstBuf; + size_t maxWriteSize; + size_t dstBufMaxSize; + LZ4F_errorCode_t errCode; +}; + +LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp) +{ + char buf[LZ4F_HEADER_SIZE_MAX]; + size_t consumedSize; + LZ4F_errorCode_t ret; + LZ4F_frameInfo_t info; + + if (fp == NULL || lz4fRead == NULL) { + return -LZ4F_ERROR_GENERIC; + } + + *lz4fRead = (LZ4_readFile_t*)calloc(1, sizeof(LZ4_readFile_t)); + if (*lz4fRead == NULL) { + return -LZ4F_ERROR_allocation_failed; + } + + ret = LZ4F_createDecompressionContext(&(*lz4fRead)->dctxPtr, LZ4F_getVersion()); + if (LZ4F_isError(ret)) { + free(*lz4fRead); + return ret; + } + + (*lz4fRead)->fp = fp; + consumedSize = fread(buf, 1, sizeof(buf), (*lz4fRead)->fp); + if (consumedSize != sizeof(buf)) { + free(*lz4fRead); + return -LZ4F_ERROR_GENERIC; + } + + ret = LZ4F_getFrameInfo((*lz4fRead)->dctxPtr, &info, buf, &consumedSize); + if (LZ4F_isError(ret)) { + LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr); + free(*lz4fRead); + return ret; + } + + switch (info.blockSizeID) { + case LZ4F_default : + case LZ4F_max64KB : + (*lz4fRead)->srcBufMaxSize = 64 * 1024; + break; + case LZ4F_max256KB: + (*lz4fRead)->srcBufMaxSize = 256 * 1024; + break; + case LZ4F_max1MB: + (*lz4fRead)->srcBufMaxSize = 1 * 1024 * 1024; + break; + case LZ4F_max4MB: + (*lz4fRead)->srcBufMaxSize = 4 * 1024 * 1024; + break; + default: + LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr); + free(*lz4fRead); + return -LZ4F_ERROR_maxBlockSize_invalid; + } + + (*lz4fRead)->srcBuf = (LZ4_byte*)malloc((*lz4fRead)->srcBufMaxSize); + if ((*lz4fRead)->srcBuf == NULL) { + LZ4F_freeDecompressionContext((*lz4fRead)->dctxPtr); + free(lz4fRead); + return -LZ4F_ERROR_allocation_failed; + } + + (*lz4fRead)->srcBufSize = sizeof(buf) - consumedSize; + memcpy((*lz4fRead)->srcBuf, buf + consumedSize, (*lz4fRead)->srcBufSize); + + return ret; +} + +size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size) +{ + LZ4_byte* p = (LZ4_byte*)buf; + size_t next = 0; + + if (lz4fRead == NULL || buf == NULL) + return -LZ4F_ERROR_GENERIC; + + while (next < size) { + size_t srcsize = lz4fRead->srcBufSize - lz4fRead->srcBufNext; + size_t dstsize = size - next; + size_t ret; + + if (srcsize == 0) { + ret = fread(lz4fRead->srcBuf, 1, lz4fRead->srcBufMaxSize, lz4fRead->fp); + if (ret > 0) { + lz4fRead->srcBufSize = ret; + srcsize = lz4fRead->srcBufSize; + lz4fRead->srcBufNext = 0; + } + else if (ret == 0) { + break; + } + else { + return -LZ4F_ERROR_GENERIC; + } + } + + ret = LZ4F_decompress(lz4fRead->dctxPtr, + p, &dstsize, + lz4fRead->srcBuf + lz4fRead->srcBufNext, + &srcsize, + NULL); + if (LZ4F_isError(ret)) { + return ret; + } + + lz4fRead->srcBufNext += srcsize; + next += dstsize; + p += dstsize; + } + + return next; +} + +LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead) +{ + if (lz4fRead == NULL) + return -LZ4F_ERROR_GENERIC; + LZ4F_freeDecompressionContext(lz4fRead->dctxPtr); + free(lz4fRead->srcBuf); + free(lz4fRead); + return LZ4F_OK_NoError; +} + +LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr) +{ + LZ4_byte buf[LZ4F_HEADER_SIZE_MAX]; + size_t ret; + + if (fp == NULL || lz4fWrite == NULL) + return -LZ4F_ERROR_GENERIC; + + *lz4fWrite = (LZ4_writeFile_t*)malloc(sizeof(LZ4_writeFile_t)); + if (*lz4fWrite == NULL) { + return -LZ4F_ERROR_allocation_failed; + } + if (prefsPtr != NULL) { + switch (prefsPtr->frameInfo.blockSizeID) { + case LZ4F_default : + case LZ4F_max64KB : + (*lz4fWrite)->maxWriteSize = 64 * 1024; + break; + case LZ4F_max256KB: + (*lz4fWrite)->maxWriteSize = 256 * 1024; + break; + case LZ4F_max1MB: + (*lz4fWrite)->maxWriteSize = 1 * 1024 * 1024; + break; + case LZ4F_max4MB: + (*lz4fWrite)->maxWriteSize = 4 * 1024 * 1024; + break; + default: + free(lz4fWrite); + return -LZ4F_ERROR_maxBlockSize_invalid; + } + } else { + (*lz4fWrite)->maxWriteSize = 64 * 1024; + } + + (*lz4fWrite)->dstBufMaxSize = LZ4F_compressBound((*lz4fWrite)->maxWriteSize, prefsPtr); + (*lz4fWrite)->dstBuf = (LZ4_byte*)malloc((*lz4fWrite)->dstBufMaxSize); + if ((*lz4fWrite)->dstBuf == NULL) { + free(*lz4fWrite); + return -LZ4F_ERROR_allocation_failed; + } + + ret = LZ4F_createCompressionContext(&(*lz4fWrite)->cctxPtr, LZ4F_getVersion()); + if (LZ4F_isError(ret)) { + free((*lz4fWrite)->dstBuf); + free(*lz4fWrite); + return ret; + } + + ret = LZ4F_compressBegin((*lz4fWrite)->cctxPtr, buf, LZ4F_HEADER_SIZE_MAX, prefsPtr); + if (LZ4F_isError(ret)) { + LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr); + free((*lz4fWrite)->dstBuf); + free(*lz4fWrite); + return ret; + } + + if (ret != fwrite(buf, 1, ret, fp)) { + LZ4F_freeCompressionContext((*lz4fWrite)->cctxPtr); + free((*lz4fWrite)->dstBuf); + free(*lz4fWrite); + return -LZ4F_ERROR_GENERIC; + } + + (*lz4fWrite)->fp = fp; + (*lz4fWrite)->errCode = LZ4F_OK_NoError; + return LZ4F_OK_NoError; +} + +size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, void* buf, size_t size) +{ + LZ4_byte* p = (LZ4_byte*)buf; + size_t remain = size; + size_t chunk; + size_t ret; + + if (lz4fWrite == NULL || buf == NULL) + return -LZ4F_ERROR_GENERIC; + while (remain) { + if (remain > lz4fWrite->maxWriteSize) + chunk = lz4fWrite->maxWriteSize; + else + chunk = remain; + + ret = LZ4F_compressUpdate(lz4fWrite->cctxPtr, + lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize, + p, chunk, + NULL); + if (LZ4F_isError(ret)) { + lz4fWrite->errCode = ret; + return ret; + } + + if(ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) { + lz4fWrite->errCode = -LZ4F_ERROR_GENERIC; + return -LZ4F_ERROR_GENERIC; + } + + p += chunk; + remain -= chunk; + } + + return size; +} + +LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite) +{ + LZ4F_errorCode_t ret = LZ4F_OK_NoError; + + if (lz4fWrite == NULL) + return -LZ4F_ERROR_GENERIC; + + if (lz4fWrite->errCode == LZ4F_OK_NoError) { + ret = LZ4F_compressEnd(lz4fWrite->cctxPtr, + lz4fWrite->dstBuf, lz4fWrite->dstBufMaxSize, + NULL); + if (LZ4F_isError(ret)) { + goto out; + } + + if (ret != fwrite(lz4fWrite->dstBuf, 1, ret, lz4fWrite->fp)) { + ret = -LZ4F_ERROR_GENERIC; + } + } + +out: + LZ4F_freeCompressionContext(lz4fWrite->cctxPtr); + free(lz4fWrite->dstBuf); + free(lz4fWrite); + return ret; +} diff --git a/mfbt/lz4/lz4file.h b/mfbt/lz4/lz4file.h new file mode 100644 index 0000000000000..5527130720fbe --- /dev/null +++ b/mfbt/lz4/lz4file.h @@ -0,0 +1,93 @@ +/* + LZ4 file library + Header File + Copyright (C) 2022, Xiaomi Inc. + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - LZ4 source repository : https://github.com/lz4/lz4 + - LZ4 public forum : https://groups.google.com/forum/#!forum/lz4c +*/ +#if defined (__cplusplus) +extern "C" { +#endif + +#ifndef LZ4FILE_H +#define LZ4FILE_H + +#include +#include "lz4frame_static.h" + +typedef struct LZ4_readFile_s LZ4_readFile_t; +typedef struct LZ4_writeFile_s LZ4_writeFile_t; + +/*! LZ4F_readOpen() : + * Set read lz4file handle. + * `lz4f` will set a lz4file handle. + * `fp` must be the return value of the lz4 file opened by fopen. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readOpen(LZ4_readFile_t** lz4fRead, FILE* fp); + +/*! LZ4F_read() : + * Read lz4file content to buffer. + * `lz4f` must use LZ4_readOpen to set first. + * `buf` read data buffer. + * `size` read data buffer size. + */ +LZ4FLIB_STATIC_API size_t LZ4F_read(LZ4_readFile_t* lz4fRead, void* buf, size_t size); + +/*! LZ4F_readClose() : + * Close lz4file handle. + * `lz4f` must use LZ4_readOpen to set first. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_readClose(LZ4_readFile_t* lz4fRead); + +/*! LZ4F_writeOpen() : + * Set write lz4file handle. + * `lz4f` will set a lz4file handle. + * `fp` must be the return value of the lz4 file opened by fopen. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeOpen(LZ4_writeFile_t** lz4fWrite, FILE* fp, const LZ4F_preferences_t* prefsPtr); + +/*! LZ4F_write() : + * Write buffer to lz4file. + * `lz4f` must use LZ4F_writeOpen to set first. + * `buf` write data buffer. + * `size` write data buffer size. + */ +LZ4FLIB_STATIC_API size_t LZ4F_write(LZ4_writeFile_t* lz4fWrite, void* buf, size_t size); + +/*! LZ4F_writeClose() : + * Close lz4file handle. + * `lz4f` must use LZ4F_writeOpen to set first. + */ +LZ4FLIB_STATIC_API LZ4F_errorCode_t LZ4F_writeClose(LZ4_writeFile_t* lz4fWrite); + +#endif /* LZ4FILE_H */ + +#if defined (__cplusplus) +} +#endif diff --git a/mfbt/lz4/lz4frame.c b/mfbt/lz4/lz4frame.c index ec02c92f72690..174f9ae4f2179 100644 --- a/mfbt/lz4/lz4frame.c +++ b/mfbt/lz4/lz4frame.c @@ -45,7 +45,7 @@ * Compiler Options **************************************/ #ifdef _MSC_VER /* Visual Studio */ -# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ #endif @@ -62,6 +62,19 @@ #endif +/*-************************************ +* Library declarations +**************************************/ +#define LZ4F_STATIC_LINKING_ONLY +#include "lz4frame.h" +#define LZ4_STATIC_LINKING_ONLY +#include "lz4.h" +#define LZ4_HC_STATIC_LINKING_ONLY +#include "lz4hc.h" +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + /*-************************************ * Memory routines **************************************/ @@ -70,7 +83,13 @@ * malloc(), calloc() and free() * towards another library or solution of their choice * by modifying below section. - */ +**/ + +#include /* memset, memcpy, memmove */ +#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ +# define MEM_INIT(p,v,s) memset((p),(v),(s)) +#endif + #ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ # include /* malloc, calloc, free */ # define ALLOC(s) malloc(s) @@ -78,23 +97,42 @@ # define FREEMEM(p) free(p) #endif -#include /* memset, memcpy, memmove */ -#ifndef LZ4_SRC_INCLUDED /* avoid redefinition when sources are coalesced */ -# define MEM_INIT(p,v,s) memset((p),(v),(s)) -#endif +static void* LZ4F_calloc(size_t s, LZ4F_CustomMem cmem) +{ + /* custom calloc defined : use it */ + if (cmem.customCalloc != NULL) { + return cmem.customCalloc(cmem.opaqueState, s); + } + /* nothing defined : use default 's calloc() */ + if (cmem.customAlloc == NULL) { + return ALLOC_AND_ZERO(s); + } + /* only custom alloc defined : use it, and combine it with memset() */ + { void* const p = cmem.customAlloc(cmem.opaqueState, s); + if (p != NULL) MEM_INIT(p, 0, s); + return p; +} } +static void* LZ4F_malloc(size_t s, LZ4F_CustomMem cmem) +{ + /* custom malloc defined : use it */ + if (cmem.customAlloc != NULL) { + return cmem.customAlloc(cmem.opaqueState, s); + } + /* nothing defined : use default 's malloc() */ + return ALLOC(s); +} -/*-************************************ -* Library declarations -**************************************/ -#define LZ4F_STATIC_LINKING_ONLY -#include "lz4frame.h" -#define LZ4_STATIC_LINKING_ONLY -#include "lz4.h" -#define LZ4_HC_STATIC_LINKING_ONLY -#include "lz4hc.h" -#define XXH_STATIC_LINKING_ONLY -#include "xxhash.h" +static void LZ4F_free(void* p, LZ4F_CustomMem cmem) +{ + /* custom malloc defined : use it */ + if (cmem.customFree != NULL) { + cmem.customFree(cmem.opaqueState, p); + return; + } + /* nothing defined : use default 's free() */ + FREEMEM(p); +} /*-************************************ @@ -143,7 +181,7 @@ static int g_debuglog_enable = 1; #endif -/* unoptimized version; solves endianess & alignment issues */ +/* unoptimized version; solves endianness & alignment issues */ static U32 LZ4F_readLE32 (const void* src) { const BYTE* const srcPtr = (const BYTE*)src; @@ -206,8 +244,6 @@ static void LZ4F_writeLE64 (void* dst, U64 value64) #define _4BITS 0x0F #define _8BITS 0xFF -#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U -#define LZ4F_MAGICNUMBER 0x184D2204U #define LZ4F_BLOCKUNCOMPRESSED_FLAG 0x80000000U #define LZ4F_BLOCKSIZEID_DEFAULT LZ4F_max64KB @@ -220,22 +256,27 @@ static const size_t BFSize = LZ4F_BLOCK_CHECKSUM_SIZE; /* block footer : checks /*-************************************ * Structures and local types **************************************/ + +typedef enum { LZ4B_COMPRESSED, LZ4B_UNCOMPRESSED} LZ4F_blockCompression_t; + typedef struct LZ4F_cctx_s { + LZ4F_CustomMem cmem; LZ4F_preferences_t prefs; U32 version; U32 cStage; const LZ4F_CDict* cdict; size_t maxBlockSize; size_t maxBufferSize; - BYTE* tmpBuff; - BYTE* tmpIn; - size_t tmpInSize; + BYTE* tmpBuff; /* internal buffer, for streaming */ + BYTE* tmpIn; /* starting position of data compress within internal buffer (>= tmpBuff) */ + size_t tmpInSize; /* amount of data to compress after tmpIn */ U64 totalInSize; XXH32_state_t xxh; void* lz4CtxPtr; U16 lz4CtxAlloc; /* sized for: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */ U16 lz4CtxState; /* in use as: 0 = none, 1 = lz4 ctx, 2 = lz4hc ctx */ + LZ4F_blockCompression_t blockCompression; } LZ4F_cctx_t; @@ -264,27 +305,33 @@ LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult) return (LZ4F_errorCodes)(-(ptrdiff_t)functionResult); } -static LZ4F_errorCode_t err0r(LZ4F_errorCodes code) +static LZ4F_errorCode_t LZ4F_returnErrorCode(LZ4F_errorCodes code) { /* A compilation error here means sizeof(ptrdiff_t) is not large enough */ LZ4F_STATIC_ASSERT(sizeof(ptrdiff_t) >= sizeof(size_t)); return (LZ4F_errorCode_t)-(ptrdiff_t)code; } +#define RETURN_ERROR(e) return LZ4F_returnErrorCode(LZ4F_ERROR_ ## e) + +#define RETURN_ERROR_IF(c,e) if (c) RETURN_ERROR(e) + +#define FORWARD_IF_ERROR(r) if (LZ4F_isError(r)) return (r) + unsigned LZ4F_getVersion(void) { return LZ4F_VERSION; } int LZ4F_compressionLevel_max(void) { return LZ4HC_CLEVEL_MAX; } -size_t LZ4F_getBlockSize(unsigned blockSizeID) +size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID) { static const size_t blockSizes[4] = { 64 KB, 256 KB, 1 MB, 4 MB }; if (blockSizeID == 0) blockSizeID = LZ4F_BLOCKSIZEID_DEFAULT; if (blockSizeID < LZ4F_max64KB || blockSizeID > LZ4F_max4MB) - return err0r(LZ4F_ERROR_maxBlockSize_invalid); - blockSizeID -= LZ4F_max64KB; - return blockSizes[blockSizeID]; -} + RETURN_ERROR(maxBlockSize_invalid); + { int const blockSizeIdx = (int)blockSizeID - (int)LZ4F_max64KB; + return blockSizes[blockSizeIdx]; +} } /*-************************************ * Private functions @@ -397,21 +444,20 @@ size_t LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, MEM_INIT(&options, 0, sizeof(options)); options.stableSrc = 1; - if (dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs)) /* condition to guarantee success */ - return err0r(LZ4F_ERROR_dstMaxSize_tooSmall); + RETURN_ERROR_IF(dstCapacity < LZ4F_compressFrameBound(srcSize, &prefs), dstMaxSize_tooSmall); { size_t const headerSize = LZ4F_compressBegin_usingCDict(cctx, dstBuffer, dstCapacity, cdict, &prefs); /* write header */ - if (LZ4F_isError(headerSize)) return headerSize; + FORWARD_IF_ERROR(headerSize); dstPtr += headerSize; /* header size */ } assert(dstEnd >= dstPtr); { size_t const cSize = LZ4F_compressUpdate(cctx, dstPtr, (size_t)(dstEnd-dstPtr), srcBuffer, srcSize, &options); - if (LZ4F_isError(cSize)) return cSize; + FORWARD_IF_ERROR(cSize); dstPtr += cSize; } assert(dstEnd >= dstPtr); { size_t const tailSize = LZ4F_compressEnd(cctx, dstPtr, (size_t)(dstEnd-dstPtr), &options); /* flush last block, and generate suffix */ - if (LZ4F_isError(tailSize)) return tailSize; + FORWARD_IF_ERROR(tailSize); dstPtr += tailSize; } assert(dstEnd >= dstStart); @@ -432,27 +478,26 @@ size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, { size_t result; #if (LZ4F_HEAPMODE) - LZ4F_cctx_t *cctxPtr; + LZ4F_cctx_t* cctxPtr; result = LZ4F_createCompressionContext(&cctxPtr, LZ4F_VERSION); - if (LZ4F_isError(result)) return result; + FORWARD_IF_ERROR(result); #else LZ4F_cctx_t cctx; LZ4_stream_t lz4ctx; - LZ4F_cctx_t *cctxPtr = &cctx; + LZ4F_cctx_t* const cctxPtr = &cctx; - DEBUGLOG(4, "LZ4F_compressFrame"); MEM_INIT(&cctx, 0, sizeof(cctx)); cctx.version = LZ4F_VERSION; cctx.maxBufferSize = 5 MB; /* mess with real buffer size to prevent dynamic allocation; works only because autoflush==1 & stableSrc==1 */ - if (preferencesPtr == NULL || - preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN) - { + if ( preferencesPtr == NULL + || preferencesPtr->compressionLevel < LZ4HC_CLEVEL_MIN ) { LZ4_initStream(&lz4ctx, sizeof(lz4ctx)); cctxPtr->lz4CtxPtr = &lz4ctx; cctxPtr->lz4CtxAlloc = 1; cctxPtr->lz4CtxState = 1; } #endif + DEBUGLOG(4, "LZ4F_compressFrame"); result = LZ4F_compressFrame_usingCDict(cctxPtr, dstBuffer, dstCapacity, srcBuffer, srcSize, @@ -461,10 +506,9 @@ size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, #if (LZ4F_HEAPMODE) LZ4F_freeCompressionContext(cctxPtr); #else - if (preferencesPtr != NULL && - preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN) - { - FREEMEM(cctxPtr->lz4CtxPtr); + if ( preferencesPtr != NULL + && preferencesPtr->compressionLevel >= LZ4HC_CLEVEL_MIN ) { + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); } #endif return result; @@ -476,30 +520,31 @@ size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, *****************************************************/ struct LZ4F_CDict_s { + LZ4F_CustomMem cmem; void* dictContent; LZ4_stream_t* fastCtx; LZ4_streamHC_t* HCCtx; }; /* typedef'd to LZ4F_CDict within lz4frame_static.h */ -/*! LZ4F_createCDict() : - * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. - * LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. - * LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. - * `dictBuffer` can be released after LZ4F_CDict creation, since its content is copied within CDict - * @return : digested dictionary for compression, or NULL if failed */ -LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize) +LZ4F_CDict* +LZ4F_createCDict_advanced(LZ4F_CustomMem cmem, const void* dictBuffer, size_t dictSize) { const char* dictStart = (const char*)dictBuffer; - LZ4F_CDict* cdict = (LZ4F_CDict*) ALLOC(sizeof(*cdict)); - DEBUGLOG(4, "LZ4F_createCDict"); + LZ4F_CDict* const cdict = (LZ4F_CDict*)LZ4F_malloc(sizeof(*cdict), cmem); + DEBUGLOG(4, "LZ4F_createCDict_advanced"); if (!cdict) return NULL; + cdict->cmem = cmem; if (dictSize > 64 KB) { dictStart += dictSize - 64 KB; dictSize = 64 KB; } - cdict->dictContent = ALLOC(dictSize); - cdict->fastCtx = LZ4_createStream(); - cdict->HCCtx = LZ4_createStreamHC(); + cdict->dictContent = LZ4F_malloc(dictSize, cmem); + cdict->fastCtx = (LZ4_stream_t*)LZ4F_malloc(sizeof(LZ4_stream_t), cmem); + if (cdict->fastCtx) + LZ4_initStream(cdict->fastCtx, sizeof(LZ4_stream_t)); + cdict->HCCtx = (LZ4_streamHC_t*)LZ4F_malloc(sizeof(LZ4_streamHC_t), cmem); + if (cdict->HCCtx) + LZ4_initStream(cdict->HCCtx, sizeof(LZ4_streamHC_t)); if (!cdict->dictContent || !cdict->fastCtx || !cdict->HCCtx) { LZ4F_freeCDict(cdict); return NULL; @@ -511,13 +556,25 @@ LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize) return cdict; } +/*! LZ4F_createCDict() : + * When compressing multiple messages / blocks with the same dictionary, it's recommended to load it just once. + * LZ4F_createCDict() will create a digested dictionary, ready to start future compression operations without startup delay. + * LZ4F_CDict can be created once and shared by multiple threads concurrently, since its usage is read-only. + * @dictBuffer can be released after LZ4F_CDict creation, since its content is copied within CDict + * @return : digested dictionary for compression, or NULL if failed */ +LZ4F_CDict* LZ4F_createCDict(const void* dictBuffer, size_t dictSize) +{ + DEBUGLOG(4, "LZ4F_createCDict"); + return LZ4F_createCDict_advanced(LZ4F_defaultCMem, dictBuffer, dictSize); +} + void LZ4F_freeCDict(LZ4F_CDict* cdict) { if (cdict==NULL) return; /* support free on NULL */ - FREEMEM(cdict->dictContent); - LZ4_freeStream(cdict->fastCtx); - LZ4_freeStreamHC(cdict->HCCtx); - FREEMEM(cdict); + LZ4F_free(cdict->dictContent, cdict->cmem); + LZ4F_free(cdict->fastCtx, cdict->cmem); + LZ4F_free(cdict->HCCtx, cdict->cmem); + LZ4F_free(cdict, cdict->cmem); } @@ -525,6 +582,20 @@ void LZ4F_freeCDict(LZ4F_CDict* cdict) * Advanced compression functions ***********************************/ +LZ4F_cctx* +LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version) +{ + LZ4F_cctx* const cctxPtr = + (LZ4F_cctx*)LZ4F_calloc(sizeof(LZ4F_cctx), customMem); + if (cctxPtr==NULL) return NULL; + + cctxPtr->cmem = customMem; + cctxPtr->version = version; + cctxPtr->cStage = 0; /* Uninitialized. Next stage : init cctx */ + + return cctxPtr; +} + /*! LZ4F_createCompressionContext() : * The first thing to do is to create a compressionContext object, which will be used in all compression operations. * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version and an LZ4F_preferences_t structure. @@ -532,17 +603,16 @@ void LZ4F_freeCDict(LZ4F_CDict* cdict) * The function will provide a pointer to an allocated LZ4F_compressionContext_t object. * If the result LZ4F_errorCode_t is not OK_NoError, there was an error during context creation. * Object can release its memory using LZ4F_freeCompressionContext(); - */ -LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version) +**/ +LZ4F_errorCode_t +LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionContextPtr, unsigned version) { - LZ4F_cctx_t* const cctxPtr = (LZ4F_cctx_t*)ALLOC_AND_ZERO(sizeof(LZ4F_cctx_t)); - if (cctxPtr==NULL) return err0r(LZ4F_ERROR_allocation_failed); - - cctxPtr->version = version; - cctxPtr->cStage = 0; /* Next stage : init stream */ - - *LZ4F_compressionContextPtr = cctxPtr; + assert(LZ4F_compressionContextPtr != NULL); /* considered a violation of narrow contract */ + /* in case it nonetheless happen in production */ + RETURN_ERROR_IF(LZ4F_compressionContextPtr == NULL, parameter_null); + *LZ4F_compressionContextPtr = LZ4F_createCompressionContext_advanced(LZ4F_defaultCMem, version); + RETURN_ERROR_IF(*LZ4F_compressionContextPtr==NULL, allocation_failed); return LZ4F_OK_NoError; } @@ -550,11 +620,10 @@ LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** LZ4F_compressionConte LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctxPtr) { if (cctxPtr != NULL) { /* support free on NULL */ - FREEMEM(cctxPtr->lz4CtxPtr); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */ - FREEMEM(cctxPtr->tmpBuff); - FREEMEM(cctxPtr); + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); /* note: LZ4_streamHC_t and LZ4_stream_t are simple POD types */ + LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem); + LZ4F_free(cctxPtr, cctxPtr->cmem); } - return LZ4F_OK_NoError; } @@ -588,11 +657,21 @@ static void LZ4F_initStream(void* ctx, } } +static int ctxTypeID_to_size(int ctxTypeID) { + switch(ctxTypeID) { + case 1: + return LZ4_sizeofState(); + case 2: + return LZ4_sizeofStateHC(); + default: + return 0; + } +} /*! LZ4F_compressBegin_usingCDict() : - * init streaming compression and writes frame header into dstBuffer. - * dstBuffer must be >= LZ4F_HEADER_SIZE_MAX bytes. - * @return : number of bytes written into dstBuffer for the header + * init streaming compression AND writes frame header into @dstBuffer. + * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * @return : number of bytes written into @dstBuffer for the header * or an error code (can be tested using LZ4F_isError()) */ size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, @@ -600,41 +679,46 @@ size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, const LZ4F_CDict* cdict, const LZ4F_preferences_t* preferencesPtr) { - LZ4F_preferences_t prefNull; + LZ4F_preferences_t const prefNull = LZ4F_INIT_PREFERENCES; BYTE* const dstStart = (BYTE*)dstBuffer; BYTE* dstPtr = dstStart; - BYTE* headerStart; - if (dstCapacity < maxFHSize) return err0r(LZ4F_ERROR_dstMaxSize_tooSmall); - MEM_INIT(&prefNull, 0, sizeof(prefNull)); + RETURN_ERROR_IF(dstCapacity < maxFHSize, dstMaxSize_tooSmall); if (preferencesPtr == NULL) preferencesPtr = &prefNull; cctxPtr->prefs = *preferencesPtr; - /* Ctx Management */ + /* cctx Management */ { U16 const ctxTypeID = (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) ? 1 : 2; - if (cctxPtr->lz4CtxAlloc < ctxTypeID) { - FREEMEM(cctxPtr->lz4CtxPtr); + int requiredSize = ctxTypeID_to_size(ctxTypeID); + int allocatedSize = ctxTypeID_to_size(cctxPtr->lz4CtxAlloc); + if (allocatedSize < requiredSize) { + /* not enough space allocated */ + LZ4F_free(cctxPtr->lz4CtxPtr, cctxPtr->cmem); if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) { - cctxPtr->lz4CtxPtr = LZ4_createStream(); + /* must take ownership of memory allocation, + * in order to respect custom allocator contract */ + cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_stream_t), cctxPtr->cmem); + if (cctxPtr->lz4CtxPtr) + LZ4_initStream(cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t)); } else { - cctxPtr->lz4CtxPtr = LZ4_createStreamHC(); + cctxPtr->lz4CtxPtr = LZ4F_malloc(sizeof(LZ4_streamHC_t), cctxPtr->cmem); + if (cctxPtr->lz4CtxPtr) + LZ4_initStreamHC(cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); } - if (cctxPtr->lz4CtxPtr == NULL) - return err0r(LZ4F_ERROR_allocation_failed); + RETURN_ERROR_IF(cctxPtr->lz4CtxPtr == NULL, allocation_failed); cctxPtr->lz4CtxAlloc = ctxTypeID; cctxPtr->lz4CtxState = ctxTypeID; } else if (cctxPtr->lz4CtxState != ctxTypeID) { - /* otherwise, a sufficient buffer is allocated, but we need to - * reset it to the correct context type */ + /* otherwise, a sufficient buffer is already allocated, + * but we need to reset it to the correct context type */ if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) { - LZ4_initStream((LZ4_stream_t *) cctxPtr->lz4CtxPtr, sizeof (LZ4_stream_t)); + LZ4_initStream((LZ4_stream_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_stream_t)); } else { - LZ4_initStreamHC((LZ4_streamHC_t *) cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); - LZ4_setCompressionLevel((LZ4_streamHC_t *) cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel); + LZ4_initStreamHC((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, sizeof(LZ4_streamHC_t)); + LZ4_setCompressionLevel((LZ4_streamHC_t*)cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel); } cctxPtr->lz4CtxState = ctxTypeID; - } - } + } } /* Buffer Management */ if (cctxPtr->prefs.frameInfo.blockSizeID == 0) @@ -647,9 +731,9 @@ size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, if (cctxPtr->maxBufferSize < requiredBuffSize) { cctxPtr->maxBufferSize = 0; - FREEMEM(cctxPtr->tmpBuff); - cctxPtr->tmpBuff = (BYTE*)ALLOC_AND_ZERO(requiredBuffSize); - if (cctxPtr->tmpBuff == NULL) return err0r(LZ4F_ERROR_allocation_failed); + LZ4F_free(cctxPtr->tmpBuff, cctxPtr->cmem); + cctxPtr->tmpBuff = (BYTE*)LZ4F_calloc(requiredBuffSize, cctxPtr->cmem); + RETURN_ERROR_IF(cctxPtr->tmpBuff == NULL, allocation_failed); cctxPtr->maxBufferSize = requiredBuffSize; } } cctxPtr->tmpIn = cctxPtr->tmpBuff; @@ -669,31 +753,32 @@ size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, /* Magic Number */ LZ4F_writeLE32(dstPtr, LZ4F_MAGICNUMBER); dstPtr += 4; - headerStart = dstPtr; - - /* FLG Byte */ - *dstPtr++ = (BYTE)(((1 & _2BITS) << 6) /* Version('01') */ - + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5) - + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4) - + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3) - + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2) - + (cctxPtr->prefs.frameInfo.dictID > 0) ); - /* BD Byte */ - *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4); - /* Optional Frame content size field */ - if (cctxPtr->prefs.frameInfo.contentSize) { - LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize); - dstPtr += 8; - cctxPtr->totalInSize = 0; - } - /* Optional dictionary ID field */ - if (cctxPtr->prefs.frameInfo.dictID) { - LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID); - dstPtr += 4; + { BYTE* const headerStart = dstPtr; + + /* FLG Byte */ + *dstPtr++ = (BYTE)(((1 & _2BITS) << 6) /* Version('01') */ + + ((cctxPtr->prefs.frameInfo.blockMode & _1BIT ) << 5) + + ((cctxPtr->prefs.frameInfo.blockChecksumFlag & _1BIT ) << 4) + + ((unsigned)(cctxPtr->prefs.frameInfo.contentSize > 0) << 3) + + ((cctxPtr->prefs.frameInfo.contentChecksumFlag & _1BIT ) << 2) + + (cctxPtr->prefs.frameInfo.dictID > 0) ); + /* BD Byte */ + *dstPtr++ = (BYTE)((cctxPtr->prefs.frameInfo.blockSizeID & _3BITS) << 4); + /* Optional Frame content size field */ + if (cctxPtr->prefs.frameInfo.contentSize) { + LZ4F_writeLE64(dstPtr, cctxPtr->prefs.frameInfo.contentSize); + dstPtr += 8; + cctxPtr->totalInSize = 0; + } + /* Optional dictionary ID field */ + if (cctxPtr->prefs.frameInfo.dictID) { + LZ4F_writeLE32(dstPtr, cctxPtr->prefs.frameInfo.dictID); + dstPtr += 4; + } + /* Header CRC Byte */ + *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart)); + dstPtr++; } - /* Header CRC Byte */ - *dstPtr = LZ4F_headerChecksum(headerStart, (size_t)(dstPtr - headerStart)); - dstPtr++; cctxPtr->cStage = 1; /* header written, now request input data block */ return (size_t)(dstPtr - dstStart); @@ -701,9 +786,9 @@ size_t LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctxPtr, /*! LZ4F_compressBegin() : - * init streaming compression and writes frame header into dstBuffer. - * dstBuffer must be >= LZ4F_HEADER_SIZE_MAX bytes. - * preferencesPtr can be NULL, in which case default parameters are selected. + * init streaming compression AND writes frame header into @dstBuffer. + * @dstCapacity must be >= LZ4F_HEADER_SIZE_MAX bytes. + * @preferencesPtr can be NULL, in which case default parameters are selected. * @return : number of bytes written into dstBuffer for the header * or an error code (can be tested using LZ4F_isError()) */ @@ -744,11 +829,13 @@ static size_t LZ4F_makeBlock(void* dst, LZ4F_blockChecksum_t crcFlag) { BYTE* const cSizePtr = (BYTE*)dst; - U32 cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize), - (int)(srcSize), (int)(srcSize-1), - level, cdict); - if (cSize == 0) { /* compression failed */ - DEBUGLOG(5, "LZ4F_makeBlock: compression failed, creating a raw block (size %u)", (U32)srcSize); + U32 cSize; + assert(compress != NULL); + cSize = (U32)compress(lz4ctx, (const char*)src, (char*)(cSizePtr+BHSize), + (int)(srcSize), (int)(srcSize-1), + level, cdict); + + if (cSize == 0 || cSize >= srcSize) { cSize = (U32)srcSize; LZ4F_writeLE32(cSizePtr, cSize | LZ4F_BLOCKUNCOMPRESSED_FLAG); memcpy(cSizePtr+BHSize, src, srcSize); @@ -766,6 +853,7 @@ static size_t LZ4F_makeBlock(void* dst, static int LZ4F_compressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) { int const acceleration = (level < 0) ? -level + 1 : 1; + DEBUGLOG(5, "LZ4F_compressBlock (srcSize=%i)", srcSize); LZ4F_initStream(ctx, cdict, level, LZ4F_blockIndependent); if (cdict) { return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); @@ -778,6 +866,7 @@ static int LZ4F_compressBlock_continue(void* ctx, const char* src, char* dst, in { int const acceleration = (level < 0) ? -level + 1 : 1; (void)cdict; /* init once at beginning of frame */ + DEBUGLOG(5, "LZ4F_compressBlock_continue (srcSize=%i)", srcSize); return LZ4_compress_fast_continue((LZ4_stream_t*)ctx, src, dst, srcSize, dstCapacity, acceleration); } @@ -796,8 +885,15 @@ static int LZ4F_compressBlockHC_continue(void* ctx, const char* src, char* dst, return LZ4_compress_HC_continue((LZ4_streamHC_t*)ctx, src, dst, srcSize, dstCapacity); } -static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level) +static int LZ4F_doNotCompressBlock(void* ctx, const char* src, char* dst, int srcSize, int dstCapacity, int level, const LZ4F_CDict* cdict) +{ + (void)ctx; (void)src; (void)dst; (void)srcSize; (void)dstCapacity; (void)level; (void)cdict; + return 0; +} + +static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int level, LZ4F_blockCompression_t compressMode) { + if (compressMode == LZ4B_UNCOMPRESSED) return LZ4F_doNotCompressBlock; if (level < LZ4HC_CLEVEL_MIN) { if (blockMode == LZ4F_blockIndependent) return LZ4F_compressBlock; return LZ4F_compressBlock_continue; @@ -806,6 +902,7 @@ static compressFunc_t LZ4F_selectCompression(LZ4F_blockMode_t blockMode, int lev return LZ4F_compressBlockHC_continue; } +/* Save history (up to 64KB) into @tmpBuff */ static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr) { if (cctxPtr->prefs.compressionLevel < LZ4HC_CLEVEL_MIN) @@ -815,38 +912,57 @@ static int LZ4F_localSaveDict(LZ4F_cctx_t* cctxPtr) typedef enum { notDone, fromTmpBuffer, fromSrcBuffer } LZ4F_lastBlockStatus; -/*! LZ4F_compressUpdate() : +static const LZ4F_compressOptions_t k_cOptionsNull = { 0, { 0, 0, 0 } }; + + + /*! LZ4F_compressUpdateImpl() : * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. - * dstBuffer MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). - * LZ4F_compressOptions_t structure is optional : you can provide NULL as argument. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If the block compression does not match the compression of the previous block, the old data is flushed + * and operations continue with the new compression mode. + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr) when block compression is turned on. + * @compressOptionsPtr is optional : provide NULL to mean "default". * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. */ -size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, - void* dstBuffer, size_t dstCapacity, +static size_t LZ4F_compressUpdateImpl(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, const void* srcBuffer, size_t srcSize, - const LZ4F_compressOptions_t* compressOptionsPtr) -{ - LZ4F_compressOptions_t cOptionsNull; + const LZ4F_compressOptions_t* compressOptionsPtr, + LZ4F_blockCompression_t blockCompression) + { size_t const blockSize = cctxPtr->maxBlockSize; const BYTE* srcPtr = (const BYTE*)srcBuffer; const BYTE* const srcEnd = srcPtr + srcSize; BYTE* const dstStart = (BYTE*)dstBuffer; BYTE* dstPtr = dstStart; LZ4F_lastBlockStatus lastBlockCompressed = notDone; - compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel); - + compressFunc_t const compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, blockCompression); + size_t bytesWritten; DEBUGLOG(4, "LZ4F_compressUpdate (srcSize=%zu)", srcSize); - if (cctxPtr->cStage != 1) return err0r(LZ4F_ERROR_GENERIC); + RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); /* state must be initialized and waiting for next block */ if (dstCapacity < LZ4F_compressBound_internal(srcSize, &(cctxPtr->prefs), cctxPtr->tmpInSize)) - return err0r(LZ4F_ERROR_dstMaxSize_tooSmall); - MEM_INIT(&cOptionsNull, 0, sizeof(cOptionsNull)); - if (compressOptionsPtr == NULL) compressOptionsPtr = &cOptionsNull; + RETURN_ERROR(dstMaxSize_tooSmall); + + if (blockCompression == LZ4B_UNCOMPRESSED && dstCapacity < srcSize) + RETURN_ERROR(dstMaxSize_tooSmall); + + /* flush currently written block, to continue with new block compression */ + if (cctxPtr->blockCompression != blockCompression) { + bytesWritten = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); + dstPtr += bytesWritten; + cctxPtr->blockCompression = blockCompression; + } + + if (compressOptionsPtr == NULL) compressOptionsPtr = &k_cOptionsNull; /* complete tmp buffer */ if (cctxPtr->tmpInSize > 0) { /* some data already within tmp buffer */ size_t const sizeToCopy = blockSize - cctxPtr->tmpInSize; + assert(blockSize > cctxPtr->tmpInSize); if (sizeToCopy > srcSize) { /* add src to tmpIn buffer */ memcpy(cctxPtr->tmpIn + cctxPtr->tmpInSize, srcBuffer, srcSize); @@ -864,11 +980,9 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, cctxPtr->cdict, cctxPtr->prefs.frameInfo.blockChecksumFlag); - if (cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) cctxPtr->tmpIn += blockSize; cctxPtr->tmpInSize = 0; - } - } + } } while ((size_t)(srcEnd - srcPtr) >= blockSize) { /* compress full blocks */ @@ -882,33 +996,38 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, } if ((cctxPtr->prefs.autoFlush) && (srcPtr < srcEnd)) { - /* compress remaining input < blockSize */ + /* autoFlush : remaining input (< blockSize) is compressed */ lastBlockCompressed = fromSrcBuffer; dstPtr += LZ4F_makeBlock(dstPtr, srcPtr, (size_t)(srcEnd - srcPtr), compress, cctxPtr->lz4CtxPtr, cctxPtr->prefs.compressionLevel, cctxPtr->cdict, cctxPtr->prefs.frameInfo.blockChecksumFlag); - srcPtr = srcEnd; + srcPtr = srcEnd; } - /* preserve dictionary if necessary */ + /* preserve dictionary within @tmpBuff whenever necessary */ if ((cctxPtr->prefs.frameInfo.blockMode==LZ4F_blockLinked) && (lastBlockCompressed==fromSrcBuffer)) { + /* linked blocks are only supported in compressed mode, see LZ4F_uncompressedUpdate */ + assert(blockCompression == LZ4B_COMPRESSED); if (compressOptionsPtr->stableSrc) { - cctxPtr->tmpIn = cctxPtr->tmpBuff; + cctxPtr->tmpIn = cctxPtr->tmpBuff; /* src is stable : dictionary remains in src across invocations */ } else { int const realDictSize = LZ4F_localSaveDict(cctxPtr); - if (realDictSize==0) return err0r(LZ4F_ERROR_GENERIC); + assert(0 <= realDictSize && realDictSize <= 64 KB); cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; } } /* keep tmpIn within limits */ - if ((cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) /* necessarily LZ4F_blockLinked && lastBlockCompressed==fromTmpBuffer */ - && !(cctxPtr->prefs.autoFlush)) + if (!(cctxPtr->prefs.autoFlush) /* no autoflush : there may be some data left within internal buffer */ + && (cctxPtr->tmpIn + blockSize) > (cctxPtr->tmpBuff + cctxPtr->maxBufferSize) ) /* not enough room to store next block */ { + /* only preserve 64KB within internal buffer. Ensures there is enough room for next block. + * note: this situation necessarily implies lastBlockCompressed==fromTmpBuffer */ int const realDictSize = LZ4F_localSaveDict(cctxPtr); cctxPtr->tmpIn = cctxPtr->tmpBuff + realDictSize; + assert((cctxPtr->tmpIn + blockSize) <= (cctxPtr->tmpBuff + cctxPtr->maxBufferSize)); } /* some input data left, necessarily < blockSize */ @@ -926,6 +1045,53 @@ size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, return (size_t)(dstPtr - dstStart); } +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +size_t LZ4F_compressUpdate(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) +{ + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_COMPRESSED); +} + +/*! LZ4F_compressUpdate() : + * LZ4F_compressUpdate() can be called repetitively to compress as much data as necessary. + * When successful, the function always entirely consumes @srcBuffer. + * src data is either buffered or compressed into @dstBuffer. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. + * This is only supported when LZ4F_blockIndependent is used + * @dstCapacity MUST be >= LZ4F_compressBound(srcSize, preferencesPtr). + * @compressOptionsPtr is optional : provide NULL to mean "default". + * @return : the number of bytes written into dstBuffer. It can be zero, meaning input data was just buffered. + * or an error code if it fails (which can be tested using LZ4F_isError()) + * After an error, the state is left in a UB state, and must be re-initialized. + */ +size_t LZ4F_uncompressedUpdate(LZ4F_cctx* cctxPtr, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* compressOptionsPtr) { + RETURN_ERROR_IF(cctxPtr->prefs.frameInfo.blockMode != LZ4F_blockIndependent, blockMode_invalid); + return LZ4F_compressUpdateImpl(cctxPtr, + dstBuffer, dstCapacity, + srcBuffer, srcSize, + compressOptionsPtr, LZ4B_UNCOMPRESSED); +} + /*! LZ4F_flush() : * When compressed data must be sent immediately, without waiting for a block to be filled, @@ -944,13 +1110,12 @@ size_t LZ4F_flush(LZ4F_cctx* cctxPtr, compressFunc_t compress; if (cctxPtr->tmpInSize == 0) return 0; /* nothing to flush */ - if (cctxPtr->cStage != 1) return err0r(LZ4F_ERROR_GENERIC); - if (dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize)) - return err0r(LZ4F_ERROR_dstMaxSize_tooSmall); - (void)compressOptionsPtr; /* not yet useful */ + RETURN_ERROR_IF(cctxPtr->cStage != 1, compressionState_uninitialized); + RETURN_ERROR_IF(dstCapacity < (cctxPtr->tmpInSize + BHSize + BFSize), dstMaxSize_tooSmall); + (void)compressOptionsPtr; /* not useful (yet) */ /* select compression function */ - compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel); + compress = LZ4F_selectCompression(cctxPtr->prefs.frameInfo.blockMode, cctxPtr->prefs.compressionLevel, cctxPtr->blockCompression); /* compress tmp buffer */ dstPtr += LZ4F_makeBlock(dstPtr, @@ -992,19 +1157,19 @@ size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr, size_t const flushSize = LZ4F_flush(cctxPtr, dstBuffer, dstCapacity, compressOptionsPtr); DEBUGLOG(5,"LZ4F_compressEnd: dstCapacity=%u", (unsigned)dstCapacity); - if (LZ4F_isError(flushSize)) return flushSize; + FORWARD_IF_ERROR(flushSize); dstPtr += flushSize; assert(flushSize <= dstCapacity); dstCapacity -= flushSize; - if (dstCapacity < 4) return err0r(LZ4F_ERROR_dstMaxSize_tooSmall); + RETURN_ERROR_IF(dstCapacity < 4, dstMaxSize_tooSmall); LZ4F_writeLE32(dstPtr, 0); dstPtr += 4; /* endMark */ if (cctxPtr->prefs.frameInfo.contentChecksumFlag == LZ4F_contentChecksumEnabled) { U32 const xxh = XXH32_digest(&(cctxPtr->xxh)); - if (dstCapacity < 8) return err0r(LZ4F_ERROR_dstMaxSize_tooSmall); + RETURN_ERROR_IF(dstCapacity < 8, dstMaxSize_tooSmall); DEBUGLOG(5,"Writing 32-bit content checksum"); LZ4F_writeLE32(dstPtr, xxh); dstPtr+=4; /* content Checksum */ @@ -1015,7 +1180,7 @@ size_t LZ4F_compressEnd(LZ4F_cctx* cctxPtr, if (cctxPtr->prefs.frameInfo.contentSize) { if (cctxPtr->prefs.frameInfo.contentSize != cctxPtr->totalInSize) - return err0r(LZ4F_ERROR_frameSize_wrong); + RETURN_ERROR(frameSize_wrong); } return (size_t)(dstPtr - dstStart); @@ -1039,6 +1204,7 @@ typedef enum { } dStage_t; struct LZ4F_dctx_s { + LZ4F_CustomMem cmem; LZ4F_frameInfo_t frameInfo; U32 version; dStage_t dStage; @@ -1056,26 +1222,37 @@ struct LZ4F_dctx_s { size_t tmpOutStart; XXH32_state_t xxh; XXH32_state_t blockChecksum; + int skipChecksum; BYTE header[LZ4F_HEADER_SIZE_MAX]; }; /* typedef'd to LZ4F_dctx in lz4frame.h */ +LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version) +{ + LZ4F_dctx* const dctx = (LZ4F_dctx*)LZ4F_calloc(sizeof(LZ4F_dctx), customMem); + if (dctx == NULL) return NULL; + + dctx->cmem = customMem; + dctx->version = version; + return dctx; +} + /*! LZ4F_createDecompressionContext() : * Create a decompressionContext object, which will track all decompression operations. * Provides a pointer to a fully allocated and initialized LZ4F_decompressionContext object. * Object can later be released using LZ4F_freeDecompressionContext(). * @return : if != 0, there was an error during context creation. */ -LZ4F_errorCode_t LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber) +LZ4F_errorCode_t +LZ4F_createDecompressionContext(LZ4F_dctx** LZ4F_decompressionContextPtr, unsigned versionNumber) { - LZ4F_dctx* const dctx = (LZ4F_dctx*)ALLOC_AND_ZERO(sizeof(LZ4F_dctx)); - if (dctx == NULL) { /* failed allocation */ - *LZ4F_decompressionContextPtr = NULL; - return err0r(LZ4F_ERROR_allocation_failed); - } + assert(LZ4F_decompressionContextPtr != NULL); /* violation of narrow contract */ + RETURN_ERROR_IF(LZ4F_decompressionContextPtr == NULL, parameter_null); /* in case it nonetheless happen in production */ - dctx->version = versionNumber; - *LZ4F_decompressionContextPtr = dctx; + *LZ4F_decompressionContextPtr = LZ4F_createDecompressionContext_advanced(LZ4F_defaultCMem, versionNumber); + if (*LZ4F_decompressionContextPtr == NULL) { /* failed allocation */ + RETURN_ERROR(allocation_failed); + } return LZ4F_OK_NoError; } @@ -1084,9 +1261,9 @@ LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx) LZ4F_errorCode_t result = LZ4F_OK_NoError; if (dctx != NULL) { /* can accept NULL input, like free() */ result = (LZ4F_errorCode_t)dctx->dStage; - FREEMEM(dctx->tmpIn); - FREEMEM(dctx->tmpOutBuffer); - FREEMEM(dctx); + LZ4F_free(dctx->tmpIn, dctx->cmem); + LZ4F_free(dctx->tmpOutBuffer, dctx->cmem); + LZ4F_free(dctx, dctx->cmem); } return result; } @@ -1099,6 +1276,7 @@ void LZ4F_resetDecompressionContext(LZ4F_dctx* dctx) dctx->dStage = dstage_getFrameHeader; dctx->dict = NULL; dctx->dictSize = 0; + dctx->skipChecksum = 0; } @@ -1118,7 +1296,7 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize DEBUGLOG(5, "LZ4F_decodeHeader"); /* need to decode header to get frameInfo */ - if (srcSize < minFHSize) return err0r(LZ4F_ERROR_frameHeader_incomplete); /* minimal frame header size */ + RETURN_ERROR_IF(srcSize < minFHSize, frameHeader_incomplete); /* minimal frame header size */ MEM_INIT(&(dctx->frameInfo), 0, sizeof(dctx->frameInfo)); /* special case : skippable frames */ @@ -1132,14 +1310,13 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize } else { dctx->dStage = dstage_getSFrameSize; return 4; - } - } + } } /* control magic number */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (LZ4F_readLE32(srcPtr) != LZ4F_MAGICNUMBER) { DEBUGLOG(4, "frame header error : unknown magic number"); - return err0r(LZ4F_ERROR_frameType_unknown); + RETURN_ERROR(frameType_unknown); } #endif dctx->frameInfo.frameType = LZ4F_frame; @@ -1153,8 +1330,8 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize contentChecksumFlag = (FLG>>2) & _1BIT; dictIDFlag = FLG & _1BIT; /* validate */ - if (((FLG>>1)&_1BIT) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */ - if (version != 1) return err0r(LZ4F_ERROR_headerVersion_wrong); /* Version Number, only supported value */ + if (((FLG>>1)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */ + if (version != 1) RETURN_ERROR(headerVersion_wrong); /* Version Number, only supported value */ } /* Frame Header Size */ @@ -1173,17 +1350,16 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize { U32 const BD = srcPtr[5]; blockSizeID = (BD>>4) & _3BITS; /* validate */ - if (((BD>>7)&_1BIT) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bit */ - if (blockSizeID < 4) return err0r(LZ4F_ERROR_maxBlockSize_invalid); /* 4-7 only supported values for the time being */ - if (((BD>>0)&_4BITS) != 0) return err0r(LZ4F_ERROR_reservedFlag_set); /* Reserved bits */ + if (((BD>>7)&_1BIT) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bit */ + if (blockSizeID < 4) RETURN_ERROR(maxBlockSize_invalid); /* 4-7 only supported values for the time being */ + if (((BD>>0)&_4BITS) != 0) RETURN_ERROR(reservedFlag_set); /* Reserved bits */ } /* check header */ assert(frameHeaderSize > 5); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION { BYTE const HC = LZ4F_headerChecksum(srcPtr+4, frameHeaderSize-5); - if (HC != srcPtr[frameHeaderSize-1]) - return err0r(LZ4F_ERROR_headerChecksum_invalid); + RETURN_ERROR_IF(HC != srcPtr[frameHeaderSize-1], headerChecksum_invalid); } #endif @@ -1192,10 +1368,9 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize dctx->frameInfo.blockChecksumFlag = (LZ4F_blockChecksum_t)blockChecksumFlag; dctx->frameInfo.contentChecksumFlag = (LZ4F_contentChecksum_t)contentChecksumFlag; dctx->frameInfo.blockSizeID = (LZ4F_blockSizeID_t)blockSizeID; - dctx->maxBlockSize = LZ4F_getBlockSize(blockSizeID); + dctx->maxBlockSize = LZ4F_getBlockSize((LZ4F_blockSizeID_t)blockSizeID); if (contentSizeFlag) - dctx->frameRemainingSize = - dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6); + dctx->frameRemainingSize = dctx->frameInfo.contentSize = LZ4F_readLE64(srcPtr+6); if (dictIDFlag) dctx->frameInfo.dictID = LZ4F_readLE32(srcPtr + frameHeaderSize - 5); @@ -1211,11 +1386,11 @@ static size_t LZ4F_decodeHeader(LZ4F_dctx* dctx, const void* src, size_t srcSize */ size_t LZ4F_headerSize(const void* src, size_t srcSize) { - if (src == NULL) return err0r(LZ4F_ERROR_srcPtr_wrong); + RETURN_ERROR_IF(src == NULL, srcPtr_wrong); /* minimal srcSize to determine header size */ if (srcSize < LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH) - return err0r(LZ4F_ERROR_frameHeader_incomplete); + RETURN_ERROR(frameHeader_incomplete); /* special case : skippable frames */ if ((LZ4F_readLE32(src) & 0xFFFFFFF0U) == LZ4F_MAGIC_SKIPPABLE_START) @@ -1224,7 +1399,7 @@ size_t LZ4F_headerSize(const void* src, size_t srcSize) /* control magic number */ #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION if (LZ4F_readLE32(src) != LZ4F_MAGICNUMBER) - return err0r(LZ4F_ERROR_frameType_unknown); + RETURN_ERROR(frameType_unknown); #endif /* Frame Header Size */ @@ -1266,13 +1441,13 @@ LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, if (dctx->dStage == dstage_storeFrameHeader) { /* frame decoding already started, in the middle of header => automatic fail */ *srcSizePtr = 0; - return err0r(LZ4F_ERROR_frameDecoding_alreadyStarted); + RETURN_ERROR(frameDecoding_alreadyStarted); } else { size_t const hSize = LZ4F_headerSize(srcBuffer, *srcSizePtr); if (LZ4F_isError(hSize)) { *srcSizePtr=0; return hSize; } if (*srcSizePtr < hSize) { *srcSizePtr=0; - return err0r(LZ4F_ERROR_frameHeader_incomplete); + RETURN_ERROR(frameHeader_incomplete); } { size_t decodeResult = LZ4F_decodeHeader(dctx, srcBuffer, hSize); @@ -1290,16 +1465,14 @@ LZ4F_errorCode_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, /* LZ4F_updateDict() : * only used for LZ4F_blockLinked mode - * Condition : dstPtr != NULL + * Condition : @dstPtr != NULL */ static void LZ4F_updateDict(LZ4F_dctx* dctx, const BYTE* dstPtr, size_t dstSize, const BYTE* dstBufferStart, unsigned withinTmp) { assert(dstPtr != NULL); - if (dctx->dictSize==0) { - dctx->dict = (const BYTE*)dstPtr; /* priority to prefix mode */ - } + if (dctx->dictSize==0) dctx->dict = (const BYTE*)dstPtr; /* will lead to prefix mode */ assert(dctx->dict != NULL); if (dctx->dict + dctx->dictSize == dstPtr) { /* prefix mode, everything within dstBuffer */ @@ -1362,7 +1535,6 @@ static void LZ4F_updateDict(LZ4F_dctx* dctx, } - /*! LZ4F_decompress() : * Call this function repetitively to regenerate compressed data in srcBuffer. * The function will attempt to decode up to *srcSizePtr bytes from srcBuffer @@ -1406,6 +1578,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, *srcSizePtr = 0; *dstSizePtr = 0; assert(dctx != NULL); + dctx->skipChecksum |= (decompressOptionsPtr->skipChecksums != 0); /* once set, disable for the remainder of the frame */ /* behaves as a state machine */ @@ -1418,7 +1591,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, DEBUGLOG(6, "dstage_getFrameHeader"); if ((size_t)(srcEnd-srcPtr) >= maxFHSize) { /* enough to decode - shortcut */ size_t const hSize = LZ4F_decodeHeader(dctx, srcPtr, (size_t)(srcEnd-srcPtr)); /* will update dStage appropriately */ - if (LZ4F_isError(hSize)) return hSize; + FORWARD_IF_ERROR(hSize); srcPtr += hSize; break; } @@ -1440,9 +1613,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, doAnotherStage = 0; /* not enough src data, ask for some more */ break; } - { size_t const hSize = LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget); /* will update dStage appropriately */ - if (LZ4F_isError(hSize)) return hSize; - } + FORWARD_IF_ERROR( LZ4F_decodeHeader(dctx, dctx->header, dctx->tmpInTarget) ); /* will update dStage appropriately */ break; case dstage_init: @@ -1453,14 +1624,12 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, + ((dctx->frameInfo.blockMode==LZ4F_blockLinked) ? 128 KB : 0); if (bufferNeeded > dctx->maxBufferSize) { /* tmp buffers too small */ dctx->maxBufferSize = 0; /* ensure allocation will be re-attempted on next entry*/ - FREEMEM(dctx->tmpIn); - dctx->tmpIn = (BYTE*)ALLOC(dctx->maxBlockSize + BFSize /* block checksum */); - if (dctx->tmpIn == NULL) - return err0r(LZ4F_ERROR_allocation_failed); - FREEMEM(dctx->tmpOutBuffer); - dctx->tmpOutBuffer= (BYTE*)ALLOC(bufferNeeded); - if (dctx->tmpOutBuffer== NULL) - return err0r(LZ4F_ERROR_allocation_failed); + LZ4F_free(dctx->tmpIn, dctx->cmem); + dctx->tmpIn = (BYTE*)LZ4F_malloc(dctx->maxBlockSize + BFSize /* block checksum */, dctx->cmem); + RETURN_ERROR_IF(dctx->tmpIn == NULL, allocation_failed); + LZ4F_free(dctx->tmpOutBuffer, dctx->cmem); + dctx->tmpOutBuffer= (BYTE*)LZ4F_malloc(bufferNeeded, dctx->cmem); + RETURN_ERROR_IF(dctx->tmpOutBuffer== NULL, allocation_failed); dctx->maxBufferSize = bufferNeeded; } } dctx->tmpInSize = 0; @@ -1509,7 +1678,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, break; } if (nextCBlockSize > dctx->maxBlockSize) { - return err0r(LZ4F_ERROR_maxBlockSize_invalid); + RETURN_ERROR(maxBlockSize_invalid); } if (blockHeader & LZ4F_BLOCKUNCOMPRESSED_FLAG) { /* next block is uncompressed */ @@ -1540,11 +1709,13 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, size_t const minBuffSize = MIN((size_t)(srcEnd-srcPtr), (size_t)(dstEnd-dstPtr)); sizeToCopy = MIN(dctx->tmpInTarget, minBuffSize); memcpy(dstPtr, srcPtr, sizeToCopy); - if (dctx->frameInfo.blockChecksumFlag) { - (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy); + if (!dctx->skipChecksum) { + if (dctx->frameInfo.blockChecksumFlag) { + (void)XXH32_update(&dctx->blockChecksum, srcPtr, sizeToCopy); + } + if (dctx->frameInfo.contentChecksumFlag) + (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy); } - if (dctx->frameInfo.contentChecksumFlag) - (void)XXH32_update(&dctx->xxh, srcPtr, sizeToCopy); if (dctx->frameInfo.contentSize) dctx->frameRemainingSize -= sizeToCopy; @@ -1590,14 +1761,15 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, } crcSrc = dctx->header; } - { U32 const readCRC = LZ4F_readLE32(crcSrc); + if (!dctx->skipChecksum) { + U32 const readCRC = LZ4F_readLE32(crcSrc); U32 const calcCRC = XXH32_digest(&dctx->blockChecksum); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION DEBUGLOG(6, "compare block checksum"); if (readCRC != calcCRC) { DEBUGLOG(4, "incorrect block checksum: %08X != %08X", readCRC, calcCRC); - return err0r(LZ4F_ERROR_blockChecksum_invalid); + RETURN_ERROR(blockChecksum_invalid); } #else (void)readCRC; @@ -1637,37 +1809,44 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, } /* At this stage, input is large enough to decode a block */ + + /* First, decode and control block checksum if it exists */ if (dctx->frameInfo.blockChecksumFlag) { + assert(dctx->tmpInTarget >= 4); dctx->tmpInTarget -= 4; assert(selectedIn != NULL); /* selectedIn is defined at this stage (either srcPtr, or dctx->tmpIn) */ { U32 const readBlockCrc = LZ4F_readLE32(selectedIn + dctx->tmpInTarget); U32 const calcBlockCrc = XXH32(selectedIn, dctx->tmpInTarget, 0); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (readBlockCrc != calcBlockCrc) - return err0r(LZ4F_ERROR_blockChecksum_invalid); + RETURN_ERROR_IF(readBlockCrc != calcBlockCrc, blockChecksum_invalid); #else (void)readBlockCrc; (void)calcBlockCrc; #endif } } - if ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize) { + /* decode directly into destination buffer if there is enough room */ + if ( ((size_t)(dstEnd-dstPtr) >= dctx->maxBlockSize) + /* unless the dictionary is stored in tmpOut: + * in which case it's faster to decode within tmpOut + * to benefit from prefix speedup */ + && !(dctx->dict!= NULL && (const BYTE*)dctx->dict + dctx->dictSize == dctx->tmpOut) ) + { const char* dict = (const char*)dctx->dict; size_t dictSize = dctx->dictSize; int decodedSize; assert(dstPtr != NULL); if (dict && dictSize > 1 GB) { - /* the dictSize param is an int, avoid truncation / sign issues */ + /* overflow control : dctx->dictSize is an int, avoid truncation / sign issues */ dict += dictSize - 64 KB; dictSize = 64 KB; } - /* enough capacity in `dst` to decompress directly there */ decodedSize = LZ4_decompress_safe_usingDict( (const char*)selectedIn, (char*)dstPtr, (int)dctx->tmpInTarget, (int)dctx->maxBlockSize, dict, (int)dictSize); - if (decodedSize < 0) return err0r(LZ4F_ERROR_GENERIC); /* decompression failed */ - if (dctx->frameInfo.contentChecksumFlag) + RETURN_ERROR_IF(decodedSize < 0, decompressionFailed); + if ((dctx->frameInfo.contentChecksumFlag) && (!dctx->skipChecksum)) XXH32_update(&(dctx->xxh), dstPtr, (size_t)decodedSize); if (dctx->frameInfo.contentSize) dctx->frameRemainingSize -= (size_t)decodedSize; @@ -1678,25 +1857,27 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, } dstPtr += decodedSize; - dctx->dStage = dstage_getBlockHeader; + dctx->dStage = dstage_getBlockHeader; /* end of block, let's get another one */ break; } /* not enough place into dst : decode into tmpOut */ - /* ensure enough place for tmpOut */ + + /* manage dictionary */ if (dctx->frameInfo.blockMode == LZ4F_blockLinked) { if (dctx->dict == dctx->tmpOutBuffer) { + /* truncate dictionary to 64 KB if too big */ if (dctx->dictSize > 128 KB) { memcpy(dctx->tmpOutBuffer, dctx->dict + dctx->dictSize - 64 KB, 64 KB); dctx->dictSize = 64 KB; } dctx->tmpOut = dctx->tmpOutBuffer + dctx->dictSize; - } else { /* dict not within tmp */ + } else { /* dict not within tmpOut */ size_t const reservedDictSpace = MIN(dctx->dictSize, 64 KB); dctx->tmpOut = dctx->tmpOutBuffer + reservedDictSpace; } } - /* Decode block */ + /* Decode block into tmpOut */ { const char* dict = (const char*)dctx->dict; size_t dictSize = dctx->dictSize; int decodedSize; @@ -1709,9 +1890,8 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, (const char*)selectedIn, (char*)dctx->tmpOut, (int)dctx->tmpInTarget, (int)dctx->maxBlockSize, dict, (int)dictSize); - if (decodedSize < 0) /* decompression failed */ - return err0r(LZ4F_ERROR_decompressionFailed); - if (dctx->frameInfo.contentChecksumFlag) + RETURN_ERROR_IF(decodedSize < 0, decompressionFailed); + if (dctx->frameInfo.contentChecksumFlag && !dctx->skipChecksum) XXH32_update(&(dctx->xxh), dctx->tmpOut, (size_t)decodedSize); if (dctx->frameInfo.contentSize) dctx->frameRemainingSize -= (size_t)decodedSize; @@ -1744,8 +1924,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, break; case dstage_getSuffix: - if (dctx->frameRemainingSize) - return err0r(LZ4F_ERROR_frameSize_wrong); /* incorrect frame size decoded */ + RETURN_ERROR_IF(dctx->frameRemainingSize, frameSize_wrong); /* incorrect frame size decoded */ if (!dctx->frameInfo.contentChecksumFlag) { /* no checksum, frame is completed */ nextSrcSizeHint = 0; LZ4F_resetDecompressionContext(dctx); @@ -1777,20 +1956,20 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, } /* if (dctx->dStage == dstage_storeSuffix) */ /* case dstage_checkSuffix: */ /* no direct entry, avoid initialization risks */ - { U32 const readCRC = LZ4F_readLE32(selectedIn); + if (!dctx->skipChecksum) { + U32 const readCRC = LZ4F_readLE32(selectedIn); U32 const resultCRC = XXH32_digest(&(dctx->xxh)); #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION - if (readCRC != resultCRC) - return err0r(LZ4F_ERROR_contentChecksum_invalid); + RETURN_ERROR_IF(readCRC != resultCRC, contentChecksum_invalid); #else (void)readCRC; (void)resultCRC; #endif - nextSrcSizeHint = 0; - LZ4F_resetDecompressionContext(dctx); - doAnotherStage = 0; - break; } + nextSrcSizeHint = 0; + LZ4F_resetDecompressionContext(dctx); + doAnotherStage = 0; + break; case dstage_getSFrameSize: if ((srcEnd - srcPtr) >= 4) { @@ -1841,7 +2020,7 @@ size_t LZ4F_decompress(LZ4F_dctx* dctx, } /* switch (dctx->dStage) */ } /* while (doAnotherStage) */ - /* preserve history within tmp whenever necessary */ + /* preserve history within tmpOut whenever necessary */ LZ4F_STATIC_ASSERT((unsigned)dstage_init == 2); if ( (dctx->frameInfo.blockMode==LZ4F_blockLinked) /* next block will use up to 64KB from previous ones */ && (dctx->dict != dctx->tmpOutBuffer) /* dictionary is not already within tmp */ diff --git a/mfbt/lz4/lz4frame.h b/mfbt/lz4/lz4frame.h index 4573317ef2164..1bdf6c4fcba57 100644 --- a/mfbt/lz4/lz4frame.h +++ b/mfbt/lz4/lz4frame.h @@ -1,7 +1,7 @@ /* - LZ4 auto-framing library + LZ4F - LZ4-Frame library Header File - Copyright (C) 2011-2017, Yann Collet. + Copyright (C) 2011-2020, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without @@ -39,7 +39,7 @@ * LZ4F also offers streaming capabilities. * * lz4.h is not required when using lz4frame.h, - * except to extract common constant such as LZ4_VERSION_NUMBER. + * except to extract common constants such as LZ4_VERSION_NUMBER. * */ #ifndef LZ4F_H_09782039843 @@ -54,12 +54,12 @@ extern "C" { /** - Introduction - - lz4frame.h implements LZ4 frame specification (doc/lz4_Frame_format.md). - lz4frame.h provides frame compression functions that take care - of encoding standard metadata alongside LZ4-compressed blocks. -*/ + * Introduction + * + * lz4frame.h implements LZ4 frame specification: see doc/lz4_Frame_format.md . + * LZ4 Frames are compatible with `lz4` CLI, + * and designed to be interoperable with any system. +**/ /*-*************************************************************** * Compiler specifics @@ -210,7 +210,7 @@ LZ4FLIB_API int LZ4F_compressionLevel_max(void); /* v1.8.0+ */ * Returns the maximum possible compressed size with LZ4F_compressFrame() given srcSize and preferences. * `preferencesPtr` is optional. It can be replaced by NULL, in which case, the function will assume default preferences. * Note : this result is only usable with LZ4F_compressFrame(). - * It may also be used with LZ4F_compressUpdate() _if no flush() operation_ is performed. + * It may also be relevant to LZ4F_compressUpdate() _only if_ no flush() operation is ever performed. */ LZ4FLIB_API size_t LZ4F_compressFrameBound(size_t srcSize, const LZ4F_preferences_t* preferencesPtr); @@ -230,7 +230,7 @@ LZ4FLIB_API size_t LZ4F_compressFrame(void* dstBuffer, size_t dstCapacity, * Advanced compression functions *************************************/ typedef struct LZ4F_cctx_s LZ4F_cctx; /* incomplete type */ -typedef LZ4F_cctx* LZ4F_compressionContext_t; /* for compatibility with previous API version */ +typedef LZ4F_cctx* LZ4F_compressionContext_t; /* for compatibility with older APIs, prefer using LZ4F_cctx */ typedef struct { unsigned stableSrc; /* 1 == src content will remain present on future calls to LZ4F_compress(); skip copying src content within tmp buffer */ @@ -243,20 +243,27 @@ typedef struct { LZ4FLIB_API unsigned LZ4F_getVersion(void); /*! LZ4F_createCompressionContext() : - * The first thing to do is to create a compressionContext object, which will be used in all compression operations. - * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version. - * The version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL. - * The function will provide a pointer to a fully allocated LZ4F_cctx object. - * If @return != zero, there was an error during context creation. - * Object can release its memory using LZ4F_freeCompressionContext(); - */ + * The first thing to do is to create a compressionContext object, + * which will keep track of operation state during streaming compression. + * This is achieved using LZ4F_createCompressionContext(), which takes as argument a version, + * and a pointer to LZ4F_cctx*, to write the resulting pointer into. + * @version provided MUST be LZ4F_VERSION. It is intended to track potential version mismatch, notably when using DLL. + * The function provides a pointer to a fully allocated LZ4F_cctx object. + * @cctxPtr MUST be != NULL. + * If @return != zero, context creation failed. + * A created compression context can be employed multiple times for consecutive streaming operations. + * Once all streaming compression jobs are completed, + * the state object can be released using LZ4F_freeCompressionContext(). + * Note1 : LZ4F_freeCompressionContext() is always successful. Its return value can be ignored. + * Note2 : LZ4F_freeCompressionContext() works fine with NULL input pointers (do nothing). +**/ LZ4FLIB_API LZ4F_errorCode_t LZ4F_createCompressionContext(LZ4F_cctx** cctxPtr, unsigned version); LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeCompressionContext(LZ4F_cctx* cctx); /*---- Compression ----*/ -#define LZ4F_HEADER_SIZE_MIN 7 /* LZ4 Frame header size can vary, depending on selected paramaters */ +#define LZ4F_HEADER_SIZE_MIN 7 /* LZ4 Frame header size can vary, depending on selected parameters */ #define LZ4F_HEADER_SIZE_MAX 19 /* Size in bytes of a block header in little-endian format. Highest bit indicates if block data is uncompressed */ @@ -301,8 +308,9 @@ LZ4FLIB_API size_t LZ4F_compressBound(size_t srcSize, const LZ4F_preferences_t* * Important rule: dstCapacity MUST be large enough to ensure operation success even in worst case situations. * This value is provided by LZ4F_compressBound(). * If this condition is not respected, LZ4F_compress() will fail (result is an errorCode). - * LZ4F_compressUpdate() doesn't guarantee error recovery. - * When an error occurs, compression context must be freed or resized. + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously an uncompressed block was written, buffered data is flushed + * before appending compressed data is continued. * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). * or an error code if it fails (which can be tested using LZ4F_isError()) @@ -347,8 +355,12 @@ typedef struct LZ4F_dctx_s LZ4F_dctx; /* incomplete type */ typedef LZ4F_dctx* LZ4F_decompressionContext_t; /* compatibility with previous API versions */ typedef struct { - unsigned stableDst; /* pledges that last 64KB decompressed data will remain available unmodified. This optimization skips storage operations in tmp buffers. */ - unsigned reserved[3]; /* must be set to zero for forward compatibility */ + unsigned stableDst; /* pledges that last 64KB decompressed data will remain available unmodified between invocations. + * This optimization skips storage operations in tmp buffers. */ + unsigned skipChecksums; /* disable checksum calculation and verification, even when one is present in frame, to save CPU time. + * Setting this option to 1 once disables all checksums for the rest of the frame. */ + unsigned reserved1; /* must be set to zero for forward compatibility */ + unsigned reserved0; /* idem */ } LZ4F_decompressOptions_t; @@ -356,9 +368,10 @@ typedef struct { /*! LZ4F_createDecompressionContext() : * Create an LZ4F_dctx object, to track all decompression operations. - * The version provided MUST be LZ4F_VERSION. - * The function provides a pointer to an allocated and initialized LZ4F_dctx object. - * The result is an errorCode, which can be tested using LZ4F_isError(). + * @version provided MUST be LZ4F_VERSION. + * @dctxPtr MUST be valid. + * The function fills @dctxPtr with the value of a pointer to an allocated and initialized LZ4F_dctx object. + * The @return is an errorCode, which can be tested using LZ4F_isError(). * dctx memory can be released using LZ4F_freeDecompressionContext(); * Result of LZ4F_freeDecompressionContext() indicates current state of decompressionContext when being released. * That is, it should be == 0 if decompression has been completed fully and correctly. @@ -371,6 +384,8 @@ LZ4FLIB_API LZ4F_errorCode_t LZ4F_freeDecompressionContext(LZ4F_dctx* dctx); * Streaming decompression functions *************************************/ +#define LZ4F_MAGICNUMBER 0x184D2204U +#define LZ4F_MAGIC_SKIPPABLE_START 0x184D2A50U #define LZ4F_MIN_SIZE_TO_KNOW_HEADER_LENGTH 5 /*! LZ4F_headerSize() : v1.9.0+ @@ -386,7 +401,7 @@ LZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize); /*! LZ4F_getFrameInfo() : * This function extracts frame parameters (max blockSize, dictID, etc.). - * Its usage is optional: user can call LZ4F_decompress() directly. + * Its usage is optional: user can also invoke LZ4F_decompress() directly. * * Extracted information will fill an existing LZ4F_frameInfo_t structure. * This can be useful for allocation and dictionary identification purposes. @@ -427,9 +442,10 @@ LZ4FLIB_API size_t LZ4F_headerSize(const void* src, size_t srcSize); * note 1 : in case of error, dctx is not modified. Decoding operation can resume from beginning safely. * note 2 : frame parameters are *copied into* an already allocated LZ4F_frameInfo_t structure. */ -LZ4FLIB_API size_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, - LZ4F_frameInfo_t* frameInfoPtr, - const void* srcBuffer, size_t* srcSizePtr); +LZ4FLIB_API size_t +LZ4F_getFrameInfo(LZ4F_dctx* dctx, + LZ4F_frameInfo_t* frameInfoPtr, + const void* srcBuffer, size_t* srcSizePtr); /*! LZ4F_decompress() : * Call this function repetitively to regenerate data compressed in `srcBuffer`. @@ -462,10 +478,11 @@ LZ4FLIB_API size_t LZ4F_getFrameInfo(LZ4F_dctx* dctx, * * After a frame is fully decoded, dctx can be used again to decompress another frame. */ -LZ4FLIB_API size_t LZ4F_decompress(LZ4F_dctx* dctx, - void* dstBuffer, size_t* dstSizePtr, - const void* srcBuffer, size_t* srcSizePtr, - const LZ4F_decompressOptions_t* dOptPtr); +LZ4FLIB_API size_t +LZ4F_decompress(LZ4F_dctx* dctx, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const LZ4F_decompressOptions_t* dOptPtr); /*! LZ4F_resetDecompressionContext() : added in v1.8.0 @@ -529,6 +546,8 @@ extern "C" { ITEM(ERROR_headerChecksum_invalid) \ ITEM(ERROR_contentChecksum_invalid) \ ITEM(ERROR_frameDecoding_alreadyStarted) \ + ITEM(ERROR_compressionState_uninitialized) \ + ITEM(ERROR_parameter_null) \ ITEM(ERROR_maxCode) #define LZ4F_GENERATE_ENUM(ENUM) LZ4F_##ENUM, @@ -539,7 +558,31 @@ typedef enum { LZ4F_LIST_ERRORS(LZ4F_GENERATE_ENUM) LZ4FLIB_STATIC_API LZ4F_errorCodes LZ4F_getErrorCode(size_t functionResult); -LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(unsigned); + +/*! LZ4F_getBlockSize() : + * Return, in scalar format (size_t), + * the maximum block size associated with blockSizeID. +**/ +LZ4FLIB_STATIC_API size_t LZ4F_getBlockSize(LZ4F_blockSizeID_t blockSizeID); + +/*! LZ4F_uncompressedUpdate() : + * LZ4F_uncompressedUpdate() can be called repetitively to add as much data uncompressed data as necessary. + * Important rule: dstCapacity MUST be large enough to store the entire source buffer as + * no compression is done for this operation + * If this condition is not respected, LZ4F_uncompressedUpdate() will fail (result is an errorCode). + * After an error, the state is left in a UB state, and must be re-initialized or freed. + * If previously a compressed block was written, buffered data is flushed + * before appending uncompressed data is continued. + * This is only supported when LZ4F_blockIndependent is used + * `cOptPtr` is optional : NULL can be provided, in which case all options are set to default. + * @return : number of bytes written into `dstBuffer` (it can be zero, meaning input data was just buffered). + * or an error code if it fails (which can be tested using LZ4F_isError()) + */ +LZ4FLIB_STATIC_API size_t +LZ4F_uncompressedUpdate(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const void* srcBuffer, size_t srcSize, + const LZ4F_compressOptions_t* cOptPtr); /********************************** * Bulk processing dictionary API @@ -583,12 +626,12 @@ LZ4FLIB_STATIC_API void LZ4F_freeCDict(LZ4F_CDict* CDict); * but it's not recommended, as it's the only way to provide dictID in the frame header. * @return : number of bytes written into dstBuffer. * or an error code if it fails (can be tested using LZ4F_isError()) */ -LZ4FLIB_STATIC_API size_t LZ4F_compressFrame_usingCDict( - LZ4F_cctx* cctx, - void* dst, size_t dstCapacity, - const void* src, size_t srcSize, - const LZ4F_CDict* cdict, - const LZ4F_preferences_t* preferencesPtr); +LZ4FLIB_STATIC_API size_t +LZ4F_compressFrame_usingCDict(LZ4F_cctx* cctx, + void* dst, size_t dstCapacity, + const void* src, size_t srcSize, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* preferencesPtr); /*! LZ4F_compressBegin_usingCDict() : @@ -598,23 +641,49 @@ LZ4FLIB_STATIC_API size_t LZ4F_compressFrame_usingCDict( * however, it's the only way to provide dictID in the frame header. * @return : number of bytes written into dstBuffer for the header, * or an error code (which can be tested using LZ4F_isError()) */ -LZ4FLIB_STATIC_API size_t LZ4F_compressBegin_usingCDict( - LZ4F_cctx* cctx, - void* dstBuffer, size_t dstCapacity, - const LZ4F_CDict* cdict, - const LZ4F_preferences_t* prefsPtr); +LZ4FLIB_STATIC_API size_t +LZ4F_compressBegin_usingCDict(LZ4F_cctx* cctx, + void* dstBuffer, size_t dstCapacity, + const LZ4F_CDict* cdict, + const LZ4F_preferences_t* prefsPtr); /*! LZ4F_decompress_usingDict() : * Same as LZ4F_decompress(), using a predefined dictionary. * Dictionary is used "in place", without any preprocessing. - * It must remain accessible throughout the entire frame decoding. */ -LZ4FLIB_STATIC_API size_t LZ4F_decompress_usingDict( - LZ4F_dctx* dctxPtr, - void* dstBuffer, size_t* dstSizePtr, - const void* srcBuffer, size_t* srcSizePtr, - const void* dict, size_t dictSize, - const LZ4F_decompressOptions_t* decompressOptionsPtr); +** It must remain accessible throughout the entire frame decoding. */ +LZ4FLIB_STATIC_API size_t +LZ4F_decompress_usingDict(LZ4F_dctx* dctxPtr, + void* dstBuffer, size_t* dstSizePtr, + const void* srcBuffer, size_t* srcSizePtr, + const void* dict, size_t dictSize, + const LZ4F_decompressOptions_t* decompressOptionsPtr); + + +/*! Custom memory allocation : + * These prototypes make it possible to pass custom allocation/free functions. + * LZ4F_customMem is provided at state creation time, using LZ4F_create*_advanced() listed below. + * All allocation/free operations will be completed using these custom variants instead of regular ones. + */ +typedef void* (*LZ4F_AllocFunction) (void* opaqueState, size_t size); +typedef void* (*LZ4F_CallocFunction) (void* opaqueState, size_t size); +typedef void (*LZ4F_FreeFunction) (void* opaqueState, void* address); +typedef struct { + LZ4F_AllocFunction customAlloc; + LZ4F_CallocFunction customCalloc; /* optional; when not defined, uses customAlloc + memset */ + LZ4F_FreeFunction customFree; + void* opaqueState; +} LZ4F_CustomMem; +static +#ifdef __GNUC__ +__attribute__((__unused__)) +#endif +LZ4F_CustomMem const LZ4F_defaultCMem = { NULL, NULL, NULL, NULL }; /**< this constant defers to stdlib's functions */ + +LZ4FLIB_STATIC_API LZ4F_cctx* LZ4F_createCompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version); +LZ4FLIB_STATIC_API LZ4F_dctx* LZ4F_createDecompressionContext_advanced(LZ4F_CustomMem customMem, unsigned version); +LZ4FLIB_STATIC_API LZ4F_CDict* LZ4F_createCDict_advanced(LZ4F_CustomMem customMem, const void* dictBuffer, size_t dictSize); + #if defined (__cplusplus) } diff --git a/mfbt/lz4/lz4frame_static.h b/mfbt/lz4/lz4frame_static.h index 925a2c5c330cd..2b44a63155be3 100644 --- a/mfbt/lz4/lz4frame_static.h +++ b/mfbt/lz4/lz4frame_static.h @@ -1,7 +1,7 @@ /* LZ4 auto-framing library Header File for static linking only - Copyright (C) 2011-2016, Yann Collet. + Copyright (C) 2011-2020, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) diff --git a/mfbt/lz4/lz4hc.c b/mfbt/lz4/lz4hc.c index a556d47920e78..b21ad6bb59943 100644 --- a/mfbt/lz4/lz4hc.c +++ b/mfbt/lz4/lz4hc.c @@ -1,6 +1,6 @@ /* LZ4 HC - High Compression Mode of LZ4 - Copyright (C) 2011-2017, Yann Collet. + Copyright (C) 2011-2020, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) @@ -42,7 +42,7 @@ * Select how default compression function will allocate workplace memory, * in stack (0:fastest), or in heap (1:requires malloc()). * Since workplace is rather large, heap mode is recommended. - */ +**/ #ifndef LZ4HC_HEAPMODE # define LZ4HC_HEAPMODE 1 #endif @@ -99,18 +99,20 @@ static void LZ4HC_clearTables (LZ4HC_CCtx_internal* hc4) static void LZ4HC_init_internal (LZ4HC_CCtx_internal* hc4, const BYTE* start) { - uptrval startingOffset = (uptrval)(hc4->end - hc4->base); - if (startingOffset > 1 GB) { + size_t const bufferSize = (size_t)(hc4->end - hc4->prefixStart); + size_t newStartingOffset = bufferSize + hc4->dictLimit; + assert(newStartingOffset >= bufferSize); /* check overflow */ + if (newStartingOffset > 1 GB) { LZ4HC_clearTables(hc4); - startingOffset = 0; + newStartingOffset = 0; } - startingOffset += 64 KB; - hc4->nextToUpdate = (U32) startingOffset; - hc4->base = start - startingOffset; + newStartingOffset += 64 KB; + hc4->nextToUpdate = (U32)newStartingOffset; + hc4->prefixStart = start; hc4->end = start; - hc4->dictBase = start - startingOffset; - hc4->dictLimit = (U32) startingOffset; - hc4->lowLimit = (U32) startingOffset; + hc4->dictStart = start; + hc4->dictLimit = (U32)newStartingOffset; + hc4->lowLimit = (U32)newStartingOffset; } @@ -119,12 +121,15 @@ LZ4_FORCE_INLINE void LZ4HC_Insert (LZ4HC_CCtx_internal* hc4, const BYTE* ip) { U16* const chainTable = hc4->chainTable; U32* const hashTable = hc4->hashTable; - const BYTE* const base = hc4->base; - U32 const target = (U32)(ip - base); + const BYTE* const prefixPtr = hc4->prefixStart; + U32 const prefixIdx = hc4->dictLimit; + U32 const target = (U32)(ip - prefixPtr) + prefixIdx; U32 idx = hc4->nextToUpdate; + assert(ip >= prefixPtr); + assert(target >= prefixIdx); while (idx < target) { - U32 const h = LZ4HC_hashPtr(base+idx); + U32 const h = LZ4HC_hashPtr(prefixPtr+idx-prefixIdx); size_t delta = idx - hashTable[h]; if (delta>LZ4_DISTANCE_MAX) delta = LZ4_DISTANCE_MAX; DELTANEXTU16(chainTable, idx) = (U16)delta; @@ -193,15 +198,14 @@ LZ4HC_countPattern(const BYTE* ip, const BYTE* const iEnd, U32 const pattern32) BYTE const byte = (BYTE)(pattern >> bitOffset); if (*ip != byte) break; ip ++; bitOffset -= 8; - } - } + } } return (unsigned)(ip - iStart); } /* LZ4HC_reverseCountPattern() : * pattern must be a sample of repetitive pattern of length 1, 2 or 4 (but not 3!) - * read using natural platform endianess */ + * read using natural platform endianness */ static unsigned LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) { @@ -211,7 +215,7 @@ LZ4HC_reverseCountPattern(const BYTE* ip, const BYTE* const iLow, U32 pattern) if (LZ4_read32(ip-4) != pattern) break; ip -= 4; } - { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianess */ + { const BYTE* bytePtr = (const BYTE*)(&pattern) + 3; /* works for any endianness */ while (likely(ip>iLow)) { if (ip[-1] != *bytePtr) break; ip--; bytePtr--; @@ -234,28 +238,28 @@ typedef enum { favorCompressionRatio=0, favorDecompressionSpeed } HCfavor_e; LZ4_FORCE_INLINE int LZ4HC_InsertAndGetWiderMatch ( - LZ4HC_CCtx_internal* hc4, - const BYTE* const ip, - const BYTE* const iLowLimit, - const BYTE* const iHighLimit, - int longest, - const BYTE** matchpos, - const BYTE** startpos, - const int maxNbAttempts, - const int patternAnalysis, - const int chainSwap, - const dictCtx_directive dict, - const HCfavor_e favorDecSpeed) + LZ4HC_CCtx_internal* const hc4, + const BYTE* const ip, + const BYTE* const iLowLimit, const BYTE* const iHighLimit, + int longest, + const BYTE** matchpos, + const BYTE** startpos, + const int maxNbAttempts, + const int patternAnalysis, const int chainSwap, + const dictCtx_directive dict, + const HCfavor_e favorDecSpeed) { U16* const chainTable = hc4->chainTable; U32* const HashTable = hc4->hashTable; const LZ4HC_CCtx_internal * const dictCtx = hc4->dictCtx; - const BYTE* const base = hc4->base; - const U32 dictLimit = hc4->dictLimit; - const BYTE* const lowPrefixPtr = base + dictLimit; - const U32 ipIndex = (U32)(ip - base); - const U32 lowestMatchIndex = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX; - const BYTE* const dictBase = hc4->dictBase; + const BYTE* const prefixPtr = hc4->prefixStart; + const U32 prefixIdx = hc4->dictLimit; + const U32 ipIndex = (U32)(ip - prefixPtr) + prefixIdx; + const int withinStartDistance = (hc4->lowLimit + (LZ4_DISTANCE_MAX + 1) > ipIndex); + const U32 lowestMatchIndex = (withinStartDistance) ? hc4->lowLimit : ipIndex - LZ4_DISTANCE_MAX; + const BYTE* const dictStart = hc4->dictStart; + const U32 dictIdx = hc4->lowLimit; + const BYTE* const dictEnd = dictStart + prefixIdx - dictIdx; int const lookBackLength = (int)(ip-iLowLimit); int nbAttempts = maxNbAttempts; U32 matchChainPos = 0; @@ -277,14 +281,13 @@ LZ4HC_InsertAndGetWiderMatch ( assert(matchIndex < ipIndex); if (favorDecSpeed && (ipIndex - matchIndex < 8)) { /* do nothing */ - } else if (matchIndex >= dictLimit) { /* within current Prefix */ - const BYTE* const matchPtr = base + matchIndex; - assert(matchPtr >= lowPrefixPtr); + } else if (matchIndex >= prefixIdx) { /* within current Prefix */ + const BYTE* const matchPtr = prefixPtr + matchIndex - prefixIdx; assert(matchPtr < ip); assert(longest >= 1); if (LZ4_read16(iLowLimit + longest - 1) == LZ4_read16(matchPtr - lookBackLength + longest - 1)) { if (LZ4_read32(matchPtr) == pattern) { - int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, lowPrefixPtr) : 0; + int const back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, prefixPtr) : 0; matchLength = MINMATCH + (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, iHighLimit); matchLength -= back; if (matchLength > longest) { @@ -293,24 +296,25 @@ LZ4HC_InsertAndGetWiderMatch ( *startpos = ip + back; } } } } else { /* lowestMatchIndex <= matchIndex < dictLimit */ - const BYTE* const matchPtr = dictBase + matchIndex; - if (LZ4_read32(matchPtr) == pattern) { - const BYTE* const dictStart = dictBase + hc4->lowLimit; + const BYTE* const matchPtr = dictStart + (matchIndex - dictIdx); + assert(matchIndex >= dictIdx); + if ( likely(matchIndex <= prefixIdx - 4) + && (LZ4_read32(matchPtr) == pattern) ) { int back = 0; - const BYTE* vLimit = ip + (dictLimit - matchIndex); + const BYTE* vLimit = ip + (prefixIdx - matchIndex); if (vLimit > iHighLimit) vLimit = iHighLimit; matchLength = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; if ((ip+matchLength == vLimit) && (vLimit < iHighLimit)) - matchLength += LZ4_count(ip+matchLength, lowPrefixPtr, iHighLimit); + matchLength += LZ4_count(ip+matchLength, prefixPtr, iHighLimit); back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictStart) : 0; matchLength -= back; if (matchLength > longest) { longest = matchLength; - *matchpos = base + matchIndex + back; /* virtual pos, relative to ip, to retrieve offset */ + *matchpos = prefixPtr - prefixIdx + matchIndex + back; /* virtual pos, relative to ip, to retrieve offset */ *startpos = ip + back; } } } - if (chainSwap && matchLength==longest) { /* better match => select a better chain */ + if (chainSwap && matchLength==longest) { /* better match => select a better chain */ assert(lookBackLength==0); /* search forward only */ if (matchIndex + (U32)longest <= ipIndex) { int const kTrigger = 4; @@ -326,8 +330,7 @@ LZ4HC_InsertAndGetWiderMatch ( distanceToNextMatch = candidateDist; matchChainPos = (U32)pos; accel = 1 << kTrigger; - } - } + } } if (distanceToNextMatch > 1) { if (distanceToNextMatch > matchIndex) break; /* avoid overflow */ matchIndex -= distanceToNextMatch; @@ -347,23 +350,24 @@ LZ4HC_InsertAndGetWiderMatch ( repeat = rep_not; } } if ( (repeat == rep_confirmed) && (matchCandidateIdx >= lowestMatchIndex) - && LZ4HC_protectDictEnd(dictLimit, matchCandidateIdx) ) { - const int extDict = matchCandidateIdx < dictLimit; - const BYTE* const matchPtr = (extDict ? dictBase : base) + matchCandidateIdx; + && LZ4HC_protectDictEnd(prefixIdx, matchCandidateIdx) ) { + const int extDict = matchCandidateIdx < prefixIdx; + const BYTE* const matchPtr = (extDict ? dictStart - dictIdx : prefixPtr - prefixIdx) + matchCandidateIdx; if (LZ4_read32(matchPtr) == pattern) { /* good candidate */ - const BYTE* const dictStart = dictBase + hc4->lowLimit; - const BYTE* const iLimit = extDict ? dictBase + dictLimit : iHighLimit; + const BYTE* const iLimit = extDict ? dictEnd : iHighLimit; size_t forwardPatternLength = LZ4HC_countPattern(matchPtr+sizeof(pattern), iLimit, pattern) + sizeof(pattern); if (extDict && matchPtr + forwardPatternLength == iLimit) { U32 const rotatedPattern = LZ4HC_rotatePattern(forwardPatternLength, pattern); - forwardPatternLength += LZ4HC_countPattern(lowPrefixPtr, iHighLimit, rotatedPattern); + forwardPatternLength += LZ4HC_countPattern(prefixPtr, iHighLimit, rotatedPattern); } - { const BYTE* const lowestMatchPtr = extDict ? dictStart : lowPrefixPtr; + { const BYTE* const lowestMatchPtr = extDict ? dictStart : prefixPtr; size_t backLength = LZ4HC_reverseCountPattern(matchPtr, lowestMatchPtr, pattern); size_t currentSegmentLength; - if (!extDict && matchPtr - backLength == lowPrefixPtr && hc4->lowLimit < dictLimit) { + if (!extDict + && matchPtr - backLength == prefixPtr + && dictIdx < prefixIdx) { U32 const rotatedPattern = LZ4HC_rotatePattern((U32)(-(int)backLength), pattern); - backLength += LZ4HC_reverseCountPattern(dictBase + dictLimit, dictStart, rotatedPattern); + backLength += LZ4HC_reverseCountPattern(dictEnd, dictStart, rotatedPattern); } /* Limit backLength not go further than lowestMatchIndex */ backLength = matchCandidateIdx - MAX(matchCandidateIdx - (U32)backLength, lowestMatchIndex); @@ -373,28 +377,28 @@ LZ4HC_InsertAndGetWiderMatch ( if ( (currentSegmentLength >= srcPatternLength) /* current pattern segment large enough to contain full srcPatternLength */ && (forwardPatternLength <= srcPatternLength) ) { /* haven't reached this position yet */ U32 const newMatchIndex = matchCandidateIdx + (U32)forwardPatternLength - (U32)srcPatternLength; /* best position, full pattern, might be followed by more match */ - if (LZ4HC_protectDictEnd(dictLimit, newMatchIndex)) + if (LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) matchIndex = newMatchIndex; else { /* Can only happen if started in the prefix */ - assert(newMatchIndex >= dictLimit - 3 && newMatchIndex < dictLimit && !extDict); - matchIndex = dictLimit; + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; } } else { U32 const newMatchIndex = matchCandidateIdx - (U32)backLength; /* farthest position in current segment, will find a match of length currentSegmentLength + maybe some back */ - if (!LZ4HC_protectDictEnd(dictLimit, newMatchIndex)) { - assert(newMatchIndex >= dictLimit - 3 && newMatchIndex < dictLimit && !extDict); - matchIndex = dictLimit; + if (!LZ4HC_protectDictEnd(prefixIdx, newMatchIndex)) { + assert(newMatchIndex >= prefixIdx - 3 && newMatchIndex < prefixIdx && !extDict); + matchIndex = prefixIdx; } else { matchIndex = newMatchIndex; if (lookBackLength==0) { /* no back possible */ size_t const maxML = MIN(currentSegmentLength, srcPatternLength); if ((size_t)longest < maxML) { - assert(base + matchIndex != ip); - if ((size_t)(ip - base) - matchIndex > LZ4_DISTANCE_MAX) break; + assert(prefixPtr - prefixIdx + matchIndex != ip); + if ((size_t)(ip - prefixPtr) + prefixIdx - matchIndex > LZ4_DISTANCE_MAX) break; assert(maxML < 2 GB); longest = (int)maxML; - *matchpos = base + matchIndex; /* virtual pos, relative to ip, to retrieve offset */ + *matchpos = prefixPtr - prefixIdx + matchIndex; /* virtual pos, relative to ip, to retrieve offset */ *startpos = ip; } { U32 const distToNextPattern = DELTANEXTU16(chainTable, matchIndex); @@ -413,12 +417,12 @@ LZ4HC_InsertAndGetWiderMatch ( if ( dict == usingDictCtxHc && nbAttempts > 0 && ipIndex - lowestMatchIndex < LZ4_DISTANCE_MAX) { - size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->base); + size_t const dictEndOffset = (size_t)(dictCtx->end - dictCtx->prefixStart) + dictCtx->dictLimit; U32 dictMatchIndex = dictCtx->hashTable[LZ4HC_hashPtr(ip)]; assert(dictEndOffset <= 1 GB); matchIndex = dictMatchIndex + lowestMatchIndex - (U32)dictEndOffset; while (ipIndex - matchIndex <= LZ4_DISTANCE_MAX && nbAttempts--) { - const BYTE* const matchPtr = dictCtx->base + dictMatchIndex; + const BYTE* const matchPtr = dictCtx->prefixStart - dictCtx->dictLimit + dictMatchIndex; if (LZ4_read32(matchPtr) == pattern) { int mlt; @@ -426,11 +430,11 @@ LZ4HC_InsertAndGetWiderMatch ( const BYTE* vLimit = ip + (dictEndOffset - dictMatchIndex); if (vLimit > iHighLimit) vLimit = iHighLimit; mlt = (int)LZ4_count(ip+MINMATCH, matchPtr+MINMATCH, vLimit) + MINMATCH; - back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->base + dictCtx->dictLimit) : 0; + back = lookBackLength ? LZ4HC_countBack(ip, matchPtr, iLowLimit, dictCtx->prefixStart) : 0; mlt -= back; if (mlt > longest) { longest = mlt; - *matchpos = base + matchIndex + back; + *matchpos = prefixPtr - prefixIdx + matchIndex + back; *startpos = ip + back; } } @@ -442,13 +446,13 @@ LZ4HC_InsertAndGetWiderMatch ( return longest; } -LZ4_FORCE_INLINE -int LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ - const BYTE* const ip, const BYTE* const iLimit, - const BYTE** matchpos, - const int maxNbAttempts, - const int patternAnalysis, - const dictCtx_directive dict) +LZ4_FORCE_INLINE int +LZ4HC_InsertAndFindBestMatch(LZ4HC_CCtx_internal* const hc4, /* Index table will be updated */ + const BYTE* const ip, const BYTE* const iLimit, + const BYTE** matchpos, + const int maxNbAttempts, + const int patternAnalysis, + const dictCtx_directive dict) { const BYTE* uselessPtr = ip; /* note : LZ4HC_InsertAndGetWiderMatch() is able to modify the starting position of a match (*startpos), @@ -751,7 +755,7 @@ LZ4_FORCE_INLINE int LZ4HC_compress_hashChain ( } else { *op++ = (BYTE)(lastRunSize << ML_BITS); } - memcpy(op, anchor, lastRunSize); + LZ4_memcpy(op, anchor, lastRunSize); op += lastRunSize; } @@ -884,13 +888,13 @@ LZ4HC_compress_generic_dictCtx ( limitedOutput_directive limit ) { - const size_t position = (size_t)(ctx->end - ctx->base) - ctx->lowLimit; + const size_t position = (size_t)(ctx->end - ctx->prefixStart) + (ctx->dictLimit - ctx->lowLimit); assert(ctx->dictCtx != NULL); if (position >= 64 KB) { ctx->dictCtx = NULL; return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); } else if (position == 0 && *srcSizePtr > 4 KB) { - memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal)); + LZ4_memcpy(ctx, ctx->dictCtx, sizeof(LZ4HC_CCtx_internal)); LZ4HC_setExternalDict(ctx, (const BYTE *)src); ctx->compressionLevel = (short)cLevel; return LZ4HC_compress_generic_noDictCtx(ctx, src, dst, srcSizePtr, dstCapacity, cLevel, limit); @@ -953,13 +957,15 @@ int LZ4_compress_HC_extStateHC (void* state, const char* src, char* dst, int src int LZ4_compress_HC(const char* src, char* dst, int srcSize, int dstCapacity, int compressionLevel) { + int cSize; #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 LZ4_streamHC_t* const statePtr = (LZ4_streamHC_t*)ALLOC(sizeof(LZ4_streamHC_t)); + if (statePtr==NULL) return 0; #else LZ4_streamHC_t state; LZ4_streamHC_t* const statePtr = &state; #endif - int const cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); + cSize = LZ4_compress_HC_extStateHC(statePtr, src, dst, srcSize, dstCapacity, compressionLevel); #if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 FREEMEM(statePtr); #endif @@ -982,6 +988,7 @@ int LZ4_compress_HC_destSize(void* state, const char* source, char* dest, int* s * Streaming Functions **************************************/ /* allocation */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_streamHC_t* LZ4_createStreamHC(void) { LZ4_streamHC_t* const state = @@ -998,13 +1005,12 @@ int LZ4_freeStreamHC (LZ4_streamHC_t* LZ4_streamHCPtr) FREEMEM(LZ4_streamHCPtr); return 0; } +#endif LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size) { LZ4_streamHC_t* const LZ4_streamHCPtr = (LZ4_streamHC_t*)buffer; - /* if compilation fails here, LZ4_STREAMHCSIZE must be increased */ - LZ4_STATIC_ASSERT(sizeof(LZ4HC_CCtx_internal) <= LZ4_STREAMHCSIZE); DEBUGLOG(4, "LZ4_initStreamHC(%p, %u)", buffer, (unsigned)size); /* check conditions */ if (buffer == NULL) return NULL; @@ -1030,13 +1036,13 @@ void LZ4_resetStreamHC_fast (LZ4_streamHC_t* LZ4_streamHCPtr, int compressionLev if (LZ4_streamHCPtr->internal_donotuse.dirty) { LZ4_initStreamHC(LZ4_streamHCPtr, sizeof(*LZ4_streamHCPtr)); } else { - /* preserve end - base : can trigger clearTable's threshold */ + /* preserve end - prefixStart : can trigger clearTable's threshold */ if (LZ4_streamHCPtr->internal_donotuse.end != NULL) { - LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.base; + LZ4_streamHCPtr->internal_donotuse.end -= (uptrval)LZ4_streamHCPtr->internal_donotuse.prefixStart; } else { - assert(LZ4_streamHCPtr->internal_donotuse.base == NULL); + assert(LZ4_streamHCPtr->internal_donotuse.prefixStart == NULL); } - LZ4_streamHCPtr->internal_donotuse.base = NULL; + LZ4_streamHCPtr->internal_donotuse.prefixStart = NULL; LZ4_streamHCPtr->internal_donotuse.dictCtx = NULL; } LZ4_setCompressionLevel(LZ4_streamHCPtr, compressionLevel); @@ -1087,14 +1093,14 @@ void LZ4_attach_HC_dictionary(LZ4_streamHC_t *working_stream, const LZ4_streamHC static void LZ4HC_setExternalDict(LZ4HC_CCtx_internal* ctxPtr, const BYTE* newBlock) { DEBUGLOG(4, "LZ4HC_setExternalDict(%p, %p)", ctxPtr, newBlock); - if (ctxPtr->end >= ctxPtr->base + ctxPtr->dictLimit + 4) + if (ctxPtr->end >= ctxPtr->prefixStart + 4) LZ4HC_Insert (ctxPtr, ctxPtr->end-3); /* Referencing remaining dictionary content */ /* Only one memory segment for extDict, so any previous extDict is lost at this stage */ ctxPtr->lowLimit = ctxPtr->dictLimit; - ctxPtr->dictLimit = (U32)(ctxPtr->end - ctxPtr->base); - ctxPtr->dictBase = ctxPtr->base; - ctxPtr->base = newBlock - ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + ctxPtr->dictLimit += (U32)(ctxPtr->end - ctxPtr->prefixStart); + ctxPtr->prefixStart = newBlock; ctxPtr->end = newBlock; ctxPtr->nextToUpdate = ctxPtr->dictLimit; /* match referencing will resume from there */ @@ -1113,11 +1119,11 @@ LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, LZ4_streamHCPtr, src, *srcSizePtr, limit); assert(ctxPtr != NULL); /* auto-init if forgotten */ - if (ctxPtr->base == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src); + if (ctxPtr->prefixStart == NULL) LZ4HC_init_internal (ctxPtr, (const BYTE*) src); /* Check overflow */ - if ((size_t)(ctxPtr->end - ctxPtr->base) > 2 GB) { - size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->base) - ctxPtr->dictLimit; + if ((size_t)(ctxPtr->end - ctxPtr->prefixStart) + ctxPtr->dictLimit > 2 GB) { + size_t dictSize = (size_t)(ctxPtr->end - ctxPtr->prefixStart); if (dictSize > 64 KB) dictSize = 64 KB; LZ4_loadDictHC(LZ4_streamHCPtr, (const char*)(ctxPtr->end) - dictSize, (int)dictSize); } @@ -1128,13 +1134,16 @@ LZ4_compressHC_continue_generic (LZ4_streamHC_t* LZ4_streamHCPtr, /* Check overlapping input/dictionary space */ { const BYTE* sourceEnd = (const BYTE*) src + *srcSizePtr; - const BYTE* const dictBegin = ctxPtr->dictBase + ctxPtr->lowLimit; - const BYTE* const dictEnd = ctxPtr->dictBase + ctxPtr->dictLimit; + const BYTE* const dictBegin = ctxPtr->dictStart; + const BYTE* const dictEnd = ctxPtr->dictStart + (ctxPtr->dictLimit - ctxPtr->lowLimit); if ((sourceEnd > dictBegin) && ((const BYTE*)src < dictEnd)) { if (sourceEnd > dictEnd) sourceEnd = dictEnd; - ctxPtr->lowLimit = (U32)(sourceEnd - ctxPtr->dictBase); - if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) ctxPtr->lowLimit = ctxPtr->dictLimit; - } } + ctxPtr->lowLimit += (U32)(sourceEnd - ctxPtr->dictStart); + ctxPtr->dictStart += (U32)(sourceEnd - ctxPtr->dictStart); + if (ctxPtr->dictLimit - ctxPtr->lowLimit < 4) { + ctxPtr->lowLimit = ctxPtr->dictLimit; + ctxPtr->dictStart = ctxPtr->prefixStart; + } } } return LZ4HC_compress_generic (ctxPtr, src, dst, srcSizePtr, dstCapacity, ctxPtr->compressionLevel, limit); } @@ -1162,7 +1171,7 @@ int LZ4_compress_HC_continue_destSize (LZ4_streamHC_t* LZ4_streamHCPtr, const ch int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictSize) { LZ4HC_CCtx_internal* const streamPtr = &LZ4_streamHCPtr->internal_donotuse; - int const prefixSize = (int)(streamPtr->end - (streamPtr->base + streamPtr->dictLimit)); + int const prefixSize = (int)(streamPtr->end - streamPtr->prefixStart); DEBUGLOG(5, "LZ4_saveDictHC(%p, %p, %d)", LZ4_streamHCPtr, safeBuffer, dictSize); assert(prefixSize >= 0); if (dictSize > 64 KB) dictSize = 64 KB; @@ -1170,12 +1179,13 @@ int LZ4_saveDictHC (LZ4_streamHC_t* LZ4_streamHCPtr, char* safeBuffer, int dictS if (dictSize > prefixSize) dictSize = prefixSize; if (safeBuffer == NULL) assert(dictSize == 0); if (dictSize > 0) - memmove(safeBuffer, streamPtr->end - dictSize, dictSize); - { U32 const endIndex = (U32)(streamPtr->end - streamPtr->base); + LZ4_memmove(safeBuffer, streamPtr->end - dictSize, dictSize); + { U32 const endIndex = (U32)(streamPtr->end - streamPtr->prefixStart) + streamPtr->dictLimit; streamPtr->end = (const BYTE*)safeBuffer + dictSize; - streamPtr->base = streamPtr->end - endIndex; + streamPtr->prefixStart = streamPtr->end - dictSize; streamPtr->dictLimit = endIndex - (U32)dictSize; streamPtr->lowLimit = endIndex - (U32)dictSize; + streamPtr->dictStart = streamPtr->prefixStart; if (streamPtr->nextToUpdate < streamPtr->dictLimit) streamPtr->nextToUpdate = streamPtr->dictLimit; } @@ -1203,7 +1213,7 @@ int LZ4_compressHC_limitedOutput_continue (LZ4_streamHC_t* ctx, const char* src, /* Deprecated streaming functions */ -int LZ4_sizeofStreamStateHC(void) { return LZ4_STREAMHCSIZE; } +int LZ4_sizeofStreamStateHC(void) { return sizeof(LZ4_streamHC_t); } /* state is presumed correctly sized, aka >= sizeof(LZ4_streamHC_t) * @return : 0 on success, !=0 if error */ @@ -1215,6 +1225,7 @@ int LZ4_resetStreamStateHC(void* state, char* inputBuffer) return 0; } +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) void* LZ4_createHC (const char* inputBuffer) { LZ4_streamHC_t* const hc4 = LZ4_createStreamHC(); @@ -1229,6 +1240,7 @@ int LZ4_freeHC (void* LZ4HC_Data) FREEMEM(LZ4HC_Data); return 0; } +#endif int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* src, char* dst, int srcSize, int cLevel) { @@ -1242,11 +1254,11 @@ int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* src, c char* LZ4_slideInputBufferHC(void* LZ4HC_Data) { - LZ4_streamHC_t *ctx = (LZ4_streamHC_t*)LZ4HC_Data; - const BYTE *bufferStart = ctx->internal_donotuse.base + ctx->internal_donotuse.lowLimit; + LZ4_streamHC_t* const ctx = (LZ4_streamHC_t*)LZ4HC_Data; + const BYTE* bufferStart = ctx->internal_donotuse.prefixStart - ctx->internal_donotuse.dictLimit + ctx->internal_donotuse.lowLimit; LZ4_resetStreamHC_fast(ctx, ctx->internal_donotuse.compressionLevel); /* avoid const char * -> char * conversion warning :( */ - return (char *)(uptrval)bufferStart; + return (char*)(uptrval)bufferStart; } @@ -1329,7 +1341,7 @@ static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, { int retval = 0; #define TRAILING_LITERALS 3 -#ifdef LZ4HC_HEAPMODE +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 LZ4HC_optimal_t* const opt = (LZ4HC_optimal_t*)ALLOC(sizeof(LZ4HC_optimal_t) * (LZ4_OPT_NUM + TRAILING_LITERALS)); #else LZ4HC_optimal_t opt[LZ4_OPT_NUM + TRAILING_LITERALS]; /* ~64 KB, which is a bit large for stack... */ @@ -1347,7 +1359,7 @@ static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, const BYTE* ovref = NULL; /* init */ -#ifdef LZ4HC_HEAPMODE +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 if (opt == NULL) goto _return_label; #endif DEBUGLOG(5, "LZ4HC_compress_optimal(dst=%p, dstCapa=%u)", dst, (unsigned)dstCapacity); @@ -1579,7 +1591,7 @@ static int LZ4HC_compress_optimal ( LZ4HC_CCtx_internal* ctx, } else { *op++ = (BYTE)(lastRunSize << ML_BITS); } - memcpy(op, anchor, lastRunSize); + LZ4_memcpy(op, anchor, lastRunSize); op += lastRunSize; } @@ -1612,7 +1624,7 @@ if (limit == fillOutput) { goto _last_literals; } _return_label: -#ifdef LZ4HC_HEAPMODE +#if defined(LZ4HC_HEAPMODE) && LZ4HC_HEAPMODE==1 FREEMEM(opt); #endif return retval; diff --git a/mfbt/lz4/lz4hc.h b/mfbt/lz4/lz4hc.h index 3d441fb6fa96e..e937acfefd857 100644 --- a/mfbt/lz4/lz4hc.h +++ b/mfbt/lz4/lz4hc.h @@ -1,7 +1,7 @@ /* LZ4 HC - High Compression Mode of LZ4 Header File - Copyright (C) 2011-2017, Yann Collet. + Copyright (C) 2011-2020, Yann Collet. BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) Redistribution and use in source and binary forms, with or without @@ -198,14 +198,17 @@ LZ4LIB_API int LZ4_saveDictHC (LZ4_streamHC_t* streamHCPtr, char* safeBuffer, in #define LZ4HC_HASH_MASK (LZ4HC_HASHTABLESIZE - 1) +/* Never ever use these definitions directly ! + * Declare or allocate an LZ4_streamHC_t instead. +**/ typedef struct LZ4HC_CCtx_internal LZ4HC_CCtx_internal; struct LZ4HC_CCtx_internal { LZ4_u32 hashTable[LZ4HC_HASHTABLESIZE]; LZ4_u16 chainTable[LZ4HC_MAXD]; const LZ4_byte* end; /* next block here to continue on current prefix */ - const LZ4_byte* base; /* All index relative to this position */ - const LZ4_byte* dictBase; /* alternate base for extDict */ + const LZ4_byte* prefixStart; /* Indexes relative to this position */ + const LZ4_byte* dictStart; /* alternate reference for extDict */ LZ4_u32 dictLimit; /* below that point, need extDict */ LZ4_u32 lowLimit; /* below that point, no more dict */ LZ4_u32 nextToUpdate; /* index from which to continue dictionary update */ @@ -216,20 +219,15 @@ struct LZ4HC_CCtx_internal const LZ4HC_CCtx_internal* dictCtx; }; - -/* Do not use these definitions directly ! - * Declare or allocate an LZ4_streamHC_t instead. - */ -#define LZ4_STREAMHCSIZE 262200 /* static size, for inter-version compatibility */ -#define LZ4_STREAMHCSIZE_VOIDP (LZ4_STREAMHCSIZE / sizeof(void*)) +#define LZ4_STREAMHC_MINSIZE 262200 /* static size, for inter-version compatibility */ union LZ4_streamHC_u { - void* table[LZ4_STREAMHCSIZE_VOIDP]; + char minStateSize[LZ4_STREAMHC_MINSIZE]; LZ4HC_CCtx_internal internal_donotuse; }; /* previously typedef'd to LZ4_streamHC_t */ /* LZ4_streamHC_t : * This structure allows static allocation of LZ4 HC streaming state. - * This can be used to allocate statically, on state, or as part of a larger structure. + * This can be used to allocate statically on stack, or as part of a larger structure. * * Such state **must** be initialized using LZ4_initStreamHC() before first use. * @@ -244,7 +242,7 @@ union LZ4_streamHC_u { * Required before first use of a statically allocated LZ4_streamHC_t. * Before v1.9.0 : use LZ4_resetStreamHC() instead */ -LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC (void* buffer, size_t size); +LZ4LIB_API LZ4_streamHC_t* LZ4_initStreamHC(void* buffer, size_t size); /*-************************************ @@ -272,9 +270,11 @@ LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_comp * LZ4_slideInputBufferHC() will truncate the history of the stream, rather * than preserve a window-sized chunk of history. */ +#if !defined(LZ4_STATIC_LINKING_ONLY_DISABLE_MEMORY_ALLOCATION) LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API void* LZ4_createHC (const char* inputBuffer); -LZ4_DEPRECATED("use LZ4_saveDictHC() instead") LZ4LIB_API char* LZ4_slideInputBufferHC (void* LZ4HC_Data); LZ4_DEPRECATED("use LZ4_freeStreamHC() instead") LZ4LIB_API int LZ4_freeHC (void* LZ4HC_Data); +#endif +LZ4_DEPRECATED("use LZ4_saveDictHC() instead") LZ4LIB_API char* LZ4_slideInputBufferHC (void* LZ4HC_Data); LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int compressionLevel); LZ4_DEPRECATED("use LZ4_compress_HC_continue() instead") LZ4LIB_API int LZ4_compressHC2_limitedOutput_continue (void* LZ4HC_Data, const char* source, char* dest, int inputSize, int maxOutputSize, int compressionLevel); LZ4_DEPRECATED("use LZ4_createStreamHC() instead") LZ4LIB_API int LZ4_sizeofStreamStateHC(void); @@ -305,7 +305,7 @@ LZ4LIB_API void LZ4_resetStreamHC (LZ4_streamHC_t* streamHCPtr, int compressionL * They should not be linked from DLL, * as there is no guarantee of API stability yet. * Prototypes will be promoted to "stable" status - * after successfull usage in real-life scenarios. + * after successful usage in real-life scenarios. ***************************************************/ #ifdef LZ4_HC_STATIC_LINKING_ONLY /* protection macro */ #ifndef LZ4_HC_SLO_098092834 diff --git a/mfbt/moz.build b/mfbt/moz.build index a04cbc0c5fb67..e77b5e5d366b8 100644 --- a/mfbt/moz.build +++ b/mfbt/moz.build @@ -192,6 +192,7 @@ DEFINES["IMPL_MFBT"] = True SOURCES += [ "lz4/lz4.c", + "lz4/lz4file.c", "lz4/lz4frame.c", "lz4/lz4hc.c", "lz4/xxhash.c", From 06712ca49723d468313b1f0c607b5a0a98a5f183 Mon Sep 17 00:00:00 2001 From: Imanol Fernandez Date: Wed, 31 Aug 2022 16:13:13 +0000 Subject: [PATCH 63/65] Bug 1788188 - Implement missing framerate methods in the WebXR spec. r=emilio In particular this adds an initial implementation for the updateTargetFrameRate method and adds the supportedFrameRates and frameRate attributes to the XRSession. Differential Revision: https://phabricator.services.mozilla.com/D156063 --- dom/vr/XRSession.cpp | 32 ++++++++++++++++++++++++++++++++ dom/vr/XRSession.h | 5 +++++ dom/webidl/WebXR.webidl | 4 ++++ 3 files changed, 41 insertions(+) diff --git a/dom/vr/XRSession.cpp b/dom/vr/XRSession.cpp index 081b8bcd95173..90789de9dbd97 100644 --- a/dom/vr/XRSession.cpp +++ b/dom/vr/XRSession.cpp @@ -231,6 +231,13 @@ XRRenderState* XRSession::RenderState() { return mActiveRenderState; } XRInputSourceArray* XRSession::InputSources() { return mInputSources; } +Nullable XRSession::GetFrameRate() { return {}; } + +void XRSession::GetSupportedFrameRates(JSContext*, + JS::MutableHandle aRetVal) { + aRetVal.set(nullptr); +} + // https://immersive-web.github.io/webxr/#apply-the-pending-render-state void XRSession::ApplyPendingRenderState() { if (mPendingRenderState == nullptr) { @@ -393,6 +400,31 @@ already_AddRefed XRSession::RequestReferenceSpace( return promise.forget(); } +already_AddRefed XRSession::UpdateTargetFrameRate(float aRate, + ErrorResult& aRv) { + nsCOMPtr global = GetParentObject(); + NS_ENSURE_TRUE(global, nullptr); + + RefPtr promise = Promise::Create(global, aRv); + NS_ENSURE_TRUE(!aRv.Failed(), nullptr); + + if (mEnded) { + promise->MaybeRejectWithInvalidStateError( + "UpdateTargetFrameRate can not be called on an XRSession that has " + "ended."); + return promise.forget(); + } + + // https://immersive-web.github.io/webxr/#dom-xrsession-updatetargetframerate + // TODO: Validate the rate with the frame rates supported from the device. + // We add a no op for now to avoid JS exceptions related to undefined method. + // The spec states that user agent MAY use rate to calculate a new display + // frame rate, so it's fine to let the default frame rate for now. + + promise->MaybeResolve(JS::UndefinedHandleValue); + return promise.forget(); +} + XRRenderState* XRSession::GetActiveRenderState() const { return mActiveRenderState; } diff --git a/dom/vr/XRSession.h b/dom/vr/XRSession.h index 7df8046be4078..9215058a1baba 100644 --- a/dom/vr/XRSession.h +++ b/dom/vr/XRSession.h @@ -67,6 +67,9 @@ class XRSession final : public DOMEventTargetHelper, public nsARefreshObserver { XRVisibilityState VisibilityState() const; XRRenderState* RenderState(); XRInputSourceArray* InputSources(); + Nullable GetFrameRate(); + void GetSupportedFrameRates(JSContext* aJSContext, + JS::MutableHandle aRetval); // WebIDL Methods void UpdateRenderState(const XRRenderStateInit& aNewState, ErrorResult& aRv); @@ -76,6 +79,8 @@ class XRSession final : public DOMEventTargetHelper, public nsARefreshObserver { mozilla::ErrorResult& aError); void CancelAnimationFrame(int32_t aHandle, mozilla::ErrorResult& aError); already_AddRefed End(ErrorResult& aRv); + already_AddRefed UpdateTargetFrameRate(float aRate, + ErrorResult& aRv); // WebIDL Events IMPL_EVENT_HANDLER(end); diff --git a/dom/webidl/WebXR.webidl b/dom/webidl/WebXR.webidl index f1f893b437d81..9437e30d1d582 100644 --- a/dom/webidl/WebXR.webidl +++ b/dom/webidl/WebXR.webidl @@ -43,12 +43,16 @@ interface XRSession : EventTarget { readonly attribute XRVisibilityState visibilityState; [SameObject] readonly attribute XRRenderState renderState; [SameObject] readonly attribute XRInputSourceArray inputSources; + readonly attribute float? frameRate; + readonly attribute Float32Array? supportedFrameRates; // Methods [Throws] void updateRenderState(optional XRRenderStateInit state = {}); [NewObject] Promise requestReferenceSpace(XRReferenceSpaceType type); + [NewObject] + Promise updateTargetFrameRate(float rate); [Throws] long requestAnimationFrame(XRFrameRequestCallback callback); From cf4e972ea5847d98470e486fa3f0e4a6d9ce43b2 Mon Sep 17 00:00:00 2001 From: Dana Keeler Date: Wed, 31 Aug 2022 16:26:19 +0000 Subject: [PATCH 64/65] Bug 1787995 - fix up missed changes in test_ech_grease.js for bug 1781104 r=necko-reviewers,valentin Differential Revision: https://phabricator.services.mozilla.com/D156009 --- netwerk/test/unit/test_ech_grease.js | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/netwerk/test/unit/test_ech_grease.js b/netwerk/test/unit/test_ech_grease.js index 446be9cb19591..8bd5d14d3957e 100644 --- a/netwerk/test/unit/test_ech_grease.js +++ b/netwerk/test/unit/test_ech_grease.js @@ -149,18 +149,7 @@ function storeCertOverride(port, cert) { let certOverrideService = Cc[ "@mozilla.org/security/certoverride;1" ].getService(Ci.nsICertOverrideService); - let overrideBits = - Ci.nsICertOverrideService.ERROR_UNTRUSTED | - Ci.nsICertOverrideService.ERROR_TIME | - Ci.nsICertOverrideService.ERROR_MISMATCH; - certOverrideService.rememberValidityOverride( - hostname, - port, - {}, - cert, - overrideBits, - true - ); + certOverrideService.rememberValidityOverride(hostname, port, {}, cert, true); } function startClient(port, useGREASE, beConservative) { From 088c86b28a01f22a953ed1451152f9c7d517d0cd Mon Sep 17 00:00:00 2001 From: Luca Greco Date: Wed, 31 Aug 2022 16:44:32 +0000 Subject: [PATCH 65/65] Bug 1775898 - Fix browser_doorhanger_install.js intermittent failures. r=mixedpuppy The failures triggered after the changes introduced in this test from Bug 1784292 and tracked by orangefactor are all triggered from the test case named `test_blockedInstallDomain_with_unified_extensions`: - in that test case we are using a new chrome window (because the test case covers the behaviors expected when the unified extension button have been enabled by flipping the related pref) and the new chrome window is closed shortly after calling BrowserTestUtils.openNewForegroundTab. - By opening and quickly closing a new chrome window, of all the tests in that test file, `test_blockedInstallDomain_with_unified_extensions` is the only one that have a pretty good chance to trigger this kind of failure. - the failure is triggered when the chrome window has been already closed by the time all the promises that BrowserTestUtils.openNewForegroundTab is internally waiting on (see the lines linked below) are settled, and that triggers the failure due to an uncaught rejection triggered as a side-effect of trying to access tabbrowser.ownerGlobal.windowGlobalChild for a chrome window that is already gone: - https://searchfox.org/mozilla-central/rev/380fc5571b039fd453b45bbb64ed13146fe9b066/testing/mochitest/BrowserTestUtils/BrowserTestUtils.jsm#278-279 The attached patch just adds an await on the call to BrowserTestUtils.openNewForegroundTab, which prevents the intermittent failure from being triggered. As a side note (either as an alternative approach to the one currently in this patch or indipendently from fixing this specific intermittent): it seems worth confirming if we could be changing BrowserTestUtils.openNewForegroundTab to expect the possibility that the chrome window have been closed and just omit the innerWindowId in the profile marker if collected too late to be able to retrieve the innerWindowId. Differential Revision: https://phabricator.services.mozilla.com/D155876 --- .../extensions/test/xpinstall/browser_doorhanger_installs.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/toolkit/mozapps/extensions/test/xpinstall/browser_doorhanger_installs.js b/toolkit/mozapps/extensions/test/xpinstall/browser_doorhanger_installs.js index 699ec10134501..5c2c9fafd8e6b 100644 --- a/toolkit/mozapps/extensions/test/xpinstall/browser_doorhanger_installs.js +++ b/toolkit/mozapps/extensions/test/xpinstall/browser_doorhanger_installs.js @@ -1488,7 +1488,7 @@ var TESTS = [ XPI: TESTROOT2 + "webmidi_permission.xpi", }) ); - BrowserTestUtils.openNewForegroundTab( + await BrowserTestUtils.openNewForegroundTab( win.gBrowser, TESTROOT + "installtrigger.html?" + triggers );