Commit d9377f2a authored by Chase Phillips's avatar Chase Phillips Committed by Commit Bot

IndexedDB: Make IDBCursor.CursorContinue() use native Mojo callback

Similar to the IDBCursor.Advance() commit 9a58b89a,
IDBCursor.CursorContinue() previously took a separate IDBCallbacks
interface that had its own lifecycle and could have a number of
methods called on it.

This CL updates CursorContinue() to use Mojo's native callback
mechanism.

Bug: 717812
Change-Id: Icc7d8b0be70a078f31d658ac9c35d83d8af12b0e
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1480810
Commit-Queue: Chase Phillips <cmp@chromium.org>
Reviewed-by: default avatarTom Sepez <tsepez@chromium.org>
Reviewed-by: default avatarDaniel Murphy <dmurph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#638234}
parent 67c41a7b
...@@ -24,9 +24,10 @@ class CursorImpl::IDBSequenceHelper { ...@@ -24,9 +24,10 @@ class CursorImpl::IDBSequenceHelper {
void Advance(uint32_t count, void Advance(uint32_t count,
base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host, base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
blink::mojom::IDBCursor::AdvanceCallback callback); blink::mojom::IDBCursor::AdvanceCallback callback);
void Continue(const IndexedDBKey& key, void Continue(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
const IndexedDBKey& key,
const IndexedDBKey& primary_key, const IndexedDBKey& primary_key,
scoped_refptr<IndexedDBCallbacks> callbacks); blink::mojom::IDBCursor::CursorContinueCallback callback);
void Prefetch(int32_t count, scoped_refptr<IndexedDBCallbacks> callbacks); void Prefetch(int32_t count, scoped_refptr<IndexedDBCallbacks> callbacks);
void PrefetchReset(int32_t used_prefetches, int32_t unused_prefetches); void PrefetchReset(int32_t used_prefetches, int32_t unused_prefetches);
...@@ -66,12 +67,10 @@ void CursorImpl::Advance(uint32_t count, ...@@ -66,12 +67,10 @@ void CursorImpl::Advance(uint32_t count,
void CursorImpl::CursorContinue( void CursorImpl::CursorContinue(
const IndexedDBKey& key, const IndexedDBKey& key,
const IndexedDBKey& primary_key, const IndexedDBKey& primary_key,
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks_info) { blink::mojom::IDBCursor::CursorContinueCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
scoped_refptr<IndexedDBCallbacks> callbacks( helper_->Continue(dispatcher_host_->AsWeakPtr(), key, primary_key,
new IndexedDBCallbacks(dispatcher_host_->AsWeakPtr(), origin_, std::move(callback));
std::move(callbacks_info), idb_runner_));
helper_->Continue(key, primary_key, std::move(callbacks));
} }
void CursorImpl::Prefetch( void CursorImpl::Prefetch(
...@@ -116,15 +115,17 @@ void CursorImpl::IDBSequenceHelper::Advance( ...@@ -116,15 +115,17 @@ void CursorImpl::IDBSequenceHelper::Advance(
} }
void CursorImpl::IDBSequenceHelper::Continue( void CursorImpl::IDBSequenceHelper::Continue(
base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,
const IndexedDBKey& key, const IndexedDBKey& key,
const IndexedDBKey& primary_key, const IndexedDBKey& primary_key,
scoped_refptr<IndexedDBCallbacks> callbacks) { blink::mojom::IDBCursor::CursorContinueCallback callback) {
DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
cursor_->Continue( cursor_->Continue(
std::move(dispatcher_host),
key.IsValid() ? std::make_unique<IndexedDBKey>(key) : nullptr, key.IsValid() ? std::make_unique<IndexedDBKey>(key) : nullptr,
primary_key.IsValid() ? std::make_unique<IndexedDBKey>(primary_key) primary_key.IsValid() ? std::make_unique<IndexedDBKey>(primary_key)
: nullptr, : nullptr,
std::move(callbacks)); std::move(callback));
} }
void CursorImpl::IDBSequenceHelper::Prefetch( void CursorImpl::IDBSequenceHelper::Prefetch(
......
...@@ -36,7 +36,7 @@ class CursorImpl : public blink::mojom::IDBCursor { ...@@ -36,7 +36,7 @@ class CursorImpl : public blink::mojom::IDBCursor {
void CursorContinue( void CursorContinue(
const blink::IndexedDBKey& key, const blink::IndexedDBKey& key,
const blink::IndexedDBKey& primary_key, const blink::IndexedDBKey& primary_key,
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks) override; blink::mojom::IDBCursor::CursorContinueCallback callback) override;
void Prefetch(int32_t count, void Prefetch(int32_t count,
blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks) override; blink::mojom::IDBCallbacksAssociatedPtrInfo callbacks) override;
void PrefetchReset(int32_t used_prefetches, void PrefetchReset(int32_t used_prefetches,
......
...@@ -36,6 +36,14 @@ IndexedDBDatabaseError CreateCursorClosedError() { ...@@ -36,6 +36,14 @@ IndexedDBDatabaseError CreateCursorClosedError() {
"The cursor has been closed."); "The cursor has been closed.");
} }
IndexedDBDatabaseError CreateError(uint16_t code,
const char* message,
IndexedDBTransaction* transaction) {
DCHECK(transaction);
transaction->IncrementNumErrorsSent();
return IndexedDBDatabaseError(code, message);
}
leveldb::Status InvokeOrSucceed(base::WeakPtr<IndexedDBCursor> weak_cursor, leveldb::Status InvokeOrSucceed(base::WeakPtr<IndexedDBCursor> weak_cursor,
IndexedDBTransaction::Operation operation, IndexedDBTransaction::Operation operation,
IndexedDBTransaction* transaction) { IndexedDBTransaction* transaction) {
...@@ -82,24 +90,6 @@ IndexedDBCursor::~IndexedDBCursor() { ...@@ -82,24 +90,6 @@ IndexedDBCursor::~IndexedDBCursor() {
Close(); Close();
} }
void IndexedDBCursor::Continue(std::unique_ptr<IndexedDBKey> key,
std::unique_ptr<IndexedDBKey> primary_key,
scoped_refptr<IndexedDBCallbacks> callbacks) {
IDB_TRACE("IndexedDBCursor::Continue");
if (closed_) {
callbacks->OnError(CreateCursorClosedError());
return;
}
transaction_->ScheduleTask(
task_type_,
BindWeakOperation(&IndexedDBCursor::CursorIterationOperation,
ptr_factory_.GetWeakPtr(), base::Passed(&key),
base::Passed(&primary_key), callbacks));
}
void IndexedDBCursor::Advance( void IndexedDBCursor::Advance(
uint32_t count, uint32_t count,
base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host, base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,
...@@ -107,11 +97,9 @@ void IndexedDBCursor::Advance( ...@@ -107,11 +97,9 @@ void IndexedDBCursor::Advance(
IDB_TRACE("IndexedDBCursor::Advance"); IDB_TRACE("IndexedDBCursor::Advance");
if (closed_) { if (closed_) {
const IndexedDBDatabaseError closed_error(CreateCursorClosedError()); const IndexedDBDatabaseError error(CreateCursorClosedError());
DCHECK_NE(closed_error.code(), 0);
std::move(callback).Run( std::move(callback).Run(
CreateIDBError(closed_error.code(), blink::mojom::IDBError::New(error.code(), error.message()),
base::string16(closed_error.message()), transaction_),
blink::mojom::IDBCursorValuePtr()); blink::mojom::IDBCursorValuePtr());
return; return;
} }
...@@ -120,7 +108,7 @@ void IndexedDBCursor::Advance( ...@@ -120,7 +108,7 @@ void IndexedDBCursor::Advance(
task_type_, task_type_,
BindWeakOperation( BindWeakOperation(
&IndexedDBCursor::CursorAdvanceOperation, ptr_factory_.GetWeakPtr(), &IndexedDBCursor::CursorAdvanceOperation, ptr_factory_.GetWeakPtr(),
count, dispatcher_host, count, std::move(dispatcher_host),
base::WrapRefCounted(dispatcher_host->context()->TaskRunner()), base::WrapRefCounted(dispatcher_host->context()->TaskRunner()),
std::move(callback))); std::move(callback)));
} }
...@@ -145,13 +133,13 @@ leveldb::Status IndexedDBCursor::CursorAdvanceOperation( ...@@ -145,13 +133,13 @@ leveldb::Status IndexedDBCursor::CursorAdvanceOperation(
return s; return s;
} }
// CreateIDBError() needs to be called before calling Close() so // CreateError() needs to be called before calling Close() so
// |transaction_| is alive. // |transaction_| is alive.
auto error = CreateIDBError(blink::kWebIDBDatabaseExceptionUnknownError, auto error = CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
base::ASCIIToUTF16("Error advancing cursor"), "Error advancing cursor", transaction_);
transaction_);
Close(); Close();
std::move(callback).Run(std::move(error), std::move(callback).Run(
blink::mojom::IDBError::New(error.code(), error.message()),
blink::mojom::IDBCursorValuePtr()); blink::mojom::IDBCursorValuePtr());
return s; return s;
} }
...@@ -166,8 +154,9 @@ leveldb::Status IndexedDBCursor::CursorAdvanceOperation( ...@@ -166,8 +154,9 @@ leveldb::Status IndexedDBCursor::CursorAdvanceOperation(
if (!IndexedDBCallbacks::CreateAllBlobs( if (!IndexedDBCallbacks::CreateAllBlobs(
dispatcher_host->blob_storage_context(), idb_runner, dispatcher_host->blob_storage_context(), idb_runner,
IndexedDBCallbacks::IndexedDBValueBlob::GetIndexedDBValueBlobs( IndexedDBCallbacks::IndexedDBValueBlob::GetIndexedDBValueBlobs(
blob_info, &mojo_value->blob_or_file_info))) blob_info, &mojo_value->blob_or_file_info))) {
return s; return s;
}
} else { } else {
mojo_value = blink::mojom::IDBValue::New(); mojo_value = blink::mojom::IDBValue::New();
} }
...@@ -179,13 +168,40 @@ leveldb::Status IndexedDBCursor::CursorAdvanceOperation( ...@@ -179,13 +168,40 @@ leveldb::Status IndexedDBCursor::CursorAdvanceOperation(
return s; return s;
} }
leveldb::Status IndexedDBCursor::CursorIterationOperation( void IndexedDBCursor::Continue(
base::WeakPtr<content::IndexedDBDispatcherHost> dispatcher_host,
std::unique_ptr<IndexedDBKey> key, std::unique_ptr<IndexedDBKey> key,
std::unique_ptr<IndexedDBKey> primary_key, std::unique_ptr<IndexedDBKey> primary_key,
scoped_refptr<IndexedDBCallbacks> callbacks, blink::mojom::IDBCursor::CursorContinueCallback callback) {
IDB_TRACE("IndexedDBCursor::Continue");
if (closed_) {
const IndexedDBDatabaseError error(CreateCursorClosedError());
std::move(callback).Run(
blink::mojom::IDBError::New(error.code(), error.message()),
blink::mojom::IDBCursorValuePtr());
return;
}
transaction_->ScheduleTask(
task_type_,
BindWeakOperation(
&IndexedDBCursor::CursorContinueOperation, ptr_factory_.GetWeakPtr(),
std::move(dispatcher_host),
base::WrapRefCounted(dispatcher_host->context()->TaskRunner()),
base::Passed(&key), base::Passed(&primary_key), std::move(callback)));
}
leveldb::Status IndexedDBCursor::CursorContinueOperation(
base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
scoped_refptr<base::SequencedTaskRunner> idb_runner,
std::unique_ptr<IndexedDBKey> key,
std::unique_ptr<IndexedDBKey> primary_key,
blink::mojom::IDBCursor::CursorContinueCallback callback,
IndexedDBTransaction* /*transaction*/) { IndexedDBTransaction* /*transaction*/) {
IDB_TRACE("IndexedDBCursor::CursorIterationOperation"); IDB_TRACE("IndexedDBCursor::CursorContinueOperation");
leveldb::Status s = leveldb::Status::OK(); leveldb::Status s = leveldb::Status::OK();
if (!dispatcher_host)
return s;
if (!cursor_ || if (!cursor_ ||
!cursor_->Continue(key.get(), primary_key.get(), !cursor_->Continue(key.get(), primary_key.get(),
...@@ -193,18 +209,44 @@ leveldb::Status IndexedDBCursor::CursorIterationOperation( ...@@ -193,18 +209,44 @@ leveldb::Status IndexedDBCursor::CursorIterationOperation(
cursor_.reset(); cursor_.reset();
if (s.ok()) { if (s.ok()) {
// This happens if we reach the end of the iterator and can't continue. // This happens if we reach the end of the iterator and can't continue.
callbacks->OnSuccess(nullptr); std::move(callback).Run(blink::mojom::IDBErrorPtr(),
blink::mojom::IDBCursorValuePtr());
return s; return s;
} }
// |transaction_| must be valid for CreateError(), so we can't call
// Close() until after calling CreateError().
IndexedDBDatabaseError error = IndexedDBDatabaseError error =
CreateError(blink::kWebIDBDatabaseExceptionUnknownError, CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
"Error continuing cursor.", transaction_); "Error continuing cursor.", transaction_);
Close(); Close();
callbacks->OnError(error); std::move(callback).Run(
blink::mojom::IDBError::New(error.code(), error.message()),
blink::mojom::IDBCursorValuePtr());
return s;
}
blink::mojom::IDBValuePtr mojo_value;
std::vector<IndexedDBBlobInfo> blob_info;
IndexedDBValue* value = Value();
if (value) {
mojo_value = IndexedDBValue::ConvertAndEraseValue(value);
blob_info.swap(value->blob_info);
if (!IndexedDBCallbacks::CreateAllBlobs(
dispatcher_host->blob_storage_context(), idb_runner,
IndexedDBCallbacks::IndexedDBValueBlob::GetIndexedDBValueBlobs(
blob_info, &mojo_value->blob_or_file_info))) {
return s; return s;
} }
} else {
mojo_value = blink::mojom::IDBValue::New();
}
callbacks->OnSuccess(this->key(), this->primary_key(), Value()); blink::mojom::IDBCursorValuePtr cursor_value =
blink::mojom::IDBCursorValue::New(this->key(), this->primary_key(),
std::move(mojo_value));
std::move(callback).Run(blink::mojom::IDBErrorPtr(), std::move(cursor_value));
return s; return s;
} }
...@@ -254,7 +296,7 @@ leveldb::Status IndexedDBCursor::CursorPrefetchIterationOperation( ...@@ -254,7 +296,7 @@ leveldb::Status IndexedDBCursor::CursorPrefetchIterationOperation(
CreateError(blink::kWebIDBDatabaseExceptionUnknownError, CreateError(blink::kWebIDBDatabaseExceptionUnknownError,
"Error continuing cursor.", transaction_); "Error continuing cursor.", transaction_);
Close(); Close();
callbacks->OnError(error); callbacks->OnError(std::move(error));
return s; return s;
} }
...@@ -338,22 +380,4 @@ void IndexedDBCursor::Close() { ...@@ -338,22 +380,4 @@ void IndexedDBCursor::Close() {
transaction_ = nullptr; transaction_ = nullptr;
} }
IndexedDBDatabaseError IndexedDBCursor::CreateError(
uint16_t code,
const char* message,
IndexedDBTransaction* transaction) {
DCHECK(transaction);
transaction->IncrementNumErrorsSent();
return IndexedDBDatabaseError(code, message);
}
blink::mojom::IDBErrorPtr IndexedDBCursor::CreateIDBError(
uint16_t code,
const base::string16& message,
IndexedDBTransaction* transaction) {
DCHECK(transaction);
transaction->IncrementNumErrorsSent();
return blink::mojom::IDBError::New(code, message);
}
} // namespace content } // namespace content
...@@ -32,9 +32,10 @@ class CONTENT_EXPORT IndexedDBCursor { ...@@ -32,9 +32,10 @@ class CONTENT_EXPORT IndexedDBCursor {
void Advance(uint32_t count, void Advance(uint32_t count,
base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host, base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
blink::mojom::IDBCursor::AdvanceCallback callback); blink::mojom::IDBCursor::AdvanceCallback callback);
void Continue(std::unique_ptr<blink::IndexedDBKey> key, void Continue(base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
std::unique_ptr<blink::IndexedDBKey> key,
std::unique_ptr<blink::IndexedDBKey> primary_key, std::unique_ptr<blink::IndexedDBKey> primary_key,
scoped_refptr<IndexedDBCallbacks> callbacks); blink::mojom::IDBCursor::CursorContinueCallback callback);
void PrefetchContinue(int number_to_fetch, void PrefetchContinue(int number_to_fetch,
scoped_refptr<IndexedDBCallbacks> callbacks); scoped_refptr<IndexedDBCallbacks> callbacks);
leveldb::Status PrefetchReset(int used_prefetches, int unused_prefetches); leveldb::Status PrefetchReset(int used_prefetches, int unused_prefetches);
...@@ -55,10 +56,12 @@ class CONTENT_EXPORT IndexedDBCursor { ...@@ -55,10 +56,12 @@ class CONTENT_EXPORT IndexedDBCursor {
void RemoveBinding(); void RemoveBinding();
void Close(); void Close();
leveldb::Status CursorIterationOperation( leveldb::Status CursorContinueOperation(
base::WeakPtr<IndexedDBDispatcherHost> dispatcher_host,
scoped_refptr<base::SequencedTaskRunner> idb_runner,
std::unique_ptr<blink::IndexedDBKey> key, std::unique_ptr<blink::IndexedDBKey> key,
std::unique_ptr<blink::IndexedDBKey> primary_key, std::unique_ptr<blink::IndexedDBKey> primary_key,
scoped_refptr<IndexedDBCallbacks> callbacks, blink::mojom::IDBCursor::CursorContinueCallback callback,
IndexedDBTransaction* transaction); IndexedDBTransaction* transaction);
leveldb::Status CursorAdvanceOperation( leveldb::Status CursorAdvanceOperation(
uint32_t count, uint32_t count,
...@@ -71,14 +74,6 @@ class CONTENT_EXPORT IndexedDBCursor { ...@@ -71,14 +74,6 @@ class CONTENT_EXPORT IndexedDBCursor {
scoped_refptr<IndexedDBCallbacks> callbacks, scoped_refptr<IndexedDBCallbacks> callbacks,
IndexedDBTransaction* transaction); IndexedDBTransaction* transaction);
static IndexedDBDatabaseError CreateError(uint16_t code,
const char* message,
IndexedDBTransaction* transaction);
static blink::mojom::IDBErrorPtr CreateIDBError(
uint16_t code,
const base::string16& message,
IndexedDBTransaction* transaction);
private: private:
blink::mojom::IDBTaskType task_type_; blink::mojom::IDBTaskType task_type_;
indexed_db::CursorType cursor_type_; indexed_db::CursorType cursor_type_;
......
...@@ -288,12 +288,14 @@ interface IDBCursor { ...@@ -288,12 +288,14 @@ interface IDBCursor {
// of the source being iterated is reached // of the source being iterated is reached
Advance(uint32 count) => (IDBError? error, IDBCursorValue? value); Advance(uint32 count) => (IDBError? error, IDBCursorValue? value);
// Avoid calling the following function "Continue" since the Mojo // CursorContinue() can call its return callback in one of 3 ways:
// Java bindings generate incorrect Java as a result. We've named // * with |error| set and |value| unset, if an error occurs
// the following function "CursorContinue" as a workaround. // * with |error| unset and |value| set, under normal operation
CursorContinue(IDBKey key, // * with |error| unset and |value| unset, under normal operation when the end
IDBKey primary_key, // of the source being iterated is reached
associated IDBCallbacks callbacks); CursorContinue(IDBKey key, IDBKey primary_key)
=> (IDBError? error, IDBCursorValue? value);
Prefetch(int32 count, associated IDBCallbacks callbacks); Prefetch(int32 count, associated IDBCallbacks callbacks);
PrefetchReset(int32 used_prefetches, int32 unused_prefetches); PrefetchReset(int32 used_prefetches, int32 unused_prefetches);
}; };
......
...@@ -121,10 +121,33 @@ void WebIDBCursorImpl::CursorContinue(const IDBKey* key, ...@@ -121,10 +121,33 @@ void WebIDBCursorImpl::CursorContinue(const IDBKey* key,
// Reset all cursor prefetch caches except for this cursor. // Reset all cursor prefetch caches except for this cursor.
IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id_, this); IndexedDBDispatcher::ResetCursorPrefetchCaches(transaction_id_, this);
callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_); callbacks->SetState(weak_factory_.GetWeakPtr(), transaction_id_);
cursor_->CursorContinue(IDBKey::Clone(key), IDBKey::Clone(primary_key), cursor_->CursorContinue(
GetCallbacksProxy(std::move(callbacks))); IDBKey::Clone(key), IDBKey::Clone(primary_key),
WTF::Bind(&WebIDBCursorImpl::CursorContinueCallback,
WTF::Unretained(this), std::move(callbacks)));
}
void WebIDBCursorImpl::CursorContinueCallback(
std::unique_ptr<WebIDBCallbacks> callbacks,
mojom::blink::IDBErrorPtr error,
mojom::blink::IDBCursorValuePtr value) {
if (error) {
callbacks->Error(error->error_code, error->error_message);
callbacks.reset();
return;
}
if (!value) {
callbacks->SuccessValue(nullptr);
callbacks.reset();
return;
}
callbacks->SuccessCursorContinue(std::move(value->key),
std::move(value->primary_key),
std::move(value->value));
callbacks.reset();
} }
void WebIDBCursorImpl::PostSuccessHandlerCallback() { void WebIDBCursorImpl::PostSuccessHandlerCallback() {
......
...@@ -30,6 +30,10 @@ class MODULES_EXPORT WebIDBCursorImpl : public WebIDBCursor { ...@@ -30,6 +30,10 @@ class MODULES_EXPORT WebIDBCursorImpl : public WebIDBCursor {
void CursorContinue(const IDBKey* key, void CursorContinue(const IDBKey* key,
const IDBKey* primary_key, const IDBKey* primary_key,
WebIDBCallbacks* callback) override; WebIDBCallbacks* callback) override;
void CursorContinueCallback(std::unique_ptr<WebIDBCallbacks> callbacks,
mojom::blink::IDBErrorPtr error,
mojom::blink::IDBCursorValuePtr value);
void PostSuccessHandlerCallback() override; void PostSuccessHandlerCallback() override;
void SetPrefetchData(Vector<std::unique_ptr<IDBKey>> keys, void SetPrefetchData(Vector<std::unique_ptr<IDBKey>> keys,
......
...@@ -54,8 +54,10 @@ class MockCursorImpl : public mojom::blink::IDBCursor { ...@@ -54,8 +54,10 @@ class MockCursorImpl : public mojom::blink::IDBCursor {
void CursorContinue( void CursorContinue(
std::unique_ptr<IDBKey> key, std::unique_ptr<IDBKey> key,
std::unique_ptr<IDBKey> primary_key, std::unique_ptr<IDBKey> primary_key,
mojom::blink::IDBCallbacksAssociatedPtrInfo callbacks) override { mojom::blink::IDBCursor::CursorContinueCallback callback) override {
++continue_calls_; ++continue_calls_;
std::move(callback).Run(mojom::blink::IDBErrorPtr(),
mojom::blink::IDBCursorValuePtr());
} }
void CursorDestroyed() { destroyed_ = true; } void CursorDestroyed() { destroyed_ = true; }
......
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