Package com.gargoylesoftware.htmlunit.javascript.host.html

Source Code of com.gargoylesoftware.htmlunit.javascript.host.html.HTMLFormElement

/*
* Copyright (c) 2002-2010 Gargoyle Software Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.gargoylesoftware.htmlunit.javascript.host.html;

import java.io.IOException;
import java.net.MalformedURLException;
import java.util.Iterator;
import java.util.List;

import net.sourceforge.htmlunit.corejs.javascript.Scriptable;

import org.apache.commons.lang.StringUtils;

import com.gargoylesoftware.htmlunit.Page;
import com.gargoylesoftware.htmlunit.WebAssert;
import com.gargoylesoftware.htmlunit.WebClient;
import com.gargoylesoftware.htmlunit.WebRequestSettings;
import com.gargoylesoftware.htmlunit.html.HtmlButton;
import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlForm;
import com.gargoylesoftware.htmlunit.html.HtmlImage;
import com.gargoylesoftware.htmlunit.html.HtmlInput;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import com.gargoylesoftware.htmlunit.html.HtmlSelect;
import com.gargoylesoftware.htmlunit.html.HtmlTextArea;

/**
* A JavaScript object for a Form.
*
* @version $Revision: 5373 $
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
* @author Daniel Gredler
* @author Kent Tong
* @author Chris Erskine
* @author Marc Guillemot
* @author Ahmed Ashour
* @author Sudhan Moghe
*
* @see <a href="http://msdn.microsoft.com/en-us/library/ms535249.aspx">MSDN documentation</a>
*/
public class HTMLFormElement extends HTMLElement {

    private static final long serialVersionUID = -1860993922147246513L;
    private HTMLCollection elements_; // has to be a member to have equality (==) working

    /**
     * Creates an instance. A default constructor is required for all JavaScript objects.
     */
    public HTMLFormElement() { }

