Skip to content

Commit 46dd40a

Browse files
committed
DomStringMap fixes
1 parent 31bbd94 commit 46dd40a

3 files changed

Lines changed: 130 additions & 49 deletions

File tree

src/main/java/org/htmlunit/javascript/host/dom/DOMStringMap.java

Lines changed: 47 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,11 @@
1717
import static org.htmlunit.html.DomElement.ATTRIBUTE_NOT_DEFINED;
1818

1919
import org.htmlunit.corejs.javascript.Scriptable;
20-
import org.htmlunit.corejs.javascript.ScriptableObject;
2120
import org.htmlunit.html.HtmlElement;
2221
import org.htmlunit.javascript.HtmlUnitScriptable;
2322
import org.htmlunit.javascript.JavaScriptEngine;
2423
import org.htmlunit.javascript.configuration.JsxClass;
2524
import org.htmlunit.javascript.configuration.JsxConstructor;
26-
import org.htmlunit.javascript.host.Window;
27-
import org.htmlunit.util.StringUtils;
2825

2926
/**
3027
* A JavaScript object for {@code DOMStringMap}.
@@ -35,6 +32,8 @@
3532
@JsxClass
3633
public final class DOMStringMap extends HtmlUnitScriptable {
3734

35+
private static final String DATA_PREFIX = "data-";
36+
3837
/**
3938
* Creates an instance.
4039
*/
@@ -68,27 +67,66 @@ public DOMStringMap(final Node node) {
6867
public Object get(final String name, final Scriptable start) {
6968
final HtmlElement e = (HtmlElement) getDomNodeOrNull();
7069
if (e != null) {
71-
final String value = e.getAttribute("data-" + StringUtils.cssDeCamelize(name));
70+
final String value = e.getAttribute(DATA_PREFIX + deCamelize(name));
7271
if (ATTRIBUTE_NOT_DEFINED != value) {
7372
return value;
7473
}
7574
}
76-
return NOT_FOUND;
75+
return super.get(name, start);
7776
}
7877

7978
/**
8079
* {@inheritDoc}
8180
*/
8281
@Override
8382
public void put(final String name, final Scriptable start, final Object value) {
84-
if (!(ScriptableObject.getTopLevelScope(this) instanceof Window) || getWindow().getWebWindow() == null) {
83+
final HtmlElement e = (HtmlElement) getDomNodeOrNull();
84+
if (e == null) {
8585
super.put(name, start, value);
8686
}
8787
else {
88-
final HtmlElement e = (HtmlElement) getDomNodeOrNull();
89-
if (e != null) {
90-
e.setAttribute("data-" + StringUtils.cssDeCamelize(name), JavaScriptEngine.toString(value));
88+
e.setAttribute("data-" + deCamelize(name), JavaScriptEngine.toString(value));
89+
}
90+
}
91+
92+
/**
93+
* {@inheritDoc}
94+
*/
95+
@Override
96+
public void delete(final String name) {
97+
final HtmlElement e = (HtmlElement) getDomNodeOrNull();
98+
if (e == null) {
99+
super.delete(name);
100+
}
101+
else {
102+
e.removeAttribute("data-" + deCamelize(name));
103+
}
104+
}
105+
106+
/**
107+
* Transforms the specified string from camel-cased to dash separated.
108+
*
109+
* @param string the string to decamelize
110+
* @return the transformed string
111+
* @see <a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/dataset#name_conversion">
112+
* MDN - HTMLElement.dataset - Name conversion</a>
113+
*/
114+
private static String deCamelize(final String string) {
115+
if (string == null || string.isEmpty()) {
116+
return string;
117+
}
118+
119+
final StringBuilder builder = new StringBuilder();
120+
for (int i = 0; i < string.length(); i++) {
121+
final char ch = string.charAt(i);
122+
if (Character.isUpperCase(ch)) {
123+
builder.append('-').append(Character.toLowerCase(ch));
124+
}
125+
else {
126+
builder.append(ch);
91127
}
92128
}
129+
return builder.toString();
93130
}
131+
94132
}

src/main/java/org/htmlunit/util/StringUtils.java

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -754,31 +754,6 @@ public static String toRootLowerCase(final String s) {
754754
return s == null ? null : s.toLowerCase(Locale.ROOT);
755755
}
756756

757-
/**
758-
* Transforms the specified string from camel-cased (e.g. <code>fontSize</code>)
759-
* to delimiter-separated (e.g. <code>font-size</code>).
760-
* to camel-cased .
761-
* @param string the string to decamelize
762-
* @return the transformed string
763-
*/
764-
public static String cssDeCamelize(final String string) {
765-
if (string == null || string.isEmpty()) {
766-
return string;
767-
}
768-
769-
final StringBuilder builder = new StringBuilder();
770-
for (int i = 0; i < string.length(); i++) {
771-
final char ch = string.charAt(i);
772-
if (Character.isUpperCase(ch)) {
773-
builder.append('-').append(Character.toLowerCase(ch));
774-
}
775-
else {
776-
builder.append(ch);
777-
}
778-
}
779-
return builder.toString();
780-
}
781-
782757
/**
783758
* Converts a string into a byte array using the specified encoding.
784759
*

src/test/java/org/htmlunit/javascript/host/dom/DOMStringMapTest.java

Lines changed: 83 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import org.htmlunit.WebDriverTestCase;
1818
import org.htmlunit.junit.annotation.Alerts;
19+
import org.htmlunit.junit.annotation.HtmlUnitNYI;
1920
import org.junit.jupiter.api.Test;
2021

2122
/**
@@ -31,19 +32,57 @@ public class DOMStringMapTest extends WebDriverTestCase {
3132
* @throws Exception if the test fails
3233
*/
3334
@Test
34-
@Alerts({"undefined", "there"})
35+
@Alerts({"undefined", "there", "heho"})
3536
public void get() throws Exception {
3637
final String html = DOCTYPE_HTML
3738
+ "<html><head>\n"
3839
+ "<script>\n"
3940
+ LOG_TITLE_FUNCTION
4041
+ "function test() {\n"
41-
+ " if (document.body.dataset) {\n"
42-
+ " log(document.body.dataset.hi);\n"
43-
+ " log(document.body.dataset.hello);\n"
44-
+ " }\n"
42+
+ " log(document.body.dataset.hi);\n"
43+
+ " log(document.body.dataset.hello);\n"
44+
+ " log(document.body.dataset.helloWorld);\n"
4545
+ "}\n"
46-
+ "</script></head><body onload='test()' data-hello='there'>\n"
46+
+ "</script></head>\n"
47+
+ "<body onload='test()' data-hello='there' data-hello-world='heho'>\n"
48+
+ "</body></html>";
49+
50+
loadPageVerifyTitle2(html);
51+
}
52+
53+
/**
54+
* @throws Exception if the test fails
55+
*/
56+
@Test
57+
@Alerts({"heho", "ReferenceError", "world is not defined",
58+
"ReferenceError", "World is not defined"})
59+
@HtmlUnitNYI(
60+
CHROME = {"heho", "ReferenceError", "\"world\" is not defined",
61+
"ReferenceError", "\"World\" is not defined"},
62+
EDGE = {"heho", "ReferenceError", "\"world\" is not defined",
63+
"ReferenceError", "\"World\" is not defined"},
64+
FF = {"heho", "ReferenceError", "\"world\" is not defined",
65+
"ReferenceError", "\"World\" is not defined"},
66+
FF_ESR = {"heho", "ReferenceError", "\"world\" is not defined",
67+
"ReferenceError", "\"World\" is not defined"})
68+
public void getInvalidName() throws Exception {
69+
final String html = DOCTYPE_HTML
70+
+ "<html><head>\n"
71+
+ "<script>\n"
72+
+ LOG_TITLE_FUNCTION
73+
+ "function test() {\n"
74+
+ " log(document.body.dataset.helloWorld);\n"
75+
76+
+ " try {\n"
77+
+ " log(document.body.dataset.hello-world);\n"
78+
+ " } catch(e) { logEx(e); log(e.message);}\n"
79+
80+
+ " try {\n"
81+
+ " log(document.body.dataset.hello-World);\n"
82+
+ " } catch(e) { logEx(e); log(e.message);}\n"
83+
+ "}\n"
84+
+ "</script></head>\n"
85+
+ "<body onload='test()' data-hello='hi' data-world='huhu' data-hello-world='heho'>\n"
4786
+ "</body></html>";
4887

4988
loadPageVerifyTitle2(html);
@@ -60,16 +99,45 @@ public void put() throws Exception {
6099
+ "<script>\n"
61100
+ LOG_TITLE_FUNCTION
62101
+ "function test() {\n"
63-
+ " if (document.body.dataset) {\n"
64-
+ " document.body.dataset.dateOfBirth = 'old';\n"
65-
+ " log(document.body.dataset.dateOfBirth);\n"
66-
+ " log(document.body.getAttribute('data-date-of-birth'));\n"
67-
+ " document.body.dataset.dateOfBirth = null;\n"
68-
+ " log(document.body.dataset.dateOfBirth);\n"
69-
+ " log(document.body.getAttribute('data-date-of-birth'));\n"
70-
+ " }\n"
102+
+ " document.body.dataset.dateOfBirth = 'old';\n"
103+
+ " log(document.body.dataset.dateOfBirth);\n"
104+
+ " log(document.body.getAttribute('data-date-of-birth'));\n"
105+
106+
+ " document.body.dataset.dateOfBirth = null;\n"
107+
+ " log(document.body.dataset.dateOfBirth);\n"
108+
+ " log(document.body.getAttribute('data-date-of-birth'));\n"
109+
+ "}\n"
110+
+ "</script></head>\n"
111+
+ "<body onload='test()'>\n"
112+
+ "</body></html>";
113+
114+
loadPageVerifyTitle2(html);
115+
}
116+
/**
117+
* @throws Exception if the test fails
118+
*/
119+
@Test
120+
@Alerts({"undefined", "undefined", "there", "undefined", "heho", "undefined"})
121+
public void delete() throws Exception {
122+
final String html = DOCTYPE_HTML
123+
+ "<html><head>\n"
124+
+ "<script>\n"
125+
+ LOG_TITLE_FUNCTION
126+
+ "function test() {\n"
127+
+ " log(document.body.dataset.hi);\n"
128+
+ " delete document.body.dataset.hi;\n"
129+
+ " log(document.body.dataset.hi);\n"
130+
131+
+ " log(document.body.dataset.hello);\n"
132+
+ " delete document.body.dataset.hello;\n"
133+
+ " log(document.body.dataset.hello);\n"
134+
135+
+ " log(document.body.dataset.helloWorld);\n"
136+
+ " delete document.body.dataset.helloWorld;\n"
137+
+ " log(document.body.dataset.helloWorld);\n"
71138
+ "}\n"
72-
+ "</script></head><body onload='test()'>\n"
139+
+ "</script></head>\n"
140+
+ "<body onload='test()' data-hello='there' data-hello-world='heho'>\n"
73141
+ "</body></html>";
74142

75143
loadPageVerifyTitle2(html);

0 commit comments

Comments
 (0)