Bug 821307. Ensure nsTextEditorState::SetValue does nothing when the new value equals the old value, even for password inputs. r=ehsan

If nsTextEditorState::SetValue is allowed to rebuild the editor DOM even when
the new value is the same as the old value, then during PrepareEditor we can
remove the content that's the current target of the event triggering the
PrepareEditor, which prevents important code from running such as the code that
focuses the text input. Normally this isn't a problem but
nsTextEditorState::SetValue's code for getting the current value is broken
for password controls when !mEditorInitialized. So we remove that broken code
path.

We have to make sure that the password text, if any, is set on the edit-rules
during their initialization so the regular path for getting the current
value returns the right thing.

--HG--
extra : rebase_source : 81a01a957b4b1e0cf868505a1b23c9110a2f4b3a
This commit is contained in:
Robert O'Callahan 2014-04-15 00:33:47 +12:00
parent 2299300a51
commit 38c751230b
13 changed files with 95 additions and 37 deletions

View File

@ -1234,6 +1234,16 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
newEditor = mEditor; // just pretend that we have a new editor!
}
// Get the current value of the textfield from the content.
// Note that if we've created a new editor, mEditor is null at this stage,
// so we will get the real value from the content.
nsAutoString defaultValue;
if (aValue) {
defaultValue = *aValue;
} else {
GetValue(defaultValue, true);
}
if (!mEditorInitialized) {
// Now initialize the editor.
//
@ -1254,7 +1264,8 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
// already does the relevant security checks.
AutoNoJSAPI nojsapi;
rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags);
rv = newEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
defaultValue);
NS_ENSURE_SUCCESS(rv, rv);
}
@ -1335,16 +1346,6 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
newEditor->SetFlags(editorFlags);
}
// Get the current value of the textfield from the content.
// Note that if we've created a new editor, mEditor is null at this stage,
// so we will get the real value from the content.
nsAutoString defaultValue;
if (aValue) {
defaultValue = *aValue;
} else {
GetValue(defaultValue, true);
}
if (shouldInitializeEditor) {
// Hold on to the newly created editor
preDestroyer.Swap(mEditor);
@ -1780,18 +1781,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput,
#endif
nsAutoString currentValue;
if (!mEditorInitialized && IsSingleLineTextControl()) {
// Grab the current value directly from the text node to make sure that we
// deal with stale data correctly.
NS_ASSERTION(mRootNode, "We should have a root node here");
nsIContent *textContent = mRootNode->GetFirstChild();
nsCOMPtr<nsIDOMCharacterData> textNode = do_QueryInterface(textContent);
if (textNode) {
textNode->GetData(currentValue);
}
} else {
mBoundFrame->GetText(currentValue);
}
mBoundFrame->GetText(currentValue);
nsWeakFrame weakFrame(mBoundFrame);

View File

@ -393,6 +393,7 @@ skip-if = (buildapp == 'b2g' && toolkit != 'gonk') #Bug 931116, b2g desktop spec
[test_bug787134.html]
[test_bug797113.html]
[test_bug803677.html]
[test_bug821307.html]
[test_bug827126.html]
[test_bug827426.html]
[test_bug838582.html]

View File

@ -0,0 +1,41 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=821307
-->
<head>
<title>Test for Bug 821307</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/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=821307">Mozilla Bug 821307</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<input id='dummy'></input>
<input type="password" id='input' value='11111111111111111' style="width:40em; font-size:40px;"></input>
<pre id="test">
<script type="application/javascript">
SimpleTest.waitForExplicitFinish();
SimpleTest.waitForFocus(function() {
var dummy = document.getElementById('dummy');
dummy.focus();
is(document.activeElement, dummy, "Check dummy element is now focused");
var input = document.getElementById('input');
var rect = input.getBoundingClientRect();
synthesizeMouse(input, 100, rect.height/2, {});
is(document.activeElement, input, "Check input element is now focused");
SimpleTest.finish();
});
</script>
</pre>
</body>
</html>

View File

@ -448,7 +448,7 @@ nsEditingSession::SetupEditorOnWindow(nsIDOMWindow *aWindow)
NS_ENSURE_SUCCESS(rv, rv);
rv = editor->Init(domDoc, nullptr /* root content */,
nullptr, mEditorFlags);
nullptr, mEditorFlags, EmptyString());
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsISelection> selection;

View File