    /**
     * JavaScript constructor. This must be declared in every JavaScript file because
     * the rhino engine won't walk up the hierarchy looking for constructors.
     */
    public final void jsConstructor() {
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void setHtmlElement(final HtmlElement htmlElement) {
        super.setHtmlElement(htmlElement);
        final HtmlForm htmlForm = getHtmlForm();
        htmlForm.setScriptObject(this);
    }

    /**
     * Returns the value of the JavaScript attribute "name".
     * @return the value of this attribute
     */
    public String jsxGet_name() {
        return getHtmlForm().getNameAttribute();
    }

    /**
     * Sets the value of the JavaScript attribute "name".
     * @param name the new value
     */
    public void jsxSet_name(final String name) {
        WebAssert.notNull("name", name);
        getHtmlForm().setNameAttribute(name);
    }

    /**
     * Returns the value of the JavaScript attribute "elements".
     * @return the value of this attribute
     */
    public HTMLCollection jsxGet_elements() {
        if (elements_ == null) {
            final HtmlForm htmlForm = getHtmlForm();

            elements_ = new HTMLCollection(this) {
                private static final long serialVersionUID = -2554743215194459203L;

                @Override
                protected List<Object> computeElements() {
                    final List<Object> response = super.computeElements();
                    response.addAll(htmlForm.getLostChildren());
                    return response;
                }
            };
            final String xpath = ".//*[(name() = 'input' or name() = 'button'"
                    + " or name() = 'select' or name() = 'textarea')]";
            elements_.init(htmlForm, xpath);

        }
        return elements_;
    }

    /**
     * Returns the value of the JavaScript attribute "length".
     * Does not count input type=image elements as browsers (IE6, Mozilla 1.7) do
     * (cf <a href="http://msdn.microsoft.com/en-us/library/ms534101.aspx">MSDN doc</a>)
     * @return the value of this attribute
     */
    public int jsxGet_length() {
        final int all = jsxGet_elements().jsxGet_length();
        final int images = getHtmlForm().getElementsByAttribute("input", "type", "image").size();
        return all - images;
    }

    /**
     * Returns the value of the JavaScript attribute "action".
     * @return the value of this attribute
     */
    public String jsxGet_action() {
        String action = getHtmlForm().getActionAttribute();
        if (getBrowserVersion().isFirefox()) {
            try {
                action = ((HtmlPage) getHtmlForm().getPage()).getFullyQualifiedUrl(action).toExternalForm();
            }
            catch (final MalformedURLException e) {
                // nothing, return action attribute
            }
        }

        return action;
    }

    /**
     * Sets the value of the JavaScript attribute "action".
     * @param action the new value
     */
    public void jsxSet_action(final String action) {
        WebAssert.notNull("action", action);
        getHtmlForm().setActionAttribute(action);
    }

    /**
     * Returns the value of the JavaScript attribute "method".
     * @return the value of this attribute
     */
    public String jsxGet_method() {
        return getHtmlForm().getMethodAttribute();
    }

    /**
     * Sets the value of the JavaScript attribute "method".
     * @param method the new value
     */
    public void jsxSet_method(final String method) {
        WebAssert.notNull("method", method);
        getHtmlForm().setMethodAttribute(method);
    }

    /**
     * Returns the value of the JavaScript attribute "target".
     * @return the value of this attribute
     */
    public String jsxGet_target() {
        return getHtmlForm().getTargetAttribute();
    }

    /**
     * Returns the <tt>onsubmit</tt> event handler for this element.
     * @return the <tt>onsubmit</tt> event handler for this element
     */
    public Object jsxGet_onsubmit() {
        return getEventHandlerProp("onsubmit");
    }

    /**
     * Sets the <tt>onsubmit</tt> event handler for this element.
     * @param onsubmit the <tt>onsubmit</tt> event handler for this element
     */
    public void jsxSet_onsubmit(final Object onsubmit) {
        setEventHandlerProp("onsubmit", onsubmit);
    }

    /**
     * Sets the value of the JavaScript attribute "target".
     * @param target the new value
     */
    public void jsxSet_target(final String target) {
        WebAssert.notNull("target", target);
        getHtmlForm().setTargetAttribute(target);
    }

    /**
     * Returns the value of the JavaScript attribute "encoding".
     * @return the value of this attribute
     */
    public String jsxGet_encoding() {
        return getHtmlForm().getEnctypeAttribute();
    }

    /**
     * Sets the value of the JavaScript attribute "encoding".
     * @param encoding the new value
     */
    public void jsxSet_encoding(final String encoding) {
        WebAssert.notNull("encoding", encoding);
        getHtmlForm().setEnctypeAttribute(encoding);
    }

    private HtmlForm getHtmlForm() {
        return (HtmlForm) getDomNodeOrDie();
    }

    /**
     * Submits the form (at the end of the current script execution).
     *
     * @throws IOException if an IO error occurs
     */
    public void jsxFunction_submit()
        throws IOException {

        final HtmlPage page = (HtmlPage) getDomNodeOrDie().getPage();
        final WebClient webClient = page.getWebClient();

        final String action = getHtmlForm().getActionAttribute();
        if (action.startsWith("javascript:")) {
            final String js = StringUtils.substringAfter(action, "javascript:");
            webClient.getJavaScriptEngine().execute(page, js, "Form action", 0);
        }
        else {
            // download should be done ASAP, response will be loaded into a window later
            final WebRequestSettings requestSettings = getHtmlForm().getWebRequestSettings(null);
            final String target = page.getResolvedTarget(jsxGet_target());
            webClient.download(page.getEnclosingWindow(), target, requestSettings, "JS form.submit()");
        }
    }

    /**
     * Retrieves a form object or an object from an elements collection.
     * @param index Integer or String that specifies the object or collection to retrieve.
     *              If this parameter is an integer, it is the zero-based index of the object.
     *              If this parameter is a string, all objects with matching name or id properties are retrieved,
     *              and a collection is returned if more than one match is made
     * @param subIndex Optional. Integer that specifies the zero-based index of the object to retrieve
     *              when a collection is returned
     * @return an object or a collection of objects if successful, or null otherwise
     */
    public Object jsxFunction_item(final Object index, final Object subIndex) {
        if (index instanceof Number) {
            return jsxGet_elements().jsxFunction_item(index);
        }
        final HtmlForm htmlForm = getHtmlForm();

        final HTMLCollection elements = new HTMLCollection(this) {
            private static final long serialVersionUID = -2554743215194459203L;

            @Override
            protected List<Object> computeElements() {
                final List<Object> response = super.computeElements();
                response.addAll(htmlForm.getLostChildren());
                return response;
            }
        };
        final String xpath = ".//*[((name() = 'input' or name() = 'button'"
                + " or name() = 'select' or name() = 'textarea')) and @name='" + index + "']";
        elements.init(htmlForm, xpath);
        if (elements.getLength() == 0) {
            return null;
        }
        else if (elements.getLength() == 1) {
            return elements.jsxFunction_item(0);
        }
        if (subIndex instanceof Number) {
            return elements.jsxFunction_item(subIndex);
        }
        return elements;
    }

    /**
     * Resets this form.
     */
    public void jsxFunction_reset() {
        getHtmlForm().reset();
    }

    /**
     * Overridden to allow the retrieval of certain form elements by ID or name.
     *
     * @param name {@inheritDoc}
     * @return {@inheritDoc}
     */
    @Override
    protected Object getWithPreemption(final String name) {
        final HtmlForm form = getHtmlForm();
        final Page page = form.getPage();
        if (page instanceof HtmlPage) {
            // Try to satisfy this request using a map-backed operation before punting and using XPath.
            // XPath operations are very expensive, and this method gets invoked quite a bit.
            // Approach: Try to match the string to a name or ID, accepting only inputs (not type=image),
            // buttons, selects and textareas that are in this form. We also include img elements
            // (the second XPath search below) in the search, because any results with more than one element
            // will end up using the XPath search anyway, so it doesn't hurt when looking for single elements.
            final List<HtmlElement> elements = ((HtmlPage) page).getElementsByIdAndOrName(name);
            if (elements.isEmpty()) {
                return NOT_FOUND;
            }

            // filter out elements that can't be accessed this way
            for (final Iterator<HtmlElement> iter = elements.iterator(); iter.hasNext();) {
                if (!isAccessibleByIdOrName(form, iter.next())) {
                    iter.remove();
                }
            }
            if (elements.isEmpty()) {
                return NOT_FOUND;
            }
            if (elements.size() == 1) {
                return getScriptableFor(elements.get(0));
            }
        }
        // The shortcut wasn't enough, which means we probably need to perform the XPath operation anyway.
        // Note that the XPath expression below HAS TO MATCH the tag name checks performed in the shortcut above.
        // Approach: Try to match the string to a name or ID, accepting only inputs (not type=image),
        // buttons, selects and textareas that are in this form. We *don't* include img elements, which will
        // only be searched if the first search fails.
        // See IsDescendantOfContextualFormFunction for info on the "is-descendant-of-contextual-form()" function.
        HTMLCollection collection = new HTMLCollection(this);
        final String xpath = "//*[is-descendant-of-contextual-form()"
            + " and (@name = '" + name + "' or @id = '" + name + "')"
            + " and ((name() = 'input' and translate(@type, 'IMAGE', 'image') != 'image') or name() = 'button'"
            + " or name() = 'select' or name() = 'textarea')]";
        collection.init(form, xpath);
        int length = collection.jsxGet_length();
        // If no form fields are found, IE and Firefox are able to find img elements by ID or name.
        if (length == 0) {
            collection = new HTMLCollection(this);
            final String xpath2 = "//*[is-descendant-of-contextual-form()"
                + " and (@name = '" + name + "' or @id = '" + name + "')"
                + " and name() = 'img']";
            collection.init(form, xpath2);
        }
        // Return whatever we have at this point.
        Object result = collection;
        length = collection.jsxGet_length();
        if (length == 0) {
            result = NOT_FOUND;
        }
        else if (length == 1) {
            result = collection.get(0, collection);
        }
        return result;
    }

    /**
     * Indicates if the element can be reached by id or name in expressions like "myForm.myField".
     * @param form the owning form
     * @param element the element to test
     * @return <code>true</code> if this element matches the conditions
     */
    private boolean isAccessibleByIdOrName(final HtmlForm form, final HtmlElement element) {
        final String tagName = element.getTagName();
        final String type = element.getAttribute("type").toLowerCase();
        if ((HtmlInput.TAG_NAME.equals(tagName) && !"image".equals(type))
                || HtmlButton.TAG_NAME.equals(tagName)
                || HtmlSelect.TAG_NAME.equals(tagName)
                || HtmlTextArea.TAG_NAME.equals(tagName)
                || HtmlImage.TAG_NAME.equals(tagName)) {
            if (form.isAncestorOf(element) || form.getLostChildren().contains(element)) {
                return true;
            }
        }
        return false;
    }

    /**
     * Returns the specified indexed property.
     * @param index the index of the property
     * @param start the scriptable object that was originally queried for this property
     * @return the property
     */
    @Override
    public Object get(final int index, final Scriptable start) {
        if (getDomNodeOrNull() == null) {
            return NOT_FOUND; // typically for the prototype
        }
        return jsxGet_elements().get(index, ((HTMLFormElement) start).jsxGet_elements());
    }
}
TOP

Related Classes of com.gargoylesoftware.htmlunit.javascript.host.html.HTMLFormElement

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.