/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * 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 Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Contributor(s): */ #include "nsIDOMHTMLButtonElement.h" #include "nsIDOMNSHTMLButtonElement.h" #include "nsIDOMHTMLFormElement.h" #include "nsIScriptObjectOwner.h" #include "nsIDOMEventReceiver.h" #include "nsIHTMLContent.h" #include "nsGenericHTMLElement.h" #include "nsHTMLAtoms.h" #include "nsHTMLIIDs.h" #include "nsIStyleContext.h" #include "nsIMutableStyleContext.h" #include "nsStyleConsts.h" #include "nsIPresContext.h" #include "nsIFormControl.h" #include "nsIForm.h" #include "nsIURL.h" #include "nsIFormControlFrame.h" #include "nsIEventStateManager.h" #include "nsIDOMEvent.h" #include "nsISizeOfHandler.h" #include "nsIDocument.h" class nsHTMLButtonElement : public nsGenericHTMLContainerFormElement, public nsIDOMHTMLButtonElement, public nsIDOMNSHTMLButtonElement { public: nsHTMLButtonElement(); virtual ~nsHTMLButtonElement(); // nsISupports NS_DECL_ISUPPORTS_INHERITED // nsIDOMNode NS_FORWARD_IDOMNODE_NO_CLONENODE(nsGenericHTMLContainerFormElement::) // nsIDOMElement NS_FORWARD_IDOMELEMENT(nsGenericHTMLContainerFormElement::) // nsIDOMHTMLElement NS_FORWARD_IDOMHTMLELEMENT(nsGenericHTMLContainerElement::) // nsIDOMHTMLButtonElement NS_DECL_IDOMHTMLBUTTONELEMENT // nsIDOMNSHTMLButtonElement NS_DECL_IDOMNSHTMLBUTTONELEMENT // overrided nsIFormControl method NS_IMETHOD GetType(PRInt32* aType); // nsIContent overrides... NS_IMETHOD GetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, nsAWritableString& aResult) const; NS_IMETHOD SetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, const nsAReadableString& aValue, PRBool aNotify); 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 HandleDOMEvent(nsIPresContext* aPresContext, nsEvent* aEvent, nsIDOMEvent** aDOMEvent, PRUint32 aFlags, nsEventStatus* aEventStatus); NS_IMETHOD SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const; protected: PRInt8 mType; }; // Construction, destruction nsresult NS_NewHTMLButtonElement(nsIHTMLContent** aInstancePtrResult, nsINodeInfo *aNodeInfo) { NS_ENSURE_ARG_POINTER(aInstancePtrResult); nsHTMLButtonElement* it = new nsHTMLButtonElement(); if (!it) { return NS_ERROR_OUT_OF_MEMORY; } nsresult rv = NS_STATIC_CAST(nsGenericElement *, it)->Init(aNodeInfo); if (NS_FAILED(rv)) { delete it; return rv; } *aInstancePtrResult = NS_STATIC_CAST(nsIHTMLContent *, it); NS_ADDREF(*aInstancePtrResult); return NS_OK; } nsHTMLButtonElement::nsHTMLButtonElement() { mType = NS_FORM_BUTTON_BUTTON; // default } nsHTMLButtonElement::~nsHTMLButtonElement() { // Null out form's pointer to us - no ref counting here! SetForm(nsnull); } // nsISupports NS_IMPL_ADDREF_INHERITED(nsHTMLButtonElement, nsGenericElement); NS_IMPL_RELEASE_INHERITED(nsHTMLButtonElement, nsGenericElement); NS_IMPL_HTMLCONTENT_QI2(nsHTMLButtonElement, nsGenericHTMLContainerFormElement, nsIDOMHTMLButtonElement, nsIDOMNSHTMLButtonElement); NS_IMETHODIMP nsHTMLButtonElement::GetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, nsAWritableString& aResult) const { if (aName == nsHTMLAtoms::disabled) { nsresult rv = nsGenericHTMLContainerFormElement::GetAttribute(kNameSpaceID_None, nsHTMLAtoms::disabled, aResult); if (rv == NS_CONTENT_ATTR_NOT_THERE) { aResult.Assign(NS_LITERAL_STRING("false")); } else { aResult.Assign(NS_LITERAL_STRING("true")); } return rv; } return nsGenericHTMLContainerFormElement::GetAttribute(aNameSpaceID, aName, aResult); } NS_IMETHODIMP nsHTMLButtonElement::SetAttribute(PRInt32 aNameSpaceID, nsIAtom* aName, const nsAReadableString& aValue, PRBool aNotify) { nsAutoString value(aValue); if (aName == nsHTMLAtoms::disabled && value.EqualsWithConversion("false", PR_TRUE)) { return UnsetAttribute(aNameSpaceID, aName, aNotify); } return nsGenericHTMLContainerFormElement::SetAttribute(aNameSpaceID, aName, aValue, aNotify); } // nsIDOMHTMLButtonElement nsresult nsHTMLButtonElement::CloneNode(PRBool aDeep, nsIDOMNode** aReturn) { NS_ENSURE_ARG_POINTER(aReturn); *aReturn = nsnull; nsHTMLButtonElement* it = new nsHTMLButtonElement(); if (!it) { return NS_ERROR_OUT_OF_MEMORY; } nsCOMPtr kungFuDeathGrip(it); nsresult rv = NS_STATIC_CAST(nsGenericElement *, 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; } // nsIContent NS_IMETHODIMP nsHTMLButtonElement::GetForm(nsIDOMHTMLFormElement** aForm) { return nsGenericHTMLContainerFormElement::GetForm(aForm); } NS_IMETHODIMP nsHTMLButtonElement::GetType(nsAWritableString& aType) { return AttributeToString(nsHTMLAtoms::type, nsHTMLValue(mType, eHTMLUnit_Enumerated), aType); } NS_IMPL_STRING_ATTR(nsHTMLButtonElement, AccessKey, accesskey) NS_IMPL_BOOL_ATTR(nsHTMLButtonElement, Disabled, disabled) NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Name, name) NS_IMPL_INT_ATTR(nsHTMLButtonElement, TabIndex, tabindex) NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Value, value) NS_IMETHODIMP nsHTMLButtonElement::Blur() { nsCOMPtr presContext; GetPresContext(this, getter_AddRefs(presContext)); return RemoveFocus(presContext); } NS_IMETHODIMP nsHTMLButtonElement::Focus() { nsCOMPtr presContext; GetPresContext(this, getter_AddRefs(presContext)); return SetFocus(presContext); } NS_IMETHODIMP nsHTMLButtonElement::SetFocus(nsIPresContext* aPresContext) { // first see if we are disabled or not. If disabled then do nothing. nsAutoString disabled; if (NS_CONTENT_ATTR_HAS_VALUE == GetAttribute(kNameSpaceID_HTML, nsHTMLAtoms::disabled, disabled)) { return NS_OK; } nsCOMPtr esm; if (NS_OK == aPresContext->GetEventStateManager(getter_AddRefs(esm))) { esm->SetContentState(this, NS_EVENT_STATE_FOCUS); } nsIFormControlFrame* formControlFrame = nsnull; nsresult rv = GetPrimaryFrame(this, formControlFrame); if (NS_SUCCEEDED(rv)) { formControlFrame->SetFocus(PR_TRUE, PR_TRUE); formControlFrame->ScrollIntoView(aPresContext); } return rv; } NS_IMETHODIMP nsHTMLButtonElement::RemoveFocus(nsIPresContext* 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 = nsnull; rv = GetPrimaryFrame(this, formControlFrame); if (NS_SUCCEEDED(rv)) { formControlFrame->SetFocus(PR_FALSE, PR_FALSE); } nsCOMPtr esm; if (NS_OK == aPresContext->GetEventStateManager(getter_AddRefs(esm))) { nsCOMPtr doc; GetDocument(*getter_AddRefs(doc)); if (!doc) return NS_ERROR_NULL_POINTER; nsCOMPtr rootContent; rootContent = getter_AddRefs(doc->GetRootContent()); rv = esm->SetContentState(rootContent, NS_EVENT_STATE_FOCUS); } return rv; } static nsGenericHTMLElement::EnumTable kButtonTypeTable[] = { { "button", NS_FORM_BUTTON_BUTTON }, { "reset", NS_FORM_BUTTON_RESET }, { "submit", NS_FORM_BUTTON_SUBMIT }, { 0 } }; NS_IMETHODIMP nsHTMLButtonElement::StringToAttribute(nsIAtom* aAttribute, const nsAReadableString& aValue, nsHTMLValue& aResult) { if (aAttribute == nsHTMLAtoms::tabindex) { if (ParseValue(aValue, 0, 32767, aResult, eHTMLUnit_Integer)) { return NS_CONTENT_ATTR_HAS_VALUE; } } else if (aAttribute == nsHTMLAtoms::type) { nsGenericHTMLElement::EnumTable *table = kButtonTypeTable; nsAutoString val(aValue); while (nsnull != table->tag) { if (val.EqualsIgnoreCase(table->tag)) { aResult.SetIntValue(table->value, eHTMLUnit_Enumerated); mType = table->value; return NS_CONTENT_ATTR_HAS_VALUE; } table++; } } else if (aAttribute == nsHTMLAtoms::disabled) { aResult.SetEmptyValue(); return NS_CONTENT_ATTR_HAS_VALUE; } return NS_CONTENT_ATTR_NOT_THERE; } NS_IMETHODIMP nsHTMLButtonElement::AttributeToString(nsIAtom* aAttribute, const nsHTMLValue& aValue, nsAWritableString& aResult) const { if (aAttribute == nsHTMLAtoms::type) { if (eHTMLUnit_Enumerated == aValue.GetUnit()) { EnumValueToString(aValue, kButtonTypeTable, aResult, PR_FALSE); return NS_CONTENT_ATTR_HAS_VALUE; } } return nsGenericHTMLContainerFormElement::AttributeToString(aAttribute, aValue, aResult); } NS_IMETHODIMP nsHTMLButtonElement::HandleDOMEvent(nsIPresContext* aPresContext, nsEvent* aEvent, nsIDOMEvent** aDOMEvent, PRUint32 aFlags, nsEventStatus* aEventStatus) { NS_ENSURE_ARG(aPresContext); NS_ENSURE_ARG_POINTER(aEventStatus); // Do not process any DOM events if the element is disabled PRBool bDisabled; nsresult rv = GetDisabled(&bDisabled); if (NS_FAILED(rv) || bDisabled) { return rv; } // Try script event handlers first nsresult ret; ret = nsGenericHTMLContainerFormElement::HandleDOMEvent(aPresContext, aEvent, aDOMEvent, aFlags, aEventStatus); if ((NS_OK == ret) && (nsEventStatus_eIgnore == *aEventStatus) && !(aFlags & NS_EVENT_FLAG_CAPTURE)) { switch (aEvent->message) { case NS_KEY_PRESS: { // For backwards compat, trigger buttons with space or enter // (bug 25300) nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent; if (keyEvent->keyCode == NS_VK_RETURN || keyEvent->charCode == NS_VK_SPACE) { 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); } } break;// NS_KEY_PRESS case NS_MOUSE_LEFT_CLICK: { // Tell the frame about the click nsIFormControlFrame* formControlFrame = nsnull; rv = GetPrimaryFrame(this, formControlFrame); if (NS_SUCCEEDED(rv)) { formControlFrame->MouseClicked(aPresContext); } } break;// NS_MOUSE_LEFT_CLICK case NS_MOUSE_LEFT_BUTTON_DOWN: { nsIEventStateManager *stateManager; if (NS_OK == aPresContext->GetEventStateManager(&stateManager)) { stateManager->SetContentState(this, NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS); NS_RELEASE(stateManager); } *aEventStatus = nsEventStatus_eConsumeNoDefault; } break; // 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 (aDOMEvent != nsnull && *aDOMEvent != nsnull) { (*aDOMEvent)->PreventBubble(); } else { ret = NS_ERROR_FAILURE; } break; case NS_MOUSE_ENTER_SYNTH: { nsIEventStateManager *stateManager; if (NS_OK == aPresContext->GetEventStateManager(&stateManager)) { stateManager->SetContentState(this, NS_EVENT_STATE_HOVER); NS_RELEASE(stateManager); } *aEventStatus = nsEventStatus_eConsumeNoDefault; } break; // XXX this doesn't seem to do anything yet case NS_MOUSE_EXIT_SYNTH: { nsIEventStateManager *stateManager; if (NS_OK == aPresContext->GetEventStateManager(&stateManager)) { stateManager->SetContentState(nsnull, NS_EVENT_STATE_HOVER); NS_RELEASE(stateManager); } *aEventStatus = nsEventStatus_eConsumeNoDefault; } break; default: break; } } return ret; } NS_IMETHODIMP nsHTMLButtonElement::GetType(PRInt32* aType) { if (aType) { *aType = mType; return NS_OK; } else { return NS_FORM_NOTOK; } } NS_IMETHODIMP nsHTMLButtonElement::SizeOf(nsISizeOfHandler* aSizer, PRUint32* aResult) const { *aResult = sizeof(*this) + BaseSizeOf(aSizer); return NS_OK; }