Skip to main content

atmos/os_lib/
dom_fragment.rs

1//! W3C DOM Parsing and Serialization: createContextualFragment 純粋処理モジュール
2//!
3//! W3C DOM Parsing and Serialization Standard §4.1 に準拠し、
4//! コンテキスト要素に応じた HTML 断片のラッピング構文補正と抽出ロジックを提供する。
5
6use alloc::string::String;
7use alloc::format;
8use crate::os_lib::dom::NodeType;
9
10/// コンテキスト要素のタグ名に応じたラッピングと抽出深さを決定する。
11///
12/// HTML パーサはコンテキストなしに `<tr>` や `<td>` などの表要素、
13/// `<option>` などのセレクト要素を渡されると、不正な構文として除去または
14/// テキスト化してしまう。W3C 仕様に基づき、親タグで一時的に包んでパースし、
15/// パース後に包んだ分だけ掘り下げて目的の子ノード群を抽出する。
16pub fn context_wrapper(context_tag: &str, html: &str) -> (String, usize) {
17    let lower = context_tag.trim().to_ascii_lowercase();
18    match lower.as_str() {
19        "table" => (format!("<table>{}</table>", html), 1),
20        "tbody" | "thead" | "tfoot" => (format!("<table><tbody>{}</tbody></table>", html), 2),
21        "tr" => (format!("<table><tbody><tr>{}</tr></tbody></table>", html), 3),
22        "colgroup" => (format!("<table><colgroup>{}</colgroup></table>", html), 2),
23        "select" | "optgroup" => (format!("<select>{}</select>", html), 1),
24        _ => (format!("<div>{}</div>", html), 1),
25    }
26}
27
28/// パースされたルートノードから、指定された深さ `depth` のコンテナ要素の子ノード一覧を抽出する。
29pub fn extract_fragment_children<'a>(
30    root: &'a crate::os_lib::dom::Node,
31    depth: usize,
32) -> &'a [crate::os_lib::dom::Node] {
33    let mut current = root;
34    for _ in 0..depth {
35        if let Some(first) = current.children.first() {
36            current = first;
37        } else {
38            return &root.children;
39        }
40    }
41    // HTML5 の自動補完: <table> 直下に <tr> がパースされた場合、自動的に <tbody> が
42    // 挟まれるため、current が <table> かつ子が <tbody> 1つの場合はその <tbody> の子要素群を返す。
43    if let NodeType::Element { tag_name, .. } = &current.node_type {
44        if tag_name == "table" && current.children.len() == 1 {
45            if let Some(first) = current.children.first() {
46                if let NodeType::Element { tag_name: sub_tag, .. } = &first.node_type {
47                    if sub_tag == "tbody" {
48                        return &first.children;
49                    }
50                }
51            }
52        }
53    }
54    &current.children
55}