Skip to content

Commit 03f5bb6

Browse files
duonglaiquangrbri
authored andcommitted
HTMLAnchorElement: extract shared HTMLHyperlinkElementUtils for URL decomposition
1 parent e523809 commit 03f5bb6

2 files changed

Lines changed: 215 additions & 83 deletions

File tree

src/main/java/org/htmlunit/javascript/host/html/HTMLAnchorElement.java

Lines changed: 15 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -241,11 +241,7 @@ public void setReferrerPolicy(final String referrerPolicy) {
241241
@JsxGetter
242242
public String getSearch() {
243243
try {
244-
final String query = getUrl().getQuery();
245-
if (query == null) {
246-
return "";
247-
}
248-
return "?" + query;
244+
return HTMLHyperlinkElementUtils.getSearch(getUrl());
249245
}
250246
catch (final MalformedURLException e) {
251247
return "";
@@ -261,20 +257,7 @@ public String getSearch() {
261257
*/
262258
@JsxSetter
263259
public void setSearch(final String search) throws Exception {
264-
final String query;
265-
if (search == null
266-
|| StringUtils.isEmptyString(search)
267-
|| StringUtils.equalsChar('?', search)) {
268-
query = null;
269-
}
270-
else if (search.charAt(0) == '?') {
271-
query = search.substring(1);
272-
}
273-
else {
274-
query = search;
275-
}
276-
277-
setUrl(UrlUtils.getUrlWithNewQuery(getUrl(), query));
260+
setUrl(HTMLHyperlinkElementUtils.setSearch(getUrl(), search));
278261
}
279262

280263
/**
@@ -285,11 +268,7 @@ else if (search.charAt(0) == '?') {
285268
@JsxGetter
286269
public String getHash() {
287270
try {
288-
final String hash = getUrl().getRef();
289-
if (hash == null) {
290-
return "";
291-
}
292-
return "#" + hash;
271+
return HTMLHyperlinkElementUtils.getHash(getUrl());
293272
}
294273
catch (final MalformedURLException e) {
295274
return "";
@@ -304,7 +283,7 @@ public String getHash() {
304283
*/
305284
@JsxSetter
306285
public void setHash(final String hash) throws Exception {
307-
setUrl(UrlUtils.getUrlWithNewRef(getUrl(), hash));
286+
setUrl(HTMLHyperlinkElementUtils.setHash(getUrl(), hash));
308287
}
309288

310289
/**
@@ -319,7 +298,7 @@ public String getHost() {
319298
final int port = url.getPort();
320299
final String host = url.getHost();
321300

322-
if (port == -1 || isDefaultPort(url.getProtocol(), port)) {
301+
if (port == -1 || HTMLHyperlinkElementUtils.isDefaultPort(url.getProtocol(), port)) {
323302
return host;
324303
}
325304
return host + ":" + port;
@@ -337,19 +316,7 @@ public String getHost() {
337316
*/
338317
@JsxSetter
339318
public void setHost(final String host) throws Exception {
340-
final String hostname;
341-
final int port;
342-
final int index = host.indexOf(':');
343-
if (index != -1) {
344-
hostname = host.substring(0, index);
345-
port = Integer.parseInt(host.substring(index + 1));
346-
}
347-
else {
348-
hostname = host;
349-
port = -1;
350-
}
351-
final URL url = UrlUtils.getUrlWithNewHostAndPort(getUrl(), hostname, port);
352-
setUrl(url);
319+
setUrl(HTMLHyperlinkElementUtils.setHost(getUrl(), host));
353320
}
354321

355322
/**
@@ -360,7 +327,7 @@ public void setHost(final String host) throws Exception {
360327
@JsxGetter
361328
public String getHostname() {
362329
try {
363-
return UrlUtils.encodeAnchor(getUrl().getHost());
330+
return HTMLHyperlinkElementUtils.getHostname(getUrl());
364331
}
365332
catch (final MalformedURLException e) {
366333
return "";
@@ -426,7 +393,7 @@ public String getPathname() {
426393
*/
427394
@JsxSetter
428395
public void setPathname(final String pathname) throws Exception {
429-
setUrl(UrlUtils.getUrlWithNewPath(getUrl(), pathname));
396+
setUrl(HTMLHyperlinkElementUtils.setPathname(getUrl(), pathname));
430397
}
431398

432399
/**
@@ -439,7 +406,7 @@ public String getPort() {
439406
try {
440407
final URL url = getUrl();
441408
final int port = url.getPort();
442-
if (port == -1 || isDefaultPort(url.getProtocol(), port)) {
409+
if (port == -1 || HTMLHyperlinkElementUtils.isDefaultPort(url.getProtocol(), port)) {
443410
return "";
444411
}
445412
return Integer.toString(port);
@@ -459,7 +426,7 @@ public String getPort() {
459426
public void setPort(final String port) throws Exception {
460427
final URL url = getUrl();
461428
final int newPort = Integer.parseInt(port);
462-
if (isDefaultPort(url.getProtocol(), newPort)) {
429+
if (HTMLHyperlinkElementUtils.isDefaultPort(url.getProtocol(), newPort)) {
463430
setUrl(UrlUtils.getUrlWithNewPort(url, -1));
464431
}
465432
else {
@@ -503,25 +470,9 @@ public String getProtocol() {
503470
*/
504471
@JsxSetter
505472
public void setProtocol(final String protocol) throws Exception {
506-
if (protocol.isEmpty()) {
507-
return;
508-
}
509-
510-
final String bareProtocol = StringUtils.substringBefore(protocol, ":").trim();
511-
if (!UrlUtils.isValidScheme(bareProtocol)) {
512-
return;
513-
}
514-
if (!UrlUtils.isSpecialScheme(bareProtocol)) {
515-
return;
516-
}
517-
518-
try {
519-
URL url = UrlUtils.getUrlWithNewProtocol(getUrl(), bareProtocol);
520-
url = UrlUtils.removeRedundantPort(url);
521-
setUrl(url);
522-
}
523-
catch (final MalformedURLException ignored) {
524-
// ignore
473+
final URL result = HTMLHyperlinkElementUtils.setProtocol(getUrl(), protocol);
474+
if (result != null) {
475+
setUrl(result);
525476
}
526477
}
527478

@@ -661,11 +612,7 @@ public String getOrigin() {
661612
@JsxGetter
662613
public String getUsername() {
663614
try {
664-
final String userInfo = getUrl().getUserInfo();
665-
if (userInfo == null) {
666-
return "";
667-
}
668-
return org.apache.commons.lang3.StringUtils.substringBefore(userInfo, ':');
615+
return HTMLHyperlinkElementUtils.getUsername(getUrl());
669616
}
670617
catch (final MalformedURLException e) {
671618
return "";
@@ -700,11 +647,7 @@ public void setUsername(final String username) {
700647
@JsxGetter
701648
public String getPassword() {
702649
try {
703-
final String userName = getUrl().getUserInfo();
704-
if (userName == null) {
705-
return "";
706-
}
707-
return StringUtils.substringAfter(userName, ":");
650+
return HTMLHyperlinkElementUtils.getPassword(getUrl());
708651
}
709652
catch (final MalformedURLException e) {
710653
return "";
@@ -821,15 +764,4 @@ public DOMTokenList getRelList() {
821764
public void setRelList(final Object rel) {
822765
setRel(JavaScriptEngine.toString(rel));
823766
}
824-
825-
/**
826-
* Checks whether the given port is the default port for the protocol.
827-
* @param protocol the protocol (e.g. {@code "http"}, {@code "https"})
828-
* @param port the port number
829-
* @return {@code true} if the port is the default for the protocol
830-
*/
831-
private static boolean isDefaultPort(final String protocol, final int port) {
832-
return ("http".equals(protocol) && port == 80)
833-
|| ("https".equals(protocol) && port == 443);
834-
}
835767
}
Lines changed: 200 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
/*
2+
* Copyright (c) 2002-2026 Gargoyle Software Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* https://www.apache.org/licenses/LICENSE-2.0
8+
*
9+
* Unless required by applicable law or agreed to in writing, software
10+
* distributed under the License is distributed on an "AS IS" BASIS,
11+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
* See the License for the specific language governing permissions and
13+
* limitations under the License.
14+
*/
15+
package org.htmlunit.javascript.host.html;
16+
17+
import java.net.MalformedURLException;
18+
import java.net.URL;
19+
20+
import org.htmlunit.util.StringUtils;
21+
import org.htmlunit.util.UrlUtils;
22+
23+
/**
24+
* Implementation of the {@code HTMLHyperlinkElementUtils} mixin.
25+
* Provides URL decomposition property logic
26+
* @see <a href="https://html.spec.whatwg.org/multipage/links.html#htmlhyperlinkelementutils">
27+
* HTMLHyperlinkElementUtils</a>
28+
*/
29+
final class HTMLHyperlinkElementUtils {
30+
31+
/**
32+
* Returns the {@code search} component of the URL.
33+
* @param url the URL
34+
* @return the query string prefixed with {@code ?}, or empty string if no query
35+
*/
36+
static String getSearch(final URL url) {
37+
final String query = url.getQuery();
38+
if (query == null) {
39+
return "";
40+
}
41+
return "?" + query;
42+
}
43+
44+
/**
45+
* Returns a new URL with the {@code search} component set.
46+
* @param url the current URL
47+
* @param search the new search value (with or without leading {@code ?})
48+
* @return the new URL
49+
* @throws MalformedURLException if an error occurs
50+
*/
51+
static URL setSearch(final URL url, final String search) throws MalformedURLException {
52+
final String query;
53+
if (search == null
54+
|| StringUtils.isEmptyString(search)
55+
|| StringUtils.equalsChar('?', search)) {
56+
query = null;
57+
}
58+
else if (search.charAt(0) == '?') {
59+
query = search.substring(1);
60+
}
61+
else {
62+
query = search;
63+
}
64+
return UrlUtils.getUrlWithNewQuery(url, query);
65+
}
66+
67+
/**
68+
* Returns the {@code hash} component of the URL.
69+
* @param url the URL
70+
* @return the fragment prefixed with {@code #}, or empty string if no fragment
71+
*/
72+
static String getHash(final URL url) {
73+
final String hash = url.getRef();
74+
if (hash == null) {
75+
return "";
76+
}
77+
return "#" + hash;
78+
}
79+
80+
/**
81+
* Returns a new URL with the {@code hash} component set.
82+
* @param url the current URL
83+
* @param hash the new hash value
84+
* @return the new URL
85+
* @throws MalformedURLException if an error occurs
86+
*/
87+
static URL setHash(final URL url, final String hash) throws MalformedURLException {
88+
return UrlUtils.getUrlWithNewRef(url, hash);
89+
}
90+
91+
/**
92+
* Returns the {@code hostname} component of the URL.
93+
* @param url the URL
94+
* @return the hostname
95+
*/
96+
static String getHostname(final URL url) {
97+
return UrlUtils.encodeAnchor(url.getHost());
98+
}
99+
100+
/**
101+
* Returns a new URL with the {@code host} component set.
102+
* Parses the host string for an optional {@code :port} suffix.
103+
* @param url the current URL
104+
* @param host the new host value (e.g. {@code "example.com:8080"})
105+
* @return the new URL
106+
* @throws MalformedURLException if an error occurs
107+
*/
108+
static URL setHost(final URL url, final String host) throws MalformedURLException {
109+
final String hostname;
110+
final int port;
111+
final int index = host.indexOf(':');
112+
if (index != -1) {
113+
hostname = host.substring(0, index);
114+
port = Integer.parseInt(host.substring(index + 1));
115+
}
116+
else {
117+
hostname = host;
118+
port = -1;
119+
}
120+
return UrlUtils.getUrlWithNewHostAndPort(url, hostname, port);
121+
}
122+
123+
/**
124+
* Returns a new URL with the {@code protocol} set, or {@code null}
125+
* if the protocol is invalid or should not be applied.
126+
* @param url the current URL
127+
* @param protocol the new protocol value (with or without trailing {@code :})
128+
* @return the new URL, or {@code null} if the protocol is invalid
129+
*/
130+
static URL setProtocol(final URL url, final String protocol) {
131+
if (protocol.isEmpty()) {
132+
return null;
133+
}
134+
135+
final String bareProtocol = StringUtils.substringBefore(protocol, ":").trim();
136+
if (!UrlUtils.isValidScheme(bareProtocol)) {
137+
return null;
138+
}
139+
if (!UrlUtils.isSpecialScheme(bareProtocol)) {
140+
return null;
141+
}
142+
143+
try {
144+
URL result = UrlUtils.getUrlWithNewProtocol(url, bareProtocol);
145+
result = UrlUtils.removeRedundantPort(result);
146+
return result;
147+
}
148+
catch (final MalformedURLException ignored) {
149+
return null;
150+
}
151+
}
152+
153+
/**
154+
* Returns a new URL with the {@code pathname} set.
155+
* @param url the current URL
156+
* @param pathname the new pathname value
157+
* @return the new URL
158+
* @throws MalformedURLException if an error occurs
159+
*/
160+
static URL setPathname(final URL url, final String pathname) throws MalformedURLException {
161+
return UrlUtils.getUrlWithNewPath(url, pathname);
162+
}
163+
164+
/**
165+
* Returns the {@code username} component of the URL.
166+
* @param url the URL
167+
* @return the username, or empty string if no user info
168+
*/
169+
static String getUsername(final URL url) {
170+
final String userInfo = url.getUserInfo();
171+
if (userInfo == null) {
172+
return "";
173+
}
174+
return StringUtils.substringBefore(userInfo, ":");
175+
}
176+
177+
/**
178+
* Returns the {@code password} component of the URL.
179+
* @param url the URL
180+
* @return the password, or empty string if no user info
181+
*/
182+
static String getPassword(final URL url) {
183+
final String userInfo = url.getUserInfo();
184+
if (userInfo == null) {
185+
return "";
186+
}
187+
return StringUtils.substringAfter(userInfo, ":");
188+
}
189+
190+
/**
191+
* Checks whether the given port is the default port for the protocol.
192+
* @param protocol the protocol (e.g. {@code "http"}, {@code "https"})
193+
* @param port the port number
194+
* @return {@code true} if the port is the default for the protocol
195+
*/
196+
static boolean isDefaultPort(final String protocol, final int port) {
197+
return ("http".equals(protocol) && port == 80)
198+
|| ("https".equals(protocol) && port == 443);
199+
}
200+
}

0 commit comments

Comments
 (0)