| // Copyright 2018 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "third_party/blink/renderer/modules/indexeddb/idb_factory.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "base/memory/ptr_util.h" |
| #include "base/memory/scoped_refptr.h" |
| #include "mojo/public/cpp/bindings/associated_remote.h" |
| #include "mojo/public/cpp/bindings/pending_associated_remote.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom-blink.h" |
| #include "third_party/blink/public/platform/web_security_origin.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_function.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_promise.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" |
| #include "third_party/blink/renderer/bindings/core/v8/script_value.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_core.h" |
| #include "third_party/blink/renderer/bindings/core/v8/v8_binding_for_testing.h" |
| #include "third_party/blink/renderer/core/testing/dummy_page_holder.h" |
| #include "third_party/blink/renderer/modules/indexeddb/idb_database_error.h" |
| #include "third_party/blink/renderer/modules/indexeddb/idb_name_and_version.h" |
| #include "third_party/blink/renderer/platform/bindings/exception_state.h" |
| #include "third_party/blink/renderer/platform/bindings/script_state.h" |
| #include "third_party/blink/renderer/platform/testing/testing_platform_support.h" |
| |
| namespace blink { |
| namespace { |
| |
| class TestHelperFunction : public ScriptFunction { |
| public: |
| static v8::Local<v8::Function> CreateFunction(ScriptState* script_state, |
| bool* called_flag) { |
| auto* self = |
| MakeGarbageCollected<TestHelperFunction>(script_state, called_flag); |
| return self->BindToV8Function(); |
| } |
| |
| TestHelperFunction(ScriptState* script_state, bool* called_flag) |
| : ScriptFunction(script_state), called_flag_(called_flag) {} |
| |
| private: |
| ScriptValue Call(ScriptValue value) override { |
| *called_flag_ = true; |
| return value; |
| } |
| |
| bool* called_flag_; |
| }; |
| |
| class BackendFactoryWithMockedDatabaseInfo : public mojom::blink::IDBFactory { |
| public: |
| explicit BackendFactoryWithMockedDatabaseInfo( |
| mojo::PendingReceiver<mojom::blink::IDBFactory> pending_receiver) |
| : receiver_(this, std::move(pending_receiver)) {} |
| |
| BackendFactoryWithMockedDatabaseInfo( |
| const BackendFactoryWithMockedDatabaseInfo&) = delete; |
| BackendFactoryWithMockedDatabaseInfo& operator=( |
| const BackendFactoryWithMockedDatabaseInfo&) = delete; |
| |
| void Open(mojo::PendingAssociatedRemote<mojom::blink::IDBCallbacks> |
| pending_callbacks, |
| mojo::PendingAssociatedRemote<mojom::blink::IDBDatabaseCallbacks> |
| database_callbacks, |
| const WTF::String& name, |
| int64_t version, |
| mojo::PendingAssociatedReceiver<mojom::blink::IDBTransaction> |
| version_change_transaction_receiver, |
| int64_t transaction_id) override { |
| NOTREACHED(); |
| } |
| |
| void DeleteDatabase(mojo::PendingAssociatedRemote<mojom::blink::IDBCallbacks> |
| pending_callbacks, |
| const WTF::String& name, |
| bool force_close) override { |
| NOTREACHED(); |
| } |
| |
| void AbortTransactionsAndCompactDatabase( |
| AbortTransactionsAndCompactDatabaseCallback callback) override { |
| NOTREACHED(); |
| } |
| |
| void AbortTransactionsForDatabase( |
| AbortTransactionsForDatabaseCallback callback) override { |
| NOTREACHED(); |
| } |
| |
| void GetDatabaseInfo(mojo::PendingAssociatedRemote<mojom::blink::IDBCallbacks> |
| pending_callbacks) override { |
| callbacks_ptr_->Bind(std::move(pending_callbacks)); |
| } |
| |
| void SetCallbacksPointer( |
| mojo::AssociatedRemote<mojom::blink::IDBCallbacks>* callbacks_ptr) { |
| callbacks_ptr_ = callbacks_ptr; |
| } |
| |
| private: |
| mojo::Receiver<mojom::blink::IDBFactory> receiver_; |
| mojo::AssociatedRemote<mojom::blink::IDBCallbacks>* callbacks_ptr_; |
| }; |
| |
| class IDBFactoryTest : public testing::Test { |
| public: |
| IDBFactoryTest(const IDBFactoryTest&) = delete; |
| IDBFactoryTest& operator=(const IDBFactoryTest&) = delete; |
| |
| protected: |
| IDBFactoryTest() = default; |
| ~IDBFactoryTest() override = default; |
| |
| ScopedTestingPlatformSupport<TestingPlatformSupport> platform_; |
| }; |
| |
| TEST_F(IDBFactoryTest, WebIDBGetDBInfoCallbacksResolvesPromise) { |
| V8TestingScope scope(KURL("https://example.com")); |
| |
| mojo::Remote<mojom::blink::IDBFactory> remote; |
| auto mock_factory = std::make_unique<BackendFactoryWithMockedDatabaseInfo>( |
| remote.BindNewPipeAndPassReceiver( |
| scope.GetExecutionContext()->GetTaskRunner( |
| TaskType::kDatabaseAccess))); |
| |
| mojo::AssociatedRemote<mojom::blink::IDBCallbacks> callbacks; |
| mock_factory->SetCallbacksPointer(&callbacks); |
| auto* factory = MakeGarbageCollected<IDBFactory>(); |
| factory->SetFactoryForTesting(std::move(remote)); |
| |
| DummyExceptionStateForTesting exception_state; |
| ScriptPromise promise = |
| factory->GetDatabaseInfo(scope.GetScriptState(), exception_state); |
| |
| // Allow the GetDatabaseInfo message to propagate across mojo pipes. |
| platform_->RunUntilIdle(); |
| |
| bool on_fulfilled = false; |
| bool on_rejected = false; |
| promise.Then( |
| TestHelperFunction::CreateFunction(scope.GetScriptState(), &on_fulfilled), |
| TestHelperFunction::CreateFunction(scope.GetScriptState(), &on_rejected)); |
| |
| EXPECT_FALSE(on_fulfilled); |
| EXPECT_FALSE(on_rejected); |
| |
| Vector<mojom::blink::IDBNameAndVersionPtr> name_and_info_list; |
| callbacks->SuccessNamesAndVersionsList(std::move(name_and_info_list)); |
| |
| EXPECT_FALSE(on_fulfilled); |
| EXPECT_FALSE(on_rejected); |
| |
| // Allow the Success message to propagate across mojo pipes. This will also |
| // perform a microtask checkpoint, so an explicit call to do that is not |
| // needed. |
| platform_->RunUntilIdle(); |
| |
| EXPECT_TRUE(on_fulfilled); |
| EXPECT_FALSE(on_rejected); |
| } |
| |
| TEST_F(IDBFactoryTest, WebIDBGetDBNamesCallbacksRejectsPromise) { |
| V8TestingScope scope(KURL("https://example.com")); |
| |
| mojo::Remote<mojom::blink::IDBFactory> remote; |
| auto mock_factory = std::make_unique<BackendFactoryWithMockedDatabaseInfo>( |
| remote.BindNewPipeAndPassReceiver( |
| scope.GetExecutionContext()->GetTaskRunner( |
| TaskType::kDatabaseAccess))); |
| |
| mojo::AssociatedRemote<mojom::blink::IDBCallbacks> callbacks; |
| mock_factory->SetCallbacksPointer(&callbacks); |
| auto* factory = MakeGarbageCollected<IDBFactory>(); |
| factory->SetFactoryForTesting(std::move(remote)); |
| |
| DummyExceptionStateForTesting exception_state; |
| ScriptPromise promise = |
| factory->GetDatabaseInfo(scope.GetScriptState(), exception_state); |
| |
| // Allow the GetDatabaseInfo message to propagate across mojo pipes. |
| platform_->RunUntilIdle(); |
| |
| bool on_fulfilled = false; |
| bool on_rejected = false; |
| promise.Then( |
| TestHelperFunction::CreateFunction(scope.GetScriptState(), &on_fulfilled), |
| TestHelperFunction::CreateFunction(scope.GetScriptState(), &on_rejected)); |
| |
| EXPECT_FALSE(on_fulfilled); |
| EXPECT_FALSE(on_rejected); |
| |
| callbacks->Error(mojom::blink::IDBException::kNoError, "message"); |
| |
| EXPECT_FALSE(on_fulfilled); |
| EXPECT_FALSE(on_rejected); |
| |
| // Allow the Error message to propagate across mojo pipes. This will also |
| // perform a microtask checkpoint, so an explicit call to do that is not |
| // needed. |
| platform_->RunUntilIdle(); |
| |
| EXPECT_FALSE(on_fulfilled); |
| EXPECT_TRUE(on_rejected); |
| } |
| |
| } // namespace |
| } // namespace blink |