• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hazendaz / httpunit / 389

12 Aug 2025 11:17PM UTC coverage: 80.48% (-0.02%) from 80.503%
389

push

github

hazendaz
Merge branch 'master' into javax

3216 of 4105 branches covered (78.34%)

Branch coverage included in aggregate %.

238 of 258 new or added lines in 68 files covered. (92.25%)

2 existing lines in 2 files now uncovered.

8254 of 10147 relevant lines covered (81.34%)

0.81 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.16
/src/main/java/com/meterware/httpunit/ParsedHTML.java
1
/*
2
 * MIT License
3
 *
4
 * Copyright 2011-2025 Russell Gold
5
 *
6
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
7
 * documentation files (the "Software"), to deal in the Software without restriction, including without limitation
8
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and
9
 * to permit persons to whom the Software is furnished to do so, subject to the following conditions:
10
 *
11
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions
12
 * of the Software.
13
 *
14
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
15
 * THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF
17
 * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
18
 * DEALINGS IN THE SOFTWARE.
19
 */
20
package com.meterware.httpunit;
21

22
import com.meterware.httpunit.dom.HTMLContainerElement;
23
import com.meterware.httpunit.dom.HTMLControl;
24
import com.meterware.httpunit.dom.HTMLDocumentImpl;
25
import com.meterware.httpunit.scripting.ScriptableDelegate;
26

27
import java.io.IOException;
28
import java.net.HttpURLConnection;
29
import java.net.URL;
30
import java.util.ArrayList;
31
import java.util.Arrays;
32
import java.util.HashMap;
33
import java.util.Iterator;
34
import java.util.List;
35
import java.util.StringTokenizer;
36

37
import org.w3c.dom.Document;
38
import org.w3c.dom.Element;
39
import org.w3c.dom.Node;
40
import org.w3c.dom.NodeList;
41
import org.w3c.dom.html.HTMLAppletElement;
42
import org.w3c.dom.html.HTMLCollection;
43
import org.w3c.dom.html.HTMLFormElement;
44
import org.w3c.dom.html.HTMLImageElement;
45
import org.w3c.dom.html.HTMLTableCellElement;
46
import org.w3c.dom.html.HTMLTableRowElement;
47

48
/**
49
 * @author <a href="mailto:russgold@httpunit.org">Russell Gold</a>
50
 * @author <a href="mailto:bx@bigfoot.com">Benoit Xhenseval</a>
51
 **/
