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 movedNode)) {
521 throw JavaScriptEngine.typeError(
522 "Failed to execute 'moveBefore' on 'Element': parameter 1 is not of type 'Node'.");
523 }
524
525 final Object referenceNodeObject = args[1];
526 if (referenceNodeObject != null && !(referenceNodeObject instanceof Node)) {
527 throw JavaScriptEngine.typeError(
528 "Failed to execute 'moveBefore' on 'Element': parameter 2 is not of type 'Node'.");
529 }
530
531 final Node node = (Node) thisObj;
532 try {
533 if (referenceNodeObject == null) {
534 node.getDomNodeOrDie().moveBefore(movedNode.getDomNodeOrDie(), null);
535 return;
536 }
537
538 node.getDomNodeOrDie().moveBefore(
539 ((Node) movedNodeObject).getDomNodeOrDie(), ((Node) referenceNodeObject).getDomNodeOrDie());
540 }
541 catch (final org.w3c.dom.DOMException e) {
542 throw JavaScriptEngine.asJavaScriptException(
543 node.getWindow(),
544 "Failed to execute 'moveChild' on '" + node + ": " + e.getMessage(),
545 e.code);
546 }
547 }
548
549
550
551
552
553
554 @JsxFunction
555 public Node cloneNode(final boolean deep) {
556 final DomNode domNode = getDomNodeOrDie();
557 final DomNode clonedNode = domNode.cloneNode(deep);
558
559 return getJavaScriptNode(clonedNode);
560 }
561
562
563
564
565
566
567
568
569 @JsxFunction
570 public boolean isEqualNode(final Node other) {
571 if (isSameNode(other)) {
572 return true;
573 }
574
575 if (other == null) {
576 return false;
577 }
578
579 if (!getClassName().equals(other.getClassName())) {
580 return false;
581 }
582
583 if (this instanceof DocumentType docType) {
584 final DocumentType otherDocType = (DocumentType) other;
585 if (!Objects.equals(docType.getName(), otherDocType.getName())
586 || !Objects.equals(docType.getPublicId(), otherDocType.getPublicId())
587 || !Objects.equals(docType.getSystemId(), otherDocType.getSystemId())) {
588 return false;
589 }
590
591 }
592 else if (this instanceof Element element) {
593 final Element otherElement = (Element) other;
594 if (!Objects.equals(element.getNodeName(), otherElement.getNodeName())
595 || !Objects.equals(element.getPrefix(), otherElement.getPrefix())
596 || !Objects.equals(element.getLocalName(), otherElement.getLocalName())) {
597 return false;
598 }
599
600 final NamedNodeMap attributesMap = element.getAttributes();
601 final NamedNodeMap otherAttributesMap = otherElement.getAttributes();
602 if (attributesMap != null || otherAttributesMap != null) {
603 if (attributesMap == null || otherAttributesMap == null) {
604 return false;
605 }
606
607 final int length = attributesMap.getLength();
608 if (length != otherAttributesMap.getLength()) {
609 return false;
610 }
611
612 final Map<String, Attr> name2Attributes = new HashMap<>();
613 for (int i = 0; i < length; i++) {
614 final Attr attribute = (Attr) attributesMap.item(i);
615 name2Attributes.put(attribute.getName(), attribute);
616 }
617
618 for (int i = 0; i < length; i++) {
619 final Attr otherAttribute = (Attr) otherAttributesMap.item(i);
620 final Attr attribute = name2Attributes.get(otherAttribute.getName());
621 if (attribute == null) {
622 return false;
623 }
624 if (!attribute.isEqualNode(otherAttribute)) {
625 return false;
626 }
627 }
628 }
629
630 }
631 else if (this instanceof Attr attr) {
632 final Attr otherAttr = (Attr) other;
633 if (!Objects.equals(attr.getName(), otherAttr.getName())
634 || !Objects.equals(attr.getLocalName(), otherAttr.getLocalName())
635 || !Objects.equals(attr.getValue(), otherAttr.getValue())) {
636 return false;
637 }
638
639 }
640 else if (this instanceof ProcessingInstruction instruction) {
641 final ProcessingInstruction otherInstruction = (ProcessingInstruction) other;
642 if (!Objects.equals(instruction.getTarget(), otherInstruction.getTarget())
643 || !Objects.equals(instruction.getData(), otherInstruction.getData())) {
644 return false;
645 }
646
647 }
648 else if (this instanceof Text || this instanceof Comment) {
649 final CharacterData data = (CharacterData) this;
650 final CharacterData otherData = (CharacterData) other;
651 if (!Objects.equals(data.getData(), otherData.getData())) {
652 return false;
653 }
654 }
655
656 final NodeList childNodes = getChildNodes();
657 final NodeList otherChildNodes = other.getChildNodes();
658 if (childNodes != null || otherChildNodes != null) {
659 if (childNodes == null || otherChildNodes == null) {
660 return false;
661 }
662
663 final int length = childNodes.getLength();
664 final int otherLength = otherChildNodes.getLength();
665 if (length != otherLength) {
666 return false;
667 }
668
669 for (int i = 0; i < length; i++) {
670 final Node childNode = (Node) childNodes.item(i);
671 final Node otherChildNode = (Node) otherChildNodes.item(i);
672 if (!childNode.isEqualNode(otherChildNode)) {
673 return false;
674 }
675 }
676 }
677
678 return true;
679 }
680
681
682
683
684
685
686
687 @JsxFunction
688 public boolean isSameNode(final Object other) {
689 return this == other;
690 }
691
692
693
694
695
696
697 @JsxFunction
698 public boolean hasChildNodes() {
699 return getDomNodeOrDie().getChildren().iterator().hasNext();
700 }
701
702
703
704
705
706
707
708
709 @JsxFunction
710 public String lookupPrefix(final String namespace) {
711 return null;
712 }
713
714
715
716
717
718 @JsxGetter
719 public NodeList getChildNodes() {
720 if (childNodes_ == null) {
721 final DomNode node = getDomNodeOrDie();
722 childNodes_ = new NodeList(node, false);
723 childNodes_.setElementsSupplier(
724 (Supplier<List<DomNode>> & Serializable)
725 () -> {
726 final List<DomNode> response = new ArrayList<>();
727 for (final DomNode child : node.getChildren()) {
728 response.add(child);
729 }
730
731 return response;
732 });
733 }
734 return childNodes_;
735 }
736
737
738
739
740
741 public final Node getParent() {
742 return getJavaScriptNode(getDomNodeOrDie().getParentNode());
743 }
744
745
746
747
748
749
750 @JsxGetter
751 public Object getParentNode() {
752 return getJavaScriptNode(getDomNodeOrDie().getParentNode());
753 }
754
755
756
757
758
759
760
761 @JsxGetter
762 public Node getNextSibling() {
763 return getJavaScriptNode(getDomNodeOrDie().getNextSibling());
764 }
765
766
767
768
769
770
771
772 @JsxGetter
773 public Node getPreviousSibling() {
774 return getJavaScriptNode(getDomNodeOrDie().getPreviousSibling());
775 }
776
777
778
779
780
781
782
783 @JsxGetter
784 public Node getFirstChild() {
785 return getJavaScriptNode(getDomNodeOrDie().getFirstChild());
786 }
787
788
789
790
791
792
793
794 @JsxGetter
795 public Node getLastChild() {
796 return getJavaScriptNode(getDomNodeOrDie().getLastChild());
797 }
798
799
800
801
802
803
804 protected Node getJavaScriptNode(final DomNode domNode) {
805 if (domNode == null) {
806 return null;
807 }
808 return (Node) getScriptableFor(domNode);
809 }
810
811
812
813
814
815 @JsxGetter
816 public HtmlUnitScriptable getOwnerDocument() {
817 final Object document = getDomNodeOrDie().getOwnerDocument();
818 if (document != null) {
819 return ((SgmlPage) document).getScriptableObject();
820 }
821 return null;
822 }
823
824
825
826
827
828
829 @JsxFunction
830 public Node getRootNode() {
831 Node parent = this;
832 while (parent != null) {
833 if (parent instanceof Document || parent instanceof DocumentFragment) {
834 return parent;
835 }
836 parent = parent.getParent();
837 }
838 return this;
839 }
840
841
842
843
844
845
846
847
848 @JsxFunction
849 public int compareDocumentPosition(final Object nodeObject) {
850 if (!(nodeObject instanceof Node node)) {
851 throw JavaScriptEngine.typeError("Could not convert JavaScript argument arg 0");
852 }
853 return getDomNodeOrDie().compareDocumentPosition(node.getDomNodeOrDie());
854 }
855
856
857
858
859 @JsxFunction
860 public void normalize() {
861 getDomNodeOrDie().normalize();
862 }
863
864
865
866
867
868 @JsxGetter
869 public String getTextContent() {
870 return getDomNodeOrDie().getTextContent();
871 }
872
873
874
875
876
877 @JsxSetter
878 public void setTextContent(final Object value) {
879 getDomNodeOrDie().setTextContent(value == null ? null : JavaScriptEngine.toString(value));
880 }
881
882
883
884
885
886
887 @JsxGetter
888 public Element getParentElement() {
889 final Node parent = getParent();
890 if (!(parent instanceof Element)) {
891 return null;
892 }
893 return (Element) parent;
894 }
895
896
897
898
899
900
901 public NamedNodeMap getAttributes() {
902 return null;
903 }
904
905
906
907
908
909
910
911 @JsxFunction
912 public boolean contains(final Object element) {
913 if (element == null || JavaScriptEngine.isUndefined(element)) {
914 return false;
915 }
916
917 if (!(element instanceof Node parent)) {
918 throw JavaScriptEngine.reportRuntimeError("Could not convert JavaScript argument arg 0");
919 }
920
921 for ( ; parent != null; parent = parent.getParentElement()) {
922 if (this == parent) {
923 return true;
924 }
925 }
926 return false;
927 }
928
929
930
931
932
933 @JsxGetter
934 public String getBaseURI() {
935 return getDomNodeOrDie().getBaseURI();
936 }
937
938
939
940
941
942
943 public boolean hasAttributes() {
944 return getDomNodeOrDie().hasAttributes();
945 }
946
947
948
949
950
951 public String getPrefix() {
952 return getDomNodeOrDie().getPrefix();
953 }
954
955
956
957
958
959 public String getLocalName() {
960 return getDomNodeOrDie().getLocalName();
961 }
962
963
964
965
966
967 public String getNamespaceURI() {
968 return getDomNodeOrDie().getNamespaceURI();
969 }
970
971
972
973
974
975 protected int getChildElementCount() {
976 final DomNode domNode = getDomNodeOrDie();
977 if (domNode instanceof DomElement element) {
978 return element.getChildElementCount();
979 }
980
981 int counter = 0;
982 for (final DomNode child : getDomNodeOrDie().getChildren()) {
983 if (child != null) {
984 final HtmlUnitScriptable scriptable = child.getScriptableObject();
985 if (scriptable instanceof Element) {
986 counter++;
987 }
988 }
989 }
990 return counter;
991 }
992
993
994
995
996
997 protected Element getFirstElementChild() {
998 final DomNode domNode = getDomNodeOrDie();
999 if (domNode instanceof DomElement element) {
1000 final DomElement child = element.getFirstElementChild();
1001 if (child != null) {
1002 return child.getScriptableObject();
1003 }
1004 return null;
1005 }
1006
1007 for (final DomNode child : domNode.getChildren()) {
1008 if (child != null) {
1009 final HtmlUnitScriptable scriptable = child.getScriptableObject();
1010 if (scriptable instanceof Element element) {
1011 return element;
1012 }
1013 }
1014 }
1015 return null;
1016 }
1017
1018
1019
1020
1021
1022 protected Element getLastElementChild() {
1023 final DomNode domNode = getDomNodeOrDie();
1024 if (domNode instanceof DomElement) {
1025 final DomElement child = ((DomElement) getDomNodeOrDie()).getLastElementChild();
1026 if (child != null) {
1027 return child.getScriptableObject();
1028 }
1029 return null;
1030 }
1031
1032 Element result = null;
1033 for (final DomNode child : domNode.getChildren()) {
1034 final HtmlUnitScriptable scriptable = child.getScriptableObject();
1035 if (scriptable instanceof Element element) {
1036 result = element;
1037 }
1038 }
1039 return result;
1040 }
1041
1042
1043
1044
1045
1046
1047
1048 protected HTMLCollection getChildren() {
1049 final DomNode node = getDomNodeOrDie();
1050 final HTMLCollection childrenColl = new HTMLCollection(node, false);
1051 childrenColl.setElementsSupplier(
1052 (Supplier<List<DomNode>> & Serializable)
1053 () -> {
1054 final List<DomNode> children = new ArrayList<>();
1055 for (final DomNode domNode : node.getChildNodes()) {
1056 if (domNode instanceof DomElement) {
1057 children.add(domNode);
1058 }
1059 }
1060 return children;
1061 });
1062 return childrenColl;
1063 }
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073 protected static void after(final Context context, final Scriptable thisObj, final Object[] args,
1074 final Function function) {
1075 final DomNode thisDomNode = ((Node) thisObj).getDomNodeOrDie();
1076 final DomNode parentNode = thisDomNode.getParentNode();
1077 final DomNode nextSibling = thisDomNode.getNextSibling();
1078 for (final Object arg : args) {
1079 final Node node = toNodeOrTextNode((Node) thisObj, arg);
1080 final DomNode newNode = node.getDomNodeOrDie();
1081 if (nextSibling == null) {
1082 parentNode.appendChild(newNode);
1083 }
1084 else {
1085 nextSibling.insertBefore(newNode);
1086 }
1087 }
1088 }
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098 protected static void append(final Context context, final Scriptable thisObj, final Object[] args,
1099 final Function function) {
1100 if (!(thisObj instanceof Node thisNode)) {
1101 throw JavaScriptEngine.typeError("Illegal invocation");
1102 }
1103
1104 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1105
1106 for (final Object arg : args) {
1107 final Node node = toNodeOrTextNode(thisNode, arg);
1108 thisDomNode.appendChild(node.getDomNodeOrDie());
1109 }
1110 }
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120 protected static void prepend(final Context context, final Scriptable thisObj, final Object[] args,
1121 final Function function) {
1122 if (!(thisObj instanceof Node thisNode)) {
1123 throw JavaScriptEngine.typeError("Illegal invocation");
1124 }
1125
1126 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1127 final DomNode firstChild = thisDomNode.getFirstChild();
1128
1129 for (final Object arg : args) {
1130 final Node node = toNodeOrTextNode(thisNode, arg);
1131 final DomNode newNode = node.getDomNodeOrDie();
1132 if (firstChild == null) {
1133 thisDomNode.appendChild(newNode);
1134 }
1135 else {
1136 firstChild.insertBefore(newNode);
1137 }
1138 }
1139 }
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149 protected static void replaceChildren(final Context context, final Scriptable thisObj, final Object[] args,
1150 final Function function) {
1151 if (!(thisObj instanceof Node thisNode)) {
1152 throw JavaScriptEngine.typeError("Illegal invocation");
1153 }
1154
1155 final DomNode thisDomNode = thisNode.getDomNodeOrDie();
1156 thisDomNode.removeAllChildren();
1157
1158 for (final Object arg : args) {
1159 final Node node = toNodeOrTextNode(thisNode, arg);
1160 thisDomNode.appendChild(node.getDomNodeOrDie());
1161 }
1162 }
1163
1164 private static Node toNodeOrTextNode(final Node thisObj, final Object obj) {
1165 if (obj instanceof Node node) {
1166 return node;
1167 }
1168 return (Node)
1169 ((HTMLDocument) thisObj.getOwnerDocument()).createTextNode(JavaScriptEngine.toString(obj));
1170 }
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180 protected static void before(final Context context, final Scriptable thisObj, final Object[] args,
1181 final Function function) {
1182 for (final Object arg : args) {
1183 final Node node = toNodeOrTextNode((Node) thisObj, arg);
1184 ((Node) thisObj).getDomNodeOrDie().insertBefore(node.getDomNodeOrDie());
1185 }
1186 }
1187
1188
1189
1190
1191
1192
1193
1194
1195 protected static void replaceWith(final Context context, final Scriptable thisObj, final Object[] args,
1196 final Function function) {
1197 final DomNode thisDomNode = ((Node) thisObj).getDomNodeOrDie();
1198 final DomNode parentNode = thisDomNode.getParentNode();
1199
1200 if (args.length == 0) {
1201 parentNode.removeChild(thisDomNode);
1202 return;
1203 }
1204
1205 final DomNode nextSibling = thisDomNode.getNextSibling();
1206 boolean isFirst = true;
1207 for (final Object arg : args) {
1208 final DomNode newNode = toNodeOrTextNode((Node) thisObj, arg).getDomNodeOrDie();
1209 if (isFirst) {
1210 isFirst = false;
1211 thisDomNode.replace(newNode);
1212 }
1213 else {
1214 if (nextSibling == null) {
1215 parentNode.appendChild(newNode);
1216 }
1217 else {
1218 nextSibling.insertBefore(newNode);
1219 }
1220 }
1221 }
1222 }
1223 }