Minor tweak
[mozilla-1.9.git] / layout / forms / nsTextControlFrame.cpp
blob049cf048c99c968d8f56e09495d3db7b61fca3be
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * Blake Ross <blakeross@telocity.com>
25 * Alternatively, the contents of this file may be used under the terms of
26 * either of the GNU General Public License Version 2 or later (the "GPL"),
27 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
28 * in which case the provisions of the GPL or the LGPL are applicable instead
29 * of those above. If you wish to allow use of your version of this file only
30 * under the terms of either the GPL or the LGPL, and not to allow others to
31 * use your version of this file under the terms of the MPL, indicate your
32 * decision by deleting the provisions above and replace them with the notice
33 * and other provisions required by the GPL or the LGPL. If you do not delete
34 * the provisions above, a recipient may use your version of this file under
35 * the terms of any one of the MPL, the GPL or the LGPL.
37 * ***** END LICENSE BLOCK ***** */
40 #include "nsCOMPtr.h"
41 #include "nsTextControlFrame.h"
42 #include "nsIDocument.h"
43 #include "nsIDOMNSHTMLTextAreaElement.h"
44 #include "nsIDOMNSHTMLInputElement.h"
45 #include "nsIFormControl.h"
46 #include "nsIServiceManager.h"
47 #include "nsFrameSelection.h"
48 #include "nsIPlaintextEditor.h"
49 #include "nsEditorCID.h"
50 #include "nsLayoutCID.h"
51 #include "nsIDocumentEncoder.h"
52 #include "nsICaret.h"
53 #include "nsISelectionListener.h"
54 #include "nsISelectionPrivate.h"
55 #include "nsIController.h"
56 #include "nsIControllers.h"
57 #include "nsIControllerContext.h"
58 #include "nsGenericHTMLElement.h"
59 #include "nsIEditorIMESupport.h"
60 #include "nsIPhonetic.h"
61 #include "nsIEditorObserver.h"
62 #include "nsIDOMHTMLTextAreaElement.h"
63 #include "nsINameSpaceManager.h"
64 #include "nsINodeInfo.h"
65 #include "nsIScrollableView.h"
66 #include "nsIScrollableFrame.h" //to turn off scroll bars
67 #include "nsFormControlFrame.h" //for registering accesskeys
68 #include "nsIDeviceContext.h" // to measure fonts
70 #include "nsIContent.h"
71 #include "nsIAtom.h"
72 #include "nsPresContext.h"
73 #include "nsGkAtoms.h"
74 #include "nsLayoutUtils.h"
75 #include "nsIComponentManager.h"
76 #include "nsIView.h"
77 #include "nsIViewManager.h"
78 #include "nsIDOMHTMLInputElement.h"
79 #include "nsIDOMElement.h"
80 #include "nsIDOMDocument.h"
81 #include "nsIPresShell.h"
82 #include "nsIComponentManager.h"
84 #include "nsBoxLayoutState.h"
85 //for keylistener for "return" check
86 #include "nsIPrivateDOMEvent.h"
87 #include "nsIDOMEventTarget.h"
88 #include "nsIDocument.h" //observe documents to send onchangenotifications
89 #include "nsIStyleSheet.h"//observe documents to send onchangenotifications
90 #include "nsIStyleRule.h"//observe documents to send onchangenotifications
91 #include "nsIDOMEventListener.h"//observe documents to send onchangenotifications
92 #include "nsGUIEvent.h"
93 #include "nsIDOMEventGroup.h"
94 #include "nsIDOM3EventTarget.h"
95 #include "nsIDOMNSEvent.h"
96 #include "nsIDOMNSUIEvent.h"
97 #include "nsIEventStateManager.h"
99 #include "nsIDOMFocusListener.h" //onchange events
100 #include "nsIDOMCharacterData.h" //for selection setting helper func
101 #include "nsIDOMNodeList.h" //for selection setting helper func
102 #include "nsIDOMRange.h" //for selection setting helper func
103 #include "nsPIDOMWindow.h" //needed for notify selection changed to update the menus ect.
104 #ifdef ACCESSIBILITY
105 #include "nsIAccessibilityService.h"
106 #endif
107 #include "nsIServiceManager.h"
108 #include "nsIDOMNode.h"
109 #include "nsITextControlElement.h"
111 #include "nsIEditorObserver.h"
112 #include "nsITransactionManager.h"
113 #include "nsIDOMText.h" //for multiline getselection
114 #include "nsNodeInfoManager.h"
115 #include "nsContentCreatorFunctions.h"
116 #include "nsIDOMKeyListener.h"
117 #include "nsIDOMEventGroup.h"
118 #include "nsIDOM3EventTarget.h"
119 #include "nsINativeKeyBindings.h"
120 #include "nsIJSContextStack.h"
122 #define DEFAULT_COLUMN_WIDTH 20
124 #include "nsContentCID.h"
125 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
127 static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
128 static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
130 static const PRInt32 DEFAULT_COLS = 20;
131 static const PRInt32 DEFAULT_ROWS = 1;
132 static const PRInt32 DEFAULT_ROWS_TEXTAREA = 2;
133 static const PRInt32 DEFAULT_UNDO_CAP = 1000;
135 static nsINativeKeyBindings *sNativeInputBindings = nsnull;
136 static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull;
138 static void
139 PlatformToDOMLineBreaks(nsString &aString)
141 // Windows linebreaks: Map CRLF to LF:
142 aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n").get(),
143 NS_LITERAL_STRING("\n").get());
145 // Mac linebreaks: Map any remaining CR to LF:
146 aString.ReplaceSubstring(NS_LITERAL_STRING("\r").get(),
147 NS_LITERAL_STRING("\n").get());
150 // wrap can be one of these three values.
151 typedef enum {
152 eHTMLTextWrap_Off = 1, // "off"
153 eHTMLTextWrap_Hard = 2, // "hard"
154 eHTMLTextWrap_Soft = 3 // the default
155 } nsHTMLTextWrap;
157 static PRBool
158 GetWrapPropertyEnum(nsIContent* aContent, nsHTMLTextWrap& aWrapProp)
160 // soft is the default; "physical" defaults to soft as well because all other
161 // browsers treat it that way and there is no real reason to maintain physical
162 // and virtual as separate entities if no one else does. Only hard and off
163 // do anything different.
164 aWrapProp = eHTMLTextWrap_Soft; // the default
166 nsAutoString wrap;
167 if (aContent->IsNodeOfType(nsINode::eHTML)) {
168 static nsIContent::AttrValuesArray strings[] =
169 {&nsGkAtoms::HARD, &nsGkAtoms::OFF, nsnull};
171 switch (aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::wrap,
172 strings, eIgnoreCase)) {
173 case 0: aWrapProp = eHTMLTextWrap_Hard; break;
174 case 1: aWrapProp = eHTMLTextWrap_Off; break;
177 return PR_TRUE;
180 return PR_FALSE;
183 class nsTextInputListener : public nsISelectionListener,
184 public nsIDOMFocusListener,
185 public nsIDOMKeyListener,
186 public nsIEditorObserver,
187 public nsSupportsWeakReference
189 public:
190 /** the default constructor
192 nsTextInputListener();
193 /** the default destructor. virtual due to the possibility of derivation.
195 virtual ~nsTextInputListener();
197 /** SetEditor gives an address to the editor that will be accessed
198 * @param aEditor the editor this listener calls for editing operations
200 void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
202 NS_DECL_ISUPPORTS
204 NS_DECL_NSISELECTIONLISTENER
206 /** nsIDOMFocusListener interfaces
207 * used to propagate focus, blur, and change notifications
208 * @see nsIDOMFocusListener
210 NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
211 NS_IMETHOD Focus(nsIDOMEvent* aEvent);
212 NS_IMETHOD Blur (nsIDOMEvent* aEvent);
213 /* END interfaces from nsIDOMFocusListener*/
215 // nsIDOMKeyListener
216 NS_IMETHOD KeyDown(nsIDOMEvent *aKeyEvent);
217 NS_IMETHOD KeyPress(nsIDOMEvent *aKeyEvent);
218 NS_IMETHOD KeyUp(nsIDOMEvent *aKeyEvent);
220 NS_DECL_NSIEDITOROBSERVER
222 protected:
224 nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate);
226 NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings();
228 protected:
230 nsTextControlFrame* mFrame; // weak reference
232 PRPackedBool mSelectionWasCollapsed;
234 * Whether we had undo items or not the last time we got EditAction()
235 * notification (when this state changes we update undo and redo menus)
237 PRPackedBool mHadUndoItems;
239 * Whether we had redo items or not the last time we got EditAction()
240 * notification (when this state changes we update undo and redo menus)
242 PRPackedBool mHadRedoItems;
247 * nsTextEditorListener implementation
250 nsTextInputListener::nsTextInputListener()
251 : mFrame(nsnull)
252 , mSelectionWasCollapsed(PR_TRUE)
253 , mHadUndoItems(PR_FALSE)
254 , mHadRedoItems(PR_FALSE)
258 nsTextInputListener::~nsTextInputListener()
262 NS_IMPL_ADDREF(nsTextInputListener)
263 NS_IMPL_RELEASE(nsTextInputListener)
265 NS_INTERFACE_MAP_BEGIN(nsTextInputListener)
266 NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
267 NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
268 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
269 NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
270 NS_INTERFACE_MAP_ENTRY(nsIDOMFocusListener)
271 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMFocusListener)
272 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMFocusListener)
273 NS_INTERFACE_MAP_END
275 // BEGIN nsIDOMSelectionListener
277 NS_IMETHODIMP
278 nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, PRInt16 aReason)
280 PRBool collapsed;
281 if (!mFrame || !aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
282 return NS_OK;
284 // Fire the select event
285 // The specs don't exactly say when we should fire the select event.
286 // IE: Whenever you add/remove a character to/from the selection. Also
287 // each time for select all. Also if you get to the end of the text
288 // field you will get new event for each keypress or a continuous
289 // stream of events if you use the mouse. IE will fire select event
290 // when the selection collapses to nothing if you are holding down
291 // the shift or mouse button.
292 // Mozilla: If we have non-empty selection we will fire a new event for each
293 // keypress (or mouseup) if the selection changed. Mozilla will also
294 // create the event each time select all is called, even if everything
295 // was previously selected, becase technically select all will first collapse
296 // and then extend. Mozilla will never create an event if the selection
297 // collapses to nothing.
298 if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON |
299 nsISelectionListener::KEYPRESS_REASON |
300 nsISelectionListener::SELECTALL_REASON)))
302 nsIContent* content = mFrame->GetContent();
303 if (content)
305 nsCOMPtr<nsIDocument> doc = content->GetDocument();
306 if (doc)
308 nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
309 if (presShell)
311 nsEventStatus status = nsEventStatus_eIgnore;
312 nsEvent event(PR_TRUE, NS_FORM_SELECTED);
314 presShell->HandleEventWithTarget(&event, mFrame, content, &status);
320 // if the collapsed state did not change, don't fire notifications
321 if (collapsed == mSelectionWasCollapsed)
322 return NS_OK;
324 mSelectionWasCollapsed = collapsed;
326 if (!mFrame) {
327 return NS_OK;
330 nsCOMPtr<nsIContent> focusedContent;
331 mFrame->PresContext()->EventStateManager()->
332 GetFocusedContent(getter_AddRefs(focusedContent));
333 if (focusedContent != mFrame->GetContent()) {
334 return NS_OK;
337 return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
340 // END nsIDOMSelectionListener
342 // BEGIN nsIFocusListener
344 NS_IMETHODIMP
345 nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
347 return NS_OK;
350 NS_IMETHODIMP
351 nsTextInputListener::Focus(nsIDOMEvent* aEvent)
353 if (!mFrame)
354 return NS_OK;
356 nsCOMPtr<nsIEditor> editor;
357 mFrame->GetEditor(getter_AddRefs(editor));
358 if (editor) {
359 editor->AddEditorObserver(this);
362 nsresult rv = mFrame->InitFocusedValue();
364 if (NS_SUCCEEDED(rv))
365 rv = mFrame->MaybeBeginSecureKeyboardInput();
367 return rv;
370 NS_IMETHODIMP
371 nsTextInputListener::Blur(nsIDOMEvent* aEvent)
373 if (!mFrame)
374 return NS_OK;
376 nsCOMPtr<nsIEditor> editor;
377 mFrame->GetEditor(getter_AddRefs(editor));
378 if (editor) {
379 editor->RemoveEditorObserver(this);
382 mFrame->MaybeEndSecureKeyboardInput();
384 return NS_OK;
387 // END nsIFocusListener
389 // BEGIN nsIDOMKeyListener
391 static void
392 DoCommandCallback(const char *aCommand, void *aData)
394 nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
395 nsIContent *content = frame->GetContent();
397 nsCOMPtr<nsIControllers> controllers;
398 nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(content);
399 if (input) {
400 input->GetControllers(getter_AddRefs(controllers));
401 } else {
402 nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea =
403 do_QueryInterface(content);
405 if (textArea) {
406 textArea->GetControllers(getter_AddRefs(controllers));
410 if (!controllers) {
411 NS_WARNING("Could not get controllers");
412 return;
415 nsCOMPtr<nsIController> controller;
416 controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
417 if (controller) {
418 controller->DoCommand(aCommand);
423 NS_IMETHODIMP
424 nsTextInputListener::KeyDown(nsIDOMEvent *aKeyEvent)
426 nsNativeKeyEvent nativeEvent;
427 nsINativeKeyBindings *bindings = GetKeyBindings();
428 if (bindings &&
429 nsContentUtils::DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent, PR_FALSE)) {
430 if (bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame)) {
431 aKeyEvent->PreventDefault();
435 return NS_OK;
438 NS_IMETHODIMP
439 nsTextInputListener::KeyPress(nsIDOMEvent *aKeyEvent)
441 nsNativeKeyEvent nativeEvent;
442 nsINativeKeyBindings *bindings = GetKeyBindings();
443 if (bindings &&
444 nsContentUtils::DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent, PR_TRUE)) {
445 if (bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame)) {
446 aKeyEvent->PreventDefault();
450 return NS_OK;
453 NS_IMETHODIMP
454 nsTextInputListener::KeyUp(nsIDOMEvent *aKeyEvent)
456 nsNativeKeyEvent nativeEvent;
457 nsINativeKeyBindings *bindings = GetKeyBindings();
458 if (bindings &&
459 nsContentUtils::DOMEventToNativeKeyEvent(aKeyEvent, &nativeEvent, PR_FALSE)) {
460 if (bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame)) {
461 aKeyEvent->PreventDefault();
465 return NS_OK;
467 // END nsIDOMKeyListener
469 // BEGIN nsIEditorObserver
471 NS_IMETHODIMP
472 nsTextInputListener::EditAction()
475 // Update the undo / redo menus
477 nsCOMPtr<nsIEditor> editor;
478 mFrame->GetEditor(getter_AddRefs(editor));
480 nsCOMPtr<nsITransactionManager> manager;
481 editor->GetTransactionManager(getter_AddRefs(manager));
482 NS_ENSURE_TRUE(manager, NS_ERROR_FAILURE);
484 // Get the number of undo / redo items
485 PRInt32 numUndoItems = 0;
486 PRInt32 numRedoItems = 0;
487 manager->GetNumberOfUndoItems(&numUndoItems);
488 manager->GetNumberOfRedoItems(&numRedoItems);
489 if (numUndoItems && !mHadUndoItems || !numUndoItems && mHadUndoItems ||
490 numRedoItems && !mHadRedoItems || !numRedoItems && mHadRedoItems) {
491 // Modify the menu if undo or redo items are different
492 UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
494 mHadUndoItems = numUndoItems != 0;
495 mHadRedoItems = numRedoItems != 0;
498 // Make sure we know we were changed (do NOT set this to false if there are
499 // no undo items; JS could change the value and we'd still need to save it)
500 mFrame->SetValueChanged(PR_TRUE);
502 // Fire input event
503 mFrame->FireOnInput();
505 return NS_OK;
508 // END nsIEditorObserver
511 nsresult
512 nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
514 NS_ENSURE_STATE(mFrame);
516 nsIContent* content = mFrame->GetContent();
517 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
519 nsCOMPtr<nsIDocument> doc = content->GetDocument();
520 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
522 nsPIDOMWindow *domWindow = doc->GetWindow();
523 NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
525 return domWindow->UpdateCommands(commandsToUpdate);
528 nsINativeKeyBindings*
529 nsTextInputListener::GetKeyBindings()
531 if (mFrame->IsTextArea()) {
532 static PRBool sNoTextAreaBindings = PR_FALSE;
534 if (!sNativeTextAreaBindings && !sNoTextAreaBindings) {
535 CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea",
536 &sNativeTextAreaBindings);
538 if (!sNativeTextAreaBindings) {
539 sNoTextAreaBindings = PR_TRUE;
543 return sNativeTextAreaBindings;
546 static PRBool sNoInputBindings = PR_FALSE;
547 if (!sNativeInputBindings && !sNoInputBindings) {
548 CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input",
549 &sNativeInputBindings);
551 if (!sNativeInputBindings) {
552 sNoInputBindings = PR_TRUE;
556 return sNativeInputBindings;
559 // END nsTextInputListener
561 class nsTextInputSelectionImpl : public nsSupportsWeakReference
562 , public nsISelectionController
564 public:
565 NS_DECL_ISUPPORTS
567 nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter);
568 ~nsTextInputSelectionImpl(){}
570 //NSISELECTIONCONTROLLER INTERFACES
571 NS_IMETHOD SetDisplaySelection(PRInt16 toggle);
572 NS_IMETHOD GetDisplaySelection(PRInt16 *_retval);
573 NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
574 NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
575 NS_IMETHOD GetSelection(PRInt16 type, nsISelection **_retval);
576 NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous);
577 NS_IMETHOD RepaintSelection(PRInt16 type);
578 NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
579 NS_IMETHOD SetCaretEnabled(PRBool enabled);
580 NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
581 NS_IMETHOD GetCaretEnabled(PRBool *_retval);
582 NS_IMETHOD GetCaretVisible(PRBool *_retval);
583 NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
584 NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
585 NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
586 NS_IMETHOD WordExtendForDelete(PRBool aForward);
587 NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
588 NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
589 NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
590 NS_IMETHOD CompleteScroll(PRBool aForward);
591 NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
592 NS_IMETHOD ScrollPage(PRBool aForward);
593 NS_IMETHOD ScrollLine(PRBool aForward);
594 NS_IMETHOD ScrollHorizontal(PRBool aLeft);
595 NS_IMETHOD SelectAll(void);
596 NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
598 private:
599 nsCOMPtr<nsFrameSelection> mFrameSelection;
600 nsCOMPtr<nsIContent> mLimiter;
601 nsWeakPtr mPresShellWeak;
604 // Implement our nsISupports methods
605 NS_IMPL_ISUPPORTS3(nsTextInputSelectionImpl,
606 nsISelectionController,
607 nsISelectionDisplay,
608 nsISupportsWeakReference)
611 // BEGIN nsTextInputSelectionImpl
613 nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter)
615 if (aSel && aShell)
617 mFrameSelection = aSel;//we are the owner now!
618 mLimiter = aLimiter;
619 mFrameSelection->Init(aShell, mLimiter);
620 mPresShellWeak = do_GetWeakReference(aShell);
624 NS_IMETHODIMP
625 nsTextInputSelectionImpl::SetDisplaySelection(PRInt16 aToggle)
627 if (!mFrameSelection)
628 return NS_ERROR_NULL_POINTER;
630 mFrameSelection->SetDisplaySelection(aToggle);
631 return NS_OK;
634 NS_IMETHODIMP
635 nsTextInputSelectionImpl::GetDisplaySelection(PRInt16 *aToggle)
637 if (!mFrameSelection)
638 return NS_ERROR_NULL_POINTER;
640 *aToggle = mFrameSelection->GetDisplaySelection();
641 return NS_OK;
644 NS_IMETHODIMP
645 nsTextInputSelectionImpl::SetSelectionFlags(PRInt16 aToggle)
647 return NS_OK;//stub this out. not used in input
650 NS_IMETHODIMP
651 nsTextInputSelectionImpl::GetSelectionFlags(PRInt16 *aOutEnable)
653 *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
654 return NS_OK;
657 NS_IMETHODIMP
658 nsTextInputSelectionImpl::GetSelection(PRInt16 type, nsISelection **_retval)
660 if (!mFrameSelection)
661 return NS_ERROR_NULL_POINTER;
663 *_retval = mFrameSelection->GetSelection(type);
665 if (!(*_retval))
666 return NS_ERROR_FAILURE;
668 NS_ADDREF(*_retval);
669 return NS_OK;
672 NS_IMETHODIMP
673 nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous)
675 if (mFrameSelection) {
676 // After ScrollSelectionIntoView(), the pending notifications might be
677 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
678 nsresult rv = mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
680 nsIScrollableView* scrollableView = mFrameSelection->GetScrollableView();
681 if (!scrollableView) {
682 return rv;
684 nsIView* view = nsnull;
685 scrollableView->GetScrolledView(view);
686 if (!view) {
687 return rv;
689 const nsRect portRect = scrollableView->View()->GetBounds();
690 const nsRect viewRect = view->GetBounds();
691 if (viewRect.XMost() < portRect.width) {
692 return scrollableView->ScrollTo(PR_MAX(viewRect.width - portRect.width, 0), -viewRect.y, 0);
695 return rv;
697 return NS_ERROR_NULL_POINTER;
700 NS_IMETHODIMP
701 nsTextInputSelectionImpl::RepaintSelection(PRInt16 type)
703 if (!mFrameSelection)
704 return NS_ERROR_FAILURE;
706 return mFrameSelection->RepaintSelection(type);
709 NS_IMETHODIMP
710 nsTextInputSelectionImpl::RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType)
712 if (!mFrameSelection)
713 return NS_ERROR_FAILURE;
715 return mFrameSelection->RepaintSelection(aSelectionType);
718 NS_IMETHODIMP
719 nsTextInputSelectionImpl::SetCaretEnabled(PRBool enabled)
721 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
723 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
724 if (!shell) return NS_ERROR_FAILURE;
726 // tell the pres shell to enable the caret, rather than settings its visibility directly.
727 // this way the presShell's idea of caret visibility is maintained.
728 nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
729 if (!selCon) return NS_ERROR_NO_INTERFACE;
730 selCon->SetCaretEnabled(enabled);
732 return NS_OK;
735 NS_IMETHODIMP
736 nsTextInputSelectionImpl::SetCaretReadOnly(PRBool aReadOnly)
738 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
739 nsresult result;
740 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
741 if (shell)
743 nsCOMPtr<nsICaret> caret;
744 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))))
746 nsISelection* domSel = mFrameSelection->
747 GetSelection(nsISelectionController::SELECTION_NORMAL);
748 if (domSel)
749 return caret->SetCaretReadOnly(aReadOnly);
752 return NS_ERROR_FAILURE;
755 NS_IMETHODIMP
756 nsTextInputSelectionImpl::GetCaretEnabled(PRBool *_retval)
758 return GetCaretVisible(_retval);
761 NS_IMETHODIMP
762 nsTextInputSelectionImpl::GetCaretVisible(PRBool *_retval)
764 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
765 nsresult result;
766 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
767 if (shell)
769 nsCOMPtr<nsICaret> caret;
770 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))))
772 nsISelection* domSel = mFrameSelection->
773 GetSelection(nsISelectionController::SELECTION_NORMAL);
774 if (domSel)
775 return caret->GetCaretVisible(_retval);
778 return NS_ERROR_FAILURE;
781 NS_IMETHODIMP
782 nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(PRBool aVisibility)
784 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
785 nsresult result;
786 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
787 if (shell)
789 nsCOMPtr<nsICaret> caret;
790 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))))
792 nsISelection* domSel = mFrameSelection->
793 GetSelection(nsISelectionController::SELECTION_NORMAL);
794 if (domSel)
795 return caret->SetVisibilityDuringSelection(aVisibility);
798 return NS_ERROR_FAILURE;
801 NS_IMETHODIMP
802 nsTextInputSelectionImpl::CharacterMove(PRBool aForward, PRBool aExtend)
804 if (mFrameSelection)
805 return mFrameSelection->CharacterMove(aForward, aExtend);
806 return NS_ERROR_NULL_POINTER;
810 NS_IMETHODIMP
811 nsTextInputSelectionImpl::WordMove(PRBool aForward, PRBool aExtend)
813 if (mFrameSelection)
814 return mFrameSelection->WordMove(aForward, aExtend);
815 return NS_ERROR_NULL_POINTER;
818 NS_IMETHODIMP
819 nsTextInputSelectionImpl::WordExtendForDelete(PRBool aForward)
821 if (mFrameSelection)
822 return mFrameSelection->WordExtendForDelete(aForward);
823 return NS_ERROR_NULL_POINTER;
826 NS_IMETHODIMP
827 nsTextInputSelectionImpl::LineMove(PRBool aForward, PRBool aExtend)
829 if (mFrameSelection)
831 nsresult result = mFrameSelection->LineMove(aForward, aExtend);
832 if (NS_FAILED(result))
833 result = CompleteMove(aForward,aExtend);
834 return result;
836 return NS_ERROR_NULL_POINTER;
840 NS_IMETHODIMP
841 nsTextInputSelectionImpl::IntraLineMove(PRBool aForward, PRBool aExtend)
843 if (mFrameSelection)
844 return mFrameSelection->IntraLineMove(aForward, aExtend);
845 return NS_ERROR_NULL_POINTER;
849 NS_IMETHODIMP
850 nsTextInputSelectionImpl::PageMove(PRBool aForward, PRBool aExtend)
852 // expected behavior for PageMove is to scroll AND move the caret
853 // and to remain relative position of the caret in view. see Bug 4302.
855 if (mPresShellWeak)
857 nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
858 if (!presShell)
859 return NS_ERROR_NULL_POINTER;
861 //get the scroll view
862 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
863 if (scrollableView)
864 mFrameSelection->CommonPageMove(aForward, aExtend, scrollableView);
866 // After ScrollSelectionIntoView(), the pending notifications might be
867 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
868 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
871 NS_IMETHODIMP
872 nsTextInputSelectionImpl::CompleteScroll(PRBool aForward)
874 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
876 if (!scrollableView)
877 return NS_ERROR_NOT_INITIALIZED;
879 return scrollableView->ScrollByWhole(!aForward); //TRUE = top, aForward TRUE=bottom
882 NS_IMETHODIMP
883 nsTextInputSelectionImpl::CompleteMove(PRBool aForward, PRBool aExtend)
885 // grab the parent / root DIV for this text widget
886 nsIContent* parentDIV = mFrameSelection->GetLimiter();
887 if (!parentDIV)
888 return NS_ERROR_UNEXPECTED;
890 // make the caret be either at the very beginning (0) or the very end
891 PRInt32 offset = 0;
892 nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
893 if (aForward)
895 offset = parentDIV->GetChildCount();
897 // Prevent the caret from being placed after the last
898 // BR node in the content tree!
900 if (offset > 0)
902 nsIContent *child = parentDIV->GetChildAt(offset - 1);
904 if (child->Tag() == nsGkAtoms::br)
906 --offset;
907 hint = nsFrameSelection::HINTRIGHT; // for Bug 106855
912 mFrameSelection->HandleClick(parentDIV, offset, offset, aExtend,
913 PR_FALSE, hint);
915 // if we got this far, attempt to scroll no matter what the above result is
916 return CompleteScroll(aForward);
919 NS_IMETHODIMP
920 nsTextInputSelectionImpl::ScrollPage(PRBool aForward)
922 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
923 if (!scrollableView)
924 return NS_ERROR_NOT_INITIALIZED;
926 return scrollableView->ScrollByPages(0, aForward ? 1 : -1);
929 NS_IMETHODIMP
930 nsTextInputSelectionImpl::ScrollLine(PRBool aForward)
932 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
933 if (!scrollableView)
934 return NS_ERROR_NOT_INITIALIZED;
936 // will we have bug #7354 because we aren't forcing an update here?
937 return scrollableView->ScrollByLines(0, aForward ? 1 : -1);
940 NS_IMETHODIMP
941 nsTextInputSelectionImpl::ScrollHorizontal(PRBool aLeft)
943 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
944 if (!scrollableView)
945 return NS_ERROR_NOT_INITIALIZED;
947 // will we have bug #7354 because we aren't forcing an update here?
948 return scrollableView->ScrollByLines(aLeft ? -1 : 1, 0);
951 NS_IMETHODIMP
952 nsTextInputSelectionImpl::SelectAll()
954 if (mFrameSelection)
955 return mFrameSelection->SelectAll();
956 return NS_ERROR_NULL_POINTER;
959 NS_IMETHODIMP
960 nsTextInputSelectionImpl::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
962 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
963 nsresult result;
964 nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak, &result);
965 if (shell)
967 return shell->CheckVisibility(node,startOffset,EndOffset, _retval);
969 return NS_ERROR_FAILURE;
973 nsIFrame*
974 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
976 return new (aPresShell) nsTextControlFrame(aPresShell, aContext);
979 NS_IMPL_ADDREF_INHERITED(nsTextControlFrame, nsBoxFrame)
980 NS_IMPL_RELEASE_INHERITED(nsTextControlFrame, nsBoxFrame)
983 NS_IMETHODIMP
984 nsTextControlFrame::QueryInterface(const nsIID& aIID, void** aInstancePtr)
986 NS_PRECONDITION(aInstancePtr, "null out param");
988 if (aIID.Equals(NS_GET_IID(nsIFormControlFrame))) {
989 *aInstancePtr = static_cast<nsIFormControlFrame*>(this);
990 return NS_OK;
992 if (aIID.Equals(NS_GET_IID(nsIAnonymousContentCreator))) {
993 *aInstancePtr = static_cast<nsIAnonymousContentCreator*>(this);
994 return NS_OK;
996 if (aIID.Equals(NS_GET_IID(nsITextControlFrame))) {
997 *aInstancePtr = static_cast<nsITextControlFrame*>(this);
998 return NS_OK;
1000 if (aIID.Equals(NS_GET_IID(nsIScrollableViewProvider)) && IsScrollable()) {
1001 *aInstancePtr = static_cast<nsIScrollableViewProvider*>(this);
1002 return NS_OK;
1004 if (aIID.Equals(NS_GET_IID(nsIPhonetic))) {
1005 *aInstancePtr = static_cast<nsIPhonetic*>(this);
1006 return NS_OK;
1009 return nsBoxFrame::QueryInterface(aIID, aInstancePtr);
1012 #ifdef ACCESSIBILITY
1013 NS_IMETHODIMP nsTextControlFrame::GetAccessible(nsIAccessible** aAccessible)
1015 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
1017 if (accService) {
1018 return accService->CreateHTMLTextFieldAccessible(static_cast<nsIFrame*>(this), aAccessible);
1021 return NS_ERROR_FAILURE;
1023 #endif
1025 nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext)
1026 : nsStackFrame(aShell, aContext)
1027 , mUseEditor(PR_FALSE)
1028 , mIsProcessing(PR_FALSE)
1029 , mNotifyOnInput(PR_TRUE)
1030 , mDidPreDestroy(PR_FALSE)
1031 , mFireChangeEventState(PR_FALSE)
1032 , mInSecureKeyboardInputMode(PR_FALSE)
1033 , mTextListener(nsnull)
1034 #ifdef DEBUG
1035 , mCreateFrameForCalled(PR_FALSE)
1036 #endif
1040 nsTextControlFrame::~nsTextControlFrame()
1042 NS_IF_RELEASE(mTextListener);
1045 static PRBool
1046 SuppressEventHandlers(nsPresContext* aPresContext)
1048 PRBool suppressHandlers = PR_FALSE;
1050 if (aPresContext)
1052 // Right now we only suppress event handlers and controller manipulation
1053 // when in a print preview or print context!
1055 // In the current implementation, we only paginate when
1056 // printing or in print preview.
1058 suppressHandlers = aPresContext->IsPaginated();
1061 return suppressHandlers;
1064 void
1065 nsTextControlFrame::PreDestroy()
1067 // notify the editor that we are going away
1068 if (mEditor)
1070 // If we were in charge of state before, relinquish it back
1071 // to the control.
1072 if (mUseEditor)
1074 // First get the frame state from the editor
1075 nsAutoString value;
1076 GetValue(value, PR_TRUE);
1078 mUseEditor = PR_FALSE;
1080 // Next store the frame state in the control
1081 // (now that mUseEditor is false values get stored
1082 // in content).
1083 SetValue(value);
1085 mEditor->PreDestroy();
1088 // Clean up the controller
1090 if (!SuppressEventHandlers(PresContext()))
1092 nsCOMPtr<nsIControllers> controllers;
1093 nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement = do_QueryInterface(mContent);
1094 if (inputElement)
1095 inputElement->GetControllers(getter_AddRefs(controllers));
1096 else
1098 nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement = do_QueryInterface(mContent);
1099 if (textAreaElement) {
1100 textAreaElement->GetControllers(getter_AddRefs(controllers));
1104 if (controllers)
1106 PRUint32 numControllers;
1107 nsresult rv = controllers->GetControllerCount(&numControllers);
1108 NS_ASSERTION((NS_SUCCEEDED(rv)), "bad result in gfx text control destructor");
1109 for (PRUint32 i = 0; i < numControllers; i ++)
1111 nsCOMPtr<nsIController> controller;
1112 rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1113 if (NS_SUCCEEDED(rv) && controller)
1115 nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
1116 if (editController)
1118 editController->SetCommandContext(nsnull);
1125 mEditor = nsnull;
1126 mSelCon = nsnull;
1127 if (mFrameSel) {
1128 mFrameSel->SetScrollableViewProvider(nsnull);
1129 mFrameSel->DisconnectFromPresShell();
1130 mFrameSel = nsnull;
1133 //unregister self from content
1134 mTextListener->SetFrame(nsnull);
1135 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), PR_FALSE);
1136 if (mTextListener)
1138 if (mContent)
1140 mContent->RemoveEventListenerByIID(static_cast<nsIDOMFocusListener *>(mTextListener), NS_GET_IID(nsIDOMFocusListener));
1143 nsCOMPtr<nsIDOMEventGroup> systemGroup;
1144 mContent->GetSystemEventGroup(getter_AddRefs(systemGroup));
1145 nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mContent);
1146 if (dom3Targ) {
1147 // cast because of ambiguous base
1148 nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
1149 (mTextListener);
1151 dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"),
1152 listener, PR_FALSE, systemGroup);
1153 dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
1154 listener, PR_FALSE, systemGroup);
1155 dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"),
1156 listener, PR_FALSE, systemGroup);
1160 mDidPreDestroy = PR_TRUE;
1163 void
1164 nsTextControlFrame::Destroy()
1166 if (mInSecureKeyboardInputMode) {
1167 MaybeEndSecureKeyboardInput();
1169 if (!mDidPreDestroy) {
1170 PreDestroy();
1172 if (mFrameSel) {
1173 mFrameSel->SetScrollableViewProvider(nsnull);
1175 nsContentUtils::DestroyAnonymousContent(&mAnonymousDiv);
1176 nsBoxFrame::Destroy();
1179 void
1180 nsTextControlFrame::RemovedAsPrimaryFrame()
1182 if (!mDidPreDestroy) {
1183 PreDestroy();
1185 else NS_ASSERTION(PR_FALSE, "RemovedAsPrimaryFrame called after PreDestroy");
1188 nsIAtom*
1189 nsTextControlFrame::GetType() const
1191 return nsGkAtoms::textInputFrame;
1194 // XXX: wouldn't it be nice to get this from the style context!
1195 PRBool nsTextControlFrame::IsSingleLineTextControl() const
1197 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mContent);
1198 if (formControl) {
1199 PRInt32 type = formControl->GetType();
1200 return (type == NS_FORM_INPUT_TEXT) || (type == NS_FORM_INPUT_PASSWORD);
1202 return PR_FALSE;
1205 PRBool nsTextControlFrame::IsTextArea() const
1207 return mContent && mContent->Tag() == nsGkAtoms::textarea;
1210 // XXX: wouldn't it be nice to get this from the style context!
1211 PRBool nsTextControlFrame::IsPlainTextControl() const
1213 // need to check HTML attribute of mContent and/or CSS.
1214 return PR_TRUE;
1217 nsresult nsTextControlFrame::MaybeBeginSecureKeyboardInput()
1219 nsresult rv = NS_OK;
1220 if (IsPasswordTextControl() && !mInSecureKeyboardInputMode) {
1221 nsIWidget* window = GetWindow();
1222 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
1223 rv = window->BeginSecureKeyboardInput();
1224 mInSecureKeyboardInputMode = NS_SUCCEEDED(rv);
1226 return rv;
1229 void nsTextControlFrame::MaybeEndSecureKeyboardInput()
1231 if (mInSecureKeyboardInputMode) {
1232 nsIWidget* window = GetWindow();
1233 if (!window)
1234 return;
1235 window->EndSecureKeyboardInput();
1236 mInSecureKeyboardInputMode = PR_FALSE;
1240 PRBool nsTextControlFrame::IsPasswordTextControl() const
1242 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mContent);
1243 return formControl && formControl->GetType() == NS_FORM_INPUT_PASSWORD;
1247 PRInt32
1248 nsTextControlFrame::GetCols()
1250 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
1251 NS_ASSERTION(content, "Content is not HTML content!");
1253 if (IsTextArea()) {
1254 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::cols);
1255 if (attr) {
1256 PRInt32 cols = attr->Type() == nsAttrValue::eInteger ?
1257 attr->GetIntegerValue() : 0;
1258 // XXX why a default of 1 char, why hide it
1259 return (cols <= 0) ? 1 : cols;
1261 } else {
1262 // Else we know (assume) it is an input with size attr
1263 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::size);
1264 if (attr && attr->Type() == nsAttrValue::eInteger) {
1265 PRInt32 cols = attr->GetIntegerValue();
1266 if (cols > 0) {
1267 return cols;
1272 return DEFAULT_COLS;
1276 PRInt32
1277 nsTextControlFrame::GetRows()
1279 if (IsTextArea()) {
1280 nsGenericHTMLElement *content =
1281 nsGenericHTMLElement::FromContent(mContent);
1282 NS_ASSERTION(content, "Content is not HTML content!");
1284 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::rows);
1285 if (attr && attr->Type() == nsAttrValue::eInteger) {
1286 PRInt32 rows = attr->GetIntegerValue();
1287 return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
1289 return DEFAULT_ROWS_TEXTAREA;
1292 return DEFAULT_ROWS;
1296 nsresult
1297 nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext,
1298 nsSize& aIntrinsicSize)
1300 // Get leading and the Average/MaxAdvance char width
1301 nscoord lineHeight = 0;
1302 nscoord charWidth = 0;
1303 nscoord charMaxAdvance = 0;
1305 nsCOMPtr<nsIFontMetrics> fontMet;
1306 nsresult rv =
1307 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
1308 NS_ENSURE_SUCCESS(rv, rv);
1309 aRenderingContext->SetFont(fontMet);
1311 lineHeight = nsHTMLReflowState::CalcLineHeight(aRenderingContext,
1312 this);
1313 fontMet->GetAveCharWidth(charWidth);
1314 fontMet->GetMaxAdvance(charMaxAdvance);
1316 // Set the width equal to the width in characters
1317 PRInt32 cols = GetCols();
1318 aIntrinsicSize.width = cols * charWidth;
1320 // To better match IE, take the maximum character width(in twips) and remove
1321 // 4 pixels add this on as additional padding(internalPadding). But only do
1322 // this if charMaxAdvance != charWidth; if they are equal, this is almost
1323 // certainly a fixed-width font.
1324 if (charWidth != charMaxAdvance) {
1325 nscoord internalPadding = PR_MAX(0, charMaxAdvance -
1326 nsPresContext::CSSPixelsToAppUnits(4));
1327 nscoord t = nsPresContext::CSSPixelsToAppUnits(1);
1328 // Round to a multiple of t
1329 nscoord rest = internalPadding % t;
1330 if (rest < t - rest) {
1331 internalPadding -= rest;
1332 } else {
1333 internalPadding += t - rest;
1335 // Now add the extra padding on (so that small input sizes work well)
1336 aIntrinsicSize.width += internalPadding;
1337 } else {
1338 // This is to account for the anonymous <br> having a 1 twip width
1339 // in Full Standards mode, see BRFrame::Reflow and bug 228752.
1340 if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
1341 aIntrinsicSize.width += 1;
1344 // Also add in the padding of our anonymous div child. Note that it hasn't
1345 // been reflowed yet, so we can't get its used padding, but it shouldn't be
1346 // using percentage padding anyway.
1347 nsMargin childPadding;
1348 if (GetFirstChild(nsnull)->GetStylePadding()->GetPadding(childPadding)) {
1349 aIntrinsicSize.width += childPadding.LeftRight();
1350 } else {
1351 NS_ERROR("Percentage padding on anonymous div?");
1355 // Increment width with cols * letter-spacing.
1357 const nsStyleCoord& lsCoord = GetStyleText()->mLetterSpacing;
1358 if (eStyleUnit_Coord == lsCoord.GetUnit()) {
1359 nscoord letterSpacing = lsCoord.GetCoordValue();
1360 if (letterSpacing != 0) {
1361 aIntrinsicSize.width += cols * letterSpacing;
1366 // Set the height equal to total number of rows (times the height of each
1367 // line, of course)
1368 aIntrinsicSize.height = lineHeight * GetRows();
1370 // Add in the size of the scrollbars for textarea
1371 if (IsTextArea()) {
1372 nsIFrame* first = GetFirstChild(nsnull);
1374 nsIScrollableFrame *scrollableFrame;
1375 CallQueryInterface(first, &scrollableFrame);
1376 NS_ASSERTION(scrollableFrame, "Child must be scrollable");
1378 nsMargin scrollbarSizes =
1379 scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext);
1381 aIntrinsicSize.width += scrollbarSizes.LeftRight();
1383 aIntrinsicSize.height += scrollbarSizes.TopBottom();;
1386 return NS_OK;
1389 void nsTextControlFrame::PostCreateFrames() {
1390 InitEditor();
1393 nsIFrame*
1394 nsTextControlFrame::CreateFrameFor(nsIContent* aContent)
1396 #ifdef DEBUG
1397 NS_ASSERTION(!mCreateFrameForCalled, "CreateFrameFor called more than once!");
1398 mCreateFrameForCalled = PR_TRUE;
1399 #endif
1401 nsPresContext *presContext = PresContext();
1402 nsIPresShell *shell = presContext->GetPresShell();
1403 if (!shell)
1404 return nsnull;
1406 nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
1407 if (!domdoc)
1408 return nsnull;
1410 // Don't create any frames here, but just setup the editor.
1411 // This way DOM Ranges (which editor uses) work properly since the anonymous
1412 // content is bound to tree after CreateAnonymousContent but before this
1413 // method.
1414 nsresult rv = NS_OK;
1415 mEditor = do_CreateInstance(kTextEditorCID, &rv);
1416 if (NS_FAILED(rv) || !mEditor)
1417 return nsnull;
1419 // Create selection
1421 mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
1422 if (NS_FAILED(rv))
1423 return nsnull;
1424 mFrameSel->SetScrollableViewProvider(this);
1426 // Create a SelectionController
1428 mSelCon = static_cast<nsISelectionController*>
1429 (new nsTextInputSelectionImpl(mFrameSel, shell, aContent));
1430 if (!mSelCon)
1431 return nsnull;
1432 mTextListener = new nsTextInputListener();
1433 if (!mTextListener)
1434 return nsnull;
1435 NS_ADDREF(mTextListener);
1437 mTextListener->SetFrame(this);
1438 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1440 // Setup the editor flags
1442 PRUint32 editorFlags = 0;
1443 if (IsPlainTextControl())
1444 editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
1445 if (IsSingleLineTextControl())
1446 editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
1447 if (IsPasswordTextControl())
1448 editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
1450 // All gfxtextcontrolframe2's are widgets
1451 editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
1453 // Use async reflow and painting for text widgets to improve
1454 // performance.
1456 // XXX: Using editor async updates exposes bugs 158782, 151882,
1457 // and 165130, so we're disabling it for now, until they
1458 // can be addressed.
1459 // editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
1461 // Now initialize the editor.
1463 // NOTE: Conversion of '\n' to <BR> happens inside the
1464 // editor's Init() call.
1466 rv = mEditor->Init(domdoc, shell, aContent, mSelCon, editorFlags);
1468 if (NS_FAILED(rv))
1469 return nsnull;
1471 // Initialize the controller for the editor
1473 if (!SuppressEventHandlers(presContext)) {
1474 nsCOMPtr<nsIControllers> controllers;
1475 nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement =
1476 do_QueryInterface(mContent);
1477 if (inputElement) {
1478 rv = inputElement->GetControllers(getter_AddRefs(controllers));
1479 } else {
1480 nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement =
1481 do_QueryInterface(mContent);
1483 if (!textAreaElement)
1484 return nsnull;
1486 rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
1489 if (NS_FAILED(rv))
1490 return nsnull;
1492 if (controllers) {
1493 PRUint32 numControllers;
1494 PRBool found = PR_FALSE;
1495 rv = controllers->GetControllerCount(&numControllers);
1496 for (PRUint32 i = 0; i < numControllers; i ++) {
1497 nsCOMPtr<nsIController> controller;
1498 rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1499 if (NS_SUCCEEDED(rv) && controller) {
1500 nsCOMPtr<nsIControllerContext> editController =
1501 do_QueryInterface(controller);
1502 if (editController) {
1503 editController->SetCommandContext(mEditor);
1504 found = PR_TRUE;
1508 if (!found)
1509 rv = NS_ERROR_FAILURE;
1513 // Initialize the plaintext editor
1514 nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(mEditor));
1515 if (textEditor) {
1516 // Set up wrapping
1517 if (IsTextArea()) {
1518 // wrap=off means -1 for wrap width no matter what cols is
1519 nsHTMLTextWrap wrapProp;
1520 ::GetWrapPropertyEnum(mContent, wrapProp);
1521 if (wrapProp == eHTMLTextWrap_Off) {
1522 // do not wrap when wrap=off
1523 textEditor->SetWrapWidth(-1);
1524 } else {
1525 // Set wrapping normally otherwise
1526 textEditor->SetWrapWidth(GetCols());
1528 } else {
1529 // Never wrap non-textareas
1530 textEditor->SetWrapColumn(-1);
1534 // Set max text field length
1535 PRInt32 maxLength;
1536 if (GetMaxLength(&maxLength)) {
1537 textEditor->SetMaxTextLength(maxLength);
1541 // Get the caret and make it a selection listener.
1543 nsCOMPtr<nsISelection> domSelection;
1544 if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1545 getter_AddRefs(domSelection))) &&
1546 domSelection) {
1547 nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
1548 nsCOMPtr<nsICaret> caret;
1549 nsCOMPtr<nsISelectionListener> listener;
1550 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))) && caret) {
1551 listener = do_QueryInterface(caret);
1552 if (listener) {
1553 selPriv->AddSelectionListener(listener);
1557 selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
1558 (mTextListener));
1561 if (mContent) {
1562 rv = mEditor->GetFlags(&editorFlags);
1564 if (NS_FAILED(rv))
1565 return nsnull;
1567 // Check if the readonly attribute is set.
1569 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
1570 editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
1572 // Check if the disabled attribute is set.
1574 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled))
1575 editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
1577 // Disable the selection if necessary.
1579 if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
1580 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
1582 mEditor->SetFlags(editorFlags);
1584 return nsnull;
1587 nsresult
1588 nsTextControlFrame::InitEditor()
1590 // This method must be called during/after the text
1591 // control frame's initial reflow to avoid any unintened
1592 // forced reflows that might result when the editor
1593 // calls into DOM/layout code while trying to set the
1594 // initial string.
1596 // This code used to be called from CreateAnonymousContent(),
1597 // but when the editor set the initial string, it would trigger
1598 // a PresShell listener which called FlushPendingNotifications()
1599 // during frame construction. This was causing other form controls
1600 // to display wrong values.
1602 // Check if this method has been called already.
1603 // If so, just return early.
1605 if (mUseEditor)
1606 return NS_OK;
1608 // If the editor is not here, then we can't use it, now can we?
1609 if (!mEditor)
1610 return NS_ERROR_NOT_INITIALIZED;
1612 // Get the current value of the textfield from the content.
1613 nsAutoString defaultValue;
1614 GetValue(defaultValue, PR_TRUE);
1616 // Turn on mUseEditor so that subsequent calls will use the
1617 // editor.
1618 mUseEditor = PR_TRUE;
1620 // If we have a default value, insert it under the div we created
1621 // above, but be sure to use the editor so that '*' characters get
1622 // displayed for password fields, etc. SetValue() will call the
1623 // editor for us.
1625 if (!defaultValue.IsEmpty()) {
1626 PRUint32 editorFlags = 0;
1628 nsresult rv = mEditor->GetFlags(&editorFlags);
1630 if (NS_FAILED(rv))
1631 return rv;
1633 // Avoid causing reentrant painting and reflowing by telling the editor
1634 // that we don't want it to force immediate view refreshes or force
1635 // immediate reflows during any editor calls.
1637 rv = mEditor->SetFlags(editorFlags |
1638 nsIPlaintextEditor::eEditorUseAsyncUpdatesMask);
1640 if (NS_FAILED(rv))
1641 return rv;
1643 // Now call SetValue() which will make the necessary editor calls to set
1644 // the default value. Make sure to turn off undo before setting the default
1645 // value, and turn it back on afterwards. This will make sure we can't undo
1646 // past the default value.
1648 rv = mEditor->EnableUndo(PR_FALSE);
1650 if (NS_FAILED(rv))
1651 return rv;
1653 SetValue(defaultValue);
1655 rv = mEditor->EnableUndo(PR_TRUE);
1656 NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
1657 // Now restore the original editor flags.
1659 rv = mEditor->SetFlags(editorFlags);
1661 if (NS_FAILED(rv))
1662 return rv;
1665 nsCOMPtr<nsITransactionManager> transMgr;
1666 mEditor->GetTransactionManager(getter_AddRefs(transMgr));
1667 NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
1669 transMgr->SetMaxTransactionCount(DEFAULT_UNDO_CAP);
1671 if (IsPasswordTextControl()) {
1672 // Disable undo for password textfields. Note that we want to do this at
1673 // the very end of InitEditor, so the calls to EnableUndo when setting the
1674 // default value don't screw us up.
1675 // Since changing the control type does a reframe, we don't have to worry
1676 // about dynamic type changes here.
1677 mEditor->EnableUndo(PR_FALSE);
1680 return NS_OK;
1683 nsresult
1684 nsTextControlFrame::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
1686 mState |= NS_FRAME_INDEPENDENT_SELECTION;
1688 nsIPresShell* shell = PresContext()->GetPresShell();
1689 if (!shell)
1690 return NS_ERROR_FAILURE;
1692 nsIDocument *doc = shell->GetDocument();
1693 if (!doc)
1694 return NS_ERROR_FAILURE;
1696 // Now create a DIV and add it to the anonymous content child list.
1697 nsCOMPtr<nsINodeInfo> nodeInfo;
1698 nsresult rv = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nsnull,
1699 kNameSpaceID_XHTML,
1700 getter_AddRefs(nodeInfo));
1701 NS_ENSURE_SUCCESS(rv, rv);
1703 rv = NS_NewHTMLElement(getter_AddRefs(mAnonymousDiv), nodeInfo, PR_FALSE);
1704 NS_ENSURE_SUCCESS(rv, rv);
1706 // Set the div native anonymous, so CSS will be its style language
1707 // no matter what.
1708 mAnonymousDiv->SetNativeAnonymous();
1710 // Set the necessary style attributes on the text control.
1712 rv = mAnonymousDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
1713 NS_LITERAL_STRING("anonymous-div"), PR_FALSE);
1714 NS_ENSURE_SUCCESS(rv, rv);
1716 if (!IsSingleLineTextControl()) {
1717 // We can't just inherit the overflow because setting visible overflow will
1718 // crash when the number of lines exceeds the height of the textarea and
1719 // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
1720 // doesn't paint the caret for some reason.
1721 const nsStyleDisplay* disp = GetStyleDisplay();
1722 if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
1723 disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
1724 rv = mAnonymousDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
1725 NS_LITERAL_STRING("overflow: inherit;"),
1726 PR_FALSE);
1727 NS_ENSURE_SUCCESS(rv, rv);
1731 if (!aElements.AppendElement(mAnonymousDiv))
1732 return NS_ERROR_OUT_OF_MEMORY;
1734 // rv = divContent->SetAttr(kNameSpaceID_None,nsGkAtoms::debug, NS_LITERAL_STRING("true"), PR_FALSE);
1735 return NS_OK;
1738 nscoord
1739 nsTextControlFrame::GetMinWidth(nsIRenderingContext* aRenderingContext)
1741 // Our min width is just our preferred width if we have auto width.
1742 nscoord result;
1743 DISPLAY_MIN_WIDTH(this, result);
1745 result = GetPrefWidth(aRenderingContext);
1747 return result;
1750 nsSize
1751 nsTextControlFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
1752 nsSize aCBSize, nscoord aAvailableWidth,
1753 nsSize aMargin, nsSize aBorder,
1754 nsSize aPadding, PRBool aShrinkWrap)
1756 nsSize autoSize;
1757 nsresult rv = CalcIntrinsicSize(aRenderingContext, autoSize);
1758 if (NS_FAILED(rv)) {
1759 // What now?
1760 autoSize.SizeTo(0, 0);
1762 #ifdef DEBUG
1763 // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width
1764 else if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
1765 nsSize ancestorAutoSize =
1766 nsStackFrame::ComputeAutoSize(aRenderingContext,
1767 aCBSize, aAvailableWidth,
1768 aMargin, aBorder,
1769 aPadding, aShrinkWrap);
1770 NS_ASSERTION(ancestorAutoSize.width == autoSize.width,
1771 "Incorrect size computed by ComputeAutoSize?");
1773 #endif
1775 return autoSize;
1779 // We inherit our GetPrefWidth from nsBoxFrame
1781 NS_IMETHODIMP
1782 nsTextControlFrame::Reflow(nsPresContext* aPresContext,
1783 nsHTMLReflowMetrics& aDesiredSize,
1784 const nsHTMLReflowState& aReflowState,
1785 nsReflowStatus& aStatus)
1787 DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
1788 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1790 // make sure the the form registers itself on the initial/first reflow
1791 if (mState & NS_FRAME_FIRST_REFLOW) {
1792 nsFormControlFrame::RegUnRegAccessKey(this, PR_TRUE);
1795 return nsStackFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
1796 aStatus);
1799 nsSize
1800 nsTextControlFrame::GetPrefSize(nsBoxLayoutState& aState)
1802 if (!DoesNeedRecalc(mPrefSize))
1803 return mPrefSize;
1805 #ifdef DEBUG_LAYOUT
1806 PropagateDebug(aState);
1807 #endif
1809 nsSize pref(0,0);
1811 nsresult rv = CalcIntrinsicSize(aState.GetRenderingContext(), pref);
1812 NS_ENSURE_SUCCESS(rv, pref);
1813 AddBorderAndPadding(pref);
1815 mPrefSize = pref;
1817 #ifdef DEBUG_rods
1819 nsMargin borderPadding(0,0,0,0);
1820 GetBorderAndPadding(borderPadding);
1821 nsSize size(169, 24);
1822 nsSize actual(pref.width/15,
1823 pref.height/15);
1824 printf("nsGfxText(field) %d,%d %d,%d %d,%d\n",
1825 size.width, size.height, actual.width, actual.height, actual.width-size.width, actual.height-size.height); // text field
1827 #endif
1829 return pref;
1832 nsSize
1833 nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState)
1835 // XXXbz why? Why not the nsBoxFrame sizes?
1836 return nsBox::GetMinSize(aState);
1839 nsSize
1840 nsTextControlFrame::GetMaxSize(nsBoxLayoutState& aState)
1842 // XXXbz why? Why not the nsBoxFrame sizes?
1843 return nsBox::GetMaxSize(aState);
1846 nscoord
1847 nsTextControlFrame::GetBoxAscent(nsBoxLayoutState& aState)
1849 // First calculate the ascent of the text inside
1850 nscoord ascent = nsStackFrame::GetBoxAscent(aState);
1852 // Now adjust the ascent for our borders and padding
1853 nsMargin borderPadding;
1854 GetBorderAndPadding(borderPadding);
1855 ascent += borderPadding.top;
1857 return ascent;
1860 PRBool
1861 nsTextControlFrame::IsCollapsed(nsBoxLayoutState& aBoxLayoutState)
1863 // We're never collapsed in the box sense.
1864 return PR_FALSE;
1867 PRBool
1868 nsTextControlFrame::IsLeaf() const
1870 return PR_TRUE;
1873 static PRBool
1874 IsFocusedContent(nsPresContext* aPresContext, nsIContent* aContent)
1876 nsCOMPtr<nsIContent> focusedContent;
1877 aPresContext->EventStateManager()->
1878 GetFocusedContent(getter_AddRefs(focusedContent));
1879 return focusedContent == aContent;
1882 //IMPLEMENTING NS_IFORMCONTROLFRAME
1883 void nsTextControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
1885 if (!aOn || !mSelCon)
1886 return;
1888 // onfocus="some_where_else.focus()" can trigger several focus
1889 // in succession. Here, we only care if we are the winner.
1890 // @see also nsTextEditorFocusListener::Focus()
1891 if (!IsFocusedContent(PresContext(), mContent))
1892 return;
1894 // tell the caret to use our selection
1896 nsCOMPtr<nsISelection> ourSel;
1897 mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1898 getter_AddRefs(ourSel));
1899 if (!ourSel) return;
1901 nsIPresShell* presShell = PresContext()->GetPresShell();
1902 nsCOMPtr<nsICaret> caret;
1903 presShell->GetCaret(getter_AddRefs(caret));
1904 if (!caret) return;
1905 caret->SetCaretDOMSelection(ourSel);
1907 // mutual-exclusion: the selection is either controlled by the
1908 // document or by the text input/area. Clear any selection in the
1909 // document since the focus is now on our independent selection.
1911 nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
1912 nsCOMPtr<nsISelection> docSel;
1913 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1914 getter_AddRefs(docSel));
1915 if (!docSel) return;
1917 PRBool isCollapsed = PR_FALSE;
1918 docSel->GetIsCollapsed(&isCollapsed);
1919 if (!isCollapsed)
1920 docSel->RemoveAllRanges();
1923 nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
1925 if (!mIsProcessing)//some kind of lock.
1927 mIsProcessing = PR_TRUE;
1928 PRBool isUserInput = (nsGkAtoms::userInput == aName);
1929 if (nsGkAtoms::value == aName || isUserInput)
1931 PRBool fireChangeEvent = GetFireChangeEventState();
1932 if (isUserInput) {
1933 SetFireChangeEventState(PR_TRUE);
1935 if (mEditor && mUseEditor) {
1936 // If the editor exists, the control needs to be informed that the value
1937 // has changed.
1938 SetValueChanged(PR_TRUE);
1940 nsresult rv = SetValue(aValue); // set new text value
1941 if (isUserInput) {
1942 SetFireChangeEventState(fireChangeEvent);
1944 NS_ENSURE_SUCCESS(rv, rv);
1946 else if (nsGkAtoms::select == aName)
1948 // Select all the text.
1950 // XXX: This is lame, we can't call mEditor->SelectAll()
1951 // because that triggers AutoCopies in unix builds.
1952 // Instead, we have to call our own homegrown version
1953 // of select all which merely builds a range that selects
1954 // all of the content and adds that to the selection.
1956 SelectAllContents();
1958 mIsProcessing = PR_FALSE;
1960 return NS_OK;
1963 nsresult
1964 nsTextControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
1966 // Return the value of the property from the widget it is not null.
1967 // If widget is null, assume the widget is GFX-rendered and return a member variable instead.
1969 if (nsGkAtoms::value == aName) {
1970 GetValue(aValue, PR_FALSE);
1972 return NS_OK;
1977 NS_IMETHODIMP
1978 nsTextControlFrame::GetEditor(nsIEditor **aEditor)
1980 NS_ENSURE_ARG_POINTER(aEditor);
1981 *aEditor = mEditor;
1982 NS_IF_ADDREF(*aEditor);
1983 return NS_OK;
1986 NS_IMETHODIMP
1987 nsTextControlFrame::OwnsValue(PRBool* aOwnsValue)
1989 NS_PRECONDITION(aOwnsValue, "aOwnsValue must be non-null");
1990 *aOwnsValue = mUseEditor;
1991 return NS_OK;
1994 NS_IMETHODIMP
1995 nsTextControlFrame::GetTextLength(PRInt32* aTextLength)
1997 NS_ENSURE_ARG_POINTER(aTextLength);
1999 nsAutoString textContents;
2000 GetValue(textContents, PR_FALSE); // this is expensive!
2001 *aTextLength = textContents.Length();
2002 return NS_OK;
2005 nsresult
2006 nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
2007 PRInt32 aStartOffset,
2008 nsIDOMNode *aEndNode,
2009 PRInt32 aEndOffset)
2011 // Create a new range to represent the new selection.
2012 // Note that we use a new range to avoid having to do
2013 // isIncreasing checks to avoid possible errors.
2015 nsCOMPtr<nsIDOMRange> range = do_CreateInstance(kRangeCID);
2016 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
2018 nsresult rv = range->SetStart(aStartNode, aStartOffset);
2019 NS_ENSURE_SUCCESS(rv, rv);
2021 rv = range->SetEnd(aEndNode, aEndOffset);
2022 NS_ENSURE_SUCCESS(rv, rv);
2024 // Get the selection, clear it and add the new range to it!
2026 nsCOMPtr<nsISelection> selection;
2027 mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2028 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2030 rv = selection->RemoveAllRanges();
2032 NS_ENSURE_SUCCESS(rv, rv);
2034 return selection->AddRange(range);
2037 nsresult
2038 nsTextControlFrame::SelectAllContents()
2040 if (!mEditor)
2041 return NS_OK;
2043 nsCOMPtr<nsIDOMElement> rootElement;
2044 nsresult rv = mEditor->GetRootElement(getter_AddRefs(rootElement));
2045 NS_ENSURE_SUCCESS(rv, rv);
2047 nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement);
2048 PRInt32 numChildren = rootContent->GetChildCount();
2050 if (numChildren > 0) {
2051 // We never want to place the selection after the last
2052 // br under the root node!
2053 nsIContent *child = rootContent->GetChildAt(numChildren - 1);
2054 if (child) {
2055 if (child->Tag() == nsGkAtoms::br)
2056 --numChildren;
2060 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
2062 return SetSelectionInternal(rootNode, 0, rootNode, numChildren);
2065 nsresult
2066 nsTextControlFrame::SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd)
2068 NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
2070 if (aSelStart > aSelEnd)
2071 return NS_ERROR_FAILURE;
2073 nsCOMPtr<nsIDOMNode> startNode, endNode;
2074 PRInt32 startOffset, endOffset;
2076 // Calculate the selection start point.
2078 nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset);
2080 NS_ENSURE_SUCCESS(rv, rv);
2082 if (aSelStart == aSelEnd) {
2083 // Collapsed selection, so start and end are the same!
2084 endNode = startNode;
2085 endOffset = startOffset;
2087 else {
2088 // Selection isn't collapsed so we have to calculate
2089 // the end point too.
2091 rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset);
2093 NS_ENSURE_SUCCESS(rv, rv);
2096 return SetSelectionInternal(startNode, startOffset, endNode, endOffset);
2099 NS_IMETHODIMP
2100 nsTextControlFrame::SetSelectionRange(PRInt32 aSelStart, PRInt32 aSelEnd)
2102 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2104 if (aSelStart > aSelEnd) {
2105 // Simulate what we'd see SetSelectionStart() was called, followed
2106 // by a SetSelectionEnd().
2108 aSelStart = aSelEnd;
2111 return SetSelectionEndPoints(aSelStart, aSelEnd);
2115 NS_IMETHODIMP
2116 nsTextControlFrame::SetSelectionStart(PRInt32 aSelectionStart)
2118 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2120 PRInt32 selStart = 0, selEnd = 0;
2122 nsresult rv = GetSelectionRange(&selStart, &selEnd);
2123 NS_ENSURE_SUCCESS(rv, rv);
2125 if (aSelectionStart > selEnd) {
2126 // Collapse to the new start point.
2127 selEnd = aSelectionStart;
2130 selStart = aSelectionStart;
2132 return SetSelectionEndPoints(selStart, selEnd);
2135 NS_IMETHODIMP
2136 nsTextControlFrame::SetSelectionEnd(PRInt32 aSelectionEnd)
2138 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2140 PRInt32 selStart = 0, selEnd = 0;
2142 nsresult rv = GetSelectionRange(&selStart, &selEnd);
2143 NS_ENSURE_SUCCESS(rv, rv);
2145 if (aSelectionEnd < selStart) {
2146 // Collapse to the new end point.
2147 selStart = aSelectionEnd;
2150 selEnd = aSelectionEnd;
2152 return SetSelectionEndPoints(selStart, selEnd);
2155 nsresult
2156 nsTextControlFrame::DOMPointToOffset(nsIDOMNode* aNode,
2157 PRInt32 aNodeOffset,
2158 PRInt32* aResult)
2160 NS_ENSURE_ARG_POINTER(aNode && aResult);
2162 *aResult = 0;
2164 nsCOMPtr<nsIDOMElement> rootElement;
2165 mEditor->GetRootElement(getter_AddRefs(rootElement));
2166 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
2168 NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
2170 nsCOMPtr<nsIDOMNodeList> nodeList;
2172 nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
2173 NS_ENSURE_SUCCESS(rv, rv);
2174 NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
2176 PRUint32 length = 0;
2177 rv = nodeList->GetLength(&length);
2178 NS_ENSURE_SUCCESS(rv, rv);
2180 if (!length || aNodeOffset < 0)
2181 return NS_OK;
2183 PRInt32 i, textOffset = 0;
2184 PRInt32 lastIndex = (PRInt32)length - 1;
2186 for (i = 0; i < (PRInt32)length; i++) {
2187 if (rootNode == aNode && i == aNodeOffset) {
2188 *aResult = textOffset;
2189 return NS_OK;
2192 nsCOMPtr<nsIDOMNode> item;
2193 rv = nodeList->Item(i, getter_AddRefs(item));
2194 NS_ENSURE_SUCCESS(rv, rv);
2195 NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
2197 nsCOMPtr<nsIDOMText> domText(do_QueryInterface(item));
2199 if (domText) {
2200 PRUint32 textLength = 0;
2202 rv = domText->GetLength(&textLength);
2203 NS_ENSURE_SUCCESS(rv, rv);
2205 if (item == aNode) {
2206 NS_ASSERTION((aNodeOffset >= 0 && aNodeOffset <= (PRInt32)textLength),
2207 "Invalid aNodeOffset!");
2208 *aResult = textOffset + aNodeOffset;
2209 return NS_OK;
2212 textOffset += textLength;
2214 else {
2215 // Must be a BR node. If it's not the last BR node
2216 // under the root, count it as a newline.
2218 if (i != lastIndex)
2219 ++textOffset;
2223 NS_ASSERTION((aNode == rootNode && aNodeOffset == (PRInt32)length),
2224 "Invalid node offset!");
2226 *aResult = textOffset;
2228 return NS_OK;
2231 nsresult
2232 nsTextControlFrame::OffsetToDOMPoint(PRInt32 aOffset,
2233 nsIDOMNode** aResult,
2234 PRInt32* aPosition)
2236 NS_ENSURE_ARG_POINTER(aResult && aPosition);
2238 *aResult = nsnull;
2239 *aPosition = 0;
2241 nsCOMPtr<nsIDOMElement> rootElement;
2242 mEditor->GetRootElement(getter_AddRefs(rootElement));
2243 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
2245 NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
2247 nsCOMPtr<nsIDOMNodeList> nodeList;
2249 nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
2250 NS_ENSURE_SUCCESS(rv, rv);
2251 NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
2253 PRUint32 length = 0;
2255 rv = nodeList->GetLength(&length);
2256 NS_ENSURE_SUCCESS(rv, rv);
2258 if (!length || aOffset < 0) {
2259 *aPosition = 0;
2260 *aResult = rootNode;
2261 NS_ADDREF(*aResult);
2262 return NS_OK;
2265 PRInt32 textOffset = 0;
2266 PRUint32 lastIndex = length - 1;
2268 for (PRUint32 i=0; i<length; i++) {
2269 nsCOMPtr<nsIDOMNode> item;
2270 rv = nodeList->Item(i, getter_AddRefs(item));
2271 NS_ENSURE_SUCCESS(rv, rv);
2272 NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
2274 nsCOMPtr<nsIDOMText> domText(do_QueryInterface(item));
2276 if (domText) {
2277 PRUint32 textLength = 0;
2279 rv = domText->GetLength(&textLength);
2280 NS_ENSURE_SUCCESS(rv, rv);
2282 // Check if aOffset falls within this range.
2283 if (aOffset >= textOffset && aOffset <= textOffset+(PRInt32)textLength) {
2284 *aPosition = aOffset - textOffset;
2285 *aResult = item;
2286 NS_ADDREF(*aResult);
2287 return NS_OK;
2290 textOffset += textLength;
2292 // If there aren't any more siblings after this text node,
2293 // return the point at the end of this text node!
2295 if (i == lastIndex) {
2296 *aPosition = textLength;
2297 *aResult = item;
2298 NS_ADDREF(*aResult);
2299 return NS_OK;
2302 else {
2303 // Must be a BR node, count it as a newline.
2305 if (aOffset == textOffset || i == lastIndex) {
2306 // We've found the correct position, or aOffset takes us
2307 // beyond the last child under rootNode, just return the point
2308 // under rootNode that is in front of this br.
2310 *aPosition = i;
2311 *aResult = rootNode;
2312 NS_ADDREF(*aResult);
2313 return NS_OK;
2316 ++textOffset;
2320 NS_ASSERTION(0, "We should never get here!");
2322 return NS_ERROR_FAILURE;
2325 NS_IMETHODIMP
2326 nsTextControlFrame::GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd)
2328 // make sure we have an editor
2329 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2331 *aSelectionStart = 0;
2332 *aSelectionEnd = 0;
2334 nsCOMPtr<nsISelection> selection;
2335 nsresult rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2336 NS_ENSURE_SUCCESS(rv, rv);
2337 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2339 PRInt32 numRanges = 0;
2340 selection->GetRangeCount(&numRanges);
2342 if (numRanges < 1)
2343 return NS_OK;
2345 // We only operate on the first range in the selection!
2347 nsCOMPtr<nsIDOMRange> firstRange;
2348 rv = selection->GetRangeAt(0, getter_AddRefs(firstRange));
2349 NS_ENSURE_SUCCESS(rv, rv);
2350 NS_ENSURE_TRUE(firstRange, NS_ERROR_FAILURE);
2352 nsCOMPtr<nsIDOMNode> startNode, endNode;
2353 PRInt32 startOffset = 0, endOffset = 0;
2355 // Get the start point of the range.
2357 rv = firstRange->GetStartContainer(getter_AddRefs(startNode));
2358 NS_ENSURE_SUCCESS(rv, rv);
2359 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
2361 rv = firstRange->GetStartOffset(&startOffset);
2362 NS_ENSURE_SUCCESS(rv, rv);
2364 // Get the end point of the range.
2366 rv = firstRange->GetEndContainer(getter_AddRefs(endNode));
2367 NS_ENSURE_SUCCESS(rv, rv);
2368 NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
2370 rv = firstRange->GetEndOffset(&endOffset);
2371 NS_ENSURE_SUCCESS(rv, rv);
2373 // Convert the start point to a selection offset.
2375 rv = DOMPointToOffset(startNode, startOffset, aSelectionStart);
2376 NS_ENSURE_SUCCESS(rv, rv);
2378 // Convert the end point to a selection offset.
2380 return DOMPointToOffset(endNode, endOffset, aSelectionEnd);
2383 /////END INTERFACE IMPLEMENTATIONS
2385 ////NSIFRAME
2386 NS_IMETHODIMP
2387 nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID,
2388 nsIAtom* aAttribute,
2389 PRInt32 aModType)
2391 if (!mEditor || !mSelCon)
2392 return NS_ERROR_NOT_INITIALIZED;
2393 nsresult rv = NS_OK;
2395 if (nsGkAtoms::maxlength == aAttribute)
2397 PRInt32 maxLength;
2398 PRBool maxDefined = GetMaxLength(&maxLength);
2400 nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(mEditor);
2401 if (textEditor)
2403 if (maxDefined)
2404 { // set the maxLength attribute
2405 textEditor->SetMaxTextLength(maxLength);
2406 // if maxLength>docLength, we need to truncate the doc content
2408 else { // unset the maxLength attribute
2409 textEditor->SetMaxTextLength(-1);
2412 rv = NS_OK; // don't propagate the error
2414 else if (nsGkAtoms::readonly == aAttribute)
2416 PRUint32 flags;
2417 mEditor->GetFlags(&flags);
2418 if (AttributeExists(nsGkAtoms::readonly))
2419 { // set readonly
2420 flags |= nsIPlaintextEditor::eEditorReadonlyMask;
2421 if (IsFocusedContent(PresContext(), mContent))
2422 mSelCon->SetCaretEnabled(PR_FALSE);
2424 else
2425 { // unset readonly
2426 flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
2427 if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
2428 IsFocusedContent(PresContext(), mContent))
2429 mSelCon->SetCaretEnabled(PR_TRUE);
2431 mEditor->SetFlags(flags);
2433 else if (mEditor && nsGkAtoms::disabled == aAttribute)
2435 PRUint32 flags;
2436 mEditor->GetFlags(&flags);
2437 if (AttributeExists(nsGkAtoms::disabled))
2438 { // set disabled
2439 flags |= nsIPlaintextEditor::eEditorDisabledMask;
2440 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
2441 if (IsFocusedContent(PresContext(), mContent))
2442 mSelCon->SetCaretEnabled(PR_FALSE);
2444 else
2445 { // unset disabled
2446 flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
2447 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
2449 mEditor->SetFlags(flags);
2451 // Allow the base class to handle common attributes supported
2452 // by all form elements...
2453 else {
2454 rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
2457 return rv;
2461 NS_IMETHODIMP
2462 nsTextControlFrame::GetText(nsString* aText)
2464 nsresult rv = NS_OK;
2465 if (IsSingleLineTextControl()) {
2466 // If we're going to remove newlines anyway, ignore the wrap property
2467 GetValue(*aText, PR_TRUE);
2468 RemoveNewlines(*aText);
2469 } else {
2470 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
2471 if (textArea) {
2472 rv = textArea->GetValue(*aText);
2475 return rv;
2479 NS_IMETHODIMP
2480 nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
2482 aPhonetic.Truncate(0);
2483 if (!mEditor)
2484 return NS_ERROR_NOT_INITIALIZED;
2485 nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(mEditor);
2486 if (imeSupport) {
2487 nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
2488 if (phonetic)
2489 phonetic->GetPhonetic(aPhonetic);
2491 return NS_OK;
2494 ///END NSIFRAME OVERLOADS
2495 /////BEGIN PROTECTED METHODS
2497 void nsTextControlFrame::RemoveNewlines(nsString &aString)
2499 // strip CR/LF and null
2500 static const char badChars[] = {10, 13, 0};
2501 aString.StripChars(badChars);
2505 PRBool
2506 nsTextControlFrame::GetMaxLength(PRInt32* aSize)
2508 *aSize = -1;
2510 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
2511 if (content) {
2512 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength);
2513 if (attr && attr->Type() == nsAttrValue::eInteger) {
2514 *aSize = attr->GetIntegerValue();
2516 return PR_TRUE;
2519 return PR_FALSE;
2522 // this is where we propagate a content changed event
2523 void
2524 nsTextControlFrame::FireOnInput()
2526 if (!mNotifyOnInput)
2527 return; // if notification is turned off, do nothing
2529 // Dispatch the "input" event
2530 nsEventStatus status = nsEventStatus_eIgnore;
2531 nsUIEvent event(PR_TRUE, NS_FORM_INPUT, 0);
2533 // Have the content handle the event, propagating it according to normal
2534 // DOM rules.
2535 nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
2536 shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
2539 nsresult
2540 nsTextControlFrame::InitFocusedValue()
2542 return GetText(&mFocusedValue);
2545 NS_IMETHODIMP
2546 nsTextControlFrame::CheckFireOnChange()
2548 nsString value;
2549 GetText(&value);
2550 if (!mFocusedValue.Equals(value))
2552 mFocusedValue = value;
2553 // Dispatch the change event
2554 nsEventStatus status = nsEventStatus_eIgnore;
2555 nsInputEvent event(PR_TRUE, NS_FORM_CHANGE, nsnull);
2556 nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
2557 shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
2559 return NS_OK;
2562 //======
2563 //privates
2565 NS_IMETHODIMP
2566 nsTextControlFrame::GetValue(nsAString& aValue, PRBool aIgnoreWrap) const
2568 aValue.Truncate(); // initialize out param
2569 nsresult rv = NS_OK;
2571 if (mEditor && mUseEditor)
2573 PRUint32 flags = (nsIDocumentEncoder::OutputLFLineBreak |
2574 nsIDocumentEncoder::OutputPreformatted |
2575 nsIDocumentEncoder::OutputPersistNBSP);
2577 if (PR_TRUE==IsPlainTextControl())
2579 flags |= nsIDocumentEncoder::OutputBodyOnly;
2582 if (!aIgnoreWrap) {
2583 nsHTMLTextWrap wrapProp;
2584 if (::GetWrapPropertyEnum(mContent, wrapProp) &&
2585 wrapProp == eHTMLTextWrap_Hard) {
2586 flags |= nsIDocumentEncoder::OutputWrap;
2590 // What follows is a bit of a hack. The problem is that we could be in
2591 // this method because we're being destroyed for whatever reason while
2592 // script is executing. If that happens, editor will run with the
2593 // privileges of the executing script, which means it may not be able to
2594 // access its own DOM nodes! Let's try to deal with that by pushing a null
2595 // JSContext on the JSContext stack to make it clear that we're native
2596 // code. Note that any script that's directly trying to access our value
2597 // has to be going through some scriptable object to do that and that
2598 // already does the relevant security checks.
2599 // XXXbz if we could just get the textContent of our anonymous content (eg
2600 // if plaintext editor didn't create <br> nodes all over), we wouldn't need
2601 // this.
2602 nsCOMPtr<nsIJSContextStack> stack =
2603 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
2604 PRBool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull));
2606 rv = mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
2607 aValue);
2609 if (pushed) {
2610 JSContext* cx;
2611 stack->Pop(&cx);
2612 NS_ASSERTION(!cx, "Unexpected JSContext popped!");
2615 else
2617 // Otherwise get the value from content.
2618 nsCOMPtr<nsIDOMHTMLInputElement> inputControl = do_QueryInterface(mContent);
2619 if (inputControl)
2621 rv = inputControl->GetValue(aValue);
2623 else
2625 nsCOMPtr<nsIDOMHTMLTextAreaElement> textareaControl
2626 = do_QueryInterface(mContent);
2627 if (textareaControl)
2629 rv = textareaControl->GetValue(aValue);
2634 return rv;
2638 // END IMPLEMENTING NS_IFORMCONTROLFRAME
2640 nsresult
2641 nsTextControlFrame::SetValue(const nsAString& aValue)
2643 // XXX this method should actually propagate errors! It'd make debugging it
2644 // so much easier...
2645 if (mEditor && mUseEditor)
2647 // This method isn't used for user-generated changes, except for calls
2648 // from nsFileControlFrame which sets mFireChangeEventState==true and
2649 // restores it afterwards (ie. we want 'change' events for those changes).
2650 // Focused value must be updated to prevent incorrect 'change' events,
2651 // but only if user hasn't changed the value.
2652 nsString val;
2653 GetText(&val);
2654 PRBool focusValueInit = !mFireChangeEventState &&
2655 mFocusedValue.Equals(val);
2657 nsCOMPtr<nsIEditor> editor = mEditor;
2658 nsWeakFrame weakFrame(this);
2659 nsAutoString currentValue;
2660 GetValue(currentValue, PR_FALSE);
2661 if (IsSingleLineTextControl())
2663 RemoveNewlines(currentValue);
2665 // this is necessary to avoid infinite recursion
2666 if (!currentValue.Equals(aValue))
2668 // \r is an illegal character in the dom, but people use them,
2669 // so convert windows and mac platform linebreaks to \n:
2670 // Unfortunately aValue is declared const, so we have to copy
2671 // in order to do this substitution.
2672 currentValue.Assign(aValue);
2673 ::PlatformToDOMLineBreaks(currentValue);
2675 nsCOMPtr<nsIDOMDocument>domDoc;
2676 nsresult rv = editor->GetDocument(getter_AddRefs(domDoc));
2677 NS_ENSURE_SUCCESS(rv, rv);
2678 NS_ENSURE_STATE(domDoc);
2680 // Time to mess with our security context... See comments in GetValue()
2681 // for why this is needed. Note that we have to do this up here, because
2682 // otherwise SelectAll() will fail.
2683 nsCOMPtr<nsIJSContextStack> stack =
2684 do_GetService("@mozilla.org/js/xpc/ContextStack;1");
2685 PRBool pushed = stack && NS_SUCCEEDED(stack->Push(nsnull));
2687 nsCOMPtr<nsISelection> domSel;
2688 nsCOMPtr<nsISelectionPrivate> selPriv;
2689 mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel));
2690 if (domSel)
2692 selPriv = do_QueryInterface(domSel);
2693 if (selPriv)
2694 selPriv->StartBatchChanges();
2697 nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon;
2698 mSelCon->SelectAll();
2699 nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(editor);
2700 if (!plaintextEditor || !weakFrame.IsAlive()) {
2701 NS_WARNING("Somehow not a plaintext editor?");
2702 if (pushed) {
2703 JSContext* cx;
2704 stack->Pop(&cx);
2705 NS_ASSERTION(!cx, "Unexpected JSContext popped!");
2707 return NS_ERROR_FAILURE;
2710 // Since this code does not handle user-generated changes to the text,
2711 // make sure we don't fire oninput when the editor notifies us.
2712 // (mNotifyOnInput must be reset before we return).
2714 // To protect against a reentrant call to SetValue, we check whether
2715 // another SetValue is already happening for this frame. If it is,
2716 // we must wait until we unwind to re-enable oninput events.
2717 PRBool outerTransaction = mNotifyOnInput;
2718 if (outerTransaction)
2719 mNotifyOnInput = PR_FALSE;
2721 // get the flags, remove readonly and disabled, set the value,
2722 // restore flags
2723 PRUint32 flags, savedFlags;
2724 editor->GetFlags(&savedFlags);
2725 flags = savedFlags;
2726 flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
2727 flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
2728 editor->SetFlags(flags);
2730 // Also don't enforce max-length here
2731 PRInt32 savedMaxLength;
2732 plaintextEditor->GetMaxTextLength(&savedMaxLength);
2733 plaintextEditor->SetMaxTextLength(-1);
2735 if (currentValue.Length() < 1)
2736 editor->DeleteSelection(nsIEditor::eNone);
2737 else {
2738 if (plaintextEditor)
2739 plaintextEditor->InsertText(currentValue);
2742 plaintextEditor->SetMaxTextLength(savedMaxLength);
2743 editor->SetFlags(savedFlags);
2744 if (selPriv)
2745 selPriv->EndBatchChanges();
2747 if (pushed) {
2748 JSContext* cx;
2749 stack->Pop(&cx);
2750 NS_ASSERTION(!cx, "Unexpected JSContext popped!");
2753 NS_ENSURE_STATE(weakFrame.IsAlive());
2754 if (outerTransaction)
2755 mNotifyOnInput = PR_TRUE;
2757 if (focusValueInit) {
2758 // Reset mFocusedValue so the onchange event doesn't fire incorrectly.
2759 InitFocusedValue();
2763 NS_ENSURE_STATE(weakFrame.IsAlive());
2764 nsIScrollableView* scrollableView = GetScrollableView();
2765 if (scrollableView)
2767 // Scroll the upper left corner of the text control's
2768 // content area back into view.
2770 scrollableView->ScrollTo(0, 0, NS_VMREFRESH_NO_SYNC);
2773 else
2775 // Otherwise set the value in content.
2776 nsCOMPtr<nsITextControlElement> textControl = do_QueryInterface(mContent);
2777 if (textControl)
2779 textControl->TakeTextFrameValue(aValue);
2782 return NS_OK;
2786 NS_IMETHODIMP
2787 nsTextControlFrame::SetInitialChildList(nsIAtom* aListName,
2788 nsIFrame* aChildList)
2790 nsresult rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
2791 if (mEditor)
2792 mEditor->PostCreate();
2793 //look for scroll view below this frame go along first child list
2794 nsIFrame* first = GetFirstChild(nsnull);
2796 // Mark the scroll frame as being a reflow root. This will allow
2797 // incremental reflows to be initiated at the scroll frame, rather
2798 // than descending from the root frame of the frame hierarchy.
2799 first->AddStateBits(NS_FRAME_REFLOW_ROOT);
2801 nsIScrollableFrame *scrollableFrame = nsnull;
2802 CallQueryInterface(first, &scrollableFrame);
2803 NS_ASSERTION(scrollableFrame, "Child must be scrollable");
2805 // we must turn off scrollbars for singleline text controls
2806 // XXX FIXME this should be removed,
2807 // nsGfxScrollFrameInner::CreateAnonymousContent handles this
2808 if (IsSingleLineTextControl())
2810 if (scrollableFrame)
2811 scrollableFrame->SetScrollbarVisibility(PR_FALSE, PR_FALSE);
2814 //register focus and key listeners
2815 if (mContent) {
2816 // register the event listeners with the DOM event receiver
2817 rv = mContent->AddEventListenerByIID(static_cast<nsIDOMFocusListener *>(mTextListener),
2818 NS_GET_IID(nsIDOMFocusListener));
2819 NS_ASSERTION(NS_SUCCEEDED(rv), "failed to register focus listener");
2820 // XXXbryner do we need to check for a null presshell here?
2821 if (!PresContext()->GetPresShell())
2822 return NS_ERROR_FAILURE;
2825 nsCOMPtr<nsIDOMEventGroup> systemGroup;
2826 mContent->GetSystemEventGroup(getter_AddRefs(systemGroup));
2827 nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mContent);
2828 if (dom3Targ) {
2829 // cast because of ambiguous base
2830 nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
2831 (mTextListener);
2833 dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keydown"),
2834 listener, PR_FALSE, systemGroup);
2835 dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
2836 listener, PR_FALSE, systemGroup);
2837 dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keyup"),
2838 listener, PR_FALSE, systemGroup);
2841 return rv;
2844 nsIScrollableView* nsTextControlFrame::GetScrollableView()
2846 nsIFrame* first = GetFirstChild(nsnull);
2847 nsIScrollableFrame* scrollableFrame = nsnull;
2848 if (first) {
2849 CallQueryInterface(first, &scrollableFrame);
2851 return scrollableFrame ? scrollableFrame->GetScrollableView() : nsnull;
2854 PRBool
2855 nsTextControlFrame::IsScrollable() const
2857 return !IsSingleLineTextControl();
2860 void
2861 nsTextControlFrame::SetValueChanged(PRBool aValueChanged)
2863 nsCOMPtr<nsITextControlElement> elem = do_QueryInterface(mContent);
2864 if (elem) {
2865 elem->SetValueChanged(aValueChanged);
2869 /* static */ void
2870 nsTextControlFrame::ShutDown()
2872 NS_IF_RELEASE(sNativeTextAreaBindings);
2873 NS_IF_RELEASE(sNativeInputBindings);