52
public class ParsedHTML {
53

54
    static final private HTMLElement[] NO_ELEMENTS = {};
1✔
55

56
    static final private String[] TEXT_ELEMENTS = { "p", "h1", "h2", "h3", "h4", "h5", "h6" };
1✔
57

58
    private Node _rootNode;
59

60
    private URL _baseURL;
61

62
    private FrameSelector _frame;
63

64
    private String _baseTarget;
65

66
    private String _characterSet;
67

68
    private WebResponse _response;
69

70
    private boolean _updateElements = true;
1✔
71

72
    private boolean _enableNoScriptNodes;
73

74
    /** map of element IDs to elements. **/
75
    private HashMap _elementsByID = new HashMap<>();
1✔
76

77
    /** map of element names to lists of elements. **/
78
    private HashMap _elementsByName = new HashMap<>();
1✔
79

80
    /** map of element class to elements. **/
81
    private HashMap _elementsByClass = new HashMap<>();
1✔
82

83
    /** map of DOM elements to HTML elements **/
84
    private ElementRegistry _registry = new ElementRegistry();
1✔
85

86
    private ArrayList _blocksList = new ArrayList<>();
1✔
87
    private TextBlock[] _blocks;
88

89
    private ArrayList _tableList = new ArrayList<>();
1✔
90
    private WebTable[] _tables;
91

92
    private ArrayList _frameList = new ArrayList<>();
1✔
93
    private WebFrame[] _frames;
94

95
    ParsedHTML(WebResponse response, FrameSelector frame, URL baseURL, String baseTarget, Node rootNode,
96
            String characterSet) {
1✔
97
        _response = response;
1✔
98
        _frame = frame;
1✔
99
        _baseURL = baseURL;
1✔
100
        _baseTarget = baseTarget;
1✔
101
        _rootNode = rootNode;
1✔
102
        _characterSet = characterSet;
1✔
103
    }
1✔
104

105
    /**
106
     * Returns the forms found in the page in the order in which they appear.
107
     *
108
     * @return an array of objects representing the forms in the page or portion of a page.
109
     **/
110
    public WebForm[] getForms() {
111
        loadElements();
1✔
112
        HTMLCollection forms = ((HTMLContainerElement) _rootNode).getForms();
1✔
113
        WebForm[] result = new WebForm[forms.getLength()];
1✔
114
        for (int i = 0; i < result.length; i++) {
1✔
115
            result[i] = getWebForm(forms.item(i));
1✔
116
        }
117
        return result;
1✔
118
    }
119

120
    private WebForm getWebForm(Node node) {
121
        WebForm webForm = (WebForm) _registry.getRegisteredElement(node);
1✔
122
        return webForm != null ? webForm : (WebForm) _registry.registerElement(node, toWebForm((Element) node));
1!
123
    }
124

125
    /**
126
     * Returns the links found in the page in the order in which they appear.
127
     **/
128
    public WebLink[] getLinks() {
129
        loadElements();
1✔
130
        HTMLCollection links = ((HTMLContainerElement) _rootNode).getLinks();
1✔
131
        WebLink[] result = new WebLink[links.getLength()];
1✔
132
        for (int i = 0; i < result.length; i++) {
1✔
133
            result[i] = (WebLink) _registry.getRegisteredElement(links.item(i));
1✔
134
            if (result[i] == null) {
1!
135
                result[i] = new WebLink(_response, _baseURL, links.item(i), _frame, _baseTarget, _characterSet);
×
136
                _registry.registerElement(links.item(i), result[i]);
×
137
            }
138
        }
139
        return result;
1✔
140
    }
141

142
    /**
143
     * Returns a proxy for each applet found embedded in this page.
144
     */
145
    public WebApplet[] getApplets() {
146
        loadElements();
1✔
147
        HTMLCollection applets = ((HTMLContainerElement) _rootNode).getApplets();
1✔
148
        WebApplet[] result = new WebApplet[applets.getLength()];
1✔
149
        for (int i = 0; i < result.length; i++) {
1✔
150
            result[i] = (WebApplet) _registry.getRegisteredElement(applets.item(i));
1✔
151
            if (result[i] == null) {
1!
152
                result[i] = new WebApplet(_response, (HTMLAppletElement) applets.item(i), _baseTarget);
×
153
                _registry.registerElement(applets.item(i), result[i]);
×
154
            }
155
        }
156
        return result;
1✔
157
    }
158

159
    /**
160
     * Returns the images found in the page in the order in which they appear.
161
     */
162
    public WebImage[] getImages() {
163
        loadElements();
1✔
164
        HTMLCollection images = ((HTMLContainerElement) _rootNode).getImages();
1✔
165
        WebImage[] result = new WebImage[images.getLength()];
1✔
166
        for (int i = 0; i < result.length; i++) {
1✔
167
            result[i] = (WebImage) _registry.getRegisteredElement(images.item(i));
1✔
168
            if (result[i] == null) {
1!
169
                result[i] = new WebImage(_response, this, _baseURL, (HTMLImageElement) images.item(i), _frame,
×
170
                        _baseTarget, _characterSet);
171
                _registry.registerElement(images.item(i), result[i]);
×
172
            }
173
        }
174
        return result;
1✔
175
    }
176

177
    /**
178
     * Returns the top-level block elements found in the page in the order in which they appear.
179
     */
180
    public TextBlock[] getTextBlocks() {
181
        loadElements();
1✔
182
        if (_blocks == null) {
1✔
183
            loadElements();
1✔
184
            _blocks = (TextBlock[]) _blocksList.toArray(new TextBlock[_blocksList.size()]);
1✔
185
        }
186
        return _blocks;
1✔
187
    }
188

189
    /**
190
     * Returns the first text block found in the page which matches the specified predicate and value.
191
     */
192
    public TextBlock getFirstMatchingTextBlock(HTMLElementPredicate predicate, Object criteria) {
193
        loadElements();
1✔
194
        TextBlock[] blocks = getTextBlocks();
1✔
195
        for (TextBlock block : blocks) {
1!
196
            if (predicate.matchesCriteria(block, criteria)) {
1✔
197
                return block;
1✔
198
            }
199
        }
200
        return null;
×
201
    }
202

203
    /**
204
     * get the next text block based on the given block
205
     *
206
     * @param block
207
     *
208
     * @return - the next text block
209
     */
210
    public TextBlock getNextTextBlock(TextBlock block) {
211
        loadElements();
1✔
212
        int index = _blocksList.indexOf(block);
1✔
213
        if (index < 0 || index == _blocksList.size() - 1) {
1!
214
            return null;
×
215
        }
216
        return (TextBlock) _blocksList.get(index + 1);
1✔
217
    }
218

219
    /**
220
     * Returns the top-level tables found in the page in the order in which they appear.
221
     *
222
     * @return an array of tables
223
     **/
224
    public WebTable[] getTables() {
225
        loadElements();
1✔
226
        if (_tables == null) {
1✔
227
            _tables = (WebTable[]) _tableList.toArray(new WebTable[_tableList.size()]);
1✔
228
        }
229
        return _tables;
1✔
230
    }
231

232
    /**
233
     * Returns the HTMLElement with the specified ID.
234
     *
235
     * @param id
236
     *            - the id of the element to return
237
     *
238
     * @return the element looked for
239
     */
240
    public HTMLElement getElementWithID(String id) {
241
        return (HTMLElement) getElementWithID(id, HTMLElement.class);
1✔
242
    }
243

244
    /**
245
     * Returns the HTML elements with the specified name.
246
     */
247
    public HTMLElement[] getElementsWithName(String name) {
248
        loadElements();
1✔
249
        ArrayList elements = (ArrayList) _elementsByName.get(name);
1✔
250
        return elements == null ? NO_ELEMENTS : (HTMLElement[]) elements.toArray(new HTMLElement[elements.size()]);
1!
251
    }
252

253
    /**
254
     * Returns the HTML elements with the specified class.
255
     */
256
    public HTMLElement[] getElementsWithClassName(String className) {
257
        loadElements();
1✔
258
        ArrayList elements = (ArrayList) _elementsByClass.get(className);
1✔
259
        return elements == null ? NO_ELEMENTS : (HTMLElement[]) elements.toArray(new HTMLElement[elements.size()]);
1!
260
    }
261

262
    /**
263
     * Returns the HTML elements with an attribute with the specified name and value.
264
     *
265
     * @param name
266
     *            - the name of the attribute to check
267
     * @param value
268
     *            - the value of the attribute to check
269
     */
270
    public HTMLElement[] getElementsWithAttribute(String name, String value) {
271
        loadElements();
1✔
272
        ArrayList elements = new ArrayList<>();
1✔
273
        for (Iterator i = _registry.iterator(); i.hasNext();) {
1✔
274
            HTMLElement element = (HTMLElement) i.next();
1✔
275
            String aValue = element.getAttribute(name);
1✔
276
            if (value.equals(aValue)) {
1✔
277
                // System.err.println(element.getTagName()+"("+name+")="+aValue);
278
                elements.add(element);
1✔
279
            }
280
        }
1✔
281
        return (HTMLElement[]) elements.toArray(new HTMLElement[elements.size()]);
1✔
282
    }
283

284
    /**
285
     * Returns a list of HTML element names contained in this HTML section.
286
     */
287
    public String[] getElementNames() {
288
        loadElements();
1✔
289
        return (String[]) _elementsByName.keySet().toArray(new String[_elementsByName.size()]);
1✔
290
    }
291

292
    /**
293
     * get the elements with the given tagname
294
     *
295
     * @param dom
296
     *            - the node to start from
297
     * @param name
298
     *            - the name of the attribute to look for
299
     *
300
     * @return
301
     */
302
    HTMLElement[] getElementsByTagName(Node dom, String name) {
303
        loadElements();
1✔
304
        if (dom instanceof Element) {
1✔
305
            return getElementsFromList(((Element) dom).getElementsByTagName(name));
1✔
306
        }
307
        return getElementsFromList(((Document) dom).getElementsByTagName(name));
1✔
308
    }
309

310
    private HTMLElement[] getElementsFromList(NodeList nl) {
311
        HTMLElement[] elements = new HTMLElement[nl.getLength()];
1✔
312
        for (int i = 0; i < elements.length; i++) {
1✔
313
            Node node = nl.item(i);
1✔
314
            elements[i] = (HTMLElement) _registry.getRegisteredElement(node);
1✔
315
            if (elements[i] == null) {
1✔
316
                elements[i] = toDefaultElement((Element) node);
1✔
317
                _registry.registerElement(node, elements[i]);
1✔
318
            }
319
        }
320
        return elements;
1✔
321
    }
322

323
    /**
324
     * Returns the form found in the page with the specified ID.
325
     **/
326
    public WebForm getFormWithID(String id) {
327
        return (WebForm) getElementWithID(id, WebForm.class);
1✔
328
    }
329

330
    /**
331
     * Returns the link found in the page with the specified ID.
332
     **/
333
    public WebLink getLinkWithID(String id) {
334
        return (WebLink) getElementWithID(id, WebLink.class);
1✔
335

336
    }
337

338
    private Object getElementWithID(String id, final Class klass) {
339
        loadElements();
1✔
340
        return whenCast(_elementsByID.get(id), klass);
1✔
341
    }
342

343
    private Object whenCast(Object o, Class klass) {
344
        return klass.isInstance(o) ? o : null;
1✔
345
    }
346

347
    /**
348
     * Returns the first link found in the page matching the specified criteria.
349
     **/
350
    public WebForm getFirstMatchingForm(HTMLElementPredicate predicate, Object criteria) {
351
        WebForm[] forms = getForms();
1✔
352
        for (WebForm form : forms) {
1✔
353
            if (predicate.matchesCriteria(form, criteria)) {
1✔
354
                return form;
1✔
355
            }
356
        }
357
        return null;
1✔
358
    }
359

360
    /**
361
     * Returns all links found in the page matching the specified criteria.
362
     **/
363
    public WebForm[] getMatchingForms(HTMLElementPredicate predicate, Object criteria) {
NEW
364
        ArrayList matches = new ArrayList<>();
×
365
        WebForm[] forms = getForms();
×
366
        for (WebForm form : forms) {
×
367
            if (predicate.matchesCriteria(form, criteria)) {
×
368
                matches.add(form);
×
369
            }
370
        }
371
        return (WebForm[]) matches.toArray(new WebForm[matches.size()]);
×
372
    }
373

374
    /**
375
     * Returns the form found in the page with the specified name.
376
     **/
377
    public WebForm getFormWithName(String name) {
378
        return getFirstMatchingForm(WebForm.MATCH_NAME, name);
1✔
379
    }
380

381
    /**
382
     * interpret the given script element
383
     *
384
     * @param element
385
     */
386
    void interpretScriptElement(Element element) {
387
        if (!HttpUnitOptions.isScriptingEnabled()) {
1✔
388
            _enableNoScriptNodes = true;
1✔
389
            return;
1✔
390
        }
391

392
        String script = getScript(element);
1✔
393
        if (script != null) {
1!
394
            try {
395
                _updateElements = false;
1✔
396
                String language = NodeUtils.getNodeAttribute(element, "language", null);
1✔
397
                if (!getResponse().getScriptingHandler().supportsScriptLanguage(language)) {
1!
398
                    _enableNoScriptNodes = true;
1✔
399
                }
400
                getResponse().getScriptingHandler().runScript(language, script);
1✔
401
            } finally {
402
                clearCaches();
1✔
403
            }
404
        }
405
    }
1✔
406

407
    /**
408
     * get the script for the given node
409
     *
410
     * @param scriptNode
411
     *
412
     * @return the script
413
     */
414
    private String getScript(Node scriptNode) {
415
        String scriptLocation = NodeUtils.getNodeAttribute(scriptNode, "src", null);
1✔
416
        if (scriptLocation == null) {
1!
417
            return NodeUtils.asText(scriptNode.getChildNodes());
1✔
418
        }
419
        try {
420
            return getIncludedScript(scriptLocation);
×
421
        } catch (Exception e) {
×
422
            throw new RuntimeException("Error loading included script: " + e);
×
423
        }
424
    }
425

426
    /**
427
     * Returns the contents of an included script, given its src attribute.
428
     *
429
     * @param srcAttribute
430
     *            the location of the script.
431
     *
432
     * @return the contents of the script.
433
     *
434
     * @throws IOException
435
     *             if there is a problem retrieving the script
436
     */
437
    String getIncludedScript(String srcAttribute) throws IOException {
438
        WebRequest req = new GetMethodWebRequest(getBaseURL(), srcAttribute);
1✔
439
        WebWindow window = getResponse().getWindow();
1✔
440
        if (window == null) {
1!
441
            throw new IllegalStateException(
×
442
                    "Unable to retrieve script included by this response, since it was loaded by getResource(). Use getResponse() instead.");
443
        }
444
        WebResponse response = window.getResource(req);
1✔
445
        // check whether the Source is available
446
        int code = response.getResponseCode();
1✔
447
        // if everything is o.k.
448
        if (code <= HttpURLConnection.HTTP_BAD_REQUEST) {
1✔
449
            return response.getText();
1✔
450
        }
451
        // in this case the text would be an error message
452
        // we do not return it but set the
453
        ScriptException se = new ScriptException(
1✔
454
                "reponseCode " + code + " on getIncludedScript for src='" + srcAttribute + "'");
455
        String badScript = null;
1✔
456
        // let scripting engine decide what to do with this exception (throw it or remember it ...)
457
        HttpUnitOptions.getScriptingEngine().handleScriptException(se, badScript);
1✔
458
        return "";
1✔
459
    }
460

461
    /**
462
     * If noscript node content is enabled, returns null - otherwise returns a concealing element.
463
     */
464
    private HTMLElement toNoscriptElement(Element element) {
465
        HTMLElement result = null;
1✔
466
        if (!_enableNoScriptNodes) {
1✔
467
            result = new NoScriptElement(element);
1✔
468
        }
469
        return result;
1✔
470
    }
471

472
    static class HtmlElementRecorder {
1✔
473

474
        protected void recordHtmlElement(NodeUtils.PreOrderTraversal pot, Node node, HTMLElement htmlElement) {
475
            if (htmlElement != null) {
1✔
476
                addToMaps(pot, node, htmlElement);
1✔
477
                addToLists(pot, htmlElement);
1✔
478
            }
479
        }
1✔
480

481
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
482
            for (Iterator i = pot.getContexts(); i.hasNext();) {
1✔
483
                Object o = i.next();
1✔
484
                if (o instanceof ParsedHTML) {
1✔
485
                    ((ParsedHTML) o).addToList(htmlElement);
1✔
486
                }
487
            }
1✔
488
        }
1✔
489

490
        protected void addToMaps(NodeUtils.PreOrderTraversal pot, Node node, HTMLElement htmlElement) {
491
            for (Iterator i = pot.getContexts(); i.hasNext();) {
1✔
492
                Object o = i.next();
1✔
493
                if (o instanceof ParsedHTML) {
1✔
494
                    ((ParsedHTML) o).addToMaps(node, htmlElement);
1✔
495
                }
496
            }
1✔
497
        }
1✔
498

499
    }
