Commit f79c6fe7 authored by Andreas Butler's avatar Andreas Butler Committed by Commit Bot

Reimplementing getDatabaseNames() as databases() for indexeddb.

Change-Id: I88a0fe75383bb9712478580a7bd60a145e2d89ba
Bug: 1228492
Reviewed-on: https://chromium-review.googlesource.com/c/1228492
Commit-Queue: Andreas Butler <andreasbutler@google.com>
Reviewed-by: default avatarKent Tamura <tkent@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Reviewed-by: default avatarChase Phillips <cmp@chromium.org>
Reviewed-by: default avatarKinuko Yasuda <kinuko@chromium.org>
Reviewed-by: default avatarKentaro Hara <haraken@chromium.org>
Cr-Commit-Position: refs/heads/master@{#600863}
parent 6bc8e421
......@@ -141,6 +141,8 @@ class IndexedDBCallbacks::IOThreadHelper {
~IOThreadHelper();
void SendError(const IndexedDBDatabaseError& error);
void SendSuccessNamesAndVersionsList(
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions);
void SendSuccessStringList(const std::vector<base::string16>& value);
void SendBlocked(int64_t existing_version);
void SendUpgradeNeeded(SafeIOThreadConnectionWrapper connection,
......@@ -236,6 +238,20 @@ void IndexedDBCallbacks::OnError(const IndexedDBDatabaseError& error) {
}
}
void IndexedDBCallbacks::OnSuccess(
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!complete_);
DCHECK(io_helper_);
base::PostTaskWithTraits(
FROM_HERE, {BrowserThread::IO},
base::BindOnce(&IOThreadHelper::SendSuccessNamesAndVersionsList,
base::Unretained(io_helper_.get()),
std::move(names_and_versions)));
complete_ = true;
}
void IndexedDBCallbacks::OnSuccess(const std::vector<base::string16>& value) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
DCHECK(!complete_);
......@@ -526,6 +542,18 @@ void IndexedDBCallbacks::IOThreadHelper::SendError(
callbacks_->Error(error.code(), error.message());
}
void IndexedDBCallbacks::IOThreadHelper::SendSuccessNamesAndVersionsList(
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!callbacks_)
return;
if (!dispatcher_host_) {
OnConnectionError();
return;
}
callbacks_->SuccessNamesAndVersionsList(std::move(names_and_versions));
}
void IndexedDBCallbacks::IOThreadHelper::SendSuccessStringList(
const std::vector<base::string16>& value) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
......
......@@ -53,6 +53,10 @@ class CONTENT_EXPORT IndexedDBCallbacks
virtual void OnError(const IndexedDBDatabaseError& error);
// IndexedDBFactory::databases
virtual void OnSuccess(
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions);
// IndexedDBFactory::GetDatabaseNames
virtual void OnSuccess(const std::vector<base::string16>& string);
......
......@@ -94,6 +94,9 @@ class IndexedDBDispatcherHost::IDBSequenceHelper {
indexed_db_context_(std::move(indexed_db_context)) {}
~IDBSequenceHelper() {}
void GetDatabaseInfoOnIDBThread(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin);
void GetDatabaseNamesOnIDBThread(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin);
void OpenOnIDBThread(
......@@ -165,6 +168,24 @@ void IndexedDBDispatcherHost::RenderProcessExited(
base::Unretained(this)));
}
void IndexedDBDispatcherHost::GetDatabaseInfo(
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info,
const url::Origin& origin) {
DCHECK_CURRENTLY_ON(BrowserThread::IO);
if (!IsValidOrigin(origin)) {
mojo::ReportBadMessage(kInvalidOrigin);
return;
}
scoped_refptr<IndexedDBCallbacks> callbacks(new IndexedDBCallbacks(
this->AsWeakPtr(), origin, std::move(callbacks_info), IDBTaskRunner()));
IDBTaskRunner()->PostTask(
FROM_HERE, base::BindOnce(&IDBSequenceHelper::GetDatabaseInfoOnIDBThread,
base::Unretained(idb_helper_),
std::move(callbacks), origin));
}
void IndexedDBDispatcherHost::GetDatabaseNames(
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info,
const url::Origin& origin) {
......@@ -281,6 +302,16 @@ base::SequencedTaskRunner* IndexedDBDispatcherHost::IDBTaskRunner() const {
return indexed_db_context_->TaskRunner();
}
void IndexedDBDispatcherHost::IDBSequenceHelper::GetDatabaseInfoOnIDBThread(
scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin) {
DCHECK(indexed_db_context_->TaskRunner()->RunsTasksInCurrentSequence());
base::FilePath indexed_db_path = indexed_db_context_->data_path();
indexed_db_context_->GetIDBFactory()->GetDatabaseInfo(callbacks, origin,
indexed_db_path);
}
void IndexedDBDispatcherHost::IDBSequenceHelper::GetDatabaseNamesOnIDBThread(
scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin) {
......
......@@ -82,6 +82,9 @@ class CONTENT_EXPORT IndexedDBDispatcherHost
~IndexedDBDispatcherHost() override;
// blink::mojom::IDBFactory implementation:
void GetDatabaseInfo(
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info,
const url::Origin& origin) override;
void GetDatabaseNames(
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info,
const url::Origin& origin) override;
......
......@@ -38,7 +38,9 @@ class CONTENT_EXPORT IndexedDBFactory
virtual void ReleaseDatabase(const IndexedDBDatabase::Identifier& identifier,
bool forced_close) = 0;
virtual void GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::FilePath& data_directory) = 0;
virtual void GetDatabaseNames(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::FilePath& data_directory) = 0;
......
......@@ -399,6 +399,50 @@ void IndexedDBFactoryImpl::ReportOutstandingBlobs(const Origin& origin,
}
}
void IndexedDBFactoryImpl::GetDatabaseInfo(
scoped_refptr<IndexedDBCallbacks> callbacks,
const Origin& origin,
const base::FilePath& data_directory) {
IDB_TRACE("IndexedDBFactoryImpl::GetDatabaseInfo");
// TODO(dmurph): Plumb data_loss back to script eventually?
IndexedDBDataLossInfo data_loss_info;
bool disk_full;
leveldb::Status s;
// TODO(dmurph): Handle this error
scoped_refptr<IndexedDBBackingStore> backing_store =
OpenBackingStore(origin, data_directory, &data_loss_info, &disk_full, &s);
if (!backing_store.get()) {
IndexedDBDatabaseError error(
blink::kWebIDBDatabaseExceptionUnknownError,
ASCIIToUTF16("Internal error opening backing store for "
"indexedDB.databases()."));
callbacks->OnError(error);
if (s.IsCorruption())
HandleBackingStoreCorruption(origin, error);
return;
}
IndexedDBMetadataCoding metadata_coding;
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions;
s = metadata_coding.ReadDatabaseNamesAndVersions(
backing_store->db(), backing_store->origin_identifier(),
&names_and_versions);
if (!s.ok()) {
DLOG(ERROR) << "Internal error getting database info";
IndexedDBDatabaseError error(blink::kWebIDBDatabaseExceptionUnknownError,
"Internal error opening backing store for "
"indexedDB.databases().");
callbacks->OnError(error);
backing_store = nullptr;
if (s.IsCorruption())
HandleBackingStoreCorruption(origin, error);
return;
}
callbacks->OnSuccess(std::move(names_and_versions));
backing_store = nullptr;
ReleaseBackingStore(origin, false /* immediate */);
}
void IndexedDBFactoryImpl::GetDatabaseNames(
scoped_refptr<IndexedDBCallbacks> callbacks,
const Origin& origin,
......
......@@ -52,7 +52,9 @@ class CONTENT_EXPORT IndexedDBFactoryImpl : public IndexedDBFactory {
// content::IndexedDBFactory overrides:
void ReleaseDatabase(const IndexedDBDatabase::Identifier& identifier,
bool forced_close) override;
void GetDatabaseInfo(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::FilePath& data_directory) override;
void GetDatabaseNames(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::FilePath& data_directory) override;
......
......@@ -18,6 +18,7 @@ using base::StringPiece;
using blink::IndexedDBDatabaseMetadata;
using blink::IndexedDBIndexMetadata;
using blink::IndexedDBKeyPath;
using blink::mojom::IDBNameAndVersionPtr;
using blink::IndexedDBObjectStoreMetadata;
using leveldb::Status;
......@@ -324,15 +325,16 @@ Status ReadObjectStores(
}
template <typename DatabaseOrTransaction>
Status ReadDatabaseNamesInternal(DatabaseOrTransaction* db_or_transaction,
const std::string& origin_identifier,
std::vector<base::string16>* names) {
Status ReadDatabaseNamesAndVersionsInternal(
DatabaseOrTransaction* db_or_transaction,
const std::string& origin_identifier,
std::vector<blink::mojom::IDBNameAndVersionPtr>* names_and_versions) {
const std::string start_key =
DatabaseNameKey::EncodeMinKeyForOrigin(origin_identifier);
const std::string stop_key =
DatabaseNameKey::EncodeStopKeyForOrigin(origin_identifier);
DCHECK(names->empty());
DCHECK(names_and_versions->empty());
std::unique_ptr<LevelDBIterator> it = CreateIterator(db_or_transaction);
Status s;
for (s = it->Seek(start_key);
......@@ -343,7 +345,7 @@ Status ReadDatabaseNamesInternal(DatabaseOrTransaction* db_or_transaction,
DatabaseNameKey database_name_key;
if (!DatabaseNameKey::Decode(&slice, &database_name_key) ||
!slice.empty()) {
// TODO(dmurph): Change UMA name to ReadDatabaseNames.
// TODO(dmurph): Change UMA name to ReadDatabaseNamesAndVersionsInternal.
INTERNAL_CONSISTENCY_ERROR_UNTESTED(GET_DATABASE_NAMES);
continue;
}
......@@ -369,10 +371,11 @@ Status ReadDatabaseNamesInternal(DatabaseOrTransaction* db_or_transaction,
}
// Ignore stale metadata from failed initial opens.
if (database_version != IndexedDBDatabaseMetadata::DEFAULT_VERSION)
names->push_back(database_name_key.database_name());
if (database_version != IndexedDBDatabaseMetadata::DEFAULT_VERSION) {
names_and_versions->push_back(blink::mojom::IDBNameAndVersion::New(
database_name_key.database_name(), database_version));
}
}
if (!s.ok())
INTERNAL_READ_ERROR(GET_DATABASE_NAMES);
......@@ -453,18 +456,38 @@ Status ReadMetadataForDatabaseNameInternal(
IndexedDBMetadataCoding::IndexedDBMetadataCoding() = default;
IndexedDBMetadataCoding::~IndexedDBMetadataCoding() = default;
Status IndexedDBMetadataCoding::ReadDatabaseNamesAndVersions(
LevelDBDatabase* db,
const std::string& origin_identifier,
std::vector<blink::mojom::IDBNameAndVersionPtr>* names_and_versions) {
return ReadDatabaseNamesAndVersionsInternal(db, origin_identifier,
names_and_versions);
}
Status IndexedDBMetadataCoding::ReadDatabaseNames(
LevelDBDatabase* db,
const std::string& origin_identifier,
std::vector<base::string16>* names) {
return ReadDatabaseNamesInternal(db, origin_identifier, names);
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions;
Status s = ReadDatabaseNamesAndVersionsInternal(db, origin_identifier,
&names_and_versions);
for (const blink::mojom::IDBNameAndVersionPtr& nav : names_and_versions) {
names->push_back(nav->name);
}
return s;
}
Status IndexedDBMetadataCoding::ReadDatabaseNames(
LevelDBTransaction* transaction,
const std::string& origin_identifier,
std::vector<base::string16>* names) {
return ReadDatabaseNamesInternal(transaction, origin_identifier, names);
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions;
Status s = ReadDatabaseNamesAndVersionsInternal(
transaction, origin_identifier, &names_and_versions);
for (const blink::mojom::IDBNameAndVersionPtr& nav : names_and_versions) {
names->push_back(nav->name);
}
return s;
}
Status IndexedDBMetadataCoding::ReadMetadataForDatabaseName(
......
......@@ -14,6 +14,7 @@
#include "base/strings/string16.h"
#include "content/common/content_export.h"
#include "third_party/blink/public/common/indexeddb/indexeddb_key_path.h"
#include "third_party/blink/public/mojom/indexeddb/indexeddb.mojom.h"
#include "third_party/leveldatabase/src/include/leveldb/status.h"
namespace blink {
......@@ -47,6 +48,12 @@ class CONTENT_EXPORT IndexedDBMetadataCoding {
const std::string& origin_identifier,
std::vector<base::string16>* names);
// Reads in the list of database names and versions for the given origin.
virtual leveldb::Status ReadDatabaseNamesAndVersions(
LevelDBDatabase* db,
const std::string& origin_identifier,
std::vector<blink::mojom::IDBNameAndVersionPtr>* names_and_versions);
// Reads in metadata for the database and all object stores & indices.
// Note: the database name is not populated in |metadata|.
virtual leveldb::Status ReadMetadataForDatabaseName(
......
......@@ -25,6 +25,10 @@ class MockIndexedDBFactory : public IndexedDBFactory {
void(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::FilePath& data_directory));
MOCK_METHOD3(GetDatabaseInfo,
void(scoped_refptr<IndexedDBCallbacks> callbacks,
const url::Origin& origin,
const base::FilePath& data_directory));
MOCK_METHOD4(
OpenProxy,
void(const base::string16& name,
......
......@@ -29,6 +29,9 @@ class MockMojoIndexedDBCallbacks : public blink::mojom::IDBCallbacks {
MOCK_METHOD2(Error, void(int32_t code, const base::string16& message));
MOCK_METHOD1(SuccessNamesAndVersionsList,
void(std::vector<blink::mojom::IDBNameAndVersionPtr> list));
MOCK_METHOD1(SuccessStringList,
void(const std::vector<base::string16>& value));
......
......@@ -12,6 +12,7 @@
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
using blink::IndexedDBDatabaseMetadata;
......@@ -20,6 +21,7 @@ using blink::WebData;
using blink::WebIDBCallbacks;
using blink::WebIDBDatabase;
using blink::WebIDBMetadata;
using blink::WebIDBNameAndVersion;
using blink::WebIDBValue;
using blink::WebString;
using blink::WebVector;
......@@ -75,6 +77,12 @@ WebIDBValue ConvertReturnValue(const blink::mojom::IDBReturnValuePtr& value) {
return web_value;
}
WebIDBNameAndVersion ConvertNameVersion(
const blink::mojom::IDBNameAndVersionPtr& name_and_version) {
return WebIDBNameAndVersion(WebString::FromUTF16(name_and_version->name),
name_and_version->version);
}
} // namespace
// static
......@@ -122,6 +130,17 @@ void IndexedDBCallbacksImpl::Error(int32_t code,
callbacks_.reset();
}
void IndexedDBCallbacksImpl::SuccessNamesAndVersionsList(
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions) {
WebVector<WebIDBNameAndVersion> web_names_and_versions;
web_names_and_versions.reserve(names_and_versions.size());
for (const blink::mojom::IDBNameAndVersionPtr& name_version :
names_and_versions)
web_names_and_versions.emplace_back(ConvertNameVersion(name_version));
callbacks_->OnSuccess(web_names_and_versions);
callbacks_.reset();
}
void IndexedDBCallbacksImpl::SuccessStringList(
const std::vector<base::string16>& value) {
WebVector<WebString> web_value(value.size());
......
......@@ -37,6 +37,9 @@ class IndexedDBCallbacksImpl : public blink::mojom::IDBCallbacks {
// blink::mojom::IDBCallbacks implementation:
void Error(int32_t code, const base::string16& message) override;
void SuccessNamesAndVersionsList(
std::vector<blink::mojom::IDBNameAndVersionPtr> names_and_versions)
override;
void SuccessStringList(const std::vector<base::string16>& value) override;
void Blocked(int64_t existing_version) override;
void UpgradeNeeded(blink::mojom::IDBDatabaseAssociatedPtrInfo database_info,
......
......@@ -10,6 +10,7 @@
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_metadata.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
#include "third_party/blink/public/platform/web_blob_info.h"
#include "third_party/blink/public/web/web_heap.h"
......@@ -30,6 +31,8 @@ class MockWebIDBCallbacks : public blink::WebIDBCallbacks {
const blink::WebIDBKey& primaryKey,
const blink::WebIDBValue& value));
MOCK_METHOD1(OnSuccess,
void(const blink::WebVector<blink::WebIDBNameAndVersion>&));
MOCK_METHOD1(OnSuccess, void(const blink::WebVector<blink::WebString>&));
void OnSuccess(blink::WebIDBCursor* cursor,
......
......@@ -29,6 +29,17 @@ WebIDBFactoryImpl::WebIDBFactoryImpl(IDBFactoryPtrInfo factory_info)
WebIDBFactoryImpl::~WebIDBFactoryImpl() = default;
void WebIDBFactoryImpl::GetDatabaseInfo(
WebIDBCallbacks* callbacks,
const WebSecurityOrigin& origin,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
auto callbacks_impl = std::make_unique<IndexedDBCallbacksImpl>(
base::WrapUnique(callbacks), IndexedDBCallbacksImpl::kNoTransaction,
nullptr);
factory_->GetDatabaseInfo(GetCallbacksProxy(std::move(callbacks_impl)),
url::Origin(origin));
}
void WebIDBFactoryImpl::GetDatabaseNames(
WebIDBCallbacks* callbacks,
const WebSecurityOrigin& origin,
......
......@@ -27,6 +27,10 @@ class WebIDBFactoryImpl : public blink::WebIDBFactory {
~WebIDBFactoryImpl() override;
// See WebIDBFactory.h for documentation on these functions.
void GetDatabaseInfo(
blink::WebIDBCallbacks* callbacks,
const blink::WebSecurityOrigin& origin,
scoped_refptr<base::SingleThreadTaskRunner> task_runner) override;
void GetDatabaseNames(
blink::WebIDBCallbacks* callbacks,
const blink::WebSecurityOrigin& origin,
......
// META: script=support.js
async_test( async function(t) {
let made_database_check = t.step_func(async function() {
let idb_databases_promise = await indexedDB.databases();
assert_true(
idb_databases_promise.some(
e => e.name == "TestDatabase" && e.version == 1),
"Call to databases() did not find database.");
t.done();
});
delete_then_open(t, "TestDatabase", ()=>{}, made_database_check);
}, "Report one database test.");
async_test( function(t) {
let done_making_databases_callback = t.step_func(async function() {
let idb_databases_promise = await indexedDB.databases();
assert_true(
idb_databases_promise.some(
e => e.name == "TestDatabase1" && e.version == 1),
"Call to databases() did not find database.");
assert_true(
idb_databases_promise.some(
e => e.name == "TestDatabase2" && e.version == 1),
"Call to databases() did not find database.");
assert_true(
idb_databases_promise.some(
e => e.name == "TestDatabase3" && e.version == 1),
"Call to databases() did not find database.");
t.done();
});
let make_databases_barrier = create_barrier(done_making_databases_callback);
delete_then_open(t, "TestDatabase1", ()=>{}, make_databases_barrier(t));
delete_then_open(t, "TestDatabase2", ()=>{}, make_databases_barrier(t));
delete_then_open(t, "TestDatabase3", ()=>{}, make_databases_barrier(t));
}, "Report multiple databases test.");
async_test( function(t) {
let delete_request = indexedDB.deleteDatabase("NonExistentDatabase");
delete_request.onsuccess = t.step_func(async function() {
let idb_databases_promise = await indexedDB.databases();
assert_false(
idb_databases_promise.some(
e => e.name == "NonExistentDatabase"),
"Call to databases() found excluded database.");
t.done();
});
}, "Don't report nonexistant databases test.");
done();
......@@ -192,3 +192,49 @@ function keep_alive(tx, store_name) {
pin = false;
};
}
/**
* Ensures that indexeddb database specified by db_name is deleted, and then
* opens a connection to that database.
*
* @param t: the testing script state
* @param db_name: name of database to delete then create
* @param upgrade_func: function to be called if db_name needs upgrading
* @param body_func: function to be called upon successful deletion
* and creation of db_name
*/
function delete_then_open(t, db_name, upgrade_func, body_func) {
var delete_request = indexedDB.deleteDatabase(db_name);
delete_request.onerror = t.unreached_func('deleteDatabase should not fail');
delete_request.onsuccess = t.step_func(function(e) {
var open_request = indexedDB.open(db_name);
open_request.onupgradeneeded = t.step_func(function() {
upgrade_func(t, open_request.result, open_request);
});
open_request.onsuccess = t.step_func(function() {
body_func(t, open_request.result);
});
});
}
/**
* Creates a barrier that one calls
*
* @param callback: function to be called after barrier is passed
*/
function create_barrier(callback) {
var count = 0;
var called = false;
return function(t) {
assert_false(called, "Barrier already used.");
++count;
return t.step_func(function() {
--count;
if (count === 0) {
assert_false(called, "Barrier already used.");
called = true;
callback();
}
});
}
}
......@@ -562,6 +562,7 @@ interface IDBFactory
attribute @@toStringTag
method cmp
method constructor
method databases
method deleteDatabase
method open
interface IDBIndex
......
......@@ -504,6 +504,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] attribute @@toStringTag
[Worker] method cmp
[Worker] method constructor
[Worker] method databases
[Worker] method deleteDatabase
[Worker] method open
[Worker] interface IDBIndex
......
......@@ -3891,6 +3891,7 @@ interface IDBFactory
attribute @@toStringTag
method cmp
method constructor
method databases
method deleteDatabase
method open
interface IDBIndex
......
......@@ -499,6 +499,7 @@ Starting worker: resources/global-interface-listing-worker.js
[Worker] attribute @@toStringTag
[Worker] method cmp
[Worker] method constructor
[Worker] method databases
[Worker] method deleteDatabase
[Worker] method open
[Worker] interface IDBIndex
......
......@@ -138,6 +138,7 @@ source_set("blink_headers") {
"platform/modules/indexeddb/web_idb_key_path.h",
"platform/modules/indexeddb/web_idb_key_range.h",
"platform/modules/indexeddb/web_idb_metadata.h",
"platform/modules/indexeddb/web_idb_name_and_version.h",
"platform/modules/indexeddb/web_idb_observation.h",
"platform/modules/indexeddb/web_idb_value.h",
"platform/modules/installedapp/web_related_application.h",
......
......@@ -131,6 +131,11 @@ struct IDBDatabaseMetadata {
array<IDBObjectStoreMetadata> object_stores;
};
struct IDBNameAndVersion {
mojo_base.mojom.String16 name;
int64 version;
};
struct IDBIndexKeys {
int64 index_id;
array<IDBKey> index_keys;
......@@ -188,6 +193,9 @@ struct IDBObserverChanges {
interface IDBCallbacks {
Error(int32 code, mojo_base.mojom.String16 message);
// Factory::GetDatabaseInfo
SuccessNamesAndVersionsList(array<IDBNameAndVersion> value);
// Factory::GetDatabaseNames
SuccessStringList(array<mojo_base.mojom.String16> value);
......@@ -345,6 +353,7 @@ interface IDBDatabase {
};
interface IDBFactory {
GetDatabaseInfo(associated IDBCallbacks callbacks, url.mojom.Origin origin);
GetDatabaseNames(associated IDBCallbacks callbacks, url.mojom.Origin origin);
Open(associated IDBCallbacks callbacks,
associated IDBDatabaseCallbacks database_callbacks,
......
......@@ -37,6 +37,7 @@ class WebIDBDatabase;
class WebIDBDatabaseError;
class WebIDBKey;
struct WebIDBMetadata;
struct WebIDBNameAndVersion;
class WebIDBValue;
class WebIDBCallbacks {
......@@ -45,6 +46,7 @@ class WebIDBCallbacks {
// Pointers transfer ownership.
virtual void OnError(const WebIDBDatabaseError&) = 0;
virtual void OnSuccess(const WebVector<WebIDBNameAndVersion>&) = 0;
virtual void OnSuccess(const WebVector<WebString>&) = 0;
virtual void OnSuccess(WebIDBCursor*,
WebIDBKey,
......
......@@ -47,6 +47,9 @@ class WebIDBFactory {
public:
virtual ~WebIDBFactory() = default;
virtual void GetDatabaseInfo(WebIDBCallbacks*,
const WebSecurityOrigin&,
scoped_refptr<base::SingleThreadTaskRunner>) = 0;
virtual void GetDatabaseNames(
WebIDBCallbacks*,
const WebSecurityOrigin&,
......
#ifndef THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_NAME_AND_VERSION_H_
#define THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_NAME_AND_VERSION_H_
#include "third_party/blink/public/platform/web_string.h"
namespace blink {
struct WebIDBNameAndVersion {
enum { kNoVersion = -1 };
WebString name;
int64_t version;
WebIDBNameAndVersion() : version(kNoVersion) {}
WebIDBNameAndVersion(WebString name, int64_t version)
: name(name), version(version) {}
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_PUBLIC_PLATFORM_MODULES_INDEXEDDB_WEB_IDB_NAME_AND_VERSION_H_
......@@ -267,6 +267,7 @@ jumbo_source_set("unit_tests") {
"eventsource/event_source_parser_test.cc",
"filesystem/dom_file_system_base_test.cc",
"filesystem/file_writer_test.cc",
"indexeddb/idb_factory_test.cc",
"indexeddb/idb_key_path_test.cc",
"indexeddb/idb_request_test.cc",
"indexeddb/idb_test_helper.cc",
......@@ -274,6 +275,8 @@ jumbo_source_set("unit_tests") {
"indexeddb/idb_value_wrapping_test.cc",
"indexeddb/mock_web_idb_database.cc",
"indexeddb/mock_web_idb_database.h",
"indexeddb/mock_web_idb_factory.cc",
"indexeddb/mock_web_idb_factory.h",
"manifest/image_resource_type_converters_test.cc",
"media_controls/elements/media_control_animated_arrow_container_element_test.cc",
"media_controls/elements/media_control_display_cutout_fullscreen_button_element_test.cc",
......
// 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.
// Spec pull-request for addition:
// https://github.com/w3c/IndexedDB/pull/240/commits
dictionary IDBDatabaseInfo {
DOMString name;
unsigned long long version;
};
......@@ -29,32 +29,155 @@
#include "third_party/blink/renderer/modules/indexeddb/idb_factory.h"
#include <memory>
#include <utility>
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_callbacks.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_callbacks.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
#include "third_party/blink/public/platform/platform.h"
#include "third_party/blink/public/platform/task_type.h"
#include "third_party/blink/public/platform/web_security_origin.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_binding_for_modules.h"
#include "third_party/blink/renderer/core/dom/document.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/execution_context/execution_context.h"
#include "third_party/blink/renderer/core/frame/use_counter.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
#include "third_party/blink/renderer/modules/indexed_db_names.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_database.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_database_callbacks.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_database_info.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_key.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_tracing.h"
#include "third_party/blink/renderer/modules/indexeddb/indexed_db_client.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/heap/persistent.h"
#include "third_party/blink/renderer/platform/histogram.h"
#include "third_party/blink/renderer/platform/weborigin/security_origin.h"
#include "third_party/blink/renderer/platform/wtf/allocator.h"
namespace blink {
namespace {
class WebIDBGetDBNamesCallbacksImpl : public WebIDBCallbacks {
public:
// static
static std::unique_ptr<WebIDBGetDBNamesCallbacksImpl> Create(
ScriptPromiseResolver* promise_resolver) {
return base::WrapUnique(
new WebIDBGetDBNamesCallbacksImpl(promise_resolver));
}
WebIDBGetDBNamesCallbacksImpl(ScriptPromiseResolver* promise_resolver)
: promise_resolver_(promise_resolver) {
probe::AsyncTaskScheduled(
ExecutionContext::From(promise_resolver_->GetScriptState()),
IndexedDBNames::IndexedDB, this);
}
~WebIDBGetDBNamesCallbacksImpl() override {
if (promise_resolver_) {
probe::AsyncTaskCanceled(
ExecutionContext::From(promise_resolver_->GetScriptState()), this);
promise_resolver_->Reject(
DOMException::Create(DOMExceptionCode::kUnknownError,
"An unexpected shutdown occured before the "
"databases() promise could be resolved"));
}
}
void OnError(const WebIDBDatabaseError& error) override {
if (!promise_resolver_)
return;
probe::AsyncTask async_task(
ExecutionContext::From(promise_resolver_->GetScriptState()), this,
"error");
promise_resolver_->Reject(
DOMException::Create(DOMExceptionCode::kUnknownError,
"The databases() promise was rejected."));
promise_resolver_.Clear();
}
void OnSuccess(const WebVector<WebIDBNameAndVersion>&
web_database_name_and_version_list) override {
if (!promise_resolver_)
return;
HeapVector<IDBDatabaseInfo> database_name_and_version_list;
for (size_t i = 0; i < web_database_name_and_version_list.size(); ++i) {
IDBDatabaseInfo idb_info;
idb_info.setName(web_database_name_and_version_list[i].name);
idb_info.setVersion(web_database_name_and_version_list[i].version);
database_name_and_version_list.push_back(idb_info);
}
probe::AsyncTask async_task(
ExecutionContext::From(promise_resolver_->GetScriptState()), this,
"success");
promise_resolver_->Resolve(database_name_and_version_list);
promise_resolver_.Clear();
}
void OnSuccess(const WebVector<WebString>&) override { NOTREACHED(); }
void OnSuccess(WebIDBCursor* cursor,
WebIDBKey key,
WebIDBKey primary_key,
WebIDBValue value) override {
NOTREACHED();
}
void OnSuccess(WebIDBDatabase* backend,
const WebIDBMetadata& metadata) override {
NOTREACHED();
}
void OnSuccess(WebIDBKey key) override { NOTREACHED(); }
void OnSuccess(WebIDBValue value) override { NOTREACHED(); }
void OnSuccess(WebVector<WebIDBValue> values) override { NOTREACHED(); }
void OnSuccess(long long value) override { NOTREACHED(); }
void OnSuccess() override { NOTREACHED(); }
void OnSuccess(WebIDBKey key,
WebIDBKey primary_key,
WebIDBValue value) override {
NOTREACHED();
}
void OnBlocked(long long old_version) override { NOTREACHED(); }
void OnUpgradeNeeded(long long old_version,
WebIDBDatabase* database,
const WebIDBMetadata& metadata,
unsigned short data_loss,
WebString data_loss_message) override {
NOTREACHED();
}
void Detach() override { NOTREACHED(); }
private:
Persistent<ScriptPromiseResolver> promise_resolver_;
};
} // namespace
static const char kPermissionDeniedErrorMessage[] =
"The user denied permission to access the database.";
IDBFactory::IDBFactory() = default;
IDBFactory::IDBFactory(std::unique_ptr<WebIDBFactory> web_idb_factory)
: web_idb_factory_(std::move(web_idb_factory)) {}
static bool IsContextValid(ExecutionContext* context) {
DCHECK(IsA<Document>(context) || context->IsWorkerGlobalScope());
if (auto* document = DynamicTo<Document>(context))
......@@ -68,6 +191,19 @@ WebIDBFactory* IDBFactory::GetFactory() {
return web_idb_factory_.get();
}
ScriptPromise IDBFactory::GetDatabaseInfo(ScriptState* script_state,
ExceptionState& exception_state) {
ScriptPromiseResolver* resolver = ScriptPromiseResolver::Create(script_state);
GetFactory()->GetDatabaseInfo(
WebIDBGetDBNamesCallbacksImpl::Create(resolver).release(),
WebSecurityOrigin(
ExecutionContext::From(script_state)->GetSecurityOrigin()),
ExecutionContext::From(script_state)
->GetTaskRunner(TaskType::kInternalIndexedDB));
ScriptPromise promise = resolver->Promise();
return promise;
}
IDBRequest* IDBFactory::GetDatabaseNames(ScriptState* script_state,
ExceptionState& exception_state) {
IDB_TRACE("IDBFactory::getDatabaseNamesRequestSetup");
......
......@@ -25,11 +25,14 @@
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_FACTORY_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_IDB_FACTORY_H_
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
#include "third_party/blink/renderer/bindings/core/v8/script_promise.h"
#include "third_party/blink/renderer/modules/indexeddb/idb_open_db_request.h"
#include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
#include "third_party/blink/renderer/platform/heap/handle.h"
#include "third_party/blink/renderer/platform/wtf/text/wtf_string.h"
......@@ -39,11 +42,15 @@ namespace blink {
class ExceptionState;
class ScriptState;
class IDBFactory final : public ScriptWrappable {
class MODULES_EXPORT IDBFactory final : public ScriptWrappable {
DEFINE_WRAPPERTYPEINFO();
public:
static IDBFactory* Create() { return new IDBFactory(); }
static IDBFactory* CreateForTest(
std::unique_ptr<WebIDBFactory> web_idb_factory) {
return new IDBFactory(std::move(web_idb_factory));
}
// Implement the IDBFactory IDL
IDBOpenDBRequest* open(ScriptState*, const String& name, ExceptionState&);
......@@ -65,8 +72,11 @@ class IDBFactory final : public ScriptWrappable {
const String& name,
ExceptionState&);
ScriptPromise GetDatabaseInfo(ScriptState*, ExceptionState&);
private:
IDBFactory();
IDBFactory(std::unique_ptr<WebIDBFactory>);
WebIDBFactory* GetFactory();
......
......@@ -29,13 +29,19 @@
Exposed=(Window,Worker)
] interface IDBFactory {
[
NewObject,
CallWith=ScriptState,
Measure,
RaisesException
NewObject,
CallWith=ScriptState,
Measure,
RaisesException
] IDBOpenDBRequest open(DOMString name,
optional [EnforceRange] unsigned long long version);
[NewObject, CallWith=ScriptState, RaisesException] IDBOpenDBRequest deleteDatabase(DOMString name);
[CallWith=ScriptState, RaisesException] short cmp(any first, any second);
[
CallWith=ScriptState,
ImplementedAs=GetDatabaseInfo,
RaisesException,
RuntimeEnabled=IDBGetDatabases
] Promise<sequence<IDBDatabaseInfo>> databases();
};
// 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 "base/memory/ptr_util.h"
#include "base/memory/scoped_refptr.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.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/core/testing/dummy_page_holder.h"
#include "third_party/blink/renderer/modules/indexeddb/mock_web_idb_factory.h"
#include "third_party/blink/renderer/platform/bindings/exception_state.h"
#include "third_party/blink/renderer/platform/bindings/script_state.h"
namespace blink {
namespace {
class TestHelperFunction : public ScriptFunction {
public:
static v8::Local<v8::Function> CreateFunction(ScriptState* script_state,
bool* called_flag) {
auto* self = new TestHelperFunction(script_state, called_flag);
return self->BindToV8Function();
}
private:
TestHelperFunction(ScriptState* script_state, bool* called_flag)
: ScriptFunction(script_state), called_flag_(called_flag) {}
ScriptValue Call(ScriptValue value) override {
*called_flag_ = true;
return value;
}
bool* called_flag_;
};
class IDBFactoryTest : public testing::Test {
protected:
IDBFactoryTest() {}
~IDBFactoryTest() override {}
};
ACTION_TEMPLATE(SaveUniquePointer,
HAS_1_TEMPLATE_PARAMS(int, k),
AND_1_VALUE_PARAMS(unique_pointer)) {
*unique_pointer = base::WrapUnique(std::get<k>(args));
}
TEST_F(IDBFactoryTest, WebIDBGetDBNamesCallbacksResolvesPromise) {
V8TestingScope scope;
std::unique_ptr<MockWebIDBFactory> web_factory = MockWebIDBFactory::Create();
std::unique_ptr<WebIDBCallbacks> wc;
EXPECT_CALL(*web_factory, GetDatabaseInfo(testing::_, testing::_, testing::_))
.Times(1)
.WillOnce(SaveUniquePointer<0>(&wc));
IDBFactory* factory = IDBFactory::CreateForTest(std::move(web_factory));
DummyExceptionStateForTesting exception_state;
ScriptPromise promise =
factory->GetDatabaseInfo(scope.GetScriptState(), exception_state);
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);
const WebVector<WebIDBNameAndVersion> wv;
wc->OnSuccess(wv);
EXPECT_FALSE(on_fulfilled);
EXPECT_FALSE(on_rejected);
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
EXPECT_TRUE(on_fulfilled);
EXPECT_FALSE(on_rejected);
}
TEST_F(IDBFactoryTest, WebIDBGetDBNamesCallbacksRejectsPromise) {
V8TestingScope scope;
std::unique_ptr<MockWebIDBFactory> web_factory = MockWebIDBFactory::Create();
std::unique_ptr<WebIDBCallbacks> wc;
EXPECT_CALL(*web_factory, GetDatabaseInfo(testing::_, testing::_, testing::_))
.Times(1)
.WillOnce(SaveUniquePointer<0>(&wc));
IDBFactory* factory = IDBFactory::CreateForTest(std::move(web_factory));
DummyExceptionStateForTesting exception_state;
ScriptPromise promise =
factory->GetDatabaseInfo(scope.GetScriptState(), exception_state);
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);
const WebVector<WebIDBNameAndVersion> wv;
wc->OnError(WebIDBDatabaseError(1));
EXPECT_FALSE(on_fulfilled);
EXPECT_FALSE(on_rejected);
v8::MicrotasksScope::PerformCheckpoint(scope.GetIsolate());
EXPECT_FALSE(on_fulfilled);
EXPECT_TRUE(on_rejected);
}
} // namespace
} // namespace blink
// 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/mock_web_idb_factory.h"
#include <memory>
#include "base/memory/ptr_util.h"
namespace blink {
MockWebIDBFactory::MockWebIDBFactory() = default;
MockWebIDBFactory::~MockWebIDBFactory() = default;
std::unique_ptr<MockWebIDBFactory> MockWebIDBFactory::Create() {
return base::WrapUnique(new MockWebIDBFactory());
}
} // namespace blink
// 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.
#ifndef THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_FACTORY_H_
#define THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_FACTORY_H_
#include <gmock/gmock.h>
#include <memory>
#include "base/single_thread_task_runner.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_factory.h"
#include "third_party/blink/public/platform/web_common.h"
#include "third_party/blink/public/platform/web_security_origin.h"
namespace base {
class SingleThreadTaskRunner;
}
namespace blink {
class WebIDBCallbacks;
class WebIDBDatabaseCallbacks;
class WebSecurityOrigin;
class WebString;
class MockWebIDBFactory : public testing::StrictMock<blink::WebIDBFactory> {
public:
~MockWebIDBFactory() override;
static std::unique_ptr<MockWebIDBFactory> Create();
MOCK_METHOD3(GetDatabaseInfo,
void(WebIDBCallbacks*,
const WebSecurityOrigin&,
scoped_refptr<base::SingleThreadTaskRunner>));
MOCK_METHOD3(GetDatabaseNames,
void(WebIDBCallbacks*,
const WebSecurityOrigin&,
scoped_refptr<base::SingleThreadTaskRunner>));
MOCK_METHOD7(Open,
void(const WebString& name,
long long version,
long long transaction_id,
WebIDBCallbacks*,
WebIDBDatabaseCallbacks*,
const WebSecurityOrigin&,
scoped_refptr<base::SingleThreadTaskRunner>));
MOCK_METHOD5(DeleteDatabase,
void(const WebString& name,
WebIDBCallbacks*,
const WebSecurityOrigin&,
bool force_close,
scoped_refptr<base::SingleThreadTaskRunner>));
private:
MockWebIDBFactory();
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_INDEXEDDB_MOCK_WEB_IDB_FACTORY_H_
......@@ -35,6 +35,7 @@
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_database_error.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_key.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_name_and_version.h"
#include "third_party/blink/public/platform/modules/indexeddb/web_idb_value.h"
#include "third_party/blink/renderer/core/dom/dom_exception.h"
#include "third_party/blink/renderer/core/probe/core_probes.h"
......@@ -51,6 +52,7 @@ using blink::WebIDBDatabaseError;
using blink::WebIDBKey;
using blink::WebIDBKeyPath;
using blink::WebIDBMetadata;
using blink::WebIDBNameAndVersion;
using blink::WebIDBValue;
using blink::WebVector;
......@@ -87,6 +89,12 @@ void WebIDBCallbacksImpl::OnError(const WebIDBDatabaseError& error) {
static_cast<DOMExceptionCode>(error.Code()), error.Message()));
}
void WebIDBCallbacksImpl::OnSuccess(
const WebVector<WebIDBNameAndVersion>& web_name_and_version_list) {
// Only implemented in idb_factory.cc for the promise-based databases() call.
NOTREACHED();
}
void WebIDBCallbacksImpl::OnSuccess(
const WebVector<WebString>& web_string_list) {
if (!request_)
......
......@@ -43,6 +43,7 @@ class WebIDBDatabase;
class WebIDBDatabaseError;
class WebIDBKey;
struct WebIDBMetadata;
struct WebIDBNameAndVersion;
class WebIDBValue;
class WebIDBCallbacksImpl final : public WebIDBCallbacks {
......@@ -55,6 +56,7 @@ class WebIDBCallbacksImpl final : public WebIDBCallbacks {
// Pointers transfer ownership.
void OnError(const WebIDBDatabaseError&) override;
void OnSuccess(const WebVector<WebIDBNameAndVersion>&) override;
void OnSuccess(const WebVector<WebString>&) override;
void OnSuccess(WebIDBCursor*,
WebIDBKey,
......
......@@ -541,6 +541,7 @@ modules_dictionary_idl_files =
"imagecapture/constrain_point_2d_parameters.idl",
"imagecapture/photo_settings.idl",
"imagecapture/point_2d.idl",
"indexeddb/idb_database_info.idl",
"indexeddb/idb_index_parameters.idl",
"indexeddb/idb_object_store_parameters.idl",
"indexeddb/idb_observer_init.idl",
......
......@@ -584,6 +584,10 @@
name: "HTMLImportsStyleApplication",
status: "stable",
},
{
name: "IDBGetDatabases",
status: "experimental",
},
{
name: "IDBObserver",
status: "experimental",
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment