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

hazendaz / httpunit / 636

05 Dec 2025 03:27AM UTC coverage: 80.509%. Remained the same
636

push

github

hazendaz
Cleanup more old since tags

you guessed it, at this point going to jautodoc the rest so the warnings on builds go away ;)

3213 of 4105 branches covered (78.27%)

Branch coverage included in aggregate %.

8249 of 10132 relevant lines covered (81.42%)

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
public class ParsedHTML {
49

50
    static final private HTMLElement[] NO_ELEMENTS = {};
1✔
51

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

54
    private Node _rootNode;
55

56
    private URL _baseURL;
57

58
    private FrameSelector _frame;
59

60
    private String _baseTarget;
61

62
    private String _characterSet;
63

64
    private WebResponse _response;
65

66
    private boolean _updateElements = true;
1✔
67

68
    private boolean _enableNoScriptNodes;
69

70
    /** map of element IDs to elements. **/
71
    private HashMap _elementsByID = new HashMap<>();
1✔
72

73
    /** map of element names to lists of elements. **/
74
    private HashMap _elementsByName = new HashMap<>();
1✔
75

76
    /** map of element class to elements. **/
77
    private HashMap _elementsByClass = new HashMap<>();
1✔
78

79
    /** map of DOM elements to HTML elements **/
80
    private ElementRegistry _registry = new ElementRegistry();
1✔
81

82
    private ArrayList _blocksList = new ArrayList<>();
1✔
83
    private TextBlock[] _blocks;
84

85
    private ArrayList _tableList = new ArrayList<>();
1✔
86
    private WebTable[] _tables;
87

88
    private ArrayList _frameList = new ArrayList<>();
1✔
89
    private WebFrame[] _frames;
90

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

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

116
    private WebForm getWebForm(Node node) {
117
        WebForm webForm = (WebForm) _registry.getRegisteredElement(node);
1✔
118
        return webForm != null ? webForm : (WebForm) _registry.registerElement(node, toWebForm((Element) node));
1!
119
    }
120

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

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

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

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

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

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

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

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

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

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

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

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

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

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

319
    /**
320
     * Returns the form found in the page with the specified ID.
321
     **/
322
    public WebForm getFormWithID(String id) {
323
        return (WebForm) getElementWithID(id, WebForm.class);
1✔
324
    }
325

326
    /**
327
     * Returns the link found in the page with the specified ID.
328
     **/
329
    public WebLink getLinkWithID(String id) {
330
        return (WebLink) getElementWithID(id, WebLink.class);
1✔
331

332
    }
333

334
    private Object getElementWithID(String id, final Class klass) {
335
        loadElements();
1✔
336
        return whenCast(_elementsByID.get(id), klass);
1✔
337
    }
338

339
    private Object whenCast(Object o, Class klass) {
340
        return klass.isInstance(o) ? o : null;
1✔
341
    }
342

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

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

370
    /**
371
     * Returns the form found in the page with the specified name.
372
     **/
373
    public WebForm getFormWithName(String name) {
374
        return getFirstMatchingForm(WebForm.MATCH_NAME, name);
1✔
375
    }
376

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

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

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

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

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

468
    static class HtmlElementRecorder {
1✔
469

470
        protected void recordHtmlElement(NodeUtils.PreOrderTraversal pot, Node node, HTMLElement htmlElement) {
471
            if (htmlElement != null) {
1✔
472
                addToMaps(pot, node, htmlElement);
1✔
473
                addToLists(pot, htmlElement);
1✔
474
            }
475
        }
1✔
476

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

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

495
    }
496

497
    abstract static class HTMLElementFactory extends HtmlElementRecorder {
1✔
498
        abstract HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element);
499

500
        void recordElement(NodeUtils.PreOrderTraversal pot, Element element, ParsedHTML parsedHTML) {
501
            HTMLElement htmlElement = toHTMLElement(pot, parsedHTML, element);
1✔
502
            recordHtmlElement(pot, element, htmlElement);
1✔
503
        }
1✔
504

505
        protected boolean isRecognized(ClientProperties properties) {
506
            return true;
1✔
507
        }
508

509
        protected boolean addToContext() {
510
            return false;
1✔
511
        }
512

513
        protected final ParsedHTML getParsedHTML(NodeUtils.PreOrderTraversal pot) {
514
            return (ParsedHTML) getClosestContext(pot, ParsedHTML.class);
×
515
        }
516

517
        protected final Object getClosestContext(NodeUtils.PreOrderTraversal pot, Class aClass) {
518
            return pot.getClosestContext(aClass);
1✔
519
        }
520

521
        protected ParsedHTML getRootContext(NodeUtils.PreOrderTraversal pot) {
522
            return (ParsedHTML) pot.getRootContext();
×
523
        }
524
    }