500

501
    abstract static class HTMLElementFactory extends HtmlElementRecorder {
1✔
502
        abstract HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element);
503

504
        void recordElement(NodeUtils.PreOrderTraversal pot, Element element, ParsedHTML parsedHTML) {
505
            HTMLElement htmlElement = toHTMLElement(pot, parsedHTML, element);
1✔
506
            recordHtmlElement(pot, element, htmlElement);
1✔
507
        }
1✔
508

509
        protected boolean isRecognized(ClientProperties properties) {
510
            return true;
1✔
511
        }
512

513
        protected boolean addToContext() {
514
            return false;
1✔
515
        }
516

517
        protected final ParsedHTML getParsedHTML(NodeUtils.PreOrderTraversal pot) {
518
            return (ParsedHTML) getClosestContext(pot, ParsedHTML.class);
×
519
        }
520

521
        protected final Object getClosestContext(NodeUtils.PreOrderTraversal pot, Class aClass) {
522
            return pot.getClosestContext(aClass);
1✔
523
        }
524

525
        protected ParsedHTML getRootContext(NodeUtils.PreOrderTraversal pot) {
526
            return (ParsedHTML) pot.getRootContext();
×
527
        }
528
    }
529

530
    static class DefaultElementFactory extends HTMLElementFactory {
1✔
531

532
        @Override
533
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
534
            // [ 1531005 ] getElementsWithAttribute **FIX**
535
            // if (element.getAttribute( "id" ).equals( "" )) {
536
            // return null;
537
            // }
538
            return parsedHTML.toDefaultElement(element);
1✔
539
        }
