From 1fc7814bee5d90f83bab428c8167af43201f8a9b Mon Sep 17 00:00:00 2001
From: Leyman <82012937+lmaxyz@users.noreply.github.com>
Date: Tue, 23 Apr 2024 20:14:40 +0300
Subject: [PATCH] Headerless xml parsing fix (#715)

* Add tests for parsing

* Fix headerless xml parsing

* Add comment

* Enable some protocols disabled before

* Remove useless body from tested xml file

* Fix code formatting

* Add changelogs

---------

Co-authored-by: Leyman Max <jiumoh2011@yandex.ru>
---
 wayland-protocols-plasma/CHANGELOG.md         |  2 +
 wayland-protocols-plasma/src/lib.rs           | 28 +++++-----
 wayland-scanner/CHANGELOG.md                  |  1 +
 wayland-scanner/src/parse.rs                  | 53 +++++++++++++++----
 .../test-headerless-protocol.xml              |  2 +
 5 files changed, 59 insertions(+), 27 deletions(-)
 create mode 100644 wayland-scanner/tests/scanner_assets/test-headerless-protocol.xml

diff --git a/wayland-protocols-plasma/CHANGELOG.md b/wayland-protocols-plasma/CHANGELOG.md
index 8187ea5d518..b086c493c1c 100644
--- a/wayland-protocols-plasma/CHANGELOG.md
+++ b/wayland-protocols-plasma/CHANGELOG.md
@@ -2,6 +2,8 @@
 
 ## Unreleased
 
+- Make available protocols that based on headerless xml files.
+
 ## 0.2.0 -- 2023-09-02
 
 ### Breaking changes
diff --git a/wayland-protocols-plasma/src/lib.rs b/wayland-protocols-plasma/src/lib.rs
index 52823ec5ecc..1553f62268c 100644
--- a/wayland-protocols-plasma/src/lib.rs
+++ b/wayland-protocols-plasma/src/lib.rs
@@ -49,14 +49,12 @@ pub mod fake_input {
     );
 }
 
-// This protocol is disabled for now as the file is not valid XML because it does not have a XML header
-//
-// pub mod fullscreen_shell {
-//     wayland_protocol!(
-//         "./plasma-wayland-protocols/src/protocols/fullscreen-shell.xml",
-//         []
-//     );
-// }
+pub mod fullscreen_shell {
+    wayland_protocol!(
+        "./plasma-wayland-protocols/src/protocols/fullscreen-shell.xml",
+        []
+    );
+}
 
 pub mod idle {
     wayland_protocol!(
@@ -179,14 +177,12 @@ pub mod slide {
     );
 }
 
-// This protocol is disabled for now as the file is not valid XML because it does not have a XML header
-//
-// pub mod surface_extension {
-//     wayland_protocol!(
-//         "./plasma-wayland-protocols/src/protocols/surface-extension.xml",
-//         []
-//     );
-// }
+pub mod surface_extension {
+    wayland_protocol!(
+        "./plasma-wayland-protocols/src/protocols/surface-extension.xml",
+        []
+    );
+}
 
 pub mod text_input {
     pub mod v1 {
diff --git a/wayland-scanner/CHANGELOG.md b/wayland-scanner/CHANGELOG.md
index 1b8ea54b10d..bb378accb7f 100644
--- a/wayland-scanner/CHANGELOG.md
+++ b/wayland-scanner/CHANGELOG.md
@@ -3,6 +3,7 @@
 ## Unreleased
 
 - Use wrapper type implementing `Sync` instead of `static mut`s.
+- Add headerless xml file parsing possibility for `parse` function.
 
 ## 0.31.1 -- 2024-01-29
 
diff --git a/wayland-scanner/src/parse.rs b/wayland-scanner/src/parse.rs
index b07c6531ccf..73aafde625c 100644
--- a/wayland-scanner/src/parse.rs
+++ b/wayland-scanner/src/parse.rs
@@ -29,8 +29,6 @@ macro_rules! extract_end_tag(
 pub fn parse<S: Read>(stream: S) -> Protocol {
     let mut reader = Reader::from_reader(BufReader::new(stream));
     reader.trim_text(true).expand_empty_elements(true);
-    // Skip first <?xml ... ?> event
-    let _ = reader.read_event_into(&mut Vec::new());
     parse_protocol(reader)
 }
 
@@ -52,17 +50,33 @@ fn parse_or_panic<T: FromStr>(txt: &[u8]) -> T {
     }
 }
 
-fn parse_protocol<R: BufRead>(mut reader: Reader<R>) -> Protocol {
-    let mut protocol = extract_from!(
-        reader => Event::Start(bytes) => {
-            assert!(bytes.name().into_inner() == b"protocol", "Missing protocol toplevel tag");
-            if let Some(attr) = bytes.attributes().filter_map(|res| res.ok()).find(|attr| attr.key.into_inner() == b"name") {
-                Protocol::new(decode_utf8_or_panic(attr.value.into_owned()))
-            } else {
-                panic!("Protocol must have a name");
+fn init_protocol<R: BufRead>(reader: &mut Reader<R>) -> Protocol {
+    // Check two firsts lines for protocol tag
+    for _ in 0..2 {
+        match reader.read_event_into(&mut Vec::new()) {
+            Ok(Event::Decl(_)) => {
+                continue;
+            }
+            Ok(Event::Start(bytes)) => {
+                assert!(bytes.name().into_inner() == b"protocol", "Missing protocol toplevel tag");
+                if let Some(attr) = bytes
+                    .attributes()
+                    .filter_map(|res| res.ok())
+                    .find(|attr| attr.key.into_inner() == b"name")
+                {
+                    return Protocol::new(decode_utf8_or_panic(attr.value.into_owned()));
+                } else {
+                    panic!("Protocol must have a name");
+                }
             }
+            _ => panic!("Ill-formed protocol file"),
         }
-    );
+    }
+    panic!("Ill-formed protocol file");
+}
+
+fn parse_protocol<R: BufRead>(mut reader: Reader<R>) -> Protocol {
+    let mut protocol = init_protocol(&mut reader);
 
     loop {
         match reader.read_event_into(&mut Vec::new()) {
@@ -366,3 +380,20 @@ fn parse_entry<R: BufRead>(reader: &mut Reader<R>, attrs: Attributes) -> Entry {
 
     entry
 }
+
+#[cfg(test)]
+mod tests {
+    #[test]
+    fn xml_parse() {
+        let protocol_file =
+            std::fs::File::open("./tests/scanner_assets/test-protocol.xml").unwrap();
+        let _ = crate::parse::parse(protocol_file);
+    }
+
+    #[test]
+    fn headerless_xml_parse() {
+        let protocol_file =
+            std::fs::File::open("./tests/scanner_assets/test-headerless-protocol.xml").unwrap();
+        let _ = crate::parse::parse(protocol_file);
+    }
+}
diff --git a/wayland-scanner/tests/scanner_assets/test-headerless-protocol.xml b/wayland-scanner/tests/scanner_assets/test-headerless-protocol.xml
new file mode 100644
index 00000000000..824ebc6e32d
--- /dev/null
+++ b/wayland-scanner/tests/scanner_assets/test-headerless-protocol.xml
@@ -0,0 +1,2 @@
+<protocol name="test-protocol">
+</protocol>
\ No newline at end of file