Back to index

lightning-sunbird  0.9+nobinonly
nsHTMLLabelElement.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 "nsCOMPtr.h"
00038 #include "nsIDOMHTMLLabelElement.h"
00039 #include "nsIDOMHTMLFormElement.h"
00040 #include "nsIDOMEventReceiver.h"
00041 #include "nsGenericHTMLElement.h"
00042 #include "nsHTMLAtoms.h"
00043 #include "nsStyleConsts.h"
00044 #include "nsPresContext.h"
00045 #include "nsIFormControl.h"
00046 #include "nsIForm.h"
00047 #include "nsIDOMHTMLDocument.h"
00048 #include "nsIDOMXULDocument.h"
00049 #include "nsIDocument.h"
00050 #include "nsIFormControlFrame.h"
00051 #include "nsIPresShell.h"
00052 #include "nsGUIEvent.h"
00053 #include "nsIEventStateManager.h"
00054 #include "nsIDOMHTMLInputElement.h"
00055 
00056 class nsHTMLLabelElement : public nsGenericHTMLFormElement,
00057                            public nsIDOMHTMLLabelElement
00058 {
00059 public:
00060   nsHTMLLabelElement(nsINodeInfo *aNodeInfo);
00061   virtual ~nsHTMLLabelElement();
00062 
00063   // nsISupports
00064   NS_DECL_ISUPPORTS_INHERITED
00065 
00066   // nsIDOMNode
00067   NS_FORWARD_NSIDOMNODE_NO_CLONENODE(nsGenericHTMLFormElement::)
00068 
00069   // nsIDOMElement
00070   NS_FORWARD_NSIDOMELEMENT(nsGenericHTMLFormElement::)
00071 
00072   // nsIDOMHTMLElement
00073   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLFormElement::)
00074 
00075   // nsIDOMNSHTMLElement
00076   NS_IMETHOD Focus();
00077 
00078   // nsIDOMHTMLLabelElement
00079   NS_DECL_NSIDOMHTMLLABELELEMENT
00080 
00081   // nsIFormControl
00082   NS_IMETHOD_(PRInt32) GetType() const { return NS_FORM_LABEL; }
00083   NS_IMETHOD Reset();
00084   NS_IMETHOD SubmitNamesValues(nsIFormSubmission* aFormSubmission,
00085                                nsIContent* aSubmitElement);
00086 
00087   // nsIContent
00088   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00089                               nsIContent* aBindingParent,
00090                               PRBool aCompileEventHandlers);
00091   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
00092                               PRBool aNullParent = PR_TRUE);
00093   virtual nsresult HandleDOMEvent(nsPresContext* aPresContext,
00094                                   nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
00095                                   PRUint32 aFlags,
00096                                   nsEventStatus* aEventStatus);
00097   virtual void SetFocus(nsPresContext* aContext);
00098   nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00099                    const nsAString& aValue, PRBool aNotify)
00100   {
00101     return SetAttr(aNameSpaceID, aName, nsnull, aValue, aNotify);
00102   }
00103   virtual nsresult SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName,
00104                            nsIAtom* aPrefix, const nsAString& aValue,
00105                            PRBool aNotify);
00106   virtual nsresult UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
00107                              PRBool aNotify);
00108 
00109 protected:
00110   already_AddRefed<nsIContent> GetForContent();
00111   already_AddRefed<nsIContent> GetFirstFormControl(nsIContent *current);
00112 
00113   // XXX It would be nice if we could use an event flag instead.
00114   PRPackedBool mHandlingEvent;
00115   PRPackedBool mFocusCalled;
00116 };
00117 
00118 // construction, destruction
00119 
00120 
00121 NS_IMPL_NS_NEW_HTML_ELEMENT(Label)
00122 
00123 
00124 nsHTMLLabelElement::nsHTMLLabelElement(nsINodeInfo *aNodeInfo)
00125   : nsGenericHTMLFormElement(aNodeInfo),
00126     mHandlingEvent(PR_FALSE),
00127     mFocusCalled(PR_FALSE)
00128 {
00129 }
00130 
00131 nsHTMLLabelElement::~nsHTMLLabelElement()
00132 {
00133 }
00134 
00135 // nsISupports 
00136 
00137 
00138 NS_IMPL_ADDREF_INHERITED(nsHTMLLabelElement, nsGenericElement) 
00139 NS_IMPL_RELEASE_INHERITED(nsHTMLLabelElement, nsGenericElement) 
00140 
00141 
00142 // QueryInterface implementation for nsHTMLLabelElement
00143 NS_HTML_CONTENT_INTERFACE_MAP_BEGIN(nsHTMLLabelElement,
00144                                     nsGenericHTMLFormElement)
00145   NS_INTERFACE_MAP_ENTRY(nsIDOMHTMLLabelElement)
00146   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(HTMLLabelElement)
00147 NS_HTML_CONTENT_INTERFACE_MAP_END
00148 
00149 
00150 // nsIDOMHTMLLabelElement
00151 
00152 
00153 NS_IMPL_DOM_CLONENODE(nsHTMLLabelElement)
00154 
00155 
00156 NS_IMETHODIMP
00157 nsHTMLLabelElement::GetForm(nsIDOMHTMLFormElement** aForm)
00158 {
00159   return nsGenericHTMLFormElement::GetForm(aForm);
00160 }
00161 
00162 
00163 NS_IMPL_STRING_ATTR(nsHTMLLabelElement, AccessKey, accesskey)
00164 NS_IMPL_STRING_ATTR(nsHTMLLabelElement, HtmlFor, _for)
00165 
00166 nsresult
00167 nsHTMLLabelElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
00168                                nsIContent* aBindingParent,
00169                                PRBool aCompileEventHandlers)
00170 {
00171   nsresult rv = nsGenericHTMLFormElement::BindToTree(aDocument, aParent,
00172                                                      aBindingParent,
00173                                                      aCompileEventHandlers);
00174   NS_ENSURE_SUCCESS(rv, rv);
00175 
00176   if (aDocument) {
00177     RegUnRegAccessKey(PR_TRUE);
00178   }
00179 
00180   return rv;
00181 }
00182 
00183 void
00184 nsHTMLLabelElement::UnbindFromTree(PRBool aDeep, PRBool aNullParent)
00185 {
00186   if (IsInDoc()) {
00187     RegUnRegAccessKey(PR_FALSE);
00188   }
00189 
00190   nsGenericHTMLFormElement::UnbindFromTree(aDeep, aNullParent);
00191 }
00192 
00193 static PRBool
00194 EventTargetIn(nsPresContext *aPresContext, nsEvent *aEvent,
00195               nsIContent *aChild, nsIContent *aStop)
00196 {
00197   nsCOMPtr<nsIContent> c;
00198   aPresContext->EventStateManager()->GetEventTargetContent(aEvent,
00199                                                            getter_AddRefs(c));
00200   nsIContent *content = c;
00201   while (content) {
00202     if (content == aChild) {
00203       return PR_TRUE;
00204     }
00205 
00206     if (content == aStop) {
00207       break;
00208     }
00209 
00210     content = content->GetParent();
00211   }
00212   return PR_FALSE;
00213 }
00214 
00215 nsresult
00216 nsHTMLLabelElement::HandleDOMEvent(nsPresContext* aPresContext,
00217                                    nsEvent* aEvent,
00218                                    nsIDOMEvent** aDOMEvent,
00219                                    PRUint32 aFlags,
00220                                    nsEventStatus* aEventStatus)
00221 {
00222   NS_ENSURE_ARG_POINTER(aEventStatus);
00223 
00224   nsresult rv = nsGenericHTMLFormElement::HandleDOMEvent(aPresContext, aEvent,
00225                                                          aDOMEvent, aFlags,
00226                                                          aEventStatus);
00227   if (NS_FAILED(rv))
00228     return rv;
00229 
00230   if (mHandlingEvent ||
00231       *aEventStatus == nsEventStatus_eConsumeNoDefault ||
00232       (aEvent->message != NS_MOUSE_LEFT_CLICK &&
00233        aEvent->message != NS_FOCUS_CONTENT) ||
00234       aFlags & NS_EVENT_FLAG_CAPTURE ||
00235       !(aFlags & NS_EVENT_FLAG_SYSTEM_EVENT))
00236     return NS_OK;
00237 
00238   nsCOMPtr<nsIContent> content = GetForContent();
00239   if (content && !EventTargetIn(aPresContext, aEvent, content, this)) {
00240     mHandlingEvent = PR_TRUE;
00241     switch (aEvent->message) {
00242       case NS_MOUSE_LEFT_CLICK:
00243         if (aEvent->eventStructType == NS_MOUSE_EVENT) {
00244           if (ShouldFocus(this)) {
00245             PRBool oldFocusCalled = mFocusCalled;
00246             mFocusCalled = PR_TRUE;
00247             // Focus the for content.
00248             SetFocus(aPresContext);
00249             mFocusCalled = oldFocusCalled;
00250           }
00251 
00252           // Dispatch a new click event to |content|
00253           //    (For compatibility with IE, we do only left click.  If
00254           //    we wanted to interpret the HTML spec very narrowly, we
00255           //    would do nothing.  If we wanted to do something
00256           //    sensible, we might send more events through like
00257           //    this.)  See bug 7554, bug 49897, and bug 96813.
00258           nsEventStatus status = *aEventStatus;
00259           rv = DispatchClickEvent(aPresContext, NS_STATIC_CAST(nsInputEvent*, aEvent),
00260                                   content, PR_FALSE, &status);
00261           // Do we care about the status this returned?  I don't think we do...
00262         }
00263         break;
00264       case NS_FOCUS_CONTENT:
00265         // Since we don't have '-moz-user-focus: normal', the only time
00266         // the event type will be NS_FOCUS_CONTENT will be when the accesskey
00267         // is activated.  We've already redirected the |SetFocus| call in that
00268         // case.
00269         // Since focus doesn't bubble, this is basically the second part
00270         // of redirecting |SetFocus|.
00271         {
00272           nsEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_FOCUS_CONTENT);
00273           nsEventStatus status = *aEventStatus;
00274           rv = DispatchEvent(aPresContext, &event, content, PR_TRUE, &status);
00275           // Do we care about the status this returned?  I don't think we do...
00276         }
00277         break;
00278     }
00279     mHandlingEvent = PR_FALSE;
00280   }
00281   return rv;
00282 }
00283 
00284 nsresult
00285 nsHTMLLabelElement::Focus()
00286 {
00287   PRBool oldFocusCalled = mFocusCalled;
00288   mFocusCalled = PR_TRUE;
00289   nsresult rv = nsGenericHTMLFormElement::Focus();
00290   mFocusCalled = oldFocusCalled;
00291   return rv;
00292 }
00293 
00294 void
00295 nsHTMLLabelElement::SetFocus(nsPresContext* aContext)
00296 {
00297   // Since we don't have '-moz-user-focus: normal', the only time
00298   // |SetFocus| will be called is when the accesskey is activated.
00299   nsCOMPtr<nsIContent> content = GetForContent();
00300   if (content) {
00301     if (mFocusCalled) {
00302       // Handle input element in a special way, so that focusing
00303       // <input type="file"> doesn't focus the input field but the
00304       // 'browse...' button.
00305       nsCOMPtr<nsIFormControl> control = do_QueryInterface(content);
00306       if (control && control->GetType() == NS_FORM_INPUT_FILE) {
00307         nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(content);
00308         if (input) {
00309           input->Focus();
00310           return;
00311         }
00312       }
00313     }
00314     content->SetFocus(aContext);
00315   }
00316 }
00317 
00318 nsresult
00319 nsHTMLLabelElement::Reset()
00320 {
00321   return NS_OK;
00322 }
00323 
00324 NS_IMETHODIMP
00325 nsHTMLLabelElement::SubmitNamesValues(nsIFormSubmission* aFormSubmission,
00326                                       nsIContent* aSubmitElement)
00327 {
00328   return NS_OK;
00329 }
00330 
00331 nsresult
00332 nsHTMLLabelElement::SetAttr(PRInt32 aNameSpaceID, nsIAtom* aName, nsIAtom* aPrefix,
00333                             const nsAString& aValue, PRBool aNotify)
00334 {
00335   if (aName == nsHTMLAtoms::accesskey && kNameSpaceID_None == aNameSpaceID) {
00336     RegUnRegAccessKey(PR_FALSE);
00337   }
00338 
00339   nsresult rv =
00340       nsGenericHTMLElement::SetAttr(aNameSpaceID, aName, aPrefix, aValue,
00341                                     aNotify);
00342 
00343   if (aName == nsHTMLAtoms::accesskey && kNameSpaceID_None == aNameSpaceID &&
00344       !aValue.IsEmpty()) {
00345     RegUnRegAccessKey(PR_TRUE);
00346   }
00347 
00348   return rv;
00349 }
00350 
00351 nsresult
00352 nsHTMLLabelElement::UnsetAttr(PRInt32 aNameSpaceID, nsIAtom* aAttribute,
00353                               PRBool aNotify)
00354 {
00355   if (aAttribute == nsHTMLAtoms::accesskey &&
00356       kNameSpaceID_None == aNameSpaceID) {
00357     RegUnRegAccessKey(PR_FALSE);
00358   }
00359 
00360   return nsGenericHTMLElement::UnsetAttr(aNameSpaceID, aAttribute, aNotify);
00361 }
00362 
00363 inline PRBool IsNonLabelFormControl(nsIContent *aContent)
00364 {
00365   return aContent->IsContentOfType(nsIContent::eHTML_FORM_CONTROL) &&
00366          aContent->Tag() != nsHTMLAtoms::label;
00367 }
00368 
00369 already_AddRefed<nsIContent>
00370 nsHTMLLabelElement::GetForContent()
00371 {
00372   nsresult rv;
00373 
00374   // Get the element that this label is for
00375   nsAutoString elementId;
00376   rv = GetHtmlFor(elementId);
00377   if (NS_SUCCEEDED(rv) && !elementId.IsEmpty()) {
00378     // We have a FOR attribute.
00379     nsCOMPtr<nsIDOMDocument> domDoc;
00380     GetOwnerDocument(getter_AddRefs(domDoc));
00381     if (domDoc) {
00382       nsCOMPtr<nsIDOMElement> domElement;
00383       domDoc->GetElementById(elementId, getter_AddRefs(domElement));
00384       nsIContent *result = nsnull;
00385       if (domElement) {
00386         CallQueryInterface(domElement, &result);
00387         if (result && !IsNonLabelFormControl(result)) {
00388           NS_RELEASE(result); // assigns null
00389         }
00390       }
00391       return result;
00392     }
00393   } else {
00394     // No FOR attribute, we are a label for our first form control element.
00395     // do a depth-first traversal to look for the first form control element
00396     return GetFirstFormControl(this);
00397   }
00398   return nsnull;
00399 }
00400 
00401 already_AddRefed<nsIContent>
00402 nsHTMLLabelElement::GetFirstFormControl(nsIContent *current)
00403 {
00404   PRUint32 numNodes = current->GetChildCount();
00405 
00406   for (PRUint32 i = 0; i < numNodes; i++) {
00407     nsIContent *child = current->GetChildAt(i);
00408     if (child) {
00409       if (IsNonLabelFormControl(child)) {
00410         NS_ADDREF(child);
00411         return child;
00412       }
00413 
00414       nsIContent* content = GetFirstFormControl(child).get();
00415       if (content) {
00416         return content;
00417       }
00418     }
00419   }
00420 
00421   return nsnull;
00422 }