540

541
        @Override
542
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
543
        }
1✔
544
    }
545

546
    private HTMLElement toDefaultElement(Element element) {
547
        return new HTMLElementBase(element) {
1✔
548
            @Override
549
            public ScriptableDelegate newScriptable() {
550
                return new HTMLElementScriptable(this);
1✔
551
            }
552

553
            @Override
554
            public ScriptableDelegate getParentDelegate() {
555
                return getResponse().getDocumentScriptable();
1✔
556
            }
557
        };
558
    }
559

560
    static class WebFormFactory extends HTMLElementFactory {
1✔
561
        @Override
562
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
563
            return parsedHTML.toWebForm(element);
1✔
564
        }
565
    }
566

567
    static class WebLinkFactory extends HTMLElementFactory {
1✔
568
        @Override
569
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
570
            return parsedHTML.toLinkAnchor(element);
1✔
571
        }
572
    }
573

574
    static class TextBlockFactory extends HTMLElementFactory {
1✔
575
        @Override
576
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
577
            return parsedHTML.toTextBlock(element);
1✔
578
        }
579

580
        @Override
581
        protected boolean addToContext() {
582
            return true;
1✔
583
        }
584

585
        @Override
586
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
587
            for (Iterator i = pot.getContexts(); i.hasNext();) {
1!
588
                Object o = i.next();
1✔
589
                if (!(o instanceof ParsedHTML)) {
1!
590
                    continue;
×
591
                }
592
                ((ParsedHTML) o).addToList(htmlElement);
1✔
593
                break;
1✔
594
            }
595
        }
1✔
596

597
    }
598

599
    static class ScriptFactory extends HTMLElementFactory {
1✔
600

601
        @Override
602
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
603
            return null;
×
604
        }
605

606
        @Override
607
        void recordElement(NodeUtils.PreOrderTraversal pot, Element element, ParsedHTML parsedHTML) {
608
            parsedHTML.interpretScriptElement(element);
1✔
609
        }
1✔
610
    }
611

612
    static class NoScriptFactory extends HTMLElementFactory {
1✔
613

614
        @Override
615
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
616
            return parsedHTML.toNoscriptElement(element);
1✔
617
        }
618

619
        @Override
620
        protected boolean addToContext() {
621
            return true;
1✔
622
        }
623
    }
624

625
    static class WebFrameFactory extends HTMLElementFactory {
1✔
626
        @Override
627
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
628
            return parsedHTML.toWebFrame(element);
1✔
629
        }
630
    }
631

632
    static class WebIFrameFactory extends HTMLElementFactory {
1✔
633
        @Override
634
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
635
            return parsedHTML.toWebIFrame(element);
1✔
636
        }
637

638
        @Override
639
        protected boolean isRecognized(ClientProperties properties) {
640
            return properties.isIframeSupported();
1✔
641
        }
642

643
        @Override
644
        protected boolean addToContext() {
645
            return true;
1✔
646
        }
647
    }
648

649
    static class WebImageFactory extends HTMLElementFactory {
1✔
650
        @Override
651
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
652
            return parsedHTML.toWebImage(element);
1✔
653
        }
654
    }
655

656
    static class WebAppletFactory extends HTMLElementFactory {
1✔
657
        @Override
658
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
659
            return parsedHTML.toWebApplet(element);
1✔
660
        }
661

662
        @Override
663
        protected boolean addToContext() {
664
            return true;
1✔
665
        }
666
    }
667

668
    static class WebTableFactory extends HTMLElementFactory {
1✔
669
        @Override
670
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
671
            return parsedHTML.toWebTable(element);
1✔
672
        }
673

674
        @Override
675
        protected boolean addToContext() {
676
            return true;
1✔
677
        }
678

679
        @Override
680
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
681
            for (Iterator i = pot.getContexts(); i.hasNext();) {
1✔
682
                Object o = i.next();
1✔
683
                if (o instanceof ParsedHTML) {
1!
684
                    ((ParsedHTML) o).addToList(htmlElement);
1✔
685
                }
686
                if (o instanceof TableCell) {
1✔
687
                    break;
1✔
688
                }
689
            }
1✔
690
        }
1✔
691
    }
692

