Bug 527935 - Fire the input event for text controls with the correct trusted-ness status; r=roc a=blocking-final+
authorEhsan Akhgari <ehsan@mozilla.com>
Tue, 07 Dec 2010 01:30:05 -0500
changeset 59460 83f76e76d7803b8f2a6ca1a72bbd1263875c4fab
parent 59459 acb2f28f8e128d20858f68cb8b850e599fda7029
child 59461 839bb8d210a9b62a1728386cdcd579fa429eb8e8
push idunknown
push userunknown
push dateunknown
reviewersroc, blocking-final
bugs527935
milestone2.0b9pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 527935 - Fire the input event for text controls with the correct trusted-ness status; r=roc a=blocking-final+
content/html/content/src/nsTextEditorState.cpp
editor/idl/nsIEditor.idl
editor/libeditor/base/nsEditor.cpp
editor/libeditor/base/nsEditor.h
editor/libeditor/base/nsEditorEventListener.cpp
editor/libeditor/text/tests/Makefile.in
editor/libeditor/text/tests/test_bug527935.html
layout/forms/nsTextControlFrame.cpp
layout/forms/nsTextControlFrame.h
--- a/content/html/content/src/nsTextEditorState.cpp
+++ b/content/html/content/src/nsTextEditorState.cpp
@@ -879,17 +879,21 @@ nsTextInputListener::EditAction()
   // no undo items; JS could change the value and we'd still need to save it)
   mFrame->SetValueChanged(PR_TRUE);
 
   if (!mSettingValue) {
     mTxtCtrlElement->OnValueChanged(PR_TRUE);
   }
 
   // Fire input event
-  mFrame->FireOnInput();
+  nsCOMPtr<nsIEditor_MOZILLA_2_0_BRANCH> editor20 = do_QueryInterface(editor);
+  NS_ASSERTION(editor20, "Something is very wrong!");
+  PRBool trusted = PR_FALSE;
+  editor20->GetLastKeypressEventTrusted(&trusted);
+  mFrame->FireOnInput(trusted);
 
   return NS_OK;
 }
 
 // END nsIEditorObserver
 
 
 nsresult
--- a/editor/idl/nsIEditor.idl
+++ b/editor/idl/nsIEditor.idl
@@ -578,8 +578,19 @@ interface nsIEditor  : nsISupports
   void debugDumpContent() ;
 
   /* Run unit tests. Noop in optimized builds */
   void debugUnitTests(out long outNumTests, out long  outNumTestsFailed);
 
   /* checks if a node is read-only or not */
   [notxpcom] boolean isModifiableNode(in nsIDOMNode aNode);
 };
+
+[uuid(54c0bc08-6c0e-4967-bb0d-ec991d78a8b3)]
+interface nsIEditor_MOZILLA_2_0_BRANCH : nsISupports
+{
+  /**
+   * Will be set to true if the last keypress event that the editor has handled
+   * has been trusted.  The value will only be valid when the edit action listeners
+   * are being called, and will throw upon access at all other times.
+   */
+  readonly attribute boolean lastKeypressEventTrusted;
+};
--- a/editor/libeditor/base/nsEditor.cpp
+++ b/editor/libeditor/base/nsEditor.cpp
@@ -157,16 +157,17 @@ nsEditor::nsEditor()
 ,  mIMEBufferLength(0)
 ,  mInIMEMode(PR_FALSE)
 ,  mIsIMEComposing(PR_FALSE)
 ,  mShouldTxnSetSelection(PR_TRUE)
 ,  mDidPreDestroy(PR_FALSE)
 ,  mDocDirtyState(-1)
 ,  mDocWeak(nsnull)
 ,  mPhonetic(nsnull)
