mirror of
https://github.com/servo/servo.git
synced 2025-07-19 05:13:55 +01:00
Move common indexeddb methods out of dom implementations (#38101)
A lot of shared functions were scattered around the dom files; I moved them into `indexed_db.rs` for clarity. Fixes: Nothing to my knowledge, just a cleanup Signed-off-by: Ashwin Naren <arihant2math@gmail.com>
This commit is contained in:
parent
e10466b4c4
commit
f70a4eb4ff
8 changed files with 314 additions and 299 deletions
|
@ -5,16 +5,16 @@
|
||||||
use std::default::Default;
|
use std::default::Default;
|
||||||
use std::iter;
|
use std::iter;
|
||||||
|
|
||||||
use webrender_api::units::DeviceIntRect;
|
|
||||||
use ipc_channel::ipc;
|
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
|
use embedder_traits::{EmbedderMsg, FormControl as EmbedderFormControl};
|
||||||
|
use embedder_traits::{SelectElementOption, SelectElementOptionOrOptgroup};
|
||||||
|
use euclid::{Point2D, Rect, Size2D};
|
||||||
use html5ever::{LocalName, Prefix, local_name};
|
use html5ever::{LocalName, Prefix, local_name};
|
||||||
|
use ipc_channel::ipc;
|
||||||
use js::rust::HandleObject;
|
use js::rust::HandleObject;
|
||||||
use style::attr::AttrValue;
|
use style::attr::AttrValue;
|
||||||
use stylo_dom::ElementState;
|
use stylo_dom::ElementState;
|
||||||
use embedder_traits::{SelectElementOptionOrOptgroup, SelectElementOption};
|
use webrender_api::units::DeviceIntRect;
|
||||||
use euclid::{Size2D, Point2D, Rect};
|
|
||||||
use embedder_traits::{FormControl as EmbedderFormControl, EmbedderMsg};
|
|
||||||
|
|
||||||
use crate::dom::bindings::refcounted::Trusted;
|
use crate::dom::bindings::refcounted::Trusted;
|
||||||
use crate::dom::event::{EventBubbles, EventCancelable, EventComposed};
|
use crate::dom::event::{EventBubbles, EventCancelable, EventComposed};
|
||||||
|
|
|
@ -30,6 +30,7 @@ use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::idbobjectstore::IDBObjectStore;
|
use crate::dom::idbobjectstore::IDBObjectStore;
|
||||||
use crate::dom::idbtransaction::IDBTransaction;
|
use crate::dom::idbtransaction::IDBTransaction;
|
||||||
use crate::dom::idbversionchangeevent::IDBVersionChangeEvent;
|
use crate::dom::idbversionchangeevent::IDBVersionChangeEvent;
|
||||||
|
use crate::indexed_db::is_valid_key_path;
|
||||||
use crate::script_runtime::CanGc;
|
use crate::script_runtime::CanGc;
|
||||||
|
|
||||||
#[dom_struct]
|
#[dom_struct]
|
||||||
|
@ -201,7 +202,7 @@ impl IDBDatabaseMethods<crate::DomTypeHolder> for IDBDatabase {
|
||||||
|
|
||||||
// Step 5
|
// Step 5
|
||||||
if let Some(path) = key_path {
|
if let Some(path) = key_path {
|
||||||
if !IDBObjectStore::is_valid_key_path(path) {
|
if !is_valid_key_path(path) {
|
||||||
return Err(Error::Syntax);
|
return Err(Error::Syntax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,24 +2,14 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::ptr;
|
|
||||||
|
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use js::jsapi::{
|
use js::rust::HandleValue;
|
||||||
ESClass, GetBuiltinClass, IsArrayBufferObject, JS_DeleteUCProperty,
|
|
||||||
JS_GetOwnUCPropertyDescriptor, JS_GetStringLength, JS_IsArrayBufferViewObject, JSObject,
|
|
||||||
ObjectOpResult, ObjectOpResult_SpecialCodes, PropertyDescriptor,
|
|
||||||
};
|
|
||||||
use js::jsval::UndefinedValue;
|
|
||||||
use js::rust::{HandleValue, MutableHandleValue};
|
|
||||||
use log::error;
|
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
use net_traits::indexeddb_thread::{
|
use net_traits::indexeddb_thread::{
|
||||||
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, IndexedDBKeyType,
|
AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, IndexedDBKeyType,
|
||||||
IndexedDBThreadMsg, SyncOperation,
|
IndexedDBThreadMsg, SyncOperation,
|
||||||
};
|
};
|
||||||
use profile_traits::ipc;
|
use profile_traits::ipc;
|
||||||
use script_bindings::conversions::SafeToJSValConvertible;
|
|
||||||
|
|
||||||
use crate::dom::bindings::cell::DomRefCell;
|
use crate::dom::bindings::cell::DomRefCell;
|
||||||
use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
|
use crate::dom::bindings::codegen::Bindings::IDBDatabaseBinding::IDBObjectStoreParameters;
|
||||||
|
@ -27,7 +17,6 @@ use crate::dom::bindings::codegen::Bindings::IDBObjectStoreBinding::IDBObjectSto
|
||||||
use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
|
use crate::dom::bindings::codegen::Bindings::IDBTransactionBinding::IDBTransactionMode;
|
||||||
// We need to alias this name, otherwise test-tidy complains at &String reference.
|
// We need to alias this name, otherwise test-tidy complains at &String reference.
|
||||||
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
|
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
|
||||||
use crate::dom::bindings::conversions::jsstring_to_str;
|
|
||||||
use crate::dom::bindings::error::{Error, Fallible};
|
use crate::dom::bindings::error::{Error, Fallible};
|
||||||
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object};
|
||||||
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
use crate::dom::bindings::root::{DomRoot, MutNullableDom};
|
||||||
|
@ -37,6 +26,7 @@ use crate::dom::domstringlist::DOMStringList;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::idbrequest::IDBRequest;
|
use crate::dom::idbrequest::IDBRequest;
|
||||||
use crate::dom::idbtransaction::IDBTransaction;
|
use crate::dom::idbtransaction::IDBTransaction;
|
||||||
|
use crate::indexed_db::{convert_value_to_key, extract_key};
|
||||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||||
|
|
||||||
#[derive(JSTraceable, MallocSizeOf)]
|
#[derive(JSTraceable, MallocSizeOf)]
|
||||||
|
@ -119,229 +109,6 @@ impl IDBObjectStore {
|
||||||
self.transaction.get()
|
self.transaction.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/IndexedDB-2/#valid-key-path
|
|
||||||
pub fn is_valid_key_path(key_path: &StrOrStringSequence) -> bool {
|
|
||||||
fn is_identifier(_s: &str) -> bool {
|
|
||||||
// FIXME: (arihant2math)
|
|
||||||
true
|
|
||||||
}
|
|
||||||
|
|
||||||
let is_valid = |path: &DOMString| {
|
|
||||||
path.is_empty() || is_identifier(path) || path.split(".").all(is_identifier)
|
|
||||||
};
|
|
||||||
|
|
||||||
match key_path {
|
|
||||||
StrOrStringSequence::StringSequence(paths) => {
|
|
||||||
if paths.is_empty() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
paths.iter().all(is_valid)
|
|
||||||
},
|
|
||||||
StrOrStringSequence::String(path) => is_valid(path),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
// https://www.w3.org/TR/IndexedDB-2/#convert-value-to-key
|
|
||||||
fn convert_value_to_key(
|
|
||||||
cx: SafeJSContext,
|
|
||||||
input: HandleValue,
|
|
||||||
seen: Option<Vec<HandleValue>>,
|
|
||||||
) -> Result<IndexedDBKeyType, Error> {
|
|
||||||
// Step 1: If seen was not given, then let seen be a new empty set.
|
|
||||||
let _seen = seen.unwrap_or_default();
|
|
||||||
|
|
||||||
// Step 2: If seen contains input, then return invalid.
|
|
||||||
// FIXME:(arihant2math) implement this
|
|
||||||
// Check if we have seen this key
|
|
||||||
// Does not currently work with HandleValue,
|
|
||||||
// as it does not implement PartialEq
|
|
||||||
|
|
||||||
// Step 3
|
|
||||||
// FIXME:(arihant2math) Accept buffer, array and date as well
|
|
||||||
if input.is_number() {
|
|
||||||
// FIXME:(arihant2math) check for NaN
|
|
||||||
return Ok(IndexedDBKeyType::Number(input.to_number()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.is_string() {
|
|
||||||
let string_ptr = std::ptr::NonNull::new(input.to_string()).unwrap();
|
|
||||||
let key = unsafe { jsstring_to_str(*cx, string_ptr).str().to_string() };
|
|
||||||
return Ok(IndexedDBKeyType::String(key));
|
|
||||||
}
|
|
||||||
|
|
||||||
if input.is_object() {
|
|
||||||
rooted!(in(*cx) let object = input.to_object());
|
|
||||||
unsafe {
|
|
||||||
let mut built_in_class = ESClass::Other;
|
|
||||||
|
|
||||||
if !GetBuiltinClass(*cx, object.handle().into(), &mut built_in_class) {
|
|
||||||
return Err(Error::Data);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ESClass::Date = built_in_class {
|
|
||||||
// FIXME:(arihant2math) implement it the correct way
|
|
||||||
let key =
|
|
||||||
structuredclone::write(cx, input, None).expect("Could not serialize key");
|
|
||||||
return Ok(IndexedDBKeyType::Date(key.serialized.clone()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if IsArrayBufferObject(*object) || JS_IsArrayBufferViewObject(*object) {
|
|
||||||
// FIXME:(arihant2math)
|
|
||||||
error!("Array buffers as keys is currently unsupported");
|
|
||||||
return Err(Error::NotSupported);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let ESClass::Array = built_in_class {
|
|
||||||
// FIXME:(arihant2math)
|
|
||||||
unimplemented!("Arrays as keys is currently unsupported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Err(Error::Data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.w3.org/TR/IndexedDB-2/#evaluate-a-key-path-on-a-value
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
fn evaluate_key_path_on_value(
|
|
||||||
cx: SafeJSContext,
|
|
||||||
value: HandleValue,
|
|
||||||
mut return_val: MutableHandleValue,
|
|
||||||
key_path: &KeyPath,
|
|
||||||
) {
|
|
||||||
// The implementation is translated from gecko:
|
|
||||||
// https://github.com/mozilla/gecko-dev/blob/master/dom/indexedDB/KeyPath.cpp
|
|
||||||
return_val.set(*value);
|
|
||||||
|
|
||||||
rooted!(in(*cx) let mut target_object = ptr::null_mut::<JSObject>());
|
|
||||||
rooted!(in(*cx) let mut current_val = *value);
|
|
||||||
rooted!(in(*cx) let mut object = ptr::null_mut::<JSObject>());
|
|
||||||
|
|
||||||
let mut target_object_prop_name: Option<String> = None;
|
|
||||||
|
|
||||||
match key_path {
|
|
||||||
KeyPath::String(path) => {
|
|
||||||
// Step 3
|
|
||||||
let path_as_string = path.to_string();
|
|
||||||
let mut tokenizer = path_as_string.split('.').peekable();
|
|
||||||
|
|
||||||
while let Some(token) = tokenizer.next() {
|
|
||||||
if target_object.get().is_null() {
|
|
||||||
if token == "length" &&
|
|
||||||
tokenizer.peek().is_none() &&
|
|
||||||
current_val.is_string()
|
|
||||||
{
|
|
||||||
rooted!(in(*cx) let input_val = current_val.to_string());
|
|
||||||
unsafe {
|
|
||||||
let string_len = JS_GetStringLength(*input_val) as u64;
|
|
||||||
string_len.safe_to_jsval(cx, return_val);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if !current_val.is_object() {
|
|
||||||
// FIXME:(rasviitanen) Return a proper error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
object.handle_mut().set(current_val.to_object());
|
|
||||||
rooted!(in(*cx) let mut desc = PropertyDescriptor::default());
|
|
||||||
rooted!(in(*cx) let mut intermediate = UndefinedValue());
|
|
||||||
|
|
||||||
// So rust says that this value is never read, but it is.
|
|
||||||
#[allow(unused)]
|
|
||||||
let mut has_prop = false;
|
|
||||||
|
|
||||||
unsafe {
|
|
||||||
let prop_name_as_utf16: Vec<u16> = token.encode_utf16().collect();
|
|
||||||
let mut is_descriptor_none: bool = false;
|
|
||||||
let ok = JS_GetOwnUCPropertyDescriptor(
|
|
||||||
*cx,
|
|
||||||
object.handle().into(),
|
|
||||||
prop_name_as_utf16.as_ptr(),
|
|
||||||
prop_name_as_utf16.len(),
|
|
||||||
desc.handle_mut().into(),
|
|
||||||
&mut is_descriptor_none,
|
|
||||||
);
|
|
||||||
|
|
||||||
if !ok {
|
|
||||||
// FIXME:(arihant2math) Handle this
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if desc.hasWritable_() || desc.hasValue_() {
|
|
||||||
intermediate.handle_mut().set(desc.handle().value_);
|
|
||||||
has_prop = true;
|
|
||||||
} else {
|
|
||||||
// If we get here it means the object doesn't have the property or the
|
|
||||||
// property is available throuch a getter. We don't want to call any
|
|
||||||
// getters to avoid potential re-entrancy.
|
|
||||||
// The blob object is special since its properties are available
|
|
||||||
// only through getters but we still want to support them for key
|
|
||||||
// extraction. So they need to be handled manually.
|
|
||||||
unimplemented!("Blob tokens are not yet supported");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if has_prop {
|
|
||||||
// Treat undefined as an error
|
|
||||||
if intermediate.is_undefined() {
|
|
||||||
// FIXME:(rasviitanen) Throw/return error
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if tokenizer.peek().is_some() {
|
|
||||||
// ...and walk to it if there are more steps...
|
|
||||||
current_val.handle_mut().set(*intermediate);
|
|
||||||
} else {
|
|
||||||
// ...otherwise use it as key
|
|
||||||
return_val.set(*intermediate);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
target_object.handle_mut().set(*object);
|
|
||||||
target_object_prop_name = Some(token.to_string());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !target_object.get().is_null() {
|
|
||||||
// We have started inserting new objects or are about to just insert
|
|
||||||
// the first one.
|
|
||||||
// FIXME:(rasviitanen) Implement this piece
|
|
||||||
unimplemented!("keyPath tokens that requires insertion are not supported.");
|
|
||||||
}
|
|
||||||
} // All tokens processed
|
|
||||||
|
|
||||||
if !target_object.get().is_null() {
|
|
||||||
// If this fails, we lose, and the web page sees a magical property
|
|
||||||
// appear on the object :-(
|
|
||||||
unsafe {
|
|
||||||
let prop_name_as_utf16: Vec<u16> =
|
|
||||||
target_object_prop_name.unwrap().encode_utf16().collect();
|
|
||||||
#[allow(clippy::cast_enum_truncation)]
|
|
||||||
let mut succeeded = ObjectOpResult {
|
|
||||||
code_: ObjectOpResult_SpecialCodes::Uninitialized as usize,
|
|
||||||
};
|
|
||||||
if !JS_DeleteUCProperty(
|
|
||||||
*cx,
|
|
||||||
target_object.handle().into(),
|
|
||||||
prop_name_as_utf16.as_ptr(),
|
|
||||||
prop_name_as_utf16.len(),
|
|
||||||
&mut succeeded,
|
|
||||||
) {
|
|
||||||
// FIXME:(rasviitanen) Throw/return error
|
|
||||||
// return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
KeyPath::StringSequence(_) => {
|
|
||||||
unimplemented!("String sequence keyPath is currently unsupported");
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn has_key_generator(&self) -> bool {
|
fn has_key_generator(&self) -> bool {
|
||||||
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
|
let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap();
|
||||||
|
|
||||||
|
@ -361,26 +128,6 @@ impl IDBObjectStore {
|
||||||
receiver.recv().unwrap()
|
receiver.recv().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
// https://www.w3.org/TR/IndexedDB-2/#extract-a-key-from-a-value-using-a-key-path
|
|
||||||
fn extract_key(
|
|
||||||
cx: SafeJSContext,
|
|
||||||
input: HandleValue,
|
|
||||||
key_path: &KeyPath,
|
|
||||||
multi_entry: Option<bool>,
|
|
||||||
) -> Result<IndexedDBKeyType, Error> {
|
|
||||||
// Step 1: Evaluate key path
|
|
||||||
// FIXME:(rasviitanen) Do this propertly
|
|
||||||
rooted!(in(*cx) let mut r = UndefinedValue());
|
|
||||||
IDBObjectStore::evaluate_key_path_on_value(cx, input, r.handle_mut(), key_path);
|
|
||||||
|
|
||||||
if let Some(_multi_entry) = multi_entry {
|
|
||||||
// FIXME:(rasviitanen) handle multi_entry cases
|
|
||||||
unimplemented!("multiEntry keys are not yet supported");
|
|
||||||
} else {
|
|
||||||
IDBObjectStore::convert_value_to_key(cx, r.handle(), None)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// https://www.w3.org/TR/IndexedDB-2/#object-store-in-line-keys
|
// https://www.w3.org/TR/IndexedDB-2/#object-store-in-line-keys
|
||||||
fn uses_inline_keys(&self) -> bool {
|
fn uses_inline_keys(&self) -> bool {
|
||||||
self.key_path.is_some()
|
self.key_path.is_some()
|
||||||
|
@ -450,10 +197,10 @@ impl IDBObjectStore {
|
||||||
let serialized_key: IndexedDBKeyType;
|
let serialized_key: IndexedDBKeyType;
|
||||||
|
|
||||||
if !key.is_undefined() {
|
if !key.is_undefined() {
|
||||||
serialized_key = IDBObjectStore::convert_value_to_key(cx, key, None)?;
|
serialized_key = convert_value_to_key(cx, key, None)?;
|
||||||
} else {
|
} else {
|
||||||
// Step 11: We should use in-line keys instead
|
// Step 11: We should use in-line keys instead
|
||||||
if let Ok(kpk) = IDBObjectStore::extract_key(
|
if let Ok(kpk) = extract_key(
|
||||||
cx,
|
cx,
|
||||||
value,
|
value,
|
||||||
self.key_path.as_ref().expect("No key path"),
|
self.key_path.as_ref().expect("No key path"),
|
||||||
|
@ -514,7 +261,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
|
||||||
self.check_readwrite_transaction_active()?;
|
self.check_readwrite_transaction_active()?;
|
||||||
// Step 6
|
// Step 6
|
||||||
// TODO: Convert to key range instead
|
// TODO: Convert to key range instead
|
||||||
let serialized_query = IDBObjectStore::convert_value_to_key(cx, query, None);
|
let serialized_query = convert_value_to_key(cx, query, None);
|
||||||
// Step 7
|
// Step 7
|
||||||
serialized_query.and_then(|q| {
|
serialized_query.and_then(|q| {
|
||||||
IDBRequest::execute_async(
|
IDBRequest::execute_async(
|
||||||
|
@ -550,7 +297,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
|
||||||
self.check_transaction_active()?;
|
self.check_transaction_active()?;
|
||||||
// Step 5
|
// Step 5
|
||||||
// TODO: Convert to key range instead
|
// TODO: Convert to key range instead
|
||||||
let serialized_query = IDBObjectStore::convert_value_to_key(cx, query, None);
|
let serialized_query = convert_value_to_key(cx, query, None);
|
||||||
// Step 6
|
// Step 6
|
||||||
serialized_query.and_then(|q| {
|
serialized_query.and_then(|q| {
|
||||||
IDBRequest::execute_async(
|
IDBRequest::execute_async(
|
||||||
|
@ -604,7 +351,7 @@ impl IDBObjectStoreMethods<crate::DomTypeHolder> for IDBObjectStore {
|
||||||
self.check_transaction_active()?;
|
self.check_transaction_active()?;
|
||||||
|
|
||||||
// Step 5
|
// Step 5
|
||||||
let serialized_query = IDBObjectStore::convert_value_to_key(cx, query, None);
|
let serialized_query = convert_value_to_key(cx, query, None);
|
||||||
|
|
||||||
// Step 6
|
// Step 6
|
||||||
serialized_query.and_then(|q| {
|
serialized_query.and_then(|q| {
|
||||||
|
|
|
@ -3,21 +3,18 @@
|
||||||
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
use std::cell::Cell;
|
use std::cell::Cell;
|
||||||
use std::iter::repeat;
|
|
||||||
|
|
||||||
use constellation_traits::StructuredSerializedData;
|
use constellation_traits::StructuredSerializedData;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use ipc_channel::router::ROUTER;
|
use ipc_channel::router::ROUTER;
|
||||||
use js::gc::MutableHandle;
|
|
||||||
use js::jsapi::Heap;
|
use js::jsapi::Heap;
|
||||||
use js::jsval::{DoubleValue, JSVal, UndefinedValue};
|
use js::jsval::{JSVal, UndefinedValue};
|
||||||
use js::rust::{HandleValue, MutableHandleValue};
|
use js::rust::HandleValue;
|
||||||
use net_traits::IpcSend;
|
use net_traits::IpcSend;
|
||||||
use net_traits::indexeddb_thread::{
|
use net_traits::indexeddb_thread::{
|
||||||
AsyncOperation, IdbResult, IndexedDBKeyType, IndexedDBThreadMsg, IndexedDBTxnMode,
|
AsyncOperation, IdbResult, IndexedDBThreadMsg, IndexedDBTxnMode,
|
||||||
};
|
};
|
||||||
use profile_traits::ipc;
|
use profile_traits::ipc;
|
||||||
use script_bindings::conversions::SafeToJSValConvertible;
|
|
||||||
use stylo_atoms::Atom;
|
use stylo_atoms::Atom;
|
||||||
|
|
||||||
use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{
|
use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{
|
||||||
|
@ -36,6 +33,7 @@ use crate::dom::eventtarget::EventTarget;
|
||||||
use crate::dom::globalscope::GlobalScope;
|
use crate::dom::globalscope::GlobalScope;
|
||||||
use crate::dom::idbobjectstore::IDBObjectStore;
|
use crate::dom::idbobjectstore::IDBObjectStore;
|
||||||
use crate::dom::idbtransaction::IDBTransaction;
|
use crate::dom::idbtransaction::IDBTransaction;
|
||||||
|
use crate::indexed_db::key_type_to_jsval;
|
||||||
use crate::realms::enter_realm;
|
use crate::realms::enter_realm;
|
||||||
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
use crate::script_runtime::{CanGc, JSContext as SafeJSContext};
|
||||||
|
|
||||||
|
@ -44,30 +42,6 @@ struct RequestListener {
|
||||||
request: Trusted<IDBRequest>,
|
request: Trusted<IDBRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(unsafe_code)]
|
|
||||||
fn key_type_to_jsval(cx: SafeJSContext, key: &IndexedDBKeyType, mut result: MutableHandleValue) {
|
|
||||||
match key {
|
|
||||||
IndexedDBKeyType::Number(n) => result.set(DoubleValue(*n)),
|
|
||||||
IndexedDBKeyType::String(s) => s.safe_to_jsval(cx, result),
|
|
||||||
IndexedDBKeyType::Binary(b) => b.safe_to_jsval(cx, result),
|
|
||||||
IndexedDBKeyType::Date(_d) => {
|
|
||||||
// TODO: implement this when Date's representation is finalized.
|
|
||||||
result.set(UndefinedValue());
|
|
||||||
},
|
|
||||||
IndexedDBKeyType::Array(a) => {
|
|
||||||
rooted_vec!(let mut values <- repeat(UndefinedValue()).take(a.len()));
|
|
||||||
for (key, value) in a.iter().zip(unsafe {
|
|
||||||
values
|
|
||||||
.iter_mut()
|
|
||||||
.map(|v| MutableHandle::from_marked_location(v))
|
|
||||||
}) {
|
|
||||||
key_type_to_jsval(cx, key, value);
|
|
||||||
}
|
|
||||||
values.safe_to_jsval(cx, result);
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RequestListener {
|
impl RequestListener {
|
||||||
fn handle_async_request_finished(&self, result: Result<Option<IdbResult>, ()>) {
|
fn handle_async_request_finished(&self, result: Result<Option<IdbResult>, ()>) {
|
||||||
let request = self.request.root();
|
let request = self.request.root();
|
||||||
|
|
|
@ -12,7 +12,6 @@ use base::id::{MessagePortId, MessagePortIndex};
|
||||||
use constellation_traits::MessagePortImpl;
|
use constellation_traits::MessagePortImpl;
|
||||||
use dom_struct::dom_struct;
|
use dom_struct::dom_struct;
|
||||||
use ipc_channel::ipc::IpcSharedMemory;
|
use ipc_channel::ipc::IpcSharedMemory;
|
||||||
use script_bindings::conversions::SafeToJSValConvertible;
|
|
||||||
use js::jsapi::{Heap, JSObject};
|
use js::jsapi::{Heap, JSObject};
|
||||||
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
|
use js::jsval::{JSVal, ObjectValue, UndefinedValue};
|
||||||
use js::rust::{
|
use js::rust::{
|
||||||
|
@ -20,6 +19,7 @@ use js::rust::{
|
||||||
MutableHandleValue as SafeMutableHandleValue,
|
MutableHandleValue as SafeMutableHandleValue,
|
||||||
};
|
};
|
||||||
use js::typedarray::ArrayBufferViewU8;
|
use js::typedarray::ArrayBufferViewU8;
|
||||||
|
use script_bindings::conversions::SafeToJSValConvertible;
|
||||||
|
|
||||||
use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
|
use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy;
|
||||||
use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{
|
use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{
|
||||||
|
|
|
@ -19,9 +19,9 @@ use js::typedarray::Float32Array;
|
||||||
use profile_traits::ipc;
|
use profile_traits::ipc;
|
||||||
use stylo_atoms::Atom;
|
use stylo_atoms::Atom;
|
||||||
use webxr_api::{
|
use webxr_api::{
|
||||||
self, util, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode,
|
self, ApiSpace, ContextId as WebXRContextId, Display, EntityTypes, EnvironmentBlendMode,
|
||||||
Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray,
|
Event as XREvent, Frame, FrameUpdateEvent, HitTestId, HitTestSource, InputFrame, InputId, Ray,
|
||||||
SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility,
|
SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility, util,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::conversions::Convert;
|
use crate::conversions::Convert;
|
||||||
|
|
292
components/script/indexed_db.rs
Normal file
292
components/script/indexed_db.rs
Normal file
|
@ -0,0 +1,292 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at https://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
use std::iter::repeat;
|
||||||
|
use std::ptr;
|
||||||
|
|
||||||
|
use js::gc::MutableHandle;
|
||||||
|
use js::jsapi::{
|
||||||
|
ESClass, GetBuiltinClass, IsArrayBufferObject, JS_DeleteUCProperty,
|
||||||
|
JS_GetOwnUCPropertyDescriptor, JS_GetStringLength, JS_IsArrayBufferViewObject, JSObject,
|
||||||
|
ObjectOpResult, ObjectOpResult_SpecialCodes, PropertyDescriptor,
|
||||||
|
};
|
||||||
|
use js::jsval::{DoubleValue, UndefinedValue};
|
||||||
|
use js::rust::{HandleValue, MutableHandleValue};
|
||||||
|
use net_traits::indexeddb_thread::IndexedDBKeyType;
|
||||||
|
use script_bindings::conversions::SafeToJSValConvertible;
|
||||||
|
use script_bindings::str::DOMString;
|
||||||
|
|
||||||
|
use crate::dom::bindings::codegen::UnionTypes::StringOrStringSequence as StrOrStringSequence;
|
||||||
|
use crate::dom::bindings::conversions::jsstring_to_str;
|
||||||
|
use crate::dom::bindings::error::Error;
|
||||||
|
use crate::dom::bindings::import::module::SafeJSContext;
|
||||||
|
use crate::dom::bindings::structuredclone;
|
||||||
|
use crate::dom::idbobjectstore::KeyPath;
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn key_type_to_jsval(
|
||||||
|
cx: SafeJSContext,
|
||||||
|
key: &IndexedDBKeyType,
|
||||||
|
mut result: MutableHandleValue,
|
||||||
|
) {
|
||||||
|
match key {
|
||||||
|
IndexedDBKeyType::Number(n) => result.set(DoubleValue(*n)),
|
||||||
|
IndexedDBKeyType::String(s) => s.safe_to_jsval(cx, result),
|
||||||
|
IndexedDBKeyType::Binary(b) => b.safe_to_jsval(cx, result),
|
||||||
|
IndexedDBKeyType::Date(_d) => {
|
||||||
|
// TODO: implement this when Date's representation is finalized.
|
||||||
|
result.set(UndefinedValue());
|
||||||
|
},
|
||||||
|
IndexedDBKeyType::Array(a) => {
|
||||||
|
rooted_vec!(let mut values <- repeat(UndefinedValue()).take(a.len()));
|
||||||
|
for (key, value) in a.iter().zip(unsafe {
|
||||||
|
values
|
||||||
|
.iter_mut()
|
||||||
|
.map(|v| MutableHandle::from_marked_location(v))
|
||||||
|
}) {
|
||||||
|
key_type_to_jsval(cx, key, value);
|
||||||
|
}
|
||||||
|
values.safe_to_jsval(cx, result);
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/IndexedDB-2/#valid-key-path
|
||||||
|
pub fn is_valid_key_path(key_path: &StrOrStringSequence) -> bool {
|
||||||
|
fn is_identifier(_s: &str) -> bool {
|
||||||
|
// FIXME: (arihant2math)
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
let is_valid = |path: &DOMString| {
|
||||||
|
path.is_empty() || is_identifier(path) || path.split(".").all(is_identifier)
|
||||||
|
};
|
||||||
|
|
||||||
|
match key_path {
|
||||||
|
StrOrStringSequence::StringSequence(paths) => {
|
||||||
|
if paths.is_empty() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
paths.iter().all(is_valid)
|
||||||
|
},
|
||||||
|
StrOrStringSequence::String(path) => is_valid(path),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
// https://www.w3.org/TR/IndexedDB-2/#convert-value-to-key
|
||||||
|
pub fn convert_value_to_key(
|
||||||
|
cx: SafeJSContext,
|
||||||
|
input: HandleValue,
|
||||||
|
seen: Option<Vec<HandleValue>>,
|
||||||
|
) -> Result<IndexedDBKeyType, Error> {
|
||||||
|
// Step 1: If seen was not given, then let seen be a new empty set.
|
||||||
|
let _seen = seen.unwrap_or_default();
|
||||||
|
|
||||||
|
// Step 2: If seen contains input, then return invalid.
|
||||||
|
// FIXME:(arihant2math) implement this
|
||||||
|
// Check if we have seen this key
|
||||||
|
// Does not currently work with HandleValue,
|
||||||
|
// as it does not implement PartialEq
|
||||||
|
|
||||||
|
// Step 3
|
||||||
|
// FIXME:(arihant2math) Accept buffer, array and date as well
|
||||||
|
if input.is_number() {
|
||||||
|
// FIXME:(arihant2math) check for NaN
|
||||||
|
return Ok(IndexedDBKeyType::Number(input.to_number()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.is_string() {
|
||||||
|
let string_ptr = std::ptr::NonNull::new(input.to_string()).unwrap();
|
||||||
|
let key = unsafe { jsstring_to_str(*cx, string_ptr).str().to_string() };
|
||||||
|
return Ok(IndexedDBKeyType::String(key));
|
||||||
|
}
|
||||||
|
|
||||||
|
if input.is_object() {
|
||||||
|
rooted!(in(*cx) let object = input.to_object());
|
||||||
|
unsafe {
|
||||||
|
let mut built_in_class = ESClass::Other;
|
||||||
|
|
||||||
|
if !GetBuiltinClass(*cx, object.handle().into(), &mut built_in_class) {
|
||||||
|
return Err(Error::Data);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ESClass::Date = built_in_class {
|
||||||
|
// FIXME:(arihant2math) implement it the correct way
|
||||||
|
let key = structuredclone::write(cx, input, None).expect("Could not serialize key");
|
||||||
|
return Ok(IndexedDBKeyType::Date(key.serialized.clone()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if IsArrayBufferObject(*object) || JS_IsArrayBufferViewObject(*object) {
|
||||||
|
// FIXME:(arihant2math)
|
||||||
|
error!("Array buffers as keys is currently unsupported");
|
||||||
|
return Err(Error::NotSupported);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let ESClass::Array = built_in_class {
|
||||||
|
// FIXME:(arihant2math)
|
||||||
|
unimplemented!("Arrays as keys is currently unsupported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Err(Error::Data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/IndexedDB-2/#evaluate-a-key-path-on-a-value
|
||||||
|
#[allow(unsafe_code)]
|
||||||
|
pub fn evaluate_key_path_on_value(
|
||||||
|
cx: SafeJSContext,
|
||||||
|
value: HandleValue,
|
||||||
|
mut return_val: MutableHandleValue,
|
||||||
|
key_path: &KeyPath,
|
||||||
|
) {
|
||||||
|
// The implementation is translated from gecko:
|
||||||
|
// https://github.com/mozilla/gecko-dev/blob/master/dom/indexedDB/KeyPath.cpp
|
||||||
|
return_val.set(*value);
|
||||||
|
|
||||||
|
rooted!(in(*cx) let mut target_object = ptr::null_mut::<JSObject>());
|
||||||
|
rooted!(in(*cx) let mut current_val = *value);
|
||||||
|
rooted!(in(*cx) let mut object = ptr::null_mut::<JSObject>());
|
||||||
|
|
||||||
|
let mut target_object_prop_name: Option<String> = None;
|
||||||
|
|
||||||
|
match key_path {
|
||||||
|
KeyPath::String(path) => {
|
||||||
|
// Step 3
|
||||||
|
let path_as_string = path.to_string();
|
||||||
|
let mut tokenizer = path_as_string.split('.').peekable();
|
||||||
|
|
||||||
|
while let Some(token) = tokenizer.next() {
|
||||||
|
if target_object.get().is_null() {
|
||||||
|
if token == "length" && tokenizer.peek().is_none() && current_val.is_string() {
|
||||||
|
rooted!(in(*cx) let input_val = current_val.to_string());
|
||||||
|
unsafe {
|
||||||
|
let string_len = JS_GetStringLength(*input_val) as u64;
|
||||||
|
string_len.safe_to_jsval(cx, return_val);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !current_val.is_object() {
|
||||||
|
// FIXME:(rasviitanen) Return a proper error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
object.handle_mut().set(current_val.to_object());
|
||||||
|
rooted!(in(*cx) let mut desc = PropertyDescriptor::default());
|
||||||
|
rooted!(in(*cx) let mut intermediate = UndefinedValue());
|
||||||
|
|
||||||
|
// So rust says that this value is never read, but it is.
|
||||||
|
#[allow(unused)]
|
||||||
|
let mut has_prop = false;
|
||||||
|
|
||||||
|
unsafe {
|
||||||
|
let prop_name_as_utf16: Vec<u16> = token.encode_utf16().collect();
|
||||||
|
let mut is_descriptor_none: bool = false;
|
||||||
|
let ok = JS_GetOwnUCPropertyDescriptor(
|
||||||
|
*cx,
|
||||||
|
object.handle().into(),
|
||||||
|
prop_name_as_utf16.as_ptr(),
|
||||||
|
prop_name_as_utf16.len(),
|
||||||
|
desc.handle_mut().into(),
|
||||||
|
&mut is_descriptor_none,
|
||||||
|
);
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
// FIXME:(arihant2math) Handle this
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if desc.hasWritable_() || desc.hasValue_() {
|
||||||
|
intermediate.handle_mut().set(desc.handle().value_);
|
||||||
|
has_prop = true;
|
||||||
|
} else {
|
||||||
|
// If we get here it means the object doesn't have the property or the
|
||||||
|
// property is available throuch a getter. We don't want to call any
|
||||||
|
// getters to avoid potential re-entrancy.
|
||||||
|
// The blob object is special since its properties are available
|
||||||
|
// only through getters but we still want to support them for key
|
||||||
|
// extraction. So they need to be handled manually.
|
||||||
|
unimplemented!("Blob tokens are not yet supported");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if has_prop {
|
||||||
|
// Treat undefined as an error
|
||||||
|
if intermediate.is_undefined() {
|
||||||
|
// FIXME:(rasviitanen) Throw/return error
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if tokenizer.peek().is_some() {
|
||||||
|
// ...and walk to it if there are more steps...
|
||||||
|
current_val.handle_mut().set(*intermediate);
|
||||||
|
} else {
|
||||||
|
// ...otherwise use it as key
|
||||||
|
return_val.set(*intermediate);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
target_object.handle_mut().set(*object);
|
||||||
|
target_object_prop_name = Some(token.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !target_object.get().is_null() {
|
||||||
|
// We have started inserting new objects or are about to just insert
|
||||||
|
// the first one.
|
||||||
|
// FIXME:(rasviitanen) Implement this piece
|
||||||
|
unimplemented!("keyPath tokens that requires insertion are not supported.");
|
||||||
|
}
|
||||||
|
} // All tokens processed
|
||||||
|
|
||||||
|
if !target_object.get().is_null() {
|
||||||
|
// If this fails, we lose, and the web page sees a magical property
|
||||||
|
// appear on the object :-(
|
||||||
|
unsafe {
|
||||||
|
let prop_name_as_utf16: Vec<u16> =
|
||||||
|
target_object_prop_name.unwrap().encode_utf16().collect();
|
||||||
|
#[allow(clippy::cast_enum_truncation)]
|
||||||
|
let mut succeeded = ObjectOpResult {
|
||||||
|
code_: ObjectOpResult_SpecialCodes::Uninitialized as usize,
|
||||||
|
};
|
||||||
|
if !JS_DeleteUCProperty(
|
||||||
|
*cx,
|
||||||
|
target_object.handle().into(),
|
||||||
|
prop_name_as_utf16.as_ptr(),
|
||||||
|
prop_name_as_utf16.len(),
|
||||||
|
&mut succeeded,
|
||||||
|
) {
|
||||||
|
// FIXME:(rasviitanen) Throw/return error
|
||||||
|
// return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
KeyPath::StringSequence(_) => {
|
||||||
|
unimplemented!("String sequence keyPath is currently unsupported");
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://www.w3.org/TR/IndexedDB-2/#extract-a-key-from-a-value-using-a-key-path
|
||||||
|
pub fn extract_key(
|
||||||
|
cx: SafeJSContext,
|
||||||
|
input: HandleValue,
|
||||||
|
key_path: &KeyPath,
|
||||||
|
multi_entry: Option<bool>,
|
||||||
|
) -> Result<IndexedDBKeyType, Error> {
|
||||||
|
// Step 1: Evaluate key path
|
||||||
|
// FIXME:(rasviitanen) Do this propertly
|
||||||
|
rooted!(in(*cx) let mut r = UndefinedValue());
|
||||||
|
evaluate_key_path_on_value(cx, input, r.handle_mut(), key_path);
|
||||||
|
|
||||||
|
if let Some(_multi_entry) = multi_entry {
|
||||||
|
// FIXME:(rasviitanen) handle multi_entry cases
|
||||||
|
unimplemented!("multiEntry keys are not yet supported");
|
||||||
|
} else {
|
||||||
|
convert_value_to_key(cx, r.handle(), None)
|
||||||
|
}
|
||||||
|
}
|
|
@ -34,6 +34,7 @@ mod dom;
|
||||||
mod canvas_context;
|
mod canvas_context;
|
||||||
mod canvas_state;
|
mod canvas_state;
|
||||||
pub(crate) mod fetch;
|
pub(crate) mod fetch;
|
||||||
|
pub(crate) mod indexed_db;
|
||||||
mod init;
|
mod init;
|
||||||
mod layout_image;
|
mod layout_image;
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue