1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.javascript.host.dom;
16
17 import java.io.Serializable;
18 import java.util.ArrayList;
19 import java.util.HashMap;
20 import java.util.List;
21 import java.util.Map;
22 import java.util.Objects;
23 import java.util.function.Supplier;
24
25 import org.htmlunit.SgmlPage;
26 import org.htmlunit.corejs.javascript.Context;
27 import org.htmlunit.corejs.javascript.Function;
28 import org.htmlunit.corejs.javascript.Scriptable;
29 import org.htmlunit.corejs.javascript.VarScope;
30 import org.htmlunit.html.DomDocumentFragment;
31 import org.htmlunit.html.DomElement;
32 import org.htmlunit.html.DomNode;
33 import org.htmlunit.html.HtmlElement;
34 import org.htmlunit.html.HtmlInlineFrame;
35 import org.htmlunit.javascript.HtmlUnitScriptable;
36 import org.htmlunit.javascript.JavaScriptEngine;
37 import org.htmlunit.javascript.configuration.JsxClass;
38 import org.htmlunit.javascript.configuration.JsxConstant;
39 import org.htmlunit.javascript.configuration.JsxConstructor;
40 import org.htmlunit.javascript.configuration.JsxFunction;
41 import org.htmlunit.javascript.configuration.JsxGetter;
42 import org.htmlunit.javascript.configuration.JsxSetter;
43 import org.htmlunit.javascript.host.Element;
44 import org.htmlunit.javascript.host.NamedNodeMap;
45 import org.htmlunit.javascript.host.event.EventTarget;
46 import org.htmlunit.javascript.host.html.HTMLCollection;
47 import org.htmlunit.javascript.host.html.HTMLDocument;
48 import org.htmlunit.javascript.host.html.HTMLHtmlElement;
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65 @JsxClass
66 public class Node extends EventTarget {
67
68
69
70
71
72
73 @JsxConstant
74 public static final int ELEMENT_NODE = org.w3c.dom.Node.ELEMENT_NODE;
75
76
77
78
79
80
81 @JsxConstant
82 public static final int ATTRIBUTE_NODE = org.w3c.dom.Node.ATTRIBUTE_NODE;
83
84
85
86
87
88
89 @JsxConstant
90 public static final int TEXT_NODE = org.w3c.dom.Node.TEXT_NODE;
91
92
93
94
95
96
97 @JsxConstant
98 public static final int CDATA_SECTION_NODE = org.w3c.dom.Node.CDATA_SECTION_NODE;
99
100
101
102
103
104
105 @JsxConstant
106 public static final int ENTITY_REFERENCE_NODE = org.w3c.dom.Node.ENTITY_REFERENCE_NODE;
107
108
109
110
111
112
113 @JsxConstant
114 public static final int ENTITY_NODE = org.w3c.dom.Node.ENTITY_NODE;
115
116
117
118
119
120
121 @JsxConstant
122 public static final int PROCESSING_INSTRUCTION_NODE = org.w3c.dom.Node.PROCESSING_INSTRUCTION_NODE;
123
124
125
126
127
128
129 @JsxConstant
130 public static final int COMMENT_NODE = org.w3c.dom.Node.COMMENT_NODE;
131
132
133
134
135
136
137 @JsxConstant
138 public static final int DOCUMENT_NODE = org.w3c.dom.Node.DOCUMENT_NODE;
139
140
141
142
143
144
145 @JsxConstant
146 public static final int DOCUMENT_TYPE_NODE = org.w3c.dom.Node.DOCUMENT_TYPE_NODE;
147
148
149
150
151
152
153 @JsxConstant
154 public static final int DOCUMENT_FRAGMENT_NODE = org.w3c.dom.Node.DOCUMENT_FRAGMENT_NODE;
155
156
157
158
159
160
161 @JsxConstant
162 public static final int NOTATION_NODE = org.w3c.dom.Node.NOTATION_NODE;
163
164
165
166
167
168
169 @JsxConstant
170 public static final int DOCUMENT_POSITION_DISCONNECTED = org.w3c.dom.Node.DOCUMENT_POSITION_DISCONNECTED;
171
172
173
174
175
176
177 @JsxConstant
178 public static final int DOCUMENT_POSITION_PRECEDING = org.w3c.dom.Node.DOCUMENT_POSITION_PRECEDING;
179
180
181
182
183
184
185 @JsxConstant
186 public static final int DOCUMENT_POSITION_FOLLOWING = org.w3c.dom.Node.DOCUMENT_POSITION_FOLLOWING;
187
188
189
190
191
192
193 @JsxConstant
194 public static final int DOCUMENT_POSITION_CONTAINS = org.w3c.dom.Node.DOCUMENT_POSITION_CONTAINS;
195
196
197
198
199
200
201 @JsxConstant
202 public static final int DOCUMENT_POSITION_CONTAINED_BY = org.w3c.dom.Node.DOCUMENT_POSITION_CONTAINED_BY;
203
204
205
206
207
208
209 @JsxConstant
210 public static final int DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC
211 = org.w3c.dom.Node.DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC;
212
213
214 private NodeList childNodes_;
215
216
217
218
219 @Override
220 @JsxConstructor
221 public void jsConstructor() {
222 super.jsConstructor();
223 }
224
225
226
227
228
229 @JsxGetter
230 public int getNodeType() {
231 return getDomNodeOrDie().getNodeType();
232 }
233
234
235
236
237
238 @JsxGetter
239 public String getNodeName() {
240 return getDomNodeOrDie().getNodeName();
241 }
242
243
244
245
246
247 @JsxGetter
248 public String getNodeValue() {
249 return getDomNodeOrDie().getNodeValue();
250 }
251
252
253
254
255
256 @JsxSetter
257 public void setNodeValue(final String newValue) {
258 getDomNodeOrDie().setNodeValue(newValue);
259 }
260
261
262
263
264
265
266 @JsxFunction
267 public Node appendChild(final Object childObject) {
268 if (childObject instanceof Node childNode) {
269
270
271 if (!isNodeInsertable(childNode)) {
272 throw JavaScriptEngine.asJavaScriptException(
273 getWindow(),
274 "Node cannot be inserted at the specified point in the hierarchy",
275 DOMException.HIERARCHY_REQUEST_ERR);
276 }
277
278
279 final DomNode childDomNode = childNode.getDomNodeOrDie();
280
281
282 final DomNode parentNode = getDomNodeOrDie();
283
284
285 try {
286 parentNode.appendChild(childDomNode);
287 }
288 catch (final org.w3c.dom.DOMException e) {
289 throw JavaScriptEngine.asJavaScriptException(getWindow(), e.getMessage(), e.code);
290 }
291
292 initInlineFrameIfNeeded(childDomNode);
293 for (final HtmlElement htmlElement : childDomNode.getHtmlElementDescendants()) {
294 initInlineFrameIfNeeded(htmlElement);
295 }
296 return childNode;
297 }
298 return null;
299 }
300
301
302
303
304
305
306 private static void initInlineFrameIfNeeded(final DomNode childDomNode) {
307 if (childDomNode instanceof HtmlInlineFrame frame) {
308 if (DomElement.ATTRIBUTE_NOT_DEFINED == frame.getSrcAttribute()) {
309 frame.loadInnerPage();
310 }
311 }
312 }
313
314
315
316
317
318
319
320
321
322
323
324 @JsxFunction
325 public static Node insertBefore(final Context context, final VarScope scope,
326 final Scriptable thisObj, final Object[] args, final Function function) {
327 return ((Node) thisObj).insertBeforeImpl(args);
328 }
329
330
331
332
333
334
335
336 protected Node insertBeforeImpl(final Object[] args) {
337 if (args.length < 1) {
338 throw JavaScriptEngine.typeError(
339 "Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 0 present.");
340 }
341
342 final Object newChildObject = args[0];
343 final Object refChildObject;
344 if (args.length > 1) {
345 refChildObject = args[1];
346 }
347 else {
348 refChildObject = JavaScriptEngine.UNDEFINED;
349 }
350
351 if (newChildObject instanceof Node newChild) {
352
353
354 if (!isNodeInsertable(newChild)) {
355 throw JavaScriptEngine.asJavaScriptException(
356 getWindow(),
357 "Node cannot be inserted at the specified point in the hierarchy",
358 DOMException.HIERARCHY_REQUEST_ERR);
359 }
360
361 final DomNode newChildNode = newChild.getDomNodeOrDie();
362 if (newChildNode instanceof DomDocumentFragment fragment) {
363 for (final DomNode child : fragment.getChildren()) {
364 if (!isNodeInsertable(child.getScriptableObject())) {
365 throw JavaScriptEngine.asJavaScriptException(
366 getWindow(),
367 "Node cannot be inserted at the specified point in the hierarchy",
368 DOMException.HIERARCHY_REQUEST_ERR);
369 }
370 }
371 }
372
373
374 final DomNode refChildNode;
375 if (JavaScriptEngine.isUndefined(refChildObject)) {
376 if (args.length == 2) {
377 refChildNode = null;
378 }
379 else {
380 throw JavaScriptEngine.typeError(
381 "Failed to execute 'insertBefore' on 'Node': 2 arguments required, but only 1 present.");
382 }
383 }
384 else if (refChildObject == null) {
385 refChildNode = null;
386 }
387 else {
388 refChildNode = ((Node) refChildObject).getDomNodeOrDie();
389 }
390
391 final DomNode domNode = getDomNodeOrDie();
392
393 try {
394 domNode.insertBefore(newChildNode, refChildNode);
395 }
396 catch (final org.w3c.dom.DOMException e) {
397 throw JavaScriptEngine.asJavaScriptException(getWindow(), e.getMessage(), DOMException.NOT_FOUND_ERR);
398 }
399 return newChild;
400 }
401 return null;
402 }
403
404
405
406
407
408
409 private static boolean isNodeInsertable(final Node childObject) {
410 if (childObject instanceof HTMLHtmlElement) {
411 final DomNode domNode = childObject.getDomNodeOrDie();
412 return domNode.getPage().getDocumentElement() != domNode;
413 }
414 return true;
415 }
416
417
418
419
420
421 protected void remove() {
422 getDomNodeOrDie().remove();
423 }
424
425
426
427
428
429
430 @JsxFunction
431 public Node removeChild(final Object childObject) {
432 if (!(childObject instanceof Node childObjectNode)) {
433 return null;
434 }
435
436
437 final DomNode childDomNode = childObjectNode.getDomNodeOrDie();
438
439 if (!getDomNodeOrDie().isAncestorOf(childDomNode)) {
440 throw JavaScriptEngine.asJavaScriptException(
441 getWindow(),
442 "Failed to execute 'removeChild' on '"
443 + this + "': The node to be removed is not a child of this node.",
444 DOMException.NOT_FOUND_ERR);
445 }
446
447 childDomNode.remove();
448 return childObjectNode;
449 }
450
451
452
453
454
455
456
457 @JsxFunction
458 public Node replaceChild(final Object newChildObject, final Object oldChildObject) {
459 if (newChildObject instanceof DocumentFragment fragment) {
460 Node firstNode = null;
461
462 final Node oldChildNode = (Node) oldChildObject;
463 final Node refChildObject = oldChildNode.getNextSibling();
464 for (final DomNode node : fragment.getDomNodeOrDie().getChildren()) {
465 if (firstNode == null) {
466 replaceChild(node.getScriptableObject(), oldChildObject);
467 firstNode = node.getScriptableObject();
468 }
469 else {
470 insertBeforeImpl(new Object[] {node.getScriptableObject(), refChildObject});
471 }
472 }
473 if (firstNode == null) {
474 removeChild(oldChildObject);
475 }
476
477 return oldChildNode;
478 }
479
480 if (newChildObject instanceof Node newChild && oldChildObject instanceof Node oldChildNode) {
481
482
483 if (!isNodeInsertable(newChild)) {
484 throw JavaScriptEngine.asJavaScriptException(
485 getWindow(),
486 "Node cannot be inserted at the specified point in the hierarchy",
487 DOMException.HIERARCHY_REQUEST_ERR);
488 }
489
490
491 final DomNode newChildDomNode = newChild.getDomNodeOrDie();
492 final DomNode oldChildDomNode = oldChildNode.getDomNodeOrDie();
493
494
495 oldChildDomNode.replace(newChildDomNode);
496
497 return oldChildNode;
498 }
499
500 return null;
501 }
502
503
504
505
506
507
508
509
510
511
512 public static void moveBefore(final Context context, final VarScope scope,
513 final Scriptable thisObj, final Object[] args, final Function function) {
514 if (args.length < 2) {
515 throw JavaScriptEngine.typeError(
516 "Failed to execute 'moveBefore' on 'Element': 2 arguments required, but only 0 present.");
517 }
518
519 final Object movedNodeObject = args[0];
520 if (!(movedNodeObject instanceof Node)) {
521 throw JavaScriptEngine.typeError(
522 "Failed to execute 'moveBefore' on 'Element': parameter 1 is not of type 'Node'.");
523 }
524 final Object referenceNodeObject = args[1];
525 if (referenceNodeObject != null && !(referenceNodeObject instanceof Node)) {
526 throw JavaScriptEngine.typeError(
527 "Failed to execute 'moveBefore' on 'Element': parameter 2 is not of type 'Node'.");
528 }
529
530 final Node node = (Node) thisObj;
531 try {
532 if (referenceNodeObject == null) {
533 node.getDomNodeOrDie().moveBefore(((Node) movedNodeObject).getDomNodeOrDie(), null);
534 return;
535 }
536
537 node.getDomNodeOrDie().moveBefore(
538 ((Node) movedNodeObject).getDomNodeOrDie(), ((Node) referenceNodeObject).getDomNodeOrDie());
539 }
540 catch (final org.w3c.dom.DOMException e) {
541 throw JavaScriptEngine.asJavaScriptException(
542 node.getWindow(),
543 "Failed to execute 'moveChild' on '" + node + ": " + e.getMessage(),
544 e.code);
545 }
546 }
547
548
549
550
551
552
553 @JsxFunction
554 public Node cloneNode(final boolean deep) {
555 final DomNode domNode = getDomNodeOrDie();
556 final DomNode clonedNode = domNode.cloneNode(deep);
557
558 return getJavaScriptNode(clonedNode);
559 }
560
561
562
563
564
565
566
567
568 @JsxFunction
569 public boolean isEqualNode(final Node other) {
570 if (isSameNode(other)) {
571 return true;
572 }
573
574 if (other == null) {
575 return false;
576 }
577
578 if (!getClassName().equals(other.getClassName())) {
579 return false;
580 }
581
582 if (this instanceof DocumentType docType) {
583 final DocumentType otherDocType = (DocumentType) other;
584 if (!Objects.equals(docType.getName(), otherDocType.getName())
585 || !Objects.equals(docType.getPublicId(), otherDocType.getPublicId())
586 || !Objects.equals(docType.getSystemId(), otherDocType.getSystemId())) {
587 return false;
588 }
589
590 }
591 else if (this instanceof Element element) {
592 final Element otherElement = (Element) other;
593 if (!Objects.equals(element.getNodeName(), otherElement.getNodeName())
594 || !Objects.equals(element.getPrefix(), otherElement.getPrefix())
595 || !Objects.equals(element.getLocalName(), otherElement.getLocalName())) {
596 return false;
597 }
598
599 final NamedNodeMap attributesMap = element.getAttributes();
600 final NamedNodeMap otherAttributesMap = otherElement.getAttributes();
601 if (attributesMap != null || otherAttributesMap != null) {
602 if (attributesMap == null || otherAttributesMap == null) {
603 return false;
604 }
605
606 final int length = attributesMap.getLength();
607 if (length != otherAttributesMap.getLength()) {
608 return false;
609 }
610
611 final Map<String, Attr> name2Attributes = new HashMap<>();
612 for (int i = 0; i < length; i++) {
613 final Attr attribute = (Attr) attributesMap.item(i);
614 name2Attributes.put(attribute.getName(), attribute);
615 }
616
617 for (int i = 0; i < length; i++) {
618 final Attr otherAttribute = (Attr) otherAttributesMap.item(i);
619 final Attr attribute = name2Attributes.get(otherAttribute.getName());
620 if (attribute == null) {
621 return false;
622 }
623 if (!attribute.isEqualNode(otherAttribute)) {
624 return false;
625 }
626 }
627 }
628
629 }
630 else if (this instanceof Attr attr) {
631 final Attr otherAttr = (Attr) other;
632 if (!Objects.equals(attr.getName(), otherAttr.getName())
633 || !Objects.equals(attr.getLocalName(), otherAttr.getLocalName())
634 || !Objects.equals(attr.getValue(), otherAttr.getValue())) {
635 return false;
636 }
637
638 }
639 else if (this instanceof ProcessingInstruction instruction) {
640 final ProcessingInstruction otherInstruction = (ProcessingInstruction) other;
641 if (!Objects.equals(instruction.getTarget(), otherInstruction.getTarget())
642 || !Objects.equals(instruction.getData(), otherInstruction.getData())) {
643 return false;
644 }
645
646 }
647 else if (this instanceof Text || this instanceof Comment) {
648 final CharacterData data = (CharacterData) this;
649 final CharacterData otherData = (CharacterData) other;
650 if (!Objects.equals(data.getData(), otherData.getData())) {
651 return false;
652 }
653 }
654
655 final NodeList childNodes = getChildNodes();
656 final NodeList otherChildNodes = other.getChildNodes();
657 if (childNodes != null || otherChildNodes != null) {
658 if (childNodes == null || otherChildNodes == null) {
659 return false;
660 }
661
662 final int length = childNodes.getLength();
663 final int otherLength = otherChildNodes.getLength();
664 if (length != otherLength) {
665 return false;
666 }
667
668 for (int i = 0; i < length; i++) {
669 final Node childNode = (Node) childNodes.item(i);
670 final Node otherChildNode = (Node) otherChildNodes.item(i);
671 if (!childNode.isEqualNode(otherChildNode)) {
672 return false;
673 }
674 }
675 }
676
677 return true;
678 }
679
680
681
682
683
684
685
686 @JsxFunction
687 public boolean isSameNode(final Object other) {
688 return this == other;
689 }
690
691
692
693
694
695
696 @JsxFunction
697 public boolean hasChildNodes() {
698 return getDomNodeOrDie().getChildren().iterator().hasNext();
699 }
700
701
702
703
704
705
706
707
708 @JsxFunction
709 public String lookupPrefix(final String namespace) {
710 return null;
711 }
712
713
714
715
716
717 @JsxGetter
718 public NodeList getChildNodes() {
719 if (childNodes_ == null) {
720 final DomNode node = getDomNodeOrDie();
721 childNodes_ = new NodeList(node, false);
722 childNodes_.setElementsSupplier(
723 (Supplier<List<DomNode>> & Serializable)
724 () -> {
725 final List<DomNode> response = new ArrayList<>();
726 for (final DomNode child : node.getChildren()) {
727 response.add(child);
728 }
729
730 return response;
731 });
732 }
733 return childNodes_;
734 }
735
736
737
738
739
740 public final Node getParent() {
741 return getJavaScriptNode(getDomNodeOrDie().getParentNode());
742 }
743
744
745
746
747
748
749 @JsxGetter
750 public Object getParentNode() {
751 return getJavaScriptNode(getDomNodeOrDie().getParentNode());
752 }
753
754
755
756
757
758
759
760 @JsxGetter
761 public Node getNextSibling() {
762 return getJavaScriptNode(getDomNodeOrDie().getNextSibling());
763 }
764
765
766
767
768
769
770
771 @JsxGetter
772 public Node getPreviousSibling() {
773 return getJavaScriptNode(getDomNodeOrDie().getPreviousSibling());
774 }
775
776
777
778
779
780
781
782 @JsxGetter
783 public Node getFirstChild() {
784 return getJavaScriptNode(getDomNodeOrDie().getFirstChild());
785 }
786
787
788
789
790
791
792
793 @JsxGetter
794 public Node getLastChild() {
795 return getJavaScriptNode(getDomNodeOrDie().getLastChild());
796 }
797
798
799
800
801
802
803 protected Node getJavaScriptNode(final DomNode domNode) {
804 if (domNode == null) {
805 return null;
806 }
807 return (Node) getScriptableFor(domNode);
808 }
809
810
811
812
813
814 @JsxGetter
815 public HtmlUnitScriptable getOwnerDocument() {
816 final Object document = getDomNodeOrDie().getOwnerDocument();
817 if (document != null) {
818 return ((SgmlPage) document).getScriptableObject();
819 }
820 return null;
821 }
822
823
824
825
826
827
828 @JsxFunction
829 public Node getRootNode() {
830 Node parent = this;
831 while (parent != null) {
832 if (parent instanceof Document || parent instanceof DocumentFragment) {
833 return parent;
834 }
835 parent = parent.getParent();
836 }
837 return this;
838 }
839
840
841
842
843
844
845
846
847 @JsxFunction
848 public int compareDocumentPosition(final Object node) {
849 if (!(node instanceof Node)) {
850 throw JavaScriptEngine.typeError("Could not convert JavaScript argument arg 0");
851 }
852 return getDomNodeOrDie().compareDocumentPosition(((Node) node).getDomNodeOrDie());
853 }
854
855
856
857
858 @JsxFunction
859 public void normalize() {
860 getDomNodeOrDie().normalize();
861 }
862
863
864
865
866
867 @JsxGetter
868 public String getTextContent() {
869 return getDomNodeOrDie().getTextContent();
870 }
871
872
873
874
875
876 @JsxSetter
877 public void setTextContent(final Object value) {
878 getDomNodeOrDie().setTextContent(value == null ? null : JavaScriptEngine.toString(value));
879 }
880
881
882
883
884
885
886 @JsxGetter
887 public Element getParentElement() {
888 final Node parent = getParent();
889 if (!(parent instanceof Element)) {
890 return null;
891 }
892 return (Element) parent;
893 }
894
895
896
897
898
899
900 public NamedNodeMap getAttributes() {
901 return null;
902 }
903
904
905
906
907
908
909
910 @JsxFunction
911 public boolean contains(final Object element) {
912 if (element == null || JavaScriptEngine.isUndefined(element)) {
913 return false;
914 }
915
916 if (!(element instanceof Node parent)) {
917 throw JavaScriptEngine.reportRuntimeError("Could not convert JavaScript argument arg 0");
918 }
919
920 for ( ; parent != null; parent = parent.getParentElement()) {
921 if (this == parent) {
922 return true;
923 }
924 }
925 return false;
926 }
927
928
929
930
931
932 @JsxGetter
933 public String getBaseURI() {
934 return getDomNodeOrDie().getBaseURI();
935 }
936
937
938
939
940
941
942 public boolean hasAttributes() {
943 return getDomNodeOrDie().hasAttributes();
944 }
945
946
947
948
949
950 public String getPrefix() {
951 return getDomNodeOrDie().getPrefix();
952 }
953
954
955
956
957
958 public String getLocalName() {
959 return getDomNodeOrDie().getLocalName();
960 }
961
962
963
964
965
966 public String getNamespaceURI() {
967 return getDomNodeOrDie().getNamespaceURI();
968 }
969
970
971
972
973
974 protected int getChildElementCount() {
975 final DomNode domNode = getDomNodeOrDie();
976 if (domNode instanceof DomElement element) {
977 return element.getChildElementCount();
978 }
979
980 int counter = 0;
981 for (final DomNode child : getDomNodeOrDie().getChildren()) {
982 if (child != null) {
983 final HtmlUnitScriptable scriptable = child.getScriptableObject();
984 if (scriptable instanceof Element) {
985 counter++;
986 }
987 }
988 }
989 return counter;
990 }
991
992
993
994
995
996 protected Element getFirstElementChild() {
997 final DomNode domNode = getDomNodeOrDie();
998 if (domNode instanceof DomElement element) {
999 final DomElement child = element.getFirstElementChild();
1000 if (child != null) {
1001 return child.getScriptableObject();
1002 }
1003 return null;
1004 }
1005
1006 for (final DomNode child : domNode.getChildren()) {
1007 if (child != null) {
1008 final HtmlUnitScriptable scriptable = child.getScriptableObject();
1009 if (scriptable instanceof Element element) {
1010 return element;
1011 }
1012 }
1013 }
1014 return null;
1015 }
1016
1017
1018
1019
1020
1021 protected Element getLastElementChild() {
1022 final DomNode domNode = getDomNodeOrDie();
1023 if (domNode instanceof DomElement) {
1024 final DomElement child = ((DomElement) getDomNodeOrDie()).getLastElementChild();
1025 if (child != null) {
1026 return child.getScriptableObject();
1027 }
1028 return null;
1029 }
1030
1031 Element result = null;
1032 for (final DomNode child : domNode.getChildren()) {
1033 final HtmlUnitScriptable scriptable = child.getScriptableObject();
1034 if (scriptable instanceof Element element) {
1035 result = element;
1036 }
1037 }
1038 return result;
1039 }
1040
1041
1042
1043
1044
1045
1046
1047 protected HTMLCollection getChildren() {
1048 final DomNode node = getDomNodeOrDie();
1049 final HTMLCollection childrenColl = new HTMLCollection(node, false);
1050 childrenColl.setElementsSupplier(
1051 (Supplier<List<DomNode>> & Serializable)
1052 () -> {
1053 final List<DomNode> children = new ArrayList<>();
1054 for (final DomNode domNode : node.getChildNodes()) {
1055 if (domNode instanceof DomElement) {
1056 children.add(domNode);
1057 }
1058 }
1059 return children;
1060 });
1061 return childrenColl;
1062 }
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072 protected static void after(final Context context, final Scriptable thisObj, final Object[] args,
1073 final Function function) {
1074 final DomNode thisDomNode = ((Node) thisObj).getDomNodeOrDie();
1075 final DomNode parentNode = thisDomNode.getParentNode();
1076 final DomNode nextSibling = thisDomNode.getNextSibling();
1077 for (final Object arg : args) {
1078 final Node node = toNodeOrTextNode((Node) thisObj, arg);
1079 final DomNode newNode = node.getDomNodeOrDie();
1080 if (nextSibling == null) {
1081 parentNode.appendChild(newNode);
1082 }
1083 else {
1084 nextSibling.insertBefore(newNode);
1085 }
1086 }
1087 }
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097 protected static void append(final Context context, final Scriptable thisObj, final Object[] args,
1098 final Function function) {
1099 if (!(thisObj instanceof Node thisNode)) {
1100 throw JavaScriptEngine.typeError("Illegal invocation");
1101 }
1102
1103 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1104
1105 for (final Object arg : args) {
1106 final Node node = toNodeOrTextNode(thisNode, arg);
1107 thisDomNode.appendChild(node.getDomNodeOrDie());
1108 }
1109 }
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119 protected static void prepend(final Context context, final Scriptable thisObj, final Object[] args,
1120 final Function function) {
1121 if (!(thisObj instanceof Node thisNode)) {
1122 throw JavaScriptEngine.typeError("Illegal invocation");
1123 }
1124
1125 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1126 final DomNode firstChild = thisDomNode.getFirstChild();
1127
1128 for (final Object arg : args) {
1129 final Node node = toNodeOrTextNode(thisNode, arg);
1130 final DomNode newNode = node.getDomNodeOrDie();
1131 if (firstChild == null) {
1132 thisDomNode.appendChild(newNode);
1133 }
1134 else {
1135 firstChild.insertBefore(newNode);
1136 }
1137 }
1138 }
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148 protected static void replaceChildren(final Context context, final Scriptable thisObj, final Object[] args,
1149 final Function function) {
1150 if (!(thisObj instanceof Node thisNode)) {
1151 throw JavaScriptEngine.typeError("Illegal invocation");
1152 }
1153
1154 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1155 thisDomNode.removeAllChildren();
1156
1157 for (final Object arg : args) {
1158 final Node node = toNodeOrTextNode(thisNode, arg);
1159 thisDomNode.appendChild(node.getDomNodeOrDie());
1160 }
1161 }
1162
1163 private static Node toNodeOrTextNode(final Node thisObj, final Object obj) {
1164 if (obj instanceof Node node) {
1165 return node;
1166 }
1167 return (Node)
1168 ((HTMLDocument) thisObj.getOwnerDocument()).createTextNode(JavaScriptEngine.toString(obj));
1169 }
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179 protected static void before(final Context context, final Scriptable thisObj, final Object[] args,
1180 final Function function) {
1181 for (final Object arg : args) {
1182 final Node node = toNodeOrTextNode((Node) thisObj, arg);
1183 ((Node) thisObj).getDomNodeOrDie().insertBefore(node.getDomNodeOrDie());
1184 }
1185 }
1186
1187
1188
1189
1190
1191
1192
1193
1194 protected static void replaceWith(final Context context, final Scriptable thisObj, final Object[] args,
1195 final Function function) {
1196 final DomNode thisDomNode = ((Node) thisObj).getDomNodeOrDie();
1197 final DomNode parentNode = thisDomNode.getParentNode();
1198
1199 if (args.length == 0) {
1200 parentNode.removeChild(thisDomNode);
1201 return;
1202 }
1203
1204 final DomNode nextSibling = thisDomNode.getNextSibling();
1205 boolean isFirst = true;
1206 for (final Object arg : args) {
1207 final DomNode newNode = toNodeOrTextNode((Node) thisObj, arg).getDomNodeOrDie();
1208 if (isFirst) {
1209 isFirst = false;
1210 thisDomNode.replace(newNode);
1211 }
1212 else {
1213 if (nextSibling == null) {
1214 parentNode.appendChild(newNode);
1215 }
1216 else {
1217 nextSibling.insertBefore(newNode);
1218 }
1219 }
1220 }
1221 }
1222 }