Backed out changeset 8366e5cc9f57 (bug 125282) because of four windows unit test...
[mozilla-central.git] / layout / forms / nsTextControlFrame.cpp
blob0d899e65577a09c7de9112b1e873d8f0a0af40af
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 "nsCaret.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"
121 #include "nsFocusManager.h"
123 #define DEFAULT_COLUMN_WIDTH 20
125 #include "nsContentCID.h"
126 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
128 static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
129 static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
131 static const PRInt32 DEFAULT_COLS = 20;
132 static const PRInt32 DEFAULT_ROWS = 1;
133 static const PRInt32 DEFAULT_ROWS_TEXTAREA = 2;
134 static const PRInt32 DEFAULT_UNDO_CAP = 1000;
136 static nsINativeKeyBindings *sNativeInputBindings = nsnull;
137 static nsINativeKeyBindings *sNativeTextAreaBindings = nsnull;
139 static void
140 PlatformToDOMLineBreaks(nsString &aString)
142 // Windows linebreaks: Map CRLF to LF:
143 aString.ReplaceSubstring(NS_LITERAL_STRING("\r\n").get(),
144 NS_LITERAL_STRING("\n").get());
146 // Mac linebreaks: Map any remaining CR to LF:
147 aString.ReplaceSubstring(NS_LITERAL_STRING("\r").get(),
148 NS_LITERAL_STRING("\n").get());
151 // wrap can be one of these three values.
152 typedef enum {
153 eHTMLTextWrap_Off = 1, // "off"
154 eHTMLTextWrap_Hard = 2, // "hard"
155 eHTMLTextWrap_Soft = 3 // the default
156 } nsHTMLTextWrap;
158 static PRBool
159 GetWrapPropertyEnum(nsIContent* aContent, nsHTMLTextWrap& aWrapProp)
161 // soft is the default; "physical" defaults to soft as well because all other
162 // browsers treat it that way and there is no real reason to maintain physical
163 // and virtual as separate entities if no one else does. Only hard and off
164 // do anything different.
165 aWrapProp = eHTMLTextWrap_Soft; // the default
167 nsAutoString wrap;
168 if (aContent->IsNodeOfType(nsINode::eHTML)) {
169 static nsIContent::AttrValuesArray strings[] =
170 {&nsGkAtoms::HARD, &nsGkAtoms::OFF, nsnull};
172 switch (aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::wrap,
173 strings, eIgnoreCase)) {
174 case 0: aWrapProp = eHTMLTextWrap_Hard; break;
175 case 1: aWrapProp = eHTMLTextWrap_Off; break;
178 return PR_TRUE;
181 return PR_FALSE;
184 class nsTextInputListener : public nsISelectionListener,
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 NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
208 // nsIDOMKeyListener
209 NS_IMETHOD KeyDown(nsIDOMEvent *aKeyEvent);
210 NS_IMETHOD KeyPress(nsIDOMEvent *aKeyEvent);
211 NS_IMETHOD KeyUp(nsIDOMEvent *aKeyEvent);
213 NS_DECL_NSIEDITOROBSERVER
215 protected:
217 nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate);
219 NS_HIDDEN_(nsINativeKeyBindings*) GetKeyBindings();
221 protected:
223 nsTextControlFrame* mFrame; // weak reference
225 PRPackedBool mSelectionWasCollapsed;
227 * Whether we had undo items or not the last time we got EditAction()
228 * notification (when this state changes we update undo and redo menus)
230 PRPackedBool mHadUndoItems;
232 * Whether we had redo items or not the last time we got EditAction()
233 * notification (when this state changes we update undo and redo menus)
235 PRPackedBool mHadRedoItems;
240 * nsTextEditorListener implementation
243 nsTextInputListener::nsTextInputListener()
244 : mFrame(nsnull)
245 , mSelectionWasCollapsed(PR_TRUE)
246 , mHadUndoItems(PR_FALSE)
247 , mHadRedoItems(PR_FALSE)
251 nsTextInputListener::~nsTextInputListener()
255 NS_IMPL_ADDREF(nsTextInputListener)
256 NS_IMPL_RELEASE(nsTextInputListener)
258 NS_INTERFACE_MAP_BEGIN(nsTextInputListener)
259 NS_INTERFACE_MAP_ENTRY(nsISelectionListener)
260 NS_INTERFACE_MAP_ENTRY(nsIEditorObserver)
261 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
262 NS_INTERFACE_MAP_ENTRY(nsIDOMKeyListener)
263 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMKeyListener)
264 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMKeyListener)
265 NS_INTERFACE_MAP_END
267 // BEGIN nsIDOMSelectionListener
269 static PRBool
270 IsFocusedContent(nsIContent* aContent)
272 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
273 if (!fm)
274 return PR_FALSE;
276 nsCOMPtr<nsIDOMElement> focusedElement;
277 fm->GetFocusedElement(getter_AddRefs(focusedElement));
278 nsCOMPtr<nsIContent> focusedContent = do_QueryInterface(focusedElement);
279 return (focusedContent == aContent);
282 NS_IMETHODIMP
283 nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, PRInt16 aReason)
285 PRBool collapsed;
286 if (!mFrame || !aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
287 return NS_OK;
289 // Fire the select event
290 // The specs don't exactly say when we should fire the select event.
291 // IE: Whenever you add/remove a character to/from the selection. Also
292 // each time for select all. Also if you get to the end of the text
293 // field you will get new event for each keypress or a continuous
294 // stream of events if you use the mouse. IE will fire select event
295 // when the selection collapses to nothing if you are holding down
296 // the shift or mouse button.
297 // Mozilla: If we have non-empty selection we will fire a new event for each
298 // keypress (or mouseup) if the selection changed. Mozilla will also
299 // create the event each time select all is called, even if everything
300 // was previously selected, becase technically select all will first collapse
301 // and then extend. Mozilla will never create an event if the selection
302 // collapses to nothing.
303 if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON |
304 nsISelectionListener::KEYPRESS_REASON |
305 nsISelectionListener::SELECTALL_REASON)))
307 nsIContent* content = mFrame->GetContent();
308 if (content)
310 nsCOMPtr<nsIDocument> doc = content->GetDocument();
311 if (doc)
313 nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
314 if (presShell)
316 nsEventStatus status = nsEventStatus_eIgnore;
317 nsEvent event(PR_TRUE, NS_FORM_SELECTED);
319 presShell->HandleEventWithTarget(&event, mFrame, content, &status);
325 // if the collapsed state did not change, don't fire notifications
326 if (collapsed == mSelectionWasCollapsed)
327 return NS_OK;
329 mSelectionWasCollapsed = collapsed;
331 if (!mFrame || !IsFocusedContent(mFrame->GetContent()))
332 return NS_OK;
334 return UpdateTextInputCommands(NS_LITERAL_STRING("select"));
337 // END nsIDOMSelectionListener
339 // BEGIN nsIDOMKeyListener
341 NS_IMETHODIMP
342 nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
344 return NS_OK;
347 static void
348 DoCommandCallback(const char *aCommand, void *aData)
350 nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
351 nsIContent *content = frame->GetContent();
353 nsCOMPtr<nsIControllers> controllers;
354 nsCOMPtr<nsIDOMNSHTMLInputElement> input = do_QueryInterface(content);
355 if (input) {
356 input->GetControllers(getter_AddRefs(controllers));
357 } else {
358 nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textArea =
359 do_QueryInterface(content);
361 if (textArea) {
362 textArea->GetControllers(getter_AddRefs(controllers));
366 if (!controllers) {
367 NS_WARNING("Could not get controllers");
368 return;
371 nsCOMPtr<nsIController> controller;
372 controllers->GetControllerForCommand(aCommand, getter_AddRefs(controller));
373 if (controller) {
374 controller->DoCommand(aCommand);
379 NS_IMETHODIMP
380 nsTextInputListener::KeyDown(nsIDOMEvent *aDOMEvent)
382 nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
383 NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
385 nsNativeKeyEvent nativeEvent;
386 nsINativeKeyBindings *bindings = GetKeyBindings();
387 if (bindings &&
388 nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_FALSE)) {
389 if (bindings->KeyDown(nativeEvent, DoCommandCallback, mFrame)) {
390 aDOMEvent->PreventDefault();
394 return NS_OK;
397 NS_IMETHODIMP
398 nsTextInputListener::KeyPress(nsIDOMEvent *aDOMEvent)
400 nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
401 NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
403 nsNativeKeyEvent nativeEvent;
404 nsINativeKeyBindings *bindings = GetKeyBindings();
405 if (bindings &&
406 nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_TRUE)) {
407 if (bindings->KeyPress(nativeEvent, DoCommandCallback, mFrame)) {
408 aDOMEvent->PreventDefault();
412 return NS_OK;
415 NS_IMETHODIMP
416 nsTextInputListener::KeyUp(nsIDOMEvent *aDOMEvent)
418 nsCOMPtr<nsIDOMKeyEvent> keyEvent(do_QueryInterface(aDOMEvent));
419 NS_ENSURE_TRUE(keyEvent, NS_ERROR_INVALID_ARG);
421 nsNativeKeyEvent nativeEvent;
422 nsINativeKeyBindings *bindings = GetKeyBindings();
423 if (bindings &&
424 nsContentUtils::DOMEventToNativeKeyEvent(keyEvent, &nativeEvent, PR_FALSE)) {
425 if (bindings->KeyUp(nativeEvent, DoCommandCallback, mFrame)) {
426 aDOMEvent->PreventDefault();
430 return NS_OK;
432 // END nsIDOMKeyListener
434 // BEGIN nsIEditorObserver
436 NS_IMETHODIMP
437 nsTextInputListener::EditAction()
440 // Update the undo / redo menus
442 nsCOMPtr<nsIEditor> editor;
443 mFrame->GetEditor(getter_AddRefs(editor));
445 nsCOMPtr<nsITransactionManager> manager;
446 editor->GetTransactionManager(getter_AddRefs(manager));
447 NS_ENSURE_TRUE(manager, NS_ERROR_FAILURE);
449 // Get the number of undo / redo items
450 PRInt32 numUndoItems = 0;
451 PRInt32 numRedoItems = 0;
452 manager->GetNumberOfUndoItems(&numUndoItems);
453 manager->GetNumberOfRedoItems(&numRedoItems);
454 if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
455 (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
456 // Modify the menu if undo or redo items are different
457 UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
459 mHadUndoItems = numUndoItems != 0;
460 mHadRedoItems = numRedoItems != 0;
463 // Make sure we know we were changed (do NOT set this to false if there are
464 // no undo items; JS could change the value and we'd still need to save it)
465 mFrame->SetValueChanged(PR_TRUE);
467 // Fire input event
468 mFrame->FireOnInput();
470 return NS_OK;
473 // END nsIEditorObserver
476 nsresult
477 nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate)
479 NS_ENSURE_STATE(mFrame);
481 nsIContent* content = mFrame->GetContent();
482 NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
484 nsCOMPtr<nsIDocument> doc = content->GetDocument();
485 NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
487 nsPIDOMWindow *domWindow = doc->GetWindow();
488 NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
490 return domWindow->UpdateCommands(commandsToUpdate);
493 nsINativeKeyBindings*
494 nsTextInputListener::GetKeyBindings()
496 if (mFrame->IsTextArea()) {
497 static PRBool sNoTextAreaBindings = PR_FALSE;
499 if (!sNativeTextAreaBindings && !sNoTextAreaBindings) {
500 CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "textarea",
501 &sNativeTextAreaBindings);
503 if (!sNativeTextAreaBindings) {
504 sNoTextAreaBindings = PR_TRUE;
508 return sNativeTextAreaBindings;
511 static PRBool sNoInputBindings = PR_FALSE;
512 if (!sNativeInputBindings && !sNoInputBindings) {
513 CallGetService(NS_NATIVEKEYBINDINGS_CONTRACTID_PREFIX "input",
514 &sNativeInputBindings);
516 if (!sNativeInputBindings) {
517 sNoInputBindings = PR_TRUE;
521 return sNativeInputBindings;
524 // END nsTextInputListener
526 class nsTextInputSelectionImpl : public nsSupportsWeakReference
527 , public nsISelectionController
529 public:
530 NS_DECL_ISUPPORTS
532 nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter);
533 ~nsTextInputSelectionImpl(){}
535 //NSISELECTIONCONTROLLER INTERFACES
536 NS_IMETHOD SetDisplaySelection(PRInt16 toggle);
537 NS_IMETHOD GetDisplaySelection(PRInt16 *_retval);
538 NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
539 NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
540 NS_IMETHOD GetSelection(PRInt16 type, nsISelection **_retval);
541 NS_IMETHOD ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous);
542 NS_IMETHOD RepaintSelection(PRInt16 type);
543 NS_IMETHOD RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType);
544 NS_IMETHOD SetCaretEnabled(PRBool enabled);
545 NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
546 NS_IMETHOD GetCaretEnabled(PRBool *_retval);
547 NS_IMETHOD GetCaretVisible(PRBool *_retval);
548 NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
549 NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
550 NS_IMETHOD CharacterExtendForDelete();
551 NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
552 NS_IMETHOD WordExtendForDelete(PRBool aForward);
553 NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
554 NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
555 NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
556 NS_IMETHOD CompleteScroll(PRBool aForward);
557 NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
558 NS_IMETHOD ScrollPage(PRBool aForward);
559 NS_IMETHOD ScrollLine(PRBool aForward);
560 NS_IMETHOD ScrollHorizontal(PRBool aLeft);
561 NS_IMETHOD SelectAll(void);
562 NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
564 private:
565 nsCOMPtr<nsFrameSelection> mFrameSelection;
566 nsCOMPtr<nsIContent> mLimiter;
567 nsWeakPtr mPresShellWeak;
570 // Implement our nsISupports methods
571 NS_IMPL_ISUPPORTS3(nsTextInputSelectionImpl,
572 nsISelectionController,
573 nsISelectionDisplay,
574 nsISupportsWeakReference)
577 // BEGIN nsTextInputSelectionImpl
579 nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter)
581 if (aSel && aShell)
583 mFrameSelection = aSel;//we are the owner now!
584 mLimiter = aLimiter;
585 mFrameSelection->Init(aShell, mLimiter);
586 mPresShellWeak = do_GetWeakReference(aShell);
590 NS_IMETHODIMP
591 nsTextInputSelectionImpl::SetDisplaySelection(PRInt16 aToggle)
593 if (!mFrameSelection)
594 return NS_ERROR_NULL_POINTER;
596 mFrameSelection->SetDisplaySelection(aToggle);
597 return NS_OK;
600 NS_IMETHODIMP
601 nsTextInputSelectionImpl::GetDisplaySelection(PRInt16 *aToggle)
603 if (!mFrameSelection)
604 return NS_ERROR_NULL_POINTER;
606 *aToggle = mFrameSelection->GetDisplaySelection();
607 return NS_OK;
610 NS_IMETHODIMP
611 nsTextInputSelectionImpl::SetSelectionFlags(PRInt16 aToggle)
613 return NS_OK;//stub this out. not used in input
616 NS_IMETHODIMP
617 nsTextInputSelectionImpl::GetSelectionFlags(PRInt16 *aOutEnable)
619 *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
620 return NS_OK;
623 NS_IMETHODIMP
624 nsTextInputSelectionImpl::GetSelection(PRInt16 type, nsISelection **_retval)
626 if (!mFrameSelection)
627 return NS_ERROR_NULL_POINTER;
629 *_retval = mFrameSelection->GetSelection(type);
631 if (!(*_retval))
632 return NS_ERROR_FAILURE;
634 NS_ADDREF(*_retval);
635 return NS_OK;
638 NS_IMETHODIMP
639 nsTextInputSelectionImpl::ScrollSelectionIntoView(PRInt16 aType, PRInt16 aRegion, PRBool aIsSynchronous)
641 if (mFrameSelection) {
642 // After ScrollSelectionIntoView(), the pending notifications might be
643 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
644 nsresult rv = mFrameSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
646 nsIScrollableView* scrollableView = mFrameSelection->GetScrollableView();
647 if (!scrollableView) {
648 return rv;
650 nsIView* view = nsnull;
651 scrollableView->GetScrolledView(view);
652 if (!view) {
653 return rv;
655 const nsRect portRect = scrollableView->View()->GetBounds();
656 const nsRect viewRect = view->GetBounds();
657 if (viewRect.XMost() < portRect.width) {
658 return scrollableView->ScrollTo(PR_MAX(viewRect.width - portRect.width, 0), -viewRect.y, 0);
661 return rv;
663 return NS_ERROR_NULL_POINTER;
666 NS_IMETHODIMP
667 nsTextInputSelectionImpl::RepaintSelection(PRInt16 type)
669 if (!mFrameSelection)
670 return NS_ERROR_FAILURE;
672 return mFrameSelection->RepaintSelection(type);
675 NS_IMETHODIMP
676 nsTextInputSelectionImpl::RepaintSelection(nsPresContext* aPresContext, SelectionType aSelectionType)
678 if (!mFrameSelection)
679 return NS_ERROR_FAILURE;
681 return mFrameSelection->RepaintSelection(aSelectionType);
684 NS_IMETHODIMP
685 nsTextInputSelectionImpl::SetCaretEnabled(PRBool enabled)
687 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
689 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
690 if (!shell) return NS_ERROR_FAILURE;
692 // tell the pres shell to enable the caret, rather than settings its visibility directly.
693 // this way the presShell's idea of caret visibility is maintained.
694 nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
695 if (!selCon) return NS_ERROR_NO_INTERFACE;
696 selCon->SetCaretEnabled(enabled);
698 return NS_OK;
701 NS_IMETHODIMP
702 nsTextInputSelectionImpl::SetCaretReadOnly(PRBool aReadOnly)
704 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
705 nsresult result;
706 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
707 if (shell)
709 nsRefPtr<nsCaret> caret;
710 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))))
712 nsISelection* domSel = mFrameSelection->
713 GetSelection(nsISelectionController::SELECTION_NORMAL);
714 if (domSel)
715 caret->SetCaretReadOnly(aReadOnly);
716 return NS_OK;
719 return NS_ERROR_FAILURE;
722 NS_IMETHODIMP
723 nsTextInputSelectionImpl::GetCaretEnabled(PRBool *_retval)
725 return GetCaretVisible(_retval);
728 NS_IMETHODIMP
729 nsTextInputSelectionImpl::GetCaretVisible(PRBool *_retval)
731 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
732 nsresult result;
733 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
734 if (shell)
736 nsRefPtr<nsCaret> caret;
737 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))))
739 nsISelection* domSel = mFrameSelection->
740 GetSelection(nsISelectionController::SELECTION_NORMAL);
741 if (domSel)
742 return caret->GetCaretVisible(_retval);
745 return NS_ERROR_FAILURE;
748 NS_IMETHODIMP
749 nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(PRBool aVisibility)
751 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
752 nsresult result;
753 nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
754 if (shell)
756 nsRefPtr<nsCaret> caret;
757 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))))
759 nsISelection* domSel = mFrameSelection->
760 GetSelection(nsISelectionController::SELECTION_NORMAL);
761 if (domSel)
762 caret->SetVisibilityDuringSelection(aVisibility);
763 return NS_OK;
766 return NS_ERROR_FAILURE;
769 NS_IMETHODIMP
770 nsTextInputSelectionImpl::CharacterMove(PRBool aForward, PRBool aExtend)
772 if (mFrameSelection)
773 return mFrameSelection->CharacterMove(aForward, aExtend);
774 return NS_ERROR_NULL_POINTER;
777 NS_IMETHODIMP
778 nsTextInputSelectionImpl::CharacterExtendForDelete()
780 if (mFrameSelection)
781 return mFrameSelection->CharacterExtendForDelete();
782 return NS_ERROR_NULL_POINTER;
785 NS_IMETHODIMP
786 nsTextInputSelectionImpl::WordMove(PRBool aForward, PRBool aExtend)
788 if (mFrameSelection)
789 return mFrameSelection->WordMove(aForward, aExtend);
790 return NS_ERROR_NULL_POINTER;
793 NS_IMETHODIMP
794 nsTextInputSelectionImpl::WordExtendForDelete(PRBool aForward)
796 if (mFrameSelection)
797 return mFrameSelection->WordExtendForDelete(aForward);
798 return NS_ERROR_NULL_POINTER;
801 NS_IMETHODIMP
802 nsTextInputSelectionImpl::LineMove(PRBool aForward, PRBool aExtend)
804 if (mFrameSelection)
806 nsresult result = mFrameSelection->LineMove(aForward, aExtend);
807 if (NS_FAILED(result))
808 result = CompleteMove(aForward,aExtend);
809 return result;
811 return NS_ERROR_NULL_POINTER;
815 NS_IMETHODIMP
816 nsTextInputSelectionImpl::IntraLineMove(PRBool aForward, PRBool aExtend)
818 if (mFrameSelection)
819 return mFrameSelection->IntraLineMove(aForward, aExtend);
820 return NS_ERROR_NULL_POINTER;
824 NS_IMETHODIMP
825 nsTextInputSelectionImpl::PageMove(PRBool aForward, PRBool aExtend)
827 // expected behavior for PageMove is to scroll AND move the caret
828 // and to remain relative position of the caret in view. see Bug 4302.
830 if (mPresShellWeak)
832 nsCOMPtr<nsIPresShell> presShell = do_QueryReferent(mPresShellWeak);
833 if (!presShell)
834 return NS_ERROR_NULL_POINTER;
836 //get the scroll view
837 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
838 if (scrollableView)
839 mFrameSelection->CommonPageMove(aForward, aExtend, scrollableView);
841 // After ScrollSelectionIntoView(), the pending notifications might be
842 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
843 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
846 NS_IMETHODIMP
847 nsTextInputSelectionImpl::CompleteScroll(PRBool aForward)
849 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
851 if (!scrollableView)
852 return NS_ERROR_NOT_INITIALIZED;
854 return scrollableView->ScrollByWhole(!aForward); //TRUE = top, aForward TRUE=bottom
857 NS_IMETHODIMP
858 nsTextInputSelectionImpl::CompleteMove(PRBool aForward, PRBool aExtend)
860 // grab the parent / root DIV for this text widget
861 nsIContent* parentDIV = mFrameSelection->GetLimiter();
862 if (!parentDIV)
863 return NS_ERROR_UNEXPECTED;
865 // make the caret be either at the very beginning (0) or the very end
866 PRInt32 offset = 0;
867 nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
868 if (aForward)
870 offset = parentDIV->GetChildCount();
872 // Prevent the caret from being placed after the last
873 // BR node in the content tree!
875 if (offset > 0)
877 nsIContent *child = parentDIV->GetChildAt(offset - 1);
879 if (child->Tag() == nsGkAtoms::br)
881 --offset;
882 hint = nsFrameSelection::HINTRIGHT; // for Bug 106855
887 mFrameSelection->HandleClick(parentDIV, offset, offset, aExtend,
888 PR_FALSE, hint);
890 // if we got this far, attempt to scroll no matter what the above result is
891 return CompleteScroll(aForward);
894 NS_IMETHODIMP
895 nsTextInputSelectionImpl::ScrollPage(PRBool aForward)
897 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
898 if (!scrollableView)
899 return NS_ERROR_NOT_INITIALIZED;
901 return scrollableView->ScrollByPages(0, aForward ? 1 : -1);
904 NS_IMETHODIMP
905 nsTextInputSelectionImpl::ScrollLine(PRBool aForward)
907 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
908 if (!scrollableView)
909 return NS_ERROR_NOT_INITIALIZED;
911 // will we have bug #7354 because we aren't forcing an update here?
912 return scrollableView->ScrollByLines(0, aForward ? 1 : -1);
915 NS_IMETHODIMP
916 nsTextInputSelectionImpl::ScrollHorizontal(PRBool aLeft)
918 nsIScrollableView *scrollableView = mFrameSelection->GetScrollableView();
919 if (!scrollableView)
920 return NS_ERROR_NOT_INITIALIZED;
922 // will we have bug #7354 because we aren't forcing an update here?
923 return scrollableView->ScrollByLines(aLeft ? -1 : 1, 0);
926 NS_IMETHODIMP
927 nsTextInputSelectionImpl::SelectAll()
929 if (mFrameSelection)
930 return mFrameSelection->SelectAll();
931 return NS_ERROR_NULL_POINTER;
934 NS_IMETHODIMP
935 nsTextInputSelectionImpl::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
937 if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
938 nsresult result;
939 nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak, &result);
940 if (shell)
942 return shell->CheckVisibility(node,startOffset,EndOffset, _retval);
944 return NS_ERROR_FAILURE;
948 nsIFrame*
949 NS_NewTextControlFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
951 return new (aPresShell) nsTextControlFrame(aPresShell, aContext);
954 NS_QUERYFRAME_HEAD(nsTextControlFrame)
955 NS_QUERYFRAME_ENTRY(nsIFormControlFrame)
956 NS_QUERYFRAME_ENTRY(nsIAnonymousContentCreator)
957 NS_QUERYFRAME_ENTRY(nsITextControlFrame)
958 if (nsIScrollableViewProvider::kFrameIID == id && IsScrollable())
959 return static_cast<nsIScrollableViewProvider*>(this);
960 NS_QUERYFRAME_TAIL_INHERITING(nsBoxFrame)
962 #ifdef ACCESSIBILITY
963 NS_IMETHODIMP nsTextControlFrame::GetAccessible(nsIAccessible** aAccessible)
965 nsCOMPtr<nsIAccessibilityService> accService = do_GetService("@mozilla.org/accessibilityService;1");
967 if (accService) {
968 return accService->CreateHTMLTextFieldAccessible(static_cast<nsIFrame*>(this), aAccessible);
971 return NS_ERROR_FAILURE;
973 #endif
975 nsTextControlFrame::nsTextControlFrame(nsIPresShell* aShell, nsStyleContext* aContext)
976 : nsStackFrame(aShell, aContext)
977 , mUseEditor(PR_FALSE)
978 , mIsProcessing(PR_FALSE)
979 , mNotifyOnInput(PR_TRUE)
980 , mDidPreDestroy(PR_FALSE)
981 , mFireChangeEventState(PR_FALSE)
982 , mInSecureKeyboardInputMode(PR_FALSE)
983 , mTextListener(nsnull)
987 nsTextControlFrame::~nsTextControlFrame()
989 NS_IF_RELEASE(mTextListener);
992 static PRBool
993 SuppressEventHandlers(nsPresContext* aPresContext)
995 PRBool suppressHandlers = PR_FALSE;
997 if (aPresContext)
999 // Right now we only suppress event handlers and controller manipulation
1000 // when in a print preview or print context!
1002 // In the current implementation, we only paginate when
1003 // printing or in print preview.
1005 suppressHandlers = aPresContext->IsPaginated();
1008 return suppressHandlers;
1011 void
1012 nsTextControlFrame::PreDestroy()
1014 // notify the editor that we are going away
1015 if (mEditor)
1017 // If we were in charge of state before, relinquish it back
1018 // to the control.
1019 if (mUseEditor)
1021 // First get the frame state from the editor
1022 nsAutoString value;
1023 GetValue(value, PR_TRUE);
1025 mUseEditor = PR_FALSE;
1027 // Next store the frame state in the control
1028 // (now that mUseEditor is false values get stored
1029 // in content).
1030 SetValue(value);
1032 mEditor->PreDestroy(PR_TRUE);
1035 // Clean up the controller
1037 if (!SuppressEventHandlers(PresContext()))
1039 nsCOMPtr<nsIControllers> controllers;
1040 nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement = do_QueryInterface(mContent);
1041 if (inputElement)
1042 inputElement->GetControllers(getter_AddRefs(controllers));
1043 else
1045 nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement = do_QueryInterface(mContent);
1046 if (textAreaElement) {
1047 textAreaElement->GetControllers(getter_AddRefs(controllers));
1051 if (controllers)
1053 PRUint32 numControllers;
1054 nsresult rv = controllers->GetControllerCount(&numControllers);
1055 NS_ASSERTION((NS_SUCCEEDED(rv)), "bad result in gfx text control destructor");
1056 for (PRUint32 i = 0; i < numControllers; i ++)
1058 nsCOMPtr<nsIController> controller;
1059 rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1060 if (NS_SUCCEEDED(rv) && controller)
1062 nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
1063 if (editController)
1065 editController->SetCommandContext(nsnull);
1072 mEditor = nsnull;
1073 mSelCon = nsnull;
1074 if (mFrameSel) {
1075 mFrameSel->SetScrollableViewProvider(nsnull);
1076 mFrameSel->DisconnectFromPresShell();
1077 mFrameSel = nsnull;
1080 nsFormControlFrame::RegUnRegAccessKey(static_cast<nsIFrame*>(this), PR_FALSE);
1081 if (mTextListener)
1083 mTextListener->SetFrame(nsnull);
1085 nsCOMPtr<nsIDOMEventGroup> systemGroup;
1086 mContent->GetSystemEventGroup(getter_AddRefs(systemGroup));
1087 nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mContent);
1088 if (dom3Targ) {
1089 // cast because of ambiguous base
1090 nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
1091 (mTextListener);
1093 dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keydown"),
1094 listener, PR_FALSE, systemGroup);
1095 dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keypress"),
1096 listener, PR_FALSE, systemGroup);
1097 dom3Targ->RemoveGroupedEventListener(NS_LITERAL_STRING("keyup"),
1098 listener, PR_FALSE, systemGroup);
1102 mDidPreDestroy = PR_TRUE;
1105 void
1106 nsTextControlFrame::Destroy()
1108 if (mInSecureKeyboardInputMode) {
1109 MaybeEndSecureKeyboardInput();
1111 if (!mDidPreDestroy) {
1112 PreDestroy();
1114 if (mFrameSel) {
1115 mFrameSel->SetScrollableViewProvider(nsnull);
1117 nsContentUtils::DestroyAnonymousContent(&mAnonymousDiv);
1118 nsBoxFrame::Destroy();
1121 void
1122 nsTextControlFrame::RemovedAsPrimaryFrame()
1124 if (!mDidPreDestroy) {
1125 PreDestroy();
1127 else NS_ASSERTION(PR_FALSE, "RemovedAsPrimaryFrame called after PreDestroy");
1130 nsIAtom*
1131 nsTextControlFrame::GetType() const
1133 return nsGkAtoms::textInputFrame;
1136 // XXX: wouldn't it be nice to get this from the style context!
1137 PRBool nsTextControlFrame::IsSingleLineTextControl() const
1139 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mContent);
1140 if (formControl) {
1141 PRInt32 type = formControl->GetType();
1142 return (type == NS_FORM_INPUT_TEXT) || (type == NS_FORM_INPUT_PASSWORD);
1144 return PR_FALSE;
1147 PRBool nsTextControlFrame::IsTextArea() const
1149 return mContent && mContent->Tag() == nsGkAtoms::textarea;
1152 // XXX: wouldn't it be nice to get this from the style context!
1153 PRBool nsTextControlFrame::IsPlainTextControl() const
1155 // need to check HTML attribute of mContent and/or CSS.
1156 return PR_TRUE;
1159 nsresult nsTextControlFrame::MaybeBeginSecureKeyboardInput()
1161 nsresult rv = NS_OK;
1162 if (IsPasswordTextControl() && !mInSecureKeyboardInputMode) {
1163 nsIWidget* window = GetWindow();
1164 NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
1165 rv = window->BeginSecureKeyboardInput();
1166 mInSecureKeyboardInputMode = NS_SUCCEEDED(rv);
1168 return rv;
1171 void nsTextControlFrame::MaybeEndSecureKeyboardInput()
1173 if (mInSecureKeyboardInputMode) {
1174 nsIWidget* window = GetWindow();
1175 if (!window)
1176 return;
1177 window->EndSecureKeyboardInput();
1178 mInSecureKeyboardInputMode = PR_FALSE;
1182 PRBool nsTextControlFrame::IsPasswordTextControl() const
1184 nsCOMPtr<nsIFormControl> formControl = do_QueryInterface(mContent);
1185 return formControl && formControl->GetType() == NS_FORM_INPUT_PASSWORD;
1189 PRInt32
1190 nsTextControlFrame::GetCols()
1192 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
1193 NS_ASSERTION(content, "Content is not HTML content!");
1195 if (IsTextArea()) {
1196 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::cols);
1197 if (attr) {
1198 PRInt32 cols = attr->Type() == nsAttrValue::eInteger ?
1199 attr->GetIntegerValue() : 0;
1200 // XXX why a default of 1 char, why hide it
1201 return (cols <= 0) ? 1 : cols;
1203 } else {
1204 // Else we know (assume) it is an input with size attr
1205 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::size);
1206 if (attr && attr->Type() == nsAttrValue::eInteger) {
1207 PRInt32 cols = attr->GetIntegerValue();
1208 if (cols > 0) {
1209 return cols;
1214 return DEFAULT_COLS;
1218 PRInt32
1219 nsTextControlFrame::GetRows()
1221 if (IsTextArea()) {
1222 nsGenericHTMLElement *content =
1223 nsGenericHTMLElement::FromContent(mContent);
1224 NS_ASSERTION(content, "Content is not HTML content!");
1226 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::rows);
1227 if (attr && attr->Type() == nsAttrValue::eInteger) {
1228 PRInt32 rows = attr->GetIntegerValue();
1229 return (rows <= 0) ? DEFAULT_ROWS_TEXTAREA : rows;
1231 return DEFAULT_ROWS_TEXTAREA;
1234 return DEFAULT_ROWS;
1238 nsresult
1239 nsTextControlFrame::CalcIntrinsicSize(nsIRenderingContext* aRenderingContext,
1240 nsSize& aIntrinsicSize)
1242 // Get leading and the Average/MaxAdvance char width
1243 nscoord lineHeight = 0;
1244 nscoord charWidth = 0;
1245 nscoord charMaxAdvance = 0;
1247 nsCOMPtr<nsIFontMetrics> fontMet;
1248 nsresult rv =
1249 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
1250 NS_ENSURE_SUCCESS(rv, rv);
1251 aRenderingContext->SetFont(fontMet);
1253 lineHeight =
1254 nsHTMLReflowState::CalcLineHeight(GetStyleContext(), NS_AUTOHEIGHT);
1255 fontMet->GetAveCharWidth(charWidth);
1256 fontMet->GetMaxAdvance(charMaxAdvance);
1258 // Set the width equal to the width in characters
1259 PRInt32 cols = GetCols();
1260 aIntrinsicSize.width = cols * charWidth;
1262 // To better match IE, take the maximum character width(in twips) and remove
1263 // 4 pixels add this on as additional padding(internalPadding). But only do
1264 // this if charMaxAdvance != charWidth; if they are equal, this is almost
1265 // certainly a fixed-width font.
1266 if (charWidth != charMaxAdvance) {
1267 nscoord internalPadding = PR_MAX(0, charMaxAdvance -
1268 nsPresContext::CSSPixelsToAppUnits(4));
1269 nscoord t = nsPresContext::CSSPixelsToAppUnits(1);
1270 // Round to a multiple of t
1271 nscoord rest = internalPadding % t;
1272 if (rest < t - rest) {
1273 internalPadding -= rest;
1274 } else {
1275 internalPadding += t - rest;
1277 // Now add the extra padding on (so that small input sizes work well)
1278 aIntrinsicSize.width += internalPadding;
1279 } else {
1280 // This is to account for the anonymous <br> having a 1 twip width
1281 // in Full Standards mode, see BRFrame::Reflow and bug 228752.
1282 if (PresContext()->CompatibilityMode() == eCompatibility_FullStandards) {
1283 aIntrinsicSize.width += 1;
1286 // Also add in the padding of our anonymous div child. Note that it hasn't
1287 // been reflowed yet, so we can't get its used padding, but it shouldn't be
1288 // using percentage padding anyway.
1289 nsMargin childPadding;
1290 if (GetFirstChild(nsnull)->GetStylePadding()->GetPadding(childPadding)) {
1291 aIntrinsicSize.width += childPadding.LeftRight();
1292 } else {
1293 NS_ERROR("Percentage padding on anonymous div?");
1297 // Increment width with cols * letter-spacing.
1299 const nsStyleCoord& lsCoord = GetStyleText()->mLetterSpacing;
1300 if (eStyleUnit_Coord == lsCoord.GetUnit()) {
1301 nscoord letterSpacing = lsCoord.GetCoordValue();
1302 if (letterSpacing != 0) {
1303 aIntrinsicSize.width += cols * letterSpacing;
1308 // Set the height equal to total number of rows (times the height of each
1309 // line, of course)
1310 aIntrinsicSize.height = lineHeight * GetRows();
1312 // Add in the size of the scrollbars for textarea
1313 if (IsTextArea()) {
1314 nsIFrame* first = GetFirstChild(nsnull);
1316 nsIScrollableFrame *scrollableFrame = do_QueryFrame(first);
1317 NS_ASSERTION(scrollableFrame, "Child must be scrollable");
1319 nsMargin scrollbarSizes =
1320 scrollableFrame->GetDesiredScrollbarSizes(PresContext(), aRenderingContext);
1322 aIntrinsicSize.width += scrollbarSizes.LeftRight();
1324 aIntrinsicSize.height += scrollbarSizes.TopBottom();;
1327 return NS_OK;
1330 void
1331 nsTextControlFrame::DelayedEditorInit()
1333 nsIDocument* doc = mContent->GetCurrentDoc();
1334 if (!doc) {
1335 return;
1338 nsWeakFrame weakFrame(this);
1340 // Flush out content on our document. Have to do this, because script
1341 // blockers don't prevent the sink flushing out content and notifying in the
1342 // process, which can destroy frames.
1343 doc->FlushPendingNotifications(Flush_ContentAndNotify);
1344 if (!weakFrame.IsAlive()) {
1345 return;
1348 // Make sure that editor init doesn't do things that would kill us off
1349 // (especially off the script blockers it'll create for its DOM mutations).
1350 nsAutoScriptBlocker scriptBlocker;
1352 // Time to mess with our security context... See comments in GetValue()
1353 // for why this is needed.
1354 nsCxPusher pusher;
1355 pusher.PushNull();
1357 InitEditor();
1358 if (IsFocusedContent(GetContent()))
1359 SetFocus(PR_TRUE, PR_FALSE);
1362 PRInt32
1363 nsTextControlFrame::GetWrapCols()
1365 if (IsTextArea()) {
1366 // wrap=off means -1 for wrap width no matter what cols is
1367 nsHTMLTextWrap wrapProp;
1368 ::GetWrapPropertyEnum(mContent, wrapProp);
1369 if (wrapProp == eHTMLTextWrap_Off) {
1370 // do not wrap when wrap=off
1371 return -1;
1374 // Otherwise we just wrap at the given number of columns
1375 return GetCols();
1378 // Never wrap non-textareas
1379 return -1;
1382 nsresult
1383 nsTextControlFrame::InitEditor()
1385 // This method initializes our editor, if needed.
1387 // This code used to be called from CreateAnonymousContent(), but
1388 // when the editor set the initial string, it would trigger a
1389 // PresShell listener which called FlushPendingNotifications()
1390 // during frame construction. This was causing other form controls
1391 // to display wrong values. So we call this from a script runner
1392 // now.
1394 // Check if this method has been called already.
1395 // If so, just return early.
1397 if (mUseEditor)
1398 return NS_OK;
1400 // Create an editor
1402 nsresult rv;
1403 mEditor = do_CreateInstance(kTextEditorCID, &rv);
1404 NS_ENSURE_SUCCESS(rv, rv);
1406 // Setup the editor flags
1408 PRUint32 editorFlags = 0;
1409 if (IsPlainTextControl())
1410 editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
1411 if (IsSingleLineTextControl())
1412 editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
1413 if (IsPasswordTextControl())
1414 editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
1416 // All nsTextControlFrames are widgets
1417 editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
1419 // Use async reflow and painting for text widgets to improve
1420 // performance.
1422 // XXX: Using editor async updates exposes bugs 158782, 151882,
1423 // and 165130, so we're disabling it for now, until they
1424 // can be addressed.
1425 // editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
1427 // Now initialize the editor.
1429 // NOTE: Conversion of '\n' to <BR> happens inside the
1430 // editor's Init() call.
1432 nsPresContext *presContext = PresContext();
1433 nsIPresShell *shell = presContext->GetPresShell();
1435 // Get the DOM document
1436 nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
1437 if (!domdoc)
1438 return NS_ERROR_FAILURE;
1440 rv = mEditor->Init(domdoc, shell, mAnonymousDiv, mSelCon, editorFlags);
1441 NS_ENSURE_SUCCESS(rv, rv);
1443 // Initialize the controller for the editor
1445 if (!SuppressEventHandlers(presContext)) {
1446 nsCOMPtr<nsIControllers> controllers;
1447 nsCOMPtr<nsIDOMNSHTMLInputElement> inputElement =
1448 do_QueryInterface(mContent);
1449 if (inputElement) {
1450 rv = inputElement->GetControllers(getter_AddRefs(controllers));
1451 } else {
1452 nsCOMPtr<nsIDOMNSHTMLTextAreaElement> textAreaElement =
1453 do_QueryInterface(mContent);
1455 if (!textAreaElement)
1456 return NS_ERROR_FAILURE;
1458 rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
1461 if (NS_FAILED(rv))
1462 return rv;
1464 if (controllers) {
1465 PRUint32 numControllers;
1466 PRBool found = PR_FALSE;
1467 rv = controllers->GetControllerCount(&numControllers);
1468 for (PRUint32 i = 0; i < numControllers; i ++) {
1469 nsCOMPtr<nsIController> controller;
1470 rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1471 if (NS_SUCCEEDED(rv) && controller) {
1472 nsCOMPtr<nsIControllerContext> editController =
1473 do_QueryInterface(controller);
1474 if (editController) {
1475 editController->SetCommandContext(mEditor);
1476 found = PR_TRUE;
1480 if (!found)
1481 rv = NS_ERROR_FAILURE;
1485 // Initialize the plaintext editor
1486 nsCOMPtr<nsIPlaintextEditor> textEditor(do_QueryInterface(mEditor));
1487 if (textEditor) {
1488 // Set up wrapping
1489 textEditor->SetWrapColumn(GetWrapCols());
1491 // Set max text field length
1492 PRInt32 maxLength;
1493 if (GetMaxLength(&maxLength)) {
1494 textEditor->SetMaxTextLength(maxLength);
1498 if (mContent) {
1499 rv = mEditor->GetFlags(&editorFlags);
1501 if (NS_FAILED(rv))
1502 return nsnull;
1504 // Check if the readonly attribute is set.
1506 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
1507 editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
1509 // Check if the disabled attribute is set.
1511 if (mContent->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled))
1512 editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
1514 // Disable the selection if necessary.
1516 if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
1517 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
1519 mEditor->SetFlags(editorFlags);
1522 // Get the current value of the textfield from the content.
1523 nsAutoString defaultValue;
1524 GetValue(defaultValue, PR_TRUE);
1526 // Turn on mUseEditor so that subsequent calls will use the
1527 // editor.
1528 mUseEditor = PR_TRUE;
1530 // If we have a default value, insert it under the div we created
1531 // above, but be sure to use the editor so that '*' characters get
1532 // displayed for password fields, etc. SetValue() will call the
1533 // editor for us.
1535 if (!defaultValue.IsEmpty()) {
1536 // Avoid causing reentrant painting and reflowing by telling the editor
1537 // that we don't want it to force immediate view refreshes or force
1538 // immediate reflows during any editor calls.
1540 rv = mEditor->SetFlags(editorFlags |
1541 nsIPlaintextEditor::eEditorUseAsyncUpdatesMask);
1543 if (NS_FAILED(rv))
1544 return rv;
1546 // Now call SetValue() which will make the necessary editor calls to set
1547 // the default value. Make sure to turn off undo before setting the default
1548 // value, and turn it back on afterwards. This will make sure we can't undo
1549 // past the default value.
1551 rv = mEditor->EnableUndo(PR_FALSE);
1553 if (NS_FAILED(rv))
1554 return rv;
1556 SetValue(defaultValue);
1558 rv = mEditor->EnableUndo(PR_TRUE);
1559 NS_ASSERTION(NS_SUCCEEDED(rv),"Transaction Manager must have failed");
1561 // Now restore the original editor flags.
1562 rv = mEditor->SetFlags(editorFlags);
1564 if (NS_FAILED(rv))
1565 return rv;
1568 nsCOMPtr<nsITransactionManager> transMgr;
1569 mEditor->GetTransactionManager(getter_AddRefs(transMgr));
1570 NS_ENSURE_TRUE(transMgr, NS_ERROR_FAILURE);
1572 transMgr->SetMaxTransactionCount(DEFAULT_UNDO_CAP);
1574 if (IsPasswordTextControl()) {
1575 // Disable undo for password textfields. Note that we want to do this at
1576 // the very end of InitEditor, so the calls to EnableUndo when setting the
1577 // default value don't screw us up.
1578 // Since changing the control type does a reframe, we don't have to worry
1579 // about dynamic type changes here.
1580 mEditor->EnableUndo(PR_FALSE);
1583 mEditor->PostCreate();
1585 return NS_OK;
1588 nsresult
1589 nsTextControlFrame::CreateAnonymousContent(nsTArray<nsIContent*>& aElements)
1591 mState |= NS_FRAME_INDEPENDENT_SELECTION;
1593 nsIPresShell* shell = PresContext()->GetPresShell();
1594 if (!shell)
1595 return NS_ERROR_FAILURE;
1597 nsIDocument *doc = shell->GetDocument();
1598 if (!doc)
1599 return NS_ERROR_FAILURE;
1601 // Now create a DIV and add it to the anonymous content child list.
1602 nsCOMPtr<nsINodeInfo> nodeInfo;
1603 nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nsnull,
1604 kNameSpaceID_XHTML);
1605 NS_ENSURE_TRUE(nodeInfo, NS_ERROR_OUT_OF_MEMORY);
1607 nsresult rv = NS_NewHTMLElement(getter_AddRefs(mAnonymousDiv), nodeInfo, PR_FALSE);
1608 NS_ENSURE_SUCCESS(rv, rv);
1610 // Set the div native anonymous, so CSS will be its style language
1611 // no matter what. We need to do this before we set the 'style' attribute.
1612 mAnonymousDiv->SetNativeAnonymous();
1614 // Set the necessary style attributes on the text control.
1616 rv = mAnonymousDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
1617 NS_LITERAL_STRING("anonymous-div"), PR_FALSE);
1618 NS_ENSURE_SUCCESS(rv, rv);
1620 nsAutoString styleValue;
1621 PRInt32 wrapCols = GetWrapCols();
1622 if (wrapCols >= 0) {
1623 styleValue.AppendLiteral("white-space:pre-wrap");
1624 } else {
1625 styleValue.AppendLiteral("white-space:pre");
1627 if (!IsSingleLineTextControl()) {
1628 // We can't just inherit the overflow because setting visible overflow will
1629 // crash when the number of lines exceeds the height of the textarea and
1630 // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
1631 // doesn't paint the caret for some reason.
1632 const nsStyleDisplay* disp = GetStyleDisplay();
1633 if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
1634 disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
1635 styleValue.AppendLiteral(";overflow:inherit");
1638 rv = mAnonymousDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::style,
1639 styleValue, PR_FALSE);
1640 NS_ENSURE_SUCCESS(rv, rv);
1642 if (!aElements.AppendElement(mAnonymousDiv))
1643 return NS_ERROR_OUT_OF_MEMORY;
1645 // Create selection
1647 mFrameSel = do_CreateInstance(kFrameSelectionCID, &rv);
1648 if (NS_FAILED(rv))
1649 return rv;
1650 mFrameSel->SetScrollableViewProvider(this);
1652 // Create a SelectionController
1654 mSelCon = static_cast<nsISelectionController*>
1655 (new nsTextInputSelectionImpl(mFrameSel, shell,
1656 mAnonymousDiv));
1657 if (!mSelCon)
1658 return NS_ERROR_OUT_OF_MEMORY;
1659 mTextListener = new nsTextInputListener();
1660 if (!mTextListener)
1661 return NS_ERROR_OUT_OF_MEMORY;
1662 NS_ADDREF(mTextListener);
1664 mTextListener->SetFrame(this);
1665 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1667 // Get the caret and make it a selection listener.
1669 nsRefPtr<nsISelection> domSelection;
1670 if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1671 getter_AddRefs(domSelection))) &&
1672 domSelection) {
1673 nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
1674 nsRefPtr<nsCaret> caret;
1675 nsCOMPtr<nsISelectionListener> listener;
1676 if (NS_SUCCEEDED(shell->GetCaret(getter_AddRefs(caret))) && caret) {
1677 listener = do_QueryInterface(caret);
1678 if (listener) {
1679 selPriv->AddSelectionListener(listener);
1683 selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
1684 (mTextListener));
1687 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1688 "Someone forgot a script blocker?");
1690 if (!nsContentUtils::AddScriptRunner(new EditorInitializer(this))) {
1691 return NS_ERROR_OUT_OF_MEMORY;
1694 return NS_OK;
1697 nscoord
1698 nsTextControlFrame::GetMinWidth(nsIRenderingContext* aRenderingContext)
1700 // Our min width is just our preferred width if we have auto width.
1701 nscoord result;
1702 DISPLAY_MIN_WIDTH(this, result);
1704 result = GetPrefWidth(aRenderingContext);
1706 return result;
1709 nsSize
1710 nsTextControlFrame::ComputeAutoSize(nsIRenderingContext *aRenderingContext,
1711 nsSize aCBSize, nscoord aAvailableWidth,
1712 nsSize aMargin, nsSize aBorder,
1713 nsSize aPadding, PRBool aShrinkWrap)
1715 nsSize autoSize;
1716 nsresult rv = CalcIntrinsicSize(aRenderingContext, autoSize);
1717 if (NS_FAILED(rv)) {
1718 // What now?
1719 autoSize.SizeTo(0, 0);
1721 #ifdef DEBUG
1722 // Note: Ancestor ComputeAutoSize only computes a width if we're auto-width
1723 else if (GetStylePosition()->mWidth.GetUnit() == eStyleUnit_Auto) {
1724 nsSize ancestorAutoSize =
1725 nsStackFrame::ComputeAutoSize(aRenderingContext,
1726 aCBSize, aAvailableWidth,
1727 aMargin, aBorder,
1728 aPadding, aShrinkWrap);
1729 NS_ASSERTION(ancestorAutoSize.width == autoSize.width,
1730 "Incorrect size computed by ComputeAutoSize?");
1732 #endif
1734 return autoSize;
1738 // We inherit our GetPrefWidth from nsBoxFrame
1740 NS_IMETHODIMP
1741 nsTextControlFrame::Reflow(nsPresContext* aPresContext,
1742 nsHTMLReflowMetrics& aDesiredSize,
1743 const nsHTMLReflowState& aReflowState,
1744 nsReflowStatus& aStatus)
1746 DO_GLOBAL_REFLOW_COUNT("nsTextControlFrame");
1747 DISPLAY_REFLOW(aPresContext, this, aReflowState, aDesiredSize, aStatus);
1749 // make sure the the form registers itself on the initial/first reflow
1750 if (mState & NS_FRAME_FIRST_REFLOW) {
1751 nsFormControlFrame::RegUnRegAccessKey(this, PR_TRUE);
1754 return nsStackFrame::Reflow(aPresContext, aDesiredSize, aReflowState,
1755 aStatus);
1758 nsSize
1759 nsTextControlFrame::GetPrefSize(nsBoxLayoutState& aState)
1761 if (!DoesNeedRecalc(mPrefSize))
1762 return mPrefSize;
1764 #ifdef DEBUG_LAYOUT
1765 PropagateDebug(aState);
1766 #endif
1768 nsSize pref(0,0);
1770 nsresult rv = CalcIntrinsicSize(aState.GetRenderingContext(), pref);
1771 NS_ENSURE_SUCCESS(rv, pref);
1772 AddBorderAndPadding(pref);
1774 mPrefSize = pref;
1776 #ifdef DEBUG_rods
1778 nsMargin borderPadding(0,0,0,0);
1779 GetBorderAndPadding(borderPadding);
1780 nsSize size(169, 24);
1781 nsSize actual(pref.width/15,
1782 pref.height/15);
1783 printf("nsGfxText(field) %d,%d %d,%d %d,%d\n",
1784 size.width, size.height, actual.width, actual.height, actual.width-size.width, actual.height-size.height); // text field
1786 #endif
1788 return pref;
1791 nsSize
1792 nsTextControlFrame::GetMinSize(nsBoxLayoutState& aState)
1794 // XXXbz why? Why not the nsBoxFrame sizes?
1795 return nsBox::GetMinSize(aState);
1798 nsSize
1799 nsTextControlFrame::GetMaxSize(nsBoxLayoutState& aState)
1801 // XXXbz why? Why not the nsBoxFrame sizes?
1802 return nsBox::GetMaxSize(aState);
1805 nscoord
1806 nsTextControlFrame::GetBoxAscent(nsBoxLayoutState& aState)
1808 // Return the baseline of the first (nominal) row, with centering for
1809 // single-line controls.
1811 // First calculate the ascent wrt the client rect
1812 nsRect clientRect;
1813 GetClientRect(clientRect);
1814 nscoord lineHeight =
1815 IsSingleLineTextControl() ? clientRect.height :
1816 nsHTMLReflowState::CalcLineHeight(GetStyleContext(), NS_AUTOHEIGHT);
1818 nsCOMPtr<nsIFontMetrics> fontMet;
1819 nsresult rv =
1820 nsLayoutUtils::GetFontMetricsForFrame(this, getter_AddRefs(fontMet));
1821 NS_ENSURE_SUCCESS(rv, 0);
1823 nscoord ascent = nsLayoutUtils::GetCenteredFontBaseline(fontMet, lineHeight);
1825 // Now adjust for our borders and padding
1826 ascent += clientRect.y;
1828 return ascent;
1831 PRBool
1832 nsTextControlFrame::IsCollapsed(nsBoxLayoutState& aBoxLayoutState)
1834 // We're never collapsed in the box sense.
1835 return PR_FALSE;
1838 PRBool
1839 nsTextControlFrame::IsLeaf() const
1841 return PR_TRUE;
1844 //IMPLEMENTING NS_IFORMCONTROLFRAME
1845 void nsTextControlFrame::SetFocus(PRBool aOn, PRBool aRepaint)
1847 nsCOMPtr<nsIEditor> editor;
1848 GetEditor(getter_AddRefs(editor));
1850 if (!aOn) {
1851 if (editor)
1852 editor->RemoveEditorObserver(mTextListener);
1854 MaybeEndSecureKeyboardInput();
1855 return;
1858 if (!mSelCon)
1859 return;
1861 if (editor)
1862 editor->AddEditorObserver(mTextListener);
1864 if (NS_SUCCEEDED(InitFocusedValue()))
1865 MaybeBeginSecureKeyboardInput();
1867 // tell the caret to use our selection
1869 nsCOMPtr<nsISelection> ourSel;
1870 mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1871 getter_AddRefs(ourSel));
1872 if (!ourSel) return;
1874 nsIPresShell* presShell = PresContext()->GetPresShell();
1875 nsRefPtr<nsCaret> caret;
1876 presShell->GetCaret(getter_AddRefs(caret));
1877 if (!caret) return;
1878 caret->SetCaretDOMSelection(ourSel);
1880 // mutual-exclusion: the selection is either controlled by the
1881 // document or by the text input/area. Clear any selection in the
1882 // document since the focus is now on our independent selection.
1884 nsCOMPtr<nsISelectionController> selCon(do_QueryInterface(presShell));
1885 nsCOMPtr<nsISelection> docSel;
1886 selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1887 getter_AddRefs(docSel));
1888 if (!docSel) return;
1890 PRBool isCollapsed = PR_FALSE;
1891 docSel->GetIsCollapsed(&isCollapsed);
1892 if (!isCollapsed)
1893 docSel->RemoveAllRanges();
1896 nsresult nsTextControlFrame::SetFormProperty(nsIAtom* aName, const nsAString& aValue)
1898 if (!mIsProcessing)//some kind of lock.
1900 mIsProcessing = PR_TRUE;
1901 PRBool isUserInput = (nsGkAtoms::userInput == aName);
1902 if (nsGkAtoms::value == aName || isUserInput)
1904 PRBool fireChangeEvent = GetFireChangeEventState();
1905 if (isUserInput) {
1906 SetFireChangeEventState(PR_TRUE);
1908 SetValueChanged(PR_TRUE);
1909 nsresult rv = SetValue(aValue); // set new text value
1910 if (isUserInput) {
1911 SetFireChangeEventState(fireChangeEvent);
1913 NS_ENSURE_SUCCESS(rv, rv);
1915 else if (nsGkAtoms::select == aName)
1917 // Select all the text.
1919 // XXX: This is lame, we can't call mEditor->SelectAll()
1920 // because that triggers AutoCopies in unix builds.
1921 // Instead, we have to call our own homegrown version
1922 // of select all which merely builds a range that selects
1923 // all of the content and adds that to the selection.
1925 SelectAllContents();
1927 mIsProcessing = PR_FALSE;
1929 return NS_OK;
1932 nsresult
1933 nsTextControlFrame::GetFormProperty(nsIAtom* aName, nsAString& aValue) const
1935 // Return the value of the property from the widget it is not null.
1936 // If widget is null, assume the widget is GFX-rendered and return a member variable instead.
1938 if (nsGkAtoms::value == aName) {
1939 GetValue(aValue, PR_FALSE);
1941 return NS_OK;
1946 NS_IMETHODIMP
1947 nsTextControlFrame::GetEditor(nsIEditor **aEditor)
1949 NS_ENSURE_ARG_POINTER(aEditor);
1950 *aEditor = mEditor;
1951 NS_IF_ADDREF(*aEditor);
1952 return NS_OK;
1955 NS_IMETHODIMP
1956 nsTextControlFrame::OwnsValue(PRBool* aOwnsValue)
1958 NS_PRECONDITION(aOwnsValue, "aOwnsValue must be non-null");
1959 *aOwnsValue = mUseEditor;
1960 return NS_OK;
1963 NS_IMETHODIMP
1964 nsTextControlFrame::GetTextLength(PRInt32* aTextLength)
1966 NS_ENSURE_ARG_POINTER(aTextLength);
1968 nsAutoString textContents;
1969 GetValue(textContents, PR_FALSE); // this is expensive!
1970 *aTextLength = textContents.Length();
1971 return NS_OK;
1974 nsresult
1975 nsTextControlFrame::SetSelectionInternal(nsIDOMNode *aStartNode,
1976 PRInt32 aStartOffset,
1977 nsIDOMNode *aEndNode,
1978 PRInt32 aEndOffset)
1980 // Create a new range to represent the new selection.
1981 // Note that we use a new range to avoid having to do
1982 // isIncreasing checks to avoid possible errors.
1984 nsCOMPtr<nsIDOMRange> range = do_CreateInstance(kRangeCID);
1985 NS_ENSURE_TRUE(range, NS_ERROR_FAILURE);
1987 nsresult rv = range->SetStart(aStartNode, aStartOffset);
1988 NS_ENSURE_SUCCESS(rv, rv);
1990 rv = range->SetEnd(aEndNode, aEndOffset);
1991 NS_ENSURE_SUCCESS(rv, rv);
1993 // Get the selection, clear it and add the new range to it!
1995 nsCOMPtr<nsISelection> selection;
1996 mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
1997 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
1999 rv = selection->RemoveAllRanges();
2001 NS_ENSURE_SUCCESS(rv, rv);
2003 return selection->AddRange(range);
2006 nsresult
2007 nsTextControlFrame::SelectAllContents()
2009 if (!mEditor)
2010 return NS_OK;
2012 nsCOMPtr<nsIDOMElement> rootElement;
2013 nsresult rv = mEditor->GetRootElement(getter_AddRefs(rootElement));
2014 NS_ENSURE_SUCCESS(rv, rv);
2016 nsCOMPtr<nsIContent> rootContent = do_QueryInterface(rootElement);
2017 PRInt32 numChildren = rootContent->GetChildCount();
2019 if (numChildren > 0) {
2020 // We never want to place the selection after the last
2021 // br under the root node!
2022 nsIContent *child = rootContent->GetChildAt(numChildren - 1);
2023 if (child) {
2024 if (child->Tag() == nsGkAtoms::br)
2025 --numChildren;
2029 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
2031 return SetSelectionInternal(rootNode, 0, rootNode, numChildren);
2034 nsresult
2035 nsTextControlFrame::SetSelectionEndPoints(PRInt32 aSelStart, PRInt32 aSelEnd)
2037 NS_ASSERTION(aSelStart <= aSelEnd, "Invalid selection offsets!");
2039 if (aSelStart > aSelEnd)
2040 return NS_ERROR_FAILURE;
2042 nsCOMPtr<nsIDOMNode> startNode, endNode;
2043 PRInt32 startOffset, endOffset;
2045 // Calculate the selection start point.
2047 nsresult rv = OffsetToDOMPoint(aSelStart, getter_AddRefs(startNode), &startOffset);
2049 NS_ENSURE_SUCCESS(rv, rv);
2051 if (aSelStart == aSelEnd) {
2052 // Collapsed selection, so start and end are the same!
2053 endNode = startNode;
2054 endOffset = startOffset;
2056 else {
2057 // Selection isn't collapsed so we have to calculate
2058 // the end point too.
2060 rv = OffsetToDOMPoint(aSelEnd, getter_AddRefs(endNode), &endOffset);
2062 NS_ENSURE_SUCCESS(rv, rv);
2065 return SetSelectionInternal(startNode, startOffset, endNode, endOffset);
2068 NS_IMETHODIMP
2069 nsTextControlFrame::SetSelectionRange(PRInt32 aSelStart, PRInt32 aSelEnd)
2071 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2073 if (aSelStart > aSelEnd) {
2074 // Simulate what we'd see SetSelectionStart() was called, followed
2075 // by a SetSelectionEnd().
2077 aSelStart = aSelEnd;
2080 return SetSelectionEndPoints(aSelStart, aSelEnd);
2084 NS_IMETHODIMP
2085 nsTextControlFrame::SetSelectionStart(PRInt32 aSelectionStart)
2087 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2089 PRInt32 selStart = 0, selEnd = 0;
2091 nsresult rv = GetSelectionRange(&selStart, &selEnd);
2092 NS_ENSURE_SUCCESS(rv, rv);
2094 if (aSelectionStart > selEnd) {
2095 // Collapse to the new start point.
2096 selEnd = aSelectionStart;
2099 selStart = aSelectionStart;
2101 return SetSelectionEndPoints(selStart, selEnd);
2104 NS_IMETHODIMP
2105 nsTextControlFrame::SetSelectionEnd(PRInt32 aSelectionEnd)
2107 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2109 PRInt32 selStart = 0, selEnd = 0;
2111 nsresult rv = GetSelectionRange(&selStart, &selEnd);
2112 NS_ENSURE_SUCCESS(rv, rv);
2114 if (aSelectionEnd < selStart) {
2115 // Collapse to the new end point.
2116 selStart = aSelectionEnd;
2119 selEnd = aSelectionEnd;
2121 return SetSelectionEndPoints(selStart, selEnd);
2124 nsresult
2125 nsTextControlFrame::DOMPointToOffset(nsIDOMNode* aNode,
2126 PRInt32 aNodeOffset,
2127 PRInt32* aResult)
2129 NS_ENSURE_ARG_POINTER(aNode && aResult);
2131 *aResult = 0;
2133 nsCOMPtr<nsIDOMElement> rootElement;
2134 mEditor->GetRootElement(getter_AddRefs(rootElement));
2135 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
2137 NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
2139 nsCOMPtr<nsIDOMNodeList> nodeList;
2141 nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
2142 NS_ENSURE_SUCCESS(rv, rv);
2143 NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
2145 PRUint32 length = 0;
2146 rv = nodeList->GetLength(&length);
2147 NS_ENSURE_SUCCESS(rv, rv);
2149 if (!length || aNodeOffset < 0)
2150 return NS_OK;
2152 PRInt32 i, textOffset = 0;
2153 PRInt32 lastIndex = (PRInt32)length - 1;
2155 for (i = 0; i < (PRInt32)length; i++) {
2156 if (rootNode == aNode && i == aNodeOffset) {
2157 *aResult = textOffset;
2158 return NS_OK;
2161 nsCOMPtr<nsIDOMNode> item;
2162 rv = nodeList->Item(i, getter_AddRefs(item));
2163 NS_ENSURE_SUCCESS(rv, rv);
2164 NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
2166 nsCOMPtr<nsIDOMText> domText(do_QueryInterface(item));
2168 if (domText) {
2169 PRUint32 textLength = 0;
2171 rv = domText->GetLength(&textLength);
2172 NS_ENSURE_SUCCESS(rv, rv);
2174 if (item == aNode) {
2175 NS_ASSERTION((aNodeOffset >= 0 && aNodeOffset <= (PRInt32)textLength),
2176 "Invalid aNodeOffset!");
2177 *aResult = textOffset + aNodeOffset;
2178 return NS_OK;
2181 textOffset += textLength;
2183 else {
2184 // Must be a BR node. If it's not the last BR node
2185 // under the root, count it as a newline.
2187 if (i != lastIndex)
2188 ++textOffset;
2192 NS_ASSERTION((aNode == rootNode && aNodeOffset == (PRInt32)length),
2193 "Invalid node offset!");
2195 *aResult = textOffset;
2197 return NS_OK;
2200 nsresult
2201 nsTextControlFrame::OffsetToDOMPoint(PRInt32 aOffset,
2202 nsIDOMNode** aResult,
2203 PRInt32* aPosition)
2205 NS_ENSURE_ARG_POINTER(aResult && aPosition);
2207 *aResult = nsnull;
2208 *aPosition = 0;
2210 nsCOMPtr<nsIDOMElement> rootElement;
2211 mEditor->GetRootElement(getter_AddRefs(rootElement));
2212 nsCOMPtr<nsIDOMNode> rootNode(do_QueryInterface(rootElement));
2214 NS_ENSURE_TRUE(rootNode, NS_ERROR_FAILURE);
2216 nsCOMPtr<nsIDOMNodeList> nodeList;
2218 nsresult rv = rootNode->GetChildNodes(getter_AddRefs(nodeList));
2219 NS_ENSURE_SUCCESS(rv, rv);
2220 NS_ENSURE_TRUE(nodeList, NS_ERROR_FAILURE);
2222 PRUint32 length = 0;
2224 rv = nodeList->GetLength(&length);
2225 NS_ENSURE_SUCCESS(rv, rv);
2227 if (!length || aOffset < 0) {
2228 *aPosition = 0;
2229 *aResult = rootNode;
2230 NS_ADDREF(*aResult);
2231 return NS_OK;
2234 PRInt32 textOffset = 0;
2235 PRUint32 lastIndex = length - 1;
2237 for (PRUint32 i=0; i<length; i++) {
2238 nsCOMPtr<nsIDOMNode> item;
2239 rv = nodeList->Item(i, getter_AddRefs(item));
2240 NS_ENSURE_SUCCESS(rv, rv);
2241 NS_ENSURE_TRUE(item, NS_ERROR_FAILURE);
2243 nsCOMPtr<nsIDOMText> domText(do_QueryInterface(item));
2245 if (domText) {
2246 PRUint32 textLength = 0;
2248 rv = domText->GetLength(&textLength);
2249 NS_ENSURE_SUCCESS(rv, rv);
2251 // Check if aOffset falls within this range.
2252 if (aOffset >= textOffset && aOffset <= textOffset+(PRInt32)textLength) {
2253 *aPosition = aOffset - textOffset;
2254 *aResult = item;
2255 NS_ADDREF(*aResult);
2256 return NS_OK;
2259 textOffset += textLength;
2261 // If there aren't any more siblings after this text node,
2262 // return the point at the end of this text node!
2264 if (i == lastIndex) {
2265 *aPosition = textLength;
2266 *aResult = item;
2267 NS_ADDREF(*aResult);
2268 return NS_OK;
2271 else {
2272 // Must be a BR node, count it as a newline.
2274 if (aOffset == textOffset || i == lastIndex) {
2275 // We've found the correct position, or aOffset takes us
2276 // beyond the last child under rootNode, just return the point
2277 // under rootNode that is in front of this br.
2279 *aPosition = i;
2280 *aResult = rootNode;
2281 NS_ADDREF(*aResult);
2282 return NS_OK;
2285 ++textOffset;
2289 NS_ASSERTION(0, "We should never get here!");
2291 return NS_ERROR_FAILURE;
2294 NS_IMETHODIMP
2295 nsTextControlFrame::GetSelectionRange(PRInt32* aSelectionStart, PRInt32* aSelectionEnd)
2297 // make sure we have an editor
2298 NS_ENSURE_TRUE(mEditor, NS_ERROR_NOT_INITIALIZED);
2300 *aSelectionStart = 0;
2301 *aSelectionEnd = 0;
2303 nsCOMPtr<nsISelection> selection;
2304 nsresult rv = mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(selection));
2305 NS_ENSURE_SUCCESS(rv, rv);
2306 NS_ENSURE_TRUE(selection, NS_ERROR_FAILURE);
2308 PRInt32 numRanges = 0;
2309 selection->GetRangeCount(&numRanges);
2311 if (numRanges < 1)
2312 return NS_OK;
2314 // We only operate on the first range in the selection!
2316 nsCOMPtr<nsIDOMRange> firstRange;
2317 rv = selection->GetRangeAt(0, getter_AddRefs(firstRange));
2318 NS_ENSURE_SUCCESS(rv, rv);
2319 NS_ENSURE_TRUE(firstRange, NS_ERROR_FAILURE);
2321 nsCOMPtr<nsIDOMNode> startNode, endNode;
2322 PRInt32 startOffset = 0, endOffset = 0;
2324 // Get the start point of the range.
2326 rv = firstRange->GetStartContainer(getter_AddRefs(startNode));
2327 NS_ENSURE_SUCCESS(rv, rv);
2328 NS_ENSURE_TRUE(startNode, NS_ERROR_FAILURE);
2330 rv = firstRange->GetStartOffset(&startOffset);
2331 NS_ENSURE_SUCCESS(rv, rv);
2333 // Get the end point of the range.
2335 rv = firstRange->GetEndContainer(getter_AddRefs(endNode));
2336 NS_ENSURE_SUCCESS(rv, rv);
2337 NS_ENSURE_TRUE(endNode, NS_ERROR_FAILURE);
2339 rv = firstRange->GetEndOffset(&endOffset);
2340 NS_ENSURE_SUCCESS(rv, rv);
2342 // Convert the start point to a selection offset.
2344 rv = DOMPointToOffset(startNode, startOffset, aSelectionStart);
2345 NS_ENSURE_SUCCESS(rv, rv);
2347 // Convert the end point to a selection offset.
2349 return DOMPointToOffset(endNode, endOffset, aSelectionEnd);
2352 /////END INTERFACE IMPLEMENTATIONS
2354 ////NSIFRAME
2355 NS_IMETHODIMP
2356 nsTextControlFrame::AttributeChanged(PRInt32 aNameSpaceID,
2357 nsIAtom* aAttribute,
2358 PRInt32 aModType)
2360 if (!mEditor || !mSelCon)
2361 return nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);;
2363 nsresult rv = NS_OK;
2365 if (nsGkAtoms::maxlength == aAttribute)
2367 PRInt32 maxLength;
2368 PRBool maxDefined = GetMaxLength(&maxLength);
2370 nsCOMPtr<nsIPlaintextEditor> textEditor = do_QueryInterface(mEditor);
2371 if (textEditor)
2373 if (maxDefined)
2374 { // set the maxLength attribute
2375 textEditor->SetMaxTextLength(maxLength);
2376 // if maxLength>docLength, we need to truncate the doc content
2378 else { // unset the maxLength attribute
2379 textEditor->SetMaxTextLength(-1);
2382 rv = NS_OK; // don't propagate the error
2384 else if (nsGkAtoms::readonly == aAttribute)
2386 PRUint32 flags;
2387 mEditor->GetFlags(&flags);
2388 if (AttributeExists(nsGkAtoms::readonly))
2389 { // set readonly
2390 flags |= nsIPlaintextEditor::eEditorReadonlyMask;
2391 if (IsFocusedContent(mContent))
2392 mSelCon->SetCaretEnabled(PR_FALSE);
2394 else
2395 { // unset readonly
2396 flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
2397 if (!(flags & nsIPlaintextEditor::eEditorDisabledMask) &&
2398 IsFocusedContent(mContent))
2399 mSelCon->SetCaretEnabled(PR_TRUE);
2401 mEditor->SetFlags(flags);
2403 else if (nsGkAtoms::disabled == aAttribute)
2405 PRUint32 flags;
2406 mEditor->GetFlags(&flags);
2407 if (AttributeExists(nsGkAtoms::disabled))
2408 { // set disabled
2409 flags |= nsIPlaintextEditor::eEditorDisabledMask;
2410 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
2411 if (IsFocusedContent(mContent))
2412 mSelCon->SetCaretEnabled(PR_FALSE);
2414 else
2415 { // unset disabled
2416 flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
2417 mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
2419 mEditor->SetFlags(flags);
2421 // Allow the base class to handle common attributes supported
2422 // by all form elements...
2423 else {
2424 rv = nsBoxFrame::AttributeChanged(aNameSpaceID, aAttribute, aModType);
2427 return rv;
2431 NS_IMETHODIMP
2432 nsTextControlFrame::GetText(nsString* aText)
2434 nsresult rv = NS_OK;
2435 if (IsSingleLineTextControl()) {
2436 // If we're going to remove newlines anyway, ignore the wrap property
2437 GetValue(*aText, PR_TRUE);
2438 RemoveNewlines(*aText);
2439 } else {
2440 nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea = do_QueryInterface(mContent);
2441 if (textArea) {
2442 rv = textArea->GetValue(*aText);
2445 return rv;
2449 nsresult
2450 nsTextControlFrame::GetPhonetic(nsAString& aPhonetic)
2452 aPhonetic.Truncate(0);
2453 if (!mEditor)
2454 return NS_ERROR_NOT_INITIALIZED;
2455 nsCOMPtr<nsIEditorIMESupport> imeSupport = do_QueryInterface(mEditor);
2456 if (imeSupport) {
2457 nsCOMPtr<nsIPhonetic> phonetic = do_QueryInterface(imeSupport);
2458 if (phonetic)
2459 phonetic->GetPhonetic(aPhonetic);
2461 return NS_OK;
2464 ///END NSIFRAME OVERLOADS
2465 /////BEGIN PROTECTED METHODS
2467 void nsTextControlFrame::RemoveNewlines(nsString &aString)
2469 // strip CR/LF and null
2470 static const char badChars[] = {10, 13, 0};
2471 aString.StripChars(badChars);
2475 PRBool
2476 nsTextControlFrame::GetMaxLength(PRInt32* aSize)
2478 *aSize = -1;
2480 nsGenericHTMLElement *content = nsGenericHTMLElement::FromContent(mContent);
2481 if (content) {
2482 const nsAttrValue* attr = content->GetParsedAttr(nsGkAtoms::maxlength);
2483 if (attr && attr->Type() == nsAttrValue::eInteger) {
2484 *aSize = attr->GetIntegerValue();
2486 return PR_TRUE;
2489 return PR_FALSE;
2492 // this is where we propagate a content changed event
2493 void
2494 nsTextControlFrame::FireOnInput()
2496 if (!mNotifyOnInput)
2497 return; // if notification is turned off, do nothing
2499 // Dispatch the "input" event
2500 nsEventStatus status = nsEventStatus_eIgnore;
2501 nsUIEvent event(PR_TRUE, NS_FORM_INPUT, 0);
2503 // Have the content handle the event, propagating it according to normal
2504 // DOM rules.
2505 nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
2506 shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
2509 nsresult
2510 nsTextControlFrame::InitFocusedValue()
2512 return GetText(&mFocusedValue);
2515 NS_IMETHODIMP
2516 nsTextControlFrame::CheckFireOnChange()
2518 nsString value;
2519 GetText(&value);
2520 if (!mFocusedValue.Equals(value))
2522 mFocusedValue = value;
2523 // Dispatch the change event
2524 nsEventStatus status = nsEventStatus_eIgnore;
2525 nsInputEvent event(PR_TRUE, NS_FORM_CHANGE, nsnull);
2526 nsCOMPtr<nsIPresShell> shell = PresContext()->PresShell();
2527 shell->HandleEventWithTarget(&event, nsnull, mContent, &status);
2529 return NS_OK;
2532 //======
2533 //privates
2535 NS_IMETHODIMP
2536 nsTextControlFrame::GetValue(nsAString& aValue, PRBool aIgnoreWrap) const
2538 aValue.Truncate(); // initialize out param
2539 nsresult rv = NS_OK;
2541 if (mEditor && mUseEditor)
2543 PRUint32 flags = (nsIDocumentEncoder::OutputLFLineBreak |
2544 nsIDocumentEncoder::OutputPreformatted |
2545 nsIDocumentEncoder::OutputPersistNBSP);
2547 if (PR_TRUE==IsPlainTextControl())
2549 flags |= nsIDocumentEncoder::OutputBodyOnly;
2552 if (!aIgnoreWrap) {
2553 nsHTMLTextWrap wrapProp;
2554 if (::GetWrapPropertyEnum(mContent, wrapProp) &&
2555 wrapProp == eHTMLTextWrap_Hard) {
2556 flags |= nsIDocumentEncoder::OutputWrap;
2560 // What follows is a bit of a hack. The problem is that we could be in
2561 // this method because we're being destroyed for whatever reason while
2562 // script is executing. If that happens, editor will run with the
2563 // privileges of the executing script, which means it may not be able to
2564 // access its own DOM nodes! Let's try to deal with that by pushing a null
2565 // JSContext on the JSContext stack to make it clear that we're native
2566 // code. Note that any script that's directly trying to access our value
2567 // has to be going through some scriptable object to do that and that
2568 // already does the relevant security checks.
2569 // XXXbz if we could just get the textContent of our anonymous content (eg
2570 // if plaintext editor didn't create <br> nodes all over), we wouldn't need
2571 // this.
2572 { /* Scope for context pusher */
2573 nsCxPusher pusher;
2574 pusher.PushNull();
2576 rv = mEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
2577 aValue);
2580 else
2582 // Otherwise get the value from content.
2583 nsCOMPtr<nsIDOMHTMLInputElement> inputControl = do_QueryInterface(mContent);
2584 if (inputControl)
2586 rv = inputControl->GetValue(aValue);
2588 else
2590 nsCOMPtr<nsIDOMHTMLTextAreaElement> textareaControl
2591 = do_QueryInterface(mContent);
2592 if (textareaControl)
2594 rv = textareaControl->GetValue(aValue);
2599 return rv;
2603 // END IMPLEMENTING NS_IFORMCONTROLFRAME
2605 nsresult
2606 nsTextControlFrame::SetValue(const nsAString& aValue)
2608 // XXX this method should actually propagate errors! It'd make debugging it
2609 // so much easier...
2610 if (mEditor && mUseEditor)
2612 // This method isn't used for user-generated changes, except for calls
2613 // from nsFileControlFrame which sets mFireChangeEventState==true and
2614 // restores it afterwards (ie. we want 'change' events for those changes).
2615 // Focused value must be updated to prevent incorrect 'change' events,
2616 // but only if user hasn't changed the value.
2617 nsString val;
2618 GetText(&val);
2619 PRBool focusValueInit = !mFireChangeEventState &&
2620 mFocusedValue.Equals(val);
2622 nsCOMPtr<nsIEditor> editor = mEditor;
2623 nsWeakFrame weakFrame(this);
2624 nsAutoString currentValue;
2625 GetValue(currentValue, PR_FALSE);
2626 if (IsSingleLineTextControl())
2628 RemoveNewlines(currentValue);
2630 // this is necessary to avoid infinite recursion
2631 if (!currentValue.Equals(aValue))
2633 // \r is an illegal character in the dom, but people use them,
2634 // so convert windows and mac platform linebreaks to \n:
2635 // Unfortunately aValue is declared const, so we have to copy
2636 // in order to do this substitution.
2637 currentValue.Assign(aValue);
2638 ::PlatformToDOMLineBreaks(currentValue);
2640 nsCOMPtr<nsIDOMDocument>domDoc;
2641 nsresult rv = editor->GetDocument(getter_AddRefs(domDoc));
2642 NS_ENSURE_SUCCESS(rv, rv);
2643 NS_ENSURE_STATE(domDoc);
2645 PRBool outerTransaction;
2646 // Time to mess with our security context... See comments in GetValue()
2647 // for why this is needed. Note that we have to do this up here, because
2648 // otherwise SelectAll() will fail.
2649 { /* Scope for context pusher */
2650 nsCxPusher pusher;
2651 pusher.PushNull();
2653 nsCOMPtr<nsISelection> domSel;
2654 nsCOMPtr<nsISelectionPrivate> selPriv;
2655 mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
2656 getter_AddRefs(domSel));
2657 if (domSel)
2659 selPriv = do_QueryInterface(domSel);
2660 if (selPriv)
2661 selPriv->StartBatchChanges();
2664 nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon;
2665 mSelCon->SelectAll();
2666 nsCOMPtr<nsIPlaintextEditor> plaintextEditor = do_QueryInterface(editor);
2667 if (!plaintextEditor || !weakFrame.IsAlive()) {
2668 NS_WARNING("Somehow not a plaintext editor?");
2669 return NS_ERROR_FAILURE;
2672 // Since this code does not handle user-generated changes to the text,
2673 // make sure we don't fire oninput when the editor notifies us.
2674 // (mNotifyOnInput must be reset before we return).
2676 // To protect against a reentrant call to SetValue, we check whether
2677 // another SetValue is already happening for this frame. If it is,
2678 // we must wait until we unwind to re-enable oninput events.
2679 outerTransaction = mNotifyOnInput;
2680 if (outerTransaction)
2681 mNotifyOnInput = PR_FALSE;
2683 // get the flags, remove readonly and disabled, set the value,
2684 // restore flags
2685 PRUint32 flags, savedFlags;
2686 editor->GetFlags(&savedFlags);
2687 flags = savedFlags;
2688 flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
2689 flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
2690 editor->SetFlags(flags);
2692 // Also don't enforce max-length here
2693 PRInt32 savedMaxLength;
2694 plaintextEditor->GetMaxTextLength(&savedMaxLength);
2695 plaintextEditor->SetMaxTextLength(-1);
2697 if (currentValue.Length() < 1)
2698 editor->DeleteSelection(nsIEditor::eNone);
2699 else {
2700 if (plaintextEditor)
2701 plaintextEditor->InsertText(currentValue);
2704 plaintextEditor->SetMaxTextLength(savedMaxLength);
2705 editor->SetFlags(savedFlags);
2706 if (selPriv)
2707 selPriv->EndBatchChanges();
2710 NS_ENSURE_STATE(weakFrame.IsAlive());
2711 if (outerTransaction)
2712 mNotifyOnInput = PR_TRUE;
2714 if (focusValueInit) {
2715 // Reset mFocusedValue so the onchange event doesn't fire incorrectly.
2716 InitFocusedValue();
2720 NS_ENSURE_STATE(weakFrame.IsAlive());
2721 nsIScrollableView* scrollableView = GetScrollableView();
2722 if (scrollableView)
2724 // Scroll the upper left corner of the text control's
2725 // content area back into view.
2727 scrollableView->ScrollTo(0, 0, 0);
2730 else
2732 // Otherwise set the value in content.
2733 nsCOMPtr<nsITextControlElement> textControl = do_QueryInterface(mContent);
2734 if (textControl)
2736 textControl->TakeTextFrameValue(aValue);
2739 return NS_OK;
2743 NS_IMETHODIMP
2744 nsTextControlFrame::SetInitialChildList(nsIAtom* aListName,
2745 nsFrameList& aChildList)
2747 nsresult rv = nsBoxFrame::SetInitialChildList(aListName, aChildList);
2749 //look for scroll view below this frame go along first child list
2750 nsIFrame* first = GetFirstChild(nsnull);
2752 // Mark the scroll frame as being a reflow root. This will allow
2753 // incremental reflows to be initiated at the scroll frame, rather
2754 // than descending from the root frame of the frame hierarchy.
2755 first->AddStateBits(NS_FRAME_REFLOW_ROOT);
2757 nsIScrollableFrame *scrollableFrame = do_QueryFrame(first);
2758 NS_ASSERTION(scrollableFrame, "Child must be scrollable");
2760 // we must turn off scrollbars for singleline text controls
2761 // XXX FIXME this should be removed,
2762 // nsGfxScrollFrameInner::CreateAnonymousContent handles this
2763 if (IsSingleLineTextControl())
2765 if (scrollableFrame)
2766 scrollableFrame->SetScrollbarVisibility(PR_FALSE, PR_FALSE);
2769 //register key listeners
2770 nsCOMPtr<nsIDOMEventGroup> systemGroup;
2771 mContent->GetSystemEventGroup(getter_AddRefs(systemGroup));
2772 nsCOMPtr<nsIDOM3EventTarget> dom3Targ = do_QueryInterface(mContent);
2773 if (dom3Targ) {
2774 // cast because of ambiguous base
2775 nsIDOMEventListener *listener = static_cast<nsIDOMKeyListener*>
2776 (mTextListener);
2778 dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keydown"),
2779 listener, PR_FALSE, systemGroup);
2780 dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keypress"),
2781 listener, PR_FALSE, systemGroup);
2782 dom3Targ->AddGroupedEventListener(NS_LITERAL_STRING("keyup"),
2783 listener, PR_FALSE, systemGroup);
2786 return rv;
2789 nsIScrollableView* nsTextControlFrame::GetScrollableView()
2791 nsIFrame* first = GetFirstChild(nsnull);
2792 nsIScrollableFrame* scrollableFrame = do_QueryFrame(first);
2793 return scrollableFrame ? scrollableFrame->GetScrollableView() : nsnull;
2796 PRBool
2797 nsTextControlFrame::IsScrollable() const
2799 return !IsSingleLineTextControl();
2802 void
2803 nsTextControlFrame::SetValueChanged(PRBool aValueChanged)
2805 nsCOMPtr<nsITextControlElement> elem = do_QueryInterface(mContent);
2806 if (elem) {
2807 elem->SetValueChanged(aValueChanged);
2811 /* static */ void
2812 nsTextControlFrame::ShutDown()
2814 NS_IF_RELEASE(sNativeTextAreaBindings);
2815 NS_IF_RELEASE(sNativeInputBindings);