diff --git a/components/script/dom/xpathexpression.rs b/components/script/dom/xpathexpression.rs index f54dd0eafdb..9a8ef2a6b8b 100644 --- a/components/script/dom/xpathexpression.rs +++ b/components/script/dom/xpathexpression.rs @@ -53,7 +53,7 @@ impl XPathExpression { &self, context_node: &Node, result_type_num: u16, - _result: Option<&XPathResult>, + result: Option<&XPathResult>, resolver: Option>, can_gc: CanGc, ) -> Fallible> { @@ -63,16 +63,23 @@ impl XPathExpression { let global = self.global(); let window = global.as_window(); - let result_value = evaluate_parsed_xpath(&self.parsed_expression, context_node, resolver)?; + let result_value = + evaluate_parsed_xpath(&self.parsed_expression, context_node, resolver)?.into(); - // TODO(vlindhol): support putting results into mutable `_result` as per the spec - Ok(XPathResult::new( - window, - None, - can_gc, - result_type, - result_value.into(), - )) + if let Some(result) = result { + // According to https://www.w3.org/TR/DOM-Level-3-XPath/xpath.html#XPathEvaluator-evaluate, reusing + // the provided result object is optional. We choose to do it here because thats what other browsers do. + result.reinitialize_with(result_type, result_value); + Ok(DomRoot::from_ref(result)) + } else { + Ok(XPathResult::new( + window, + None, + can_gc, + result_type, + result_value, + )) + } } } diff --git a/components/script/dom/xpathresult.rs b/components/script/dom/xpathresult.rs index 890e559ba97..85ac43c0a38 100644 --- a/components/script/dom/xpathresult.rs +++ b/components/script/dom/xpathresult.rs @@ -2,7 +2,7 @@ * 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::cell::Cell; +use std::cell::{Cell, RefCell}; use dom_struct::dom_struct; use js::rust::HandleObject; @@ -84,8 +84,8 @@ impl From for XPathResultValue { pub(crate) struct XPathResult { reflector_: Reflector, window: Dom, - result_type: XPathResultType, - value: XPathResultValue, + result_type: Cell, + value: RefCell, iterator_invalid: Cell, iterator_pos: Cell, } @@ -112,10 +112,10 @@ impl XPathResult { XPathResult { reflector_: Reflector::new(), window: Dom::from_ref(window), - result_type: inferred_result_type, + result_type: Cell::new(inferred_result_type), iterator_invalid: Cell::new(false), iterator_pos: Cell::new(0), - value, + value: RefCell::new(value), } } @@ -136,17 +136,24 @@ impl XPathResult { can_gc, ) } + + pub(crate) fn reinitialize_with(&self, result_type: XPathResultType, value: XPathResultValue) { + self.result_type.set(result_type); + *self.value.borrow_mut() = value; + self.iterator_invalid.set(false); + self.iterator_pos.set(0); + } } impl XPathResultMethods for XPathResult { /// fn ResultType(&self) -> u16 { - self.result_type as u16 + self.result_type.get() as u16 } /// fn GetNumberValue(&self) -> Fallible { - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { (XPathResultValue::Number(n), XPathResultType::Number) => Ok(*n), _ => Err(Error::Type( "Can't get number value for non-number XPathResult".to_string(), @@ -156,7 +163,7 @@ impl XPathResultMethods for XPathResult { /// fn GetStringValue(&self) -> Fallible { - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { (XPathResultValue::String(s), XPathResultType::String) => Ok(s.clone()), _ => Err(Error::Type( "Can't get string value for non-string XPathResult".to_string(), @@ -166,7 +173,7 @@ impl XPathResultMethods for XPathResult { /// fn GetBooleanValue(&self) -> Fallible { - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { (XPathResultValue::Boolean(b), XPathResultType::Boolean) => Ok(*b), _ => Err(Error::Type( "Can't get boolean value for non-boolean XPathResult".to_string(), @@ -183,7 +190,7 @@ impl XPathResultMethods for XPathResult { )); } - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { ( XPathResultValue::Nodeset(nodes), XPathResultType::OrderedNodeIterator | XPathResultType::UnorderedNodeIterator, @@ -208,7 +215,7 @@ impl XPathResultMethods for XPathResult { let is_iterator_invalid = self.iterator_invalid.get(); if is_iterator_invalid || matches!( - self.result_type, + self.result_type.get(), XPathResultType::OrderedNodeIterator | XPathResultType::UnorderedNodeIterator ) { @@ -222,7 +229,7 @@ impl XPathResultMethods for XPathResult { /// fn GetSnapshotLength(&self) -> Fallible { - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { ( XPathResultValue::Nodeset(nodes), XPathResultType::OrderedNodeSnapshot | XPathResultType::UnorderedNodeSnapshot, @@ -235,7 +242,7 @@ impl XPathResultMethods for XPathResult { /// fn SnapshotItem(&self, index: u32) -> Fallible>> { - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { ( XPathResultValue::Nodeset(nodes), XPathResultType::OrderedNodeSnapshot | XPathResultType::UnorderedNodeSnapshot, @@ -248,7 +255,7 @@ impl XPathResultMethods for XPathResult { /// fn GetSingleNodeValue(&self) -> Fallible>> { - match (&self.value, self.result_type) { + match (&*self.value.borrow(), self.result_type.get()) { ( XPathResultValue::Nodeset(nodes), XPathResultType::AnyUnorderedNode | XPathResultType::FirstOrderedNode, diff --git a/tests/wpt/mozilla/meta/MANIFEST.json b/tests/wpt/mozilla/meta/MANIFEST.json index 9743fc8c0d4..c0472bade2f 100644 --- a/tests/wpt/mozilla/meta/MANIFEST.json +++ b/tests/wpt/mozilla/meta/MANIFEST.json @@ -14613,6 +14613,13 @@ {} ] ], + "xpath-result-out-parameter.html": [ + "e4206aba44d2c4f6d299d9860d80b5f736094789", + [ + null, + {} + ] + ], "zero_size_canvas_crash.html": [ "45eb9b559e8d6105baca5ab4d336de520d33b36b", [ diff --git a/tests/wpt/mozilla/tests/mozilla/xpath-result-out-parameter.html b/tests/wpt/mozilla/tests/mozilla/xpath-result-out-parameter.html new file mode 100644 index 00000000000..e4206aba44d --- /dev/null +++ b/tests/wpt/mozilla/tests/mozilla/xpath-result-out-parameter.html @@ -0,0 +1,40 @@ + + + + + + +