blob: 08bb066521e58bce830367bbadce9bc849ef245c [file] [log] [blame]
// Copyright 2025 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/indexed_db/file_path_util.h"
#include <string>
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "components/services/storage/public/cpp/buckets/bucket_locator.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/storage_key/storage_key.h"
#include "url/origin.h"
namespace content::indexed_db {
TEST(FilePathUtilTest, GetSqliteDbDirectory) {
// First party, default bucket: need to append an origin.
storage::BucketLocator bucket_locator(
storage::BucketId::FromUnsafeValue(1),
blink::StorageKey::CreateFirstParty(
url::Origin::Create(GURL("https://example.com/"))),
/*is_default=*/true);
EXPECT_EQ(base::FilePath::FromASCII("IndexedDB")
.Append(GetSqliteDbDirectory(bucket_locator)),
base::FilePath::FromASCII("IndexedDB")
.AppendASCII("https_example.com_0"));
// Non-default bucket: no origin, since the base path includes the bucket ID.
storage::BucketLocator bucket_locator_non_default(
storage::BucketId::FromUnsafeValue(2),
blink::StorageKey::CreateFirstParty(
url::Origin::Create(GURL("https://example.com/"))),
/*is_default=*/false);
EXPECT_EQ(base::FilePath::FromASCII("2")
.AppendASCII("IndexedDB")
.Append(GetSqliteDbDirectory(bucket_locator_non_default)),
base::FilePath::FromASCII("2").AppendASCII("IndexedDB"));
// Third party bucket: no origin, since the base path includes the bucket ID.
storage::BucketLocator bucket_locator_third_party(
storage::BucketId::FromUnsafeValue(3),
blink::StorageKey::Create(
url::Origin::Create(GURL("https://example.com/")),
net::SchemefulSite(GURL("https://foo.com/")),
blink::mojom::AncestorChainBit::kCrossSite,
/*third_party_partitioning_allowed=*/true),
/*is_default=*/true);
EXPECT_EQ(base::FilePath::FromASCII("3")
.AppendASCII("IndexedDB")
.Append(GetSqliteDbDirectory(bucket_locator_third_party)),
base::FilePath::FromASCII("3").AppendASCII("IndexedDB"));
}
TEST(FilePathUtilTest, DatabaseNameToFileName) {
struct {
std::u16string dom_string_input;
base::FilePath::StringType expected_file_name;
} test_cases[] = {
{u"basic_name",
FILE_PATH_LITERAL(
"6ZV5AGIHZJWHPZ2UQ4IQUJJWBTQPYXGGRWTDQLBFA7AK4XTIXGCQ")},
// Output is case sensitive.
{u"BASIC_name",
FILE_PATH_LITERAL(
"PMOE3RT3OU4BAFK6RL7E4MHR7N5Q65X2POQZUH3CRZCE6IXHRXTA")},
// Empty string.
{u"", FILE_PATH_LITERAL("0")},
// Characters that aren't valid filename characters.
{u"invalid/name",
FILE_PATH_LITERAL(
"EYZCOEWM37YXW6VHDSELIIN7IGHYYHWW2FHMD6PJJO7ROTVYRBRQ")},
{u"invalid\\name",
FILE_PATH_LITERAL(
"J46FRRIBKKJPJYXWUUGN252JKBAI2HMZAP4TAU2NYPAM36S74OJQ")},
{u"invalid name",
FILE_PATH_LITERAL(
"IGMS2QMW3GNPSJXWTHHFO5VSHBL6IMGXYDD5JSKAZSXH44T4SO6A")},
{u"sneaky.text",
FILE_PATH_LITERAL(
"IE4ONQ2VLLMXKWXWB6B4HBN3JICQDPQBXBDYUUPBJX6UU26VD2SQ")},
// Valid UTF16.
{u"\x4f60\x597d ",
FILE_PATH_LITERAL(
"NDE2443GY5Z36EMF2LPWR54H47YOVZ3EEF4V5J4JIAXO4O2RCS5A")},
// Invalid UTF16. The first character is a truncated UTF-16 character.
{u"\xd800\x597d",
FILE_PATH_LITERAL(
"VYO3RQVZ43IVZ3MZLKNC7BOZULNVR2S4EPLOBM527U4WG5MISGZQ")},
// Long string with invalid characters.
{u"too_long_name_too_long_name_too_long_name_too_long_name\xd800\x597d",
FILE_PATH_LITERAL(
"IDYIO2S422KGRTC2OQ24OSGW3HSSSF6RWM3O64SKMLFIPZ5ICCZQ")},
};
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
const base::FilePath& dir = temp_dir.GetPath();
std::set<base::FilePath> expected_files;
std::set<base::FilePath> enumerated_files;
for (const auto& test_case : test_cases) {
// Encode it.
base::FilePath file_name =
DatabaseNameToFileName(test_case.dom_string_input);
EXPECT_TRUE(!file_name.IsAbsolute());
EXPECT_EQ(file_name.BaseName(), file_name);
EXPECT_EQ(file_name.value(), test_case.expected_file_name);
expected_files.insert(file_name);
// Write the file in a directory so we can enumerate it later.
ASSERT_TRUE(base::WriteFile(dir.Append(file_name),
base::byte_span_from_cstring("42")));
// Also write another file which is generated by SQLite.
ASSERT_TRUE(base::WriteFile(
dir.Append(file_name).InsertBeforeExtensionASCII("-wal"),
base::byte_span_from_cstring("42")));
}
EnumerateDatabasesInDirectory(dir, [&](const base::FilePath& path) {
enumerated_files.insert(path.BaseName());
});
EXPECT_EQ(expected_files, enumerated_files);
}
} // namespace content::indexed_db