Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLButtonElement.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is Mozilla Communicator client code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *
00024  * Alternatively, the contents of this file may be used under the terms of
00025  * either of the GNU General Public License Version 2 or later (the "GPL"),
00026  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00027  * in which case the provisions of the GPL or the LGPL are applicable instead
00028  * of those above. If you wish to allow use of your version of this file only
00029  * under the terms of either the GPL or the LGPL, and not to allow others to
00030  * use your version of this file under the terms of the MPL, indicate your
00031  * decision by deleting the provisions above and replace them with the notice
00032  * and other provisions required by the GPL or the LGPL. If you do not delete
00033  * the provisions above, a recipient may use your version of this file under
00034  * the terms of any one of the MPL, the GPL or the LGPL.
00035  *
00036  * ***** END LICENSE BLOCK ***** */
00037 #include "nsIDOMHTMLButtonElement.h"
00038 #include "nsIDOMNSHTMLButtonElement.h"
00039 #include "nsIDOMHTMLFormElement.h"
00040 #include "nsIDOMEventReceiver.h"
00041 #include "nsGenericHTMLElement.h"
00042 #include "nsHTMLAtoms.h"
00043 #include "nsIPresShell.h"
00044 #include "nsStyleConsts.h"
00045 #include "nsPresContext.h"
00046 #include "nsIFormControl.h"
00047 #include "nsIForm.h"
00048 #include "nsIFormSubmission.h"
00049 #include "nsIURL.h"
00050 
00051 #include "nsIFrame.h"
00052 #include "nsIFormControlFrame.h"
00053 #include "nsIEventStateManager.h"
00054 #include "nsIDOMEvent.h"
00055 #include "nsIDOMNSEvent.h"
00056 #include "nsIDocument.h"
00057 #include "nsGUIEvent.h"
00058 #include "nsUnicharUtils.h"
00059 
00060 
00061 class nsHTMLButtonElement : public nsGenericHTMLFormElement,
00062                             public nsIDOMHTMLButtonElement,
00063                             public nsIDOMNSHTMLButtonElement
00064 {
00065 public:
00066   nsHTMLButtonElement(nsINodeInfo *aNodeInfo);
00067   virtual ~nsHTMLButtonElement();
00068 
00069   // nsISupports
00070   NS_DECL_ISUPPORTS_INHERITED
00071 
00072   // nsIDOMNode
00073   NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLFormElement::)
00074 
00075   // nsIDOMElement
00076   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLFormElement::)
00077 
00078   // nsIDOMHTMLElement
00079   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLFormElement::)
00080 
00081   // nsIDOMHTMLButtonElement
00082   NS_DECL_NSIDOMHTMLBUTTONELEMENT
00083 
00084   // nsIDOMNSHTMLButtonElement
00085   // Can't just use the macro, since it shares GetType with
00086   // nsIDOMHTMLButtonElement
00087   NS_IMETHOD Blur();
00088   NS_IMETHOD Focus();
00089   NS_IMETHOD Click();
00090   NS_IMETHOD SetType(const nsAString& aType);
00091 
00092   // overrided nsIFormControl method
00093   NS_IMETHOD_(PRInt32) GetType() const { return mType; }
00094   NS_IMETHOD Reset();
00095   NS_IMETHOD SubmitNamesValues(nsIFormSubmission* aFormSubmission,
00096                                nsIContent* aSubmitElement);
00097 
00098   // nsIContent overrides...
00099   virtual void SetFocus(nsPresContext* aPresContext);
00100   virtual PRBool IsFocusable(PRInt32 *aTabIndex = nsnull);
00101   virtual PRBool ParseAttribute(nsIAtom* aAttribute,
00102                                 const nsAString& aValue,
00103                                 nsAttrValue& aResult);
00104   virtual nsresult HandleDOMEvent(nsPresContext* aPresContext,
00105                                   nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
00106                                   PRUint32 aFlags,
00107                                   nsEventStatus* aEventStatus);
00108 
00109 protected:
00110   PRInt8 mType;
00111   PRPackedBool mHandlingClick;
00112 
00113 private:
00114   // The analogue of defaultValue in the DOM for input and textarea
00115   nsresult SetDefaultValue(const nsAString& aDefaultValue);
00116   nsresult GetDefaultValue(nsAString& aDefaultValue);
00117 };
00118 
00119 
00120 // Construction, destruction
00121 
00122 
00123 NS_IMPL_NS_NEW_HTML_ELEMENT(Button)
00124 
00125 
00126 nsHTMLButtonElement::nsHTMLButtonElement(nsINodeInfo *aNodeInfo)
00127   : nsGenericHTMLFormElement(aNodeInfo)
00128 {
00129   mType = NS_FORM_BUTTON_SUBMIT; // default
00130   mHandlingClick = PR_FALSE;
00131 }
00132 
00133 nsHTMLButtonElement::~nsHTMLButtonElement()
00134 {
00135 }
00136 
00137 // nsISupports
00138 
00139 NS_IMPL_ADDREF_INHERITED(nsHTMLButtonElement, nsGenericElement)
00140 NS_IMPL_RELEASE_INHERITED(nsHTMLButtonElement, nsGenericElement)
00141 
00142 
00143 // QueryInterface implementation for nsHTMLButtonElement
00144 NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLButtonElement,
00145                                     nsGenericHTMLFormElement)
00146   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLButtonElement)
00147   NS_INTERFACE_MAP_ENTRY(nsIDOMNSHTMLButtonElement)
00148   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLButtonElement)
00149 NS_HTML_CONTENT_INTERFACE_MAP_END
00150 
00151 // nsIDOMHTMLButtonElement
00152 
00153 
00154 NS_IMPL_DOM_CLONENODE(nsHTMLButtonElement)
00155 
00156 
00157 // nsIDOMHTMLButtonElement
00158 
00159 NS_IMETHODIMP
00160 nsHTMLButtonElement::GetForm(nsIDOMHTMLFormElement** aForm)
00161 {
00162   return nsGenericHTMLFormElement::GetForm(aForm);
00163 }
00164 
00165 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, AccessKey, accesskey)
00166 NS_IMPL_BOOL_ATTR(nsHTMLButtonElement, Disabled, disabled)
00167 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Name, name)
00168 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, TabIndex, tabindex, 0)
00169 NS_IMPL_STRING_ATTR(nsHTMLButtonElement, Value, value)
00170 NS_IMPL_STRING_ATTR_DEFAULT_VALUE(nsHTMLButtonElement, Type, type, "submit")
00171 
00172 NS_IMETHODIMP
00173 nsHTMLButtonElement::Blur()
00174 {
00175   if (ShouldFocus(this)) {
00176     SetElementFocus(PR_FALSE);
00177   }
00178 
00179   return NS_OK;
00180 }
00181 
00182 NS_IMETHODIMP
00183 nsHTMLButtonElement::Focus()
00184 {
00185   if (ShouldFocus(this)) {
00186     SetElementFocus(PR_TRUE);
00187   }
00188 
00189   return NS_OK;
00190 }
00191 
00192 NS_IMETHODIMP
00193 nsHTMLButtonElement::Click()
00194 {
00195   if (mHandlingClick)
00196     return NS_OK;
00197 
00198   mHandlingClick = PR_TRUE;
00199   // Hold on to the document in case one of the events makes it die or
00200   // something...
00201   nsCOMPtr<nsIDocument> doc = GetCurrentDoc();
00202 
00203   if (doc) {
00204     nsIPresShell *shell = doc->GetShellAt(0);
00205     if (shell) {
00206       nsCOMPtr<nsPresContext> context = shell->GetPresContext();
00207       if (context) {
00208         // Click() is never called from native code, but it may be
00209         // called from chrome JS. Mark this event trusted if Click()
00210         // is called from chrome code.
00211         nsMouseEvent event(nsContentUtils::IsCallerChrome(),
00212                            NS_MOUSE_LEFT_CLICK, nsnull,
00213                            nsMouseEvent::eReal);
00214         nsEventStatus status = nsEventStatus_eIgnore;
00215         HandleDOMEvent(context, &event, nsnull,
00216                        NS_EVENT_FLAG_INIT, &status);
00217       }
00218     }
00219   }
00220 
00221   mHandlingClick = PR_FALSE;
00222 
00223   return NS_OK;
00224 }
00225 
00226 PRBool
00227 nsHTMLButtonElement::IsFocusable(PRInt32 *aTabIndex)
00228 {
00229   if (!nsGenericHTMLElement::IsFocusable(aTabIndex)) {
00230     return PR_FALSE;
00231   }
00232   if (aTabIndex && (sTabFocusModel & eTabFocus_formElementsMask) == 0) {
00233     *aTabIndex = -1;
00234   }
00235   return PR_TRUE;
00236 }
00237 
00238 void
00239 nsHTMLButtonElement::SetFocus(nsPresContext* aPresContext)
00240 {
00241   if (!aPresContext)
00242     return;
00243 
00244   // first see if we are disabled or not. If disabled then do nothing.
00245   if (HasAttr(kNameSpaceID_None, nsHTMLAtoms::disabled)) {
00246     return;
00247   }
00248 
00249   aPresContext->EventStateManager()->SetContentState(this,
00250                                                      NS_EVENT_STATE_FOCUS);
00251 
00252   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
00253 
00254   if (formControlFrame) {
00255     formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
00256     formControlFrame->ScrollIntoView(aPresContext);
00257   }
00258 }
00259 
00260 static const nsAttrValue::EnumTable kButtonTypeTable[] = {
00261   { "button", NS_FORM_BUTTON_BUTTON },
00262   { "reset", NS_FORM_BUTTON_RESET },
00263   { "submit", NS_FORM_BUTTON_SUBMIT },
00264   { 0 }
00265 };
00266 
00267 PRBool
00268 nsHTMLButtonElement::ParseAttribute(nsIAtom* aAttribute,
00269                                     const nsAString& aValue,
00270                                     nsAttrValue& aResult)
00271 {
00272   if (aAttribute == nsHTMLAtoms::type) {
00273     // XXX ARG!! This is major evilness. ParseAttribute
00274     // shouldn't set members. Override SetAttr instead
00275     PRBool res = aResult.ParseEnumValue(aValue, kButtonTypeTable);
00276     if (res) {
00277       mType = aResult.GetEnumValue();
00278     }
00279     return res;
00280   }
00281 
00282   return nsGenericHTMLElement::ParseAttribute(aAttribute, aValue, aResult);
00283 }
00284 
00285 nsresult
00286 nsHTMLButtonElement::HandleDOMEvent(nsPresContext* aPresContext,
00287                                     nsEvent* aEvent,
00288                                     nsIDOMEvent** aDOMEvent,
00289                                     PRUint32 aFlags,
00290                                     nsEventStatus* aEventStatus)
00291 {
00292   NS_ENSURE_ARG_POINTER(aEventStatus);
00293 
00294   // Do not process any DOM events if the element is disabled
00295   PRBool bDisabled;
00296   nsresult rv = GetDisabled(&bDisabled);
00297   if (NS_FAILED(rv) || bDisabled) {
00298     return rv;
00299   }
00300 
00301   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_FALSE);
00302 
00303   if (formControlFrame) {
00304     nsIFrame* formFrame = nsnull;
00305     CallQueryInterface(formControlFrame, &formFrame);
00306 
00307     if (formFrame) {
00308       const nsStyleUserInterface* uiStyle = formFrame->GetStyleUserInterface();
00309 
00310       if (uiStyle->mUserInput == NS_STYLE_USER_INPUT_NONE ||
00311           uiStyle->mUserInput == NS_STYLE_USER_INPUT_DISABLED)
00312         return NS_OK;
00313     }
00314   }
00315 
00316   PRBool bInSubmitClick = mType == NS_FORM_BUTTON_SUBMIT && 
00317                           !(aFlags & NS_EVENT_FLAG_CAPTURE) &&
00318                           !(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT) &&
00319                           aEvent->message == NS_MOUSE_LEFT_CLICK &&
00320                           mForm;
00321 
00322   if (bInSubmitClick) {
00323     // tell the form that we are about to enter a click handler.
00324     // that means that if there are scripted submissions, the
00325     // latest one will be deferred until after the exit point of the handler. 
00326     mForm->OnSubmitClickBegin();
00327   }
00328 
00329   // Try script event handlers first
00330   nsresult ret;
00331   ret = nsGenericHTMLFormElement::HandleDOMEvent(aPresContext, aEvent,
00332                                                  aDOMEvent, aFlags,
00333                                                  aEventStatus);
00334 
00335   // mForm is null if the event handler removed us from the document (bug 194582).
00336   if (bInSubmitClick && mForm) {
00337     // tell the form that we are about to exit a click handler
00338     // so the form knows not to defer subsequent submissions
00339     // the pending ones that were created during the handler
00340     // will be flushed or forgoten.
00341     mForm->OnSubmitClickEnd();
00342   }
00343 
00344   if (NS_SUCCEEDED(ret) && 
00345      !(aFlags & NS_EVENT_FLAG_CAPTURE) && !(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT)) {
00346     if (nsEventStatus_eIgnore == *aEventStatus) { 
00347       switch (aEvent->message) {
00348 
00349       case NS_KEY_PRESS:
00350       case NS_KEY_UP:
00351         {
00352           // For backwards compat, trigger buttons with space or enter
00353           // (bug 25300)
00354           nsKeyEvent * keyEvent = (nsKeyEvent *)aEvent;
00355           if ((keyEvent->keyCode == NS_VK_RETURN && NS_KEY_PRESS == aEvent->message) ||
00356               keyEvent->keyCode == NS_VK_SPACE  && NS_KEY_UP == aEvent->message) {
00357             nsEventStatus status = nsEventStatus_eIgnore;
00358 
00359             nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent),
00360                                NS_MOUSE_LEFT_CLICK, nsnull,
00361                                nsMouseEvent::eReal);
00362             rv = HandleDOMEvent(aPresContext, &event, nsnull,
00363                                 NS_EVENT_FLAG_INIT, &status);
00364           }
00365         }
00366         break;// NS_KEY_PRESS
00367 
00368       case NS_MOUSE_LEFT_CLICK:
00369         {
00370           nsCOMPtr<nsIPresShell> presShell = aPresContext->GetPresShell();
00371           if (presShell) {
00372             // single-click
00373             nsUIEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_UI_ACTIVATE, 1);
00374             nsEventStatus status = nsEventStatus_eIgnore;
00375 
00376             presShell->HandleDOMEventWithTarget(this, &event, &status);
00377             *aEventStatus = status;
00378           }
00379         }
00380         break;
00381 
00382       case NS_UI_ACTIVATE:
00383         {
00384           if (mForm && (mType == NS_FORM_BUTTON_SUBMIT ||
00385                         mType == NS_FORM_BUTTON_RESET)) {
00386             nsFormEvent event(PR_TRUE,
00387                               (mType == NS_FORM_BUTTON_RESET)
00388                               ? NS_FORM_RESET : NS_FORM_SUBMIT);
00389             event.originator      = this;
00390             nsEventStatus status  = nsEventStatus_eIgnore;
00391 
00392             nsCOMPtr<nsIPresShell> presShell = aPresContext->GetPresShell();
00393             // If |nsIPresShell::Destroy| has been called due to
00394             // handling the event (base class HandleDOMEvent, above),
00395             // the pres context will return a null pres shell.  See
00396             // bug 125624.
00397             if (presShell) {
00398               nsCOMPtr<nsIContent> form(do_QueryInterface(mForm));
00399               presShell->HandleDOMEventWithTarget(form, &event, &status);
00400             }
00401           }
00402         }
00403         break;// NS_MOUSE_LEFT_CLICK
00404 
00405       case NS_MOUSE_LEFT_BUTTON_DOWN:
00406         {
00407           aPresContext->EventStateManager()->
00408             SetContentState(this,
00409                             NS_EVENT_STATE_ACTIVE | NS_EVENT_STATE_FOCUS);
00410 
00411           *aEventStatus = nsEventStatus_eConsumeNoDefault; 
00412         }
00413         break;
00414 
00415       // cancel all of these events for buttons
00416       case NS_MOUSE_MIDDLE_BUTTON_DOWN:
00417       case NS_MOUSE_MIDDLE_BUTTON_UP:
00418       case NS_MOUSE_MIDDLE_DOUBLECLICK:
00419       case NS_MOUSE_RIGHT_DOUBLECLICK:
00420       case NS_MOUSE_RIGHT_BUTTON_DOWN:
00421       case NS_MOUSE_RIGHT_BUTTON_UP:
00422         {
00423           if (aDOMEvent) {
00424             (*aDOMEvent)->StopPropagation();
00425           } else {
00426             ret = NS_ERROR_FAILURE;
00427           }
00428         }
00429 
00430         break;
00431 
00432       case NS_MOUSE_ENTER_SYNTH:
00433         {
00434           aPresContext->EventStateManager()->
00435             SetContentState(this, NS_EVENT_STATE_HOVER);
00436 
00437           *aEventStatus = nsEventStatus_eConsumeNoDefault; 
00438         }
00439         break;
00440 
00441         // XXX this doesn't seem to do anything yet
00442       case NS_MOUSE_EXIT_SYNTH:
00443         {
00444           aPresContext->EventStateManager()->
00445             SetContentState(nsnull, NS_EVENT_STATE_HOVER);
00446 
00447           *aEventStatus = nsEventStatus_eConsumeNoDefault; 
00448         }
00449         break;
00450 
00451       default:
00452         break;
00453       }
00454          } else {
00455       switch (aEvent->message) {
00456         // Make sure any pending submissions from a call to
00457         // form.submit() in a left click handler or an activate
00458         // handler gets flushed, even if the event handler prevented
00459         // the default action.
00460       case NS_MOUSE_LEFT_CLICK:
00461       case NS_UI_ACTIVATE:
00462         if (mForm && mType == NS_FORM_BUTTON_SUBMIT) {
00463           // Tell the form to flush a possible pending submission.
00464           // the reason is that the script returned false (the event was
00465           // not ignored) so if there is a stored submission, it needs to
00466           // be submitted immediatelly.
00467           mForm->FlushPendingSubmission();
00468         }
00469         break;// NS_UI_ACTIVATE
00470       } //switch
00471     } //if
00472   } //if
00473 
00474   return ret;
00475 }
00476 
00477 nsresult
00478 nsHTMLButtonElement::GetDefaultValue(nsAString& aDefaultValue)
00479 {
00480   return GetAttr(kNameSpaceID_None, nsHTMLAtoms::value, aDefaultValue);
00481 }
00482 
00483 nsresult
00484 nsHTMLButtonElement::SetDefaultValue(const nsAString& aDefaultValue)
00485 {
00486   return SetAttr(kNameSpaceID_None, nsHTMLAtoms::value, aDefaultValue, PR_TRUE);
00487 }
00488 
00489 NS_IMETHODIMP
00490 nsHTMLButtonElement::Reset()
00491 {
00492   return NS_OK;
00493 }
00494 
00495 NS_IMETHODIMP
00496 nsHTMLButtonElement::SubmitNamesValues(nsIFormSubmission* aFormSubmission,
00497                                        nsIContent* aSubmitElement)
00498 {
00499   nsresult rv = NS_OK;
00500 
00501   //
00502   // We only submit if we were the button pressed
00503   //
00504   if (aSubmitElement != this) {
00505     return NS_OK;
00506   }
00507 
00508   //
00509   // Disabled elements don't submit
00510   //
00511   PRBool disabled;
00512   rv = GetDisabled(&disabled);
00513   if (NS_FAILED(rv) || disabled) {
00514     return rv;
00515   }
00516 
00517   //
00518   // Get the name (if no name, no submit)
00519   //
00520   nsAutoString name;
00521   rv = GetAttr(kNameSpaceID_None, nsHTMLAtoms::name, name);
00522   if (NS_FAILED(rv) || rv == NS_CONTENT_ATTR_NOT_THERE) {
00523     return rv;
00524   }
00525 
00526   //
00527   // Get the value
00528   //
00529   nsAutoString value;
00530   rv = GetValue(value);
00531   if (NS_FAILED(rv)) {
00532     return rv;
00533   }
00534 
00535   //
00536   // Submit
00537   //
00538   rv = aFormSubmission->AddNameValuePair(this, name, value);
00539 
00540   return rv;
00541 }