693
    static class TableRowFactory extends HTMLElementFactory {
1✔
694
        @Override
695
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
696
            WebTable wt = getWebTable(pot);
1✔
697
            if (wt == null) {
1!
698
                return null;
×
699
            }
700
            return wt.newTableRow((HTMLTableRowElement) element);
1✔
701
        }
702

703
        private WebTable getWebTable(NodeUtils.PreOrderTraversal pot) {
704
            return (WebTable) getClosestContext(pot, WebTable.class);
1✔
705
        }
706

707
        @Override
708
        protected boolean addToContext() {
709
            return true;
1✔
710
        }
711

712
        @Override
713
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
714
            getWebTable(pot).addRow((TableRow) htmlElement);
1✔
715
        }
1✔
716
    }
717

718
    static class TableCellFactory extends HTMLElementFactory {
1✔
719
        @Override
720
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
721
            TableRow tr = getTableRow(pot);
1✔
722
            if (tr == null) {
1!
723
                return null;
×
724
            }
725
            return tr.newTableCell((HTMLTableCellElement) element);
1✔
726
        }
727

728
        private TableRow getTableRow(NodeUtils.PreOrderTraversal pot) {
729
            return (TableRow) getClosestContext(pot, TableRow.class);
1✔
730
        }
731

732
        @Override
733
        protected boolean addToContext() {
734
            return true;
1✔
735
        }
736

737
        @Override
738
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
739
            getTableRow(pot).addTableCell((TableCell) htmlElement);
1✔
740
        }
1✔
741
    }
742

743
    static class FormControlFactory extends HTMLElementFactory {
1✔
744

745
        @Override
746
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
747
            HTMLFormElement form = ((HTMLControl) element).getForm();
1✔
748
            if (form == null) {
1✔
749
                return newControlWithoutForm(parsedHTML, element);
1✔
750
            }
751
            return parsedHTML.getWebForm(form).newFormControl(element);
1✔
752
        }
753

754
        private HTMLElement newControlWithoutForm(ParsedHTML parsedHTML, Element element) {
755
            if ((element.getNodeName().equalsIgnoreCase("button") || element.getNodeName().equalsIgnoreCase("input"))
1!
756
                    && isValidNonFormButtonType(NodeUtils.getNodeAttribute(element, "type"))) {
1!
757
                return parsedHTML.toButtonWithoutForm(element);
1✔
758
            }
759
            return null;
×
760
        }
761

762
        private boolean isValidNonFormButtonType(String buttonType) {
763
            return buttonType.equals("") || buttonType.equalsIgnoreCase("button");
1!
764
        }
765
    }
766

767
    static class WebListFactory extends HTMLElementFactory {
1✔
768
        @Override
769
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
770
            return parsedHTML.toOrderedList(element);
×
771
        }
772

773
        @Override
774
        protected boolean addToContext() {
775
            return true;
×
776
        }
777

778
        @Override
779
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
780
            TextBlock textBlock = getTextBlock(pot);
×
781
            if (textBlock != null) {
×
782
                textBlock.addList((WebList) htmlElement);
×
783
            }
784
        }
×
785

786
        private TextBlock getTextBlock(NodeUtils.PreOrderTraversal pot) {
787
            return (TextBlock) getClosestContext(pot, TextBlock.class);
×
788
        }
789
    }
790

791
    static class ListItemFactory extends HTMLElementFactory {
1✔
792
        @Override
793
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
794
            WebList webList = getWebList(pot);
×
795
            if (webList == null) {
×
796
                return null;
×
797
            }
798
            return webList.addNewItem(element);
×
799
        }
800

801
        private WebList getWebList(NodeUtils.PreOrderTraversal pot) {
802
            return (WebList) getClosestContext(pot, WebList.class);
×
803
        }
804

805
        @Override
806
        protected boolean addToContext() {
807
            return true;
×
808
        }
809

810
        @Override
811
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
812
        }
×
813
    }
814

815
    private static HashMap _htmlFactoryClasses = new HashMap<>();
1✔
816
    private static HTMLElementFactory _defaultFactory = new DefaultElementFactory();
1✔
817

818
    static {
819
        _htmlFactoryClasses.put("a", new WebLinkFactory());
1✔
820
        _htmlFactoryClasses.put("area", new WebLinkFactory());
1✔
821
        _htmlFactoryClasses.put("form", new WebFormFactory());
1✔
822
        _htmlFactoryClasses.put("img", new WebImageFactory());
1✔
823
        _htmlFactoryClasses.put("applet", new WebAppletFactory());
1✔
824
        _htmlFactoryClasses.put("table", new WebTableFactory());
1✔
825
        _htmlFactoryClasses.put("tr", new TableRowFactory());
1✔
826
        _htmlFactoryClasses.put("td", new TableCellFactory());
1✔
827
        _htmlFactoryClasses.put("th", new TableCellFactory());
1✔
828
        _htmlFactoryClasses.put("frame", new WebFrameFactory());
1✔
829
        _htmlFactoryClasses.put("iframe", new WebIFrameFactory());
1✔
830
        _htmlFactoryClasses.put("script", new ScriptFactory());
1✔
831
        _htmlFactoryClasses.put("noscript", new NoScriptFactory());
1✔
832
        _htmlFactoryClasses.put("ol", new WebListFactory());
1✔
833
        _htmlFactoryClasses.put("ul", new WebListFactory());
1✔
834
        _htmlFactoryClasses.put("li", new ListItemFactory());
1✔
835

836
        for (String element : TEXT_ELEMENTS) {
1✔
837
            _htmlFactoryClasses.put(element, new TextBlockFactory());
1✔
838
        }
839

840
        for (Iterator i = Arrays.asList(FormControl.getControlElementTags()).iterator(); i.hasNext();) {
1✔
841
            _htmlFactoryClasses.put(i.next(), new FormControlFactory());
1✔
842
        }
843
    }
1✔
844

845
    private static HTMLElementFactory getHTMLElementFactory(String tagName) {
846
        final HTMLElementFactory factory = (HTMLElementFactory) _htmlFactoryClasses.get(tagName);
1✔
847
        return factory != null ? factory : _defaultFactory;
1✔
848
    }
849

