From c51a6481a1ec8184014080012376f82536c2cadc Mon Sep 17 00:00:00 2001 From: Ehsan Akhgari Date: Tue, 6 Apr 2010 21:08:58 -0400 Subject: [PATCH] Back out the editor lazy inititialization patches (bug 221820) to try to solve the orange in bug 557689 --- content/base/public/nsContentUtils.h | 12 - content/base/src/nsContentUtils.cpp | 21 - content/html/content/src/Makefile.in | 2 - .../html/content/src/nsHTMLInputElement.cpp | 55 +-- .../content/src/nsHTMLTextAreaElement.cpp | 4 +- content/html/content/test/test_bug518122.html | 8 +- editor/libeditor/text/nsTextEditRules.cpp | 10 - layout/forms/Makefile.in | 2 - layout/forms/nsITextControlFrame.h | 7 - layout/forms/nsTextControlFrame.cpp | 427 ++++++------------ layout/forms/nsTextControlFrame.h | 47 +- layout/forms/test/test_bug446663.html | 1 - .../content/tests/chrome/test_bug418874.xul | 12 +- 13 files changed, 153 insertions(+), 455 deletions(-) diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h index b290bf7a8680..a2b3f6a12125 100644 --- a/content/base/public/nsContentUtils.h +++ b/content/base/public/nsContentUtils.h @@ -1583,18 +1583,6 @@ public: static nsresult ReparentClonedObjectToScope(JSContext* cx, JSObject* obj, JSObject* scope); - /** - * Strip all \n, \r and nulls from the given string - * @param aString the string to remove newlines from [in/out] - */ - static void RemoveNewlines(nsString &aString); - - /** - * Convert Windows and Mac platform linebreaks to \n. - * @param aString the string to convert the newlines inside [in/out] - */ - static void PlatformToDOMLineBreaks(nsString &aString); - private: static PRBool InitializeEventTable(); diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp index 77d862b17263..4ece5dffe7d1 100644 --- a/content/base/src/nsContentUtils.cpp +++ b/content/base/src/nsContentUtils.cpp @@ -5931,24 +5931,3 @@ mozAutoRemovableBlockerRemover::~mozAutoRemovableBlockerRemover() } } } - -void nsContentUtils::RemoveNewlines(nsString &aString) -{ - // strip CR/LF and null - static const char badChars[] = {'\r', '\n', 0}; - aString.StripChars(badChars); -} - -void nsContentUtils::PlatformToDOMLineBreaks(nsString &aString) -{ - if (aString.FindChar(PRUnichar('\r')) != -1) { - // Windows linebreaks: Map CRLF to LF: - aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n").get(), - NS_LITERAL_STRING("\n").get()); - - // Mac linebreaks: Map any remaining CR to LF: - aString.ReplaceSubstring(NS_LITERAL_STRING("\r").get(), - NS_LITERAL_STRING("\n").get()); - } -} - diff --git a/content/html/content/src/Makefile.in b/content/html/content/src/Makefile.in index 6d5061444b67..337cd88ce87c 100644 --- a/content/html/content/src/Makefile.in +++ b/content/html/content/src/Makefile.in @@ -130,8 +130,6 @@ INCLUDES += \ -I$(srcdir)/../../../../layout/xul/base/src \ -I$(srcdir)/../../../../layout/generic \ -I$(srcdir)/../../../../dom/base \ - -I$(srcdir)/../../../../editor/libeditor/base \ - -I$(srcdir)/../../../../editor/libeditor/text \ -I$(srcdir) \ $(NULL) diff --git a/content/html/content/src/nsHTMLInputElement.cpp b/content/html/content/src/nsHTMLInputElement.cpp index 464ef65d5ae5..672725df150f 100644 --- a/content/html/content/src/nsHTMLInputElement.cpp +++ b/content/html/content/src/nsHTMLInputElement.cpp @@ -110,8 +110,6 @@ #include "mozAutoDocUpdate.h" #include "nsHTMLFormElement.h" -#include "nsTextEditRules.h" - // XXX align=left, hspace, vspace, border? other nav4 attrs static NS_DEFINE_CID(kXULControllersCID, NS_XULCONTROLLERS_CID); @@ -425,12 +423,6 @@ protected: */ nsresult UpdateFileList(); - /** - * Determine whether the editor needs to be initialized explicitly for - * a particular event. - */ - PRBool NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const; - nsCOMPtr mControllers; /** @@ -882,13 +874,6 @@ nsHTMLInputElement::GetValue(nsAString& aValue) } else { CopyUTF8toUTF16(mValue, aValue); } - - // If the value is not owned by the frame, then we should handle any - // exiting newline characters inside it, instead of relying on the - // editor to do it for us. - nsString value(aValue); - nsTextEditRules::HandleNewLines(value, -1); - aValue.Assign(value); } return NS_OK; @@ -1016,9 +1001,7 @@ nsHTMLInputElement::TakeTextFrameValue(const nsAString& aValue) if (mValue) { nsMemory::Free(mValue); } - nsString value(aValue); - nsContentUtils::PlatformToDOMLineBreaks(value); - mValue = ToNewUTF8String(value); + mValue = ToNewUTF8String(aValue); return NS_OK; } @@ -1139,8 +1122,9 @@ nsHTMLInputElement::SetValueInternal(const nsAString& aValue, // value yet (per OwnsValue()), it will turn around and call // TakeTextFrameValue() on us, but will update its display with the new // value if needed. - return formControlFrame->SetFormProperty( + formControlFrame->SetFormProperty( aUserInput ? nsGkAtoms::userInput : nsGkAtoms::value, aValue); + return NS_OK; } SetValueChanged(PR_TRUE); @@ -1590,32 +1574,6 @@ nsHTMLInputElement::Click() return NS_OK; } -PRBool -nsHTMLInputElement::NeedToInitializeEditorForEvent(nsEventChainPreVisitor& aVisitor) const -{ - // We only need to initialize the editor for text input controls because they - // are lazily initialized. We don't need to initialize the control for - // certain types of events, because we know that those events are safe to be - // handled without the editor being initialized. These events include: - // mousein/move/out, and DOM mutation events. - if ((mType == NS_FORM_INPUT_TEXT || - mType == NS_FORM_INPUT_PASSWORD) && - aVisitor.mEvent->eventStructType != NS_MUTATION_EVENT) { - - switch (aVisitor.mEvent->message) { - case NS_MOUSE_MOVE: - case NS_MOUSE_ENTER: - case NS_MOUSE_EXIT: - case NS_MOUSE_ENTER_SYNTH: - case NS_MOUSE_EXIT_SYNTH: - return PR_FALSE; - break; - } - return PR_TRUE; - } - return PR_FALSE; -} - nsresult nsHTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) { @@ -1642,13 +1600,6 @@ nsHTMLInputElement::PreHandleEvent(nsEventChainPreVisitor& aVisitor) } } - // Initialize the editor if needed. - if (NeedToInitializeEditorForEvent(aVisitor)) { - nsITextControlFrame* textControlFrame = do_QueryFrame(GetPrimaryFrame()); - if (textControlFrame) - textControlFrame->EnsureEditorInitialized(); - } - //FIXME Allow submission etc. also when there is no prescontext, Bug 329509. if (!aVisitor.mPresContext) { return nsGenericHTMLElement::PreHandleEvent(aVisitor); diff --git a/content/html/content/src/nsHTMLTextAreaElement.cpp b/content/html/content/src/nsHTMLTextAreaElement.cpp index d6387f1b1b14..737a0820bb4a 100644 --- a/content/html/content/src/nsHTMLTextAreaElement.cpp +++ b/content/html/content/src/nsHTMLTextAreaElement.cpp @@ -435,9 +435,7 @@ nsHTMLTextAreaElement::TakeTextFrameValue(const nsAString& aValue) if (mValue) { nsMemory::Free(mValue); } - nsString value(aValue); - nsContentUtils::PlatformToDOMLineBreaks(value); - mValue = ToNewUTF8String(value); + mValue = ToNewUTF8String(aValue); return NS_OK; } diff --git a/content/html/content/test/test_bug518122.html b/content/html/content/test/test_bug518122.html index 43828a8d9280..04b3610571f1 100644 --- a/content/html/content/test/test_bug518122.html +++ b/content/html/content/test/test_bug518122.html @@ -105,12 +105,8 @@ function runTests() { for (var i = 0; i < textareas.length; ++i) { runTestsFor(textareas[i], simple_tests, value_append_tests); } - var input = document.getElementsByTagName("input")[0]; - runTestsFor(input, simple_tests_for_input, value_append_tests_for_input); - // initialize the editor - input.focus(); - input.blur(); - runTestsFor(input, simple_tests_for_input, value_append_tests_for_input); + runTestsFor(document.getElementsByTagName("input")[0], + simple_tests_for_input, value_append_tests_for_input); SimpleTest.finish(); } diff --git a/editor/libeditor/text/nsTextEditRules.cpp b/editor/libeditor/text/nsTextEditRules.cpp index 0c619c4b22ff..1e82e34453e1 100644 --- a/editor/libeditor/text/nsTextEditRules.cpp +++ b/editor/libeditor/text/nsTextEditRules.cpp @@ -156,16 +156,6 @@ nsTextEditRules::Init(nsPlaintextEditor *aEditor, PRUint32 aFlags) nsresult res = CreateBogusNodeIfNeeded(selection); if (NS_FAILED(res)) return res; - // If the selection hasn't been set up yet, set it up collapsed to the end of - // our editable content. - PRInt32 rangeCount; - res = selection->GetRangeCount(&rangeCount); - NS_ENSURE_SUCCESS(res, res); - if (!rangeCount) { - res = mEditor->EndOfDocument(); - NS_ENSURE_SUCCESS(res, res); - } - if (mFlags & nsIPlaintextEditor::eEditorPlaintextMask) { // ensure trailing br node diff --git a/layout/forms/Makefile.in b/layout/forms/Makefile.in index 0c42187933b6..228b7395cc14 100644 --- a/layout/forms/Makefile.in +++ b/layout/forms/Makefile.in @@ -91,8 +91,6 @@ LOCAL_INCLUDES = \ -I$(srcdir)/../xul/base/src \ -I$(srcdir)/../../content/base/src \ -I$(srcdir)/../../content/html/content/src \ - -I$(srcdir)/../../editor/libeditor/base \ - -I$(srcdir)/../../editor/libeditor/text \ $(NULL) DEFINES += -D_IMPL_NS_LAYOUT diff --git a/layout/forms/nsITextControlFrame.h b/layout/forms/nsITextControlFrame.h index 751a1e3d2082..223b7d898111 100644 --- a/layout/forms/nsITextControlFrame.h +++ b/layout/forms/nsITextControlFrame.h @@ -87,13 +87,6 @@ public: virtual nsFrameSelection* GetOwnedFrameSelection() = 0; virtual nsresult GetPhonetic(nsAString& aPhonetic) = 0; - - /** - * Ensure editor is initialized with the proper flags and the default value. - * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created - * @throws various and sundry other things - */ - virtual nsresult EnsureEditorInitialized() = 0; }; #endif diff --git a/layout/forms/nsTextControlFrame.cpp b/layout/forms/nsTextControlFrame.cpp index 407accc74a0b..9e747145342e 100644 --- a/layout/forms/nsTextControlFrame.cpp +++ b/layout/forms/nsTextControlFrame.cpp @@ -118,7 +118,6 @@ #include "nsINativeKeyBindings.h" #include "nsIJSContextStack.h" #include "nsFocusManager.h" -#include "nsTextEditRules.h" #define DEFAULT_COLUMN_WIDTH 20 @@ -136,6 +135,18 @@ static const PRInt32 DEFAULT_UNDO_CAP = 1000; static nsINativeKeyBindings *sNativeInputBindings = nsnull; static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull; +static void +PlatformToDOMLineBreaks(nsString &aString) +{ + // Windows linebreaks: Map CRLF to LF: + aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n").get(), + NS_LITERAL_STRING("\n").get()); + + // Mac linebreaks: Map any remaining CR to LF: + aString.ReplaceSubstring(NS_LITERAL_STRING("\r").get(), + NS_LITERAL_STRING("\n").get()); +} + // wrap can be one of these three values. typedef enum { eHTMLTextWrap_Off = 1, // "off" @@ -939,31 +950,6 @@ NS_IMETHODIMP nsTextControlFrame::GetAccessible(nsIAccessible** aAccessible) } #endif -#ifdef DEBUG -class EditorInitializerEntryTracker { -public: - explicit EditorInitializerEntryTracker(nsTextControlFrame &frame) - : mFrame(frame) - , mFirstEntry(PR_FALSE) - { - if (!mFrame.mInEditorInitialization) { - mFrame.mInEditorInitialization = PR_TRUE; - mFirstEntry = PR_TRUE; - } - } - ~EditorInitializerEntryTracker() - { - if (mFirstEntry) { - mFrame.mInEditorInitialization = PR_FALSE; - } - } - PRBool EnteredMoreThanOnce() const { return !mFirstEntry; } -private: - nsTextControlFrame &mFrame; - PRBool mFirstEntry; -}; -#endif - nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext) : nsStackFrame(aShell, aContext) , mUseEditor(PR_FALSE) @@ -973,9 +959,6 @@ nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aCo , mFireChangeEventState(PR_FALSE) , mInSecureKeyboardInputMode(PR_FALSE) , mTextListener(nsnull) -#ifdef DEBUG - , mInEditorInitialization(PR_FALSE) -#endif { } @@ -1316,6 +1299,38 @@ nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext, return NS_OK; } +void +nsTextControlFrame::DelayedEditorInit() +{ + nsIDocument* doc = mContent->GetCurrentDoc(); + if (!doc) { + return; + } + + nsWeakFrame weakFrame(this); + + // Flush out content on our document. Have to do this, because script + // blockers don't prevent the sink flushing out content and notifying in the + // process, which can destroy frames. + doc->FlushPendingNotifications(Flush_ContentAndNotify); + if (!weakFrame.IsAlive()) { + return; + } + + // Make sure that editor init doesn't do things that would kill us off + // (especially off the script blockers it'll create for its DOM mutations). + nsAutoScriptBlocker scriptBlocker; + + // Time to mess with our security context... See comments in GetValue() + // for why this is needed. + nsCxPusher pusher; + pusher.PushNull(); + + InitEditor(); + if (IsFocusedContent(GetContent())) + SetFocus(PR_TRUE, PR_FALSE); +} + PRInt32 nsTextControlFrame::GetWrapCols() { @@ -1337,68 +1352,23 @@ nsTextControlFrame::GetWrapCols() } nsresult -nsTextControlFrame::EnsureEditorInitialized() -{ - nsWeakFrame weakFrame(this); - nsresult rv = EnsureEditorInitializedInternal(); - NS_ENSURE_STATE(weakFrame.IsAlive()); - return rv; -} - -nsresult -nsTextControlFrame::EnsureEditorInitializedInternal() +nsTextControlFrame::InitEditor() { // This method initializes our editor, if needed. - + // This code used to be called from CreateAnonymousContent(), but // when the editor set the initial string, it would trigger a // PresShell listener which called FlushPendingNotifications() // during frame construction. This was causing other form controls - // to display wrong values. Additionally, calling this every time - // a text frame control is instantiated means that we're effectively - // instantiating the editor for all text fields, even if they - // never get used. So, now this method is being called lazily only - // when we actually need an editor. + // to display wrong values. So we call this from a script runner + // now. // Check if this method has been called already. // If so, just return early. + if (mUseEditor) return NS_OK; - nsIDocument* doc = mContent->GetCurrentDoc(); - NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE); - - nsWeakFrame weakFrame(this); - - // Flush out content on our document. Have to do this, because script - // blockers don't prevent the sink flushing out content and notifying in the - // process, which can destroy frames. - doc->FlushPendingNotifications(Flush_ContentAndNotify); - NS_ENSURE_TRUE(weakFrame.IsAlive(), NS_ERROR_FAILURE); - - // Make sure that editor init doesn't do things that would kill us off - // (especially off the script blockers it'll create for its DOM mutations). - nsAutoScriptBlocker scriptBlocker; - - // Time to mess with our security context... See comments in GetValue() - // for why this is needed. - nsCxPusher pusher; - pusher.PushNull(); - - // Make sure that we try to focus the content even if the method fails - class EnsureSetFocus { - public: - explicit EnsureSetFocus(nsTextControlFrame* aFrame) - : mFrame(aFrame) {} - ~EnsureSetFocus() { - if (IsFocusedContent(mFrame->GetContent())) - mFrame->SetFocus(PR_TRUE, PR_FALSE); - } - private: - nsTextControlFrame *mFrame; - }; - EnsureSetFocus makeSureSetFocusHappens(this); - // Create an editor nsresult rv; @@ -1439,9 +1409,6 @@ nsTextControlFrame::EnsureEditorInitializedInternal() if (!domdoc) return NS_ERROR_FAILURE; - // Make sure we clear out the non-breaking space before we initialize the editor - UpdateValueDisplay(PR_FALSE, PR_TRUE); - rv = mEditor->Init(domdoc, shell, mValueDiv, mSelCon, editorFlags); NS_ENSURE_SUCCESS(rv, rv); @@ -1532,53 +1499,48 @@ nsTextControlFrame::EnsureEditorInitializedInternal() // editor. mUseEditor = PR_TRUE; -#ifdef DEBUG - // Make sure we are not being called again until we're finished. - // If reentrancy happens, just pretend that we don't have an editor. - const EditorInitializerEntryTracker tracker(*this); - NS_ASSERTION(!tracker.EnteredMoreThanOnce(), - "EnsureEditorInitialized has been called while a previous call was in progress"); -#endif - - // Set the editor's contents to our default value. We have to be - // sure to use the editor so that '*' characters get displayed for - // password fields, etc. SetValue() will call the editor for us. We - // want to call this even if defaultValue is empty, since empty text - // inputs have a single non-breaking space in the textnode under - // mAnonymousDiv, and this space needs to go away as we init the - // editor. - - // Avoid causing reentrant painting and reflowing by telling the editor - // that we don't want it to force immediate view refreshes or force - // immediate reflows during any editor calls. - - rv = mEditor->SetFlags(editorFlags | - nsIPlaintextEditor::eEditorUseAsyncUpdatesMask); - NS_ENSURE_SUCCESS(rv, rv); - - // Now call SetValue() which will make the necessary editor calls to set - // the default value. Make sure to turn off undo before setting the default - // value, and turn it back on afterwards. This will make sure we can't undo - // past the default value. - - rv = mEditor->EnableUndo(PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - - SetValue(defaultValue); - - rv = mEditor->EnableUndo(PR_TRUE); - NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed"); - - // Now restore the original editor flags. - rv = mEditor->SetFlags(editorFlags); - NS_ENSURE_SUCCESS(rv, rv); + // If we have a default value, insert it under the div we created + // above, but be sure to use the editor so that '*' characters get + // displayed for password fields, etc. SetValue() will call the + // editor for us. if (!defaultValue.IsEmpty()) { + // Avoid causing reentrant painting and reflowing by telling the editor + // that we don't want it to force immediate view refreshes or force + // immediate reflows during any editor calls. + + rv = mEditor->SetFlags(editorFlags | + nsIPlaintextEditor::eEditorUseAsyncUpdatesMask); + + if (NS_FAILED(rv)) + return rv; + + // Now call SetValue() which will make the necessary editor calls to set + // the default value. Make sure to turn off undo before setting the default + // value, and turn it back on afterwards. This will make sure we can't undo + // past the default value. + + rv = mEditor->EnableUndo(PR_FALSE); + + if (NS_FAILED(rv)) + return rv; + + SetValue(defaultValue); + + rv = mEditor->EnableUndo(PR_TRUE); + NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed"); + + // Now restore the original editor flags. + rv = mEditor->SetFlags(editorFlags); + // By default the placeholder is shown, // we should hide it if the default value is not empty. nsWeakFrame weakFrame(this); HidePlaceholder(); NS_ENSURE_STATE(weakFrame.IsAlive()); + + if (NS_FAILED(rv)) + return rv; } nsCOMPtr transMgr; @@ -1609,7 +1571,7 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray& aElements) { mState |= NS_FRAME_INDEPENDENT_SELECTION; - nsIPresShell *shell = PresContext()->GetPresShell(); + nsIPresShell* shell = PresContext()->GetPresShell(); if (!shell) return NS_ERROR_FAILURE; @@ -1657,13 +1619,6 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray& aElements) if (!aElements.AppendElement(mValueDiv)) return NS_ERROR_OUT_OF_MEMORY; - // Now create the placeholder anonymous content - rv = CreatePlaceholderDiv(aElements, doc->NodeInfoManager()); - NS_ENSURE_SUCCESS(rv, rv); - - rv = UpdateValueDisplay(PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - // Create selection mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv); @@ -1704,16 +1659,17 @@ nsTextControlFrame::CreateAnonymousContent(nsTArray& aElements) (mTextListener)); } - if (!IsSingleLineTextControl()) { - // textareas are eagerly initialized - NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), - "Someone forgot a script blocker?"); + NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), + "Someone forgot a script blocker?"); - if (!nsContentUtils::AddScriptRunner(new EditorInitializer(this))) { - return NS_ERROR_OUT_OF_MEMORY; - } + if (!nsContentUtils::AddScriptRunner(new EditorInitializer(this))) { + return NS_ERROR_OUT_OF_MEMORY; } + // Now create the placeholder anonymous content + rv = CreatePlaceholderDiv(aElements, doc->NodeInfoManager()); + NS_ENSURE_SUCCESS(rv, rv); + return NS_OK; } @@ -1978,7 +1934,7 @@ nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aV mIsProcessing = PR_FALSE; } return NS_OK; -} +} nsresult nsTextControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const @@ -1990,7 +1946,7 @@ nsTextControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const GetValue(aValue, PR_FALSE); } return NS_OK; -} +} @@ -1998,10 +1954,6 @@ NS_IMETHODIMP nsTextControlFrame::GetEditor(nsIEditor **aEditor) { NS_ENSURE_ARG_POINTER(aEditor); - - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); - *aEditor = mEditor; NS_IF_ADDREF(*aEditor); return NS_OK; @@ -2137,9 +2089,8 @@ nsTextControlFrame::SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd) NS_IMETHODIMP nsTextControlFrame::SetSelectionRange(PRInt32 aSelStart, PRInt32 aSelEnd) { - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); - + NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED); + if (aSelStart > aSelEnd) { // Simulate what we'd see SetSelectionStart() was called, followed // by a SetSelectionEnd(). @@ -2154,12 +2105,11 @@ nsTextControlFrame::SetSelectionRange(PRInt32 aSelStart, PRInt32 aSelEnd) NS_IMETHODIMP nsTextControlFrame::SetSelectionStart(PRInt32 aSelectionStart) { - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED); PRInt32 selStart = 0, selEnd = 0; - rv = GetSelectionRange(&selStart, &selEnd); + nsresult rv = GetSelectionRange(&selStart, &selEnd); NS_ENSURE_SUCCESS(rv, rv); if (aSelectionStart > selEnd) { @@ -2175,12 +2125,11 @@ nsTextControlFrame::SetSelectionStart(PRInt32 aSelectionStart) NS_IMETHODIMP nsTextControlFrame::SetSelectionEnd(PRInt32 aSelectionEnd) { - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); - + NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED); + PRInt32 selStart = 0, selEnd = 0; - rv = GetSelectionRange(&selStart, &selEnd); + nsresult rv = GetSelectionRange(&selStart, &selEnd); NS_ENSURE_SUCCESS(rv, rv); if (aSelectionEnd < selStart) { @@ -2202,9 +2151,6 @@ nsTextControlFrame::DOMPointToOffset(nsIDOMNode* aNode, *aResult = 0; - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr rootElement; mEditor->GetRootElement(getter_AddRefs(rootElement)); nsCOMPtr rootNode(do_QueryInterface(rootElement)); @@ -2213,7 +2159,7 @@ nsTextControlFrame::DOMPointToOffset(nsIDOMNode* aNode, nsCOMPtr nodeList; - rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); + nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); @@ -2282,9 +2228,6 @@ nsTextControlFrame::OffsetToDOMPoint(PRInt32 aOffset, *aResult = nsnull; *aPosition = 0; - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); - nsCOMPtr rootElement; mEditor->GetRootElement(getter_AddRefs(rootElement)); nsCOMPtr rootNode(do_QueryInterface(rootElement)); @@ -2293,7 +2236,7 @@ nsTextControlFrame::OffsetToDOMPoint(PRInt32 aOffset, nsCOMPtr nodeList; - rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); + nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE); @@ -2373,14 +2316,13 @@ NS_IMETHODIMP nsTextControlFrame::GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd) { // make sure we have an editor - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED); *aSelectionStart = 0; *aSelectionEnd = 0; nsCOMPtr selection; - rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); + nsresult rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection)); NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE); @@ -2442,15 +2384,6 @@ nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID, nsIAtom* aAttribute, PRInt32 aModType) { - // First, check for the placeholder attribute, because it doesn't - // depend on the editor being present. - if (nsGkAtoms::placeholder == aAttribute) - { - nsWeakFrame weakFrame(this); - UpdatePlaceholderText(PR_TRUE); - NS_ENSURE_STATE(weakFrame.IsAlive()); - } - if (!mEditor || !mSelCon) return nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);; @@ -2491,7 +2424,7 @@ nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID, if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) && IsFocusedContent(mContent)) mSelCon->SetCaretEnabled(PR_TRUE); - } + } mEditor->SetFlags(flags); } else if (nsGkAtoms::disabled == aAttribute) @@ -2509,11 +2442,14 @@ nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID, { // unset disabled flags &= ~(nsIPlaintextEditor::eEditorDisabledMask); mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN); - } + } mEditor->SetFlags(flags); } - else if (!mUseEditor && nsGkAtoms::value == aAttribute) { - UpdateValueDisplay(PR_TRUE); + else if (nsGkAtoms::placeholder == aAttribute) + { + nsWeakFrame weakFrame(this); + UpdatePlaceholderText(PR_TRUE); + NS_ENSURE_STATE(weakFrame.IsAlive()); } // Allow the base class to handle common attributes supported // by all form elements... @@ -2532,7 +2468,7 @@ nsTextControlFrame::GetText(nsString& aText) if (IsSingleLineTextControl()) { // If we're going to remove newlines anyway, ignore the wrap property GetValue(aText, PR_TRUE); - nsContentUtils::RemoveNewlines(aText); + RemoveNewlines(aText); } else { nsCOMPtr textArea = do_QueryInterface(mContent); if (textArea) { @@ -2547,10 +2483,8 @@ nsresult nsTextControlFrame::GetPhonetic(nsAString& aPhonetic) { aPhonetic.Truncate(0); - - nsresult rv = EnsureEditorInitialized(); - NS_ENSURE_SUCCESS(rv, rv); - + if (!mEditor) + return NS_ERROR_NOT_INITIALIZED; nsCOMPtr imeSupport = do_QueryInterface(mEditor); if (imeSupport) { nsCOMPtr phonetic = do_QueryInterface(imeSupport); @@ -2563,6 +2497,14 @@ nsTextControlFrame::GetPhonetic(nsAString& aPhonetic) ///END NSIFRAME OVERLOADS /////BEGIN PROTECTED METHODS +void nsTextControlFrame::RemoveNewlines(nsString &aString) +{ + // strip CR/LF and null + static const char badChars[] = {10, 13, 0}; + aString.StripChars(badChars); +} + + PRBool nsTextControlFrame::GetMaxLength(PRInt32* aSize) { @@ -2734,7 +2676,9 @@ nsTextControlFrame::SetValue(const nsAString& aValue) // Unfortunately aValue is declared const, so we have to copy // in order to do this substitution. nsString newValue(aValue); - nsContentUtils::PlatformToDOMLineBreaks(newValue); + if (aValue.FindChar(PRUnichar('\r')) != -1) { + ::PlatformToDOMLineBreaks(newValue); + } nsCOMPtr domDoc; editor->GetDocument(getter_AddRefs(domDoc)); @@ -2849,12 +2793,6 @@ nsTextControlFrame::SetValue(const nsAString& aValue) { textControl->TakeTextFrameValue(aValue); } - // The only time mEditor is non-null but mUseEditor is false is when the - // frame is being torn down. If that's what's going on, don't bother with - // updating the display. - if (!mEditor) { - UpdateValueDisplay(PR_TRUE, PR_FALSE, &aValue); - } } return NS_OK; } @@ -2921,115 +2859,6 @@ nsTextControlFrame::SetValueChanged(PRBool aValueChanged) } } - -nsresult -nsTextControlFrame::UpdateValueDisplay(PRBool aNotify, - PRBool aBeforeEditorInit, - const nsAString *aValue) -{ - if (!IsSingleLineTextControl()) // textareas don't use this - return NS_OK; - - NS_PRECONDITION(mValueDiv, "Must have a div content\n"); - NS_PRECONDITION(!mUseEditor, - "Do not call this after editor has been initialized"); - NS_ASSERTION(mValueDiv->GetChildCount() <= 1, - "Cannot have more than one child node"); - NS_ASSERTION(mPlaceholderDiv, "A placeholder div must exist"); - - enum { - NO_NODE, - TXT_NODE, - BR_NODE - } childNodeType = NO_NODE; - nsIContent* childNode = mValueDiv->GetChildAt(0); -#ifdef NS_DEBUG - if (aBeforeEditorInit) - NS_ASSERTION(childNode, "A child node should exist before initializing the editor"); -#endif - - if (childNode) { - if (childNode->IsNodeOfType(nsINode::eELEMENT)) - childNodeType = BR_NODE; - else if (childNode->IsNodeOfType(nsINode::eTEXT)) - childNodeType = TXT_NODE; -#ifdef NS_DEBUG - else - NS_NOTREACHED("Invalid child node found"); -#endif - } - - // Get the current value of the textfield from the content. - nsAutoString value; - if (aValue) { - value = *aValue; - } else { - GetValue(value, PR_TRUE); - } - - // Update the display of the placeholder value if needed. - { - nsWeakFrame weakFrame(this); - if (value.IsEmpty()) { - ShowPlaceholder(); - } else { - HidePlaceholder(); - } - NS_ENSURE_STATE(weakFrame.IsAlive()); - } - - if (aBeforeEditorInit && value.IsEmpty()) { - mValueDiv->RemoveChildAt(0, PR_TRUE, PR_FALSE); - return NS_OK; - } - - nsTextEditRules::HandleNewLines(value, -1); - nsresult rv; - if (value.IsEmpty()) { - if (childNodeType != BR_NODE) { - nsCOMPtr nodeInfo; - nodeInfo = mContent->NodeInfo() - ->NodeInfoManager() - ->GetNodeInfo(nsGkAtoms::br, nsnull, - kNameSpaceID_XHTML); - NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY); - - nsCOMPtr brNode; - rv = NS_NewHTMLElement(getter_AddRefs(brNode), nodeInfo, PR_FALSE); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr brElement = do_QueryInterface(brNode); - NS_ENSURE_TRUE(brElement, NS_ERROR_UNEXPECTED); - brElement->SetAttribute(kMOZEditorBogusNodeAttr, kMOZEditorBogusNodeValue); - - mValueDiv->RemoveChildAt(0, aNotify, PR_FALSE); - mValueDiv->AppendChildTo(brNode, aNotify); - } - } else { - if (IsPasswordTextControl()) - nsTextEditRules::FillBufWithPWChars(&value, value.Length()); - - // Set up a textnode with our value - nsCOMPtr textNode; - if (childNodeType != TXT_NODE) { - rv = NS_NewTextNode(getter_AddRefs(textNode), - mContent->NodeInfo()->NodeInfoManager()); - NS_ENSURE_SUCCESS(rv, rv); - - NS_ASSERTION(textNode, "Must have textcontent!\n"); - - mValueDiv->RemoveChildAt(0, aNotify, PR_FALSE); - mValueDiv->AppendChildTo(textNode, aNotify); - } else { - textNode = childNode; - } - - textNode->SetText(value, aNotify); - } - return NS_OK; -} - - /* static */ void nsTextControlFrame::ShutDown() { @@ -3112,7 +2941,7 @@ nsTextControlFrame::UpdatePlaceholderText(PRBool aNotify) nsAutoString placeholderValue; mContent->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue); - nsContentUtils::RemoveNewlines(placeholderValue); + RemoveNewlines(placeholderValue); NS_ASSERTION(mPlaceholderDiv->GetChildAt(0), "placeholder div has no child"); mPlaceholderDiv->GetChildAt(0)->SetText(placeholderValue, aNotify); diff --git a/layout/forms/nsTextControlFrame.h b/layout/forms/nsTextControlFrame.h index 7e72b7ad74d9..c6427c96035c 100644 --- a/layout/forms/nsTextControlFrame.h +++ b/layout/forms/nsTextControlFrame.h @@ -62,7 +62,6 @@ class nsIAccessible; #endif class nsTextInputSelectionImpl; class nsTextControlFrame; -class EditorInitializerEntryTracker; class nsAnonDivObserver : public nsStubMutationObserver { @@ -176,13 +175,6 @@ public: nsresult GetPhonetic(nsAString& aPhonetic); - /** - * Ensure mEditor is initialized with the proper flags and the default value. - * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created - * @throws various and sundry other things - */ - virtual nsresult EnsureEditorInitialized(); - //==== END NSITEXTCONTROLFRAME //==== OVERLOAD of nsIFrame virtual nsIAtom* GetType() const; @@ -256,7 +248,7 @@ protected: mWeakFrame.GetFrame()->PresContext()->GetPresShell(); PRBool observes = shell->ObservesNativeAnonMutationsForPrint(); shell->ObserveNativeAnonMutationsForPrint(PR_TRUE); - mFrame->EnsureEditorInitializedInternal(); + mFrame->DelayedEditorInit(); shell->ObserveNativeAnonMutationsForPrint(observes); } return NS_OK; @@ -267,6 +259,10 @@ protected: nsTextControlFrame* mFrame; }; + // Init our editor and then make sure to focus our text input + // listener if our content node has focus. + void DelayedEditorInit(); + nsresult DOMPointToOffset(nsIDOMNode* aNode, PRInt32 aNodeOffset, PRInt32 *aResult); nsresult OffsetToDOMPoint(PRInt32 aOffset, nsIDOMNode** aResult, PRInt32* aPosition); @@ -276,23 +272,23 @@ protected: * @return whether this control is scrollable */ PRBool IsScrollable() const; - /** - * Update the textnode under our anonymous div to show the new - * value. This should only be called when we have no editor yet. - * @throws NS_ERROR_UNEXPECTED if the div has no text content + * Initialize mEditor with the proper flags and the default value. + * @throws NS_ERROR_NOT_INITIALIZED if mEditor has not been created + * @throws various and sundry other things */ - nsresult UpdateValueDisplay(PRBool aNotify, - PRBool aBeforeEditorInit = PR_FALSE, - const nsAString *aValue = nsnull); - + nsresult InitEditor(); + /** + * Strip all \n, \r and nulls from the given string + * @param aString the string to remove newlines from [in/out] + */ + void RemoveNewlines(nsString &aString); /** * Get the maxlength attribute * @param aMaxLength the value of the max length attr * @returns PR_FALSE if attr not defined */ PRBool GetMaxLength(PRInt32* aMaxLength); - /** * Find out whether an attribute exists on the content or not. * @param aAtt the attribute to determine the existence of @@ -306,6 +302,9 @@ protected: * @param aPresContext the current pres context */ void PreDestroy(); + /** + * Fire the onChange event. + */ // Helper methods /** @@ -313,12 +312,10 @@ protected: * @return the number of columns to use */ PRInt32 GetCols(); - /** * Get the column index to wrap at, or -1 if we shouldn't wrap */ PRInt32 GetWrapCols(); - /** * Get the rows attribute (if textarea) or a default * @return the number of rows to use @@ -345,11 +342,6 @@ private: nsresult SetPlaceholderClass(PRBool aVisible, PRBool aNotify); nsresult UpdatePlaceholderText(PRBool aNotify); - // This method performs the actual tasks of initializing the editor. - // EnsureEditorInitialized is a wrapper of this method which wraps it with - // a weak frame check. - virtual nsresult EnsureEditorInitializedInternal(); - private: nsCOMPtr mValueDiv; nsCOMPtr mPlaceholderDiv; @@ -366,11 +358,6 @@ private: PRPackedBool mFireChangeEventState; PRPackedBool mInSecureKeyboardInputMode; -#ifdef DEBUG - PRPackedBool mInEditorInitialization; - friend class EditorInitializerEntryTracker; -#endif - nsRefPtr mSelCon; nsCOMPtr mFrameSel; nsTextInputListener* mTextListener; diff --git a/layout/forms/test/test_bug446663.html b/layout/forms/test/test_bug446663.html index 181c82645076..88e5ab93e3f9 100644 --- a/layout/forms/test/test_bug446663.html +++ b/layout/forms/test/test_bug446663.html @@ -27,7 +27,6 @@ function test_edit_cmds(id) { netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect'); var elm = document.getElementById(id); - elm.focus(); elm.select(); elm.controllers.getControllerForCommand('cmd_cut') .doCommand('cmd_cut'); diff --git a/toolkit/content/tests/chrome/test_bug418874.xul b/toolkit/content/tests/chrome/test_bug418874.xul index b9fb153b6df4..4fbd0a5dd57e 100644 --- a/toolkit/content/tests/chrome/test_bug418874.xul +++ b/toolkit/content/tests/chrome/test_bug418874.xul @@ -72,7 +72,7 @@ function doTest() { var t1 = $("t1"); var t2 = $("t2"); - setTextboxValue(t1, "1"); + t1.value = "1"; var t1Enabled = {}; var t1CanUndo = {}; t1.editor.canUndo(t1Enabled, t1CanUndo); @@ -80,7 +80,7 @@ "undo correctly enabled when placeholder was not changed through property"); t2.placeholder = "reallyempty"; - setTextboxValue(t2, "2"); + t2.value = "2"; var t2Enabled = {}; var t2CanUndo = {}; t2.editor.canUndo(t2Enabled, t2CanUndo); @@ -90,14 +90,6 @@ SimpleTest.finish(); } - function setTextboxValue(textbox, value) { - textbox.focus(); - for (var i = 0; i < value.length; ++i) { - synthesizeKey(value.charAt(i), {}); - } - textbox.blur(); - } - SimpleTest.waitForFocus(doTest); ]]>