Source Code Cross Referenced for HtmlPage.java in  » Testing » htmlunit » com » gargoylesoftware » htmlunit » html » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Testing » htmlunit » com.gargoylesoftware.htmlunit.html 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


0001:        /*
0002:         * Copyright (c) 2002-2008 Gargoyle Software Inc. All rights reserved.
0003:         *
0004:         * Redistribution and use in source and binary forms, with or without
0005:         * modification, are permitted provided that the following conditions are met:
0006:         *
0007:         * 1. Redistributions of source code must retain the above copyright notice,
0008:         *    this list of conditions and the following disclaimer.
0009:         * 2. Redistributions in binary form must reproduce the above copyright notice,
0010:         *    this list of conditions and the following disclaimer in the documentation
0011:         *    and/or other materials provided with the distribution.
0012:         * 3. The end-user documentation included with the redistribution, if any, must
0013:         *    include the following acknowledgment:
0014:         *
0015:         *       "This product includes software developed by Gargoyle Software Inc.
0016:         *        (http://www.GargoyleSoftware.com/)."
0017:         *
0018:         *    Alternately, this acknowledgment may appear in the software itself, if
0019:         *    and wherever such third-party acknowledgments normally appear.
0020:         * 4. The name "Gargoyle Software" must not be used to endorse or promote
0021:         *    products derived from this software without prior written permission.
0022:         *    For written permission, please contact info@GargoyleSoftware.com.
0023:         * 5. Products derived from this software may not be called "HtmlUnit", nor may
0024:         *    "HtmlUnit" appear in their name, without prior written permission of
0025:         *    Gargoyle Software Inc.
0026:         *
0027:         * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
0028:         * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0029:         * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GARGOYLE
0030:         * SOFTWARE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
0031:         * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
0032:         * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
0033:         * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
0034:         * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
0035:         * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
0036:         * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0037:         */
0038:        package com.gargoylesoftware.htmlunit.html;
0039:
0040:        import java.io.IOException;
0041:        import java.net.MalformedURLException;
0042:        import java.net.URL;
0043:        import java.util.ArrayList;
0044:        import java.util.Arrays;
0045:        import java.util.Collections;
0046:        import java.util.Comparator;
0047:        import java.util.HashMap;
0048:        import java.util.Iterator;
0049:        import java.util.List;
0050:        import java.util.Map;
0051:        import java.util.Vector;
0052:
0053:        import org.apache.commons.httpclient.HttpStatus;
0054:        import org.apache.commons.httpclient.util.EncodingUtil;
0055:        import org.apache.commons.lang.StringUtils;
0056:        import org.apache.commons.logging.Log;
0057:        import org.apache.commons.logging.LogFactory;
0058:        import org.jaxen.JaxenException;
0059:        import org.mozilla.javascript.Context;
0060:        import org.mozilla.javascript.Function;
0061:        import org.mozilla.javascript.Script;
0062:        import org.mozilla.javascript.Scriptable;
0063:
0064:        import com.gargoylesoftware.htmlunit.Assert;
0065:        import com.gargoylesoftware.htmlunit.ElementNotFoundException;
0066:        import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
0067:        import com.gargoylesoftware.htmlunit.OnbeforeunloadHandler;
0068:        import com.gargoylesoftware.htmlunit.Page;
0069:        import com.gargoylesoftware.htmlunit.ScriptException;
0070:        import com.gargoylesoftware.htmlunit.ScriptResult;
0071:        import com.gargoylesoftware.htmlunit.SgmlPage;
0072:        import com.gargoylesoftware.htmlunit.TextUtil;
0073:        import com.gargoylesoftware.htmlunit.WebAssert;
0074:        import com.gargoylesoftware.htmlunit.WebClient;
0075:        import com.gargoylesoftware.htmlunit.WebRequestSettings;
0076:        import com.gargoylesoftware.htmlunit.WebResponse;
0077:        import com.gargoylesoftware.htmlunit.WebWindow;
0078:        import com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine;
0079:        import com.gargoylesoftware.htmlunit.javascript.host.Event;
0080:        import com.gargoylesoftware.htmlunit.javascript.host.Node;
0081:        import com.gargoylesoftware.htmlunit.javascript.host.Window;
0082:
0083:        /**
0084:         * A representation of an HTML page returned from a server. This class is the
0085:         * DOM Document implementation.
0086:         *
0087:         * @version $Revision: 2155 $
0088:         * @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
0089:         * @author Alex Nikiforoff
0090:         * @author Noboru Sinohara
0091:         * @author David K. Taylor
0092:         * @author Andreas Hangler
0093:         * @author <a href="mailto:cse@dynabean.de">Christian Sell</a>
0094:         * @author Chris Erskine
0095:         * @author Marc Guillemot
0096:         * @author Ahmed Ashour
0097:         */
0098:        public final class HtmlPage extends SgmlPage implements  Cloneable {
0099:
0100:            private static final long serialVersionUID = 1779746292119944291L;
0101:
0102:            private String originalCharset_;
0103:            private Map idMap_ = new HashMap(); // a map of (id, List(HtmlElement))
0104:            private Map nameMap_ = new HashMap(); // a map of (name, List(HtmlElement))
0105:            private HtmlElement documentElement_;
0106:            private HtmlElement elementWithFocus_;
0107:
0108:            private final transient Log javascriptLog_ = LogFactory
0109:                    .getLog("com.gargoylesoftware.htmlunit.javascript");
0110:
0111:            private List/* HtmlAttributeChangeListener */attributeListeners_;
0112:            private final transient Object lock_ = new Object(); // used for synchronization
0113:
0114:            /**
0115:             * Create an instance of HtmlPage
0116:             *
0117:             * @param originatingUrl The url that was used to load this page.
0118:             * @param webResponse The web response that was used to create this page
0119:             * @param webWindow The window that this page is being loaded into.
0120:             */
0121:            public HtmlPage(final URL originatingUrl,
0122:                    final WebResponse webResponse, final WebWindow webWindow) {
0123:                super (webResponse, webWindow);
0124:            }
0125:
0126:            /**
0127:             * @return this page
0128:             */
0129:            public HtmlPage getPage() {
0130:                return this ;
0131:            }
0132:
0133:            /**
0134:             * Initialize this page.
0135:             * @throws IOException If an IO problem occurs.
0136:             * @throws FailingHttpStatusCodeException If the server returns a failing status code AND the property
0137:             * {@link WebClient#setThrowExceptionOnFailingStatusCode(boolean)} is set to true.
0138:             */
0139:            public void initialize() throws IOException,
0140:                    FailingHttpStatusCodeException {
0141:                loadFrames();
0142:                getDocumentHtmlElement().setReadyState(READY_STATE_COMPLETE);
0143:                if (!getWebClient().getBrowserVersion().isIE()) {
0144:                    executeEventHandlersIfNeeded(Event.TYPE_DOM_DOCUMENT_LOADED);
0145:                }
0146:                executeDeferredScriptsIfNeeded();
0147:                executeEventHandlersIfNeeded(Event.TYPE_LOAD);
0148:                executeRefreshIfNeeded();
0149:            }
0150:
0151:            /**
0152:             * Clean up this page.
0153:             * @throws IOException If an IO problem occurs.
0154:             */
0155:            public void cleanUp() throws IOException {
0156:                executeEventHandlersIfNeeded(Event.TYPE_UNLOAD);
0157:                deregisterFramesIfNeeded();
0158:            }
0159:
0160:            /**
0161:             * Get the root element of this document.
0162:             * @return The root element
0163:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0164:             * different.  Use getDocumentHtmlElement instead.
0165:             */
0166:            public HtmlElement getDocumentElement() {
0167:                return getDocumentHtmlElement();
0168:            }
0169:
0170:            /**
0171:             * Get the root HtmlElement of this document.
0172:             * @return The root element
0173:             */
0174:            public HtmlElement getDocumentHtmlElement() {
0175:                if (documentElement_ == null) {
0176:                    DomNode childNode = getFirstDomChild();
0177:                    while (childNode != null
0178:                            && !(childNode instanceof  HtmlElement)) {
0179:                        childNode = childNode.getNextDomSibling();
0180:                    }
0181:                    documentElement_ = (HtmlElement) childNode;
0182:                }
0183:                return documentElement_;
0184:            }
0185:
0186:            /**
0187:             * Return the charset used in the page.
0188:             * The sources of this information are from 1).meta element which
0189:             * http-equiv attribute value is 'content-type', or if not from
0190:             * the response header.
0191:             * @return the value of charset.
0192:             */
0193:            public String getPageEncoding() {
0194:                if (originalCharset_ != null) {
0195:                    return originalCharset_;
0196:                }
0197:
0198:                final List list = getMetaTags("content-type");
0199:                for (int i = 0; i < list.size(); i++) {
0200:                    final HtmlMeta meta = (HtmlMeta) list.get(i);
0201:                    final String contents = meta.getContentAttribute();
0202:                    final int pos = contents.toLowerCase().indexOf("charset=");
0203:                    if (pos >= 0) {
0204:                        originalCharset_ = contents.substring(pos + 8);
0205:                        getLog().debug(
0206:                                "Page Encoding detected: " + originalCharset_);
0207:                        return originalCharset_;
0208:                    }
0209:                }
0210:                if (originalCharset_ == null) {
0211:                    originalCharset_ = getWebResponse().getContentCharSet();
0212:                }
0213:                return originalCharset_;
0214:            }
0215:
0216:            /**
0217:             * Create a new HTML element with the given tag name.
0218:             *
0219:             * @param tagName The tag name, preferably in lowercase
0220:             * @return the new HTML element.
0221:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0222:             * different.  Use createHtmlElement instead.
0223:             */
0224:            public HtmlElement createElement(final String tagName) {
0225:                return createHtmlElement(tagName);
0226:            }
0227:
0228:            /**
0229:             * Create a new HTML element with the given tag name.
0230:             *
0231:             * @param tagName The tag name, preferably in lowercase
0232:             * @return the new HTML element.
0233:             */
0234:            public HtmlElement createHtmlElement(final String tagName) {
0235:                final String tagLower = tagName.toLowerCase();
0236:                return HTMLParser.getFactory(tagLower).createElement(this ,
0237:                        tagLower, null);
0238:            }
0239:
0240:            /**
0241:             * Create a new HTML element with the given namespace and qualified name.
0242:             *
0243:             * @param namespaceURI the URI that identifies an XML namespace.
0244:             * @param qualifiedName The qualified name of the element type to instantiate
0245:             * @return the new HTML element.
0246:             * @deprecated This method conflicts with the W3C DOM API since the return values are
0247:             * different.  Use createHtmlElementNS instead.
0248:             */
0249:            public HtmlElement createElementNS(final String namespaceURI,
0250:                    final String qualifiedName) {
0251:                return createHtmlElementNS(namespaceURI, qualifiedName);
0252:            }
0253:
0254:            /**
0255:             * Create a new HtmlElement with the given namespace and qualified name.
0256:             *
0257:             * @param namespaceURI the URI that identifies an XML namespace.
0258:             * @param qualifiedName The qualified name of the element type to instantiate
0259:             * @return the new HTML element.
0260:             */
0261:            public HtmlElement createHtmlElementNS(final String namespaceURI,
0262:                    final String qualifiedName) {
0263:                final String tagLower = qualifiedName.toLowerCase().substring(
0264:                        qualifiedName.indexOf(':') + 1);
0265:                return HTMLParser.getFactory(tagLower).createElementNS(this ,
0266:                        namespaceURI, qualifiedName, null);
0267:            }
0268:
0269:            /**
0270:             * Return the HtmlAnchor with the specified name
0271:             *
0272:             * @param name The name to search by
0273:             * @return See above
0274:             * @throws ElementNotFoundException If the anchor could not be found.
0275:             */
0276:            public HtmlAnchor getAnchorByName(final String name)
0277:                    throws ElementNotFoundException {
0278:                return (HtmlAnchor) getDocumentHtmlElement()
0279:                        .getOneHtmlElementByAttribute("a", "name", name);
0280:            }
0281:
0282:            /**
0283:             *  Return the {@link HtmlAnchor} with the specified href
0284:             *
0285:             * @param href The string to search by
0286:             * @return The HtmlAnchor
0287:             * @throws ElementNotFoundException If the anchor could not be found.
0288:             */
0289:            public HtmlAnchor getAnchorByHref(final String href)
0290:                    throws ElementNotFoundException {
0291:                return (HtmlAnchor) getDocumentHtmlElement()
0292:                        .getOneHtmlElementByAttribute("a", "href", href);
0293:            }
0294:
0295:            /**
0296:             * Return a list of all anchors contained in this page.
0297:             * @return the list of {@link HtmlAnchor} in this page.
0298:             */
0299:            public List getAnchors() {
0300:                return getDocumentHtmlElement().getHtmlElementsByTagNames(
0301:                        Collections.singletonList("a"));
0302:            }
0303:
0304:            /**
0305:             * Return the first anchor that contains the specified text.
0306:             * @param text The text to search for
0307:             * @return The first anchor that was found.
0308:             * @throws ElementNotFoundException If no anchors are found with the specified text
0309:             */
0310:            public HtmlAnchor getFirstAnchorByText(final String text)
0311:                    throws ElementNotFoundException {
0312:                Assert.notNull("text", text);
0313:
0314:                final Iterator iterator = getAnchors().iterator();
0315:                while (iterator.hasNext()) {
0316:                    final HtmlAnchor anchor = (HtmlAnchor) iterator.next();
0317:                    if (text.equals(anchor.asText())) {
0318:                        return anchor;
0319:                    }
0320:                }
0321:                throw new ElementNotFoundException("a", "<text>", text);
0322:            }
0323:
0324:            /**
0325:             * Return the first form that matches the specified name
0326:             * @param name The name to search for
0327:             * @return The first form.
0328:             * @exception ElementNotFoundException If no forms match the specified result.
0329:             */
0330:            public HtmlForm getFormByName(final String name)
0331:                    throws ElementNotFoundException {
0332:                final List forms = getDocumentHtmlElement()
0333:                        .getHtmlElementsByAttribute("form", "name", name);
0334:                if (forms.size() == 0) {
0335:                    throw new ElementNotFoundException("form", "name", name);
0336:                } else {
0337:                    return (HtmlForm) forms.get(0);
0338:                }
0339:            }
0340:
0341:            /**
0342:             * Return a list of all the forms in the page.
0343:             * @return All the forms.
0344:             */
0345:            public List getForms() {
0346:                return getDocumentHtmlElement().getHtmlElementsByTagNames(
0347:                        Arrays.asList(new String[] { "form" }));
0348:            }
0349:
0350:            /**
0351:             * Given a relative url (ie /foo), return a fully qualified url based on
0352:             * the url that was used to load this page
0353:             *
0354:             * @param relativeUrl The relative url
0355:             * @return See above
0356:             * @exception MalformedURLException If an error occurred when creating a URL object
0357:             */
0358:            public URL getFullyQualifiedUrl(String relativeUrl)
0359:                    throws MalformedURLException {
0360:
0361:                final List baseElements = getDocumentHtmlElement()
0362:                        .getHtmlElementsByTagName("base");
0363:                URL baseUrl;
0364:                if (baseElements.isEmpty()) {
0365:                    baseUrl = getWebResponse().getUrl();
0366:                } else {
0367:                    if (baseElements.size() > 1) {
0368:                        notifyIncorrectness("Multiple 'base' detected, only the first is used.");
0369:                    }
0370:                    final HtmlBase htmlBase = (HtmlBase) baseElements.get(0);
0371:                    boolean insideHead = false;
0372:                    for (DomNode parent = htmlBase.getParentDomNode(); parent != null; parent = parent
0373:                            .getParentDomNode()) {
0374:                        if (parent instanceof  HtmlHead) {
0375:                            insideHead = true;
0376:                            break;
0377:                        }
0378:                    }
0379:
0380:                    //http://www.w3.org/TR/1999/REC-html401-19991224/struct/links.html#edef-BASE
0381:                    if (!insideHead) {
0382:                        notifyIncorrectness("Element 'base' must appear in <head>, it is ignored.");
0383:                    }
0384:
0385:                    final String href = htmlBase.getHrefAttribute();
0386:                    if (!insideHead || StringUtils.isEmpty(href)) {
0387:                        baseUrl = getWebResponse().getUrl();
0388:                    } else {
0389:                        try {
0390:                            baseUrl = new URL(href);
0391:                        } catch (final MalformedURLException e) {
0392:                            notifyIncorrectness("Invalid base url: \"" + href
0393:                                    + "\", ignoring it");
0394:                            baseUrl = getWebResponse().getUrl();
0395:                        }
0396:                    }
0397:                }
0398:
0399:                // to handle http: and http:/ in FF (Bug 1714767)
0400:                if (getWebClient().getBrowserVersion().isNetscape()) {
0401:                    boolean incorrectnessNotified = false;
0402:                    while (relativeUrl.startsWith("http:")
0403:                            && !relativeUrl.startsWith("http://")) {
0404:                        if (!incorrectnessNotified) {
0405:                            notifyIncorrectness("Incorrect URL \""
0406:                                    + relativeUrl + "\" has been corrected");
0407:                            incorrectnessNotified = true;
0408:                        }
0409:                        relativeUrl = "http:/" + relativeUrl.substring(5);
0410:                    }
0411:                }
0412:
0413:                return WebClient.expandUrl(baseUrl, relativeUrl);
0414:            }
0415:
0416:            /**
0417:             * Given a target attribute value, resolve the target using a base target for the page.
0418:             *
0419:             * @param elementTarget The target specified as an attribute of the element.
0420:             * @return The resolved target to use for the element.
0421:             */
0422:            public String getResolvedTarget(final String elementTarget) {
0423:                final List baseElements = getDocumentHtmlElement()
0424:                        .getHtmlElementsByTagNames(
0425:                                Collections.singletonList("base"));
0426:                final String resolvedTarget;
0427:                if (baseElements.isEmpty()) {
0428:                    resolvedTarget = elementTarget;
0429:                } else if (elementTarget != null && elementTarget.length() > 0) {
0430:                    resolvedTarget = elementTarget;
0431:                } else {
0432:                    final HtmlBase htmlBase = (HtmlBase) baseElements.get(0);
0433:                    resolvedTarget = htmlBase.getTargetAttribute();
0434:                }
0435:                return resolvedTarget;
0436:            }
0437:
0438:            /**
0439:             * Return a list of ids (strings) that correspond to the tabbable elements
0440:             * in this page. Return them in the same order specified in {@link #getTabbableElements}
0441:             *
0442:             * @return The list of id's
0443:             */
0444:            public List getTabbableElementIds() {
0445:                final List list = new ArrayList(getTabbableElements());
0446:                final int listSize = list.size();
0447:
0448:                for (int i = 0; i < listSize; i++) {
0449:                    list.set(i, ((HtmlElement) list.get(i))
0450:                            .getAttributeValue("id"));
0451:                }
0452:
0453:                return Collections.unmodifiableList(list);
0454:            }
0455:
0456:            /**
0457:             * Returns a list of all elements that are tabbable in the order that will
0458:             * be used for tabbing.<p>
0459:             *
0460:             * The rules for determining tab order are as follows:
0461:             * <ol>
0462:             *   <li>Those elements that support the tabindex attribute and assign a
0463:             *   positive value to it are navigated first. Navigation proceeds from the
0464:             *   element with the lowest tabindex value to the element with the highest
0465:             *   value. Values need not be sequential nor must they begin with any
0466:             *   particular value. Elements that have identical tabindex values should
0467:             *   be navigated in the order they appear in the character stream.
0468:             *   <li>Those elements that do not support the tabindex attribute or
0469:             *   support it and assign it a value of "0" are navigated next. These
0470:             *   elements are navigated in the order they appear in the character
0471:             *   stream.
0472:             *   <li>Elements that are disabled do not participate in the tabbing
0473:             *   order.
0474:             * </ol>
0475:             * Additionally, the value of tabindex must be within 0 and 32767. Any
0476:             * values outside this range will be ignored.<p>
0477:             *
0478:             * The following elements support the <tt>tabindex</tt> attribute: A, AREA, BUTTON,
0479:             * INPUT, OBJECT, SELECT, and TEXTAREA.<p>
0480:             *
0481:             * @return A list containing all the tabbable elements in proper tab order.
0482:             */
0483:            public List getTabbableElements() {
0484:                final List tags = Arrays.asList(new Object[] { "a", "area",
0485:                        "button", "input", "object", "select", "textarea" });
0486:                final List tabbableElements = new ArrayList();
0487:                final Iterator iterator = getAllHtmlChildElements();
0488:                while (iterator.hasNext()) {
0489:                    final HtmlElement element = (HtmlElement) iterator.next();
0490:                    final String tagName = element.getTagName();
0491:                    if (tags.contains(tagName)) {
0492:                        final boolean disabled = element
0493:                                .isAttributeDefined("disabled");
0494:                        if (!disabled
0495:                                && element.getTabIndex() != HtmlElement.TAB_INDEX_OUT_OF_BOUNDS) {
0496:                            tabbableElements.add(element);
0497:                        }
0498:                    }
0499:                }
0500:                Collections.sort(tabbableElements, createTabOrderComparator());
0501:                return Collections.unmodifiableList(tabbableElements);
0502:            }
0503:
0504:            private Comparator createTabOrderComparator() {
0505:                return new Comparator() {
0506:                    public int compare(final Object object1,
0507:                            final Object object2) {
0508:
0509:                        final HtmlElement element1 = (HtmlElement) object1;
0510:                        final HtmlElement element2 = (HtmlElement) object2;
0511:
0512:                        final Short i1 = element1.getTabIndex();
0513:                        final Short i2 = element2.getTabIndex();
0514:
0515:                        final short index1;
0516:                        if (i1 != null) {
0517:                            index1 = i1.shortValue();
0518:                        } else {
0519:                            index1 = -1;
0520:                        }
0521:
0522:                        final short index2;
0523:                        if (i2 != null) {
0524:                            index2 = i2.shortValue();
0525:                        } else {
0526:                            index2 = -1;
0527:                        }
0528:
0529:                        final int result;
0530:                        if (index1 > 0 && index2 > 0) {
0531:                            result = index1 - index2;
0532:                        } else if (index1 > 0) {
0533:                            result = -1;
0534:                        } else if (index2 > 0) {
0535:                            result = +1;
0536:                        } else if (index1 == index2) {
0537:                            result = 0;
0538:                        } else {
0539:                            result = index2 - index1;
0540:                        }
0541:
0542:                        return result;
0543:                    }
0544:                };
0545:            }
0546:
0547:            /**
0548:             * Returns the HTML element that is assigned to the specified access key. An
0549:             * access key (aka mnemonic key) is used for keyboard navigation of the
0550:             * page.<p>
0551:             *
0552:             * Only the following HTML elements may have <tt>accesskey</tt>s defined: A, AREA,
0553:             * BUTTON, INPUT, LABEL, LEGEND, and TEXTAREA.
0554:             *
0555:             * @param accessKey The key to look for
0556:             * @return The HTML element that is assigned to the specified key or null
0557:             *      if no elements can be found that match the specified key.
0558:             */
0559:            public HtmlElement getHtmlElementByAccessKey(final char accessKey) {
0560:                final List elements = getHtmlElementsByAccessKey(accessKey);
0561:                if (elements.isEmpty()) {
0562:                    return null;
0563:                } else {
0564:                    return (HtmlElement) elements.get(0);
0565:                }
0566:            }
0567:
0568:            /**
0569:             * Returns all the HTML elements that are assigned to the specified access key. An
0570:             * access key (aka mnemonic key) is used for keyboard navigation of the
0571:             * page.<p>
0572:             *
0573:             * The HTML specification seems to indicate that one accesskey cannot be used
0574:             * for multiple elements however Internet Explorer does seem to support this.
0575:             * It's worth noting that Mozilla does not support multiple elements with one
0576:             * access key so you are making your html browser specific if you rely on this
0577:             * feature.<p>
0578:             *
0579:             * Only the following html elements may have <tt>accesskey</tt>s defined: A, AREA,
0580:             * BUTTON, INPUT, LABEL, LEGEND, and TEXTAREA.
0581:             *
0582:             * @param accessKey The key to look for
0583:             * @return A list of HTML elements that are assigned to the specified accesskey.
0584:             */
0585:            public List getHtmlElementsByAccessKey(final char accessKey) {
0586:                final List elements = new ArrayList();
0587:
0588:                final String searchString = ("" + accessKey).toLowerCase();
0589:                final List acceptableTagNames = Arrays.asList(new Object[] {
0590:                        "a", "area", "button", "input", "label", "legend",
0591:                        "textarea" });
0592:
0593:                final Iterator iterator = getAllHtmlChildElements();
0594:                while (iterator.hasNext()) {
0595:                    final HtmlElement element = (HtmlElement) iterator.next();
0596:                    if (acceptableTagNames.contains(element.getTagName())) {
0597:                        final String accessKeyAttribute = element
0598:                                .getAttributeValue("accesskey");
0599:                        if (searchString.equalsIgnoreCase(accessKeyAttribute)) {
0600:                            elements.add(element);
0601:                        }
0602:                    }
0603:                }
0604:
0605:                return elements;
0606:            }
0607:
0608:            /**
0609:             * Many html elements are "tabbable" and can have a "tabindex" attribute
0610:             * that determines the order in which the components are navigated when
0611:             * pressing the tab key. To ensure good usability for keyboard navigation,
0612:             * all tabbable elements should have the tabindex attribute set.<p>
0613:             *
0614:             * Assert that all tabbable elements have a valid value set for "tabindex".
0615:             * If they don't then throw an exception as per {@link
0616:             * WebClient#assertionFailed(String)}
0617:             *
0618:             * @deprecated
0619:             * @see WebAssert#assertAllTabIndexAttributesSet(HtmlPage)
0620:             */
0621:            public void assertAllTabIndexAttributesSet() {
0622:                WebAssert.assertAllTabIndexAttributesSet(this );
0623:            }
0624:
0625:            /**
0626:             * Many html components can have an "accesskey" attribute which defines a
0627:             * hot key for keyboard navigation. Assert that all access keys (mnemonics)
0628:             * in this page are unique. If they aren't then throw an exception as per
0629:             * {@link WebClient#assertionFailed(String)}
0630:             *
0631:             * @deprecated
0632:             * @see WebAssert#assertAllAccessKeyAttributesUnique(HtmlPage)
0633:             */
0634:            public void assertAllAccessKeyAttributesUnique() {
0635:                WebAssert.assertAllAccessKeyAttributesUnique(this );
0636:            }
0637:
0638:            /**
0639:             * Each html element can have an id attribute and by definition, all ids
0640:             * must be unique within the document. <p>
0641:             *
0642:             * Assert that all ids in this page are unique. If they aren't then throw
0643:             * an exception as per {@link WebClient#assertionFailed(String)}
0644:             *
0645:             * @deprecated
0646:             * @see WebAssert#assertAllIdAttributesUnique(HtmlPage)
0647:             */
0648:            public void assertAllIdAttributesUnique() {
0649:                WebAssert.assertAllIdAttributesUnique(this );
0650:            }
0651:
0652:            /**
0653:             * Execute the specified javascript within the page.
0654:             * The usage would be similar to what can be achieved to execute javascript in the current page
0655:             * by entering a "javascript:...some js code..." in the url field of a "normal" browser.
0656:             * <p>
0657:             * <b>Note: </b> the provided code won't be executed if JavaScript has been disabled on the WebClient
0658:             * (see {@link WebClient#isJavaScriptEnabled()}.
0659:             * @param sourceCode The javascript code to execute.
0660:             * @return A ScriptResult which will contain both the current page (which may be different than
0661:             * the previous page) and a javascript result object.
0662:             */
0663:            public ScriptResult executeJavaScript(final String sourceCode) {
0664:
0665:                return executeJavaScriptIfPossible(sourceCode,
0666:                        "injected script", 1);
0667:            }
0668:
0669:            /**
0670:             * <p>
0671:             * Execute the specified javascript if a javascript engine was successfully
0672:             * instantiated.  If this javascript causes the current page to be reloaded
0673:             * (through location="" or form.submit()) then return the new page.  Otherwise
0674:             * return the current page.
0675:             * </p>
0676:             * <p><b>Please note:</b> Although this method is public, it is not intended for
0677:             * general execution of javascript.  Users of HtmlUnit should interact with the pages
0678:             * as a user would by clicking on buttons or links and having the javascript event
0679:             * handlers execute as needed..
0680:             * </p>
0681:             *
0682:             * @param sourceCode The javascript code to execute.
0683:             * @param sourceName The name for this chunk of code.  This name will be displayed
0684:             * in any error messages.
0685:             * @param htmlElement The html element for which this script is being executed.
0686:             * This element will be the context during the javascript execution.  If null,
0687:             * the context will default to the window.
0688:             * @return A ScriptResult which will contain both the current page (which may be different than
0689:             * the previous page and a javascript result object.
0690:             * @deprecated use {@link #executeJavaScript(String)} instead
0691:             */
0692:            public ScriptResult executeJavaScriptIfPossible(
0693:                    final String sourceCode, final String sourceName,
0694:                    final HtmlElement htmlElement) {
0695:
0696:                return executeJavaScriptIfPossible(sourceCode, sourceName, 1);
0697:            }
0698:
0699:            /**
0700:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0701:             * <p>
0702:             * Execute the specified javascript if a javascript engine was successfully
0703:             * instantiated.  If this javascript causes the current page to be reloaded
0704:             * (through location="" or form.submit()) then return the new page.  Otherwise
0705:             * return the current page.
0706:             * </p>
0707:             * <p><b>Please note:</b> Although this method is public, it is not intended for
0708:             * general execution of javascript.  Users of HtmlUnit should interact with the pages
0709:             * as a user would by clicking on buttons or links and having the javascript event
0710:             * handlers execute as needed..
0711:             * </p>
0712:             *
0713:             * @param sourceCode The javascript code to execute.
0714:             * @param sourceName The name for this chunk of code.  This name will be displayed
0715:             * in any error messages.
0716:             * @param startLine the line at which the script source starts
0717:             * @return A ScriptResult which will contain both the current page (which may be different than
0718:             * the previous page and a javascript result object.
0719:             */
0720:            public ScriptResult executeJavaScriptIfPossible(String sourceCode,
0721:                    final String sourceName, final int startLine) {
0722:
0723:                if (!getWebClient().isJavaScriptEnabled()) {
0724:                    return new ScriptResult(null, this );
0725:                }
0726:
0727:                final String prefix = "javascript:";
0728:                final int prefixLength = prefix.length();
0729:
0730:                if (sourceCode.length() > prefixLength
0731:                        && sourceCode.substring(0, prefixLength)
0732:                                .equalsIgnoreCase(prefix)) {
0733:                    sourceCode = sourceCode.substring(prefixLength);
0734:                }
0735:
0736:                final WebWindow window = getEnclosingWindow();
0737:                getWebClient().pushClearFirstWindow();
0738:                final Object result = getWebClient().getJavaScriptEngine()
0739:                        .execute(this , sourceCode, sourceName, startLine);
0740:
0741:                WebWindow firstWindow = getWebClient().popFirstWindow();
0742:                if (firstWindow == null) {
0743:                    firstWindow = window;
0744:                }
0745:
0746:                return new ScriptResult(result, firstWindow.getEnclosedPage());
0747:            }
0748:
0749:            /**
0750:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0751:             *
0752:             * Execute a Function in the given context.
0753:             *
0754:             * @param function The javascript Function to call.
0755:             * @param thisObject The "this" object to be used during invocation.
0756:             * @param args The arguments to pass into the call.
0757:             * @param htmlElementScope The html element for which this script is being executed.
0758:             * This element will be the context during the javascript execution.  If null,
0759:             * the context will default to the page.
0760:             * @return A ScriptResult which will contain both the current page (which may be different than
0761:             * the previous page and a javascript result object.
0762:             */
0763:            public ScriptResult executeJavaScriptFunctionIfPossible(
0764:                    final Function function, final Scriptable this Object,
0765:                    final Object[] args, final DomNode htmlElementScope) {
0766:
0767:                final WebWindow window = getEnclosingWindow();
0768:                getWebClient().pushClearFirstWindow();
0769:
0770:                if (!getWebClient().isJavaScriptEnabled()) {
0771:                    return new ScriptResult(null, this );
0772:                }
0773:
0774:                final JavaScriptEngine engine = getWebClient()
0775:                        .getJavaScriptEngine();
0776:                final Object result = engine.callFunction(this , function,
0777:                        this Object, args, htmlElementScope);
0778:
0779:                WebWindow firstWindow = getWebClient().popFirstWindow();
0780:                if (firstWindow == null) {
0781:                    firstWindow = window;
0782:                }
0783:                return new ScriptResult(result, firstWindow.getEnclosedPage());
0784:            }
0785:
0786:            /**
0787:             * Return the log object for this element.
0788:             * @return The log object for this element.
0789:             */
0790:            protected Log getJsLog() {
0791:                return javascriptLog_;
0792:            }
0793:
0794:            /**
0795:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0796:             *
0797:             * @param srcAttribute The source attribute from the script tag.
0798:             * @param charset The charset attribute from the script tag.
0799:             */
0800:            void loadExternalJavaScriptFile(final String srcAttribute,
0801:                    final String charset) {
0802:                if (getWebClient().isJavaScriptEnabled()) {
0803:                    final URL scriptURL;
0804:                    try {
0805:                        scriptURL = getFullyQualifiedUrl(srcAttribute);
0806:                        if (scriptURL.getProtocol().equals("javascript")) {
0807:                            getLog().info(
0808:                                    "Ignoring script src [" + srcAttribute
0809:                                            + "]");
0810:                            return;
0811:                        }
0812:                    } catch (final MalformedURLException e) {
0813:                        getLog().error(
0814:                                "Unable to build url for script src tag ["
0815:                                        + srcAttribute + "]");
0816:                        if (getWebClient().isThrowExceptionOnScriptError()) {
0817:                            throw new ScriptException(this , e);
0818:                        }
0819:                        return;
0820:                    }
0821:
0822:                    final Script script = loadJavaScriptFromUrl(scriptURL,
0823:                            charset);
0824:                    if (script != null) {
0825:                        getWebClient().getJavaScriptEngine().execute(this ,
0826:                                script);
0827:                    }
0828:                }
0829:            }
0830:
0831:            /**
0832:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
0833:             *
0834:             * Return true if a script with the specified type and language attributes
0835:             * is actually JavaScript.
0836:             * According to <a href="http://www.w3.org/TR/REC-html40/types.html#h-6.7">W3C recommendation</a>
0837:             * are content types case insensitive.
0838:             * @param typeAttribute The type attribute specified in the script tag.
0839:             * @param languageAttribute The language attribute specified in the script tag.
0840:             * @return true if the script is javascript
0841:             */
0842:            public static boolean isJavaScript(final String typeAttribute,
0843:                    final String languageAttribute) {
0844:                // Unless otherwise specified, we have to assume that any script is javascript
0845:                final boolean isJavaScript;
0846:                if (languageAttribute != null
0847:                        && languageAttribute.length() != 0) {
0848:                    isJavaScript = TextUtil.startsWithIgnoreCase(
0849:                            languageAttribute, "javascript");
0850:                } else if (typeAttribute != null && typeAttribute.length() != 0) {
0851:                    isJavaScript = typeAttribute
0852:                            .equalsIgnoreCase("text/javascript");
0853:                } else {
0854:                    isJavaScript = true;
0855:                }
0856:
0857:                return isJavaScript;
0858:            }
0859:
0860:            /**
0861:             * Loads JavaScript from the specified URL. This method may return <tt>null</tt> if
0862:             * there is a problem loading the code from the specified URL.
0863:             *
0864:             * @param url the url of the script
0865:             * @param charset the charset to use to read the text
0866:             * @return the content of the file
0867:             */
0868:            private Script loadJavaScriptFromUrl(final URL url,
0869:                    final String charset) {
0870:                String scriptEncoding = charset;
0871:                getPageEncoding();
0872:
0873:                WebResponse webResponse;
0874:                try {
0875:                    final WebRequestSettings requestSettings = new WebRequestSettings(
0876:                            url);
0877:                    webResponse = getWebClient().loadWebResponse(
0878:                            requestSettings);
0879:                } catch (final IOException e) {
0880:                    getLog().error(
0881:                            "Error loading JavaScript from ["
0882:                                    + url.toExternalForm() + "].", e);
0883:                    return null;
0884:                }
0885:
0886:                final JavaScriptEngine javaScriptEngine = getWebClient()
0887:                        .getJavaScriptEngine();
0888:                final Script cachedScript = javaScriptEngine
0889:                        .getCachedScript(webResponse);
0890:                if (cachedScript != null) {
0891:                    return cachedScript;
0892:                }
0893:
0894:                getWebClient().printContentIfNecessary(webResponse);
0895:                getWebClient().throwFailingHttpStatusCodeExceptionIfNecessary(
0896:                        webResponse);
0897:
0898:                final int statusCode = webResponse.getStatusCode();
0899:                final boolean successful = (statusCode >= HttpStatus.SC_OK && statusCode < HttpStatus.SC_MULTIPLE_CHOICES);
0900:                if (!successful) {
0901:                    return null;
0902:                }
0903:
0904:                final String contentType = webResponse.getContentType();
0905:                if (!contentType.equalsIgnoreCase("text/javascript")
0906:                        && !contentType
0907:                                .equalsIgnoreCase("application/x-javascript")) {
0908:                    getLog().warn(
0909:                            "Expected content type of 'text/javascript' or 'application/x-javascript' for "
0910:                                    + "remotely loaded JavaScript element at '"
0911:                                    + url + "', " + "but got '" + contentType
0912:                                    + "'.");
0913:                }
0914:
0915:                if (StringUtils.isEmpty(scriptEncoding)) {
0916:                    final String contentCharset = webResponse
0917:                            .getContentCharSet();
0918:                    if (!contentCharset.equals(TextUtil.DEFAULT_CHARSET)) {
0919:                        scriptEncoding = contentCharset;
0920:                    } else if (!originalCharset_
0921:                            .equals(TextUtil.DEFAULT_CHARSET)) {
0922:                        scriptEncoding = originalCharset_;
0923:                    } else {
0924:                        scriptEncoding = TextUtil.DEFAULT_CHARSET;
0925:                    }
0926:                }
0927:
0928:                final byte[] data = webResponse.getResponseBody();
0929:                final String scriptCode = EncodingUtil.getString(data, 0,
0930:                        data.length, scriptEncoding);
0931:
0932:                final Script script = javaScriptEngine.compile(this ,
0933:                        scriptCode, url.toExternalForm(), 1);
0934:                javaScriptEngine.cacheScript(webResponse, script);
0935:                return script;
0936:            }
0937:
0938:            /**
0939:             * Return the title of this page or an empty string if the title wasn't specified.
0940:             *
0941:             * @return the title of this page or an empty string if the title wasn't specified.
0942:             */
0943:            public String getTitleText() {
0944:                final HtmlTitle titleElement = getTitleElement();
0945:                if (titleElement != null) {
0946:                    return titleElement.asText();
0947:                }
0948:                return "";
0949:            }
0950:
0951:            /**
0952:             * Set the text for the title of this page.  If there is not a title element
0953:             * on this page, then one has to be generated.
0954:             * @param message The new text
0955:             */
0956:            public void setTitleText(final String message) {
0957:                HtmlTitle titleElement = getTitleElement();
0958:                if (titleElement == null) {
0959:                    getLog().debug("No title element, creating one");
0960:                    final HtmlHead head = (HtmlHead) getFirstChildElement(
0961:                            getDocumentHtmlElement(), HtmlHead.class);
0962:                    if (head == null) {
0963:                        // perhaps should we create head too?
0964:                        throw new IllegalStateException(
0965:                                "Headelement was not defined for this page");
0966:                    }
0967:                    titleElement = new HtmlTitle(null, HtmlTitle.TAG_NAME,
0968:                            this , Collections.EMPTY_MAP);
0969:                    if (head.getFirstDomChild() != null) {
0970:                        head.getFirstDomChild().insertBefore(titleElement);
0971:                    } else {
0972:                        head.appendDomChild(titleElement);
0973:                    }
0974:                }
0975:
0976:                titleElement.setNodeValue(message);
0977:            }
0978:
0979:            /**
0980:             * Get the first child of startElement that is an instance of the given class.
0981:             * @param startElement The parent element
0982:             * @param clazz The class to search for.
0983:             * @return <code>null</code> if no child found
0984:             */
0985:            private HtmlElement getFirstChildElement(
0986:                    final HtmlElement startElement, final Class clazz) {
0987:                final Iterator iterator = startElement
0988:                        .getChildElementsIterator();
0989:                while (iterator.hasNext()) {
0990:                    final HtmlElement element = (HtmlElement) iterator.next();
0991:                    if (clazz.isInstance(element)) {
0992:                        return element;
0993:                    }
0994:                }
0995:
0996:                return null;
0997:            }
0998:
0999:            /**
1000:             * Get the title element for this page.  Returns null if one is not found.
1001:             *
1002:             * @return the title element for this page or null if this is not one.
1003:             */
1004:            private HtmlTitle getTitleElement() {
1005:                final HtmlHead head = (HtmlHead) getFirstChildElement(
1006:                        getDocumentHtmlElement(), HtmlHead.class);
1007:                if (head != null) {
1008:                    return (HtmlTitle) getFirstChildElement(head,
1009:                            HtmlTitle.class);
1010:                }
1011:
1012:                return null;
1013:            }
1014:
1015:            /**
1016:             * Look for and execute any appropriate event handlers.  Look for body
1017:             * and frame tags.
1018:             * @param eventType either {@link Event#TYPE_LOAD}, {@link Event#TYPE_UNLOAD}, or {@link Event#TYPE_BEFORE_UNLOAD}.
1019:             * @return true if user accepted onbeforeunload (not relevant to other events).
1020:             */
1021:            private boolean executeEventHandlersIfNeeded(final String eventType) {
1022:                if (!getWebClient().isJavaScriptEnabled()) {
1023:                    return true;
1024:                }
1025:
1026:                final Window jsWindow = (Window) getEnclosingWindow()
1027:                        .getScriptObject();
1028:                if (jsWindow != null) {
1029:                    final HtmlElement element = getDocumentHtmlElement();
1030:                    final Event event = new Event(element, eventType);
1031:                    element.fireEvent(event);
1032:                    if (!isOnbeforeunloadAccepted(this , event)) {
1033:                        return false;
1034:                    }
1035:                }
1036:
1037:                // the event of the contained frames or iframe tags
1038:                final List frames = getDocumentHtmlElement()
1039:                        .getHtmlElementsByTagNames(
1040:                                Arrays
1041:                                        .asList(new String[] { "frame",
1042:                                                "iframe" }));
1043:                for (final Iterator iter = frames.iterator(); iter.hasNext();) {
1044:                    final BaseFrame frame = (BaseFrame) iter.next();
1045:                    final Function frameTagEventHandler = frame
1046:                            .getEventHandler("on" + eventType);
1047:                    if (frameTagEventHandler != null) {
1048:                        getLog().debug(
1049:                                "Executing on" + eventType + " handler for "
1050:                                        + frame);
1051:                        final Event event = new Event(frame, eventType);
1052:                        ((Node) frame.getScriptObject()).executeEvent(event);
1053:                        if (!isOnbeforeunloadAccepted(frame.getPage(), event)) {
1054:                            return false;
1055:                        }
1056:                    }
1057:                }
1058:                return true;
1059:            }
1060:
1061:            private boolean isOnbeforeunloadAccepted(final HtmlPage page,
1062:                    final Event event) {
1063:                if (event.jsxGet_type().equals(Event.TYPE_BEFORE_UNLOAD)
1064:                        && event.jsxGet_returnValue() != null) {
1065:                    final OnbeforeunloadHandler handler = getWebClient()
1066:                            .getOnbeforeunloadHandler();
1067:                    if (handler == null) {
1068:                        getLog()
1069:                                .warn(
1070:                                        "document.onbeforeunload() returned a string in event.returnValue,"
1071:                                                + " but no onbeforeunload handler installed.");
1072:                    } else {
1073:                        final String message = Context.toString(event
1074:                                .jsxGet_returnValue());
1075:                        return handler.handleEvent(page, message);
1076:                    }
1077:                }
1078:                return true;
1079:            }
1080:
1081:            /**
1082:             * If a refresh has been specified either through a meta tag or an http
1083:             * response header, then perform that refresh.
1084:             * @throws IOException if an IO problem occurs
1085:             */
1086:            private void executeRefreshIfNeeded() throws IOException {
1087:                // If this page is not in a frame then a refresh has already happened,
1088:                // most likely through the javascript onload handler, so we don't do a
1089:                // second refresh.
1090:                final WebWindow window = getEnclosingWindow();
1091:                if (window == null) {
1092:                    return;
1093:                }
1094:
1095:                final String refreshString = getRefreshStringOrNull();
1096:                if (refreshString == null || refreshString.length() == 0) {
1097:                    return;
1098:                }
1099:
1100:                final int time;
1101:                final URL url;
1102:
1103:                int index = refreshString.indexOf(";");
1104:                final boolean timeOnly = (index == -1);
1105:
1106:                if (timeOnly) {
1107:                    // Format: <meta http-equiv='refresh' content='10'>
1108:                    try {
1109:                        time = Integer.parseInt(refreshString);
1110:                    } catch (final NumberFormatException e) {
1111:                        getLog().error(
1112:                                "Malformed refresh string (no ';' but not a number): "
1113:                                        + refreshString, e);
1114:                        return;
1115:                    }
1116:                    url = getWebResponse().getUrl();
1117:                } else {
1118:                    // Format: <meta http-equiv='refresh' content='10;url=http://www.blah.com'>
1119:                    try {
1120:                        time = Integer.parseInt(refreshString.substring(0,
1121:                                index).trim());
1122:                    } catch (final NumberFormatException e) {
1123:                        getLog().error(
1124:                                "Malformed refresh string (no valid number before ';') "
1125:                                        + refreshString, e);
1126:                        return;
1127:                    }
1128:                    index = refreshString.indexOf("URL=", index);
1129:                    if (index == -1) {
1130:                        index = refreshString.indexOf("url=", index);
1131:                    }
1132:                    if (index == -1) {
1133:                        getLog().error(
1134:                                "Malformed refresh string (found ';' but no 'url='): "
1135:                                        + refreshString);
1136:                        return;
1137:                    }
1138:                    final StringBuffer buffer = new StringBuffer(refreshString
1139:                            .substring(index + 4));
1140:                    if (buffer.toString().trim().length() == 0) {
1141:                        //content='10; URL=' is treated as content='10'
1142:                        url = getWebResponse().getUrl();
1143:                    } else {
1144:                        if (buffer.charAt(0) == '"' || buffer.charAt(0) == 0x27) {
1145:                            buffer.deleteCharAt(0);
1146:                        }
1147:                        if (buffer.charAt(buffer.length() - 1) == '"'
1148:                                || buffer.charAt(buffer.length() - 1) == 0x27) {
1149:                            buffer.deleteCharAt(buffer.length() - 1);
1150:                        }
1151:                        final String urlString = buffer.toString();
1152:                        try {
1153:                            url = getFullyQualifiedUrl(urlString);
1154:                        } catch (final MalformedURLException e) {
1155:                            getLog().error(
1156:                                    "Malformed url in refresh string: "
1157:                                            + refreshString, e);
1158:                            throw e;
1159:                        }
1160:                    }
1161:                }
1162:
1163:                getWebClient().getRefreshHandler().handleRefresh(this , url,
1164:                        time);
1165:            }
1166:
1167:            /**
1168:             * Return an auto-refresh string if specified.  This will look in both the meta
1169:             * tags (taking care of &lt;noscript&gt; if any) and inside the http response headers.
1170:             * @return the auto-refresh string.
1171:             */
1172:            private String getRefreshStringOrNull() {
1173:                final Iterator iterator = getMetaTags("refresh").iterator();
1174:                final boolean javaScriptEnabled = getWebClient()
1175:                        .isJavaScriptEnabled();
1176:                while (iterator.hasNext()) {
1177:                    final HtmlMeta meta = (HtmlMeta) iterator.next();
1178:                    if ((!javaScriptEnabled || getFirstParent(meta,
1179:                            HtmlNoScript.TAG_NAME) == null)) {
1180:                        return meta.getContentAttribute();
1181:                    }
1182:                }
1183:                return getWebResponse().getResponseHeaderValue("Refresh");
1184:            }
1185:
1186:            /**
1187:             * Executes any deferred scripts, if necessary.
1188:             */
1189:            private void executeDeferredScriptsIfNeeded() {
1190:                if (!getWebClient().isJavaScriptEnabled()) {
1191:                    return;
1192:                }
1193:                if (!getWebClient().getBrowserVersion().isIE()) {
1194:                    return;
1195:                }
1196:                final List scripts = getDocumentHtmlElement()
1197:                        .getHtmlElementsByTagName("script");
1198:                for (final Iterator i = scripts.iterator(); i.hasNext();) {
1199:                    final HtmlScript script = (HtmlScript) i.next();
1200:                    final String defer = script.getDeferAttribute();
1201:                    if (defer != HtmlElement.ATTRIBUTE_NOT_DEFINED) {
1202:                        script.executeScriptIfNeeded(true);
1203:                    }
1204:                }
1205:            }
1206:
1207:            /**
1208:             * Gets the first parent with the given node name
1209:             * @param node the node to start with
1210:             * @param nodeName the name of the search node
1211:             * @return <code>null</code> if no parent found with this name
1212:             */
1213:            private DomNode getFirstParent(final DomNode node,
1214:                    final String nodeName) {
1215:                DomNode parent = node.getParentDomNode();
1216:                while (parent != null) {
1217:                    if (parent.getNodeName().equals(nodeName)) {
1218:                        return parent;
1219:                    }
1220:                    parent = parent.getParentDomNode();
1221:                }
1222:                return null;
1223:            }
1224:
1225:            /**
1226:             * Deregister frames that are no longer in use.
1227:             */
1228:            public void deregisterFramesIfNeeded() {
1229:                for (final Iterator iter = getFrames().iterator(); iter
1230:                        .hasNext();) {
1231:                    final WebWindow window = (WebWindow) iter.next();
1232:                    getWebClient().deregisterWebWindow(window);
1233:                    if (window.getEnclosedPage() instanceof  HtmlPage) {
1234:                        final HtmlPage page = (HtmlPage) window
1235:                                .getEnclosedPage();
1236:                        if (page != null) {
1237:                            // seems quite silly, but for instance if the src attribute of an iframe is not
1238:                            // set, the error only occurs when leaving the page
1239:                            page.deregisterFramesIfNeeded();
1240:                        }
1241:                    }
1242:                }
1243:            }
1244:
1245:            /**
1246:             * Return a list containing all the frames (from frame and iframe tags) in this page.
1247:             * @return a list of {@link FrameWindow}
1248:             */
1249:            public List getFrames() {
1250:                final List list = new ArrayList();
1251:                final WebWindow enclosingWindow = getEnclosingWindow();
1252:                for (final Iterator iter = getWebClient().getWebWindows()
1253:                        .iterator(); iter.hasNext();) {
1254:                    final WebWindow window = (WebWindow) iter.next();
1255:                    // quite strange but for a TopLevelWindow parent == self
1256:                    if (enclosingWindow == window.getParentWindow()
1257:                            && enclosingWindow != window) {
1258:                        list.add(window);
1259:                    }
1260:                }
1261:                return list;
1262:            }
1263:
1264:            /**
1265:             * Returns the first frame contained in this page with the specified name.
1266:             * @param name The name to search for
1267:             * @return The first frame found.
1268:             * @exception ElementNotFoundException If no frame exist in this page with the specified name.
1269:             */
1270:            public FrameWindow getFrameByName(final String name)
1271:                    throws ElementNotFoundException {
1272:                final List frames = getFrames();
1273:                for (final Iterator iter = frames.iterator(); iter.hasNext();) {
1274:                    final FrameWindow frame = (FrameWindow) iter.next();
1275:                    if (frame.getName().equals(name)) {
1276:                        return frame;
1277:                    }
1278:                }
1279:
1280:                throw new ElementNotFoundException("frame or iframe", "name",
1281:                        name);
1282:            }
1283:
1284:            /**
1285:             * Simulate pressing an access key.  This may change the focus, may click buttons and may invoke
1286:             * javascript.
1287:             *
1288:             * @param accessKey The key that will be pressed.
1289:             * @return The element that has the focus after pressing this access key or null if no element
1290:             * has the focus.
1291:             * @throws IOException If an io error occurs during the processing of this access key.  This
1292:             * would only happen if the access key triggered a button which in turn caused a page load.
1293:             */
1294:            public HtmlElement pressAccessKey(final char accessKey)
1295:                    throws IOException {
1296:                final HtmlElement element = getHtmlElementByAccessKey(accessKey);
1297:                if (element != null) {
1298:                    element.focus();
1299:                    final Page newPage;
1300:                    if (element instanceof  HtmlAnchor) {
1301:                        newPage = ((HtmlAnchor) element).click();
1302:                    } else if (element instanceof  HtmlArea) {
1303:                        newPage = ((HtmlArea) element).click();
1304:                    } else if (element instanceof  HtmlButton) {
1305:                        newPage = ((HtmlButton) element).click();
1306:                    } else if (element instanceof  HtmlInput) {
1307:                        newPage = ((HtmlInput) element).click();
1308:                    } else if (element instanceof  HtmlLabel) {
1309:                        newPage = ((HtmlLabel) element).click();
1310:                    } else if (element instanceof  HtmlLegend) {
1311:                        newPage = ((HtmlLegend) element).click();
1312:                    } else if (element instanceof  HtmlTextArea) {
1313:                        newPage = ((HtmlTextArea) element).click();
1314:                    } else {
1315:                        newPage = this ;
1316:                    }
1317:
1318:                    if (newPage != this  && getElementWithFocus() == element) {
1319:                        // The page was reloaded therefore no element on this page will have the focus.
1320:                        getElementWithFocus().blur();
1321:                    }
1322:                }
1323:
1324:                return getElementWithFocus();
1325:            }
1326:
1327:            /**
1328:             * Move the focus to the next element in the tab order.  To determine the specified tab
1329:             * order, refer to {@link HtmlPage#getTabbableElements()}
1330:             *
1331:             * @return The element that has focus after calling this method.
1332:             */
1333:            public HtmlElement tabToNextElement() {
1334:                final List elements = getTabbableElements();
1335:                if (elements.isEmpty()) {
1336:                    moveFocusToElement(null);
1337:                    return null;
1338:                }
1339:
1340:                final HtmlElement elementToGiveFocus;
1341:                final HtmlElement elementWithFocus = getElementWithFocus();
1342:                if (elementWithFocus == null) {
1343:                    elementToGiveFocus = (HtmlElement) elements.get(0);
1344:                } else {
1345:                    final int index = elements.indexOf(elementWithFocus);
1346:                    if (index == -1) {
1347:                        // The element with focus isn't on this page
1348:                        elementToGiveFocus = (HtmlElement) elements.get(0);
1349:                    } else {
1350:                        if (index == elements.size() - 1) {
1351:                            elementToGiveFocus = (HtmlElement) elements.get(0);
1352:                        } else {
1353:                            elementToGiveFocus = (HtmlElement) elements
1354:                                    .get(index + 1);
1355:                        }
1356:                    }
1357:                }
1358:
1359:                moveFocusToElement(elementToGiveFocus);
1360:                return elementToGiveFocus;
1361:            }
1362:
1363:            /**
1364:             * Move the focus to the previous element in the tab order.  To determine the specified tab
1365:             * order, refer to {@link HtmlPage#getTabbableElements()}
1366:             *
1367:             * @return The element that has focus after calling this method.
1368:             */
1369:            public HtmlElement tabToPreviousElement() {
1370:                final List elements = getTabbableElements();
1371:                if (elements.isEmpty()) {
1372:                    moveFocusToElement(null);
1373:                    return null;
1374:                }
1375:
1376:                final HtmlElement elementToGiveFocus;
1377:                final HtmlElement elementWithFocus = getElementWithFocus();
1378:                if (elementWithFocus == null) {
1379:                    elementToGiveFocus = (HtmlElement) elements.get(elements
1380:                            .size() - 1);
1381:                } else {
1382:                    final int index = elements.indexOf(elementWithFocus);
1383:                    if (index == -1) {
1384:                        // The element with focus isn't on this page
1385:                        elementToGiveFocus = (HtmlElement) elements
1386:                                .get(elements.size() - 1);
1387:                    } else {
1388:                        if (index == 0) {
1389:                            elementToGiveFocus = (HtmlElement) elements
1390:                                    .get(elements.size() - 1);
1391:                        } else {
1392:                            elementToGiveFocus = (HtmlElement) elements
1393:                                    .get(index - 1);
1394:                        }
1395:                    }
1396:                }
1397:
1398:                moveFocusToElement(elementToGiveFocus);
1399:                return elementToGiveFocus;
1400:            }
1401:
1402:            /**
1403:             * Returns the HTML element with the specified ID. If more than one element
1404:             * has this ID (not allowed by the HTML spec), then this method returns the
1405:             * first one.
1406:             *
1407:             * @param id the ID value to search by
1408:             * @return the HTML element with the specified ID
1409:             * @throws ElementNotFoundException if no element was found that matches the id
1410:             */
1411:            public HtmlElement getHtmlElementById(final String id)
1412:                    throws ElementNotFoundException {
1413:                final List elements = (List) idMap_.get(id);
1414:                if (elements != null) {
1415:                    return (HtmlElement) elements.get(0);
1416:                }
1417:                throw new ElementNotFoundException("*", "id", id);
1418:            }
1419:
1420:            /**
1421:             * Returns the HTML elements with the specified name attribute. If there are no elements
1422:             * with the specified name, this method returns an empty list. Please note that
1423:             * the lists returned by this method are immutable.
1424:             *
1425:             * @param name the name value to search by
1426:             * @return the HTML elements with the specified name attribute
1427:             */
1428:            public List getHtmlElementsByName(final String name) {
1429:                final List list = (List) nameMap_.get(name);
1430:                if (list != null) {
1431:                    return Collections.unmodifiableList(list);
1432:                } else {
1433:                    return Collections.EMPTY_LIST;
1434:                }
1435:            }
1436:
1437:            /**
1438:             * Returns the HTML elements with the specified string for their name or ID. If there are
1439:             * no elements with the specified name or ID, this method returns an empty list. Please note
1440:             * that lists returned by this method are immutable.
1441:             *
1442:             * @param idAndOrName the value to search for
1443:             * @return the HTML elements with the specified string for their name or ID
1444:             */
1445:            public List getHtmlElementsByIdAndOrName(final String idAndOrName) {
1446:                final List list1 = (List) idMap_.get(idAndOrName);
1447:                final List list2 = (List) nameMap_.get(idAndOrName);
1448:                final List list = new ArrayList();
1449:                if (list1 != null) {
1450:                    list.addAll(list1);
1451:                }
1452:                if (list2 != null) {
1453:                    list.addAll(list2);
1454:                }
1455:                return Collections.unmodifiableList(list);
1456:            }
1457:
1458:            /**
1459:             * Adds an element to the ID and name maps, if necessary.
1460:             * @param element the element to be added to the ID and name maps
1461:             */
1462:            void addMappedElement(final HtmlElement element) {
1463:                addMappedElement(element, false);
1464:            }
1465:
1466:            /**
1467:             * Adds an element to the ID and name maps, if necessary.
1468:             * @param element the element to be added to the ID and name maps
1469:             * @param recurse indicates if children must be added too
1470:             */
1471:            void addMappedElement(final HtmlElement element,
1472:                    final boolean recurse) {
1473:                if (isDescendant(element)) {
1474:                    addElement(idMap_, element, "id", recurse);
1475:                    addElement(nameMap_, element, "name", recurse);
1476:                }
1477:            }
1478:
1479:            /**
1480:             * Checks whether the specified element is descendant of this HtmlPage or not.
1481:             */
1482:            private boolean isDescendant(final HtmlElement element) {
1483:                for (DomNode parent = element; parent != null; parent = parent
1484:                        .getParentDomNode()) {
1485:                    if (parent == this ) {
1486:                        return true;
1487:                    }
1488:                }
1489:                return false;
1490:            }
1491:
1492:            private void addElement(final Map map, final HtmlElement element,
1493:                    final String attribute, final boolean recurse) {
1494:                final String value = element.getAttributeValue(attribute);
1495:                if (!StringUtils.isEmpty(value)) {
1496:                    List elements = (List) map.get(value);
1497:                    if (elements == null) {
1498:                        elements = new Vector();
1499:                        elements.add(element);
1500:                        map.put(value, elements);
1501:                    } else if (!elements.contains(element)) {
1502:                        elements.add(element);
1503:                    }
1504:                }
1505:                if (recurse) {
1506:                    for (final Iterator i = element.getChildElementsIterator(); i
1507:                            .hasNext();) {
1508:                        final HtmlElement child = (HtmlElement) i.next();
1509:                        addElement(map, child, attribute, true);
1510:                    }
1511:                }
1512:            }
1513:
1514:            /**
1515:             * Removes an element from the ID and name maps, if necessary.
1516:             * @param element the element to be removed from the ID and name maps
1517:             */
1518:            void removeMappedElement(final HtmlElement element) {
1519:                removeMappedElement(element, false, false);
1520:            }
1521:
1522:            /**
1523:             * Removes an element and optionally its children from the ID and name maps, if necessary.
1524:             * @param element the element to be removed from the ID and name maps
1525:             * @param recurse indicates if children must be removed too
1526:             * @param descendant indicates of the element was descendant of this HtmlPage, but now its parent might be null.
1527:             */
1528:            void removeMappedElement(final HtmlElement element,
1529:                    final boolean recurse, final boolean descendant) {
1530:                if (descendant || isDescendant(element)) {
1531:                    removeElement(idMap_, element, "id", recurse);
1532:                    removeElement(nameMap_, element, "name", recurse);
1533:                }
1534:            }
1535:
1536:            private void removeElement(final Map map,
1537:                    final HtmlElement element, final String att,
1538:                    final boolean recurse) {
1539:                final String value = element.getAttributeValue(att);
1540:                if (!StringUtils.isEmpty(value)) {
1541:                    final List elements = (List) map.remove(value);
1542:                    if (elements != null && elements.size() != 1) {
1543:                        elements.remove(element);
1544:                        map.put(value, elements);
1545:                    }
1546:                }
1547:                if (recurse) {
1548:                    for (final Iterator i = element.getChildElementsIterator(); i
1549:                            .hasNext();) {
1550:                        final HtmlElement child = (HtmlElement) i.next();
1551:                        removeElement(map, child, att, true);
1552:                    }
1553:                }
1554:            }
1555:
1556:            /**
1557:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1558:             *
1559:             * @param node the node that has just been added to the document.
1560:             */
1561:            void notifyNodeAdded(final DomNode node) {
1562:                if (node instanceof  HtmlElement) {
1563:                    boolean insideNoScript = false;
1564:                    if (getWebClient().isJavaScriptEnabled()) {
1565:                        for (DomNode parent = node.getParentDomNode(); parent != null; parent = parent
1566:                                .getParentDomNode()) {
1567:                            if (parent instanceof  HtmlNoScript) {
1568:                                insideNoScript = true;
1569:                                break;
1570:                            }
1571:                        }
1572:                    }
1573:                    if (!insideNoScript) {
1574:                        addMappedElement((HtmlElement) node, true);
1575:                    }
1576:                }
1577:                node.onAddedToPage();
1578:            }
1579:
1580:            /**
1581:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1582:             *
1583:             * @param node the node that has just been removed from the tree
1584:             */
1585:            void notifyNodeRemoved(final DomNode node) {
1586:                if (node instanceof  HtmlElement) {
1587:                    removeMappedElement((HtmlElement) node, true, true);
1588:                }
1589:            }
1590:
1591:            /**
1592:             * Loads the content of the contained frames. This is done after the page is completely
1593:             * loaded to allow script contained in the frames to reference elements from the
1594:             * page located after the closing </frame> tag.
1595:             * @throws FailingHttpStatusCodeException If the server returns a failing status code AND the property
1596:             * {@link WebClient#setThrowExceptionOnFailingStatusCode(boolean)} is set to true.
1597:             */
1598:            void loadFrames() throws FailingHttpStatusCodeException {
1599:                final List frameTags = Arrays.asList(new String[] { "frame",
1600:                        "iframe" });
1601:                final List frames = getDocumentHtmlElement()
1602:                        .getHtmlElementsByTagNames(frameTags);
1603:                for (final Iterator iter = frames.iterator(); iter.hasNext();) {
1604:                    final BaseFrame frame = (BaseFrame) iter.next();
1605:                    // test if the frame should really be loaded:
1606:                    // if a script has already changed its content, it should be skipped
1607:                    // use == and not equals(...) to identify initial content (versus url set to "about:blank")
1608:                    if (frame.getEnclosedPage().getWebResponse().getUrl() == WebClient.URL_ABOUT_BLANK) {
1609:                        frame.loadInnerPage();
1610:                    }
1611:                }
1612:            }
1613:
1614:            /**
1615:             * {@inheritDoc}
1616:             */
1617:            public String asXml() {
1618:                return getDocumentHtmlElement().asXml();
1619:            }
1620:
1621:            /**
1622:             * Gives a basic representation for debugging purposes
1623:             * @return a basic representation
1624:             */
1625:            public String toString() {
1626:                final StringBuffer buffer = new StringBuffer();
1627:                buffer.append("HtmlPage(");
1628:                buffer.append(getWebResponse().getUrl());
1629:                buffer.append(")@");
1630:                buffer.append(hashCode());
1631:                return buffer.toString();
1632:            }
1633:
1634:            /**
1635:             * Move the focus to the specified component.  This will trigger any relevant javascript
1636:             * event handlers.
1637:             *
1638:             * @param newElement The element that will receive the focus, use <code>null</code> to remove focus from any element
1639:             * @return true if the specified element now has the focus.
1640:             * @see #getElementWithFocus()
1641:             * @see #tabToNextElement()
1642:             * @see #tabToPreviousElement()
1643:             * @see #pressAccessKey(char)
1644:             * @see #assertAllTabIndexAttributesSet()
1645:             */
1646:            public boolean moveFocusToElement(final HtmlElement newElement) {
1647:                if (elementWithFocus_ == newElement) {
1648:                    // nothing to do
1649:                    return true;
1650:                } else if (newElement != null && newElement.getPage() != this ) {
1651:                    throw new IllegalArgumentException(
1652:                            "Can't move focus to an element from an other page");
1653:                }
1654:
1655:                if (elementWithFocus_ != null) {
1656:                    elementWithFocus_.fireEvent(Event.TYPE_BLUR);
1657:                }
1658:
1659:                elementWithFocus_ = newElement;
1660:                if (newElement != null) {
1661:                    newElement.fireEvent(Event.TYPE_FOCUS);
1662:                }
1663:
1664:                // If a page reload happened as a result of the focus change then obviously this
1665:                // element will not have the focus because its page has gone away.
1666:                return this  == getEnclosingWindow().getEnclosedPage();
1667:            }
1668:
1669:            /**
1670:             * Return the element with the focus or null if no element has the focus.
1671:             * @return The element with focus or null.
1672:             * @see #moveFocusToElement(HtmlElement)
1673:             */
1674:            public HtmlElement getElementWithFocus() {
1675:                return elementWithFocus_;
1676:            }
1677:
1678:            /**
1679:             * Gets the meta tag for a given http-equiv value.
1680:             * @param httpEquiv the http-equiv value
1681:             * @return a list of {@link HtmlMeta}
1682:             */
1683:            protected List getMetaTags(final String httpEquiv) {
1684:                final String nameLC = httpEquiv.toLowerCase();
1685:                final List tags = getDocumentHtmlElement()
1686:                        .getHtmlElementsByTagNames(
1687:                                Collections.singletonList("meta"));
1688:                for (final Iterator iter = tags.iterator(); iter.hasNext();) {
1689:                    final HtmlMeta element = (HtmlMeta) iter.next();
1690:                    if (!nameLC.equals(element.getHttpEquivAttribute()
1691:                            .toLowerCase())) {
1692:                        iter.remove();
1693:                    }
1694:                }
1695:                return tags;
1696:            }
1697:
1698:            /**
1699:             * Select the specified radio button in the page (outside any &lt;form&gt;).
1700:             *
1701:             * @param radioButtonInput The radio Button
1702:             */
1703:            void setCheckedRadioButton(
1704:                    final HtmlRadioButtonInput radioButtonInput) {
1705:                try {
1706:                    //May be done in single xpath search?
1707:                    final List pageInputs = getByXPath("//input[lower-case(@type)='radio' "
1708:                            + "and @name='"
1709:                            + radioButtonInput.getNameAttribute() + "']");
1710:                    final List formInputs = getByXPath("//form//input[lower-case(@type)='radio' "
1711:                            + "and @name='"
1712:                            + radioButtonInput.getNameAttribute() + "']");
1713:
1714:                    pageInputs.removeAll(formInputs);
1715:
1716:                    for (final Iterator iterator = pageInputs.iterator(); iterator
1717:                            .hasNext();) {
1718:                        final HtmlRadioButtonInput input = (HtmlRadioButtonInput) iterator
1719:                                .next();
1720:                        if (input == radioButtonInput) {
1721:                            input.setAttributeValue("checked", "checked");
1722:                        } else {
1723:                            input.removeAttribute("checked");
1724:                        }
1725:                    }
1726:                } catch (final JaxenException e) {
1727:                    getLog().error(e);
1728:                }
1729:            }
1730:
1731:            /**
1732:             * Creates a clone of this instance, and clears cached state
1733:             * to be not shared with the original.
1734:             *
1735:             * @return a clone of this instance.
1736:             */
1737:            protected Object clone() {
1738:                try {
1739:                    final HtmlPage result = (HtmlPage) super .clone();
1740:                    result.documentElement_ = null;
1741:                    result.elementWithFocus_ = null;
1742:                    result.idMap_ = new HashMap();
1743:                    result.nameMap_ = new HashMap();
1744:                    return result;
1745:                } catch (final CloneNotSupportedException e) {
1746:                    throw new IllegalStateException("Clone not supported");
1747:                }
1748:            }
1749:
1750:            /**
1751:             * Override cloneNode to add cloned elements to the clone, not to the original.
1752:             * {@inheritDoc}
1753:             * @deprecated
1754:             */
1755:            public DomNode cloneNode(final boolean deep) {
1756:                final HtmlPage result = (HtmlPage) super .cloneDomNode(deep);
1757:                if (deep) {
1758:                    // fix up idMap_ and result's idMap_s
1759:                    final Iterator it = result.getAllHtmlChildElements();
1760:                    while (it.hasNext()) {
1761:                        final HtmlElement child = (HtmlElement) it.next();
1762:                        removeMappedElement(child);
1763:                        result.addMappedElement(child);
1764:                    }
1765:                }
1766:                return result;
1767:            }
1768:
1769:            /**
1770:             * Adds an HtmlAttributeChangeListener to the listener list.
1771:             * The listener is registered for all attributes of all HtmlElements contained in this page.
1772:             *
1773:             * @param listener the attribute change listener to be added.
1774:             * @see #removeHtmlAttributeChangeListener(HtmlAttributeChangeListener)
1775:             */
1776:            public void addHtmlAttributeChangeListener(
1777:                    final HtmlAttributeChangeListener listener) {
1778:                Assert.notNull("listener", listener);
1779:                synchronized (lock_) {
1780:                    if (attributeListeners_ == null) {
1781:                        attributeListeners_ = new ArrayList();
1782:                    }
1783:                    if (!attributeListeners_.contains(listener)) {
1784:                        attributeListeners_.add(listener);
1785:                    }
1786:                }
1787:            }
1788:
1789:            /**
1790:             * Removes an HtmlAttributeChangeListener from the listener list.
1791:             * This method should be used to remove HtmlAttributeChangeListener that were registered
1792:             * for all attributes of all HtmlElements contained in this page.
1793:             *
1794:             * @param listener the attribute change listener to be removed.
1795:             * @see #addHtmlAttributeChangeListener(HtmlAttributeChangeListener)
1796:             */
1797:            public void removeHtmlAttributeChangeListener(
1798:                    final HtmlAttributeChangeListener listener) {
1799:                Assert.notNull("listener", listener);
1800:                synchronized (lock_) {
1801:                    if (attributeListeners_ != null) {
1802:                        attributeListeners_.remove(listener);
1803:                    }
1804:                }
1805:            }
1806:
1807:            /**
1808:             * Notifies all registered listeners for the given event to add an attribute.
1809:             * @param event the event to fire
1810:             */
1811:            void fireHtmlAttributeAdded(final HtmlAttributeChangeEvent event) {
1812:                final List listeners = safeGetAttributeListeners();
1813:                if (listeners != null) {
1814:                    for (final Iterator iterator = listeners.iterator(); iterator
1815:                            .hasNext();) {
1816:                        final HtmlAttributeChangeListener listener = (HtmlAttributeChangeListener) iterator
1817:                                .next();
1818:                        listener.attributeAdded(event);
1819:                    }
1820:                }
1821:            }
1822:
1823:            /**
1824:             * Notifies all registered listeners for the given event to replace an attribute.
1825:             * @param event the event to fire
1826:             */
1827:            void fireHtmlAttributeReplaced(final HtmlAttributeChangeEvent event) {
1828:                final List listeners = safeGetAttributeListeners();
1829:                if (listeners != null) {
1830:                    for (final Iterator iterator = listeners.iterator(); iterator
1831:                            .hasNext();) {
1832:                        final HtmlAttributeChangeListener listener = (HtmlAttributeChangeListener) iterator
1833:                                .next();
1834:                        listener.attributeReplaced(event);
1835:                    }
1836:                }
1837:            }
1838:
1839:            /**
1840:             * Notifies all registered listeners for the given event to remove an attribute.
1841:             * @param event the event to fire
1842:             */
1843:            void fireHtmlAttributeRemoved(final HtmlAttributeChangeEvent event) {
1844:                final List listeners = safeGetAttributeListeners();
1845:                if (listeners != null) {
1846:                    for (final Iterator iterator = listeners.iterator(); iterator
1847:                            .hasNext();) {
1848:                        final HtmlAttributeChangeListener listener = (HtmlAttributeChangeListener) iterator
1849:                                .next();
1850:                        listener.attributeRemoved(event);
1851:                    }
1852:                }
1853:            }
1854:
1855:            private List safeGetAttributeListeners() {
1856:                synchronized (lock_) {
1857:                    if (attributeListeners_ != null) {
1858:                        return new ArrayList(attributeListeners_);
1859:                    } else {
1860:                        return null;
1861:                    }
1862:                }
1863:            }
1864:
1865:            /**
1866:             * <span style="color:red">INTERNAL API - SUBJECT TO CHANGE AT ANY TIME - USE AT YOUR OWN RISK.</span><br/>
1867:             *
1868:             * @return true if the OnbeforeunloadHandler has accepted to change the page.
1869:             */
1870:            public boolean isOnbeforeunloadAccepted() {
1871:                return executeEventHandlersIfNeeded(Event.TYPE_BEFORE_UNLOAD);
1872:            }
1873:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.