bug 514212 - Typed letters in password fields become asterisks immediately r=neil
authorBrad Lassey <blassey@mozilla.com>
Mon, 02 Nov 2009 10:37:25 -0500
changeset 34469 23e6d66d7d65a602699a926aa09e8a3b68df5d47
parent 34468 00924c08d33d9fb32d1fdf9d7a44514169c4e1e9
child 34470 e570e3747242f7220f3fc68f5e8223af364da4b2
push id10063
push userblassey@mozilla.com
push dateMon, 02 Nov 2009 15:39:32 +0000
treeherdermozilla-central@23e6d66d7d65 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersneil
bugs514212
milestone1.9.3a1pre
bug 514212 - Typed letters in password fields become asterisks immediately r=neil
editor/libeditor/text/nsTextEditRules.cpp
editor/libeditor/text/nsTextEditRules.h
widget/public/nsILookAndFeel.h
--- a/editor/libeditor/text/nsTextEditRules.cpp
+++ b/editor/libeditor/text/nsTextEditRules.cpp
@@ -58,16 +58,18 @@
 #include "nsEditorUtils.h"
 #include "EditTxn.h"
 #include "nsIPrefBranch.h"
 #include "nsIPrefService.h"
 #include "nsUnicharUtils.h"
 #include "nsILookAndFeel.h"
 #include "nsWidgetsCID.h"
 #include "DeleteTextTxn.h"
+#include "nsNodeIterator.h"
+#include "nsIDOMNodeFilter.h"
 
 // for IBMBIDI
 #include "nsIPresShell.h"
 #include "nsFrameSelection.h"
 
 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
 
 #define CANCEL_OPERATION_IF_READONLY_OR_DISABLED \
@@ -654,18 +656,42 @@ nsTextEditRules::WillInsertText(PRInt32 
       break;
     }
 
     outString->Assign(tString);
   }
 
   if (mFlags & nsIPlaintextEditor::eEditorPasswordMask)
   {
-    res = EchoInsertionToPWBuff(start, end, outString);
-    if (NS_FAILED(res)) return res;
+    // manage the password buffer
+    mPasswordText.Insert(*outString, start);
+
+    nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID);
+    if (lookAndFeel->GetEchoPassword()) {
+      if (mPasswordText.Length() > outString->Length()) {
+        HideLastPWInput();
+      }
+      mLastStart = start;
+      mLastLength = outString->Length();
+      if (mTimer)
+      {
+        mTimer->Cancel();
+      }
+      else
+      {
+        mTimer = do_CreateInstance("@mozilla.org/timer;1", &res);
+        if (NS_FAILED(res)) return res;
+      }
+      mTimer->InitWithCallback(this, 600, nsITimer::TYPE_ONE_SHOT);
+    } 
+    else 
+    {
+      res = FillBufWithPWChars(outString, outString->Length());
+      if (NS_FAILED(res)) return res;
+    }
   }
 
   // get the (collapsed) selection location
   nsCOMPtr<nsIDOMNode> selNode;
   PRInt32 selOffset;
   res = mEditor->GetStartNodeAndOffset(aSelection, address_of(selNode), &selOffset);
   if (NS_FAILED(res)) return res;
 
@@ -1362,39 +1388,73 @@ nsTextEditRules::RemoveIMETextFromPWBuf(
     mPasswordText.Cut(mPasswordIMEIndex, mPasswordIMEText.Length());
     aStart = mPasswordIMEIndex;
   }
 
   mPasswordIMEText.Assign(*aIMEString);
   return NS_OK;
 }
 
