Fix/rescue 227 regressions (#232)

* fix: restore regressions introduced by #227

#227 deleted build.rs (reverting #224's version-from-tag feature),
removed DOM cycle guards in append_child/insert_before that prevent
infinite loops on self-append/self-insert, dropped the CharacterData
textContent fast-path, removed PI/Doctype Rust ops, and cut the
env_allows_private_network() check from validate_fetch_url.

Restored:
- crates/obscura-cli/build.rs (version from tag/env)
- OBSCURA_BUILD_VERSION in version string and banner
- allow_private_network env var propagation in main.rs
- append_child/insert_before self-cycle guards in tree.rs
- textContent CharacterData fast-path in tree.rs
- create_processing_instruction, create_doctype, pi_target,
  doctype_name, doctype_public_id ops in ops.rs
- ProcessingInstruction branch in set_text_content op
- env_allows_private_network() check in validate_fetch_url

* fix(js): Europe/Berlin timezone, plugins iterator recursion

Two bugs in the stealth bootstrap code from #227:
- 'Europe/Germany' is not a valid IANA timezone; Intl.DateTimeFormat
  silently fell back to UTC. Changed to 'Europe/Berlin'.
- plugins[Symbol.iterator] called this[Symbol.iterator]() recursively,
  blowing the stack on Array.from(navigator.plugins). Fixed to bind
  Array.prototype[Symbol.iterator] directly to the array.
This commit is contained in:
SGavrl authored and GitHub committed 2026-06-03 18:16:20 +00:00
1 parent 990f2a63c8
commit 6b3cc1a686
5 files changed
+130 -6

No files matched your search

+35
View File
@@ -223,6 +223,14 @@ impl DomTree {
}
pub fn append_child(&self, parent_id: NodeId, child_id: NodeId) {
// Per DOM spec, appending a node to itself is a HierarchyRequestError;
// here we treat it as a no-op rather than panic. Without this the
// sibling-pointer fixup below sets the node's prev_sibling to itself
// and every later child-walk loops forever (same failure mode that
// insert_before's self-cycle guard was added to prevent).
if parent_id == child_id {
return;
}
self.detach(child_id);
let mut inner = self.inner.borrow_mut();
@@ -252,6 +260,15 @@ impl DomTree {
}
pub fn insert_before(&self, existing_id: NodeId, new_sibling_id: NodeId) {
// Per DOM spec: if the node being inserted IS the reference node,
// the operation is a no-op (the node is already in its target
// position). Without this, the linked-list fixup below sets the
// node's prev_sibling and next_sibling to itself, creating a cycle
// -- every later traversal (childNodes, querySelectorAll, etc) then
// loops forever and the test page hangs while obscura burns RAM.
if existing_id == new_sibling_id {
return;
}
let (parent_id, prev_id) = {
let inner = self.inner.borrow();
let node = match inner.nodes.get(existing_id.index()).and_then(|n| n.as_ref()) {
@@ -463,6 +480,20 @@ impl DomTree {
pub fn text_content(&self, node_id: NodeId) -> String {
let inner = self.inner.borrow();
// Per DOM spec, calling textContent ON a CharacterData node
// (Text, Comment, ProcessingInstruction) returns its .data.
// Calling textContent on an Element walks descendants and
// concatenates Text node content only (Comment + PI are
// skipped). Handle the direct-CharacterData case here so the
// descent helper can keep its element-centric behavior.
if let Some(Some(node)) = inner.nodes.get(node_id.index()) {
match &node.data {
NodeData::Text { contents } => return contents.clone(),
NodeData::Comment { contents } => return contents.clone(),
NodeData::ProcessingInstruction { data, .. } => return data.clone(),
_ => {}
}
}
let mut result = String::new();
collect_text_inner(&inner, node_id, &mut result);
result
@@ -570,6 +601,10 @@ fn collect_text_inner(inner: &DomTreeInner, node_id: NodeId, buf: &mut String) {
if let Some(Some(node)) = inner.nodes.get(node_id.index()) {
match &node.data {
NodeData::Text { contents } => buf.push_str(contents),
// Comment and ProcessingInstruction are intentionally NOT
// appended when traversing descendants: per spec, textContent
// on an Element only includes Text descendants. Direct
// textContent on a Comment/PI is handled by the caller.
_ => {
let mut child = node.first_child;
while let Some(child_id) = child {