@ -21,7 +21,7 @@ interface nsIEditActionListener;
interface nsIInlineSpellChecker;
interface nsITransferable;
[scriptable, uuid(753b38d1-ee03-4e58-a650-1076ccccdb7f)]
[scriptable, uuid(65523eab-db1f-44aa-893e-dfe57ad306f0)]
interface nsIEditor : nsISupports
{
@ -56,7 +56,8 @@ interface nsIEditor : nsISupports
[noscript] void init(in nsIDOMDocument doc,
in nsIContent aRoot,
in nsISelectionController aSelCon,
in unsigned long aFlags);
in unsigned long aFlags,
in AString initialValue);
void setAttributeOrEquivalent(in nsIDOMElement element,
in AString sourceAttrName,

View File

@ -6,10 +6,9 @@
#ifndef nsEditRules_h__
#define nsEditRules_h__
// FB45AC36-E8F1-44ae-8FB7-466E1BE119B0
#define NS_IEDITRULES_IID \
{ 0x2cc50d11, 0x9909, 0x433f, \
{ 0xb6, 0xfb, 0x4c, 0xf2, 0x56, 0xe5, 0xe5, 0x71 } }
{ 0x3836386d, 0x806a, 0x488d, \
{ 0x8b, 0xab, 0xaf, 0x42, 0xbb, 0x4c, 0x90, 0x66 } }
#include "nsEditor.h"
@ -43,6 +42,7 @@ public:
//NOTE: Use NS_DECL_ISUPPORTS_INHERITED in any class inherited from nsIEditRules
NS_IMETHOD Init(nsPlaintextEditor *aEditor)=0;
NS_IMETHOD SetInitialValue(const nsAString& aValue) = 0;
NS_IMETHOD DetachEditor()=0;
NS_IMETHOD BeforeEdit(EditAction action,
nsIEditor::EDirection aDirection) = 0;

View File

@ -204,7 +204,9 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEditor)
NS_IMETHODIMP
nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags)
nsEditor::Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
nsISelectionController *aSelCon, uint32_t aFlags,
const nsAString& aValue)
{
NS_PRECONDITION(aDoc, "bad arg");
if (!aDoc)

View File

@ -225,10 +225,12 @@ NS_IMETHODIMP
nsHTMLEditor::Init(nsIDOMDocument *aDoc,
nsIContent *aRoot,
nsISelectionController *aSelCon,
uint32_t aFlags)
uint32_t aFlags,
const nsAString& aInitialValue)
{
NS_PRECONDITION(aDoc && !aSelCon, "bad arg");
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
MOZ_ASSERT(aInitialValue.IsEmpty(), "Non-empty initial values not supported");
nsresult result = NS_OK, rulesRes = NS_OK;
@ -238,7 +240,7 @@ nsHTMLEditor::Init(nsIDOMDocument *aDoc,
nsAutoEditInitRulesTrigger rulesTrigger(static_cast<nsPlaintextEditor*>(this), rulesRes);
// Init the plaintext editor
result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags);
result = nsPlaintextEditor::Init(aDoc, aRoot, nullptr, aFlags, aInitialValue);
if (NS_FAILED(result)) { return result; }
// Init mutation observer

View File

@ -249,7 +249,9 @@ public:
nsresult EndUpdateViewBatch();
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags);
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
nsISelectionController *aSelCon, uint32_t aFlags,
const nsAString& aValue);
NS_IMETHOD PreDestroy(bool aDestroyingFrames);
/** Internal, static version */

View File

@ -115,7 +115,8 @@ NS_INTERFACE_MAP_END_INHERITING(nsEditor)
NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
nsIContent *aRoot,
nsISelectionController *aSelCon,
uint32_t aFlags)
uint32_t aFlags,
const nsAString& aInitialValue)
{
NS_PRECONDITION(aDoc, "bad arg");
NS_ENSURE_TRUE(aDoc, NS_ERROR_NULL_POINTER);
@ -126,13 +127,12 @@ NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
mRules = nullptr;
}
if (1)
{
// block to scope nsAutoEditInitRulesTrigger
nsAutoEditInitRulesTrigger rulesTrigger(this, rulesRes);
// Init the base editor
res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags);
res = nsEditor::Init(aDoc, aRoot, aSelCon, aFlags, aInitialValue);
}
// check the "single line editor newline handling"
@ -140,6 +140,13 @@ NS_IMETHODIMP nsPlaintextEditor::Init(nsIDOMDocument *aDoc,
GetDefaultEditorPrefs(mNewlineHandling, mCaretStyle);
NS_ENSURE_SUCCESS(rulesRes, rulesRes);
// mRules may not have been initialized yet, when this is called via
// nsHTMLEditor::Init.
if (mRules) {
mRules->SetInitialValue(aInitialValue);
}
return res;
}

View File

@ -73,7 +73,9 @@ public:
bool aSuppressTransaction);
/** prepare the editor for use */
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot, nsISelectionController *aSelCon, uint32_t aFlags);
NS_IMETHOD Init(nsIDOMDocument *aDoc, nsIContent *aRoot,
nsISelectionController *aSelCon, uint32_t aFlags,
const nsAString& aValue);
NS_IMETHOD GetDocumentIsEmpty(bool *aDocumentIsEmpty);
NS_IMETHOD GetIsDocumentEditable(bool *aIsDocumentEditable);

View File

@ -135,6 +135,15 @@ nsTextEditRules::Init(nsPlaintextEditor *aEditor)
return res;
}
NS_IMETHODIMP
nsTextEditRules::SetInitialValue(const nsAString& aValue)
{
if (IsPasswordEditor()) {
mPasswordText = aValue;
}
return NS_OK;
}
NS_IMETHODIMP
nsTextEditRules::DetachEditor()
{

View File

@ -47,6 +47,7 @@ public:
// nsIEditRules methods
NS_IMETHOD Init(nsPlaintextEditor *aEditor);
NS_IMETHOD SetInitialValue(const nsAString& aValue);
NS_IMETHOD DetachEditor();
NS_IMETHOD BeforeEdit(EditAction action,
nsIEditor::EDirection aDirection);