850
    private void loadElements() {
851
        if (!_updateElements) {
1✔
852
            return;
1✔
853
        }
854

855
        NodeUtils.NodeAction action = new NodeUtils.NodeAction() {
1✔
856
            @Override
857
            public boolean processElement(NodeUtils.PreOrderTraversal pot, Element element) {
858
                HTMLElementFactory factory = getHTMLElementFactory(element.getNodeName().toLowerCase());
1✔
859
                if (factory == null || !factory.isRecognized(getClientProperties())
1!
860
                        || pot.getClosestContext(ContentConcealer.class) != null) {
1✔
861
                    return true;
1✔
862
                }
863

864
                if (!_registry.hasNode(element)) {
1✔
865
                    factory.recordElement(pot, element, ParsedHTML.this);
1✔
866
                }
867
                if (factory.addToContext()) {
1✔
868
                    pot.pushContext(_registry.getRegisteredElement(element));
1✔
869
                }
870

871
                return true;
1✔
872
            }
873

874
            @Override
875
            public void processTextNode(NodeUtils.PreOrderTraversal pot, Node textNode) {
876
                if (textNode.getNodeValue().trim().isEmpty()) {
1✔
877
                    return;
1✔
878
                }
879

880
                Node parent = textNode.getParentNode();
1✔
881
                if (!parent.getNodeName().equalsIgnoreCase("body")
1✔
882
                        || pot.getClosestContext(ContentConcealer.class) != null) {
1!
883
                    return;
1✔
884
                }
885
                new HtmlElementRecorder().recordHtmlElement(pot, textNode, newTextBlock(textNode));
1✔
886
            }
1✔
887
        };
888
        NodeUtils.PreOrderTraversal nt = new NodeUtils.PreOrderTraversal(getRootNode());
1✔
889
        nt.pushBaseContext(this);
1✔
890
        nt.perform(action);
1✔
891

892
        _updateElements = false;
1✔
893
    }
1✔
894

895
    private ClientProperties getClientProperties() {
896
        WebWindow window = _response.getWindow();
1✔
897
        return window == null ? ClientProperties.getDefaultProperties() : window.getClient().getClientProperties();
1✔
898
    }
899

900
    private Button toButtonWithoutForm(Element element) {
901
        return new Button(_response, (HTMLControl) element);
1✔
902
    }
903

904
    private WebForm toWebForm(Element element) {
905
        return new WebForm(_response, _baseURL, element, _frame, _baseTarget, _characterSet, _registry);
1✔
906
    }
907

908
    private WebFrame toWebFrame(Element element) {
909
        return new WebFrame(_response, _baseURL, element, _frame);
1✔
910
    }
911

912
    private WebFrame toWebIFrame(Element element) {
913
        return new WebIFrame(_baseURL, element, _frame);
1✔
914
    }
915

916
    /**
917
     * convert the given child to a link anchor
918
     *
919
     * @param child
920
     *
921
     * @return
922
     */
923
    private WebLink toLinkAnchor(Element child) {
924
        return !isWebLink(child) ? null : new WebLink(_response, _baseURL, child, _frame, _baseTarget, _characterSet);
1✔
925
    }
926

927
    /**
928
     * check whether the given node is a Web link by checking that the node is of type "A"
929
     *
930
     * @param node
931
     *            - the node to check
932
     *
933
     * @return whether the given node represents a web link
934
     */
935
    public static boolean isWebLink(Node node) {
936
        /*
937
         * Bug report [ 1156972 ] isWebLink doesn't recognize all anchor tags by fregienj claims this be changed to
938
         * check whether the case-insensitive node name is "A" -> should be isAnchor method and getAnchor
939
         */
940
        boolean result = false;
1✔
941
        String tagName = ((Element) node).getTagName();
1✔
942
        if (!tagName.equalsIgnoreCase("area") && !tagName.equalsIgnoreCase("a")) {
1✔
943
        } else {
944
            // pre 1.7 code - still active
945
            result = node.getAttributes().getNamedItem("href") != null;
1✔
946
            // proposed patch - not activated
947
            // result=true;
948
        }
949
        return result;
1✔
950
    }
951

952
    private WebImage toWebImage(Element child) {
953
        return new WebImage(_response, this, _baseURL, (HTMLImageElement) child, _frame, _baseTarget, _characterSet);
1✔
954
    }
955

956
    private WebApplet toWebApplet(Element element) {
957
        return new WebApplet(_response, (HTMLAppletElement) element, _baseTarget);
1✔
958
    }
959

960
    private WebTable toWebTable(Element element) {
961
        return new WebTable(_response, _frame, element, _baseURL, _baseTarget, _characterSet);
1✔
962
    }
963

964
    private TextBlock toTextBlock(Element element) {
965
        return new TextBlock(_response, _frame, _baseURL, _baseTarget, element, _characterSet);
1✔
966
    }
967

968
    private TextBlock newTextBlock(Node textNode) {
969
        return new TextBlock(_response, _frame, _baseURL, _baseTarget, textNode, _characterSet);
1✔
970
    }
971

972
    private WebList toOrderedList(Element element) {
973
        return new WebList(_response, _frame, _baseURL, _baseTarget, element, _characterSet);
×
974
    }
975

976
    private void addToMaps(Node node, HTMLElement htmlElement) {
977
        _registry.registerElement(node, htmlElement);
1✔
978
        if (htmlElement.getID() != null) {
1!
979
            _elementsByID.put(htmlElement.getID(), htmlElement);
1✔
980
        }
981
        if (htmlElement.getName() != null) {
1✔
982
            addNamedElement(htmlElement.getName(), htmlElement);
1✔
983
        }
984
        if (htmlElement.getClassName() != null) {
1!
985
            StringTokenizer tokenizer = new StringTokenizer(htmlElement.getClassName());
1✔
986
            String token;
987

988
            while (tokenizer.hasMoreElements()) {
1✔
989
                token = tokenizer.nextToken();
1✔
990

991
                if (_elementsByClass.containsKey(token)) {
1✔
992
                    ((ArrayList) _elementsByClass.get(token)).add(htmlElement);
1✔
993
                } else {
994
                    ArrayList arrayList = new ArrayList<>();
1✔
995
                    arrayList.add(htmlElement);
1✔
996
                    _elementsByClass.put(token, arrayList);
1✔
997
                }
1✔
998
            }
999
        }
1000
    }
1✔
1001

1002
    private void addNamedElement(String name, HTMLElement htmlElement) {
1003
        List list = (List) _elementsByName.get(name);
1✔
1004
        if (list == null) {
1✔
1005
            _elementsByName.put(name, list = new ArrayList<>());
1✔
1006
        }
1007
        list.add(htmlElement);
1✔
1008
    }
1✔
1009

