Bug 456617. Refactor some ugly code to only live in one spot. r+sr=jst
authorBoris Zbarsky <bzbarsky@mit.edu>
Tue, 07 Oct 2008 14:53:23 -0400
changeset 20109 97d43a4491acb065ec7e7659198c9053946e4caf
parent 20108 305cbf365db22702c7c22fc5b4e6553acea1b139
child 20110 9395c3a493b2136290adbd9bd7132f7662a93774
push idunknown
push userunknown
push dateunknown
bugs456617
milestone1.9.1b2pre
Bug 456617. Refactor some ugly code to only live in one spot. r+sr=jst
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLButtonElement.cpp
content/html/content/src/nsHTMLInputElement.cpp
content/html/content/src/nsHTMLSelectElement.cpp
content/html/content/src/nsHTMLTextAreaElement.cpp
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -106,16 +106,17 @@
 #include "nsIDOMText.h"
 
 #include "nsIEditor.h"
 #include "nsIEditorIMESupport.h"
 #include "nsEventDispatcher.h"
 #include "nsLayoutUtils.h"
 #include "nsContentCreatorFunctions.h"
 #include "mozAutoDocUpdate.h"
+#include "nsIFocusController.h"
 
 class nsINodeInfo;
 class nsIDOMNodeList;
 class nsRuleWalker;
 
 // XXX todo: add in missing out-of-memory checks
 
 //----------------------------------------------------------------------
@@ -2665,16 +2666,63 @@ nsGenericHTMLFormElement::SetFocusAndScr
       if (presShell) {
         presShell->ScrollContentIntoView(this, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
                                          NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
       }
     }
   }
 }
 
+void
+nsGenericHTMLFormElement::DoSetFocus(nsPresContext* aPresContext)
+{
+  if (!aPresContext)
+    return;
+
+  if (FocusState() == eActiveWindow) {
+    SetFocusAndScrollIntoView(aPresContext);
+  }
+}
+
+nsGenericHTMLFormElement::FocusTristate
+nsGenericHTMLFormElement::FocusState()
+{
+  // We can't be focused if we aren't in a document
+  nsIDocument* doc = GetCurrentDoc();
+  if (!doc)
+    return eUnfocusable;
+
+  // first see if we are disabled or not. If disabled then do nothing.
+  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
+    return eUnfocusable;
+  }
+
+  // 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<nsPIDOMWindow> win = doc->GetWindow();
+  if (win) {
+    nsIFocusController *focusController = win->GetRootFocusController();
+    if (focusController) {
+      PRBool isActive = PR_FALSE;
+      focusController->GetActive(&isActive);
+      if (!isActive) {
+        focusController->SetFocusedWindow(win);
+        nsCOMPtr<nsIDOMElement> el =
+          do_QueryInterface(static_cast<nsGenericHTMLElement*>(this));
+        focusController->SetFocusedElement(el);
+
+        return eInactiveWindow;
+      }
+    }
+  }
+
+  return eActiveWindow;
+}
+
 //----------------------------------------------------------------------
 
 nsGenericHTMLFrameElement::~nsGenericHTMLFrameElement()
 {
   if (mFrameLoader) {
     mFrameLoader->Destroy();
   }
 }
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -863,16 +863,32 @@ protected:
    * Returns true if the control can be disabled
    */
   PRBool CanBeDisabled() const;
 
   void UpdateEditableFormControlState();
 
   void SetFocusAndScrollIntoView(nsPresContext* aPresContext);
 
+  // A sane SetFocus implementation for focusable form controls
+  void DoSetFocus(nsPresContext* aPresContext);
+
+  // The focusability state of this form control.  eUnfocusable means that it
+  // shouldn't be focused at all, eInactiveWindow means it's in an inactive
+  // window, eActiveWindow means it's in an active window.
+  enum FocusTristate {
+    eUnfocusable,
+    eInactiveWindow,
+    eActiveWindow
+  };
+
+  // Get our focus state.  If this returns eInactiveWindow, it will set this
+  // element as the focused element for that window.
+  FocusTristate FocusState();
+
   /** The form that contains this control */
   nsIForm* mForm;
 };
 
 // If this flag is set on an nsGenericHTMLFormElement, that means that we have
 // added ourselves to our mForm.  It's possible to have a non-null mForm, but
 // not have this flag set.  That happens when the form is set via the content
 // sink.