525

526
    static class DefaultElementFactory extends HTMLElementFactory {
1✔
527

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

537
        @Override
538
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
539
        }
1✔
540
    }
541

542
    private HTMLElement toDefaultElement(Element element) {
543
        return new HTMLElementBase(element) {
1✔
544
            @Override
545
            public ScriptableDelegate newScriptable() {
546
                return new HTMLElementScriptable(this);
1✔
547
            }
548

549
            @Override
550
            public ScriptableDelegate getParentDelegate() {
551
                return getResponse().getDocumentScriptable();
1✔
552
            }
553
        };
554
    }
555

556
    static class WebFormFactory extends HTMLElementFactory {
1✔
557
        @Override
558
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
559
            return parsedHTML.toWebForm(element);
1✔
560
        }
561
    }
562

563
    static class WebLinkFactory extends HTMLElementFactory {
1✔
564
        @Override
565
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
566
            return parsedHTML.toLinkAnchor(element);
1✔
567
        }
568
    }
569

570
    static class TextBlockFactory extends HTMLElementFactory {
1✔
571
        @Override
572
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
573
            return parsedHTML.toTextBlock(element);
1✔
574
        }
575

576
        @Override
577
        protected boolean addToContext() {
578
            return true;
1✔
579
        }
580

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

593
    }
594

595
    static class ScriptFactory extends HTMLElementFactory {
1✔
596

597
        @Override
598
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
599
            return null;
×
600
        }
601

602
        @Override
603
        void recordElement(NodeUtils.PreOrderTraversal pot, Element element, ParsedHTML parsedHTML) {
604
            parsedHTML.interpretScriptElement(element);
1✔
605
        }
1✔
606
    }
607

608
    static class NoScriptFactory extends HTMLElementFactory {
1✔
609

610
        @Override
611
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
612
            return parsedHTML.toNoscriptElement(element);
1✔
613
        }
614

615
        @Override
616
        protected boolean addToContext() {
617
            return true;
1✔
618
        }
619
    }
620

621
    static class WebFrameFactory extends HTMLElementFactory {
1✔
622
        @Override
623
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
624
            return parsedHTML.toWebFrame(element);
1✔
625
        }
626
    }
627

628
    static class WebIFrameFactory extends HTMLElementFactory {
1✔
629
        @Override
630
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
631
            return parsedHTML.toWebIFrame(element);
1✔
632
        }
633

634
        @Override
635
        protected boolean isRecognized(ClientProperties properties) {
636
            return properties.isIframeSupported();
1✔
637
        }
638

639
        @Override
640
        protected boolean addToContext() {
641
            return true;
1✔
642
        }
643
    }
644

645
    static class WebImageFactory extends HTMLElementFactory {
1✔
646
        @Override
647
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
648
            return parsedHTML.toWebImage(element);
1✔
649
        }
650
    }
651

652
    static class WebAppletFactory extends HTMLElementFactory {
1✔
653
        @Override
654
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
655
            return parsedHTML.toWebApplet(element);
1✔
656
        }
657

658
        @Override
659
        protected boolean addToContext() {
660
            return true;
1✔
661
        }
662
    }
663

664
    static class WebTableFactory extends HTMLElementFactory {
1✔
665
        @Override
666
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
667
            return parsedHTML.toWebTable(element);
1✔
668
        }
669

670
        @Override
671
        protected boolean addToContext() {
672
            return true;
1✔
673
        }
674

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

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

699
        private WebTable getWebTable(NodeUtils.PreOrderTraversal pot) {
700
            return (WebTable) getClosestContext(pot, WebTable.class);
1✔
701
        }
702

703
        @Override
704
        protected boolean addToContext() {
705
            return true;
1✔
706
        }
707

708
        @Override
709
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
710
            getWebTable(pot).addRow((TableRow) htmlElement);
1✔
711
        }
1✔
712
    }
713

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

724
        private TableRow getTableRow(NodeUtils.PreOrderTraversal pot) {
725
            return (TableRow) getClosestContext(pot, TableRow.class);
1✔
726
        }
727

728
        @Override
729
        protected boolean addToContext() {
730
            return true;
1✔
731
        }
732

733
        @Override
734
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
735
            getTableRow(pot).addTableCell((TableCell) htmlElement);
1✔
736
        }
1✔
737
    }
738

