diff --git a/vendor/xslt-tests/filters b/vendor/xslt-tests/filters index f5218eac4..fa9b913a6 100644 --- a/vendor/xslt-tests/filters +++ b/vendor/xslt-tests/filters @@ -592,7 +592,6 @@ attribute-0401 attribute-0501 attribute-0601 attribute-0701 -attribute-0801 attribute-0803 attribute-0804 attribute-0805 @@ -756,7 +755,6 @@ axes-045 axes-046 axes-048 axes-049 -axes-050 axes-051 axes-052 axes-057 @@ -1011,12 +1009,10 @@ bug-0701 bug-0801 bug-0901 bug-1001 -bug-1101 bug-1201 bug-1202 bug-1203 bug-1301 -bug-1401 bug-1402 bug-1403 bug-1405 @@ -1024,8 +1020,6 @@ bug-1406 bug-1501 bug-1601 bug-1701 -bug-1801 -bug-1802 bug-1901 bug-2001 bug-2101 @@ -1051,7 +1045,6 @@ bug-4001 bug-4301 bug-4401 bug-4501 -bug-4601 bug-4701 bug-4702 bug-4703 @@ -1368,9 +1361,6 @@ copy-2801 copy-3001 copy-3002 copy-3003 -copy-3102 -copy-3301 -copy-3302 copy-3501 copy-3601 copy-3602 @@ -1729,14 +1719,12 @@ element-0003 element-0004 element-0005 element-0006 -element-0102 element-0104 element-0105 element-0106 element-0107 element-0108 element-0110 -element-0111 element-0201 element-0301 element-0302 @@ -3223,7 +3211,6 @@ import-schema-060 import-schema-061 import-schema-062 import-schema-063 -import-schema-064 import-schema-065 import-schema-066 import-schema-067 @@ -3647,20 +3634,13 @@ load-xquery-module-002 load-xquery-module-003 load-xquery-module-004 = lre -lre-003 -lre-005 lre-006 -lre-010 lre-011 lre-012 -lre-014 -lre-017 -lre-018 lre-019 lre-020 lre-021 lre-022 -lre-024 lre-025 lre-026 lre-027 @@ -4431,7 +4411,6 @@ mode-1905 = namespace namespace-0101 namespace-0201 -namespace-0202 namespace-0501 namespace-0601 namespace-0602 @@ -4442,7 +4421,6 @@ namespace-0802 namespace-0901 namespace-0902 namespace-0903 -namespace-0904 namespace-0906 namespace-0907 namespace-0908 @@ -4453,7 +4431,6 @@ namespace-0912 namespace-0913 namespace-0914 namespace-1101 -namespace-1102 namespace-1103 namespace-1104 namespace-1502 @@ -4464,13 +4441,10 @@ namespace-1801 namespace-1901 namespace-2001 namespace-2101 -namespace-2201 namespace-2301 namespace-2302 namespace-2401 namespace-2501 -namespace-2503 -namespace-2601 namespace-2602 namespace-2606 namespace-2607 @@ -4479,9 +4453,7 @@ namespace-2609 namespace-2610 namespace-2611 namespace-2612 -namespace-2614 namespace-2615 -namespace-2616 namespace-2617 namespace-2618 namespace-2619 @@ -4502,8 +4474,6 @@ namespace-2701 namespace-2801 namespace-2901 namespace-3002 -namespace-3003 -namespace-3004 namespace-3101 namespace-3102 namespace-3103 @@ -4523,31 +4493,18 @@ namespace-3116 namespace-3117 namespace-3118 namespace-3119 -namespace-3120 -namespace-3121 -namespace-3122 -namespace-3123 namespace-3124 -namespace-3126 namespace-3127 namespace-3128 -namespace-3129 namespace-3130 namespace-3131 namespace-3132 -namespace-3133 namespace-3134 -namespace-3135 namespace-3136 namespace-3137 -namespace-3138 -namespace-3139 namespace-3140 -namespace-3142 namespace-3143 namespace-3144 -namespace-3145 -namespace-3146 namespace-3147 namespace-3148 namespace-3149 @@ -4557,30 +4514,19 @@ namespace-3152 namespace-3153 namespace-3154 namespace-3155 -namespace-3158 -namespace-3159 namespace-3160 namespace-3161 -namespace-3162 namespace-3163 namespace-3164 namespace-3201 namespace-3202 namespace-3203 namespace-3301 -namespace-3302 -namespace-3303 namespace-3304 -namespace-3306 -namespace-3307 -namespace-3308 -namespace-3309 namespace-3310 namespace-3311 -namespace-3312 namespace-3313 namespace-3314 -namespace-3315 namespace-3401 namespace-3501 namespace-3502 @@ -4588,23 +4534,10 @@ namespace-3503 namespace-3504 namespace-3505 namespace-3601 -namespace-4001 -namespace-4002 -namespace-4003 -namespace-4004 -namespace-4005 -namespace-4007 -namespace-4008 -namespace-4009 -namespace-4301 namespace-4302 namespace-4401 -namespace-4501 namespace-4601 namespace-4801 -namespace-4901 -namespace-5001 -namespace-5101 namespace-5201 namespace-5601 namespace-5602 @@ -4612,8 +4545,6 @@ namespace-5701 namespace-5901 namespace-5902 namespace-5903 -namespace-6002 -namespace-6003 namespace-6201 namespace-6202 = namespace-alias @@ -13619,7 +13550,6 @@ xml-to-json-D511 = xml-version xml-version-002 xml-version-003 -xml-version-006 xml-version-007 xml-version-008 xml-version-009 @@ -13678,7 +13608,6 @@ xpath-default-namespace-0107 xpath-default-namespace-0108 xpath-default-namespace-0201 xpath-default-namespace-0202 -xpath-default-namespace-0301 xpath-default-namespace-0401 xpath-default-namespace-0501 xpath-default-namespace-0502 diff --git a/xee-interpreter/src/sequence/serialization.rs b/xee-interpreter/src/sequence/serialization.rs index 3c3157be8..25b1c5e6d 100644 --- a/xee-interpreter/src/sequence/serialization.rs +++ b/xee-interpreter/src/sequence/serialization.rs @@ -207,12 +207,35 @@ pub(crate) fn serialize_sequence( } } +/// Namespace fixup on a normalized document: constructed elements and +/// attributes can use a namespace that has no prefix binding anywhere in +/// the tree; invent prefixes for these so the result can be serialized. +/// The document can have multiple top-level elements, and each is fixed +/// up separately. +fn namespace_fixup(xot: &mut Xot, node: xot::Node) -> Result<(), error::Error> { + let xml_prefix = xot.xml_prefix(); + let xml_namespace = xot.xml_namespace(); + let elements: Vec<_> = xot + .children(node) + .filter(|child| xot.is_element(*child)) + .collect(); + for element in elements { + // bind the implicit xml prefix explicitly so create_missing_prefixes + // doesn't invent a prefix for it; the binding is never serialized + xot.namespaces_mut(element) + .insert(xml_prefix, xml_namespace); + xot.create_missing_prefixes(element)?; + } + Ok(()) +} + fn serialize_xml( arg: &Sequence, parameters: SerializationParameters, xot: &mut Xot, ) -> Result { let node = arg.normalize(¶meters.item_separator, xot)?; + namespace_fixup(xot, node)?; let indentation = xot_indentation(¶meters, xot); let cdata_section_elements = xot_names(¶meters.cdata_section_elements, xot); let declaration = if !parameters.omit_xml_declaration { diff --git a/xee-xslt-ast/src/ast_core.rs b/xee-xslt-ast/src/ast_core.rs index a2056c9af..67abb554e 100644 --- a/xee-xslt-ast/src/ast_core.rs +++ b/xee-xslt-ast/src/ast_core.rs @@ -112,25 +112,6 @@ pub enum ExcludeResultPrefixes { Prefixes(Vec), } -impl ExcludeResultPrefixes { - // TODO: This combine isn't good enough; it should take existing prefixes - // into account, which we do have on context - pub(crate) fn combine(&self, other: ExcludeResultPrefixes) -> Self { - match (self, other) { - (ExcludeResultPrefixes::All, _) => ExcludeResultPrefixes::All, - (_, ExcludeResultPrefixes::All) => ExcludeResultPrefixes::All, - ( - ExcludeResultPrefixes::Prefixes(prefixes), - ExcludeResultPrefixes::Prefixes(other_prefixes), - ) => { - let mut prefixes = prefixes.clone(); - prefixes.extend(other_prefixes); - ExcludeResultPrefixes::Prefixes(prefixes) - } - } - } -} - #[derive(Debug, Clone, PartialEq, Eq)] #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub enum ExcludeResultPrefix { @@ -1827,6 +1808,8 @@ pub type SequenceConstructor = Vec; #[cfg_attr(feature = "serde", derive(serde::Serialize))] pub struct ElementNode { pub name: Name, + /// prefix/namespace pairs this element declares in the result + pub namespaces: Vec<(String, String)>, pub attributes: Vec<(Name, ValueTemplate)>, pub sequence_constructor: SequenceConstructor, pub span: Span, diff --git a/xee-xslt-ast/src/attributes.rs b/xee-xslt-ast/src/attributes.rs index d345ecc11..dc1c284f1 100644 --- a/xee-xslt-ast/src/attributes.rs +++ b/xee-xslt-ast/src/attributes.rs @@ -31,11 +31,13 @@ impl<'a> Attributes<'a> { pub(crate) fn with_standard(self) -> Result { // create a new content has a context including standard attributes - let content = self.content.with_context( - self.content - .context - .with_standard(self.content.xot_namespaces(), self.standard()?), - ); + let content = self + .content + .with_context(self.content.context.with_standard( + self.content.state, + self.content.xot_namespaces(), + self.standard()?, + )); // we now create a new attributes object that has the new content Ok(Self { content, ..self }) } diff --git a/xee-xslt-ast/src/context.rs b/xee-xslt-ast/src/context.rs index fdbcb0655..154b8a3e4 100644 --- a/xee-xslt-ast/src/context.rs +++ b/xee-xslt-ast/src/context.rs @@ -22,9 +22,13 @@ pub(crate) struct Context { pub(crate) expand_text: bool, version: Decimal, xpath_default_namespace: ast::Uri, - // cumulative - exclude_result_prefixes: ast::ExcludeResultPrefixes, + // cumulative; the namespaces designated by exclude-result-prefixes and + // extension-element-prefixes, resolved against the in-scope bindings of + // the element bearing the attribute + excluded_namespaces: HashSet, extension_element_prefixes: Vec, + // the in-scope prefixes of the nearest enclosing literal result element + literal_result_element_prefixes: xot::Prefixes, } impl Context { @@ -46,8 +50,9 @@ impl Context { expand_text: false, version: Decimal::from_str("3.0").unwrap(), xpath_default_namespace: "".to_string(), - exclude_result_prefixes: ast::ExcludeResultPrefixes::Prefixes(vec![]), + excluded_namespaces: HashSet::new(), extension_element_prefixes: vec![], + literal_result_element_prefixes: xot::Prefixes::new(), } } @@ -96,6 +101,7 @@ impl Context { pub(crate) fn with_standard( &self, + state: &State, namespaces: xot::Namespaces, standard: ast::Standard, ) -> Self { @@ -137,13 +143,42 @@ impl Context { } else { self.xpath_default_namespace.clone() }; - let exclude_result_prefixes = - if let Some(exclude_result_prefixes) = standard.exclude_result_prefixes { - self.exclude_result_prefixes - .combine(exclude_result_prefixes) - } else { - self.exclude_result_prefixes.clone() - }; + // exclude-result-prefixes and extension-element-prefixes designate + // namespaces by resolving each prefix against the bindings in scope + // at the element bearing the attribute + let mut excluded_namespaces = self.excluded_namespaces.clone(); + let exclude_prefix = |wanted: &str, excluded: &mut HashSet| { + // TODO: a prefix without a binding is a static error (XTSE0808) + for (prefix_id, namespace_id) in &expanded_prefixes { + if state.xot.prefix_str(*prefix_id) == wanted { + excluded.insert(*namespace_id); + } + } + }; + if let Some(exclude_result_prefixes) = standard.exclude_result_prefixes { + match exclude_result_prefixes { + ast::ExcludeResultPrefixes::All => { + excluded_namespaces.extend(expanded_prefixes.values().copied()); + } + ast::ExcludeResultPrefixes::Prefixes(prefixes) => { + for prefix in prefixes { + match prefix { + ast::ExcludeResultPrefix::Default => { + exclude_prefix("", &mut excluded_namespaces) + } + ast::ExcludeResultPrefix::Prefix(prefix) => { + exclude_prefix(&prefix, &mut excluded_namespaces) + } + } + } + } + } + } + if let Some(extension_element_prefixes) = &standard.extension_element_prefixes { + for prefix in extension_element_prefixes { + exclude_prefix(prefix, &mut excluded_namespaces); + } + } let extension_element_prefixes = if let Some(extension_element_prefixes) = standard.extension_element_prefixes { // TODO for now just add all prefixes. This isn't right. @@ -164,12 +199,62 @@ impl Context { expand_text, version, xpath_default_namespace, - exclude_result_prefixes, + excluded_namespaces, extension_element_prefixes, ..self.clone() } } + /// A context for the children of a literal result element, which + /// remembers the in-scope prefixes the element declares in the result. + pub(crate) fn with_literal_result_element(&self) -> Self { + Self { + literal_result_element_prefixes: self.prefixes.clone(), + ..self.clone() + } + } + + /// A context for content that constructs a separate tree (such as + /// xsl:variable): its elements cannot rely on an enclosing literal + /// result element to declare their namespaces. + pub(crate) fn without_literal_result_element(&self) -> Self { + Self { + literal_result_element_prefixes: xot::Prefixes::new(), + ..self.clone() + } + } + + /// The namespaces a literal result element declares in the result: its + /// in-scope namespaces, except the XSLT namespace, excluded namespaces + /// (exclude-result-prefixes and extension element prefixes), and + /// namespaces already declared in the result by the nearest enclosing + /// literal result element. See XSLT 3.0 section 11.1.3. + pub(crate) fn literal_result_element_namespaces(&self, state: &State) -> Vec<(String, String)> { + let mut namespaces = Vec::new(); + for (prefix_id, namespace_id) in &self.prefixes { + if self.literal_result_element_prefixes.get(prefix_id) == Some(namespace_id) { + continue; + } + if *namespace_id == state.names.xsl_ns + || self.excluded_namespaces.contains(namespace_id) + { + continue; + } + let namespace = state.xot.namespace_str(*namespace_id); + // an xmlns="" undeclaration is not a namespace + if namespace.is_empty() { + continue; + } + namespaces.push(( + state.xot.prefix_str(*prefix_id).to_string(), + namespace.to_string(), + )); + } + // sort for a stable declaration order + namespaces.sort(); + namespaces + } + pub(crate) fn namespaces<'a>(&'a self, state: &'a State) -> Namespaces { let mut namespaces = HashMap::new(); for (prefix, ns) in &self.prefixes { diff --git a/xee-xslt-ast/src/instruction.rs b/xee-xslt-ast/src/instruction.rs index 7db34b7c9..1b785b06f 100644 --- a/xee-xslt-ast/src/instruction.rs +++ b/xee-xslt-ast/src/instruction.rs @@ -138,8 +138,14 @@ impl InstructionParser for ast::ElementNode { .state .xot .name_ref(attributes.element.name(), content.node)?; + let namespaces = content + .context + .literal_result_element_namespaces(content.state); + // children only declare namespaces this element doesn't already + let content = content.with_context(content.context.with_literal_result_element()); Ok(ast::ElementNode { name: name.to_owned(), + namespaces, attributes: element_attributes, span: content.span()?, sequence_constructor: content.sequence_constructor()?, @@ -1092,7 +1098,12 @@ impl InstructionParser for ast::Message { span: content.span()?, - sequence_constructor: content.sequence_constructor()?, + // this content constructs a separate tree; its elements + // cannot rely on an enclosing literal result element for + // their namespace declarations + sequence_constructor: content + .with_context(content.context.without_literal_result_element()) + .sequence_constructor()?, }) } } @@ -1411,7 +1422,12 @@ impl InstructionParser for ast::Param { span: content.span()?, - sequence_constructor: content.sequence_constructor()?, + // this content constructs a separate tree; its elements + // cannot rely on an enclosing literal result element for + // their namespace declarations + sequence_constructor: content + .with_context(content.context.without_literal_result_element()) + .sequence_constructor()?, }) } } @@ -1684,7 +1700,12 @@ impl InstructionParser for ast::Variable { span: content.span()?, - sequence_constructor: content.sequence_constructor()?, + // this content constructs a separate tree; its elements + // cannot rely on an enclosing literal result element for + // their namespace declarations + sequence_constructor: content + .with_context(content.context.without_literal_result_element()) + .sequence_constructor()?, }) } @@ -1736,7 +1757,12 @@ impl InstructionParser for ast::WithParam { span: content.span()?, - sequence_constructor: content.sequence_constructor()?, + // this content constructs a separate tree; its elements + // cannot rely on an enclosing literal result element for + // their namespace declarations + sequence_constructor: content + .with_context(content.context.without_literal_result_element()) + .sequence_constructor()?, }) } } diff --git a/xee-xslt-ast/tests/snapshots/snapshot_tests__attributes_on_literal_element.snap b/xee-xslt-ast/tests/snapshots/snapshot_tests__attributes_on_literal_element.snap index 9f69d0dce..bdb802221 100644 --- a/xee-xslt-ast/tests/snapshots/snapshot_tests__attributes_on_literal_element.snap +++ b/xee-xslt-ast/tests/snapshots/snapshot_tests__attributes_on_literal_element.snap @@ -1,5 +1,6 @@ --- -source: xee-xslt-ast/src/instruction.rs +source: xee-xslt-ast/tests/snapshot_tests.rs +assertion_line: 258 expression: "parse_sequence_constructor_item(r#\"

