diff options
Diffstat (limited to 'components/script')
24 files changed, 577 insertions, 83 deletions
diff --git a/components/script/body.rs b/components/script/body.rs index 113f3ac7adb..cc7870a0845 100644 --- a/components/script/body.rs +++ b/components/script/body.rs @@ -7,7 +7,7 @@ use std::{ptr, slice, str}; use constellation_traits::BlobImpl; use encoding_rs::{Encoding, UTF_8}; -use ipc_channel::ipc::{self, IpcReceiver, IpcSender}; +use ipc_channel::ipc::{self, IpcReceiver, IpcSender, IpcSharedMemory}; use ipc_channel::router::ROUTER; use js::jsapi::{Heap, JS_ClearPendingException, JSObject, Value as JSValue}; use js::jsval::{JSVal, UndefinedValue}; @@ -73,7 +73,7 @@ struct TransmitBodyConnectHandler { task_source: SendableTaskSource, bytes_sender: Option<IpcSender<BodyChunkResponse>>, control_sender: IpcSender<BodyChunkRequest>, - in_memory: Option<Vec<u8>>, + in_memory: Option<IpcSharedMemory>, in_memory_done: bool, source: BodySource, } @@ -83,7 +83,7 @@ impl TransmitBodyConnectHandler { stream: Trusted<ReadableStream>, task_source: SendableTaskSource, control_sender: IpcSender<BodyChunkRequest>, - in_memory: Option<Vec<u8>>, + in_memory: Option<IpcSharedMemory>, source: BodySource, ) -> TransmitBodyConnectHandler { TransmitBodyConnectHandler { @@ -160,7 +160,7 @@ impl TransmitBodyConnectHandler { .bytes_sender .as_ref() .expect("No bytes sender to transmit source.") - .send(BodyChunkResponse::Chunk(bytes.clone())); + .send(BodyChunkResponse::Chunk(bytes)); return; } warn!("Re-directs for file-based Blobs not supported yet."); @@ -310,7 +310,11 @@ impl Callback for TransmitBodyPromiseHandler { // Step 5.1 and 5.2, transmit chunk. // Send the chunk to the body transmitter in net::http_loader::obtain_response. // TODO: queue a fetch task on request to process request body for request. - let _ = self.bytes_sender.send(BodyChunkResponse::Chunk(chunk)); + let _ = self + .bytes_sender + .send(BodyChunkResponse::Chunk(IpcSharedMemory::from_bytes( + &chunk, + ))); } } diff --git a/components/script/dom/attr.rs b/components/script/dom/attr.rs index 52d0ca7e20c..9f1520bd085 100644 --- a/components/script/dom/attr.rs +++ b/components/script/dom/attr.rs @@ -8,7 +8,7 @@ use std::mem; use devtools_traits::AttrInfo; use dom_struct::dom_struct; -use html5ever::{LocalName, Namespace, Prefix, ns}; +use html5ever::{LocalName, Namespace, Prefix, local_name, ns}; use style::attr::{AttrIdentifier, AttrValue}; use style::values::GenericAtomIdent; use stylo_atoms::Atom; @@ -179,7 +179,7 @@ impl Attr { assert_eq!(Some(owner), self.owner().as_deref()); owner.will_mutate_attr(self); self.swap_value(&mut value); - if *self.namespace() == ns!() { + if is_relevant_attribute(self.namespace(), self.local_name()) { vtable_for(owner.upcast()).attribute_mutated( self, AttributeMutation::Set(Some(&value)), @@ -283,3 +283,9 @@ impl<'dom> AttrHelpersForLayout<'dom> for LayoutDom<'dom, Attr> { &self.unsafe_get().identifier.namespace.0 } } + +/// A helper function to check if attribute is relevant. +pub(crate) fn is_relevant_attribute(namespace: &Namespace, local_name: &LocalName) -> bool { + // <https://svgwg.org/svg2-draft/linking.html#XLinkHrefAttribute> + namespace == &ns!() || (namespace == &ns!(xlink) && local_name == &local_name!("href")) +} diff --git a/components/script/dom/bindings/structuredclone.rs b/components/script/dom/bindings/structuredclone.rs index c9a49ba00c9..70638238123 100644 --- a/components/script/dom/bindings/structuredclone.rs +++ b/components/script/dom/bindings/structuredclone.rs @@ -44,7 +44,7 @@ use crate::dom::dompointreadonly::DOMPointReadOnly; use crate::dom::globalscope::GlobalScope; use crate::dom::messageport::MessagePort; use crate::dom::readablestream::ReadableStream; -use crate::dom::types::DOMException; +use crate::dom::types::{DOMException, TransformStream}; use crate::dom::writablestream::WritableStream; use crate::realms::{AlreadyInRealm, InRealm, enter_realm}; use crate::script_runtime::{CanGc, JSContext as SafeJSContext}; @@ -65,6 +65,7 @@ pub(super) enum StructuredCloneTags { ReadableStream = 0xFFFF8006, DomException = 0xFFFF8007, WritableStream = 0xFFFF8008, + TransformStream = 0xFFFF8009, Max = 0xFFFFFFFF, } @@ -85,6 +86,7 @@ impl From<TransferrableInterface> for StructuredCloneTags { TransferrableInterface::MessagePort => StructuredCloneTags::MessagePort, TransferrableInterface::ReadableStream => StructuredCloneTags::ReadableStream, TransferrableInterface::WritableStream => StructuredCloneTags::WritableStream, + TransferrableInterface::TransformStream => StructuredCloneTags::TransformStream, } } } @@ -265,6 +267,7 @@ fn receiver_for_type( TransferrableInterface::MessagePort => receive_object::<MessagePort>, TransferrableInterface::ReadableStream => receive_object::<ReadableStream>, TransferrableInterface::WritableStream => receive_object::<WritableStream>, + TransferrableInterface::TransformStream => receive_object::<TransformStream>, } } @@ -390,6 +393,7 @@ fn transfer_for_type(val: TransferrableInterface) -> TransferOperation { TransferrableInterface::MessagePort => try_transfer::<MessagePort>, TransferrableInterface::ReadableStream => try_transfer::<ReadableStream>, TransferrableInterface::WritableStream => try_transfer::<WritableStream>, + TransferrableInterface::TransformStream => try_transfer::<TransformStream>, } } @@ -438,6 +442,7 @@ unsafe fn can_transfer_for_type( TransferrableInterface::MessagePort => can_transfer::<MessagePort>(obj, cx), TransferrableInterface::ReadableStream => can_transfer::<ReadableStream>(obj, cx), TransferrableInterface::WritableStream => can_transfer::<WritableStream>(obj, cx), + TransferrableInterface::TransformStream => can_transfer::<TransformStream>(obj, cx), } } diff --git a/components/script/dom/create.rs b/components/script/dom/create.rs index 5722dc4f6ac..2e7c4cf8def 100644 --- a/components/script/dom/create.rs +++ b/components/script/dom/create.rs @@ -85,6 +85,7 @@ use crate::dom::htmlulistelement::HTMLUListElement; use crate::dom::htmlunknownelement::HTMLUnknownElement; use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::svgelement::SVGElement; +use crate::dom::svgimageelement::SVGImageElement; use crate::dom::svgsvgelement::SVGSVGElement; use crate::realms::{InRealm, enter_realm}; use crate::script_runtime::CanGc; @@ -114,6 +115,7 @@ fn create_svg_element( } match name.local { + local_name!("image") => make!(SVGImageElement), local_name!("svg") => make!(SVGSVGElement), _ => make!(SVGElement), } diff --git a/components/script/dom/document.rs b/components/script/dom/document.rs index ad95b9b9a94..78cb2c33075 100644 --- a/components/script/dom/document.rs +++ b/components/script/dom/document.rs @@ -4313,7 +4313,7 @@ impl Document { }, Some(csp_list) => { let element = csp::Element { - nonce: el.nonce_attribute_if_nonceable().map(Cow::Owned), + nonce: el.nonce_value_if_nonceable().map(Cow::Owned), }; csp_list.should_elements_inline_type_behavior_be_blocked(&element, type_, source) }, diff --git a/components/script/dom/element.rs b/components/script/dom/element.rs index 5c79dbc0a5b..ed58548a3e5 100644 --- a/components/script/dom/element.rs +++ b/components/script/dom/element.rs @@ -63,8 +63,9 @@ use xml5ever::serialize::TraversalScope::{ ChildrenOnly as XmlChildrenOnly, IncludeNode as XmlIncludeNode, }; +use crate::conversions::Convert; use crate::dom::activation::Activatable; -use crate::dom::attr::{Attr, AttrHelpersForLayout}; +use crate::dom::attr::{Attr, AttrHelpersForLayout, is_relevant_attribute}; use crate::dom::bindings::cell::{DomRefCell, Ref, RefMut, ref_filter_map}; use crate::dom::bindings::codegen::Bindings::AttrBinding::AttrMethods; use crate::dom::bindings::codegen::Bindings::DocumentBinding::DocumentMethods; @@ -80,7 +81,9 @@ use crate::dom::bindings::codegen::Bindings::ShadowRootBinding::{ use crate::dom::bindings::codegen::Bindings::WindowBinding::{ ScrollBehavior, ScrollToOptions, WindowMethods, }; -use crate::dom::bindings::codegen::UnionTypes::{NodeOrString, TrustedScriptURLOrUSVString}; +use crate::dom::bindings::codegen::UnionTypes::{ + NodeOrString, TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString, TrustedScriptURLOrUSVString, +}; use crate::dom::bindings::conversions::DerivedFrom; use crate::dom::bindings::error::{Error, ErrorResult, Fallible}; use crate::dom::bindings::inheritance::{Castable, ElementTypeId, HTMLElementTypeId, NodeTypeId}; @@ -143,8 +146,8 @@ use crate::dom::intersectionobserver::{IntersectionObserver, IntersectionObserve use crate::dom::mutationobserver::{Mutation, MutationObserver}; use crate::dom::namednodemap::NamedNodeMap; use crate::dom::node::{ - BindContext, ChildrenMutation, LayoutNodeHelpers, Node, NodeDamage, NodeFlags, NodeTraits, - ShadowIncluding, UnbindContext, + BindContext, ChildrenMutation, CloneChildrenFlag, LayoutNodeHelpers, Node, NodeDamage, + NodeFlags, NodeTraits, ShadowIncluding, UnbindContext, }; use crate::dom::nodelist::NodeList; use crate::dom::promise::Promise; @@ -152,6 +155,7 @@ use crate::dom::raredata::ElementRareData; use crate::dom::servoparser::ServoParser; use crate::dom::shadowroot::{IsUserAgentWidget, ShadowRoot}; use crate::dom::text::Text; +use crate::dom::trustedhtml::TrustedHTML; use crate::dom::validation::Validatable; use crate::dom::validitystate::ValidationFlags; use crate::dom::virtualmethods::{VirtualMethods, vtable_for}; @@ -355,7 +359,7 @@ impl Element { if damage == NodeDamage::OtherNodeDamage { doc.note_node_with_dirty_descendants(self.upcast()); - restyle.damage = RestyleDamage::rebuild_and_reflow(); + restyle.damage = RestyleDamage::reconstruct(); } } @@ -1701,7 +1705,7 @@ impl Element { assert!(attr.GetOwnerElement().as_deref() == Some(self)); self.will_mutate_attr(attr); self.attrs.borrow_mut().push(Dom::from_ref(attr)); - if attr.namespace() == &ns!() { + if is_relevant_attribute(attr.namespace(), attr.local_name()) { vtable_for(self.upcast()).attribute_mutated(attr, AttributeMutation::Set(None), can_gc); } } @@ -1843,7 +1847,7 @@ impl Element { local_name: &LocalName, value: DOMString, ) -> AttrValue { - if *namespace == ns!() { + if is_relevant_attribute(namespace, local_name) { vtable_for(self.upcast()).parse_plain_attribute(local_name, value) } else { AttrValue::String(value.into()) @@ -1898,7 +1902,7 @@ impl Element { self.attrs.borrow_mut().remove(idx); attr.set_owner(None); - if attr.namespace() == &ns!() { + if is_relevant_attribute(attr.namespace(), attr.local_name()) { vtable_for(self.upcast()).attribute_mutated( &attr, AttributeMutation::Removed, @@ -1992,6 +1996,15 @@ impl Element { .unwrap_or_else(|_| TrustedScriptURLOrUSVString::USVString(USVString(value.to_owned()))) } + pub(crate) fn get_trusted_html_attribute(&self, local_name: &LocalName) -> TrustedHTMLOrString { + assert_eq!(*local_name, local_name.to_ascii_lowercase()); + let value = match self.get_attribute(&ns!(), local_name) { + Some(attr) => (&**attr.value()).into(), + None => "".into(), + }; + TrustedHTMLOrString::String(value) + } + pub(crate) fn get_string_attribute(&self, local_name: &LocalName) -> DOMString { match self.get_attribute(&ns!(), local_name) { Some(x) => x.Value(), @@ -2175,10 +2188,53 @@ impl Element { }; } + /// <https://html.spec.whatwg.org/multipage/#nonce-attributes> + pub(crate) fn update_nonce_internal_slot(&self, nonce: String) { + self.ensure_rare_data().cryptographic_nonce = nonce; + } + + /// <https://html.spec.whatwg.org/multipage/#nonce-attributes> + pub(crate) fn nonce_value(&self) -> String { + match self.rare_data().as_ref() { + None => String::new(), + Some(rare_data) => rare_data.cryptographic_nonce.clone(), + } + } + + /// <https://html.spec.whatwg.org/multipage/#nonce-attributes> + pub(crate) fn update_nonce_post_connection(&self) { + // Whenever an element including HTMLOrSVGElement becomes browsing-context connected, + // the user agent must execute the following steps on the element: + if !self.upcast::<Node>().is_connected_with_browsing_context() { + return; + } + let global = self.owner_global(); + // Step 1: Let CSP list be element's shadow-including root's policy container's CSP list. + let csp_list = match global.get_csp_list() { + None => return, + Some(csp_list) => csp_list, + }; + // Step 2: If CSP list contains a header-delivered Content Security Policy, + // and element has a nonce content attribute whose value is not the empty string, then: + if !csp_list.contains_a_header_delivered_content_security_policy() || + self.get_string_attribute(&local_name!("nonce")).is_empty() + { + return; + } + // Step 2.1: Let nonce be element's [[CryptographicNonce]]. + let nonce = self.nonce_value(); + // Step 2.2: Set an attribute value for element using "nonce" and the empty string. + self.set_string_attribute(&local_name!("nonce"), "".into(), CanGc::note()); + // Step 2.3: Set element's [[CryptographicNonce]] to nonce. + self.update_nonce_internal_slot(nonce); + } + /// <https://www.w3.org/TR/CSP/#is-element-nonceable> - pub(crate) fn nonce_attribute_if_nonceable(&self) -> Option<String> { + pub(crate) fn nonce_value_if_nonceable(&self) -> Option<String> { // Step 1: If element does not have an attribute named "nonce", return "Not Nonceable". - let nonce_attribute = self.get_attribute(&ns!(), &local_name!("nonce"))?; + if !self.has_attribute(&local_name!("nonce")) { + return None; + } // Step 2: If element is a script element, then for each attribute of element’s attribute list: if self.downcast::<HTMLScriptElement>().is_some() { for attr in self.attrs().iter() { @@ -2200,7 +2256,7 @@ impl Element { // TODO(https://github.com/servo/servo/issues/4577 and https://github.com/whatwg/html/issues/3257): // Figure out how to retrieve this information from the parser // Step 4: Return "Nonceable". - Some(nonce_attribute.value().to_string().trim().to_owned()) + Some(self.nonce_value().trim().to_owned()) } // https://dom.spec.whatwg.org/#insert-adjacent @@ -2322,18 +2378,25 @@ impl Element { Ok(fragment) } + /// Step 4 of <https://html.spec.whatwg.org/multipage/#dom-element-insertadjacenthtml> pub(crate) fn fragment_parsing_context( owner_doc: &Document, element: Option<&Self>, can_gc: CanGc, ) -> DomRoot<Self> { + // If context is not an Element or all of the following are true: match element { Some(elem) + // context's node document is an HTML document; + // context's local name is "html"; and + // context's namespace is the HTML namespace, if elem.local_name() != &local_name!("html") || !elem.html_element_in_html_document() => { DomRoot::from_ref(elem) }, + // set context to the result of creating an element + // given this's node document, "body", and the HTML namespace. _ => DomRoot::upcast(HTMLBodyElement::new( local_name!("body"), None, @@ -2446,6 +2509,13 @@ impl Element { Dom::from_ref(&*ElementInternals::new(elem, can_gc)) })) } + + pub(crate) fn outer_html(&self, can_gc: CanGc) -> Fallible<DOMString> { + match self.GetOuterHTML(can_gc)? { + TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => Ok(str), + TrustedHTMLOrNullIsEmptyString::TrustedHTML(_) => unreachable!(), + } + } } impl ElementMethods<crate::DomTypeHolder> for Element { @@ -2704,7 +2774,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element { attr.set_owner(Some(self)); self.attrs.borrow_mut()[position] = Dom::from_ref(attr); old_attr.set_owner(None); - if attr.namespace() == &ns!() { + if is_relevant_attribute(attr.namespace(), attr.local_name()) { vtable.attribute_mutated( attr, AttributeMutation::Set(Some(&old_attr.value())), @@ -3100,7 +3170,17 @@ impl ElementMethods<crate::DomTypeHolder> for Element { } /// <https://html.spec.whatwg.org/multipage/#dom-element-sethtmlunsafe> - fn SetHTMLUnsafe(&self, html: DOMString, can_gc: CanGc) { + fn SetHTMLUnsafe(&self, html: TrustedHTMLOrString, can_gc: CanGc) -> ErrorResult { + // Step 1. Let compliantHTML be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, html, "Element setHTMLUnsafe", and "script". + let html = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + html, + "Element", + "setHTMLUnsafe", + can_gc, + )?); // Step 2. Let target be this's template contents if this is a template element; otherwise this. let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() { DomRoot::upcast(template.Content(can_gc)) @@ -3110,6 +3190,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element { // Step 3. Unsafely set HTML given target, this, and compliantHTML Node::unsafely_set_html(&target, self, html, can_gc); + Ok(()) } /// <https://html.spec.whatwg.org/multipage/#dom-element-gethtml> @@ -3125,7 +3206,7 @@ impl ElementMethods<crate::DomTypeHolder> for Element { } /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml> - fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<DOMString> { + fn GetInnerHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> { let qname = QualName::new( self.prefix().clone(), self.namespace().clone(), @@ -3142,16 +3223,28 @@ impl ElementMethods<crate::DomTypeHolder> for Element { .xml_serialize(XmlChildrenOnly(Some(qname))) }; - Ok(result) + Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result)) } /// <https://html.spec.whatwg.org/multipage/#dom-element-innerhtml> - fn SetInnerHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult { - // Step 2. + fn SetInnerHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult { + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, the given value, "Element innerHTML", and "script". + let value = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + value.convert(), + "Element", + "innerHTML", + can_gc, + )?); // https://github.com/w3c/DOM-Parsing/issues/1 let target = if let Some(template) = self.downcast::<HTMLTemplateElement>() { + // Step 4: If context is a template element, then set context to + // the template element's template contents (a DocumentFragment). DomRoot::upcast(template.Content(can_gc)) } else { + // Step 2: Let context be this. DomRoot::from_ref(self.upcast()) }; @@ -3168,15 +3261,17 @@ impl ElementMethods<crate::DomTypeHolder> for Element { return Ok(()); } - // Step 1. + // Step 3: Let fragment be the result of invoking the fragment parsing algorithm steps + // with context and compliantString. let frag = self.parse_fragment(value, can_gc)?; + // Step 5: Replace all with fragment within context. Node::replace_all(Some(frag.upcast()), &target, can_gc); Ok(()) } /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml> - fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<DOMString> { + fn GetOuterHTML(&self, can_gc: CanGc) -> Fallible<TrustedHTMLOrNullIsEmptyString> { // FIXME: This should use the fragment serialization algorithm, which takes // care of distinguishing between html/xml documents let result = if self.owner_document().is_html_document() { @@ -3186,27 +3281,39 @@ impl ElementMethods<crate::DomTypeHolder> for Element { self.upcast::<Node>().xml_serialize(XmlIncludeNode) }; - Ok(result) + Ok(TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(result)) } /// <https://html.spec.whatwg.org/multipage/#dom-element-outerhtml> - fn SetOuterHTML(&self, value: DOMString, can_gc: CanGc) -> ErrorResult { + fn SetOuterHTML(&self, value: TrustedHTMLOrNullIsEmptyString, can_gc: CanGc) -> ErrorResult { + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, the given value, "Element outerHTML", and "script". + let value = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + value.convert(), + "Element", + "outerHTML", + can_gc, + )?); let context_document = self.owner_document(); let context_node = self.upcast::<Node>(); - // Step 1. + // Step 2: Let parent be this's parent. let context_parent = match context_node.GetParentNode() { None => { - // Step 2. + // Step 3: If parent is null, return. There would be no way to + // obtain a reference to the nodes created even if the remaining steps were run. return Ok(()); }, Some(parent) => parent, }; let parent = match context_parent.type_id() { - // Step 3. + // Step 4: If parent is a Document, throw a "NoModificationAllowedError" DOMException. NodeTypeId::Document(_) => return Err(Error::NoModificationAllowed), - // Step 4. + // Step 5: If parent is a DocumentFragment, set parent to the result of + // creating an element given this's node document, "body", and the HTML namespace. NodeTypeId::DocumentFragment(_) => { let body_elem = Element::create( QualName::new(None, ns!(html), local_name!("body")), @@ -3222,9 +3329,10 @@ impl ElementMethods<crate::DomTypeHolder> for Element { _ => context_node.GetParentElement().unwrap(), }; - // Step 5. + // Step 6: Let fragment be the result of invoking the + // fragment parsing algorithm steps given parent and compliantString. let frag = parent.parse_fragment(value, can_gc)?; - // Step 6. + // Step 7: Replace this with fragment within this's parent. context_parent.ReplaceChild(frag.upcast(), context_node, can_gc)?; Ok(()) } @@ -3391,38 +3499,57 @@ impl ElementMethods<crate::DomTypeHolder> for Element { fn InsertAdjacentHTML( &self, position: DOMString, - text: DOMString, + text: TrustedHTMLOrString, can_gc: CanGc, ) -> ErrorResult { - // Step 1. + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, string, "Element insertAdjacentHTML", and "script". + let text = DOMString::from(TrustedHTML::get_trusted_script_compliant_string( + &self.owner_global(), + text, + "Element", + "insertAdjacentHTML", + can_gc, + )?); let position = position.parse::<AdjacentPosition>()?; + // Step 2: Let context be null. + // Step 3: Use the first matching item from this list: let context = match position { + // If position is an ASCII case-insensitive match for the string "beforebegin" + // If position is an ASCII case-insensitive match for the string "afterend" AdjacentPosition::BeforeBegin | AdjacentPosition::AfterEnd => { match self.upcast::<Node>().GetParentNode() { + // Step 3.2: If context is null or a Document, throw a "NoModificationAllowedError" DOMException. Some(ref node) if node.is::<Document>() => { return Err(Error::NoModificationAllowed); }, None => return Err(Error::NoModificationAllowed), + // Step 3.1: Set context to this's parent. Some(node) => node, } }, + // If position is an ASCII case-insensitive match for the string "afterbegin" + // If position is an ASCII case-insensitive match for the string "beforeend" AdjacentPosition::AfterBegin | AdjacentPosition::BeforeEnd => { + // Set context to this. DomRoot::from_ref(self.upcast::<Node>()) }, }; - // Step 2. + // Step 4. let context = Element::fragment_parsing_context( &context.owner_doc(), context.downcast::<Element>(), can_gc, ); - // Step 3. + // Step 5: Let fragment be the result of invoking the + // fragment parsing algorithm steps with context and compliantString. let fragment = context.parse_fragment(text, can_gc)?; - // Step 4. + // Step 6. self.insert_adjacent(position, fragment.upcast(), can_gc) .map(|_| ()) } @@ -4113,6 +4240,31 @@ impl VirtualMethods for Element { self.tag_name.clear(); } } + + fn post_connection_steps(&self) { + if let Some(s) = self.super_type() { + s.post_connection_steps(); + } + + self.update_nonce_post_connection(); + } + + /// <https://html.spec.whatwg.org/multipage/#nonce-attributes%3Aconcept-node-clone-ext> + fn cloning_steps( + &self, + copy: &Node, + maybe_doc: Option<&Document>, + clone_children: CloneChildrenFlag, + can_gc: CanGc, + ) { + if let Some(s) = self.super_type() { + s.cloning_steps(copy, maybe_doc, clone_children, can_gc); + } + let elem = copy.downcast::<Element>().unwrap(); + if let Some(rare_data) = self.rare_data().as_ref() { + elem.update_nonce_internal_slot(rare_data.cryptographic_nonce.clone()); + } + } } #[derive(Clone, PartialEq)] diff --git a/components/script/dom/globalscope.rs b/components/script/dom/globalscope.rs index 902d4622db9..55db2e4d248 100644 --- a/components/script/dom/globalscope.rs +++ b/components/script/dom/globalscope.rs @@ -3562,10 +3562,6 @@ impl GlobalScopeHelpers<crate::DomTypeHolder> for GlobalScope { GlobalScope::from_reflector(reflector, realm) } - unsafe fn from_object_maybe_wrapped(obj: *mut JSObject, cx: *mut JSContext) -> DomRoot<Self> { - GlobalScope::from_object_maybe_wrapped(obj, cx) - } - fn origin(&self) -> &MutableOrigin { GlobalScope::origin(self) } diff --git a/components/script/dom/htmlelement.rs b/components/script/dom/htmlelement.rs index 32a979ad138..f41370386e9 100644 --- a/components/script/dom/htmlelement.rs +++ b/components/script/dom/htmlelement.rs @@ -645,13 +645,16 @@ impl HTMLElementMethods<crate::DomTypeHolder> for HTMLElement { Ok(internals) } - // FIXME: The nonce should be stored in an internal slot instead of an - // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce) // https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce - make_getter!(Nonce, "nonce"); + fn Nonce(&self) -> DOMString { + self.as_element().nonce_value().into() + } // https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce - make_setter!(SetNonce, "nonce"); + fn SetNonce(&self, value: DOMString) { + self.as_element() + .update_nonce_internal_slot(value.to_string()) + } // https://html.spec.whatwg.org/multipage/#dom-fe-autofocus fn Autofocus(&self) -> bool { @@ -1138,6 +1141,15 @@ impl VirtualMethods for HTMLElement { }, } }, + (&local_name!("nonce"), mutation) => match mutation { + AttributeMutation::Set(_) => { + let nonce = &**attr.value(); + element.update_nonce_internal_slot(nonce.to_owned()); + }, + AttributeMutation::Removed => { + element.update_nonce_internal_slot("".to_owned()); + }, + }, _ => {}, } } diff --git a/components/script/dom/htmliframeelement.rs b/components/script/dom/htmliframeelement.rs index 0fbff86e44a..18116eee8ae 100644 --- a/components/script/dom/htmliframeelement.rs +++ b/components/script/dom/htmliframeelement.rs @@ -27,6 +27,8 @@ use crate::dom::attr::Attr; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::HTMLIFrameElementBinding::HTMLIFrameElementMethods; use crate::dom::bindings::codegen::Bindings::WindowBinding::Window_Binding::WindowMethods; +use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString; +use crate::dom::bindings::error::Fallible; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::reflector::DomGlobal; use crate::dom::bindings::root::{DomRoot, LayoutDom, MutNullableDom}; @@ -40,6 +42,7 @@ use crate::dom::eventtarget::EventTarget; use crate::dom::globalscope::GlobalScope; use crate::dom::htmlelement::HTMLElement; use crate::dom::node::{Node, NodeDamage, NodeTraits, UnbindContext}; +use crate::dom::trustedhtml::TrustedHTML; use crate::dom::virtualmethods::VirtualMethods; use crate::dom::windowproxy::WindowProxy; use crate::script_runtime::CanGc; @@ -595,10 +598,29 @@ impl HTMLIFrameElementMethods<crate::DomTypeHolder> for HTMLIFrameElement { make_url_setter!(SetSrc, "src"); // https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc - make_getter!(Srcdoc, "srcdoc"); + fn Srcdoc(&self) -> TrustedHTMLOrString { + let element = self.upcast::<Element>(); + element.get_trusted_html_attribute(&local_name!("srcdoc")) + } // https://html.spec.whatwg.org/multipage/#dom-iframe-srcdoc - make_setter!(SetSrcdoc, "srcdoc"); + fn SetSrcdoc(&self, value: TrustedHTMLOrString, can_gc: CanGc) -> Fallible<()> { + // Step 1: Let compliantString be the result of invoking the + // Get Trusted Type compliant string algorithm with TrustedHTML, + // this's relevant global object, the given value, "HTMLIFrameElement srcdoc", and "script". + let element = self.upcast::<Element>(); + let local_name = &local_name!("srcdoc"); + let value = TrustedHTML::get_trusted_script_compliant_string( + &element.owner_global(), + value, + "HTMLIFrameElement", + local_name, + can_gc, + )?; + // Step 2: Set an attribute value given this, srcdoc's local name, and compliantString. + element.set_attribute(local_name, AttrValue::String(value), can_gc); + Ok(()) + } // https://html.spec.whatwg.org/multipage/#dom-iframe-sandbox fn Sandbox(&self, can_gc: CanGc) -> DomRoot<DOMTokenList> { diff --git a/components/script/dom/htmllinkelement.rs b/components/script/dom/htmllinkelement.rs index 18bd426acdb..f4e7683cf2a 100644 --- a/components/script/dom/htmllinkelement.rs +++ b/components/script/dom/htmllinkelement.rs @@ -31,7 +31,6 @@ use crate::dom::attr::Attr; use crate::dom::bindings::cell::DomRefCell; use crate::dom::bindings::codegen::Bindings::DOMTokenListBinding::DOMTokenList_Binding::DOMTokenListMethods; use crate::dom::bindings::codegen::Bindings::HTMLLinkElementBinding::HTMLLinkElementMethods; -use crate::dom::bindings::codegen::GenericBindings::HTMLElementBinding::HTMLElement_Binding::HTMLElementMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::refcounted::Trusted; use crate::dom::bindings::reflector::DomGlobal; @@ -344,7 +343,7 @@ impl HTMLLinkElement { destination: Some(destination), integrity: String::new(), link_type: String::new(), - cryptographic_nonce_metadata: self.upcast::<HTMLElement>().Nonce().into(), + cryptographic_nonce_metadata: self.upcast::<Element>().nonce_value(), cross_origin: cors_setting_for_element(element), referrer_policy: referrer_policy_for_element(element), policy_container: document.policy_container().to_owned(), diff --git a/components/script/dom/htmlscriptelement.rs b/components/script/dom/htmlscriptelement.rs index 4ee1397b4ed..d1b3cfd3467 100644 --- a/components/script/dom/htmlscriptelement.rs +++ b/components/script/dom/htmlscriptelement.rs @@ -781,7 +781,7 @@ impl HTMLScriptElement { }; // Step 24. Let cryptographic nonce be el's [[CryptographicNonce]] internal slot's value. - let cryptographic_nonce = self.upcast::<HTMLElement>().Nonce().into(); + let cryptographic_nonce = self.upcast::<Element>().nonce_value(); // Step 25. If el has an integrity attribute, then let integrity metadata be that attribute's value. // Otherwise, let integrity metadata be the empty string. diff --git a/components/script/dom/mod.rs b/components/script/dom/mod.rs index 1622cf57b79..91a4e1b1359 100644 --- a/components/script/dom/mod.rs +++ b/components/script/dom/mod.rs @@ -547,6 +547,7 @@ pub(crate) mod submitevent; pub(crate) mod subtlecrypto; pub(crate) mod svgelement; pub(crate) mod svggraphicselement; +pub(crate) mod svgimageelement; pub(crate) mod svgsvgelement; pub(crate) mod testbinding; pub(crate) mod testbindingiterable; diff --git a/components/script/dom/node.rs b/components/script/dom/node.rs index ca785773b48..5f08abce354 100644 --- a/components/script/dom/node.rs +++ b/components/script/dom/node.rs @@ -4207,6 +4207,9 @@ impl From<ElementTypeIdWrapper> for LayoutElementType { LayoutElementType::HTMLTextAreaElement }, ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( + SVGGraphicsElementTypeId::SVGImageElement, + )) => LayoutElementType::SVGImageElement, + ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( SVGGraphicsElementTypeId::SVGSVGElement, )) => LayoutElementType::SVGSVGElement, _ => LayoutElementType::Element, diff --git a/components/script/dom/promise.rs b/components/script/dom/promise.rs index 80b62f161bc..0efffbe6fe2 100644 --- a/components/script/dom/promise.rs +++ b/components/script/dom/promise.rs @@ -29,7 +29,6 @@ use js::rust::wrappers::{ ResolvePromise, SetAnyPromiseIsHandled, SetPromiseUserInputEventHandlingState, }; use js::rust::{HandleObject, HandleValue, MutableHandleObject, Runtime}; -use script_bindings::interfaces::PromiseHelpers; use crate::dom::bindings::conversions::root_from_object; use crate::dom::bindings::error::{Error, ErrorToJsval}; @@ -388,16 +387,6 @@ fn create_native_handler_function( } } -impl PromiseHelpers<crate::DomTypeHolder> for Promise { - fn new_resolved( - global: &GlobalScope, - cx: SafeJSContext, - value: impl ToJSValConvertible, - ) -> Rc<Promise> { - Promise::new_resolved(global, cx, value, CanGc::note()) - } -} - impl FromJSValConvertibleRc for Promise { #[allow(unsafe_code)] unsafe fn from_jsval( @@ -407,16 +396,12 @@ impl FromJSValConvertibleRc for Promise { if value.get().is_null() { return Ok(ConversionResult::Failure("null not allowed".into())); } - if !value.get().is_object() { - return Ok(ConversionResult::Failure("not an object".into())); - } - rooted!(in(cx) let obj = value.get().to_object()); let cx = SafeJSContext::from_ptr(cx); let in_realm_proof = AlreadyInRealm::assert_for_cx(cx); let global_scope = GlobalScope::from_context(*cx, InRealm::Already(&in_realm_proof)); - let promise = Promise::new_resolved(&global_scope, cx, *obj, CanGc::note()); + let promise = Promise::new_resolved(&global_scope, cx, value, CanGc::note()); Ok(ConversionResult::Success(promise)) } } diff --git a/components/script/dom/raredata.rs b/components/script/dom/raredata.rs index 3afa000511e..0c048956217 100644 --- a/components/script/dom/raredata.rs +++ b/components/script/dom/raredata.rs @@ -75,4 +75,5 @@ pub(crate) struct ElementRareData { /// > Element objects have an internal [[RegisteredIntersectionObservers]] slot, /// > which is initialized to an empty list. This list holds IntersectionObserverRegistration records, which have: pub(crate) registered_intersection_observers: Vec<IntersectionObserverRegistration>, + pub(crate) cryptographic_nonce: String, } diff --git a/components/script/dom/readablestream.rs b/components/script/dom/readablestream.rs index 4982bfa32e3..d631a01e1e7 100644 --- a/components/script/dom/readablestream.rs +++ b/components/script/dom/readablestream.rs @@ -11,6 +11,7 @@ use std::rc::Rc; use base::id::{MessagePortId, MessagePortIndex}; use constellation_traits::MessagePortImpl; use dom_struct::dom_struct; +use ipc_channel::ipc::IpcSharedMemory; use js::conversions::ToJSValConvertible; use js::jsapi::{Heap, JSObject}; use js::jsval::{JSVal, NullValue, ObjectValue, UndefinedValue}; @@ -1131,12 +1132,14 @@ impl ReadableStream { /// Return bytes for synchronous use, if the stream has all data in memory. /// Useful for native source integration only. - pub(crate) fn get_in_memory_bytes(&self) -> Option<Vec<u8>> { + pub(crate) fn get_in_memory_bytes(&self) -> Option<IpcSharedMemory> { match self.controller.borrow().as_ref() { Some(ControllerType::Default(controller)) => controller .get() .expect("Stream should have controller.") - .get_in_memory_bytes(), + .get_in_memory_bytes() + .as_deref() + .map(IpcSharedMemory::from_bytes), _ => { unreachable!("Getting in-memory bytes for a stream with a non-default controller") }, diff --git a/components/script/dom/svgelement.rs b/components/script/dom/svgelement.rs index 9c8b990826d..a380dcff5ac 100644 --- a/components/script/dom/svgelement.rs +++ b/components/script/dom/svgelement.rs @@ -8,12 +8,13 @@ use js::rust::HandleObject; use script_bindings::str::DOMString; use stylo_dom::ElementState; +use crate::dom::attr::Attr; use crate::dom::bindings::codegen::Bindings::SVGElementBinding::SVGElementMethods; use crate::dom::bindings::inheritance::Castable; use crate::dom::bindings::root::{Dom, DomRoot, MutNullableDom}; use crate::dom::cssstyledeclaration::{CSSModificationAccess, CSSStyleDeclaration, CSSStyleOwner}; use crate::dom::document::Document; -use crate::dom::element::Element; +use crate::dom::element::{AttributeMutation, Element}; use crate::dom::node::{Node, NodeTraits}; use crate::dom::virtualmethods::VirtualMethods; use crate::script_runtime::CanGc; @@ -59,11 +60,33 @@ impl SVGElement { can_gc, ) } + + fn as_element(&self) -> &Element { + self.upcast::<Element>() + } } impl VirtualMethods for SVGElement { fn super_type(&self) -> Option<&dyn VirtualMethods> { - Some(self.upcast::<Element>() as &dyn VirtualMethods) + Some(self.as_element() as &dyn VirtualMethods) + } + + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) { + self.super_type() + .unwrap() + .attribute_mutated(attr, mutation, can_gc); + let element = self.as_element(); + if let (&local_name!("nonce"), mutation) = (attr.local_name(), mutation) { + match mutation { + AttributeMutation::Set(_) => { + let nonce = &**attr.value(); + element.update_nonce_internal_slot(nonce.to_owned()); + }, + AttributeMutation::Removed => { + element.update_nonce_internal_slot(String::new()); + }, + } + } } } @@ -82,13 +105,19 @@ impl SVGElementMethods<crate::DomTypeHolder> for SVGElement { }) } - // FIXME: The nonce should be stored in an internal slot instead of an - // attribute (https://html.spec.whatwg.org/multipage/#cryptographicnonce) + // <https://html.spec.whatwg.org/multipage/#globaleventhandlers> + global_event_handlers!(); + // https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce - make_getter!(Nonce, "nonce"); + fn Nonce(&self) -> DOMString { + self.as_element().nonce_value().into() + } // https://html.spec.whatwg.org/multipage/#dom-noncedelement-nonce - make_setter!(SetNonce, "nonce"); + fn SetNonce(&self, value: DOMString) { + self.as_element() + .update_nonce_internal_slot(value.to_string()) + } // https://html.spec.whatwg.org/multipage/#dom-fe-autofocus fn Autofocus(&self) -> bool { diff --git a/components/script/dom/svgimageelement.rs b/components/script/dom/svgimageelement.rs new file mode 100644 index 00000000000..17a5a9149d8 --- /dev/null +++ b/components/script/dom/svgimageelement.rs @@ -0,0 +1,96 @@ +/* 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 dom_struct::dom_struct; +use html5ever::{LocalName, Prefix, local_name, ns}; +use js::rust::HandleObject; +use style::attr::AttrValue; + +use crate::dom::attr::Attr; +use crate::dom::bindings::inheritance::Castable; +use crate::dom::bindings::root::DomRoot; +use crate::dom::bindings::str::DOMString; +use crate::dom::document::Document; +use crate::dom::element::AttributeMutation; +use crate::dom::node::{Node, NodeTraits}; +use crate::dom::svggraphicselement::SVGGraphicsElement; +use crate::dom::virtualmethods::VirtualMethods; +use crate::script_runtime::CanGc; + +/// <https://svgwg.org/svg2-draft/embedded.html#Placement> +const DEFAULT_WIDTH: u32 = 300; +const DEFAULT_HEIGHT: u32 = 150; + +#[dom_struct] +pub(crate) struct SVGImageElement { + svggraphicselement: SVGGraphicsElement, +} + +impl SVGImageElement { + fn new_inherited( + local_name: LocalName, + prefix: Option<Prefix>, + document: &Document, + ) -> SVGImageElement { + SVGImageElement { + svggraphicselement: SVGGraphicsElement::new_inherited(local_name, prefix, document), + } + } + + #[cfg_attr(crown, allow(crown::unrooted_must_root))] + pub(crate) fn new( + local_name: LocalName, + prefix: Option<Prefix>, + document: &Document, + proto: Option<HandleObject>, + can_gc: CanGc, + ) -> DomRoot<SVGImageElement> { + Node::reflect_node_with_proto( + Box::new(SVGImageElement::new_inherited(local_name, prefix, document)), + document, + proto, + can_gc, + ) + } + + /// <https://svgwg.org/svg2-draft/linking.html#processingURL> + fn fetch_image_resource(&self) { + // TODO: Process and fetch the image resource (as HTMLImageElement). + // Reject any resource fetching request immediately. + self.owner_global() + .task_manager() + .dom_manipulation_task_source() + .queue_simple_event(self.upcast(), atom!("error")); + } +} + +impl VirtualMethods for SVGImageElement { + fn super_type(&self) -> Option<&dyn VirtualMethods> { + Some(self.upcast::<SVGGraphicsElement>() as &dyn VirtualMethods) + } + + fn attribute_mutated(&self, attr: &Attr, mutation: AttributeMutation, can_gc: CanGc) { + self.super_type() + .unwrap() + .attribute_mutated(attr, mutation, can_gc); + if attr.local_name() == &local_name!("href") && + matches!(attr.namespace(), &ns!() | &ns!(xlink)) + { + if let AttributeMutation::Set(_) = mutation { + self.fetch_image_resource(); + } + } + } + + fn parse_plain_attribute(&self, name: &LocalName, value: DOMString) -> AttrValue { + match *name { + local_name!("width") => AttrValue::from_u32(value.into(), DEFAULT_WIDTH), + local_name!("height") => AttrValue::from_u32(value.into(), DEFAULT_HEIGHT), + _ => self + .super_type() + .unwrap() + .parse_plain_attribute(name, value), + } + } +} diff --git a/components/script/dom/transformstream.rs b/components/script/dom/transformstream.rs index 023fe7ac483..0251498980d 100644 --- a/components/script/dom/transformstream.rs +++ b/components/script/dom/transformstream.rs @@ -3,9 +3,12 @@ * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ use std::cell::Cell; +use std::collections::HashMap; use std::ptr::{self}; use std::rc::Rc; +use base::id::{MessagePortId, MessagePortIndex}; +use constellation_traits::MessagePortImpl; use dom_struct::dom_struct; use js::jsapi::{Heap, IsPromiseObject, JSObject}; use js::jsval::{JSVal, ObjectValue, UndefinedValue}; @@ -14,6 +17,9 @@ use script_bindings::callback::ExceptionHandling; use script_bindings::realms::InRealm; use super::bindings::codegen::Bindings::QueuingStrategyBinding::QueuingStrategySize; +use super::bindings::structuredclone::StructuredData; +use super::bindings::transferable::Transferable; +use super::messageport::MessagePort; use super::promisenativehandler::Callback; use super::types::{TransformStreamDefaultController, WritableStream}; use crate::dom::bindings::cell::DomRefCell; @@ -997,3 +1003,103 @@ impl TransformStreamMethods<crate::DomTypeHolder> for TransformStream { self.writable.get().expect("writable stream is not set") } } + +/// <https://streams.spec.whatwg.org/#ts-transfer> +impl Transferable for TransformStream { + type Index = MessagePortIndex; + type Data = MessagePortImpl; + + fn transfer(&self) -> Result<(MessagePortId, MessagePortImpl), ()> { + let global = self.global(); + let realm = enter_realm(&*global); + let comp = InRealm::Entered(&realm); + let cx = GlobalScope::get_cx(); + let can_gc = CanGc::note(); + + // Let readable be value.[[readable]]. + let readable = self.get_readable(); + + // Let writable be value.[[writable]]. + let writable = self.get_writable(); + + // If ! IsReadableStreamLocked(readable) is true, throw a "DataCloneError" DOMException. + if readable.is_locked() { + return Err(()); + } + + // If ! IsWritableStreamLocked(writable) is true, throw a "DataCloneError" DOMException. + if writable.is_locked() { + return Err(()); + } + + // Create the shared port pair + let port_1 = MessagePort::new(&global, can_gc); + global.track_message_port(&port_1, None); + let port_2 = MessagePort::new(&global, can_gc); + global.track_message_port(&port_2, None); + global.entangle_ports(*port_1.message_port_id(), *port_2.message_port_id()); + + // Create a proxy WritableStream wired to port_1 + let proxy_writable = WritableStream::new_with_proto(&global, None, can_gc); + proxy_writable.setup_cross_realm_transform_writable(cx, &port_1, can_gc); + + // Pipe readable into the proxy writable (→ port_1) + let pipe1 = readable.pipe_to( + cx, + &global, + &proxy_writable, + false, + false, + false, + comp, + can_gc, + ); + pipe1.set_promise_is_handled(); + + // Create a proxy ReadableStream wired to port_1 + let proxy_readable = ReadableStream::new_with_proto(&global, None, can_gc); + proxy_readable.setup_cross_realm_transform_readable(cx, &port_1, can_gc); + + // Pipe proxy readable (← port_1) into writable + let pipe2 = + proxy_readable.pipe_to(cx, &global, &writable, false, false, false, comp, can_gc); + pipe2.set_promise_is_handled(); + + // Set dataHolder.[[readable]] to ! StructuredSerializeWithTransfer(readable, « readable »). + // Set dataHolder.[[writable]] to ! StructuredSerializeWithTransfer(writable, « writable »). + port_2.transfer() + } + + fn transfer_receive( + owner: &GlobalScope, + id: MessagePortId, + port_impl: MessagePortImpl, + ) -> Result<DomRoot<Self>, ()> { + let can_gc = CanGc::note(); + + // Let readableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[readable]], the current Realm). + // Set value.[[readable]] to readableRecord.[[Deserialized]]. + let readable = ReadableStream::transfer_receive(owner, id, port_impl.clone())?; + + // Let writableRecord be ! StructuredDeserializeWithTransfer(dataHolder.[[writable]], the current Realm). + let writable = WritableStream::transfer_receive(owner, id, port_impl)?; + + // Set value.[[readable]] to readableRecord.[[Deserialized]]. + // Set value.[[writable]] to writableRecord.[[Deserialized]]. + // Set value.[[backpressure]], value.[[backpressureChangePromise]], and value.[[controller]] to undefined. + let stream = TransformStream::new_with_proto(owner, None, can_gc); + stream.readable.set(Some(&readable)); + stream.writable.set(Some(&writable)); + + Ok(stream) + } + + fn serialized_storage<'a>( + data: StructuredData<'a, '_>, + ) -> &'a mut Option<HashMap<MessagePortId, Self::Data>> { + match data { + StructuredData::Reader(r) => &mut r.port_impls, + StructuredData::Writer(w) => &mut w.ports, + } + } +} diff --git a/components/script/dom/trustedhtml.rs b/components/script/dom/trustedhtml.rs index 8508f28c150..d1ca3cd5e71 100644 --- a/components/script/dom/trustedhtml.rs +++ b/components/script/dom/trustedhtml.rs @@ -6,8 +6,11 @@ use std::fmt; use dom_struct::dom_struct; +use crate::conversions::Convert; use crate::dom::bindings::codegen::Bindings::TrustedHTMLBinding::TrustedHTMLMethods; -use crate::dom::bindings::codegen::UnionTypes::TrustedHTMLOrString; +use crate::dom::bindings::codegen::UnionTypes::{ + TrustedHTMLOrNullIsEmptyString, TrustedHTMLOrString, +}; use crate::dom::bindings::error::Fallible; use crate::dom::bindings::reflector::{Reflector, reflect_dom_object}; use crate::dom::bindings::root::DomRoot; @@ -80,3 +83,16 @@ impl TrustedHTMLMethods<crate::DomTypeHolder> for TrustedHTML { DOMString::from(&*self.data) } } + +impl Convert<TrustedHTMLOrString> for TrustedHTMLOrNullIsEmptyString { + fn convert(self) -> TrustedHTMLOrString { + match self { + TrustedHTMLOrNullIsEmptyString::TrustedHTML(trusted_html) => { + TrustedHTMLOrString::TrustedHTML(trusted_html) + }, + TrustedHTMLOrNullIsEmptyString::NullIsEmptyString(str) => { + TrustedHTMLOrString::String(str) + }, + } + } +} diff --git a/components/script/dom/virtualmethods.rs b/components/script/dom/virtualmethods.rs index 57ecba7b172..1d992b1f301 100644 --- a/components/script/dom/virtualmethods.rs +++ b/components/script/dom/virtualmethods.rs @@ -61,6 +61,7 @@ use crate::dom::htmlvideoelement::HTMLVideoElement; use crate::dom::node::{BindContext, ChildrenMutation, CloneChildrenFlag, Node, UnbindContext}; use crate::dom::shadowroot::ShadowRoot; use crate::dom::svgelement::SVGElement; +use crate::dom::svgimageelement::SVGImageElement; use crate::dom::svgsvgelement::SVGSVGElement; /// Trait to allow DOM nodes to opt-in to overriding (or adding to) common @@ -299,6 +300,9 @@ pub(crate) fn vtable_for(node: &Node) -> &dyn VirtualMethods { node.downcast::<HTMLTitleElement>().unwrap() as &dyn VirtualMethods }, NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( + SVGGraphicsElementTypeId::SVGImageElement, + ))) => node.downcast::<SVGImageElement>().unwrap() as &dyn VirtualMethods, + NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGGraphicsElement( SVGGraphicsElementTypeId::SVGSVGElement, ))) => node.downcast::<SVGSVGElement>().unwrap() as &dyn VirtualMethods, NodeTypeId::Element(ElementTypeId::SVGElement(SVGElementTypeId::SVGElement)) => { diff --git a/components/script/messaging.rs b/components/script/messaging.rs index e0ea9e30af2..08d6fc841cf 100644 --- a/components/script/messaging.rs +++ b/components/script/messaging.rs @@ -91,6 +91,7 @@ impl MixedMessage { #[cfg(feature = "webgpu")] ScriptThreadMessage::SetWebGPUPort(..) => None, ScriptThreadMessage::SetScrollStates(id, ..) => Some(*id), + ScriptThreadMessage::EvaluateJavaScript(id, _, _) => Some(*id), }, MixedMessage::FromScript(inner_msg) => match inner_msg { MainThreadScriptMsg::Common(CommonScriptMsg::Task(_, _, pipeline_id, _)) => { diff --git a/components/script/script_thread.rs b/components/script/script_thread.rs index d6ab18be49b..4815e6feae1 100644 --- a/components/script/script_thread.rs +++ b/components/script/script_thread.rs @@ -50,9 +50,9 @@ use devtools_traits::{ }; use embedder_traits::user_content_manager::UserContentManager; use embedder_traits::{ - CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent, MediaSessionActionType, - MouseButton, MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, - WebDriverScriptCommand, + CompositorHitTestResult, EmbedderMsg, FocusSequenceNumber, InputEvent, + JavaScriptEvaluationError, JavaScriptEvaluationId, MediaSessionActionType, MouseButton, + MouseButtonAction, MouseButtonEvent, Theme, ViewportDetails, WebDriverScriptCommand, }; use euclid::Point2D; use euclid::default::Rect; @@ -156,6 +156,7 @@ use crate::script_runtime::{ }; use crate::task_queue::TaskQueue; use crate::task_source::{SendableTaskSource, TaskSourceName}; +use crate::webdriver_handlers::jsval_to_webdriver; use crate::{devtools, webdriver_handlers}; thread_local!(static SCRIPT_THREAD_ROOT: Cell<Option<*const ScriptThread>> = const { Cell::new(None) }); @@ -1878,6 +1879,9 @@ impl ScriptThread { ScriptThreadMessage::SetScrollStates(pipeline_id, scroll_states) => { self.handle_set_scroll_states(pipeline_id, scroll_states) }, + ScriptThreadMessage::EvaluateJavaScript(pipeline_id, evaluation_id, script) => { + self.handle_evaluate_javascript(pipeline_id, evaluation_id, script, can_gc); + }, } } @@ -3815,6 +3819,53 @@ impl ScriptThread { ) } } + + fn handle_evaluate_javascript( + &self, + pipeline_id: PipelineId, + evaluation_id: JavaScriptEvaluationId, + script: String, + can_gc: CanGc, + ) { + let Some(window) = self.documents.borrow().find_window(pipeline_id) else { + let _ = self.senders.pipeline_to_constellation_sender.send(( + pipeline_id, + ScriptToConstellationMessage::FinishJavaScriptEvaluation( + evaluation_id, + Err(JavaScriptEvaluationError::WebViewNotReady), + ), + )); + return; + }; + + let global_scope = window.as_global_scope(); + let realm = enter_realm(global_scope); + let context = window.get_cx(); + + rooted!(in(*context) let mut return_value = UndefinedValue()); + global_scope.evaluate_js_on_global_with_result( + &script, + return_value.handle_mut(), + ScriptFetchOptions::default_classic_script(global_scope), + global_scope.api_base_url(), + can_gc, + ); + let result = match jsval_to_webdriver( + context, + global_scope, + return_value.handle(), + (&realm).into(), + can_gc, + ) { + Ok(ref value) => Ok(value.into()), + Err(_) => Err(JavaScriptEvaluationError::SerializationError), + }; + + let _ = self.senders.pipeline_to_constellation_sender.send(( + pipeline_id, + ScriptToConstellationMessage::FinishJavaScriptEvaluation(evaluation_id, result), + )); + } } impl Drop for ScriptThread { diff --git a/components/script/webdriver_handlers.rs b/components/script/webdriver_handlers.rs index 330ae4f0788..6b4264d945e 100644 --- a/components/script/webdriver_handlers.rs +++ b/components/script/webdriver_handlers.rs @@ -902,7 +902,7 @@ pub(crate) fn handle_get_page_source( .find_document(pipeline) .ok_or(ErrorStatus::UnknownError) .and_then(|document| match document.GetDocumentElement() { - Some(element) => match element.GetOuterHTML(can_gc) { + Some(element) => match element.outer_html(can_gc) { Ok(source) => Ok(source.to_string()), Err(_) => { match XMLSerializer::new(document.window(), None, can_gc) |