+NS_IMETHODIMP nsTextEditRules::Notify(class nsITimer *) {
+  return HideLastPWInput();
+}
+
+nsresult nsTextEditRules::HideLastPWInput() {
+  nsCOMPtr<nsIDOMNode> selNode;
+  nsCOMPtr<nsISelection> selection;
+  PRInt32 selOffset;
+  PRUint32 start, end;
+  nsresult res = mEditor->GetSelection(getter_AddRefs(selection));
+  if (NS_FAILED(res)) return res;
+  res = mEditor->GetTextSelectionOffsets(selection, start, end);
+  if (NS_FAILED(res)) return res;
+  res = mEditor->GetStartNodeAndOffset(selection, address_of(selNode), &selOffset);
+  if (NS_FAILED(res)) return res;
+  if (!mEditor->IsTextNode(selNode)) {
+    // Get an nsINode from the nsIDOMNode
+    nsCOMPtr<nsINode> node = do_QueryInterface(selNode);
+    // if node is null, return NS_OK because there's no text to hide
+    if (!node) return NS_OK;
+    // This should be the root node, walk the tree looking for text nodes
+    nsNodeIterator iter(node, nsIDOMNodeFilter::SHOW_TEXT, nsnull, PR_TRUE);
+    while (!mEditor->IsTextNode(selNode)) {
+      if (NS_FAILED(res = iter.NextNode(getter_AddRefs(selNode))) || 
+          selNode == nsnull) {
+        return NS_SUCCEEDED(res) ? NS_ERROR_NULL_POINTER : res;
+      }
+    }
+  }
+  nsCOMPtr<nsIDOMCharacterData> nodeAsText(do_QueryInterface(selNode));
+  if (!nodeAsText) return NS_ERROR_FAILURE;
+  nsAutoString hiddenText;
+  FillBufWithPWChars(&hiddenText, mLastLength);
+  nodeAsText->ReplaceData(mLastStart, mLastLength, hiddenText);
+  selection->Collapse(selNode, start);
+  if (start != end)
+    selection->Extend(selNode, end);
+  return NS_OK;
+}
+
 nsresult
-nsTextEditRules::EchoInsertionToPWBuff(PRInt32 aStart, PRInt32 aEnd, nsAString *aOutString)
+nsTextEditRules::FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength)
 {
   if (!aOutString) {return NS_ERROR_NULL_POINTER;}
 
-  // manage the password buffer
-  mPasswordText.Insert(*aOutString, aStart);
-
   // change the output to the platform password character
   PRUnichar passwordChar = PRUnichar('*');
   nsCOMPtr<nsILookAndFeel> lookAndFeel = do_GetService(kLookAndFeelCID);
   if (lookAndFeel)
   {
     passwordChar = lookAndFeel->GetPasswordCharacter();
   }
 
-  PRInt32 length = aOutString->Length();
   PRInt32 i;
   aOutString->Truncate();
-  for (i=0; i<length; i++)
-  {
+  for (i=0; i < aLength; i++)
     aOutString->Append(passwordChar);
-  }
 
   return NS_OK;
 }
 
 
 ///////////////////////////////////////////////////////////////////////////
 // CreateMozBR: put a BR node with moz attribute at {aNode, aOffset}
 //                       
--- a/editor/libeditor/text/nsTextEditRules.h
+++ b/editor/libeditor/text/nsTextEditRules.h
@@ -39,33 +39,35 @@
 #define nsTextEditRules_h__
 
 #include "nsCOMPtr.h"
 
 #include "nsPlaintextEditor.h"
 #include "nsIDOMNode.h"
 
 #include "nsEditRules.h"
+#include "nsITimer.h"
 
 /** Object that encapsulates HTML text-specific editing rules.
   *  
   * To be a good citizen, edit rules must live by these restrictions:
   * 1. All data manipulation is through the editor.  
   *    Content nodes in the document tree must <B>not</B> be manipulated directly.
   *    Content nodes in document fragments that are not part of the document itself
   *    may be manipulated at will.  Operations on document fragments must <B>not</B>
   *    go through the editor.
   * 2. Selection must not be explicitly set by the rule method.  
   *    Any manipulation of Selection must be done by the editor.
   */
