diff --git a/components/script/dom/htmlselectelement.rs b/components/script/dom/htmlselectelement.rs index bb976d32e57..f7730913783 100644 --- a/components/script/dom/htmlselectelement.rs +++ b/components/script/dom/htmlselectelement.rs @@ -5,16 +5,16 @@ use std::default::Default; use std::iter; -use webrender_api::units::DeviceIntRect; -use ipc_channel::ipc; 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 ipc_channel::ipc; use js::rust::HandleObject; use style::attr::AttrValue; use stylo_dom::ElementState; -use embedder_traits::{SelectElementOptionOrOptgroup, SelectElementOption}; -use euclid::{Size2D, Point2D, Rect}; -use embedder_traits::{FormControl as EmbedderFormControl, EmbedderMsg}; +use webrender_api::units::DeviceIntRect; use crate::dom::bindings::refcounted::Trusted; use crate::dom::event::{EventBubbles, EventCancelable, EventComposed}; diff --git a/components/script/dom/idbdatabase.rs b/components/script/dom/idbdatabase.rs index cf99446dcc1..d104857c4f5 100644 --- a/components/script/dom/idbdatabase.rs +++ b/components/script/dom/idbdatabase.rs @@ -30,6 +30,7 @@ use crate::dom::globalscope::GlobalScope; use crate::dom::idbobjectstore::IDBObjectStore; use crate::dom::idbtransaction::IDBTransaction; use crate::dom::idbversionchangeevent::IDBVersionChangeEvent; +use crate::indexed_db::is_valid_key_path; use crate::script_runtime::CanGc; #[dom_struct] @@ -201,7 +202,7 @@ impl IDBDatabaseMethods for IDBDatabase { // Step 5 if let Some(path) = key_path { - if !IDBObjectStore::is_valid_key_path(path) { + if !is_valid_key_path(path) { return Err(Error::Syntax); } } diff --git a/components/script/dom/idbobjectstore.rs b/components/script/dom/idbobjectstore.rs index 2ec5225b4ea..6cd1c3a117b 100644 --- a/components/script/dom/idbobjectstore.rs +++ b/components/script/dom/idbobjectstore.rs @@ -2,24 +2,14 @@ * 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::ptr; - use dom_struct::dom_struct; -use js::jsapi::{ - 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 js::rust::HandleValue; use net_traits::IpcSend; use net_traits::indexeddb_thread::{ AsyncOperation, AsyncReadOnlyOperation, AsyncReadWriteOperation, IndexedDBKeyType, IndexedDBThreadMsg, SyncOperation, }; use profile_traits::ipc; -use script_bindings::conversions::SafeToJSValConvertible; use crate::dom::bindings::cell::DomRefCell; 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; // 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::conversions::jsstring_to_str; use crate::dom::bindings::error::{Error, Fallible}; use crate::dom::bindings::reflector::{DomGlobal, Reflector, reflect_dom_object}; use crate::dom::bindings::root::{DomRoot, MutNullableDom}; @@ -37,6 +26,7 @@ use crate::dom::domstringlist::DOMStringList; use crate::dom::globalscope::GlobalScope; use crate::dom::idbrequest::IDBRequest; use crate::dom::idbtransaction::IDBTransaction; +use crate::indexed_db::{convert_value_to_key, extract_key}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; #[derive(JSTraceable, MallocSizeOf)] @@ -119,229 +109,6 @@ impl IDBObjectStore { 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>, - ) -> Result { - // 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::()); - rooted!(in(*cx) let mut current_val = *value); - rooted!(in(*cx) let mut object = ptr::null_mut::()); - - let mut target_object_prop_name: Option = 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 = 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 = - 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 { let (sender, receiver) = ipc::channel(self.global().time_profiler_chan().clone()).unwrap(); @@ -361,26 +128,6 @@ impl IDBObjectStore { 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, - ) -> Result { - // 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 fn uses_inline_keys(&self) -> bool { self.key_path.is_some() @@ -450,10 +197,10 @@ impl IDBObjectStore { let serialized_key: IndexedDBKeyType; if !key.is_undefined() { - serialized_key = IDBObjectStore::convert_value_to_key(cx, key, None)?; + serialized_key = convert_value_to_key(cx, key, None)?; } else { // Step 11: We should use in-line keys instead - if let Ok(kpk) = IDBObjectStore::extract_key( + if let Ok(kpk) = extract_key( cx, value, self.key_path.as_ref().expect("No key path"), @@ -514,7 +261,7 @@ impl IDBObjectStoreMethods for IDBObjectStore { self.check_readwrite_transaction_active()?; // Step 6 // 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 serialized_query.and_then(|q| { IDBRequest::execute_async( @@ -550,7 +297,7 @@ impl IDBObjectStoreMethods for IDBObjectStore { self.check_transaction_active()?; // Step 5 // 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 serialized_query.and_then(|q| { IDBRequest::execute_async( @@ -604,7 +351,7 @@ impl IDBObjectStoreMethods for IDBObjectStore { self.check_transaction_active()?; // 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 serialized_query.and_then(|q| { diff --git a/components/script/dom/idbrequest.rs b/components/script/dom/idbrequest.rs index b2f2e3fe08f..cd19957a6dd 100644 --- a/components/script/dom/idbrequest.rs +++ b/components/script/dom/idbrequest.rs @@ -3,21 +3,18 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; -use std::iter::repeat; use constellation_traits::StructuredSerializedData; use dom_struct::dom_struct; use ipc_channel::router::ROUTER; -use js::gc::MutableHandle; use js::jsapi::Heap; -use js::jsval::{DoubleValue, JSVal, UndefinedValue}; -use js::rust::{HandleValue, MutableHandleValue}; +use js::jsval::{JSVal, UndefinedValue}; +use js::rust::HandleValue; use net_traits::IpcSend; use net_traits::indexeddb_thread::{ - AsyncOperation, IdbResult, IndexedDBKeyType, IndexedDBThreadMsg, IndexedDBTxnMode, + AsyncOperation, IdbResult, IndexedDBThreadMsg, IndexedDBTxnMode, }; use profile_traits::ipc; -use script_bindings::conversions::SafeToJSValConvertible; use stylo_atoms::Atom; use crate::dom::bindings::codegen::Bindings::IDBRequestBinding::{ @@ -36,6 +33,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::idbobjectstore::IDBObjectStore; use crate::dom::idbtransaction::IDBTransaction; +use crate::indexed_db::key_type_to_jsval; use crate::realms::enter_realm; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; @@ -44,30 +42,6 @@ struct RequestListener { request: Trusted, } -#[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 { fn handle_async_request_finished(&self, result: Result, ()>) { let request = self.request.root(); diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 1f7732a7397..c79ff8aacbb 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -12,7 +12,6 @@ use base::id::{MessagePortId, MessagePortIndex}; use constellation_traits::MessagePortImpl; use dom_struct::dom_struct; use ipc_channel::ipc::IpcSharedMemory; -use script_bindings::conversions::SafeToJSValConvertible; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, ObjectValue, UndefinedValue}; use js::rust::{ @@ -20,6 +19,7 @@ use js::rust::{ MutableHandleValue as SafeMutableHandleValue, }; use js::typedarray::ArrayBufferViewU8; +use script_bindings::conversions::SafeToJSValConvertible; use crate::dom::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategy; use crate::dom::bindings::codegen::Bindings::ReadableStreamBinding::{ diff --git a/components/script/dom/webxr/xrsession.rs b/components/script/dom/webxr/xrsession.rs index 6ead8f65445..752150de775 100644 --- a/components/script/dom/webxr/xrsession.rs +++ b/components/script/dom/webxr/xrsession.rs @@ -19,9 +19,9 @@ use js::typedarray::Float32Array; use profile_traits::ipc; use stylo_atoms::Atom; 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, - SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility, + SelectEvent, SelectKind, Session, SessionId, View, Viewer, Visibility, util, }; use crate::conversions::Convert; diff --git a/components/script/indexed_db.rs b/components/script/indexed_db.rs new file mode 100644 index 00000000000..e5cfd4459d3 --- /dev/null +++ b/components/script/indexed_db.rs @@ -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>, +) -> Result { + // 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::()); + rooted!(in(*cx) let mut current_val = *value); + rooted!(in(*cx) let mut object = ptr::null_mut::()); + + let mut target_object_prop_name: Option = 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 = 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 = + 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, +) -> Result { + // 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) + } +} diff --git a/components/script/lib.rs b/components/script/lib.rs index 73564d85c38..a358dba4a8a 100644 --- a/components/script/lib.rs +++ b/components/script/lib.rs @@ -34,6 +34,7 @@ mod dom; mod canvas_context; mod canvas_state; pub(crate) mod fetch; +pub(crate) mod indexed_db; mod init; mod layout_image;