blob: fd5aff9e060223ffc987b88ecced345b72defb26 [file] [log] [blame]
// 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