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