\"#)" --- Ok(Content(Element(ElementNode( @@ -8,6 +9,7 @@ Ok(Content(Element(ElementNode( namespace_str: "", prefix_str: "", ), + namespaces: [], attributes: [ (OwnedName( local_name_str: "foo", diff --git a/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element.snap b/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element.snap index 32bb5ac98..6a8fbe7cd 100644 --- a/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element.snap +++ b/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element.snap @@ -1,5 +1,6 @@ --- -source: xee-xslt-ast/src/instruction.rs +source: xee-xslt-ast/tests/snapshot_tests.rs +assertion_line: 98 expression: "parse_sequence_constructor_item(r#\"\"#)" --- Ok(Content(Element(ElementNode( @@ -8,6 +9,7 @@ Ok(Content(Element(ElementNode( namespace_str: "", prefix_str: "", ), + namespaces: [], attributes: [], sequence_constructor: [], span: Span( diff --git a/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element_with_standard_attribute.snap b/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element_with_standard_attribute.snap index 69299e37a..3b27b417c 100644 --- a/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element_with_standard_attribute.snap +++ b/xee-xslt-ast/tests/snapshots/snapshot_tests__literal_result_element_with_standard_attribute.snap @@ -1,5 +1,6 @@ --- -source: xee-xslt-ast/src/instruction.rs +source: xee-xslt-ast/tests/snapshot_tests.rs +assertion_line: 103 expression: "parse_sequence_constructor_item(r#\"\"#)" --- Ok(Content(Element(ElementNode( @@ -8,6 +9,7 @@ Ok(Content(Element(ElementNode( namespace_str: "", prefix_str: "", ), + namespaces: [], attributes: [], sequence_constructor: [], span: Span( diff --git a/xee-xslt-ast/tests/snapshots/snapshot_tests__nested_literal_elements.snap b/xee-xslt-ast/tests/snapshots/snapshot_tests__nested_literal_elements.snap index 1d5aa5e19..46b70130d 100644 --- a/xee-xslt-ast/tests/snapshots/snapshot_tests__nested_literal_elements.snap +++ b/xee-xslt-ast/tests/snapshots/snapshot_tests__nested_literal_elements.snap @@ -1,5 +1,6 @@ --- -source: xee-xslt-ast/src/instruction.rs +source: xee-xslt-ast/tests/snapshot_tests.rs +assertion_line: 244 expression: "parse_sequence_constructor_item(r#\"

\"#)" --- Ok(Instruction(If(If( @@ -30,6 +31,7 @@ Ok(Instruction(If(If( namespace_str: "", prefix_str: "", ), + namespaces: [], attributes: [], sequence_constructor: [ Content(Element(ElementNode( @@ -38,6 +40,7 @@ Ok(Instruction(If(If( namespace_str: "", prefix_str: "", ), + namespaces: [], attributes: [], sequence_constructor: [], span: Span( diff --git a/xee-xslt-ast/tests/snapshots/snapshot_tests__sequence_constructor_nested_in_literal_element.snap b/xee-xslt-ast/tests/snapshots/snapshot_tests__sequence_constructor_nested_in_literal_element.snap index ca1318c41..43996c06e 100644 --- a/xee-xslt-ast/tests/snapshots/snapshot_tests__sequence_constructor_nested_in_literal_element.snap +++ b/xee-xslt-ast/tests/snapshots/snapshot_tests__sequence_constructor_nested_in_literal_element.snap @@ -1,5 +1,6 @@ --- -source: xee-xslt-ast/src/instruction.rs +source: xee-xslt-ast/tests/snapshot_tests.rs +assertion_line: 251 expression: "parse_sequence_constructor_item(r#\"

foo

\"#)" --- Ok(Instruction(If(If( @@ -30,6 +31,7 @@ Ok(Instruction(If(If( namespace_str: "", prefix_str: "", ), + namespaces: [], attributes: [], sequence_constructor: [ Instruction(If(If( diff --git a/xee-xslt-compiler/src/ast_ir.rs b/xee-xslt-compiler/src/ast_ir.rs index a58500360..1020cf04d 100644 --- a/xee-xslt-compiler/src/ast_ir.rs +++ b/xee-xslt-compiler/src/ast_ir.rs @@ -483,6 +483,22 @@ impl<'a> IrConverter<'a> { let (element_atom, mut bindings) = bindings .bind_expr_no_span(&mut self.variables, name_expr) .atom_bindings(); + for (prefix, namespace) in &element_node.namespaces { + let namespace_expr = ir::Expr::XmlNamespace(ir::XmlNamespace { + prefix: self.constant_string(prefix), + namespace: self.constant_string(namespace), + }); + let (namespace_atom, namespace_bindings) = Bindings::empty() + .bind_expr_no_span(&mut self.variables, namespace_expr) + .atom_bindings(); + let append_expr = ir::Expr::XmlAppend(ir::XmlAppend { + parent: element_atom.clone(), + child: namespace_atom, + }); + let append_bindings = + namespace_bindings.bind_expr_no_span(&mut self.variables, append_expr); + bindings = bindings.concat(append_bindings); + } for (name, value) in &element_node.attributes { let (value_atom, value_bindings) = self.attribute_value_template(value)?.atom_bindings(); @@ -714,8 +730,12 @@ impl<'a> IrConverter<'a> { } fn empty_string(&self) -> ir::AtomS { + self.constant_string("") + } + + fn constant_string(&self, value: &str) -> ir::AtomS { Spanned::new( - ir::Atom::Const(ir::Const::String("".to_string())), + ir::Atom::Const(ir::Const::String(value.to_string())), (0..0).into(), ) } @@ -943,7 +963,10 @@ impl<'a> IrConverter<'a> { ir::Atom::Const(ir::Const::String(name.local_name().to_string())), (0..0).into(), ); - let namespace = self.empty_string(); + let namespace = Spanned::new( + ir::Atom::Const(ir::Const::String(name.namespace().to_string())), + (0..0).into(), + ); let binding = self .variables diff --git a/xee-xslt-compiler/tests/test_xslt.rs b/xee-xslt-compiler/tests/test_xslt.rs index 946324eba..3267ac367 100644 --- a/xee-xslt-compiler/tests/test_xslt.rs +++ b/xee-xslt-compiler/tests/test_xslt.rs @@ -1230,3 +1230,231 @@ fn test_basic_iterate_params() { "124" ); } + +// https://github.com/Paligo/xee/issues/139 +#[test] +fn test_literal_result_element_default_namespace() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!( + xml(&xot, output), + r#""# + ); +} + +#[test] +fn test_literal_result_element_prefixed_namespace() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!( + xml(&xot, output), + r#""# + ); +} + +#[test] +fn test_literal_result_element_stylesheet_default_namespace() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!( + xml(&xot, output), + r#""# + ); +} + +#[test] +fn test_literal_result_element_copies_unused_namespace() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!(xml(&xot, output), r#""#); +} + +#[test] +fn test_literal_result_element_exclude_result_prefixes() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!(xml(&xot, output), ""); +} + +#[test] +fn test_xsl_element_with_namespace_serializes() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + let serialized = output + .serialize( + xee_interpreter::sequence::SerializationParameters::new(), + &mut xot, + ) + .unwrap(); + assert!( + serialized.contains("http://www.w3.org/1999/xhtml"), + "namespace must survive serialization, got: {}", + serialized + ); +} + +#[test] +fn test_literal_result_element_namespaced_attribute() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!(xml(&xot, output), r#""#); +} + +#[test] +fn test_literal_result_element_in_variable_keeps_namespaces() { + // xsl:variable content builds a separate tree, so its elements must + // carry their own in-scope namespace copies + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!( + xml(&xot, output), + r#""# + ); +} + +#[test] +fn test_exclude_result_prefixes_resolved_where_specified() { + // exclude-result-prefixes designates the namespace bound to the prefix + // at the element bearing the attribute, not wherever the prefix is + // later rebound + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + assert_eq!(xml(&xot, output), r#""#); +} + +#[test] +fn test_exclude_result_prefixes_all_keeps_local_namespaces() { + // #all designates the namespaces in scope at the bearing element; a + // namespace declared further down on the literal result element itself + // is not excluded + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r##" + + +"##, + ) + .unwrap(); + assert_eq!(xml(&xot, output), r#""#); +} + +#[test] +fn test_serialize_fixes_prefixes_on_all_top_level_elements() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + let serialized = output + .serialize( + xee_interpreter::sequence::SerializationParameters::new(), + &mut xot, + ) + .unwrap(); + assert!( + serialized.contains("http://one") && serialized.contains("http://two"), + "both top-level elements need their namespaces bound, got: {}", + serialized + ); +} + +#[test] +fn test_serialize_html_method_applies_namespace_fixup() { + let mut xot = Xot::new(); + let output = evaluate( + &mut xot, + "", + r#" + + +"#, + ) + .unwrap(); + let mut parameters = xee_interpreter::sequence::SerializationParameters::new(); + parameters.method = xee_interpreter::sequence::QNameOrString::String("html".to_string()); + let serialized = output.serialize(parameters, &mut xot); + assert!( + serialized.is_ok(), + "html serialization must not fail on a constructed namespace: {:?}", + serialized + ); +}