Files
tubestation/content/html/content/src/nsHTMLInputElement.cpp

2114 lines
62 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: NPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Netscape Public License
* Version 1.1 (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.mozilla.org/NPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is Mozilla Communicator client code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Pierre Phaneuf <pp@ludusdesign.com>
*
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the NPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the NPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsCOMPtr.h"
#include "nsIDOMHTMLInputElement.h"
#include "nsIDOMNSHTMLInputElement.h"
#include "nsITextControlElement.h"
#include "nsIControllers.h"
#include "nsIEditorController.h"
#include "nsIFocusController.h"
#include "nsPIDOMWindow.h"
#include "nsIScriptGlobalObject.h"
#include "nsContentCID.h"
#include "nsIComponentManager.h"
#include "nsIDOMHTMLFormElement.h"
#include "nsIDOMEventReceiver.h"
#include "nsIHTMLContent.h"
#include "nsGenericHTMLElement.h"
#include "nsHTMLAtoms.h"
#include "nsHTMLIIDs.h"
#include "nsIStyleContext.h"
#include "nsStyleConsts.h"
#include "nsIPresContext.h"
#include "nsIHTMLAttributes.h"
#include "nsIFormControl.h"
#include "nsIForm.h"
#include "nsIGfxTextControlFrame.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsIFormControlFrame.h"
#include "nsIFrame.h"
#include "nsIEventStateManager.h"
#include "nsISizeOfHandler.h"
#include "nsIServiceManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsDOMError.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIEditor.h"
#include "nsGUIEvent.h"
#include "nsIPresState.h"
#include "nsIDOMEvent.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMNodeList.h"
#include "nsIDOMHTMLCollection.h"
#include "nsICheckboxControlFrame.h"
#include "nsIRadioControlFrame.h"
#include "nsIFormManager.h"
#include "nsIImageControlFrame.h"
#include "nsLinebreakConverter.h" //to strip out carriage returns
#include "nsReadableUtils.h"
#include "nsIDOMMutationEvent.h"
#include "nsIDOMEventReceiver.h"
#include "nsMutationEvent.h"
#include "nsRuleNode.h"
// XXX align=left, hspace, vspace, border? other nav4 attrs
static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID);
typedef nsIGfxTextControlFrame2 textControlPlace;
//
// Accessors for mBitField
//
#define BF_SKIP_FOCUS_EVENT 0
#define BF_HANDLING_CLICK 1
#define BF_VALUE_CHANGED 2
#define BF_CHECKED_CHANGED 3
#define BF_CHECKED 4
#define GET_BOOLBIT(bitfield, field) (((bitfield) & (0x01 << (field))) \
? PR_TRUE : PR_FALSE)
#define SET_BOOLBIT(bitfield, field, b) ((b) \
? ((bitfield) |= (0x01 << (field))) \
: ((bitfield) &= ~(0x01 << (field))))
class nsHTMLInputElement : public nsGenericHTMLLeafFormElement,
public nsIDOMHTMLInputElement,
public nsIDOMNSHTMLInputElement,
public nsITextControlElement
{
public:
nsHTMLInputElement();
virtual ~nsHTMLInputElement();
// nsISupports
NS_DECL_ISUPPORTS_INHERITED
// nsIDOMNode
NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLLeafFormElement::)
// nsIDOMElement
NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLLeafFormElement::)
// nsIDOMHTMLElement
NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLLeafFormElement::)
// nsIDOMHTMLInputElement
NS_DECL_NSIDOMHTMLINPUTELEMENT
// nsIDOMNSHTMLInputElement
NS_DECL_NSIDOMNSHTMLINPUTELEMENT
// Overriden nsIFormControl methods
NS_IMETHOD GetType(PRInt32* aType);
NS_IMETHOD Reset();
NS_IMETHOD IsSuccessful(nsIContent* aSubmitElement, PRBool *_retval);
NS_IMETHOD GetMaxNumValues(PRInt32 *_retval);
NS_IMETHOD GetNamesValues(PRInt32 aMaxNumValues,
PRInt32& aNumValues,
nsString* aValues,
nsString* aNames);
NS_IMETHOD SaveState(nsIPresContext* aPresContext, nsIPresState** aState);
NS_IMETHOD RestoreState(nsIPresContext* aPresContext, nsIPresState* aState);
// nsIContent
NS_IMETHOD SetFocus(nsIPresContext* aPresContext);
NS_IMETHOD RemoveFocus(nsIPresContext* aPresContext);
NS_IMETHOD StringToAttribute(nsIAtom* aAttribute,
const nsAReadableString& aValue,
nsHTMLValue& aResult);
NS_IMETHOD AttributeToString(nsIAtom* aAttribute,
const nsHTMLValue& aValue,
nsAWritableString& aResult) const;
NS_IMETHOD GetMappedAttributeImpact(const nsIAtom* aAttribute, PRInt32 aModType,
PRInt32& aHint) const;
NS_IMETHOD GetAttributeMappingFunction(nsMapRuleToAttributesFunc& aMapRuleFunc) const;
NS_IMETHOD HandleDOMEvent(nsIPresContext* aPresContext, nsEvent* aEvent,
nsIDOMEvent** aDOMEvent, PRUint32 aFlags,
nsEventStatus* aEventStatus);
#ifdef DEBUG
NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const;
#endif
NS_IMETHOD SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
const nsAReadableString& aValue, PRBool aNotify) {
nsresult rv = nsGenericHTMLLeafFormElement::SetAttr(aNameSpaceID, aName,
aValue, aNotify);
if (aName == nsHTMLAtoms::value
&& !GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)
&& (mType == NS_FORM_INPUT_TEXT
|| mType == NS_FORM_INPUT_PASSWORD
|| mType == NS_FORM_INPUT_FILE
|| mType == NS_FORM_INPUT_HIDDEN)) {
Reset();
}
if (aName == nsHTMLAtoms::checked
&& !GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)
&& (mType == NS_FORM_INPUT_CHECKBOX || mType == NS_FORM_INPUT_RADIO)) {
Reset();
}
return rv;
}
NS_IMETHOD SetAttr(nsINodeInfo* aNodeInfo, const nsAReadableString& aValue,
PRBool aNotify) {
// This will end up calling the other SetAttr().
return nsGenericHTMLLeafFormElement::SetAttr(aNodeInfo, aValue, aNotify);
}
NS_IMETHOD UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
PRBool aNotify) {
nsresult rv = nsGenericHTMLLeafElement::UnsetAttr(aNameSpaceID,
aAttribute,
aNotify);
if (aAttribute == nsHTMLAtoms::value
&& !GET_BOOLBIT(mBitField, BF_VALUE_CHANGED)) {
Reset();
}
if (aAttribute == nsHTMLAtoms::checked
&& !GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) {
Reset();
}
return rv;
}
// nsITextControlElement
NS_IMETHOD SetValueGuaranteed(const nsAString& aValue, nsIGfxTextControlFrame2* aFrame);
NS_IMETHOD SetValueChanged(PRBool aValueChanged);
protected:
// Helper method
void SetPresStateChecked(nsIHTMLContent * aHTMLContent,
PRBool aValue);
NS_IMETHOD SetValueSecure(const nsAReadableString& aValue,
nsIGfxTextControlFrame2* aFrame,
PRBool aCheckSecurity);
nsresult GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd);
nsresult MouseClickForAltText(nsIPresContext* aPresContext);
//Helper method
#ifdef ACCESSIBILITY
nsresult FireEventForAccessibility(nsIPresContext* aPresContext,
const nsAReadableString& aEventType);
#endif
void SelectAll(nsIPresContext* aPresContext);
PRBool IsImage() const
{
nsAutoString tmp;
GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::type, tmp);
return tmp.EqualsIgnoreCase("image");
}
void FireOnChange();
nsCOMPtr<nsIControllers> mControllers;
PRInt8 mType;
// See GET_BOOLBIT / SET_BOOLBIT macros and BF_* field identifiers
PRInt8 mBitField;
char* mValue;
};
//
// construction, destruction
//
nsresult
NS_NewHTMLInputElement(nsIHTMLContent** aInstancePtrResult,
nsINodeInfo *aNodeInfo)
{
NS_ENSURE_ARG_POINTER(aInstancePtrResult);
nsHTMLInputElement* it = new nsHTMLInputElement();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsresult rv = NS_STATIC_CAST(nsGenericHTMLElement *, it)->Init(aNodeInfo);
if (NS_FAILED(rv)) {
delete it;
return rv;
}
*aInstancePtrResult = NS_STATIC_CAST(nsIHTMLContent *, it);
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
nsHTMLInputElement::nsHTMLInputElement()
{
mType = NS_FORM_INPUT_TEXT; // default value
mBitField = 0;
mValue = nsnull;
}
nsHTMLInputElement::~nsHTMLInputElement()
{
// Null out form's pointer to us - no ref counting here!
SetForm(nsnull);
if (mValue) {
nsMemory::Free(mValue);
}
}
// nsISupports
NS_IMPL_ADDREF_INHERITED(nsHTMLInputElement, nsGenericElement)
NS_IMPL_RELEASE_INHERITED(nsHTMLInputElement, nsGenericElement)
// QueryInterface implementation for nsHTMLInputElement
NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLInputElement,
nsGenericHTMLLeafFormElement)
NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLInputElement)
NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLInputElement)
NS_INTERFACE_MAP_ENTRY(nsITextControlElement)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLInputElement)
NS_HTML_CONTENT_INTERFACE_MAP_END
// nsIDOMNode
nsresult
nsHTMLInputElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn)
{
NS_ENSURE_ARG_POINTER(aReturn);
*aReturn = nsnull;
nsHTMLInputElement* it = new nsHTMLInputElement();
if (!it) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsIDOMNode> kungFuDeathGrip(it);
nsresult rv = NS_STATIC_CAST(nsGenericHTMLElement *, it)->Init(mNodeInfo);
if (NS_FAILED(rv))
return rv;
CopyInnerTo(this, it, aDeep);
*aReturn = NS_STATIC_CAST(nsIDOMNode *, it);
NS_ADDREF(*aReturn);
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetForm(nsIDOMHTMLFormElement** aForm)
{
return nsGenericHTMLLeafFormElement::GetForm(aForm);
}
NS_IMETHODIMP
nsHTMLInputElement::GetDefaultValue(nsAWritableString& aDefaultValue)
{
return GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::value, aDefaultValue);
}
NS_IMETHODIMP
nsHTMLInputElement::SetDefaultValue(const nsAReadableString& aDefaultValue)
{
return SetAttr(kNameSpaceID_HTML, nsHTMLAtoms::value, aDefaultValue,
PR_TRUE);
}
NS_IMETHODIMP
nsHTMLInputElement::GetDefaultChecked(PRBool* aDefaultChecked)
{
nsHTMLValue val;
nsresult rv;
rv = GetHTMLAttribute(nsHTMLAtoms::checked, val);
*aDefaultChecked = (NS_CONTENT_ATTR_NOT_THERE != rv);
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::SetDefaultChecked(PRBool aDefaultChecked)
{
nsresult rv;
if (aDefaultChecked) {
rv = SetAttr(kNameSpaceID_HTML, nsHTMLAtoms::checked,
NS_LITERAL_STRING(""), PR_TRUE);
} else {
rv = UnsetAttr(kNameSpaceID_HTML, nsHTMLAtoms::checked, PR_TRUE);
}
return rv;
}
//NS_IMPL_STRING_ATTR(nsHTMLInputElement, DefaultValue, defaultvalue)
//NS_IMPL_BOOL_ATTR(nsHTMLInputElement, DefaultChecked, defaultchecked)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Accept, accept)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, AccessKey, accesskey)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Align, align)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Alt, alt)
//NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Checked, checked)
NS_IMPL_BOOL_ATTR(nsHTMLInputElement, Disabled, disabled)
NS_IMPL_INT_ATTR(nsHTMLInputElement, MaxLength, maxlength)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Name, name)
NS_IMPL_BOOL_ATTR(nsHTMLInputElement, ReadOnly, readonly)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Size, size)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, Src, src)
NS_IMPL_INT_ATTR(nsHTMLInputElement, TabIndex, tabindex)
NS_IMPL_STRING_ATTR(nsHTMLInputElement, UseMap, usemap)
//NS_IMPL_STRING_ATTR(nsHTMLInputElement, Value, value)
NS_IMETHODIMP
nsHTMLInputElement::GetType(nsAWritableString& aValue)
{
nsresult rv = GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::type, aValue);
if (rv == NS_CONTENT_ATTR_NOT_THERE)
aValue.Assign(NS_LITERAL_STRING("text"));
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::SetType(const nsAReadableString& aValue)
{
return nsGenericHTMLLeafFormElement::SetAttr(kNameSpaceID_HTML,
nsHTMLAtoms::type, aValue,
PR_TRUE);
}
NS_IMETHODIMP
nsHTMLInputElement::GetValue(nsAWritableString& aValue)
{
PRInt32 type;
GetType(&type);
if (type == NS_FORM_INPUT_TEXT || type == NS_FORM_INPUT_PASSWORD ||
type == NS_FORM_INPUT_FILE) {
// No need to flush here, if there's no frame created for this
// input yet, there won't be a value in it (that we don't already
// have) even if we force it to be created
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
PRBool frameOwnsValue = PR_FALSE;
if (formControlFrame) {
nsIGfxTextControlFrame2* textControlFrame = nsnull;
CallQueryInterface(formControlFrame, &textControlFrame);
if (textControlFrame) {
textControlFrame->OwnsValue(&frameOwnsValue);
} else {
// We assume if it's not a text control frame that it owns the value
frameOwnsValue = PR_TRUE;
}
}
if (frameOwnsValue) {
formControlFrame->GetProperty(nsHTMLAtoms::value, aValue);
} else {
if (!GET_BOOLBIT(mBitField, BF_VALUE_CHANGED) || !mValue) {
GetDefaultValue(aValue);
} else {
aValue = NS_ConvertUTF8toUCS2(mValue);
}
}
return NS_OK;
}
// Treat value == defaultValue for other input elements
nsresult rv = GetAttr(kNameSpaceID_HTML, nsHTMLAtoms::value, aValue);
if (rv == NS_CONTENT_ATTR_NOT_THERE &&
(type == NS_FORM_INPUT_RADIO || type == NS_FORM_INPUT_CHECKBOX)) {
// The default value of a radio or checkbox input is "on".
aValue.Assign(NS_LITERAL_STRING("on"));
return NS_OK;
}
return rv;
}
NS_IMETHODIMP
nsHTMLInputElement::SetValue(const nsAReadableString& aValue)
{
return SetValueSecure(aValue, nsnull, PR_TRUE);
}
NS_IMETHODIMP
nsHTMLInputElement::SetValueGuaranteed(const nsAString& aValue,
nsIGfxTextControlFrame2* aFrame)
{
return SetValueSecure(aValue, aFrame, PR_FALSE);
}
NS_IMETHODIMP
nsHTMLInputElement::SetValueSecure(const nsAReadableString& aValue,
nsIGfxTextControlFrame2* aFrame,
PRBool aCheckSecurity)
{
PRInt32 type;
GetType(&type);
if (NS_FORM_INPUT_TEXT == type || NS_FORM_INPUT_PASSWORD == type ||
NS_FORM_INPUT_FILE == type) {
if (aCheckSecurity && NS_FORM_INPUT_FILE == type) {
nsresult rv;
nsCOMPtr<nsIScriptSecurityManager> securityManager =
do_GetService(NS_SCRIPTSECURITYMANAGER_CONTRACTID, &rv);
if (NS_FAILED(rv)) {
return rv;
}
PRBool enabled;
rv = securityManager->IsCapabilityEnabled("UniversalFileRead", &enabled);
if (NS_FAILED(rv)) {
return rv;
}
if (!enabled) {
// setting the value of a "FILE" input widget requires the
// UniversalFileRead privilege
return NS_ERROR_DOM_SECURITY_ERR;
}
}
nsIGfxTextControlFrame2* textControlFrame = aFrame;
nsIFormControlFrame* formControlFrame = textControlFrame;
if (!textControlFrame) {
// No need to flush here, if there's no frame at this point we
// don't need to force creation of one just to tell it about this
// new value.
formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
CallQueryInterface(formControlFrame, &textControlFrame);
}
}
PRBool frameOwnsValue = PR_FALSE;
if (textControlFrame) {
textControlFrame->OwnsValue(&frameOwnsValue);
}
if (frameOwnsValue) {
nsCOMPtr<nsIPresContext> presContext;
GetPresContext(this, getter_AddRefs(presContext));
formControlFrame->SetProperty(presContext, nsHTMLAtoms::value, aValue);
} else {
if (mValue) {
nsMemory::Free(mValue);
}
mValue = ToNewUTF8String(aValue);
SetValueChanged(PR_TRUE);
return mValue ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
return NS_OK;
}
// Treat value == defaultValue for other input elements.
return nsGenericHTMLLeafFormElement::SetAttr(kNameSpaceID_HTML,
nsHTMLAtoms::value,
aValue, PR_TRUE);
}
NS_IMETHODIMP
nsHTMLInputElement::SetValueChanged(PRBool aValueChanged)
{
SET_BOOLBIT(mBitField, BF_VALUE_CHANGED, aValueChanged);
if (!aValueChanged) {
if (mValue) {
nsMemory::Free(mValue);
mValue = nsnull;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetChecked(PRBool* aChecked)
{
PRInt32 type;
GetType(&type);
if (type == NS_FORM_INPUT_RADIO) {
nsAutoString value(NS_LITERAL_STRING("0"));
// No need to flush here, if there's no frame created for this input
// yet, we know our own checked state.
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
formControlFrame->GetProperty(nsHTMLAtoms::checked, value);
}
else {
// Retrieve the presentation state instead.
nsCOMPtr<nsIPresState> presState;
GetPrimaryPresState(this, getter_AddRefs(presState));
// Obtain the value property from the presentation state.
if (presState) {
presState->GetStateProperty(NS_LITERAL_STRING("checked"), value);
}
}
if (value.Equals(NS_LITERAL_STRING("1"))) {
*aChecked = PR_TRUE;
} else {
*aChecked = PR_FALSE;
}
} else {
if (!GET_BOOLBIT(mBitField, BF_CHECKED_CHANGED)) {
GetDefaultChecked(aChecked);
} else {
*aChecked = GET_BOOLBIT(mBitField, BF_CHECKED);
}
}
return NS_OK;
}
void
nsHTMLInputElement::SetPresStateChecked(nsIHTMLContent * aHTMLContent,
PRBool aValue)
{
nsCOMPtr<nsIPresState> presState;
GetPrimaryPresState(aHTMLContent, getter_AddRefs(presState));
// Obtain the value property from the presentation state.
if (presState) {
nsAutoString value; value.AssignWithConversion( aValue ? "1" : "0" );
presState->SetStateProperty(NS_LITERAL_STRING("checked"), value);
}
}
NS_IMETHODIMP
nsHTMLInputElement::SetChecked(PRBool aValue)
{
SET_BOOLBIT(mBitField, BF_CHECKED_CHANGED, PR_TRUE);
//
// Set the value
//
SET_BOOLBIT(mBitField, BF_CHECKED, aValue);
//
// Notify the frame
//
// No need to flush here since if there's no frame for this input at
// this point we don't care about creating one, once it's created
// the frame will do the right thing.
PRInt32 type;
GetType(&type);
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
nsCOMPtr<nsIPresContext> presContext;
GetPresContext(this, getter_AddRefs(presContext));
if (type == NS_FORM_INPUT_CHECKBOX) {
nsICheckboxControlFrame* checkboxFrame = nsnull;
CallQueryInterface(formControlFrame, &checkboxFrame);
if (checkboxFrame) {
checkboxFrame->OnChecked(presContext, aValue);
}
} else if (type == NS_FORM_INPUT_RADIO) {
// the value is being toggled
nsAutoString val; val.AssignWithConversion(aValue ? "1" : "0");
formControlFrame->SetProperty(presContext, nsHTMLAtoms::checked, val);
}
}
else if (type == NS_FORM_INPUT_RADIO) {
SetPresStateChecked(this, aValue);
nsAutoString name;
GetName(name);
nsCOMPtr<nsIDOMHTMLFormElement> formElement;
if (NS_SUCCEEDED(GetForm(getter_AddRefs(formElement))) && formElement) {
nsCOMPtr<nsIDOMHTMLCollection> controls;
nsresult rv = formElement->GetElements(getter_AddRefs(controls));
if (controls) {
PRUint32 numControls;
controls->GetLength(&numControls);
for (PRUint32 i = 0; i < numControls; i++) {
nsCOMPtr<nsIDOMNode> elementNode;
controls->Item(i, getter_AddRefs(elementNode));
if (elementNode) {
nsCOMPtr<nsIDOMHTMLInputElement>
inputElement(do_QueryInterface(elementNode));
if (inputElement && inputElement.get() !=
NS_STATIC_CAST(nsIDOMHTMLInputElement *, this)) {
nsAutoString childName;
rv = inputElement->GetName(childName);
if (NS_SUCCEEDED(rv)) {
if (name == childName) {
nsCOMPtr<nsIHTMLContent>
htmlContent(do_QueryInterface(inputElement));
SetPresStateChecked(htmlContent, PR_FALSE);
}
}
}
}
}
}
}
}
else if (type == NS_FORM_INPUT_CHECKBOX) {
if (GET_BOOLBIT(mBitField, BF_CHECKED))
SetAttr(kNameSpaceID_None, nsHTMLAtoms::inputCheckedPseudo,
NS_LITERAL_STRING(""), PR_TRUE);
else
UnsetAttr(kNameSpaceID_None, nsHTMLAtoms::inputCheckedPseudo, PR_TRUE);
}
return NS_OK;
}
void
nsHTMLInputElement::FireOnChange()
{
//
// Since the value is changing, send out an onchange event (bug 23571)
//
nsCOMPtr<nsIPresContext> presContext;
GetPresContext(this, getter_AddRefs(presContext));
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event;
event.eventStructType = NS_EVENT;
event.message = NS_FORM_CHANGE;
HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT, &status);
}
NS_IMETHODIMP
nsHTMLInputElement::Blur()
{
return SetElementFocus(PR_FALSE);
}
NS_IMETHODIMP
nsHTMLInputElement::Focus()
{
return SetElementFocus(PR_TRUE);
}
NS_IMETHODIMP
nsHTMLInputElement::SetFocus(nsIPresContext* aPresContext)
{
NS_ENSURE_ARG_POINTER(aPresContext);
// We can't be focus'd if we don't have a document
if (! mDocument)
return NS_OK;
// first see if we are disabled or not. If disabled then do nothing.
nsAutoString disabled;
if (NS_CONTENT_ATTR_HAS_VALUE == GetAttr(kNameSpaceID_HTML,
nsHTMLAtoms::disabled, disabled)) {
return NS_OK;
}
// If the window is not active, do not allow the focus to bring the
// window to the front. We update the focus controller, but do
// nothing else.
nsCOMPtr<nsIFocusController> focusController;
nsCOMPtr<nsIScriptGlobalObject> globalObj;
mDocument->GetScriptGlobalObject(getter_AddRefs(globalObj));
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(globalObj));
win->GetRootFocusController(getter_AddRefs(focusController));
PRBool isActive = PR_FALSE;
focusController->GetActive(&isActive);
if (!isActive) {
focusController->SetFocusedElement(this);
return NS_OK;
}
nsCOMPtr<nsIEventStateManager> esm;
aPresContext->GetEventStateManager(getter_AddRefs(esm));
if (esm) {
esm->SetContentState(this, NS_EVENT_STATE_FOCUS);
}
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
formControlFrame->ScrollIntoView(aPresContext);
// Could call SelectAll(aPresContext) here to automatically
// select text when we receive focus - only for text and password!
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::RemoveFocus(nsIPresContext* aPresContext)
{
NS_ENSURE_ARG_POINTER(aPresContext);
// If we are disabled, we probably shouldn't have focus in the
// first place, so allow it to be removed.
nsresult rv = NS_OK;
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_FALSE, PR_FALSE);
}
nsCOMPtr<nsIEventStateManager> esm;
aPresContext->GetEventStateManager(getter_AddRefs(esm));
if (esm) {
nsCOMPtr<nsIDocument> doc;
GetDocument(*getter_AddRefs(doc));
if (!doc)
return NS_ERROR_NULL_POINTER;
nsCOMPtr<nsIContent> rootContent;
doc->GetRootContent(getter_AddRefs(rootContent));
rv = esm->SetContentState(rootContent, NS_EVENT_STATE_FOCUS);
}
return rv;
}
NS_IMETHODIMP
nsHTMLInputElement::Select()
{
nsresult rv = NS_OK;
// first see if we are disabled or not. If disabled then do nothing.
nsAutoString disabled;
if (NS_CONTENT_ATTR_HAS_VALUE == GetAttr(kNameSpaceID_HTML,
nsHTMLAtoms::disabled, disabled)) {
return rv;
}
// see what type of input we are. Only select for texts and passwords
PRInt32 type;
GetType(&type);
if (NS_FORM_INPUT_PASSWORD == type ||
NS_FORM_INPUT_TEXT == type) {
// XXX Bug? We have to give the input focus before contents can be
// selected
nsCOMPtr<nsIPresContext> presContext;
GetPresContext(this, getter_AddRefs(presContext));
// If the window is not active, do not allow the select to bring the
// window to the front. We update the focus controller, but do
// nothing else.
nsCOMPtr<nsIScriptGlobalObject> globalObj;
mDocument->GetScriptGlobalObject(getter_AddRefs(globalObj));
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(globalObj));
nsCOMPtr<nsIFocusController> focusController;
win->GetRootFocusController(getter_AddRefs(focusController));
PRBool isActive = PR_FALSE;
focusController->GetActive(&isActive);
if (!isActive) {
focusController->SetFocusedElement(this);
SelectAll(presContext);
return NS_OK;
}
// Just like SetFocus() but without the ScrollIntoView()!
nsEventStatus status = nsEventStatus_eIgnore;
nsEvent event;
event.eventStructType = NS_EVENT;
event.message = NS_FORM_SELECTED;
rv = HandleDOMEvent(presContext, &event, nsnull, NS_EVENT_FLAG_INIT,
&status);
// If the DOM event was not canceled (e.g. by a JS event handler
// returning false)
if (status == nsEventStatus_eIgnore) {
nsCOMPtr<nsIEventStateManager> esm;
if (NS_OK == presContext->GetEventStateManager(getter_AddRefs(esm))) {
esm->SetContentState(this, NS_EVENT_STATE_FOCUS);
}
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
// Now Select all the text!
SelectAll(presContext);
}
}
}
return rv;
}
void
nsHTMLInputElement::SelectAll(nsIPresContext* aPresContext)
{
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
formControlFrame->SetProperty(aPresContext, nsHTMLAtoms::select,
nsAutoString());
}
}
NS_IMETHODIMP
nsHTMLInputElement::Click()
{
nsresult rv = NS_OK;
if (GET_BOOLBIT(mBitField, BF_HANDLING_CLICK)) // Fixes crash as in bug 41599
return rv; // --heikki@netscape.com
// first see if we are disabled or not. If disabled then do nothing.
nsAutoString disabled;
if (NS_CONTENT_ATTR_HAS_VALUE == GetAttr(kNameSpaceID_HTML,
nsHTMLAtoms::disabled, disabled)) {
return rv;
}
// see what type of input we are. Only click button, checkbox, radio,
// reset, submit, & image
PRInt32 type;
GetType(&type);
if (NS_FORM_INPUT_BUTTON == type ||
NS_FORM_INPUT_CHECKBOX == type ||
NS_FORM_INPUT_RADIO == type ||
NS_FORM_INPUT_RESET == type ||
NS_FORM_INPUT_SUBMIT == type) {
nsCOMPtr<nsIDocument> doc; // Strong
rv = GetDocument(*getter_AddRefs(doc));
if (doc) {
PRInt32 numShells = doc->GetNumberOfShells();
nsCOMPtr<nsIPresContext> context;
for (PRInt32 i=0; i<numShells; i++) {
nsCOMPtr<nsIPresShell> shell;
doc->GetShellAt(i, getter_AddRefs(shell));
if (shell) {
rv = shell->GetPresContext(getter_AddRefs(context));
if (context) {
nsEventStatus status = nsEventStatus_eIgnore;
nsMouseEvent event;
event.eventStructType = NS_MOUSE_EVENT;
event.message = NS_MOUSE_LEFT_CLICK;
event.isShift = PR_FALSE;
event.isControl = PR_FALSE;
event.isAlt = PR_FALSE;
event.isMeta = PR_FALSE;
event.clickCount = 0;
event.widget = nsnull;
SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_TRUE);
rv = HandleDOMEvent(context, &event, nsnull, NS_EVENT_FLAG_INIT,
&status);
SET_BOOLBIT(mBitField, BF_HANDLING_CLICK, PR_FALSE);
}
}
}
}
}
return NS_OK;
}
nsresult
nsHTMLInputElement::MouseClickForAltText(nsIPresContext* aPresContext)
{
NS_ENSURE_ARG_POINTER(aPresContext);
PRBool disabled;
nsresult rv = GetDisabled(&disabled);
if (NS_FAILED(rv) || disabled) {
return rv;
}
// Generate a submit event targeted at the form content
nsCOMPtr<nsIContent> form(do_QueryInterface(mForm));
if (form) {
nsCOMPtr<nsIPresShell> shell;
aPresContext->GetShell(getter_AddRefs(shell));
if (shell) {
nsCOMPtr<nsIContent> formControl = this; // kungFuDeathGrip
nsFormEvent event;
event.eventStructType = NS_FORM_EVENT;
event.message = NS_FORM_SUBMIT;
event.originator = formControl;
nsEventStatus status = nsEventStatus_eIgnore;
shell->HandleDOMEventWithTarget(form, &event, &status);
}
}
return rv;
}
NS_IMETHODIMP
nsHTMLInputElement::HandleDOMEvent(nsIPresContext* aPresContext,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent,
PRUint32 aFlags,
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
if ((aEvent->message == NS_FOCUS_CONTENT
&& GET_BOOLBIT(mBitField, BF_SKIP_FOCUS_EVENT))
|| (aEvent->message == NS_BLUR_CONTENT
&& GET_BOOLBIT(mBitField, BF_SKIP_FOCUS_EVENT))) {
return NS_OK;
}
nsCOMPtr<nsIDOMEventTarget> oldTarget;
// Do not process any DOM events if the element is disabled
PRBool disabled;
nsresult rv = GetDisabled(&disabled);
if (NS_FAILED(rv) || disabled) {
return rv;
}
// Bugscape 2369: Type might change during JS event handler
// This pointer is only valid until
// nsGenericHTMLLeafFormElement::HandleDOMEvent
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
if (formControlFrame) {
nsIFrame* formFrame = nsnull;
CallQueryInterface(formControlFrame, &formFrame);
if (formFrame) {
const nsStyleUserInterface* uiStyle;
formFrame->GetStyleData(eStyleStruct_UserInterface,
(const nsStyleStruct *&)uiStyle);
if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE ||
uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED) {
return NS_OK;
}
}
}
// If we're a file input we have anonymous content underneath
// that we need to hide. We need to set the event target now
// to ourselves
//
// Bugscape 2369: Type might change during JS event handler
// This is only valid until
// nsGenericHTMLLeafFormElement::HandleDOMEvent
//
PRInt32 type;
GetType(&type);
if (type == NS_FORM_INPUT_FILE ||
type == NS_FORM_INPUT_TEXT ) {
// If the event is starting here that's fine. If it's not
// init'ing here it started beneath us and needs modification.
if (!(NS_EVENT_FLAG_INIT & aFlags)) {
if (!*aDOMEvent) {
// We haven't made a DOMEvent yet. Force making one now.
nsCOMPtr<nsIEventListenerManager> listenerManager;
rv = GetListenerManager(getter_AddRefs(listenerManager));
if (NS_FAILED(rv)) {
return rv;
}
nsAutoString empty;
rv = listenerManager->CreateEvent(aPresContext, aEvent, empty,
aDOMEvent);
if (NS_FAILED(rv)) {
return rv;
}
}
if (!*aDOMEvent) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPrivateDOMEvent> privateEvent(do_QueryInterface(*aDOMEvent));
if (!privateEvent) {
return NS_ERROR_FAILURE;
}
(*aDOMEvent)->GetTarget(getter_AddRefs(oldTarget));
nsCOMPtr<nsIDOMEventTarget> originalTarget;
nsCOMPtr<nsIDOMNSEvent> nsevent(do_QueryInterface(*aDOMEvent));
if (nsevent) {
nsevent->GetOriginalTarget(getter_AddRefs(originalTarget));
}
if (!originalTarget) {
privateEvent->SetOriginalTarget(oldTarget);
}
nsCOMPtr<nsIDOMEventTarget>
target(do_QueryInterface((nsIDOMHTMLInputElement*)this));
privateEvent->SetTarget(target);
}
}
// Preset the the value of the checkbox or the radiobutton before
// calling into script If the event gets "cancelled" then we have to
// "back out" this change, but the odds of that are slimmer than it
// being set each time
//
// Start by remember the original value and for radio buttons we
// must get the currently selected radiobtn. So we go get the
// content instead of the frame
PRBool orginalCheckedValue = PR_FALSE;
PRBool checkWasSet = PR_FALSE;
nsCOMPtr<nsIContent> selectedRadiobtn;
if (!(aFlags & NS_EVENT_FLAG_CAPTURE) &&
aEvent->message == NS_MOUSE_LEFT_CLICK) {
GetChecked(&orginalCheckedValue);
checkWasSet = PR_TRUE;
switch(type) {
case NS_FORM_INPUT_CHECKBOX:
{
PRBool checked;
GetChecked(&checked);
SetChecked(!checked);
FireOnChange();
// Fire an event to notify accessibility
#ifdef ACCESSIBILITY
FireEventForAccessibility( aPresContext, NS_LITERAL_STRING("CheckboxStateChange"));
#endif
}
break;
case NS_FORM_INPUT_RADIO:
{
// Get the currently selected button from the radio group
// we get access to that via the nsIRadioControlFrame interface
// because the current grouping is kept in the frame.
if (formControlFrame) {
nsIRadioControlFrame * rb = nsnull;
CallQueryInterface(formControlFrame, &rb);
if (rb) {
rb->GetRadioGroupSelectedContent(getter_AddRefs(selectedRadiobtn));
}
}
SetChecked(PR_TRUE);
FireOnChange();
// Fire an event to notify accessibility
#ifdef ACCESSIBILITY
if ( selectedRadiobtn != this ) {
FireEventForAccessibility( aPresContext, NS_LITERAL_STRING("RadiobuttonStateChange"));;
}
#endif
}
break;
default:
break;
} //switch
}
// If NS_EVENT_FLAG_NO_CONTENT_DISPATCH is set we will not allow content to handle
// this event. But to allow middle mouse button paste to work we must allow
// middle clicks to go to text fields anyway.
PRBool noContentDispatch = aEvent->flags & NS_EVENT_FLAG_NO_CONTENT_DISPATCH;
if ((type == NS_FORM_INPUT_TEXT || type == NS_FORM_INPUT_PASSWORD) &&
aEvent->message == NS_MOUSE_MIDDLE_CLICK) {
aEvent->flags &= ~NS_EVENT_FLAG_NO_CONTENT_DISPATCH;
}
// Try script event handlers first if its not a focus/blur event
//we dont want the doc to get these
rv = nsGenericHTMLLeafFormElement::HandleDOMEvent(aPresContext,
aEvent,
aDOMEvent,
aFlags,
aEventStatus);
// Reset the flag for other content besides this text field
aEvent->flags |= noContentDispatch ? NS_EVENT_FLAG_NO_CONTENT_DISPATCH : NS_EVENT_FLAG_NONE;
// now check to see if the event was "cancelled"
if (nsEventStatus_eConsumeNoDefault == *aEventStatus && checkWasSet
&& (type == NS_FORM_INPUT_CHECKBOX || type == NS_FORM_INPUT_RADIO)) {
// if it was cancelled and a radio button, then set the old
// selected btn to TRUE. if it is a checkbox then set it to its
// original value
if (selectedRadiobtn) {
nsCOMPtr<nsIDOMHTMLInputElement> inputElement(do_QueryInterface(selectedRadiobtn));
if (inputElement) {
inputElement->SetChecked(PR_TRUE);
}
} else {
SetChecked(orginalCheckedValue);
}
}
// Bugscape 2369: Frame might have changed during event handler
formControlFrame = GetFormControlFrame(PR_FALSE);
// Finish the special file control processing...
if (oldTarget) {
// If the event is starting here that's fine. If it's not
// init'ing here it started beneath us and needs modification.
if (!(NS_EVENT_FLAG_INIT & aFlags)) {
if (!*aDOMEvent) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPrivateDOMEvent> privateEvent =
do_QueryInterface(*aDOMEvent);
if (!privateEvent) {
return NS_ERROR_FAILURE;
}
// This will reset the target to its original value
privateEvent->SetTarget(oldTarget);
}
}
// Bugscape 2369: type might have changed during event handler
GetType(&type);
if (type == NS_FORM_INPUT_IMAGE &&
aEvent->message == NS_MOUSE_LEFT_BUTTON_UP &&
nsEventStatus_eIgnore == *aEventStatus &&
aFlags & NS_EVENT_FLAG_BUBBLE) {
// Tell the frame about the click
return MouseClickForAltText(aPresContext);
}
if ((NS_OK == rv) && (nsEventStatus_eIgnore == *aEventStatus) &&
!(aFlags & NS_EVENT_FLAG_CAPTURE)) {
switch (aEvent->message) {
case NS_FOCUS_CONTENT:
{
if (formControlFrame) {
SET_BOOLBIT(mBitField, BF_SKIP_FOCUS_EVENT, PR_TRUE);
formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
SET_BOOLBIT(mBitField, BF_SKIP_FOCUS_EVENT, PR_FALSE);
return NS_OK;
}
}
break; // NS_FOCUS_CONTENT
case NS_KEY_PRESS:
case NS_KEY_UP:
{
// For backwards compat, trigger checks/radios/buttons with
// space or enter (bug 25300)
nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
if ((aEvent->message == NS_KEY_PRESS &&
keyEvent->keyCode == NS_VK_RETURN) ||
(aEvent->message == NS_KEY_UP &&
keyEvent->keyCode == NS_VK_SPACE)) {
switch(type) {
case NS_FORM_INPUT_CHECKBOX:
case NS_FORM_INPUT_RADIO:
{
// Checkbox and Radio try to submit on Enter press
if (keyEvent->keyCode != NS_VK_SPACE) {
// Generate a submit event targeted at the form content
nsCOMPtr<nsIContent> form(do_QueryInterface(mForm));
if (form) {
nsCOMPtr<nsIPresShell> shell;
aPresContext->GetShell(getter_AddRefs(shell));
if (shell) {
nsCOMPtr<nsIContent> formControl = this; // kungFuDeathGrip
nsFormEvent event;
event.eventStructType = NS_FORM_EVENT;
event.message = NS_FORM_SUBMIT;
event.originator = formControl;
nsEventStatus status = nsEventStatus_eIgnore;
shell->HandleDOMEventWithTarget(form, &event, &status);
}
}
break; // If we are submitting, do not send click event
}
// else fall through and treat Space like click...
}
case NS_FORM_INPUT_BUTTON:
case NS_FORM_INPUT_RESET:
case NS_FORM_INPUT_SUBMIT:
case NS_FORM_INPUT_IMAGE: // Bug 34418
{
nsEventStatus status = nsEventStatus_eIgnore;
nsMouseEvent event;
event.eventStructType = NS_MOUSE_EVENT;
event.message = NS_MOUSE_LEFT_CLICK;
event.isShift = PR_FALSE;
event.isControl = PR_FALSE;
event.isAlt = PR_FALSE;
event.isMeta = PR_FALSE;
event.clickCount = 0;
event.widget = nsnull;
rv = HandleDOMEvent(aPresContext, &event, nsnull,
NS_EVENT_FLAG_INIT, &status);
} // case
} // switch
}
} break;// NS_KEY_PRESS || NS_KEY_UP
// cancel all of these events for buttons
case NS_MOUSE_MIDDLE_BUTTON_DOWN:
case NS_MOUSE_MIDDLE_BUTTON_UP:
case NS_MOUSE_MIDDLE_DOUBLECLICK:
case NS_MOUSE_RIGHT_DOUBLECLICK:
case NS_MOUSE_RIGHT_BUTTON_DOWN:
case NS_MOUSE_RIGHT_BUTTON_UP:
{
if (type == NS_FORM_INPUT_BUTTON ||
type == NS_FORM_INPUT_RESET ||
type == NS_FORM_INPUT_SUBMIT ) {
nsCOMPtr<nsIDOMNSEvent> nsevent;
if (aDOMEvent) {
nsevent = do_QueryInterface(*aDOMEvent);
}
if (nsevent) {
nsevent->PreventBubble();
} else {
rv = NS_ERROR_FAILURE;
}
}
break;
}
case NS_MOUSE_LEFT_CLICK:
{
switch(type) {
case NS_FORM_INPUT_BUTTON:
case NS_FORM_INPUT_RESET:
case NS_FORM_INPUT_SUBMIT:
case NS_FORM_INPUT_IMAGE:
{
nsFormEvent event;
event.eventStructType = NS_FORM_EVENT;
event.message = (type == NS_FORM_INPUT_RESET) ? NS_FORM_RESET : NS_FORM_SUBMIT;
event.originator = this;
nsEventStatus status = nsEventStatus_eIgnore;
nsCOMPtr<nsIPresShell> presShell;
aPresContext->GetShell(getter_AddRefs(presShell));
nsCOMPtr<nsIContent> form(do_QueryInterface(mForm));
presShell->HandleEventWithTarget(&event, nsnull, form, NS_EVENT_FLAG_INIT, &status);
}
break;
default:
break;
} //switch
} break;// NS_MOUSE_LEFT_BUTTON_DOWN
} //switch
} // if
return rv;
}
// nsIHTMLContent
static nsGenericHTMLElement::EnumTable kInputTypeTable[] = {
{ "browse", NS_FORM_BROWSE }, // XXX not valid html, but it is convenient
{ "button", NS_FORM_INPUT_BUTTON },
{ "checkbox", NS_FORM_INPUT_CHECKBOX },
{ "file", NS_FORM_INPUT_FILE },
{ "hidden", NS_FORM_INPUT_HIDDEN },
{ "reset", NS_FORM_INPUT_RESET },
{ "image", NS_FORM_INPUT_IMAGE },
{ "password", NS_FORM_INPUT_PASSWORD },
{ "radio", NS_FORM_INPUT_RADIO },
{ "submit", NS_FORM_INPUT_SUBMIT },
{ "text", NS_FORM_INPUT_TEXT },
{ 0 }
};
NS_IMETHODIMP
nsHTMLInputElement::StringToAttribute(nsIAtom* aAttribute,
const nsAReadableString& aValue,
nsHTMLValue& aResult)
{
if (aAttribute == nsHTMLAtoms::type) {
nsGenericHTMLElement::EnumTable *table = kInputTypeTable;
nsAutoString valueStr(aValue);
while (nsnull != table->tag) {
if (valueStr.EqualsIgnoreCase(table->tag)) {
// If the type is being changed to file, set the element value
// to the empty string. This is for security.
if (table->value == NS_FORM_INPUT_FILE)
SetValue(NS_LITERAL_STRING(""));
aResult.SetIntValue(table->value, eHTMLUnit_Enumerated);
mType = table->value; // set the type of this input
return NS_CONTENT_ATTR_HAS_VALUE;
}
table++;
}
}
else if (aAttribute == nsHTMLAtoms::checked) {
aResult.SetEmptyValue();
return NS_CONTENT_ATTR_HAS_VALUE;
}
else if (aAttribute == nsHTMLAtoms::disabled) {
aResult.SetEmptyValue();
return NS_CONTENT_ATTR_HAS_VALUE;
}
else if (aAttribute == nsHTMLAtoms::readonly) {
aResult.SetEmptyValue();
return NS_CONTENT_ATTR_HAS_VALUE;
}
else if (aAttribute == nsHTMLAtoms::width) {
if (ParseValueOrPercent(aValue, aResult, eHTMLUnit_Pixel)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::height) {
if (ParseValueOrPercent(aValue, aResult, eHTMLUnit_Pixel)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::maxlength) {
if (ParseValue(aValue, 0, aResult, eHTMLUnit_Integer)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::size) {
if (ParseValue(aValue, 0, aResult, eHTMLUnit_Integer)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::tabindex) {
if (ParseValue(aValue, 0, aResult, eHTMLUnit_Integer)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::border) {
if (ParseValue(aValue, 0, aResult, eHTMLUnit_Pixel)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::align) {
if (ParseAlignValue(aValue, aResult)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (IsImage()) {
if (ParseImageAttribute(aAttribute, aValue, aResult)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
return NS_CONTENT_ATTR_NOT_THERE;
}
NS_IMETHODIMP
nsHTMLInputElement::AttributeToString(nsIAtom* aAttribute,
const nsHTMLValue& aValue,
nsAWritableString& aResult) const
{
if (aAttribute == nsHTMLAtoms::type) {
if (eHTMLUnit_Enumerated == aValue.GetUnit()) {
// The DOM spec says that input types should be capitalized but
// AFAIK all existing browsers return the type in lower case,
// http://bugzilla.mozilla.org/show_bug.cgi?id=32369 is the bug
// about this problem, we pass PR_FALSE as the last argument
// here to avoid capitalizing the input type (this is required for
// backwards compatibility). -- jst@netscape.com
// Update. The DOM spec will be changed to have input types be
// all-lowercase. See
// http://bugzilla.mozilla.org/show_bug.cgi?id=113174#c12
// -- bzbarsky@mit.edu
EnumValueToString(aValue, kInputTypeTable, aResult);
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::align) {
if (eHTMLUnit_Enumerated == aValue.GetUnit()) {
VAlignValueToString(aValue, aResult);
return NS_CONTENT_ATTR_HAS_VALUE;
}
}
else if (aAttribute == nsHTMLAtoms::checked) {
aResult.Assign(NS_LITERAL_STRING("checked"));
return NS_CONTENT_ATTR_HAS_VALUE;
}
else if (IsImage() && ImageAttributeToString(aAttribute, aValue,
aResult)) {
return NS_CONTENT_ATTR_HAS_VALUE;
}
return nsGenericHTMLLeafFormElement::AttributeToString(aAttribute, aValue,
aResult);
}
static void
MapAttributesIntoRule(const nsIHTMLMappedAttributes* aAttributes,
nsRuleData* aData)
{
if (!aData)
return;
nsHTMLValue value;
aAttributes->GetAttribute(nsHTMLAtoms::type, value);
if (eHTMLUnit_Enumerated == value.GetUnit()) {
switch (value.GetIntValue()) {
case NS_FORM_INPUT_IMAGE: {
nsGenericHTMLElement::MapImageBorderAttributeInto(aAttributes, aData);
nsGenericHTMLElement::MapImageMarginAttributeInto(aAttributes, aData);
nsGenericHTMLElement::MapImagePositionAttributeInto(aAttributes, aData);
break;
}
}
}
nsGenericHTMLElement::MapAlignAttributeInto(aAttributes, aData);
nsGenericHTMLElement::MapCommonAttributesInto(aAttributes, aData);
}
NS_IMETHODIMP
nsHTMLInputElement::GetMappedAttributeImpact(const nsIAtom* aAttribute, PRInt32 aModType,
PRInt32& aHint) const
{
if (aAttribute == nsHTMLAtoms::value) {
aHint = NS_STYLE_HINT_REFLOW;
}
else if ((aAttribute == nsHTMLAtoms::align) ||
(aAttribute == nsHTMLAtoms::type)) {
aHint = NS_STYLE_HINT_FRAMECHANGE;
}
else if (!GetCommonMappedAttributesImpact(aAttribute, aHint)) {
if (!GetImageMappedAttributesImpact(aAttribute, aHint)) {
if (!GetImageBorderAttributeImpact(aAttribute, aHint)) {
aHint = NS_STYLE_HINT_CONTENT;
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetAttributeMappingFunction(nsMapRuleToAttributesFunc& aMapRuleFunc) const
{
aMapRuleFunc = &MapAttributesIntoRule;
return NS_OK;
}
// nsIFormControl
NS_IMETHODIMP
nsHTMLInputElement::GetType(PRInt32* aType)
{
if (aType) {
*aType = mType;
return NS_OK;
} else {
return NS_FORM_NOTOK;
}
}
#ifdef DEBUG
NS_IMETHODIMP
nsHTMLInputElement::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const
{
*aResult = sizeof(*this) + BaseSizeOf(aSizer);
return NS_OK;
}
#endif
// Controllers Methods
NS_IMETHODIMP
nsHTMLInputElement::GetControllers(nsIControllers** aResult)
{
NS_ENSURE_ARG_POINTER(aResult);
PRInt32 type;
GetType(&type);
//XXX: what about type "file"?
if (NS_FORM_INPUT_TEXT == type || NS_FORM_INPUT_PASSWORD == type)
{
if (!mControllers)
{
NS_ENSURE_SUCCESS (
nsComponentManager::CreateInstance(kXULControllersCID,
nsnull,
NS_GET_IID(nsIControllers),
getter_AddRefs(mControllers)),
NS_ERROR_FAILURE);
if (!mControllers) { return NS_ERROR_NULL_POINTER; }
nsresult rv;
nsCOMPtr<nsIController>
controller(do_CreateInstance("@mozilla.org/editor/editorcontroller;1",
&rv));
if (NS_FAILED(rv)) return rv;
nsCOMPtr<nsIEditorController>
editorController = do_QueryInterface(controller, &rv);
if (NS_FAILED(rv))
return rv;
rv = editorController->Init(nsnull);
if (NS_FAILED(rv))
return rv;
mControllers->AppendController(controller);
}
}
*aResult = mControllers;
NS_IF_ADDREF(*aResult);
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetTextLength(PRInt32* aTextLength)
{
nsAutoString val;
nsresult rv = GetValue(val);
*aTextLength = val.Length();
return rv;
}
NS_IMETHODIMP
nsHTMLInputElement::SetSelectionRange(PRInt32 aSelectionStart,
PRInt32 aSelectionEnd)
{
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
nsCOMPtr<textControlPlace>
textControlFrame(do_QueryInterface(formControlFrame));
if (textControlFrame)
textControlFrame->SetSelectionRange(aSelectionStart, aSelectionEnd);
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetSelectionStart(PRInt32* aSelectionStart)
{
NS_ENSURE_ARG_POINTER(aSelectionStart);
PRInt32 selEnd;
return GetSelectionRange(aSelectionStart, &selEnd);
}
NS_IMETHODIMP
nsHTMLInputElement::SetSelectionStart(PRInt32 aSelectionStart)
{
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
nsCOMPtr<textControlPlace>
textControlFrame(do_QueryInterface(formControlFrame));
if (textControlFrame)
textControlFrame->SetSelectionStart(aSelectionStart);
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::GetSelectionEnd(PRInt32* aSelectionEnd)
{
NS_ENSURE_ARG_POINTER(aSelectionEnd);
PRInt32 selStart;
return GetSelectionRange(&selStart, aSelectionEnd);
}
NS_IMETHODIMP
nsHTMLInputElement::SetSelectionEnd(PRInt32 aSelectionEnd)
{
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
nsCOMPtr<textControlPlace>
textControlFrame(do_QueryInterface(formControlFrame));
if (textControlFrame)
textControlFrame->SetSelectionEnd(aSelectionEnd);
}
return NS_OK;
}
nsresult
nsHTMLInputElement::GetSelectionRange(PRInt32* aSelectionStart,
PRInt32* aSelectionEnd)
{
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
if (formControlFrame) {
nsCOMPtr<textControlPlace>
textControlFrame(do_QueryInterface(formControlFrame));
if (textControlFrame)
textControlFrame->GetSelectionRange(aSelectionStart, aSelectionEnd);
}
return NS_OK;
}
#ifdef ACCESSIBILITY
nsresult
nsHTMLInputElement::FireEventForAccessibility(nsIPresContext* aPresContext,
const nsAReadableString& aEventType)
{
nsCOMPtr<nsIEventListenerManager> listenerManager;
nsresult rv = GetListenerManager(getter_AddRefs(listenerManager));
if ( !listenerManager )
return rv;
// Create the DOM event
nsCOMPtr<nsIDOMEvent> domEvent;
rv = listenerManager->CreateEvent(aPresContext,
nsnull,
NS_LITERAL_STRING("MutationEvents"),
getter_AddRefs(domEvent) );
if ( !domEvent )
return NS_ERROR_FAILURE;
// Initialize the mutation event
nsCOMPtr<nsIDOMMutationEvent> mutEvent(do_QueryInterface(domEvent));
if ( !mutEvent )
return NS_ERROR_FAILURE;
nsAutoString empty;
mutEvent->InitMutationEvent( aEventType, PR_TRUE, PR_TRUE, nsnull, empty, empty, empty, nsIDOMMutationEvent::MODIFICATION);
// Set the target of the event to this nsHTMLInputElement, which should be checkbox content??
nsCOMPtr<nsIPrivateDOMEvent> privEvent(do_QueryInterface(domEvent));
if ( ! privEvent )
return NS_ERROR_FAILURE;
nsCOMPtr<nsIDOMEventTarget> targ(do_QueryInterface(NS_STATIC_CAST(nsIDOMHTMLInputElement *, this)));
if ( ! targ )
return NS_ERROR_FAILURE;
privEvent->SetTarget(targ);
// Dispatch the event
nsCOMPtr<nsIDOMEventReceiver> eventReceiver(do_QueryInterface(listenerManager));
if ( ! eventReceiver )
return NS_ERROR_FAILURE;
PRBool noDefault;
eventReceiver->DispatchEvent(domEvent, &noDefault);
return NS_OK;
}
#endif
nsresult
nsHTMLInputElement::Reset()
{
nsresult rv = NS_OK;
PRInt32 type;
GetType(&type);
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
switch (type) {
case NS_FORM_INPUT_CHECKBOX:
case NS_FORM_INPUT_RADIO:
{
PRBool resetVal;
GetDefaultChecked(&resetVal);
rv = SetChecked(resetVal);
SET_BOOLBIT(mBitField, BF_CHECKED_CHANGED, PR_FALSE);
break;
}
case NS_FORM_INPUT_HIDDEN:
case NS_FORM_INPUT_PASSWORD:
case NS_FORM_INPUT_TEXT:
{
// If the frame is there, we have to set the value so that it will show
// up.
if (formControlFrame) {
nsAutoString resetVal;
GetDefaultValue(resetVal);
rv = SetValue(resetVal);
}
SetValueChanged(PR_FALSE);
break;
}
case NS_FORM_INPUT_FILE:
{
// Resetting it to blank should not perform security check
rv = SetValueSecure(NS_LITERAL_STRING(""), nsnull, PR_FALSE);
break;
}
default:
break;
}
// Notify frame that it has been reset
if (formControlFrame) {
formControlFrame->OnContentReset();
}
return rv;
}
nsresult
nsHTMLInputElement::IsSuccessful(nsIContent* aSubmitElement,
PRBool *_retval)
{
*_retval = PR_FALSE;
// if it's disabled, it won't submit
PRBool disabled;
nsresult rv = GetDisabled(&disabled);
if (disabled) {
return NS_OK;
}
PRInt32 type;
GetType(&type);
// if it dosn't have a name it we don't submit
if (type != NS_FORM_INPUT_IMAGE) {
nsAutoString val;
rv = GetAttr(kNameSpaceID_None, nsHTMLAtoms::name, val);
if (rv == NS_CONTENT_ATTR_NOT_THERE) {
return NS_OK;
}
}
switch (type) {
case NS_FORM_INPUT_CHECKBOX:
case NS_FORM_INPUT_RADIO:
{
GetChecked(_retval);
break;
}
case NS_FORM_INPUT_HIDDEN:
case NS_FORM_INPUT_PASSWORD:
case NS_FORM_INPUT_TEXT:
case NS_FORM_INPUT_FILE:
{
*_retval = PR_TRUE;
break;
}
case NS_FORM_INPUT_RESET:
case NS_FORM_INPUT_BUTTON:
{
*_retval = PR_FALSE;
break;
}
case NS_FORM_INPUT_SUBMIT:
case NS_FORM_INPUT_IMAGE:
{
*_retval = (this == aSubmitElement);
}
}
return NS_OK;
}
nsresult
nsHTMLInputElement::GetMaxNumValues(PRInt32 *_retval)
{
PRInt32 type;
GetType(&type);
if (type == NS_FORM_INPUT_IMAGE) {
nsAutoString name;
nsAutoString value;
GetName(name);
GetValue(value);
if (name.IsEmpty() || value.IsEmpty()) {
*_retval = 2;
} else {
*_retval = 3;
}
} else {
*_retval = 1;
}
return NS_OK;
}
nsresult
nsHTMLInputElement::GetNamesValues(PRInt32 aMaxNumValues,
PRInt32& aNumValues,
nsString* aValues,
nsString* aNames)
{
nsresult rv;
PRInt32 type;
GetType(&type);
switch (type) {
case NS_FORM_INPUT_CHECKBOX:
case NS_FORM_INPUT_RADIO:
{
NS_ENSURE_TRUE(aMaxNumValues >= 1, NS_ERROR_UNEXPECTED);
GetName(aNames[0]);
GetValue(aValues[0]);
aNumValues = 1;
break;
}
case NS_FORM_INPUT_HIDDEN:
case NS_FORM_INPUT_PASSWORD:
case NS_FORM_INPUT_TEXT:
{
NS_ENSURE_TRUE(aMaxNumValues >= 1, NS_ERROR_UNEXPECTED);
GetName(aNames[0]);
GetValue(aValues[0]);
aNumValues = 1;
break;
}
case NS_FORM_INPUT_FILE:
{
NS_ENSURE_TRUE(aMaxNumValues >= 1, NS_ERROR_UNEXPECTED);
GetName(aNames[0]);
GetValue(aValues[0]);
aNumValues = 1;
break;
}
case NS_FORM_INPUT_IMAGE:
{
NS_ENSURE_TRUE(aMaxNumValues >= 2, NS_ERROR_UNEXPECTED);
// Go to the frame to find out where it was clicked. This is the only
// case where I can actually see using the frame, because you're talking
// about a value--mouse click--that is rightfully the domain of the frame.
//
// If the frame isn't there or isn't an ImageControlFrame, then we're not
// submitting these values no matter *how* nicely you ask.
PRInt32 clickedX;
PRInt32 clickedY;
nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
nsCOMPtr<nsIImageControlFrame> imageControlFrame(
do_QueryInterface(formControlFrame));
if (imageControlFrame) {
imageControlFrame->GetClickedX(&clickedX);
imageControlFrame->GetClickedY(&clickedY);
} else {
aNumValues = 0;
return NS_OK;
}
// Convert the values to strings for submission
char buf[20];
sprintf(&buf[0], "%d", clickedX);
aValues[0].AssignWithConversion(&buf[0]);
sprintf(&buf[0], "%d", clickedY);
aValues[1].AssignWithConversion(&buf[0]);
// Figure out the proper name of the x and y values
nsAutoString name;
rv = GetName(name);
if (!name.IsEmpty()) {
aNames[0] = name;
aNames[0].Append(NS_LITERAL_STRING(".x"));
aNames[1] = name;
aNames[1].Append(NS_LITERAL_STRING(".y"));
nsAutoString value;
rv = GetValue(value);
if (!value.IsEmpty()) {
aNames[2] = name;
aValues[2] = value;
aNumValues = 3;
} else {
aNumValues = 2;
}
} else {
// If the Image Element has no name, simply return x and y
// to Nav and IE compatability.
aNames[0] = NS_LITERAL_STRING("x");
aNames[1] = NS_LITERAL_STRING("y");
aNumValues = 2;
}
break;
}
case NS_FORM_INPUT_RESET:
case NS_FORM_INPUT_BUTTON:
{
aNumValues = 0;
break;
}
case NS_FORM_INPUT_SUBMIT:
{
NS_ENSURE_TRUE(aMaxNumValues >= 1, NS_ERROR_UNEXPECTED);
GetName(aNames[0]);
GetValue(aValues[0]);
aNumValues = 1;
break;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsHTMLInputElement::SaveState(nsIPresContext* aPresContext,
nsIPresState** aState)
{
nsresult rv = NS_OK;
PRInt32 type;
GetType(&type);
switch (type) {
// Never save passwords in session history
case NS_FORM_INPUT_PASSWORD:
break;
case NS_FORM_INPUT_TEXT:
case NS_FORM_INPUT_FILE:
nsresult rv = GetPrimaryPresState(this, aState);
if (*aState) {
nsString value;
GetValue(value);
// XXX Should use nsAutoString above but ConvertStringLineBreaks
// requires mOwnsBuffer!
rv = nsLinebreakConverter::ConvertStringLineBreaks(
value,
nsLinebreakConverter::eLinebreakPlatform,
nsLinebreakConverter::eLinebreakContent);
NS_ASSERTION(NS_SUCCEEDED(rv), "Converting linebreaks failed!");
rv = (*aState)->SetStateProperty(NS_LITERAL_STRING("value"), value);
NS_ASSERTION(NS_SUCCEEDED(rv), "value save failed!");
}
break;
}
return rv;
}
NS_IMETHODIMP
nsHTMLInputElement::RestoreState(nsIPresContext* aPresContext,
nsIPresState* aState)
{
nsresult rv = NS_OK;
PRInt32 type;
GetType(&type);
switch (type) {
// Never save passwords in session history
case NS_FORM_INPUT_PASSWORD:
break;
case NS_FORM_INPUT_TEXT:
case NS_FORM_INPUT_FILE:
nsAutoString value;
rv = aState->GetStateProperty(NS_LITERAL_STRING("value"), value);
NS_ASSERTION(NS_SUCCEEDED(rv), "value restore failed!");
SetValue(value);
break;
}
return rv;
}