2
* Copyright (c) 2002-2008 Gargoyle Software Inc.
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
* you may not use this file except in compliance with the License.
6
* You may obtain a copy of the License at
7
* http://www.apache.org/licenses/LICENSE-2.0
9
* Unless required by applicable law or agreed to in writing, software
10
* distributed under the License is distributed on an "AS IS" BASIS,
11
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
* See the License for the specific language governing permissions and
13
* limitations under the License.
15
package com.gargoylesoftware.htmlunit.javascript.host;
17
import java.io.IOException;
18
import java.util.List;
20
import org.mozilla.javascript.Scriptable;
22
import com.gargoylesoftware.htmlunit.Page;
23
import com.gargoylesoftware.htmlunit.WebAssert;
24
import com.gargoylesoftware.htmlunit.html.HtmlButton;
25
import com.gargoylesoftware.htmlunit.html.HtmlElement;
26
import com.gargoylesoftware.htmlunit.html.HtmlForm;
27
import com.gargoylesoftware.htmlunit.html.HtmlImage;
28
import com.gargoylesoftware.htmlunit.html.HtmlInput;
29
import com.gargoylesoftware.htmlunit.html.HtmlPage;
30
import com.gargoylesoftware.htmlunit.html.HtmlSelect;
31
import com.gargoylesoftware.htmlunit.html.HtmlTextArea;
32
import com.gargoylesoftware.htmlunit.html.SubmittableElement;
35
* A JavaScript object for a Form.
37
* @version $Revision: 3969 $
38
* @author <a href="mailto:mbowler@GargoyleSoftware.com">Mike Bowler</a>
39
* @author Daniel Gredler
41
* @author Chris Erskine
42
* @author Marc Guillemot
43
* @author Ahmed Ashour
44
* @author Sudhan Moghe
46
* @see <a href="http://msdn.microsoft.com/en-us/library/ms535249.aspx">MSDN documentation</a>
48
public class HTMLFormElement extends HTMLElement {
50
private static final long serialVersionUID = -1860993922147246513L;
51
private HTMLCollection elements_; // has to be a member to have equality (==) working
54
* Creates an instance. A default constructor is required for all JavaScript objects.
56
public HTMLFormElement() { }
59
* JavaScript constructor. This must be declared in every JavaScript file because
60
* the rhino engine won't walk up the hierarchy looking for constructors.
62
public final void jsConstructor() {
69
public void setHtmlElement(final HtmlElement htmlElement) {
70
super.setHtmlElement(htmlElement);
71
final HtmlForm htmlForm = getHtmlForm();
72
htmlForm.setScriptObject(this);
76
* Returns the value of the JavaScript attribute "name".
77
* @return the value of this attribute
79
public String jsxGet_name() {
80
return getHtmlForm().getNameAttribute();
84
* Sets the value of the JavaScript attribute "name".
85
* @param name the new value
87
public void jsxSet_name(final String name) {
88
WebAssert.notNull("name", name);
89
getHtmlForm().setNameAttribute(name);
93
* Returns the value of the JavaScript attribute "elements".
94
* @return the value of this attribute
96
public HTMLCollection jsxGet_elements() {
97
if (elements_ == null) {
98
final HtmlForm htmlForm = getHtmlForm();
100
elements_ = new HTMLCollection(this) {
101
private static final long serialVersionUID = -2554743215194459203L;
104
protected List<Object> computeElements() {
105
final List<Object> response = super.computeElements();
106
response.addAll(htmlForm.getLostChildren());
110
final String xpath = ".//*[(name() = 'input' or name() = 'button'"
111
+ " or name() = 'select' or name() = 'textarea')]";
112
elements_.init(htmlForm, xpath);
119
* Returns the value of the JavaScript attribute "length".
120
* Does not count input type=image elements as browsers (IE6, Mozilla 1.7) do
121
* (cf <a href="http://msdn.microsoft.com/en-us/library/ms534101.aspx">MSDN doc</a>)
122
* @return the value of this attribute
124
public int jsxGet_length() {
125
final int all = jsxGet_elements().jsxGet_length();
126
final int images = getHtmlForm().getElementsByAttribute("input", "type", "image").size();
131
* Returns the value of the JavaScript attribute "action".
132
* @return the value of this attribute
134
public String jsxGet_action() {
135
return getHtmlForm().getActionAttribute();
139
* Sets the value of the JavaScript attribute "action".
140
* @param action the new value
142
public void jsxSet_action(final String action) {
143
WebAssert.notNull("action", action);
144
getHtmlForm().setActionAttribute(action);
148
* Returns the value of the JavaScript attribute "method".
149
* @return the value of this attribute
151
public String jsxGet_method() {
152
return getHtmlForm().getMethodAttribute();
156
* Sets the value of the JavaScript attribute "method".
157
* @param method the new value
159
public void jsxSet_method(final String method) {
160
WebAssert.notNull("method", method);
161
getHtmlForm().setMethodAttribute(method);
165
* Returns the value of the JavaScript attribute "target".
166
* @return the value of this attribute
168
public String jsxGet_target() {
169
return getHtmlForm().getTargetAttribute();
173
* Returns the <tt>onsubmit</tt> event handler for this element.
174
* @return the <tt>onsubmit</tt> event handler for this element
176
public Object jsxGet_onsubmit() {
177
return getEventHandlerProp("onsubmit");
181
* Sets the <tt>onsubmit</tt> event handler for this element.
182
* @param onsubmit the <tt>onsubmit</tt> event handler for this element
184
public void jsxSet_onsubmit(final Object onsubmit) {
185
setEventHandlerProp("onsubmit", onsubmit);
189
* Sets the value of the JavaScript attribute "target".
190
* @param target the new value
192
public void jsxSet_target(final String target) {
193
WebAssert.notNull("target", target);
194
getHtmlForm().setTargetAttribute(target);
198
* Returns the value of the JavaScript attribute "encoding".
199
* @return the value of this attribute
201
public String jsxGet_encoding() {
202
return getHtmlForm().getEnctypeAttribute();
206
* Sets the value of the JavaScript attribute "encoding".
207
* @param encoding the new value
209
public void jsxSet_encoding(final String encoding) {
210
WebAssert.notNull("encoding", encoding);
211
getHtmlForm().setEnctypeAttribute(encoding);
214
private HtmlForm getHtmlForm() {
215
return (HtmlForm) getHtmlElementOrDie();
221
* @throws IOException if an io error occurs
223
public void jsxFunction_submit() throws IOException {
224
getHtmlForm().submit((SubmittableElement) null);
230
public void jsxFunction_reset() {
231
getHtmlForm().reset();
235
* Overridden to allow the retrieval of certain form elements by ID or name.
237
* @param name {@inheritDoc}
238
* @return {@inheritDoc}
241
protected Object getWithPreemption(final String name) {
242
final HtmlForm form = getHtmlForm();
243
final Page page = form.getPage();
244
if (page instanceof HtmlPage) {
245
// Try to satisfy this request using a map-backed operation before punting and using XPath.
246
// XPath operations are very expensive, and this method gets invoked quite a bit.
247
// Approach: Try to match the string to a name or ID, accepting only inputs (not type=image),
248
// buttons, selects and textareas that are in this form. We also include img elements
249
// (the second XPath search below) in the search, because any results with more than one element
250
// will end up using the XPath search anyway, so it doesn't hurt when looking for single elements.
251
final List<HtmlElement> elements = ((HtmlPage) page).getElementsByIdAndOrName(name);
252
if (elements.isEmpty()) {
255
if (elements.size() == 1) {
256
final HtmlElement element = elements.get(0);
257
final String tagName = element.getTagName();
258
final String type = element.getAttribute("type").toLowerCase();
259
if ((HtmlInput.TAG_NAME.equals(tagName) && !"image".equals(type))
260
|| HtmlButton.TAG_NAME.equals(tagName)
261
|| HtmlSelect.TAG_NAME.equals(tagName)
262
|| HtmlTextArea.TAG_NAME.equals(tagName)
263
|| HtmlImage.TAG_NAME.equals(tagName)) {
264
if (form.isAncestorOf(element) || form.getLostChildren().contains(element)) {
265
return getScriptableFor(element);
273
// The shortcut wasn't enough, which means we probably need to perform the XPath operation anyway.
274
// Note that the XPath expression below HAS TO MATCH the tag name checks performed in the shortcut above.
275
// Approach: Try to match the string to a name or ID, accepting only inputs (not type=image),
276
// buttons, selects and textareas that are in this form. We *don't* include img elements, which will
277
// only be searched if the first search fails.
278
HTMLCollection collection = new HTMLCollection(this);
279
final String xpath = ".//*[(@name = '" + name + "' or @id = '" + name + "')"
280
+ " and ((name() = 'input' and translate(@type, 'IMAGE', 'image') != 'image') or name() = 'button'"
281
+ " or name() = 'select' or name() = 'textarea')]";
282
collection.init(form, xpath);
283
int length = collection.jsxGet_length();
284
// If no form fields are found, IE and Firefox are able to find img elements by ID or name.
286
collection = new HTMLCollection(this);
287
final String xpath2 = ".//*[(@name = '" + name + "' or @id = '" + name + "')"
288
+ " and name() = 'img']";
289
collection.init(form, xpath2);
291
// Return whatever we have at this point.
292
Object result = collection;
293
length = collection.jsxGet_length();
297
else if (length == 1) {
298
result = collection.get(0, collection);
304
* Returns the specified indexed property.
305
* @param index the index of the property
306
* @param start the scriptable object that was originally queried for this property
307
* @return the property
310
public Object get(final int index, final Scriptable start) {
311
if (getDomNodeOrNull() == null) {
312
return NOT_FOUND; // typically for the prototype
314
return jsxGet_elements().get(index, ((HTMLFormElement) start).jsxGet_elements());