1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 package org.htmlunit.html;
16
17 import java.io.PrintWriter;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.HashSet;
21 import java.util.Map;
22
23 import org.apache.commons.lang3.StringUtils;
24 import org.htmlunit.SgmlPage;
25 import org.htmlunit.html.impl.SelectableTextInput;
26 import org.htmlunit.html.impl.SelectableTextSelectionDelegate;
27 import org.htmlunit.javascript.host.event.Event;
28 import org.htmlunit.javascript.host.event.MouseEvent;
29 import org.htmlunit.util.NameValuePair;
30 import org.w3c.dom.Node;
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48 public class HtmlTextArea extends HtmlElement implements DisabledElement, SubmittableElement,
49 LabelableElement, SelectableTextInput, FormFieldWithNameHistory, ValidatableElement {
50
51 public static final String TAG_NAME = "textarea";
52
53 private String defaultValue_;
54 private String valueAtFocus_;
55 private final String originalName_;
56 private Collection<String> newNames_ = Collections.emptySet();
57 private String customValidity_;
58
59 private SelectableTextSelectionDelegate selectionDelegate_ = new SelectableTextSelectionDelegate(this);
60 private DoTypeProcessor doTypeProcessor_ = new DoTypeProcessor(this);
61
62
63
64
65
66
67
68
69 HtmlTextArea(final String qualifiedName, final SgmlPage page,
70 final Map<String, DomAttr> attributes) {
71 super(qualifiedName, page, attributes);
72 originalName_ = getNameAttribute();
73 }
74
75
76
77
78
79
80 private void initDefaultValue() {
81 if (defaultValue_ == null) {
82 defaultValue_ = readValue();
83 }
84 }
85
86
87
88
89 @Override
90 public boolean handles(final Event event) {
91 if (event instanceof MouseEvent) {
92 return true;
93 }
94
95 return super.handles(event);
96 }
97
98
99
100
101
102
103 @Override
104 public final String getText() {
105 return readValue();
106 }
107
108 private String readValue() {
109 final StringBuilder builder = new StringBuilder();
110 for (final DomNode node : getChildren()) {
111 if (node instanceof DomText) {
112 builder.append(((DomText) node).getData());
113 }
114 }
115
116 if (builder.length() != 0 && builder.charAt(0) == '\n') {
117 builder.deleteCharAt(0);
118 }
119 return builder.toString();
120 }
121
122
123
124
125
126
127
128
129
130 @Override
131 public final void setText(final String newValue) {
132 setTextInternal(newValue);
133
134 HtmlInput.executeOnChangeHandlerIfAppropriate(this);
135 }
136
137 private void setTextInternal(final String newValue) {
138 initDefaultValue();
139 DomNode child = getFirstChild();
140 if (child == null) {
141 final DomText newChild = new DomText(getPage(), newValue);
142 appendChild(newChild);
143 }
144 else {
145 DomNode next = child.getNextSibling();
146 while (next != null && !(next instanceof DomText)) {
147 child = next;
148 next = child.getNextSibling();
149 }
150
151 if (next == null) {
152 removeChild(child);
153 final DomText newChild = new DomText(getPage(), newValue);
154 appendChild(newChild);
155 }
156 else {
157 ((DomText) next).setData(newValue);
158 }
159 }
160
161 final int pos = newValue.length();
162 setSelectionStart(pos);
163 setSelectionEnd(pos);
164 }
165
166
167
168
169 @Override
170 public NameValuePair[] getSubmitNameValuePairs() {
171 String text = getText();
172 text = text.replace("\r\n", "\n").replace("\n", "\r\n");
173
174 return new NameValuePair[]{new NameValuePair(getNameAttribute(), text)};
175 }
176
177
178
179
180
181 @Override
182 public void reset() {
183 initDefaultValue();
184 setText(defaultValue_);
185 }
186
187
188
189
190
191 @Override
192 public void setDefaultValue(String defaultValue) {
193 initDefaultValue();
194 if (defaultValue == null) {
195 defaultValue = "";
196 }
197
198
199 if (getText().equals(getDefaultValue())) {
200 setTextInternal(defaultValue);
201 }
202 defaultValue_ = defaultValue;
203 }
204
205
206
207
208
209 @Override
210 public String getDefaultValue() {
211 initDefaultValue();
212 return defaultValue_;
213 }
214
215
216
217
218
219
220
221
222 @Override
223 public void setDefaultChecked(final boolean defaultChecked) {
224
225 }
226
227
228
229
230
231
232
233
234 @Override
235 public boolean isDefaultChecked() {
236 return false;
237 }
238
239
240
241
242
243
244
245
246 public final String getNameAttribute() {
247 return getAttributeDirect(DomElement.NAME_ATTRIBUTE);
248 }
249
250
251
252
253
254
255
256
257 public final String getRowsAttribute() {
258 return getAttributeDirect("rows");
259 }
260
261
262
263
264
265
266
267
268 public final String getColumnsAttribute() {
269 return getAttributeDirect("cols");
270 }
271
272
273
274
275 @Override
276 public final boolean isDisabled() {
277 if (hasAttribute(ATTRIBUTE_DISABLED)) {
278 return true;
279 }
280
281 Node node = getParentNode();
282 while (node != null) {
283 if (node instanceof DisabledElement
284 && ((DisabledElement) node).isDisabled()) {
285 return true;
286 }
287 node = node.getParentNode();
288 }
289
290 return false;
291 }
292
293
294
295
296 @Override
297 public final String getDisabledAttribute() {
298 return getAttributeDirect(ATTRIBUTE_DISABLED);
299 }
300
301
302
303
304
305
306
307
308 public final String getReadOnlyAttribute() {
309 return getAttributeDirect("readonly");
310 }
311
312
313
314
315
316
317
318
319 public final String getTabIndexAttribute() {
320 return getAttributeDirect("tabindex");
321 }
322
323
324
325
326
327
328
329
330 public final String getAccessKeyAttribute() {
331 return getAttributeDirect("accesskey");
332 }
333
334
335
336
337
338
339
340
341 public final String getOnFocusAttribute() {
342 return getAttributeDirect("onfocus");
343 }
344
345
346
347
348
349
350
351
352 public final String getOnBlurAttribute() {
353 return getAttributeDirect("onblur");
354 }
355
356
357
358
359
360
361
362
363 public final String getOnSelectAttribute() {
364 return getAttributeDirect("onselect");
365 }
366
367
368
369
370
371
372
373
374 public final String getOnChangeAttribute() {
375 return getAttributeDirect("onchange");
376 }
377
378
379
380
381 @Override
382 public void select() {
383 selectionDelegate_.select();
384 }
385
386
387
388
389 @Override
390 public String getSelectedText() {
391 return selectionDelegate_.getSelectedText();
392 }
393
394
395
396
397 @Override
398 public int getSelectionStart() {
399 return selectionDelegate_.getSelectionStart();
400 }
401
402
403
404
405 @Override
406 public void setSelectionStart(final int selectionStart) {
407 selectionDelegate_.setSelectionStart(selectionStart);
408 }
409
410
411
412
413 @Override
414 public int getSelectionEnd() {
415 return selectionDelegate_.getSelectionEnd();
416 }
417
418
419
420
421 @Override
422 public void setSelectionEnd(final int selectionEnd) {
423 selectionDelegate_.setSelectionEnd(selectionEnd);
424 }
425
426
427
428
429
430
431
432 @Override
433 protected void printXml(final String indent, final PrintWriter printWriter) {
434 printWriter.print(indent + "<");
435 printOpeningTagContentAsXml(printWriter);
436
437 printWriter.print(">");
438 printWriter.print(org.htmlunit.util.StringUtils.escapeXml(getText()));
439 printWriter.print("</textarea>");
440 }
441
442
443
444
445 @Override
446 protected void doType(final char c, final boolean lastType) {
447 doTypeProcessor_.doType(getText(), selectionDelegate_, c, this, lastType);
448 }
449
450
451
452
453 @Override
454 protected void doType(final int keyCode, final boolean lastType) {
455 doTypeProcessor_.doType(getText(), selectionDelegate_, keyCode, this, lastType);
456 }
457
458
459
460
461 @Override
462 protected void typeDone(final String newValue, final boolean notifyAttributeChangeListeners) {
463 setTextInternal(newValue);
464 }
465
466
467
468
469 @Override
470 protected boolean acceptChar(final char c) {
471 return super.acceptChar(c) || c == '\n' || c == '\r';
472 }
473
474
475
476
477 @Override
478 public void focus() {
479 super.focus();
480 valueAtFocus_ = getText();
481 }
482
483
484
485
486 @Override
487 public void removeFocus() {
488 super.removeFocus();
489 if (valueAtFocus_ != null && !valueAtFocus_.equals(getText())) {
490 HtmlInput.executeOnChangeHandlerIfAppropriate(this);
491 }
492 valueAtFocus_ = null;
493 }
494
495
496
497
498
499
500 public void setReadOnly(final boolean isReadOnly) {
501 if (isReadOnly) {
502 setAttribute("readOnly", "readOnly");
503 }
504 else {
505 removeAttribute("readOnly");
506 }
507 }
508
509
510
511
512
513 public boolean isReadOnly() {
514 return hasAttribute("readOnly");
515 }
516
517
518
519
520 @Override
521 protected void setAttributeNS(final String namespaceURI, final String qualifiedName, final String attributeValue,
522 final boolean notifyAttributeChangeListeners, final boolean notifyMutationObservers) {
523 final String qualifiedNameLC = org.htmlunit.util.StringUtils.toRootLowerCase(qualifiedName);
524 if (DomElement.NAME_ATTRIBUTE.equals(qualifiedNameLC)) {
525 if (newNames_.isEmpty()) {
526 newNames_ = new HashSet<>();
527 }
528 newNames_.add(attributeValue);
529 }
530 super.setAttributeNS(namespaceURI, qualifiedNameLC, attributeValue, notifyAttributeChangeListeners,
531 notifyMutationObservers);
532 }
533
534
535
536
537 @Override
538 public String getOriginalName() {
539 return originalName_;
540 }
541
542
543
544
545 @Override
546 public Collection<String> getNewNames() {
547 return newNames_;
548 }
549
550
551
552
553
554 @Override
555 protected boolean isEmptyXmlTagExpanded() {
556 return true;
557 }
558
559
560
561
562 @Override
563 public DisplayStyle getDefaultStyleDisplay() {
564 return DisplayStyle.INLINE_BLOCK;
565 }
566
567
568
569
570
571
572 public String getPlaceholder() {
573 return getAttributeDirect("placeholder");
574 }
575
576
577
578
579
580
581 public void setPlaceholder(final String placeholder) {
582 setAttribute("placeholder", placeholder);
583 }
584
585
586
587
588 @Override
589 protected boolean isRequiredSupported() {
590 return true;
591 }
592
593
594
595
596 @Override
597 public DomNode cloneNode(final boolean deep) {
598 final HtmlTextArea newnode = (HtmlTextArea) super.cloneNode(deep);
599 newnode.selectionDelegate_ = new SelectableTextSelectionDelegate(newnode);
600 newnode.doTypeProcessor_ = new DoTypeProcessor(newnode);
601 newnode.newNames_ = new HashSet<>(newNames_);
602
603 return newnode;
604 }
605
606
607
608
609 @Override
610 public boolean willValidate() {
611 return !isDisabled() && !isReadOnly();
612 }
613
614
615
616
617 @Override
618 public void setCustomValidity(final String message) {
619 customValidity_ = message;
620 }
621
622
623
624
625 @Override
626 public boolean isValid() {
627 return isValidValidityState();
628 }
629
630
631
632
633 @Override
634 public boolean isCustomErrorValidityState() {
635 return !StringUtils.isEmpty(customValidity_);
636 }
637
638 @Override
639 public boolean isValidValidityState() {
640 return !isCustomErrorValidityState()
641 && !isValueMissingValidityState();
642 }
643
644
645
646
647 @Override
648 public boolean isValueMissingValidityState() {
649 return ATTRIBUTE_NOT_DEFINED != getAttributeDirect(ATTRIBUTE_REQUIRED)
650 && getText().isEmpty();
651 }
652 }