Commit a6e61490 authored by Victor Costan's avatar Victor Costan Committed by Commit Bot

IndexedDB: Mark transactions inactive during structured cloning.

Bug: 1016038
Change-Id: I3faa9e5071e09a5923e9321fc1449431f22ca666
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1869384
Commit-Queue: Victor Costan <pwnall@chromium.org>
Reviewed-by: default avatarJoshua Bell <jsbell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#708006}
parent f60af82e
...@@ -414,6 +414,7 @@ IDBRequest* IDBObjectStore::DoPut(ScriptState* script_state, ...@@ -414,6 +414,7 @@ IDBRequest* IDBObjectStore::DoPut(ScriptState* script_state,
v8::Isolate* isolate = script_state->GetIsolate(); v8::Isolate* isolate = script_state->GetIsolate();
DCHECK(isolate->InContext()); DCHECK(isolate->InContext());
transaction_->SetActiveDuringSerialization(false);
// TODO(crbug.com/719053): This wasm behavior differs from other browsers. // TODO(crbug.com/719053): This wasm behavior differs from other browsers.
SerializedScriptValue::SerializeOptions::WasmSerializationPolicy wasm_policy = SerializedScriptValue::SerializeOptions::WasmSerializationPolicy wasm_policy =
ExecutionContext::From(script_state)->IsSecureContext() ExecutionContext::From(script_state)->IsSecureContext()
...@@ -421,6 +422,7 @@ IDBRequest* IDBObjectStore::DoPut(ScriptState* script_state, ...@@ -421,6 +422,7 @@ IDBRequest* IDBObjectStore::DoPut(ScriptState* script_state,
: SerializedScriptValue::SerializeOptions::kBlockedInNonSecureContext; : SerializedScriptValue::SerializeOptions::kBlockedInNonSecureContext;
IDBValueWrapper value_wrapper(isolate, value.V8Value(), wasm_policy, IDBValueWrapper value_wrapper(isolate, value.V8Value(), wasm_policy,
exception_state); exception_state);
transaction_->SetActiveDuringSerialization(true);
if (exception_state.HadException()) if (exception_state.HadException())
return nullptr; return nullptr;
......
...@@ -320,18 +320,30 @@ void IDBTransaction::IndexDeleted(IDBIndex* index) { ...@@ -320,18 +320,30 @@ void IDBTransaction::IndexDeleted(IDBIndex* index) {
deleted_indexes_.push_back(index); deleted_indexes_.push_back(index);
} }
void IDBTransaction::SetActive(bool active) { void IDBTransaction::SetActive(bool new_is_active) {
DCHECK_NE(state_, kFinished) << "A finished transaction tried to SetActive(" DCHECK_NE(state_, kFinished)
<< (active ? "true" : "false") << ")"; << "A finished transaction tried to SetActive(" << new_is_active << ")";
if (state_ == kFinishing) if (state_ == kFinishing)
return; return;
DCHECK_NE(active, (state_ == kActive)); DCHECK_NE(new_is_active, (state_ == kActive));
state_ = active ? kActive : kInactive; state_ = new_is_active ? kActive : kInactive;
if (!active && request_list_.IsEmpty() && transaction_backend()) if (!new_is_active && request_list_.IsEmpty() && transaction_backend())
transaction_backend()->Commit(num_errors_handled_); transaction_backend()->Commit(num_errors_handled_);
} }
void IDBTransaction::SetActiveDuringSerialization(bool new_is_active) {
if (new_is_active) {
DCHECK_EQ(state_, kInactive)
<< "Incorrect state restore during Structured Serialization";
state_ = kActive;
} else {
DCHECK_EQ(state_, kActive)
<< "Structured serialization attempted while transaction is inactive";
state_ = kInactive;
}
}
void IDBTransaction::abort(ExceptionState& exception_state) { void IDBTransaction::abort(ExceptionState& exception_state) {
if (state_ == kFinishing || state_ == kFinished) { if (state_ == kFinishing || state_ == kFinished) {
exception_state.ThrowDOMException( exception_state.ThrowDOMException(
......
...@@ -155,7 +155,14 @@ class MODULES_EXPORT IDBTransaction final ...@@ -155,7 +155,14 @@ class MODULES_EXPORT IDBTransaction final
// Called when deleting an index whose IDBIndex had been created. // Called when deleting an index whose IDBIndex had been created.
void IndexDeleted(IDBIndex*); void IndexDeleted(IDBIndex*);
void SetActive(bool); // Called during event dispatch.
//
// This can trigger transaction auto-commit.
void SetActive(bool new_is_active);
// Called right before and after structured serialization.
void SetActiveDuringSerialization(bool new_is_active);
void SetError(DOMException*); void SetError(DOMException*);
DEFINE_ATTRIBUTE_EVENT_LISTENER(abort, kAbort) DEFINE_ATTRIBUTE_EVENT_LISTENER(abort, kAbort)
......
// META: script=support-promises.js
// META: title=Indexed DB transaction state during Structured Serializing
// META: timeout=long
'use strict';
promise_test(async testCase => {
const db = await createDatabase(testCase, database => {
database.createObjectStore('store');
});
const transaction = db.transaction(['store'], 'readwrite');
const objectStore = transaction.objectStore('store');
let getterCalled = false;
const activeValue = {};
Object.defineProperty(activeValue, 'propertyName', {
enumerable: true,
get: testCase.step_func(() => {
getterCalled = true;
assert_throws('TransactionInactiveError', () => {
objectStore.get('key');
}, 'transaction should not be active during structured clone');
return 'value that should not be used';
}),
});
objectStore.add(activeValue, 'key');
await promiseForTransaction(testCase, transaction);
db.close();
assert_true(getterCalled,
"activeValue's getter should be called during test");
}, 'Transaction inactive during structured clone in IDBObjectStore.add()');
promise_test(async testCase => {
const db = await createDatabase(testCase, database => {
database.createObjectStore('store');
});
const transaction = db.transaction(['store'], 'readwrite');
const objectStore = transaction.objectStore('store');
let getterCalled = false;
const activeValue = {};
Object.defineProperty(activeValue, 'propertyName', {
enumerable: true,
get: testCase.step_func(() => {
getterCalled = true;
assert_throws('TransactionInactiveError', () => {
objectStore.get('key');
}, 'transaction should not be active during structured clone');
return 'value that should not be used';
}),
});
objectStore.put(activeValue, 'key');
await promiseForTransaction(testCase, transaction);
db.close();
assert_true(getterCalled,
"activeValue's getter should be called during test");
}, 'Transaction inactive during structured clone in IDBObjectStore.put()');
promise_test(async testCase => {
const db = await createDatabase(testCase, database => {
const objectStore = database.createObjectStore('store');
objectStore.put({}, 'key');
});
const transaction = db.transaction(['store'], 'readwrite');
const objectStore = transaction.objectStore('store');
let getterCalled = false;
const activeValue = {};
Object.defineProperty(activeValue, 'propertyName', {
enumerable: true,
get: testCase.step_func(() => {
getterCalled = true;
assert_throws('TransactionInactiveError', () => {
objectStore.get('key');
}, 'transaction should not be active during structured clone');
return 'value that should not be used';
}),
});
const request = objectStore.openCursor();
request.onsuccess = testCase.step_func(() => {
const cursor = request.result;
cursor.update(activeValue);
});
await promiseForTransaction(testCase, transaction);
db.close();
assert_true(getterCalled,
"activeValue's getter should be called during test");
}, 'Transaction inactive during structured clone in IDBCursor.update()');
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