1010
    private void addToList(HTMLElement htmlElement) {
1011
        ArrayList list = getListForElement(htmlElement);
1✔
1012
        if (list != null) {
1✔
1013
            list.add(htmlElement);
1✔
1014
        }
1015
    }
1✔
1016

1017
    private ArrayList getListForElement(HTMLElement element) {
1018
        if (element instanceof WebTable) {
1✔
1019
            return _tableList;
1✔
1020
        }
1021
        if (element instanceof WebFrame) {
1✔
1022
            return _frameList;
1✔
1023
        }
1024
        if (element instanceof BlockElement) {
1✔
1025
            return _blocksList;
1✔
1026
        }
1027
        return null;
1✔
1028
    }
1029

1030
    /**
1031
     * Returns the first link which contains the specified text.
1032
     **/
1033
    public WebLink getLinkWith(String text) {
1034
        return getFirstMatchingLink(WebLink.MATCH_CONTAINED_TEXT, text);
1✔
1035
    }
1036

1037
    /**
1038
     * Returns the link which contains the first image with the specified text as its 'alt' attribute.
1039
     **/
1040
    public WebLink getLinkWithImageText(String text) {
1041
        WebImage image = getImageWithAltText(text);
1✔
1042
        return image == null ? null : image.getLink();
1!
1043
    }
1044

1045
    /**
1046
     * Returns the link found in the page with the specified name.
1047
     **/
1048
    public WebLink getLinkWithName(String name) {
1049
        return getFirstMatchingLink(WebLink.MATCH_NAME, name);
1✔
1050
    }
1051

1052
    /**
1053
     * Returns the first link found in the page matching the specified criteria.
1054
     **/
1055
    public WebLink getFirstMatchingLink(HTMLElementPredicate predicate, Object criteria) {
1056
        WebLink[] links = getLinks();
1✔
1057
        for (WebLink link : links) {
1✔
1058
            if (predicate.matchesCriteria(link, criteria)) {
1✔
1059
                return link;
1✔
1060
            }
1061
        }
1062
        return null;
1✔
1063
    }
1064

1065
    /**
1066
     * Returns all links found in the page matching the specified criteria.
1067
     **/
1068
    public WebLink[] getMatchingLinks(HTMLElementPredicate predicate, Object criteria) {
1069
        ArrayList matches = new ArrayList<>();
1✔
1070
        WebLink[] links = getLinks();
1✔
1071
        for (WebLink link : links) {
1✔
1072
            if (predicate.matchesCriteria(link, criteria)) {
1✔
1073
                matches.add(link);
1✔
1074
            }
1075
        }
1076
        return (WebLink[]) matches.toArray(new WebLink[matches.size()]);
1✔
1077
    }
1078

1079
    /**
1080
     * Returns the image found in the page with the specified name.
1081
     **/
1082
    public WebImage getImageWithName(String name) {
1083
        WebImage[] images = getImages();
1✔
1084
        for (WebImage image : images) {
1!
1085
            if (HttpUnitUtils.matches(name, image.getName())) {
1!
1086
                return image;
1✔
1087
            }
1088
        }
1089
        return null;
×
1090
    }
1091

1092
    /**
1093
     * Returns the first image found in the page with the specified src attribute.
1094
     **/
1095
    public WebImage getImageWithSource(String source) {
1096
        WebImage[] images = getImages();
1✔
1097
        for (WebImage image : images) {
1✔
1098
            if (HttpUnitUtils.matches(source, image.getSource())) {
1✔
1099
                return image;
1✔
1100
            }
1101
        }
1102
        return null;
1✔
1103
    }
1104

1105
    /**
1106
     * Returns the first image found in the page with the specified alt attribute.
1107
     **/
1108
    public WebImage getImageWithAltText(String altText) {
1109
        WebImage[] images = getImages();
1✔
1110
        for (WebImage image : images) {
1!
1111
            if (HttpUnitUtils.matches(altText, image.getAltText())) {
1✔
1112
                return image;
1✔
1113
            }
1114
        }
1115
        return null;
×
1116
    }
1117

1118
    /**
1119
     * Returns the first table in the response which matches the specified predicate and value. Will recurse into any
1120
     * nested tables, as needed.
1121
     *
1122
     * @return the selected table, or null if none is found
1123
     **/
1124
    public WebTable getFirstMatchingTable(HTMLElementPredicate predicate, Object criteria) {
1125
        return getTableSatisfyingPredicate(getTables(), predicate, criteria);
1✔
1126
    }
1127

1128
    /**
1129
     * Returns the tables in the response which match the specified predicate and value. Will recurse into any nested
1130
     * tables, as needed.
1131
     *
1132
     * @return the selected tables, or null if none are found
1133
     **/
1134
    public WebTable[] getMatchingTables(HTMLElementPredicate predicate, Object criteria) {
1135
        return getTablesSatisfyingPredicate(getTables(), predicate, criteria);
×
1136
    }
1137

1138
    /**
1139
     * Returns the first table in the response which has the specified text as the full text of its first non-blank row
1140
     * and non-blank column. Will recurse into any nested tables, as needed.
1141
     *
1142
     * @return the selected table, or null if none is found
1143
     **/
1144
    public WebTable getTableStartingWith(String text) {
1145
        return getFirstMatchingTable(WebTable.MATCH_FIRST_NONBLANK_CELL, text);
1✔
1146
    }
1147

1148
    /**
1149
     * Returns the first table in the response which has the specified text as a prefix of the text in its first
1150
     * non-blank row and non-blank column. Will recurse into any nested tables, as needed.
1151
     *
1152
     * @return the selected table, or null if none is found
1153
     **/
1154
    public WebTable getTableStartingWithPrefix(String text) {
1155
        return getFirstMatchingTable(WebTable.MATCH_FIRST_NONBLANK_CELL_PREFIX, text);
1✔
1156
    }
1157

1158
    /**
1159
     * Returns the first table in the response which has the specified text as its summary attribute. Will recurse into
1160
     * any nested tables, as needed.
1161
     *
1162
     * @return the selected table, or null if none is found
1163
     **/
1164
    public WebTable getTableWithSummary(String summary) {
1165
        return getFirstMatchingTable(WebTable.MATCH_SUMMARY, summary);
1✔
1166
    }
1167

1168
    /**
1169
     * Returns the first table in the response which has the specified text as its ID attribute. Will recurse into any
1170
     * nested tables, as needed.
1171
     *
1172
     * @return the selected table, or null if none is found
1173
     **/