739
    static class FormControlFactory extends HTMLElementFactory {
1✔
740

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

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

758
        private boolean isValidNonFormButtonType(String buttonType) {
759
            return buttonType.equals("") || buttonType.equalsIgnoreCase("button");
1!
760
        }
761
    }
762

763
    static class WebListFactory extends HTMLElementFactory {
1✔
764
        @Override
765
        HTMLElement toHTMLElement(NodeUtils.PreOrderTraversal pot, ParsedHTML parsedHTML, Element element) {
766
            return parsedHTML.toOrderedList(element);
×
767
        }
768

769
        @Override
770
        protected boolean addToContext() {
771
            return true;
×
772
        }
773

774
        @Override
775
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
776
            TextBlock textBlock = getTextBlock(pot);
×
777
            if (textBlock != null) {
×
778
                textBlock.addList((WebList) htmlElement);
×
779
            }
780
        }
×
781

782
        private TextBlock getTextBlock(NodeUtils.PreOrderTraversal pot) {
783
            return (TextBlock) getClosestContext(pot, TextBlock.class);
×
784
        }
785
    }
786

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

797
        private WebList getWebList(NodeUtils.PreOrderTraversal pot) {
798
            return (WebList) getClosestContext(pot, WebList.class);
×
799
        }
800

801
        @Override
802
        protected boolean addToContext() {
803
            return true;
×
804
        }
805

806
        @Override
807
        protected void addToLists(NodeUtils.PreOrderTraversal pot, HTMLElement htmlElement) {
808
        }
×
809
    }
810

811
    private static HashMap _htmlFactoryClasses = new HashMap<>();
1✔
812
    private static HTMLElementFactory _defaultFactory = new DefaultElementFactory();
1✔
813

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

832
        for (String element : TEXT_ELEMENTS) {
1✔
833
            _htmlFactoryClasses.put(element, new TextBlockFactory());
1✔
834
        }
835

836
        for (Iterator i = Arrays.asList(FormControl.getControlElementTags()).iterator(); i.hasNext();) {
1✔
837
            _htmlFactoryClasses.put(i.next(), new FormControlFactory());
1✔
838
        }
839
    }
1✔
840

841
    private static HTMLElementFactory getHTMLElementFactory(String tagName) {
842
        final HTMLElementFactory factory = (HTMLElementFactory) _htmlFactoryClasses.get(tagName);
1✔
843
        return factory != null ? factory : _defaultFactory;
1✔
844
    }
845

846
    private void loadElements() {
847
        if (!_updateElements) {
1✔
848
            return;
1✔
849
        }
850

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

860
                if (!_registry.hasNode(element)) {
1✔
861
                    factory.recordElement(pot, element, ParsedHTML.this);
1✔
862
                }
863
                if (factory.addToContext()) {
1✔
864
                    pot.pushContext(_registry.getRegisteredElement(element));
1✔
865
                }
866

867
                return true;
1✔
868
            }
869

870
            @Override
871
            public void processTextNode(NodeUtils.PreOrderTraversal pot, Node textNode) {
872
                if (textNode.getNodeValue().trim().isEmpty()) {
1✔
873
                    return;
1✔
874
                }
875

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

888
        _updateElements = false;
1✔
889
    }
1✔
890

891
    private ClientProperties getClientProperties() {
892
        WebWindow window = _response.getWindow();
1✔
893
        return window == null ? ClientProperties.getDefaultProperties() : window.getClient().getClientProperties();
1✔
894
    }
895

896
    private Button toButtonWithoutForm(Element element) {
897
        return new Button(_response, (HTMLControl) element);
1✔
898
    }
899

900
    private WebForm toWebForm(Element element) {
901
        return new WebForm(_response, _baseURL, element, _frame, _baseTarget, _characterSet, _registry);
1✔
902
    }
903

904
    private WebFrame toWebFrame(Element element) {
905
        return new WebFrame(_response, _baseURL, element, _frame);
1✔
906
    }
907

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

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

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

948
    private WebImage toWebImage(Element child) {
949
        return new WebImage(_response, this, _baseURL, (HTMLImageElement) child, _frame, _baseTarget, _characterSet);
1✔
950
    }
951

952
    private WebApplet toWebApplet(Element element) {
953
        return new WebApplet(_response, (HTMLAppletElement) element, _baseTarget);
1✔
954
    }
955

956
    private WebTable toWebTable(Element element) {
957
        return new WebTable(_response, _frame, element, _baseURL, _baseTarget, _characterSet);
1✔
958
    }
959

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