+,  mLastKeypressEventWasTrusted(eTriUnset)
 {
   //initialize member variables here
 }
 
 nsEditor::~nsEditor()
 {
   NS_ASSERTION(!mDocWeak || mDidPreDestroy, "Why PreDestroy hasn't been called?");
 
@@ -199,16 +200,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mActionListeners)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mEditorObservers)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMARRAY(mDocStateListeners)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEventTarget)
  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mEventListener)
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEditor)
+ NS_INTERFACE_MAP_ENTRY(nsIEditor_MOZILLA_2_0_BRANCH)
  NS_INTERFACE_MAP_ENTRY(nsIPhonetic)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
  NS_INTERFACE_MAP_ENTRY(nsIEditorIMESupport)
  NS_INTERFACE_MAP_ENTRY(nsIEditor)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEditor)
 NS_INTERFACE_MAP_END
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsEditor, nsIEditor)
@@ -5302,8 +5304,33 @@ nsEditor::IsAcceptableInputEvent(nsIDOME
   NS_ENSURE_SUCCESS(rv, PR_FALSE);
   if (isTrusted) {
     return PR_TRUE;
   }
   // Otherwise, we shouldn't handle any input events when we're not an active
   // element of the DOM window.
   return IsActiveInDOMWindow();
 }
+
+NS_IMETHODIMP
+nsEditor::GetLastKeypressEventTrusted(PRBool *aWasTrusted)
+{
+  NS_ENSURE_ARG_POINTER(aWasTrusted);
+
+  if (mLastKeypressEventWasTrusted == eTriUnset) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  *aWasTrusted = (mLastKeypressEventWasTrusted == eTriTrue);
+  return NS_OK;
+}
+
+void
+nsEditor::BeginKeypressHandling(nsIDOMNSEvent* aEvent)
+{
+  NS_ASSERTION(mLastKeypressEventWasTrusted == eTriUnset, "How come our status is not clear?");
+
+  if (aEvent) {
+    PRBool isTrusted = PR_FALSE;
+    aEvent->GetIsTrusted(&isTrusted);
+    mLastKeypressEventWasTrusted = isTrusted ? eTriTrue : eTriFalse;
+  }
+}
--- a/editor/libeditor/base/nsEditor.h
+++ b/editor/libeditor/base/nsEditor.h
@@ -82,29 +82,31 @@ class EditAggregateTxn;
 class IMETextTxn;
 class AddStyleSheetTxn;
 class RemoveStyleSheetTxn;
 class nsIFile;
 class nsISelectionController;
 class nsIDOMEventTarget;
 class nsCSSStyleSheet;
 class nsKeyEvent;
+class nsIDOMNSEvent;
 
 #define kMOZEditorBogusNodeAttrAtom nsEditProperty::mozEditorBogusNode
 #define kMOZEditorBogusNodeValue NS_LITERAL_STRING("TRUE")
 
 /** implementation of an editor object.  it will be the controller/focal point 
  *  for the main editor services. i.e. the GUIManager, publishing, transaction 
  *  manager, event interfaces. the idea for the event interfaces is to have them 
  *  delegate the actual commands to the editor independent of the XPFE implementation.
  */
 class nsEditor : public nsIEditor,
                  public nsIEditorIMESupport,
                  public nsSupportsWeakReference,