--- a/content/html/content/src/nsHTMLButtonElement.cpp
+++ b/content/html/content/src/nsHTMLButtonElement.cpp
@@ -256,25 +256,17 @@ nsHTMLButtonElement::IsHTMLFocusable(PRB
   *aIsFocusable = PR_TRUE;
 
   return PR_FALSE;
 }
 
 void
 nsHTMLButtonElement::SetFocus(nsPresContext* aPresContext)
 {
-  if (!aPresContext)
-    return;
-
-  // first see if we are disabled or not. If disabled then do nothing.
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
-    return;
-  }
-
-  SetFocusAndScrollIntoView(aPresContext);
+  DoSetFocus(aPresContext);
 }
 
 static const nsAttrValue::EnumTable kButtonTypeTable[] = {
   { "button", NS_FORM_BUTTON_BUTTON },
   { "reset", NS_FORM_BUTTON_RESET },
   { "submit", NS_FORM_BUTTON_SUBMIT },
   { 0 }
 };
--- a/content/html/content/src/nsHTMLInputElement.cpp
+++ b/content/html/content/src/nsHTMLInputElement.cpp
@@ -41,17 +41,16 @@
 #include "nsITextControlElement.h"
 #include "nsIFileControlElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIRadioControlElement.h"
 #include "nsIRadioVisitor.h"
 #include "nsIPhonetic.h"
 
 #include "nsIControllers.h"
-#include "nsIFocusController.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentCID.h"
 #include "nsIComponentManager.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIDOMEventTarget.h"
 #include "nsGenericHTMLElement.h"
 #include "nsGkAtoms.h"
 #include "nsStyleConsts.h"