-class nsTextEditRules : public nsIEditRules
+class nsTextEditRules : public nsIEditRules, public nsITimerCallback
 {
 public:
+  NS_DECL_NSITIMERCALLBACK
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
-  NS_DECL_CYCLE_COLLECTION_CLASS(nsTextEditRules)
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextEditRules, nsIEditRules)
   
               nsTextEditRules();
   virtual     ~nsTextEditRules();
 
   // nsIEditRules methods
   NS_IMETHOD Init(nsPlaintextEditor *aEditor, PRUint32 aFlags);
   NS_IMETHOD DetachEditor();
   NS_IMETHOD BeforeEdit(PRInt32 action, nsIEditor::EDirection aDirection);
@@ -181,29 +183,31 @@ protected:
       over aMaxLength */
   nsresult TruncateInsertionIfNeeded(nsISelection             *aSelection, 
                                      const nsAString          *aInString,
                                      nsAString                *aOutString,
                                      PRInt32                   aMaxLength);
   
   /** Echo's the insertion text into the password buffer, and converts
       insertion text to '*'s */                                        
-  nsresult EchoInsertionToPWBuff(PRInt32 aStart, PRInt32 aEnd, nsAString *aOutString);
+  nsresult FillBufWithPWChars(nsAString *aOutString, PRInt32 aLength);
 
   /** Remove IME composition text from password buffer */
   nsresult RemoveIMETextFromPWBuf(PRUint32 &aStart, nsAString *aIMEString);
 
   nsresult CreateMozBR(nsIDOMNode *inParent, PRInt32 inOffset, nsCOMPtr<nsIDOMNode> *outBRNode);
 
   nsresult CheckBidiLevelForDeletion(nsISelection         *aSelection,
                                      nsIDOMNode           *aSelNode, 
                                      PRInt32               aSelOffset, 
                                      nsIEditor::EDirection aAction,
                                      PRBool               *aCancel);
 
+  nsresult HideLastPWInput();
+
   // data members
   nsPlaintextEditor   *mEditor;        // note that we do not refcount the editor
   nsString             mPasswordText;  // a buffer we use to store the real value of password editors
   nsString             mPasswordIMEText;  // a buffer we use to track the IME composition string
   PRUint32             mPasswordIMEIndex;
   nsCOMPtr<nsIDOMNode> mBogusNode;     // magic node acts as placeholder in empty doc
   nsCOMPtr<nsIDOMNode> mCachedSelectionNode;    // cached selected node
   PRInt32              mCachedSelectionOffset;  // cached selected offset
@@ -211,16 +215,19 @@ protected:
   PRUint32             mActionNesting;
   PRPackedBool         mLockRulesSniffing;
   PRPackedBool         mDidExplicitlySetInterline;
   PRPackedBool         mDeleteBidiImmediately; // in bidirectional text, delete
                                                // characters not visually 
                                                // adjacent to the caret without
                                                // moving the caret first.
   PRInt32              mTheAction;     // the top level editor action
+  nsCOMPtr<nsITimer>   mTimer;
+  PRUint32             mLastStart, mLastLength;
+
   // friends
   friend class nsAutoLockRulesSniffing;
 
 };
 
 
 
 class nsTextRulesInfo : public nsRulesInfo
--- a/widget/public/nsILookAndFeel.h
+++ b/widget/public/nsILookAndFeel.h
@@ -366,16 +366,25 @@ public:
   NS_IMETHOD GetColor(const nsColorID aID, nscolor &aColor) = 0;
   NS_IMETHOD GetMetric(const nsMetricID aID, PRInt32 & aMetric) = 0;
   NS_IMETHOD GetMetric(const nsMetricFloatID aID, float & aMetric) = 0;
   virtual PRUnichar GetPasswordCharacter()
   {
     return PRUnichar('*');
   }
 
+  virtual PRBool GetEchoPassword()
+  {
+#ifdef MOZ_GFX_OPTIMIZE_MOBILE
+    return PR_TRUE;
+#else
+    return PR_FALSE;
+#endif
+  }
+
   NS_IMETHOD LookAndFeelChanged() = 0;
 
 
 #ifdef NS_DEBUG
   typedef enum {
     eMetricSize_TextField = 0,
     eMetricSize_TextArea  = 1,
     eMetricSize_ListBox   = 2,