964
    private TextBlock newTextBlock(Node textNode) {
965
        return new TextBlock(_response, _frame, _baseURL, _baseTarget, textNode, _characterSet);
1✔
966
    }
967

968
    private WebList toOrderedList(Element element) {
969
        return new WebList(_response, _frame, _baseURL, _baseTarget, element, _characterSet);
×
970
    }
971

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

984
            while (tokenizer.hasMoreElements()) {
1✔
985
                token = tokenizer.nextToken();
1✔
986

987
                if (_elementsByClass.containsKey(token)) {
1✔
988
                    ((ArrayList) _elementsByClass.get(token)).add(htmlElement);
1✔
989
                } else {
990
                    ArrayList arrayList = new ArrayList<>();
1✔
991
                    arrayList.add(htmlElement);
1✔
992
                    _elementsByClass.put(token, arrayList);
1✔
993
                }
1✔
994
            }
995
        }
996
    }
1✔
997

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

1006
    private void addToList(HTMLElement htmlElement) {
1007
        ArrayList list = getListForElement(htmlElement);
1✔
1008
        if (list != null) {
1✔
1009
            list.add(htmlElement);
1✔
1010
        }
1011
    }
1✔
1012

1013
    private ArrayList getListForElement(HTMLElement element) {
1014
        if (element instanceof WebTable) {
1✔
1015
            return _tableList;
1✔
1016
        }
1017
        if (element instanceof WebFrame) {
1✔
1018
            return _frameList;
1✔
1019
        }
1020
        if (element instanceof BlockElement) {
1✔
1021
            return _blocksList;
1✔
1022
        }
1023
        return null;
1✔
1024
    }
1025

1026
    /**
1027
     * Returns the first link which contains the specified text.
1028
     **/
1029
    public WebLink getLinkWith(String text) {
1030
        return getFirstMatchingLink(WebLink.MATCH_CONTAINED_TEXT, text);
1✔
1031
    }
1032

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

1041
    /**
1042
     * Returns the link found in the page with the specified name.
1043
     **/
1044
    public WebLink getLinkWithName(String name) {
1045
        return getFirstMatchingLink(WebLink.MATCH_NAME, name);
1✔
1046
    }
1047

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

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

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

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

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

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

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

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

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

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

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

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

1181
    // ---------------------------------- Object methods --------------------------------
1182

1183
    @Override
1184
    public String toString() {
1185
        return _baseURL.toExternalForm() + System.lineSeparator() + _rootNode;
×
1186
    }
1187

1188
    // ---------------------------------- package members --------------------------------
1189

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

1206
    private void clearCaches() {
1207
        _tables = null;
1✔
1208
        _frames = null;
1✔
1209
        _blocks = null;
1✔
1210
        _updateElements = true;
1✔
1211
    }
1✔
1212

1213
    /**
1214
     * Returns the base URL for this HTML segment.
1215
     **/
1216
    URL getBaseURL() {
1217
        return _baseURL;
1✔
1218
    }
1219

1220
    WebResponse getResponse() {
1221
        return _response;
1✔
1222
    }
1223

1224
    /**
1225
     * Returns the domain object model associated with this page, to be used internally.
1226
     **/
1227
    Node getOriginalDOM() {
1228
        return getRootNode();
1✔
1229
    }
1230

1231
    HTMLElement getElement(Node node) {
1232
        return (HTMLElement) _registry.getRegisteredElement(node);
1✔
1233
    }
1234

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

1246
    // ---------------------------------- private members --------------------------------
1247

1248
    Node getRootNode() {
1249
        if (_rootNode == null) {
1!
1250
            throw new IllegalStateException("The root node has not been specified");
×
1251
        }
1252
        return _rootNode;
1✔
1253
    }
1254

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

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

1311
    class WebIFrame extends WebFrame implements ContentConcealer {
1312

1313
        public WebIFrame(URL baseURL, Node frameNode, FrameSelector parentFrame) {
1✔
1314
            super(_response, baseURL, frameNode, parentFrame);
1✔
1315
        }
1✔
1316
    }
1317

1318
    /**
1319
     * NoScript Element
1320
     */
1321
    class NoScriptElement extends HTMLElementBase implements ContentConcealer {
1322

1323
        public NoScriptElement(Node node) {
1✔
1324
            super(node);
1✔
1325
        }
1✔
1326

1327
        @Override
1328
        public ScriptableDelegate newScriptable() {
1329
            return null;
×
1330
        }
1331

1332
        @Override
1333
        public ScriptableDelegate getParentDelegate() {
1334
            return null;
×
1335
        }
1336
    }
1337

1338
}
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