Skip to content

Commit 542b3ea

Browse files
authored
feat(dom): Implement .text property for HTMLScriptElement (#390)
1 parent 7aebd10 commit 542b3ea

5 files changed

Lines changed: 153 additions & 13 deletions

File tree

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
<!DOCTYPE html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8">
5+
<title>Test HTMLScriptElement.text Getter/Setter</title>
6+
<style>
7+
body { font-family: sans-serif; }
8+
.result-pass { color: green; }
9+
.result-fail { color: red; }
10+
.result { margin-bottom: 4px; }
11+
pre { background: #f6f8fa; padding: 6px 10px; border-radius: 3px; }
12+
</style>
13+
</head>
14+
<body>
15+
<h2>Test <code>HTMLScriptElement.text</code> Getter/Setter</h2>
16+
<div id="results"></div>
17+
<pre id="details"></pre>
18+
<script id="script1" type="text/javascript">
19+
// Hello from script1
20+
var x = 42;
21+
</script>
22+
<script>
23+
function logResult(pass, message) {
24+
const div = document.createElement('div');
25+
div.className = 'result ' + (pass ? 'result-pass' : 'result-fail');
26+
div.textContent = (pass ? "PASS: " : "FAIL: ") + message;
27+
document.getElementById('results').appendChild(div);
28+
console.log((pass ? "PASS: " : "FAIL: ") + message);
29+
}
30+
function appendDetails(text) {
31+
document.getElementById('details').textContent += text + "\n";
32+
}
33+
34+
const s1 = document.getElementById('script1');
35+
appendDetails("Original .text: " + JSON.stringify(s1.text));
36+
appendDetails("Original .textContent: " + JSON.stringify(s1.textContent));
37+
logResult(
38+
typeof s1.text === "string" && s1.text.includes('var x = 42;'),
39+
"Getter: .text should contain script content"
40+
);
41+
42+
s1.text = "// Replaced!\nlet y = 99;";
43+
appendDetails("After set .text: " + JSON.stringify(s1.text));
44+
logResult(
45+
s1.text === "// Replaced!\nlet y = 99;",
46+
"Setter: .text should replace script content"
47+
);
48+
logResult(
49+
s1.textContent === s1.text,
50+
".textContent should equal .text after set"
51+
);
52+
53+
s1.text = "";
54+
appendDetails("After set .text to empty: " + JSON.stringify(s1.text));
55+
logResult(
56+
s1.text === "",
57+
"Setter: .text='' should clear script content"
58+
);
59+
logResult(
60+
s1.childNodes.length === 0,
61+
"Setter: .text='' should remove all child nodes"
62+
);
63+
64+
s1.text = "console.log('test again');";
65+
appendDetails("After set .text again: " + JSON.stringify(s1.text));
66+
logResult(
67+
s1.text === "console.log('test again');",
68+
"Setter: .text can be set multiple times"
69+
);
70+
71+
// Test multiple text nodes case
72+
const scriptMulti = document.createElement('script');
73+
const textNode1 = document.createTextNode('var a = 1;');
74+
const textNode2 = document.createTextNode('var b = 2;');
75+
const comment = document.createComment('this is a comment');
76+
77+
scriptMulti.appendChild(textNode1);
78+
scriptMulti.appendChild(comment);
79+
scriptMulti.appendChild(textNode2);
80+
81+
appendDetails("Multiple text nodes .text: " + JSON.stringify(scriptMulti.text));
82+
logResult(
83+
scriptMulti.text === 'var a = 1;var b = 2;',
84+
"Getter: .text should concatenate multiple text nodes, ignoring comments"
85+
);
86+
appendDetails("Multiple text nodes .textContent: " + JSON.stringify(scriptMulti.textContent));
87+
logResult(
88+
scriptMulti.textContent === 'var a = 1;var b = 2;',
89+
"Multiple text nodes: .textContent should equal .text"
90+
);
91+
92+
// Test no text nodes case
93+
const scriptEmpty = document.createElement('script');
94+
const spanElement = document.createElement('span');
95+
spanElement.textContent = 'nested content';
96+
const commentOnly = document.createComment('only comment');
97+
98+
scriptEmpty.appendChild(spanElement);
99+
scriptEmpty.appendChild(commentOnly);
100+
101+
appendDetails("No text nodes .text: " + JSON.stringify(scriptEmpty.text));
102+
logResult(
103+
scriptEmpty.text === '',
104+
"Getter: .text should return empty string when no direct text children"
105+
);
106+
appendDetails("No text nodes .textContent: " + JSON.stringify(scriptEmpty.textContent));
107+
logResult(
108+
scriptEmpty.textContent === 'nested content',
109+
"No text nodes: .textContent should include nested element content"
110+
);
111+
logResult(
112+
scriptEmpty.text !== scriptEmpty.textContent,
113+
"No text nodes: .text should differ from .textContent (spec compliance)"
114+
);
115+
116+
// Test single text node fast path
117+
const scriptSingle = document.createElement('script');
118+
const singleTextNode = document.createTextNode('single text content');
119+
scriptSingle.appendChild(singleTextNode);
120+
121+
appendDetails("Single text node .text: " + JSON.stringify(scriptSingle.text));
122+
logResult(
123+
scriptSingle.text === 'single text content',
124+
"Getter: .text should handle single text node (fast path)"
125+
);
126+
logResult(
127+
scriptSingle.text === scriptSingle.textContent,
128+
"Single text node: .text should equal .textContent"
129+
);
130+
</script>
131+
</body>
132+
</html>

src/client/html/html_script_element.hpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,14 @@ namespace endor
6565
src = value;
6666
setAttribute("src", value);
6767
}
68+
inline void setText(const string &value)
69+
{
70+
setTextContent(value);
71+
}
72+
inline string getText() const
73+
{
74+
return textContent();
75+
}
6876

6977
protected:
7078
void createdCallback(bool from_scripting) override;

src/client/script_bindings/events/all_events.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ namespace endor
2727
void Initialize(Isolate *isolate)
2828
{
2929
#define XX(T) T::Initialize(isolate);
30-
ALL_EVENT_CLASSES_MAP(XX)
30+
ALL_EVENT_CLASSES_MAP(XX);
3131
#undef XX
3232
}
3333

src/client/script_bindings/events/all_events.hpp

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,19 +17,19 @@ namespace endor
1717
namespace event_bindings
1818
{
1919
/**
20-
* Initialize all event bindings with V8.
21-
*
22-
* @param isolate The V8 isolate.
23-
*/
20+
* Initialize all event bindings with V8.
21+
*
22+
* @param isolate The V8 isolate.
23+
*/
2424
void Initialize(v8::Isolate *isolate);
2525

2626
/**
27-
* Create a new Event object in V8 from a native dom::Event.
28-
*
29-
* @param isolate The V8 isolate.
30-
* @param nativeEvent The native dom::Event to wrap.
31-
* @returns A V8 Object representing the event.
32-
*/
27+
* Create a new Event object in V8 from a native dom::Event.
28+
*
29+
* @param isolate The V8 isolate.
30+
* @param nativeEvent The native dom::Event to wrap.
31+
* @returns A V8 Object representing the event.
32+
*/
3333
v8::Local<v8::Object> MakeEvent(v8::Isolate *isolate, dom::Event *nativeEvent);
3434
}
3535
}

src/client/script_bindings/html/html_script_element.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ namespace endor
159159
Isolate *isolate = info.GetIsolate();
160160
HandleScope scope(isolate);
161161
info.GetReturnValue().Set(String::NewFromUtf8(isolate,
162-
handle()->text.c_str())
162+
handle()->getText().c_str())
163163
.ToLocalChecked());
164164
}
165165

@@ -176,7 +176,7 @@ namespace endor
176176
}
177177

178178
String::Utf8Value text(isolate, value);
179-
handle()->text = *text ? *text : "";
179+
handle()->setText(*text ? *text : "");
180180
}
181181

182182
void HTMLScriptElement::CharsetGetter(const PropertyCallbackInfo<Value> &info)

0 commit comments

Comments
 (0)