-                 public nsIPhonetic
+                 public nsIPhonetic,
+                 public nsIEditor_MOZILLA_2_0_BRANCH
 {
 public:
 
   enum IterDirection
   {
     kIterForward,
     kIterBackward
   };
@@ -150,16 +152,19 @@ public:
   /* ------------ nsIEditor methods -------------- */
   NS_DECL_NSIEDITOR
   /* ------------ nsIEditorIMESupport methods -------------- */
   NS_DECL_NSIEDITORIMESUPPORT
   
   // nsIPhonetic
   NS_DECL_NSIPHONETIC
 
+  // nsIEditor_MOZILLA_2_0_BRANCH
+  NS_DECL_NSIEDITOR_MOZILLA_2_0_BRANCH
+
 public:
 
   
   NS_IMETHOD InsertTextImpl(const nsAString& aStringToInsert, 
                                nsCOMPtr<nsIDOMNode> *aInOutNode, 
                                PRInt32 *aInOutOffset,
                                nsIDOMDocument *aDoc);
   nsresult InsertTextIntoTextNodeImpl(const nsAString& aStringToInsert, 
@@ -194,16 +199,19 @@ public:
   nsresult CreateHTMLContent(const nsAString& aTag, nsIContent** aContent);
 
   // IME event handlers
   virtual nsresult BeginIMEComposition();
   virtual nsresult UpdateIMEComposition(const nsAString &aCompositionString,
                                         nsIPrivateTextRangeList *aTextRange)=0;
   nsresult EndIMEComposition();
 
+  void BeginKeypressHandling(nsIDOMNSEvent* aEvent);
+  void EndKeypressHandling() { mLastKeypressEventWasTrusted = eTriUnset; }
+
 protected:
   nsCString mContentMIMEType;       // MIME type of the doc we are editing.
 
   /** create a transaction for setting aAttribute to aValue on aElement
     */
   NS_IMETHOD CreateTxnForSetAttribute(nsIDOMElement *aElement, 
                                       const nsAString &  aAttribute, 
                                       const nsAString &  aValue,
@@ -741,16 +749,18 @@ protected:
   nsWeakPtr        mDocWeak;  // weak reference to the nsIDOMDocument
   // The form field as an event receiver
   nsCOMPtr<nsPIDOMEventTarget> mEventTarget;
 
   nsString* mPhonetic;
 
  nsCOMPtr<nsIDOMEventListener> mEventListener;
 
+  Tristate mLastKeypressEventWasTrusted;
+
   friend PRBool NSCanUnload(nsISupports* serviceMgr);
   friend class nsAutoTxnsConserveSelection;
   friend class nsAutoSelectionReset;
   friend class nsAutoRules;
   friend class nsRangeUpdater;
 };
 
 
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -73,16 +73,30 @@
 #include "nsIDOMEventTarget.h"
 #include "nsIEventStateManager.h"
 #include "nsISelectionPrivate.h"
 #include "nsIDOMDragEvent.h"
 #include "nsIFocusManager.h"
 #include "nsIDOMWindow.h"
 #include "nsContentUtils.h"
 
+class nsAutoEditorKeypressOperation {
+public:
+  nsAutoEditorKeypressOperation(nsEditor *aEditor, nsIDOMNSEvent *aEvent)
+    : mEditor(aEditor) {
+    mEditor->BeginKeypressHandling(aEvent);
+  }
+  ~nsAutoEditorKeypressOperation() {
+    mEditor->EndKeypressHandling();
+  }
+
+private:
+  nsEditor *mEditor;
+};
+
 nsEditorEventListener::nsEditorEventListener() :
   mEditor(nsnull), mCaretDrawn(PR_FALSE), mCommitText(PR_FALSE),
   mInTransaction(PR_FALSE)
 {
 }
 
 nsEditorEventListener::~nsEditorEventListener() 
 {
@@ -315,16 +329,20 @@ NS_IMETHODIMP
 nsEditorEventListener::KeyPress(nsIDOMEvent* aKeyEvent)
 {
   NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_AVAILABLE);
 
   if (!mEditor->IsAcceptableInputEvent(aKeyEvent)) {
     return NS_OK;
   }
 
+  // Transfer the event's trusted-ness to our editor
+  nsCOMPtr<nsIDOMNSEvent> NSEvent = do_QueryInterface(aKeyEvent);
+  nsAutoEditorKeypressOperation operation(mEditor, NSEvent);
+
   // DOM event handling happens in two passes, the client pass and the system
   // pass.  We do all of our processing in the system pass, to allow client
   // handlers the opportunity to cancel events and prevent typing in the editor.
   // If the client pass cancelled the event, defaultPrevented will be true
   // below.
 
   nsCOMPtr<nsIDOMNSUIEvent> UIEvent = do_QueryInterface(aKeyEvent);
   if(UIEvent) {
--- a/editor/libeditor/text/tests/Makefile.in
+++ b/editor/libeditor/text/tests/Makefile.in
@@ -41,16 +41,17 @@ srcdir		= @srcdir@
 VPATH		= @srcdir@
 relativesrcdir  = editor/libeditor/text/tests
 
 include $(DEPTH)/config/autoconf.mk
 include $(topsrcdir)/config/rules.mk
 
 _TEST_FILES = \
 		test_bug471722.html \
+		test_bug527935.html \
 		test_bug569988.html \
 		test_bug590554.html \
 		test_bug596001.html \
 		test_bug596333.html \
 		test_bug596506.html \
 		test_bug597331.html \
 		test_bug600570.html \
 		test_bug602130.html \
new file mode 100644
--- /dev/null
+++ b/editor/libeditor/text/tests/test_bug527935.html
@@ -0,0 +1,84 @@
+<!DOCTYPE HTML>
+<html>
+<!--
+https://bugzilla.mozilla.org/show_bug.cgi?id=527935
+-->
+<head>
+  <title>Test for Bug 527935</title>
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
+  <script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
+  <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
+</head>
+<body>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=527935">Mozilla Bug 527935</a>
+<p id="display"></p>
+<div id="content">
+  <iframe id="formTarget" name="formTarget"></iframe>
+  <form action="data:text/html," target="formTarget">
+    <input name="test" id="initValue"><input type="submit">
+  </form>
+</div>
+<pre id="test">
+<script type="application/javascript">
+
+function getAutocompletePopup() {
+    netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
+    var Ci = Components.interfaces;
+    chromeWin = window.QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIWebNavigation)
+                      .QueryInterface(Ci.nsIDocShellTreeItem)
+                      .rootTreeItem
+                      .QueryInterface(Ci.nsIInterfaceRequestor)
+                      .getInterface(Ci.nsIDOMWindow)
+                      .QueryInterface(Ci.nsIDOMChromeWindow);
+    autocompleteMenu = chromeWin.document.getElementById("PopupAutoComplete");
+    ok(autocompleteMenu, "Got autocomplete popup");
+
+    return autocompleteMenu;
+}
+
+/** Test for Bug 527935 **/
+SimpleTest.waitForExplicitFinish();
+SimpleTest.waitForFocus(function() {
+  var formTarget = document.getElementById("formTarget");
+  var initValue = document.getElementById("initValue");
+
+  formTarget.addEventListener("load", function() {
+    var newInput = document.createElement("input");
+    newInput.setAttribute("name", "test");
+    document.body.appendChild(newInput);
+
+    setTimeout(function() {
+      var popupShown = false;
+      var popup = getAutocompletePopup();
+      function listener() {
+        popupShown = true;
+      }
+      popup.addEventListener("popupshowing", listener, false);
+
+      var event = document.createEvent("KeyboardEvent");
+
+      event.initKeyEvent("keypress", true, true, null, false, false,
+                         false, false, 0, "f".charCodeAt(0));
+      newInput.value = "";
+      newInput.focus();
+      newInput.dispatchEvent(event);
+
+      setTimeout(function() {
+        ok(!popupShown, "Popup must not be opened");
+        popup.removeEventListener("popupshowing", listener, false);
+        SimpleTest.finish();
+      }, 1000);
+    }, 0);
+  }, false);
+
+  initValue.focus();
+  initValue.value = "foo";
+  synthesizeKey("VK_ENTER", {});
+});
+
+</script>
+</pre>
+</body>
+</html>
--- a/layout/forms/nsTextControlFrame.cpp
+++ b/layout/forms/nsTextControlFrame.cpp
@@ -1325,24 +1325,24 @@ nsTextControlFrame::GetMaxLength(PRInt32
       return PR_TRUE;
     }
   }
   return PR_FALSE;
 }
 
 // this is where we propagate a content changed event
 void
-nsTextControlFrame::FireOnInput()
+nsTextControlFrame::FireOnInput(PRBool aTrusted)
 {
   if (!mNotifyOnInput)
     return; // if notification is turned off, do nothing
   
   // Dispatch the "input" event
   nsEventStatus status = nsEventStatus_eIgnore;
-  nsUIEvent event(PR_TRUE, NS_FORM_INPUT, 0);
+  nsUIEvent event(aTrusted, NS_FORM_INPUT, 0);
 
   // Have the content handle the event, propagating it according to normal
   // DOM rules.
   nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
   shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
 }
 
 nsresult
--- a/layout/forms/nsTextControlFrame.h
+++ b/layout/forms/nsTextControlFrame.h
@@ -164,17 +164,17 @@ public:
                               nsIAtom*        aAttribute,
                               PRInt32         aModType);
 
   nsresult GetText(nsString& aText);
 
   NS_DECL_QUERYFRAME
 
 public: //for methods who access nsTextControlFrame directly
-  void FireOnInput();
+  void FireOnInput(PRBool aTrusted);
   void SetValueChanged(PRBool aValueChanged);
   /** Called when the frame is focused, to remember the value for onChange. */
   nsresult InitFocusedValue();
 
   void SetFireChangeEventState(PRBool aNewState)
   {
     mFireChangeEventState = aNewState;
   }