@@ -1260,134 +1259,85 @@ nsHTMLInputElement::Focus()
   }
 
   return NS_OK;
 }
 
 void
 nsHTMLInputElement::SetFocus(nsPresContext* aPresContext)
 {
-  if (!aPresContext)
-    return;
-
-  // We can't be focus'd if we aren't in a document
-  nsIDocument* doc = GetCurrentDoc();
-  if (!doc)
-    return;
-
-  // first see if we are disabled or not. If disabled then do nothing.
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
-    return;
-  }
- 
-  // 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<nsPIDOMWindow> win = doc->GetWindow();
-  if (win) {
-    nsIFocusController *focusController = win->GetRootFocusController();
-    if (focusController) {
-      PRBool isActive = PR_FALSE;
-      focusController->GetActive(&isActive);
-      if (!isActive) {
-        focusController->SetFocusedWindow(win);
-        focusController->SetFocusedElement(this);
-
-        return;
-      }
-    }
-  }
-
-  SetFocusAndScrollIntoView(aPresContext);
+  DoSetFocus(aPresContext);
 }
 
 NS_IMETHODIMP
 nsHTMLInputElement::Select()
 {
-  nsresult rv = NS_OK;
-
-  nsIDocument* doc = GetCurrentDoc();
-  if (!doc)
+  if (mType != NS_FORM_INPUT_PASSWORD && mType != NS_FORM_INPUT_TEXT) {
     return NS_OK;
-
-  // first see if we are disabled or not. If disabled then do nothing.
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
+  }
+
+  // XXX Bug?  We have to give the input focus before contents can be
+  // selected
+
+  FocusTristate state = FocusState();
+  if (state == eUnfocusable) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPresContext> presContext = GetPresContext();
+  if (state == eInactiveWindow) {
+    SelectAll(presContext);
     return NS_OK;
   }
 
-  if (mType == NS_FORM_INPUT_PASSWORD || mType == NS_FORM_INPUT_TEXT) {
-    // XXX Bug?  We have to give the input focus before contents can be
-    // selected
-
-    nsCOMPtr<nsPresContext> presContext = GetPresContext();
-
-    // 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.
-    nsPIDOMWindow *win = doc->GetWindow();
-    if (win) {
-      nsIFocusController *focusController = win->GetRootFocusController();
-      if (focusController) {
-        PRBool isActive = PR_FALSE;
-        focusController->GetActive(&isActive);
-        if (!isActive) {
-          focusController->SetFocusedWindow(win);
-          focusController->SetFocusedElement(this);
-          SelectAll(presContext);
-          return NS_OK;
-        }
+  // Just like SetFocus() but without the ScrollIntoView()!
+  nsEventStatus status = nsEventStatus_eIgnore;
+    
+  //If already handling select event, don't dispatch a second.
+  if (!GET_BOOLBIT(mBitField, BF_HANDLING_SELECT_EVENT)) {
+    nsEvent event(nsContentUtils::IsCallerChrome(), NS_FORM_SELECTED);
+
+    SET_BOOLBIT(mBitField, BF_HANDLING_SELECT_EVENT, PR_TRUE);
+    nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
+                                presContext, &event, nsnull, &status);
+    SET_BOOLBIT(mBitField, BF_HANDLING_SELECT_EVENT, PR_FALSE);
+  }
+
+  // If the DOM event was not canceled (e.g. by a JS event handler
+  // returning false)
+  if (status == nsEventStatus_eIgnore) {
+    PRBool shouldFocus = ShouldFocus(this);
+
+    if (presContext && shouldFocus) {
+      nsIEventStateManager *esm = presContext->EventStateManager();
+      // XXX Fix for bug 135345 - ESM currently does not check to see if we
+      // have focus before attempting to set focus again and may cause
+      // infinite recursion.  For now check if we have focus and do not set
+      // focus again if already focused.
+      PRInt32 currentState;
+      esm->GetContentState(this, currentState);
+      if (!(currentState & NS_EVENT_STATE_FOCUS) &&
+          !esm->SetContentState(this, NS_EVENT_STATE_FOCUS)) {
+        return NS_OK; // We ended up unfocused, e.g. due to a DOM event handler.
       }
     }
 
-    // Just like SetFocus() but without the ScrollIntoView()!
-    nsEventStatus status = nsEventStatus_eIgnore;
-    
-    //If already handling select event, don't dispatch a second.
-    if (!GET_BOOLBIT(mBitField, BF_HANDLING_SELECT_EVENT)) {
-      nsEvent event(nsContentUtils::IsCallerChrome(), NS_FORM_SELECTED);
-
-      SET_BOOLBIT(mBitField, BF_HANDLING_SELECT_EVENT, PR_TRUE);
-      nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this),
-                                  presContext, &event, nsnull, &status);
-      SET_BOOLBIT(mBitField, BF_HANDLING_SELECT_EVENT, PR_FALSE);
-    }
-
-    // If the DOM event was not canceled (e.g. by a JS event handler
-    // returning false)
-    if (status == nsEventStatus_eIgnore) {
-      PRBool shouldFocus = ShouldFocus(this);
-
-      if (presContext && shouldFocus) {
-        nsIEventStateManager *esm = presContext->EventStateManager();
-        // XXX Fix for bug 135345 - ESM currently does not check to see if we
-        // have focus before attempting to set focus again and may cause
-        // infinite recursion.  For now check if we have focus and do not set
-        // focus again if already focused.
-        PRInt32 currentState;
-        esm->GetContentState(this, currentState);
-        if (!(currentState & NS_EVENT_STATE_FOCUS) &&
-            !esm->SetContentState(this, NS_EVENT_STATE_FOCUS)) {
-          return rv; // We ended up unfocused, e.g. due to a DOM event handler.
-        }
+    nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
+
+    if (formControlFrame) {
+      if (shouldFocus) {
+        formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
       }
 
-      nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
-
-      if (formControlFrame) {
-        if (shouldFocus) {
-          formControlFrame->SetFocus(PR_TRUE, PR_TRUE);
-        }
-
-        // Now Select all the text!
-        SelectAll(presContext);
-      }
+      // Now Select all the text!
+      SelectAll(presContext);
     }
   }
 
-  return rv;
+  return NS_OK;
 }
 
 void
 nsHTMLInputElement::SelectAll(nsPresContext* aPresContext)
 {
   nsIFormControlFrame* formControlFrame = GetFormControlFrame(PR_TRUE);
 
   if (formControlFrame) {
--- a/content/html/content/src/nsHTMLSelectElement.cpp
+++ b/content/html/content/src/nsHTMLSelectElement.cpp
@@ -1218,25 +1218,17 @@ nsHTMLSelectElement::Focus()
   }
 
   return NS_OK;
 }
 
 void
 nsHTMLSelectElement::SetFocus(nsPresContext* aPresContext)
 {
-  if (!aPresContext)
-    return;
-
-  // first see if we are disabled or not. If disabled then do nothing.
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
-    return;
-  }
-
-  SetFocusAndScrollIntoView(aPresContext);
+  DoSetFocus(aPresContext);
 }
 
 PRBool
 nsHTMLSelectElement::IsHTMLFocusable(PRBool *aIsFocusable, PRInt32 *aTabIndex)
 {
   if (nsGenericHTMLElement::IsHTMLFocusable(aIsFocusable, aTabIndex)) {
     return PR_TRUE;
   }
--- a/content/html/content/src/nsHTMLTextAreaElement.cpp
+++ b/content/html/content/src/nsHTMLTextAreaElement.cpp
@@ -36,17 +36,16 @@
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMNSHTMLTextAreaElement.h"
 #include "nsITextControlElement.h"
 #include "nsIDOMNSEditableElement.h"
 #include "nsIControllers.h"
-#include "nsIFocusController.h"
 #include "nsPIDOMWindow.h"
 #include "nsContentCID.h"
 #include "nsCOMPtr.h"
 #include "nsIComponentManager.h"
 #include "nsIDOMHTMLFormElement.h"
 #include "nsIFormControl.h"
 #include "nsIForm.h"
 #include "nsIFormSubmission.h"
@@ -302,87 +301,43 @@ nsHTMLTextAreaElement::Focus()
   }
 
   return NS_OK;
 }
 
 void
 nsHTMLTextAreaElement::SetFocus(nsPresContext* aPresContext)
 {
-  if (!aPresContext)
-    return;
-
-  // first see if we are disabled or not. If disabled then do nothing.
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
-    return;
-  }
-
-  // We can't be focus'd if we aren't in a document
-  nsIDocument* doc = GetCurrentDoc();
-  if (!doc)
-    return;
-
-  // 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.
-  nsPIDOMWindow* win = doc->GetWindow();
-  if (win) {
-    nsIFocusController *focusController = win->GetRootFocusController();
-    PRBool isActive = PR_FALSE;
-    focusController->GetActive(&isActive);
-    if (!isActive) {
-      focusController->SetFocusedWindow(win);
-      focusController->SetFocusedElement(this);
-
-      return;
-    }
-  }
-
-  SetFocusAndScrollIntoView(aPresContext);
+  DoSetFocus(aPresContext);
 }
 
 NS_IMETHODIMP
 nsHTMLTextAreaElement::Select()
 {
   nsresult rv = NS_OK;
 
-  // first see if we are disabled or not. If disabled then do nothing.
-  if (HasAttr(kNameSpaceID_None, nsGkAtoms::disabled)) {
-    return rv;
-  }
-
-  // We can't be focus'd if we aren't in a document
-  nsIDocument* doc = GetCurrentDoc();
-  if (!doc)
-    return rv;
-
-  // 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.
-  nsPIDOMWindow* win = doc->GetWindow();
-  if (win) {
-    nsIFocusController *focusController = win->GetRootFocusController();
-    PRBool isActive = PR_FALSE;
-    focusController->GetActive(&isActive);
-    if (!isActive) {
-      focusController->SetFocusedWindow(win);
-      focusController->SetFocusedElement(this);
-
-      return rv;
-    }
-  }
-
   // XXX Bug?  We have to give the input focus before contents can be
   // selected
 
+  FocusTristate state = FocusState();
+  if (state == eUnfocusable) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsPresContext> presContext = GetPresContext();
+  if (state == eInactiveWindow) {
+    SelectAll(presContext);
+    return NS_OK;
+  }
+
   // Just like SetFocus() but without the ScrollIntoView()!
-  nsCOMPtr<nsPresContext> presContext = GetPresContext();
 
   nsEventStatus status = nsEventStatus_eIgnore;
   nsGUIEvent event(PR_TRUE, NS_FORM_SELECTED, nsnull);
+  // XXXbz nsHTMLInputElement guards against this reentering; shouldn't we?
   nsEventDispatcher::Dispatch(static_cast<nsIContent*>(this), presContext,
                               &event, nsnull, &status);
 
   // If the DOM event was not canceled (e.g. by a JS event handler
   // returning false)
   if (status == nsEventStatus_eIgnore) {
     PRBool shouldFocus = ShouldFocus(this);