1174
    public WebTable getTableWithID(String ID) {
1175
        return getFirstMatchingTable(WebTable.MATCH_ID, ID);
1✔
1176
    }
1177

1178
    /**
1179
     * Returns a copy of the domain object model associated with this page.
1180
     **/
1181
    public Node getDOM() {
1182
        return getRootNode().cloneNode( /* deep */ true);
1✔
1183
    }
1184

1185
    // ---------------------------------- Object methods --------------------------------
1186

1187
    @Override
1188
    public String toString() {
1189
        return _baseURL.toExternalForm() + System.lineSeparator() + _rootNode;
×
1190
    }
1191

1192
    // ---------------------------------- package members --------------------------------
1193

1194
    /**
1195
     * Specifies the root node for this HTML fragment. It will be either an HTMLDocument or an HTMLElement representing
1196
     * a page fragment.
1197
     */
1198
    void setRootNode(Node rootNode) {
1199
        if (_rootNode != null && rootNode != _rootNode) {
1!
1200
            throw new IllegalStateException("The root node has already been defined as " + _rootNode
×
1201
                    + " and cannot be redefined as " + rootNode);
1202
        }
1203
        _rootNode = rootNode;
1✔
1204
        if (rootNode instanceof HTMLDocumentImpl) {
1!
1205
            ((HTMLDocumentImpl) rootNode).setIFramesEnabled(getClientProperties().isIframeSupported());
1✔
1206
        }
1207
        clearCaches();
1✔
1208
    }
1✔
1209

1210
    private void clearCaches() {
1211
        _tables = null;
1✔
1212
        _frames = null;
1✔
1213
        _blocks = null;
1✔
1214
        _updateElements = true;
1✔
1215
    }
1✔
1216

1217
    /**
1218
     * Returns the base URL for this HTML segment.
1219
     **/
1220
    URL getBaseURL() {
1221
        return _baseURL;
1✔
1222
    }
1223

1224
    WebResponse getResponse() {
1225
        return _response;
1✔
1226
    }
1227

1228
    /**
1229
     * Returns the domain object model associated with this page, to be used internally.
1230
     **/
1231
    Node getOriginalDOM() {
1232
        return getRootNode();
1✔
1233
    }
1234

1235
    HTMLElement getElement(Node node) {
1236
        return (HTMLElement) _registry.getRegisteredElement(node);
1✔
1237
    }
1238

1239
    /**
1240
     * Returns the frames found in the page in the order in which they appear.
1241
     **/
1242
    public WebFrame[] getFrames() {
1243
        if (_frames == null) {
1✔
1244
            loadElements();
1✔
1245
            _frames = (WebFrame[]) _frameList.toArray(new WebFrame[_frameList.size()]);
1✔
1246
        }
1247
        return _frames;
1✔
1248
    }
1249

1250
    // ---------------------------------- private members --------------------------------
1251

1252
    Node getRootNode() {
1253
        if (_rootNode == null) {
1!
1254
            throw new IllegalStateException("The root node has not been specified");
×
1255
        }
1256
        return _rootNode;
1✔
1257
    }
1258

1259
    /**
1260
     * Returns the table with the specified text in its summary attribute.
1261
     **/
1262
    private WebTable getTableSatisfyingPredicate(WebTable[] tables, HTMLElementPredicate predicate, Object value) {
1263
        for (WebTable table : tables) {
1✔
1264
            if (predicate.matchesCriteria(table, value)) {
1✔
1265
                return table;
1✔
1266
            }
1267
            for (int j = 0; j < table.getRowCount(); j++) {
1✔
1268
                for (int k = 0; k < table.getColumnCount(); k++) {
1✔
1269
                    TableCell cell = table.getTableCell(j, k);
1✔
1270
                    if (cell != null) {
1!
1271
                        WebTable[] innerTables = cell.getTables();
1✔
1272
                        if (innerTables.length != 0) {
1✔
1273
                            WebTable result = getTableSatisfyingPredicate(innerTables, predicate, value);
1✔
1274
                            if (result != null) {
1✔
1275
                                return result;
1✔
1276
                            }
1277
                        }
1278
                    }
1279
                }
1280
            }
1281
        }
1282
        return null;
1✔
1283
    }
1284

1285
    /**
1286
     * Returns the tables which match the specified criteria.
1287
     **/
1288
    private WebTable[] getTablesSatisfyingPredicate(WebTable[] tables, HTMLElementPredicate predicate, Object value) {
NEW
1289
        ArrayList matches = new ArrayList<>();
×
1290
        for (WebTable table : tables) {
×
1291
            if (predicate.matchesCriteria(table, value)) {
×
1292
                matches.add(table);
×
1293
            }
1294
            for (int j = 0; j < table.getRowCount(); j++) {
×
1295
                for (int k = 0; k < table.getColumnCount(); k++) {
×
1296
                    TableCell cell = table.getTableCell(j, k);
×
1297
                    if (cell != null) {
×
1298
                        WebTable[] innerTables = cell.getTables();
×
1299
                        if (innerTables.length != 0) {
×
1300
                            WebTable[] result = getTablesSatisfyingPredicate(innerTables, predicate, value);
×
1301
                            if (result != null && result.length > 0) {
×
1302
                                matches.addAll(Arrays.asList(result));
×
1303
                            }
1304
                        }
1305
                    }
1306
                }
1307
            }
1308
        }
1309
        if (matches.size() > 0) {
×
1310
            return (WebTable[]) matches.toArray(new WebTable[matches.size()]);
×
1311
        }
1312
        return null;
×
1313
    }
1314

1315
    class WebIFrame extends WebFrame implements ContentConcealer {
1316

1317
        public WebIFrame(URL baseURL, Node frameNode, FrameSelector parentFrame) {
1✔
1318
            super(_response, baseURL, frameNode, parentFrame);
1✔
1319
        }
1✔
1320
    }
1321

1322
    /**
1323
     * NoScript Element
1324
     */
1325
    class NoScriptElement extends HTMLElementBase implements ContentConcealer {
1326

1327
        public NoScriptElement(Node node) {
1✔
1328
            super(node);
1✔
1329
        }
1✔
1330

1331
        @Override
1332
        public ScriptableDelegate newScriptable() {
1333
            return null;
×
1334
        }
1335

1336
        @Override
1337
        public ScriptableDelegate getParentDelegate() {
1338
            return null;
×
1339
        }
1340
    }
1341

1342
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc