1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "nsTextEditorState.h"
8 #include "mozilla/TextInputListener.h"
11 #include "nsIPresShell.h"
14 #include "nsLayoutCID.h"
15 #include "nsITextControlFrame.h"
16 #include "nsContentCreatorFunctions.h"
17 #include "nsTextControlFrame.h"
18 #include "nsIControllers.h"
19 #include "nsITransactionManager.h"
20 #include "nsIControllerContext.h"
21 #include "nsAttrValue.h"
22 #include "nsAttrValueInlines.h"
23 #include "nsGenericHTMLElement.h"
24 #include "nsIDOMEventListener.h"
25 #include "nsIEditorObserver.h"
26 #include "nsIWidget.h"
27 #include "nsIDocumentEncoder.h"
28 #include "nsPIDOMWindow.h"
29 #include "nsServiceManagerUtils.h"
30 #include "mozilla/dom/Selection.h"
31 #include "mozilla/TextEditRules.h"
32 #include "mozilla/EventListenerManager.h"
33 #include "nsContentUtils.h"
34 #include "mozilla/Preferences.h"
35 #include "nsTextNode.h"
36 #include "nsIController.h"
37 #include "mozilla/AutoRestore.h"
38 #include "mozilla/TextEvents.h"
39 #include "mozilla/dom/Event.h"
40 #include "mozilla/dom/ScriptSettings.h"
41 #include "mozilla/dom/HTMLInputElement.h"
42 #include "mozilla/dom/HTMLTextAreaElement.h"
43 #include "mozilla/dom/Text.h"
44 #include "nsNumberControlFrame.h"
45 #include "nsFrameSelection.h"
46 #include "mozilla/ErrorResult.h"
47 #include "mozilla/Telemetry.h"
49 using namespace mozilla
;
50 using namespace mozilla::dom
;
53 SetEditorFlagsIfNecessary(EditorBase
& aEditorBase
, uint32_t aFlags
)
55 if (aEditorBase
.Flags() == aFlags
) {
58 return aEditorBase
.SetFlags(aFlags
);
61 class MOZ_STACK_CLASS ValueSetter
64 explicit ValueSetter(TextEditor
* aTextEditor
)
65 : mTextEditor(aTextEditor
)
66 // To protect against a reentrant call to SetValue, we check whether
67 // another SetValue is already happening for this editor. If it is,
68 // we must wait until we unwind to re-enable oninput events.
69 , mOuterTransaction(aTextEditor
->IsSuppressingDispatchingInputEvent())
71 MOZ_ASSERT(aTextEditor
);
75 mTextEditor
->SuppressDispatchingInputEvent(mOuterTransaction
);
79 mTextEditor
->SuppressDispatchingInputEvent(true);
83 RefPtr
<TextEditor
> mTextEditor
;
84 bool mOuterTransaction
;
87 class RestoreSelectionState
: public Runnable
{
89 RestoreSelectionState(nsTextEditorState
* aState
, nsTextControlFrame
* aFrame
)
90 : mozilla::Runnable("RestoreSelectionState")
92 , mTextEditorState(aState
)
96 NS_IMETHOD
Run() override
{
97 if (!mTextEditorState
) {
101 AutoHideSelectionChanges hideSelectionChanges
102 (mFrame
->GetConstFrameSelection());
105 // SetSelectionRange leads to Selection::AddRange which flushes Layout -
106 // need to block script to avoid nested PrepareEditor calls (bug 642800).
107 nsAutoScriptBlocker scriptBlocker
;
108 nsTextEditorState::SelectionProperties
& properties
=
109 mTextEditorState
->GetSelectionProperties();
110 if (properties
.IsDirty()) {
111 mFrame
->SetSelectionRange(properties
.GetStart(),
113 properties
.GetDirection());
115 if (!mTextEditorState
->mSelectionRestoreEagerInit
) {
116 mTextEditorState
->HideSelectionIfBlurred();
118 mTextEditorState
->mSelectionRestoreEagerInit
= false;
121 if (mTextEditorState
) {
122 mTextEditorState
->FinishedRestoringSelection();
127 // Let the text editor tell us we're no longer relevant - avoids use of AutoWeakFrame
130 mTextEditorState
= nullptr;
134 nsTextControlFrame
* mFrame
;
135 nsTextEditorState
* mTextEditorState
;
138 class MOZ_RAII AutoRestoreEditorState final
141 explicit AutoRestoreEditorState(TextEditor
* aTextEditor
142 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
143 : mTextEditor(aTextEditor
)
144 , mSavedFlags(mTextEditor
->Flags())
145 , mSavedMaxLength(mTextEditor
->MaxTextLength())
147 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
148 MOZ_ASSERT(mTextEditor
);
150 // EditorBase::SetFlags() is a virtual method. Even though it does nothing
151 // if new flags and current flags are same, the calling cost causes
152 // appearing the method in profile. So, this class should check if it's
153 // necessary to call.
154 uint32_t flags
= mSavedFlags
;
155 flags
&= ~(nsIPlaintextEditor::eEditorDisabledMask
);
156 flags
&= ~(nsIPlaintextEditor::eEditorReadonlyMask
);
157 flags
|= nsIPlaintextEditor::eEditorDontEchoPassword
;
158 if (mSavedFlags
!= flags
) {
159 mTextEditor
->SetFlags(flags
);
161 mTextEditor
->SetMaxTextLength(-1);
164 ~AutoRestoreEditorState()
166 mTextEditor
->SetMaxTextLength(mSavedMaxLength
);
167 SetEditorFlagsIfNecessary(*mTextEditor
, mSavedFlags
);
171 TextEditor
* mTextEditor
;
172 uint32_t mSavedFlags
;
173 int32_t mSavedMaxLength
;
174 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
177 class MOZ_RAII AutoDisableUndo final
180 explicit AutoDisableUndo(TextEditor
* aTextEditor
181 MOZ_GUARD_OBJECT_NOTIFIER_PARAM
)
182 : mTextEditor(aTextEditor
)
183 , mNumberOfMaximumTransactions(0)
185 MOZ_GUARD_OBJECT_NOTIFIER_INIT
;
186 MOZ_ASSERT(mTextEditor
);
188 mNumberOfMaximumTransactions
=
189 mTextEditor
? mTextEditor
->NumberOfMaximumTransactions() : 0;
190 DebugOnly
<bool> disabledUndoRedo
= mTextEditor
->DisableUndoRedo();
191 NS_WARNING_ASSERTION(disabledUndoRedo
,
192 "Failed to disable undo/redo transactions");
197 // Don't change enable/disable of undo/redo if it's enabled after
198 // it's disabled by the constructor because we shouldn't change
199 // the maximum undo/redo count to the old value.
200 if (mTextEditor
->IsUndoRedoEnabled()) {
203 // If undo/redo was enabled, mNumberOfMaximumTransactions is -1 or lager
204 // than 0. Only when it's 0, it was disabled.
205 if (mNumberOfMaximumTransactions
) {
206 DebugOnly
<bool> enabledUndoRedo
=
207 mTextEditor
->EnableUndoRedo(mNumberOfMaximumTransactions
);
208 NS_WARNING_ASSERTION(enabledUndoRedo
,
209 "Failed to enable undo/redo transactions");
211 DebugOnly
<bool> disabledUndoRedo
= mTextEditor
->DisableUndoRedo();
212 NS_WARNING_ASSERTION(disabledUndoRedo
,
213 "Failed to disable undo/redo transactions");
218 TextEditor
* mTextEditor
;
219 int32_t mNumberOfMaximumTransactions
;
220 MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
225 nsITextControlElement::GetWrapPropertyEnum(nsIContent
* aContent
,
226 nsITextControlElement::nsHTMLTextWrap
& aWrapProp
)
228 // soft is the default; "physical" defaults to soft as well because all other
229 // browsers treat it that way and there is no real reason to maintain physical
230 // and virtual as separate entities if no one else does. Only hard and off
231 // do anything different.
232 aWrapProp
= eHTMLTextWrap_Soft
; // the default
235 if (aContent
->IsHTMLElement()) {
236 static Element::AttrValuesArray strings
[] =
237 {nsGkAtoms::HARD
, nsGkAtoms::OFF
, nullptr};
239 switch (aContent
->AsElement()->FindAttrValueIn(kNameSpaceID_None
,
240 nsGkAtoms::wrap
, strings
,
242 case 0: aWrapProp
= eHTMLTextWrap_Hard
; break;
243 case 1: aWrapProp
= eHTMLTextWrap_Off
; break;
253 already_AddRefed
<nsITextControlElement
>
254 nsITextControlElement::GetTextControlElementFromEditingHost(nsIContent
* aHost
)
260 nsCOMPtr
<nsITextControlElement
> parent
=
261 do_QueryInterface(aHost
->GetParent());
263 return parent
.forget();
267 SuppressEventHandlers(nsPresContext
* aPresContext
)
269 bool suppressHandlers
= false;
273 // Right now we only suppress event handlers and controller manipulation
274 // when in a print preview or print context!
276 // In the current implementation, we only paginate when
277 // printing or in print preview.
279 suppressHandlers
= aPresContext
->IsPaginated();
282 return suppressHandlers
;
285 class nsAnonDivObserver final
: public nsStubMutationObserver
288 explicit nsAnonDivObserver(nsTextEditorState
* aTextEditorState
)
289 : mTextEditorState(aTextEditorState
) {}
291 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
292 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
293 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
294 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
297 ~nsAnonDivObserver() {}
298 nsTextEditorState
* mTextEditorState
;
301 class nsTextInputSelectionImpl final
: public nsSupportsWeakReference
302 , public nsISelectionController
304 ~nsTextInputSelectionImpl(){}
307 NS_DECL_CYCLE_COLLECTING_ISUPPORTS
308 NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextInputSelectionImpl
, nsISelectionController
)
310 nsTextInputSelectionImpl(nsFrameSelection
*aSel
, nsIPresShell
*aShell
, nsIContent
*aLimiter
);
312 void SetScrollableFrame(nsIScrollableFrame
*aScrollableFrame
);
313 nsFrameSelection
* GetConstFrameSelection()
314 { return mFrameSelection
; }
315 // Will return null if !mFrameSelection.
316 Selection
* GetSelection(SelectionType aSelectionType
);
318 //NSISELECTIONCONTROLLER INTERFACES
319 NS_IMETHOD
SetDisplaySelection(int16_t toggle
) override
;
320 NS_IMETHOD
GetDisplaySelection(int16_t* _retval
) override
;
321 NS_IMETHOD
SetSelectionFlags(int16_t aInEnable
) override
;
322 NS_IMETHOD
GetSelectionFlags(int16_t *aOutEnable
) override
;
323 NS_IMETHOD
GetSelectionFromScript(RawSelectionType aRawSelectionType
,
324 Selection
** aSelection
) override
;
325 Selection
* GetSelection(RawSelectionType aRawSelectionType
) override
;
326 NS_IMETHOD
ScrollSelectionIntoView(RawSelectionType aRawSelectionType
,
327 int16_t aRegion
, int16_t aFlags
) override
;
328 NS_IMETHOD
RepaintSelection(RawSelectionType aRawSelectionType
) override
;
329 nsresult
RepaintSelection(nsPresContext
* aPresContext
,
330 SelectionType aSelectionType
);
331 NS_IMETHOD
SetCaretEnabled(bool enabled
) override
;
332 NS_IMETHOD
SetCaretReadOnly(bool aReadOnly
) override
;
333 NS_IMETHOD
GetCaretEnabled(bool* _retval
) override
;
334 NS_IMETHOD
GetCaretVisible(bool* _retval
) override
;
335 NS_IMETHOD
SetCaretVisibilityDuringSelection(bool aVisibility
) override
;
336 NS_IMETHOD
PhysicalMove(int16_t aDirection
, int16_t aAmount
, bool aExtend
) override
;
337 NS_IMETHOD
CharacterMove(bool aForward
, bool aExtend
) override
;
338 NS_IMETHOD
CharacterExtendForDelete() override
;
339 NS_IMETHOD
CharacterExtendForBackspace() override
;
340 NS_IMETHOD
WordMove(bool aForward
, bool aExtend
) override
;
341 NS_IMETHOD
WordExtendForDelete(bool aForward
) override
;
342 NS_IMETHOD
LineMove(bool aForward
, bool aExtend
) override
;
343 NS_IMETHOD
IntraLineMove(bool aForward
, bool aExtend
) override
;
344 NS_IMETHOD
PageMove(bool aForward
, bool aExtend
) override
;
345 NS_IMETHOD
CompleteScroll(bool aForward
) override
;
346 NS_IMETHOD
CompleteMove(bool aForward
, bool aExtend
) override
;
347 NS_IMETHOD
ScrollPage(bool aForward
) override
;
348 NS_IMETHOD
ScrollLine(bool aForward
) override
;
349 NS_IMETHOD
ScrollCharacter(bool aRight
) override
;
350 NS_IMETHOD
SelectAll(void) override
;
351 NS_IMETHOD
CheckVisibility(nsINode
*node
, int16_t startOffset
, int16_t EndOffset
, bool* _retval
) override
;
352 virtual nsresult
CheckVisibilityContent(nsIContent
* aNode
, int16_t aStartOffset
, int16_t aEndOffset
, bool* aRetval
) override
;
355 RefPtr
<nsFrameSelection
> mFrameSelection
;
356 nsCOMPtr
<nsIContent
> mLimiter
;
357 nsIScrollableFrame
*mScrollFrame
;
358 nsWeakPtr mPresShellWeak
;
361 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextInputSelectionImpl
)
362 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextInputSelectionImpl
)
363 NS_INTERFACE_TABLE_HEAD(nsTextInputSelectionImpl
)
364 NS_INTERFACE_TABLE(nsTextInputSelectionImpl
,
365 nsISelectionController
,
367 nsISupportsWeakReference
)
368 NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsTextInputSelectionImpl
)
371 NS_IMPL_CYCLE_COLLECTION(nsTextInputSelectionImpl
, mFrameSelection
, mLimiter
)
374 // BEGIN nsTextInputSelectionImpl
376 nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection
*aSel
,
377 nsIPresShell
*aShell
,
378 nsIContent
*aLimiter
)
379 : mScrollFrame(nullptr)
383 mFrameSelection
= aSel
;//we are the owner now!
385 bool accessibleCaretEnabled
=
386 PresShell::AccessibleCaretEnabled(aLimiter
->OwnerDoc()->GetDocShell());
387 mFrameSelection
->Init(aShell
, mLimiter
, accessibleCaretEnabled
);
388 mPresShellWeak
= do_GetWeakReference(aShell
);
393 nsTextInputSelectionImpl::SetScrollableFrame(nsIScrollableFrame
*aScrollableFrame
)
395 mScrollFrame
= aScrollableFrame
;
396 if (!mScrollFrame
&& mFrameSelection
) {
397 mFrameSelection
->DisconnectFromPresShell();
398 mFrameSelection
= nullptr;
403 nsTextInputSelectionImpl::GetSelection(SelectionType aSelectionType
)
405 if (!mFrameSelection
) {
409 return mFrameSelection
->GetSelection(aSelectionType
);
413 nsTextInputSelectionImpl::SetDisplaySelection(int16_t aToggle
)
415 if (!mFrameSelection
)
416 return NS_ERROR_NULL_POINTER
;
418 mFrameSelection
->SetDisplaySelection(aToggle
);
423 nsTextInputSelectionImpl::GetDisplaySelection(int16_t *aToggle
)
425 if (!mFrameSelection
)
426 return NS_ERROR_NULL_POINTER
;
428 *aToggle
= mFrameSelection
->GetDisplaySelection();
433 nsTextInputSelectionImpl::SetSelectionFlags(int16_t aToggle
)
435 return NS_OK
;//stub this out. not used in input
439 nsTextInputSelectionImpl::GetSelectionFlags(int16_t *aOutEnable
)
441 *aOutEnable
= nsISelectionDisplay::DISPLAY_TEXT
;
446 nsTextInputSelectionImpl::GetSelectionFromScript(RawSelectionType aRawSelectionType
,
447 Selection
** aSelection
)
449 if (!mFrameSelection
)
450 return NS_ERROR_NULL_POINTER
;
453 mFrameSelection
->GetSelection(ToSelectionType(aRawSelectionType
));
455 // GetSelection() fails only when aRawSelectionType is invalid value.
456 if (!(*aSelection
)) {
457 return NS_ERROR_INVALID_ARG
;
460 NS_ADDREF(*aSelection
);
465 nsTextInputSelectionImpl::GetSelection(RawSelectionType aRawSelectionType
)
467 return GetSelection(ToSelectionType(aRawSelectionType
));
471 nsTextInputSelectionImpl::ScrollSelectionIntoView(
472 RawSelectionType aRawSelectionType
,
476 if (!mFrameSelection
)
477 return NS_ERROR_FAILURE
;
479 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
480 return frameSelection
->ScrollSelectionIntoView(
481 ToSelectionType(aRawSelectionType
),
486 nsTextInputSelectionImpl::RepaintSelection(RawSelectionType aRawSelectionType
)
488 if (!mFrameSelection
)
489 return NS_ERROR_FAILURE
;
491 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
492 return frameSelection
->RepaintSelection(ToSelectionType(aRawSelectionType
));
496 nsTextInputSelectionImpl::RepaintSelection(nsPresContext
* aPresContext
,
497 SelectionType aSelectionType
)
499 if (!mFrameSelection
)
500 return NS_ERROR_FAILURE
;
502 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
503 return frameSelection
->RepaintSelection(aSelectionType
);
507 nsTextInputSelectionImpl::SetCaretEnabled(bool enabled
)
509 if (!mPresShellWeak
) return NS_ERROR_NOT_INITIALIZED
;
511 nsCOMPtr
<nsIPresShell
> shell
= do_QueryReferent(mPresShellWeak
);
512 if (!shell
) return NS_ERROR_FAILURE
;
514 // tell the pres shell to enable the caret, rather than settings its visibility directly.
515 // this way the presShell's idea of caret visibility is maintained.
516 nsCOMPtr
<nsISelectionController
> selCon
= do_QueryInterface(shell
);
517 if (!selCon
) return NS_ERROR_NO_INTERFACE
;
518 selCon
->SetCaretEnabled(enabled
);
524 nsTextInputSelectionImpl::SetCaretReadOnly(bool aReadOnly
)
526 if (!mPresShellWeak
) return NS_ERROR_NOT_INITIALIZED
;
528 nsCOMPtr
<nsIPresShell
> shell
= do_QueryReferent(mPresShellWeak
, &result
);
531 RefPtr
<nsCaret
> caret
= shell
->GetCaret();
533 Selection
* selection
=
534 mFrameSelection
->GetSelection(SelectionType::eNormal
);
536 caret
->SetCaretReadOnly(aReadOnly
);
541 return NS_ERROR_FAILURE
;
545 nsTextInputSelectionImpl::GetCaretEnabled(bool *_retval
)
547 return GetCaretVisible(_retval
);
551 nsTextInputSelectionImpl::GetCaretVisible(bool *_retval
)
553 if (!mPresShellWeak
) return NS_ERROR_NOT_INITIALIZED
;
555 nsCOMPtr
<nsIPresShell
> shell
= do_QueryReferent(mPresShellWeak
, &result
);
558 RefPtr
<nsCaret
> caret
= shell
->GetCaret();
560 *_retval
= caret
->IsVisible();
564 return NS_ERROR_FAILURE
;
568 nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(bool aVisibility
)
570 if (!mPresShellWeak
) return NS_ERROR_NOT_INITIALIZED
;
572 nsCOMPtr
<nsIPresShell
> shell
= do_QueryReferent(mPresShellWeak
, &result
);
575 RefPtr
<nsCaret
> caret
= shell
->GetCaret();
577 Selection
* selection
=
578 mFrameSelection
->GetSelection(SelectionType::eNormal
);
580 caret
->SetVisibilityDuringSelection(aVisibility
);
585 return NS_ERROR_FAILURE
;
589 nsTextInputSelectionImpl::PhysicalMove(int16_t aDirection
, int16_t aAmount
,
592 if (mFrameSelection
) {
593 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
594 return frameSelection
->PhysicalMove(aDirection
, aAmount
, aExtend
);
596 return NS_ERROR_NULL_POINTER
;
600 nsTextInputSelectionImpl::CharacterMove(bool aForward
, bool aExtend
)
602 if (mFrameSelection
) {
603 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
604 return frameSelection
->CharacterMove(aForward
, aExtend
);
606 return NS_ERROR_NULL_POINTER
;
610 nsTextInputSelectionImpl::CharacterExtendForDelete()
612 if (mFrameSelection
) {
613 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
614 return frameSelection
->CharacterExtendForDelete();
616 return NS_ERROR_NULL_POINTER
;
620 nsTextInputSelectionImpl::CharacterExtendForBackspace()
622 if (mFrameSelection
) {
623 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
624 return frameSelection
->CharacterExtendForBackspace();
626 return NS_ERROR_NULL_POINTER
;
630 nsTextInputSelectionImpl::WordMove(bool aForward
, bool aExtend
)
632 if (mFrameSelection
) {
633 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
634 return frameSelection
->WordMove(aForward
, aExtend
);
636 return NS_ERROR_NULL_POINTER
;
640 nsTextInputSelectionImpl::WordExtendForDelete(bool aForward
)
642 if (mFrameSelection
) {
643 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
644 return frameSelection
->WordExtendForDelete(aForward
);
646 return NS_ERROR_NULL_POINTER
;
650 nsTextInputSelectionImpl::LineMove(bool aForward
, bool aExtend
)
654 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
655 nsresult result
= frameSelection
->LineMove(aForward
, aExtend
);
656 if (NS_FAILED(result
))
657 result
= CompleteMove(aForward
,aExtend
);
660 return NS_ERROR_NULL_POINTER
;
665 nsTextInputSelectionImpl::IntraLineMove(bool aForward
, bool aExtend
)
667 if (mFrameSelection
) {
668 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
669 return frameSelection
->IntraLineMove(aForward
, aExtend
);
671 return NS_ERROR_NULL_POINTER
;
676 nsTextInputSelectionImpl::PageMove(bool aForward
, bool aExtend
)
678 // expected behavior for PageMove is to scroll AND move the caret
679 // and to remain relative position of the caret in view. see Bug 4302.
682 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
683 frameSelection
->CommonPageMove(aForward
, aExtend
, mScrollFrame
);
685 // After ScrollSelectionIntoView(), the pending notifications might be
686 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
687 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL
,
688 nsISelectionController::SELECTION_FOCUS_REGION
,
689 nsISelectionController::SCROLL_SYNCHRONOUS
|
690 nsISelectionController::SCROLL_FOR_CARET_MOVE
);
694 nsTextInputSelectionImpl::CompleteScroll(bool aForward
)
697 return NS_ERROR_NOT_INITIALIZED
;
699 mScrollFrame
->ScrollBy(nsIntPoint(0, aForward
? 1 : -1),
700 nsIScrollableFrame::WHOLE
,
701 nsIScrollableFrame::INSTANT
);
706 nsTextInputSelectionImpl::CompleteMove(bool aForward
, bool aExtend
)
708 NS_ENSURE_STATE(mFrameSelection
);
709 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
711 // grab the parent / root DIV for this text widget
712 nsIContent
* parentDIV
= frameSelection
->GetLimiter();
714 return NS_ERROR_UNEXPECTED
;
716 // make the caret be either at the very beginning (0) or the very end
718 CaretAssociationHint hint
= CARET_ASSOCIATE_BEFORE
;
721 offset
= parentDIV
->GetChildCount();
723 // Prevent the caret from being placed after the last
724 // BR node in the content tree!
728 nsIContent
*child
= parentDIV
->GetLastChild();
730 if (child
->IsHTMLElement(nsGkAtoms::br
))
733 hint
= CARET_ASSOCIATE_AFTER
; // for Bug 106855
738 frameSelection
->HandleClick(parentDIV
, offset
, offset
, aExtend
,
741 // if we got this far, attempt to scroll no matter what the above result is
742 return CompleteScroll(aForward
);
746 nsTextInputSelectionImpl::ScrollPage(bool aForward
)
749 return NS_ERROR_NOT_INITIALIZED
;
751 mScrollFrame
->ScrollBy(nsIntPoint(0, aForward
? 1 : -1),
752 nsIScrollableFrame::PAGES
,
753 nsIScrollableFrame::SMOOTH
);
758 nsTextInputSelectionImpl::ScrollLine(bool aForward
)
761 return NS_ERROR_NOT_INITIALIZED
;
763 mScrollFrame
->ScrollBy(nsIntPoint(0, aForward
? 1 : -1),
764 nsIScrollableFrame::LINES
,
765 nsIScrollableFrame::SMOOTH
);
770 nsTextInputSelectionImpl::ScrollCharacter(bool aRight
)
773 return NS_ERROR_NOT_INITIALIZED
;
775 mScrollFrame
->ScrollBy(nsIntPoint(aRight
? 1 : -1, 0),
776 nsIScrollableFrame::LINES
,
777 nsIScrollableFrame::SMOOTH
);
782 nsTextInputSelectionImpl::SelectAll()
784 if (mFrameSelection
) {
785 RefPtr
<nsFrameSelection
> frameSelection
= mFrameSelection
;
786 return frameSelection
->SelectAll();
788 return NS_ERROR_NULL_POINTER
;
792 nsTextInputSelectionImpl::CheckVisibility(nsINode
*node
, int16_t startOffset
, int16_t EndOffset
, bool *_retval
)
794 if (!mPresShellWeak
) return NS_ERROR_NOT_INITIALIZED
;
796 nsCOMPtr
<nsISelectionController
> shell
= do_QueryReferent(mPresShellWeak
, &result
);
799 return shell
->CheckVisibility(node
,startOffset
,EndOffset
, _retval
);
801 return NS_ERROR_FAILURE
;
806 nsTextInputSelectionImpl::CheckVisibilityContent(nsIContent
* aNode
,
807 int16_t aStartOffset
,
811 if (!mPresShellWeak
) {
812 return NS_ERROR_NOT_INITIALIZED
;
815 nsCOMPtr
<nsISelectionController
> shell
= do_QueryReferent(mPresShellWeak
);
816 NS_ENSURE_TRUE(shell
, NS_ERROR_FAILURE
);
818 return shell
->CheckVisibilityContent(aNode
, aStartOffset
, aEndOffset
, aRetval
);
822 * mozilla::TextInputListener implementation
825 TextInputListener::TextInputListener(nsITextControlElement
* aTxtCtrlElement
)
827 , mTxtCtrlElement(aTxtCtrlElement
)
828 , mSelectionWasCollapsed(true)
829 , mHadUndoItems(false)
830 , mHadRedoItems(false)
831 , mSettingValue(false)
832 , mSetValueChanged(true)
833 , mListeningToSelectionChange(false)
837 NS_IMPL_CYCLE_COLLECTING_ADDREF(TextInputListener
)
838 NS_IMPL_CYCLE_COLLECTING_RELEASE(TextInputListener
)
840 NS_INTERFACE_MAP_BEGIN(TextInputListener
)
841 NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference
)
842 NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener
)
843 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIDOMEventListener
)
844 NS_INTERFACE_MAP_ENTRIES_CYCLE_COLLECTION(TextInputListener
)
847 NS_IMPL_CYCLE_COLLECTION_0(TextInputListener
)
850 TextInputListener::OnSelectionChange(Selection
& aSelection
,
853 if (!mListeningToSelectionChange
) {
857 AutoWeakFrame weakFrame
= mFrame
;
859 // Fire the select event
860 // The specs don't exactly say when we should fire the select event.
861 // IE: Whenever you add/remove a character to/from the selection. Also
862 // each time for select all. Also if you get to the end of the text
863 // field you will get new event for each keypress or a continuous
864 // stream of events if you use the mouse. IE will fire select event
865 // when the selection collapses to nothing if you are holding down
866 // the shift or mouse button.
867 // Mozilla: If we have non-empty selection we will fire a new event for each
868 // keypress (or mouseup) if the selection changed. Mozilla will also
869 // create the event each time select all is called, even if everything
870 // was previously selected, becase technically select all will first collapse
871 // and then extend. Mozilla will never create an event if the selection
872 // collapses to nothing.
873 bool collapsed
= aSelection
.IsCollapsed();
874 if (!collapsed
&& (aReason
& (nsISelectionListener::MOUSEUP_REASON
|
875 nsISelectionListener::KEYPRESS_REASON
|
876 nsISelectionListener::SELECTALL_REASON
))) {
877 nsIContent
* content
= mFrame
->GetContent();
879 nsCOMPtr
<nsIDocument
> doc
= content
->GetComposedDoc();
881 nsCOMPtr
<nsIPresShell
> presShell
= doc
->GetShell();
883 nsEventStatus status
= nsEventStatus_eIgnore
;
884 WidgetEvent
event(true, eFormSelect
);
886 presShell
->HandleEventWithTarget(&event
, mFrame
, content
, &status
);
892 // if the collapsed state did not change, don't fire notifications
893 if (collapsed
== mSelectionWasCollapsed
) {
897 mSelectionWasCollapsed
= collapsed
;
899 if (!weakFrame
.IsAlive() || !mFrame
||
900 !nsContentUtils::IsFocusedContent(mFrame
->GetContent())) {
904 UpdateTextInputCommands(NS_LITERAL_STRING("select"),
905 &aSelection
, aReason
);
909 DoCommandCallback(Command aCommand
, void* aData
)
911 nsTextControlFrame
*frame
= static_cast<nsTextControlFrame
*>(aData
);
912 nsIContent
*content
= frame
->GetContent();
914 nsCOMPtr
<nsIControllers
> controllers
;
915 HTMLInputElement
* input
= HTMLInputElement::FromNode(content
);
917 input
->GetControllers(getter_AddRefs(controllers
));
919 HTMLTextAreaElement
* textArea
=
920 HTMLTextAreaElement::FromNode(content
);
923 textArea
->GetControllers(getter_AddRefs(controllers
));
928 NS_WARNING("Could not get controllers");
932 const char* commandStr
= WidgetKeyboardEvent::GetCommandStr(aCommand
);
934 nsCOMPtr
<nsIController
> controller
;
935 controllers
->GetControllerForCommand(commandStr
, getter_AddRefs(controller
));
941 nsresult rv
= controller
->IsCommandEnabled(commandStr
, &commandEnabled
);
942 NS_ENSURE_SUCCESS_VOID(rv
);
943 if (commandEnabled
) {
944 controller
->DoCommand(commandStr
);
949 TextInputListener::HandleEvent(Event
* aEvent
)
951 if (aEvent
->DefaultPrevented()) {
955 if (!aEvent
->IsTrusted()) {
959 WidgetKeyboardEvent
* keyEvent
=
960 aEvent
->WidgetEventPtr()->AsKeyboardEvent();
962 return NS_ERROR_UNEXPECTED
;
965 if (keyEvent
->mMessage
!= eKeyPress
) {
969 nsIWidget::NativeKeyBindingsType nativeKeyBindingsType
=
970 mTxtCtrlElement
->IsTextArea() ?
971 nsIWidget::NativeKeyBindingsForMultiLineEditor
:
972 nsIWidget::NativeKeyBindingsForSingleLineEditor
;
974 nsIWidget
* widget
= keyEvent
->mWidget
;
975 // If the event is created by chrome script, the widget is nullptr.
977 widget
= mFrame
->GetNearestWidget();
978 NS_ENSURE_TRUE(widget
, NS_OK
);
981 // WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
982 // If the event is created by chrome script, it is nullptr but we need to
983 // execute native key bindings. Therefore, we need to set widget to
984 // WidgetEvent::mWidget temporarily.
985 AutoRestore
<nsCOMPtr
<nsIWidget
>> saveWidget(keyEvent
->mWidget
);
986 keyEvent
->mWidget
= widget
;
987 if (keyEvent
->ExecuteEditCommands(nativeKeyBindingsType
,
988 DoCommandCallback
, mFrame
)) {
989 aEvent
->PreventDefault();
995 TextInputListener::OnEditActionHandled()
998 // We've been disconnected from the nsTextEditorState object, nothing to do
1003 AutoWeakFrame weakFrame
= mFrame
;
1005 nsITextControlFrame
* frameBase
= do_QueryFrame(mFrame
);
1006 nsTextControlFrame
* frame
= static_cast<nsTextControlFrame
*> (frameBase
);
1007 NS_ASSERTION(frame
, "Where is our frame?");
1009 // Update the undo / redo menus
1011 RefPtr
<TextEditor
> textEditor
= frame
->GetTextEditor();
1013 // Get the number of undo / redo items
1014 size_t numUndoItems
= textEditor
->NumberOfUndoItems();
1015 size_t numRedoItems
= textEditor
->NumberOfRedoItems();
1016 if ((numUndoItems
&& !mHadUndoItems
) || (!numUndoItems
&& mHadUndoItems
) ||
1017 (numRedoItems
&& !mHadRedoItems
) || (!numRedoItems
&& mHadRedoItems
)) {
1018 // Modify the menu if undo or redo items are different
1019 UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
1021 mHadUndoItems
= numUndoItems
!= 0;
1022 mHadRedoItems
= numRedoItems
!= 0;
1025 if (!weakFrame
.IsAlive()) {
1029 HandleValueChanged(frame
);
1033 TextInputListener::HandleValueChanged(nsTextControlFrame
* aFrame
)
1035 // Make sure we know we were changed (do NOT set this to false if there are
1036 // no undo items; JS could change the value and we'd still need to save it)
1037 if (mSetValueChanged
) {
1039 nsITextControlFrame
* frameBase
= do_QueryFrame(mFrame
);
1040 aFrame
= static_cast<nsTextControlFrame
*> (frameBase
);
1041 NS_ASSERTION(aFrame
, "Where is our frame?");
1043 aFrame
->SetValueChanged(true);
1046 if (!mSettingValue
) {
1047 mTxtCtrlElement
->OnValueChanged(/* aNotify = */ true,
1048 /* aWasInteractiveUserChange = */ true);
1053 TextInputListener::UpdateTextInputCommands(const nsAString
& aCommandsToUpdate
,
1054 Selection
* aSelection
,
1057 nsIContent
* content
= mFrame
->GetContent();
1058 NS_ENSURE_TRUE(content
, NS_ERROR_FAILURE
);
1060 nsCOMPtr
<nsIDocument
> doc
= content
->GetComposedDoc();
1061 NS_ENSURE_TRUE(doc
, NS_ERROR_FAILURE
);
1063 nsPIDOMWindowOuter
* domWindow
= doc
->GetWindow();
1064 NS_ENSURE_TRUE(domWindow
, NS_ERROR_FAILURE
);
1066 domWindow
->UpdateCommands(aCommandsToUpdate
, aSelection
, aReason
);
1070 // END mozilla::TextInputListener
1072 // nsTextEditorState
1074 nsTextEditorState::nsTextEditorState(nsITextControlElement
* aOwningElement
)
1075 : mTextCtrlElement(aOwningElement
)
1076 , mBoundFrame(nullptr)
1077 , mEverInited(false)
1078 , mEditorInitialized(false)
1079 , mInitializing(false)
1080 , mValueTransferInProgress(false)
1081 , mSelectionCached(true)
1082 , mSelectionRestoreEagerInit(false)
1083 , mPlaceholderVisibility(false)
1084 , mPreviewVisibility(false)
1085 , mIsCommittingComposition(false)
1086 // When adding more member variable initializations here, add the same
1087 // also to ::Construct.
1089 MOZ_COUNT_CTOR(nsTextEditorState
);
1093 nsTextEditorState::Construct(nsITextControlElement
* aOwningElement
,
1094 nsTextEditorState
** aReusedState
)
1096 if (*aReusedState
) {
1097 nsTextEditorState
* state
= *aReusedState
;
1098 *aReusedState
= nullptr;
1099 state
->mTextCtrlElement
= aOwningElement
;
1100 state
->mBoundFrame
= nullptr;
1101 state
->mSelectionProperties
= SelectionProperties();
1102 state
->mEverInited
= false;
1103 state
->mEditorInitialized
= false;
1104 state
->mInitializing
= false;
1105 state
->mValueTransferInProgress
= false;
1106 state
->mSelectionCached
= true;
1107 state
->mSelectionRestoreEagerInit
= false;
1108 state
->mPlaceholderVisibility
= false;
1109 state
->mPreviewVisibility
= false;
1110 state
->mIsCommittingComposition
= false;
1111 // When adding more member variable initializations here, add the same
1112 // also to the constructor.
1116 return new nsTextEditorState(aOwningElement
);
1119 nsTextEditorState::~nsTextEditorState()
1121 MOZ_COUNT_DTOR(nsTextEditorState
);
1126 nsTextEditorState::GetRootNode()
1128 return mBoundFrame
? mBoundFrame
->GetRootNode() : nullptr;
1132 nsTextEditorState::GetPreviewNode()
1134 return mBoundFrame
? mBoundFrame
->GetPreviewNode() : nullptr;
1138 nsTextEditorState::Clear()
1141 mTextEditor
->SetTextInputListener(nullptr);
1145 // Oops, we still have a frame!
1146 // This should happen when the type of a text input control is being changed
1147 // to something which is not a text control. In this case, we should pretend
1148 // that a frame is being destroyed, and clean up after ourselves properly.
1149 UnbindFromFrame(mBoundFrame
);
1150 mTextEditor
= nullptr;
1152 // If we have a bound frame around, UnbindFromFrame will call DestroyEditor
1156 mTextListener
= nullptr;
1159 void nsTextEditorState::Unlink()
1161 nsTextEditorState
* tmp
= this;
1163 NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelCon
)
1164 NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextEditor
)
1167 void nsTextEditorState::Traverse(nsCycleCollectionTraversalCallback
& cb
)
1169 nsTextEditorState
* tmp
= this;
1170 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelCon
)
1171 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextEditor
)
1175 nsTextEditorState::GetConstFrameSelection() {
1177 return mSelCon
->GetConstFrameSelection();
1182 nsTextEditorState::GetTextEditor()
1185 nsresult rv
= PrepareEditor();
1186 NS_ENSURE_SUCCESS(rv
, nullptr);
1191 nsISelectionController
*
1192 nsTextEditorState::GetSelectionController() const
1197 // Helper class, used below in BindToFrame().
1198 class PrepareEditorEvent
: public Runnable
{
1200 PrepareEditorEvent(nsTextEditorState
& aState
,
1201 nsIContent
* aOwnerContent
,
1202 const nsAString
& aCurrentValue
)
1203 : mozilla::Runnable("PrepareEditorEvent")
1205 , mOwnerContent(aOwnerContent
)
1206 , mCurrentValue(aCurrentValue
)
1208 aState
.mValueTransferInProgress
= true;
1211 NS_IMETHOD
Run() override
{
1212 NS_ENSURE_TRUE(mState
, NS_ERROR_NULL_POINTER
);
1214 // Transfer the saved value to the editor if we have one
1215 const nsAString
*value
= nullptr;
1216 if (!mCurrentValue
.IsEmpty()) {
1217 value
= &mCurrentValue
;
1220 nsAutoScriptBlocker scriptBlocker
;
1222 mState
->PrepareEditor(value
);
1224 mState
->mValueTransferInProgress
= false;
1230 WeakPtr
<nsTextEditorState
> mState
;
1231 nsCOMPtr
<nsIContent
> mOwnerContent
; // strong reference
1232 nsAutoString mCurrentValue
;
1236 nsTextEditorState::BindToFrame(nsTextControlFrame
* aFrame
)
1238 NS_ASSERTION(aFrame
, "The frame to bind to should be valid");
1239 NS_ENSURE_ARG_POINTER(aFrame
);
1241 NS_ASSERTION(!mBoundFrame
, "Cannot bind twice, need to unbind first");
1242 NS_ENSURE_TRUE(!mBoundFrame
, NS_ERROR_FAILURE
);
1244 // If we'll need to transfer our current value to the editor, save it before
1245 // binding to the frame.
1246 nsAutoString currentValue
;
1248 GetValue(currentValue
, true);
1251 mBoundFrame
= aFrame
;
1253 Element
* rootNode
= aFrame
->GetRootNode();
1254 MOZ_ASSERT(rootNode
);
1256 nsIPresShell
* shell
= aFrame
->PresContext()->GetPresShell();
1260 RefPtr
<nsFrameSelection
> frameSel
= new nsFrameSelection();
1262 // Create a SelectionController
1263 mSelCon
= new nsTextInputSelectionImpl(frameSel
, shell
, rootNode
);
1264 MOZ_ASSERT(!mTextListener
, "Should not overwrite the object");
1265 mTextListener
= new TextInputListener(mTextCtrlElement
);
1267 mTextListener
->SetFrame(mBoundFrame
);
1268 mSelCon
->SetDisplaySelection(nsISelectionController::SELECTION_ON
);
1270 // Get the caret and make it a selection listener.
1271 // FYI: It's safe to use raw pointer for calling
1272 // Selection::AddSelectionListner() because it only appends the listener
1273 // to its internal array.
1274 Selection
* selection
= mSelCon
->GetSelection(SelectionType::eNormal
);
1276 RefPtr
<nsCaret
> caret
= shell
->GetCaret();
1278 selection
->AddSelectionListener(caret
);
1280 mTextListener
->StartToListenToSelectionChange();
1283 // If an editor exists from before, prepare it for usage
1285 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
1286 NS_ENSURE_TRUE(content
, NS_ERROR_FAILURE
);
1288 // Set the correct direction on the newly created root node
1289 if (mTextEditor
->IsRightToLeft()) {
1290 rootNode
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, NS_LITERAL_STRING("rtl"), false);
1291 } else if (mTextEditor
->IsLeftToRight()) {
1292 rootNode
->SetAttr(kNameSpaceID_None
, nsGkAtoms::dir
, NS_LITERAL_STRING("ltr"), false);
1294 // otherwise, inherit the content node's direction
1297 nsContentUtils::AddScriptRunner(
1298 new PrepareEditorEvent(*this, content
, currentValue
));
1306 void Init(TextEditor
* aTextEditor
) { mTextEditor
= aTextEditor
; }
1310 mTextEditor
->PreDestroy(true);
1313 void Swap(RefPtr
<TextEditor
>& aTextEditor
)
1315 return mTextEditor
.swap(aTextEditor
);
1318 RefPtr
<TextEditor
> mTextEditor
;
1322 nsTextEditorState::PrepareEditor(const nsAString
*aValue
)
1325 // Cannot create an editor without a bound frame.
1326 // Don't return a failure code, because js callers can't handle that.
1330 if (mEditorInitialized
) {
1331 // Do not initialize the editor multiple times.
1335 AutoHideSelectionChanges
hideSelectionChanges(GetConstFrameSelection());
1337 // Don't attempt to initialize recursively!
1338 InitializationGuard
guard(*this);
1339 if (guard
.IsInitializingRecursively()) {
1340 return NS_ERROR_NOT_INITIALIZED
;
1343 // Note that we don't check mTextEditor here, because we might already have
1344 // one around, in which case we don't create a new one, and we'll just tie
1345 // the required machinery to it.
1347 nsPresContext
*presContext
= mBoundFrame
->PresContext();
1348 nsIPresShell
*shell
= presContext
->GetPresShell();
1350 // Setup the editor flags
1351 uint32_t editorFlags
= nsIPlaintextEditor::eEditorPlaintextMask
;
1352 if (IsSingleLineTextControl())
1353 editorFlags
|= nsIPlaintextEditor::eEditorSingleLineMask
;
1354 if (IsPasswordTextControl())
1355 editorFlags
|= nsIPlaintextEditor::eEditorPasswordMask
;
1357 // All nsTextControlFrames are widgets
1358 editorFlags
|= nsIPlaintextEditor::eEditorWidgetMask
;
1360 // Spell check is diabled at creation time. It is enabled once
1361 // the editor comes into focus.
1362 editorFlags
|= nsIPlaintextEditor::eEditorSkipSpellCheck
;
1364 bool shouldInitializeEditor
= false;
1365 RefPtr
<TextEditor
> newTextEditor
; // the editor that we might create
1366 nsresult rv
= NS_OK
;
1367 PreDestroyer preDestroyer
;
1369 shouldInitializeEditor
= true;
1372 newTextEditor
= new TextEditor();
1373 preDestroyer
.Init(newTextEditor
);
1375 // Make sure we clear out the non-breaking space before we initialize the editor
1376 rv
= mBoundFrame
->UpdateValueDisplay(true, true);
1377 NS_ENSURE_SUCCESS(rv
, rv
);
1379 if (aValue
|| !mEditorInitialized
) {
1380 // Set the correct value in the root node
1381 rv
= mBoundFrame
->UpdateValueDisplay(true, !mEditorInitialized
, aValue
);
1382 NS_ENSURE_SUCCESS(rv
, rv
);
1385 newTextEditor
= mTextEditor
; // just pretend that we have a new editor!
1387 // Don't lose application flags in the process.
1388 if (newTextEditor
->IsMailEditor()) {
1389 editorFlags
|= nsIPlaintextEditor::eEditorMailMask
;
1393 // Get the current value of the textfield from the content.
1394 // Note that if we've created a new editor, mTextEditor is null at this stage,
1395 // so we will get the real value from the content.
1396 nsAutoString defaultValue
;
1398 defaultValue
= *aValue
;
1400 GetValue(defaultValue
, true);
1403 if (!mEditorInitialized
) {
1404 // Now initialize the editor.
1406 // NOTE: Conversion of '\n' to <BR> happens inside the
1407 // editor's Init() call.
1409 // Get the DOM document
1410 nsCOMPtr
<nsIDocument
> doc
= shell
->GetDocument();
1411 if (NS_WARN_IF(!doc
)) {
1412 return NS_ERROR_FAILURE
;
1415 // What follows is a bit of a hack. The editor uses the public DOM APIs
1416 // for its content manipulations, and it causes it to fail some security
1417 // checks deep inside when initializing. So we explictly make it clear that
1418 // we're native code.
1419 // Note that any script that's directly trying to access our value
1420 // has to be going through some scriptable object to do that and that
1421 // already does the relevant security checks.
1422 AutoNoJSAPI nojsapi
;
1424 rv
= newTextEditor
->Init(*doc
, GetRootNode(), mSelCon
, editorFlags
,
1426 NS_ENSURE_SUCCESS(rv
, rv
);
1429 // Initialize the controller for the editor
1431 if (!SuppressEventHandlers(presContext
)) {
1432 nsCOMPtr
<nsIControllers
> controllers
;
1433 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
1434 HTMLInputElement
* inputElement
=
1435 HTMLInputElement::FromNodeOrNull(content
);
1437 rv
= inputElement
->GetControllers(getter_AddRefs(controllers
));
1439 HTMLTextAreaElement
* textAreaElement
=
1440 HTMLTextAreaElement::FromNodeOrNull(content
);
1442 if (!textAreaElement
)
1443 return NS_ERROR_FAILURE
;
1445 rv
= textAreaElement
->GetControllers(getter_AddRefs(controllers
));
1448 NS_ENSURE_SUCCESS(rv
, rv
);
1451 uint32_t numControllers
;
1453 rv
= controllers
->GetControllerCount(&numControllers
);
1454 for (uint32_t i
= 0; i
< numControllers
; i
++) {
1455 nsCOMPtr
<nsIController
> controller
;
1456 rv
= controllers
->GetControllerAt(i
, getter_AddRefs(controller
));
1457 if (NS_SUCCEEDED(rv
) && controller
) {
1458 nsCOMPtr
<nsIControllerContext
> editController
=
1459 do_QueryInterface(controller
);
1460 if (editController
) {
1461 editController
->SetCommandContext(
1462 static_cast<nsIEditor
*>(newTextEditor
));
1468 rv
= NS_ERROR_FAILURE
;
1472 // Initialize the plaintext editor
1473 if (shouldInitializeEditor
) {
1475 newTextEditor
->SetWrapColumn(GetWrapCols());
1478 // Set max text field length
1479 newTextEditor
->SetMaxTextLength(GetMaxLength());
1481 if (nsCOMPtr
<Element
> element
= do_QueryInterface(mTextCtrlElement
)) {
1482 editorFlags
= newTextEditor
->Flags();
1484 // Check if the readonly attribute is set.
1485 if (element
->HasAttr(kNameSpaceID_None
, nsGkAtoms::readonly
))
1486 editorFlags
|= nsIPlaintextEditor::eEditorReadonlyMask
;
1488 // Check if the disabled attribute is set.
1489 // TODO: call IsDisabled() here!
1490 if (element
->HasAttr(kNameSpaceID_None
, nsGkAtoms::disabled
))
1491 editorFlags
|= nsIPlaintextEditor::eEditorDisabledMask
;
1493 // Disable the selection if necessary.
1494 if (newTextEditor
->IsDisabled()) {
1495 mSelCon
->SetDisplaySelection(nsISelectionController::SELECTION_OFF
);
1498 SetEditorFlagsIfNecessary(*newTextEditor
, editorFlags
);
1501 if (shouldInitializeEditor
) {
1502 // Hold on to the newly created editor
1503 preDestroyer
.Swap(mTextEditor
);
1506 // If we have a default value, insert it under the div we created
1507 // above, but be sure to use the editor so that '*' characters get
1508 // displayed for password fields, etc. SetValue() will call the
1511 if (!defaultValue
.IsEmpty()) {
1512 rv
= SetEditorFlagsIfNecessary(*newTextEditor
, editorFlags
);
1513 if (NS_WARN_IF(NS_FAILED(rv
))) {
1517 // Now call SetValue() which will make the necessary editor calls to set
1518 // the default value. Make sure to turn off undo before setting the default
1519 // value, and turn it back on afterwards. This will make sure we can't undo
1520 // past the default value.
1521 // So, we use eSetValue_Internal flag only that it will turn off undo.
1523 bool success
= SetValue(defaultValue
, eSetValue_Internal
);
1524 NS_ENSURE_TRUE(success
, NS_ERROR_OUT_OF_MEMORY
);
1526 // Now restore the original editor flags.
1527 rv
= SetEditorFlagsIfNecessary(*newTextEditor
, editorFlags
);
1528 if (NS_WARN_IF(NS_FAILED(rv
))) {
1533 if (IsPasswordTextControl()) {
1534 // Disable undo for <input type="password">. Note that we want to do this
1535 // at the very end of InitEditor(), so the calls to EnableUndoRedo() when
1536 // setting the default value don't screw us up. Since changing the
1537 // control type does a reframe, we don't have to worry about dynamic type
1539 DebugOnly
<bool> disabledUndoRedo
= newTextEditor
->DisableUndoRedo();
1540 NS_WARNING_ASSERTION(disabledUndoRedo
,
1541 "Failed to disable undo/redo transaction");
1543 DebugOnly
<bool> enabledUndoRedo
=
1544 newTextEditor
->EnableUndoRedo(nsITextControlElement::DEFAULT_UNDO_CAP
);
1545 NS_WARNING_ASSERTION(enabledUndoRedo
,
1546 "Failed to enable undo/redo transaction");
1549 if (!mEditorInitialized
) {
1550 newTextEditor
->PostCreate();
1552 mEditorInitialized
= true;
1555 if (mTextListener
) {
1556 newTextEditor
->SetTextInputListener(mTextListener
);
1559 // Restore our selection after being bound to a new frame
1560 HTMLInputElement
* number
= GetParentNumberControl(mBoundFrame
);
1561 if (number
? number
->IsSelectionCached() : mSelectionCached
) {
1562 if (mRestoringSelection
) // paranoia
1563 mRestoringSelection
->Revoke();
1564 mRestoringSelection
= new RestoreSelectionState(this, mBoundFrame
);
1565 if (mRestoringSelection
) {
1566 nsContentUtils::AddScriptRunner(mRestoringSelection
);
1570 // The selection cache is no longer going to be valid.
1572 // XXXbz Shouldn't we do this at the point when we're actually about to
1573 // restore the properties or something? As things stand, if UnbindFromFrame
1574 // happens before our RestoreSelectionState runs, it looks like we'll lose our
1575 // selection info, because we will think we don't have it cached and try to
1576 // read it from the selection controller, which will not have it yet.
1578 number
->ClearSelectionCached();
1580 mSelectionCached
= false;
1587 nsTextEditorState::FinishedRestoringSelection()
1589 mRestoringSelection
= nullptr;
1593 nsTextEditorState::IsSelectionCached() const
1596 HTMLInputElement
* number
= GetParentNumberControl(mBoundFrame
);
1598 return number
->IsSelectionCached();
1601 return mSelectionCached
;
1604 nsTextEditorState::SelectionProperties
&
1605 nsTextEditorState::GetSelectionProperties()
1608 HTMLInputElement
* number
= GetParentNumberControl(mBoundFrame
);
1610 return number
->GetSelectionProperties();
1613 return mSelectionProperties
;
1617 nsTextEditorState::SyncUpSelectionPropertiesBeforeDestruction()
1620 UnbindFromFrame(mBoundFrame
);
1625 nsTextEditorState::SetSelectionProperties(nsTextEditorState::SelectionProperties
& aProps
)
1628 mBoundFrame
->SetSelectionRange(aProps
.GetStart(),
1630 aProps
.GetDirection());
1632 mSelectionProperties
= aProps
;
1637 nsTextEditorState::GetSelectionRange(uint32_t* aSelectionStart
,
1638 uint32_t* aSelectionEnd
,
1641 MOZ_ASSERT(aSelectionStart
);
1642 MOZ_ASSERT(aSelectionEnd
);
1643 MOZ_ASSERT(IsSelectionCached() || GetSelectionController(),
1644 "How can we not have a cached selection if we have no selection "
1647 // Note that we may have both IsSelectionCached() _and_
1648 // GetSelectionController() if we haven't initialized our editor yet.
1649 if (IsSelectionCached()) {
1650 const SelectionProperties
& props
= GetSelectionProperties();
1651 *aSelectionStart
= props
.GetStart();
1652 *aSelectionEnd
= props
.GetEnd();
1656 Selection
* sel
= mSelCon
->GetSelection(SelectionType::eNormal
);
1657 if (NS_WARN_IF(!sel
)) {
1658 aRv
.Throw(NS_ERROR_FAILURE
);
1662 mozilla::dom::Element
* root
= GetRootNode();
1663 if (NS_WARN_IF(!root
)) {
1664 aRv
.Throw(NS_ERROR_UNEXPECTED
);
1667 nsContentUtils::GetSelectionInTextControl(sel
, root
,
1668 *aSelectionStart
, *aSelectionEnd
);
1671 nsITextControlFrame::SelectionDirection
1672 nsTextEditorState::GetSelectionDirection(ErrorResult
& aRv
)
1674 MOZ_ASSERT(IsSelectionCached() || GetSelectionController(),
1675 "How can we not have a cached selection if we have no selection "
1678 // Note that we may have both IsSelectionCached() _and_
1679 // GetSelectionController() if we haven't initialized our editor yet.
1680 if (IsSelectionCached()) {
1681 return GetSelectionProperties().GetDirection();
1684 Selection
* sel
= mSelCon
->GetSelection(SelectionType::eNormal
);
1685 if (NS_WARN_IF(!sel
)) {
1686 aRv
.Throw(NS_ERROR_FAILURE
);
1687 return nsITextControlFrame::eForward
; // Doesn't really matter
1690 nsDirection direction
= sel
->GetDirection();
1691 if (direction
== eDirNext
) {
1692 return nsITextControlFrame::eForward
;
1695 MOZ_ASSERT(direction
== eDirPrevious
);
1696 return nsITextControlFrame::eBackward
;
1700 nsTextEditorState::SetSelectionRange(uint32_t aStart
, uint32_t aEnd
,
1701 nsITextControlFrame::SelectionDirection aDirection
,
1704 MOZ_ASSERT(IsSelectionCached() || mBoundFrame
,
1705 "How can we have a non-cached selection but no frame?");
1707 if (aStart
> aEnd
) {
1711 bool changed
= false;
1712 nsresult rv
= NS_OK
; // For the ScrollSelectionIntoView() return value.
1713 if (IsSelectionCached()) {
1715 // XXXbz is "false" the right thing to pass here? Hard to tell, given the
1716 // various mismatches between our impl and the spec.
1717 GetValue(value
, false);
1718 uint32_t length
= value
.Length();
1719 if (aStart
> length
) {
1722 if (aEnd
> length
) {
1725 SelectionProperties
& props
= GetSelectionProperties();
1726 changed
= props
.GetStart() != aStart
||
1727 props
.GetEnd() != aEnd
||
1728 props
.GetDirection() != aDirection
;
1729 props
.SetStart(aStart
);
1731 props
.SetDirection(aDirection
);
1733 WeakPtr
<nsTextEditorState
> self(this);
1734 aRv
= mBoundFrame
->SetSelectionRange(aStart
, aEnd
, aDirection
);
1735 if (aRv
.Failed() || !self
.get()) {
1738 rv
= mBoundFrame
->ScrollSelectionIntoView();
1739 // Press on to firing the event even if that failed, like our old code did.
1740 // But is that really what we want? Firing the event _and_ throwing from
1741 // here is weird. Maybe we should just ignore ScrollSelectionIntoView
1744 // XXXbz This is preserving our current behavior of firing a "select" event
1745 // on all mutations when we have an editor, but we should really consider
1751 // It sure would be nice if we had an existing Element* or so to work with.
1752 nsCOMPtr
<nsINode
> node
= do_QueryInterface(mTextCtrlElement
);
1753 RefPtr
<AsyncEventDispatcher
> asyncDispatcher
=
1754 new AsyncEventDispatcher(node
,
1755 NS_LITERAL_STRING("select"),
1757 ChromeOnlyDispatch::eNo
);
1758 asyncDispatcher
->PostDOMEvent();
1761 if (NS_FAILED(rv
)) {
1767 nsTextEditorState::SetSelectionStart(const Nullable
<uint32_t>& aStart
,
1771 if (!aStart
.IsNull()) {
1772 start
= aStart
.Value();
1775 uint32_t ignored
, end
;
1776 GetSelectionRange(&ignored
, &end
, aRv
);
1781 nsITextControlFrame::SelectionDirection dir
= GetSelectionDirection(aRv
);
1790 SetSelectionRange(start
, end
, dir
, aRv
);
1794 nsTextEditorState::SetSelectionEnd(const Nullable
<uint32_t>& aEnd
,
1798 if (!aEnd
.IsNull()) {
1802 uint32_t start
, ignored
;
1803 GetSelectionRange(&start
, &ignored
, aRv
);
1808 nsITextControlFrame::SelectionDirection dir
= GetSelectionDirection(aRv
);
1813 SetSelectionRange(start
, end
, dir
, aRv
);
1817 DirectionToName(nsITextControlFrame::SelectionDirection dir
, nsAString
& aDirection
)
1819 if (dir
== nsITextControlFrame::eNone
) {
1820 NS_WARNING("We don't actually support this... how did we get it?");
1821 aDirection
.AssignLiteral("none");
1822 } else if (dir
== nsITextControlFrame::eForward
) {
1823 aDirection
.AssignLiteral("forward");
1824 } else if (dir
== nsITextControlFrame::eBackward
) {
1825 aDirection
.AssignLiteral("backward");
1827 MOZ_ASSERT_UNREACHABLE("Invalid SelectionDirection value");
1832 nsTextEditorState::GetSelectionDirectionString(nsAString
& aDirection
,
1835 nsITextControlFrame::SelectionDirection dir
= GetSelectionDirection(aRv
);
1839 DirectionToName(dir
, aDirection
);
1842 static nsITextControlFrame::SelectionDirection
1843 DirectionStringToSelectionDirection(const nsAString
& aDirection
)
1845 if (aDirection
.EqualsLiteral("backward")) {
1846 return nsITextControlFrame::eBackward
;
1849 // We don't support directionless selections.
1850 return nsITextControlFrame::eForward
;
1854 nsTextEditorState::SetSelectionDirection(const nsAString
& aDirection
,
1857 nsITextControlFrame::SelectionDirection dir
=
1858 DirectionStringToSelectionDirection(aDirection
);
1860 if (IsSelectionCached()) {
1861 GetSelectionProperties().SetDirection(dir
);
1865 uint32_t start
, end
;
1866 GetSelectionRange(&start
, &end
, aRv
);
1871 SetSelectionRange(start
, end
, dir
, aRv
);
1874 static nsITextControlFrame::SelectionDirection
1875 DirectionStringToSelectionDirection(const Optional
<nsAString
>& aDirection
)
1877 if (!aDirection
.WasPassed()) {
1878 // We don't support directionless selections.
1879 return nsITextControlFrame::eForward
;
1882 return DirectionStringToSelectionDirection(aDirection
.Value());
1886 nsTextEditorState::SetSelectionRange(uint32_t aSelectionStart
,
1887 uint32_t aSelectionEnd
,
1888 const Optional
<nsAString
>& aDirection
,
1891 nsITextControlFrame::SelectionDirection dir
=
1892 DirectionStringToSelectionDirection(aDirection
);
1894 SetSelectionRange(aSelectionStart
, aSelectionEnd
, dir
, aRv
);
1898 nsTextEditorState::SetRangeText(const nsAString
& aReplacement
,
1901 uint32_t start
, end
;
1902 GetSelectionRange(&start
, &end
, aRv
);
1907 SetRangeText(aReplacement
, start
, end
, SelectionMode::Preserve
,
1908 aRv
, Some(start
), Some(end
));
1912 nsTextEditorState::SetRangeText(const nsAString
& aReplacement
, uint32_t aStart
,
1913 uint32_t aEnd
, SelectionMode aSelectMode
,
1915 const Maybe
<uint32_t>& aSelectionStart
,
1916 const Maybe
<uint32_t>& aSelectionEnd
)
1918 if (aStart
> aEnd
) {
1919 aRv
.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR
);
1924 mTextCtrlElement
->GetValueFromSetRangeText(value
);
1925 uint32_t inputValueLength
= value
.Length();
1927 if (aStart
> inputValueLength
) {
1928 aStart
= inputValueLength
;
1931 if (aEnd
> inputValueLength
) {
1932 aEnd
= inputValueLength
;
1935 uint32_t selectionStart
, selectionEnd
;
1936 if (!aSelectionStart
) {
1937 MOZ_ASSERT(!aSelectionEnd
);
1938 GetSelectionRange(&selectionStart
, &selectionEnd
, aRv
);
1943 MOZ_ASSERT(aSelectionEnd
);
1944 selectionStart
= *aSelectionStart
;
1945 selectionEnd
= *aSelectionEnd
;
1948 MOZ_ASSERT(aStart
<= aEnd
);
1949 value
.Replace(aStart
, aEnd
- aStart
, aReplacement
);
1950 nsresult rv
= mTextCtrlElement
->SetValueFromSetRangeText(value
);
1951 if (NS_FAILED(rv
)) {
1956 uint32_t newEnd
= aStart
+ aReplacement
.Length();
1957 int32_t delta
= aReplacement
.Length() - (aEnd
- aStart
);
1959 switch (aSelectMode
) {
1960 case mozilla::dom::SelectionMode::Select
:
1962 selectionStart
= aStart
;
1963 selectionEnd
= newEnd
;
1966 case mozilla::dom::SelectionMode::Start
:
1968 selectionStart
= selectionEnd
= aStart
;
1971 case mozilla::dom::SelectionMode::End
:
1973 selectionStart
= selectionEnd
= newEnd
;
1976 case mozilla::dom::SelectionMode::Preserve
:
1978 if (selectionStart
> aEnd
) {
1979 selectionStart
+= delta
;
1980 } else if (selectionStart
> aStart
) {
1981 selectionStart
= aStart
;
1984 if (selectionEnd
> aEnd
) {
1985 selectionEnd
+= delta
;
1986 } else if (selectionEnd
> aStart
) {
1987 selectionEnd
= newEnd
;
1992 MOZ_CRASH("Unknown mode!");
1995 SetSelectionRange(selectionStart
, selectionEnd
, Optional
<nsAString
>(), aRv
);
1999 nsTextEditorState::GetParentNumberControl(nsFrame
* aFrame
) const
2002 nsIContent
* content
= aFrame
->GetContent();
2003 MOZ_ASSERT(content
);
2004 nsIContent
* parent
= content
->GetParent();
2008 nsIContent
* parentOfParent
= parent
->GetParent();
2009 if (!parentOfParent
) {
2012 HTMLInputElement
* input
= HTMLInputElement::FromNode(parentOfParent
);
2014 // This function might be called during frame reconstruction as a result
2015 // of changing the input control's type from number to something else. In
2016 // that situation, the type of the control has changed, but its frame has
2017 // not been reconstructed yet. So we need to check the type of the input
2018 // control in addition to the type of the frame.
2019 return (input
->ControlType() == NS_FORM_INPUT_NUMBER
) ? input
: nullptr;
2026 nsTextEditorState::DestroyEditor()
2028 // notify the editor that we are going away
2029 if (mEditorInitialized
) {
2030 mTextEditor
->PreDestroy(true);
2031 mEditorInitialized
= false;
2036 nsTextEditorState::UnbindFromFrame(nsTextControlFrame
* aFrame
)
2038 NS_ENSURE_TRUE_VOID(mBoundFrame
);
2040 // If it was, however, it should be unbounded from the same frame.
2041 MOZ_ASSERT(aFrame
== mBoundFrame
, "Unbinding from the wrong frame");
2042 NS_ENSURE_TRUE_VOID(!aFrame
|| aFrame
== mBoundFrame
);
2044 // If the editor is modified but nsIEditorObserver::EditAction() hasn't been
2045 // called yet, we need to notify it here because editor may be destroyed
2046 // before EditAction() is called if selection listener causes flushing layout.
2047 if (mTextListener
&& mTextEditor
&& mEditorInitialized
&&
2048 mTextEditor
->IsInEditSubAction()) {
2049 mTextListener
->OnEditActionHandled();
2052 // We need to start storing the value outside of the editor if we're not
2053 // going to use it anymore, so retrieve it for now.
2055 GetValue(value
, true);
2057 if (mRestoringSelection
) {
2058 mRestoringSelection
->Revoke();
2059 mRestoringSelection
= nullptr;
2062 // Save our selection state if needed.
2063 // Note that GetSelectionRange will attempt to work with our selection
2064 // controller, so we should make sure we do it before we start doing things
2065 // like destroying our editor (if we have one), tearing down the selection
2066 // controller, and so forth.
2067 if (!IsSelectionCached()) {
2068 // Go ahead and cache it now.
2069 uint32_t start
= 0, end
= 0;
2070 GetSelectionRange(&start
, &end
, IgnoreErrors());
2072 nsITextControlFrame::SelectionDirection direction
=
2073 GetSelectionDirection(IgnoreErrors());
2075 SelectionProperties
& props
= GetSelectionProperties();
2076 props
.SetStart(start
);
2078 props
.SetDirection(direction
);
2079 HTMLInputElement
* number
= GetParentNumberControl(aFrame
);
2081 // If we are inside a number control, cache the selection on the
2082 // parent control, because this text editor state will be destroyed
2083 // together with the native anonymous text control.
2084 number
->SetSelectionCached();
2086 mSelectionCached
= true;
2090 // Destroy our editor
2093 // Clean up the controller
2094 if (!SuppressEventHandlers(mBoundFrame
->PresContext()))
2096 nsCOMPtr
<nsIControllers
> controllers
;
2097 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
2098 HTMLInputElement
* inputElement
=
2099 HTMLInputElement::FromNodeOrNull(content
);
2101 inputElement
->GetControllers(getter_AddRefs(controllers
));
2104 HTMLTextAreaElement
* textAreaElement
=
2105 HTMLTextAreaElement::FromNodeOrNull(content
);
2106 if (textAreaElement
) {
2107 textAreaElement
->GetControllers(getter_AddRefs(controllers
));
2113 uint32_t numControllers
;
2114 nsresult rv
= controllers
->GetControllerCount(&numControllers
);
2115 NS_ASSERTION((NS_SUCCEEDED(rv
)), "bad result in gfx text control destructor");
2116 for (uint32_t i
= 0; i
< numControllers
; i
++)
2118 nsCOMPtr
<nsIController
> controller
;
2119 rv
= controllers
->GetControllerAt(i
, getter_AddRefs(controller
));
2120 if (NS_SUCCEEDED(rv
) && controller
)
2122 nsCOMPtr
<nsIControllerContext
> editController
= do_QueryInterface(controller
);
2125 editController
->SetCommandContext(nullptr);
2133 if (mTextListener
) {
2134 mTextListener
->EndListeningToSelectionChange();
2137 mSelCon
->SetScrollableFrame(nullptr);
2143 mTextListener
->SetFrame(nullptr);
2145 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(mTextCtrlElement
);
2146 EventListenerManager
* manager
= target
->GetExistingListenerManager();
2148 manager
->RemoveEventListenerByType(mTextListener
,
2149 NS_LITERAL_STRING("keydown"),
2150 TrustedEventsAtSystemGroupBubble());
2151 manager
->RemoveEventListenerByType(mTextListener
,
2152 NS_LITERAL_STRING("keypress"),
2153 TrustedEventsAtSystemGroupBubble());
2154 manager
->RemoveEventListenerByType(mTextListener
,
2155 NS_LITERAL_STRING("keyup"),
2156 TrustedEventsAtSystemGroupBubble());
2159 mTextListener
= nullptr;
2162 mBoundFrame
= nullptr;
2164 // Now that we don't have a frame any more, store the value in the text buffer.
2165 // The only case where we don't do this is if a value transfer is in progress.
2166 if (!mValueTransferInProgress
) {
2167 bool success
= SetValue(value
, eSetValue_Internal
);
2168 // TODO Find something better to do if this fails...
2169 NS_ENSURE_TRUE_VOID(success
);
2174 nsTextEditorState::GetMaxLength()
2176 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
2177 nsGenericHTMLElement
* element
=
2178 nsGenericHTMLElement::FromNodeOrNull(content
);
2179 if (NS_WARN_IF(!element
)) {
2183 const nsAttrValue
* attr
= element
->GetParsedAttr(nsGkAtoms::maxlength
);
2184 if (attr
&& attr
->Type() == nsAttrValue::eInteger
) {
2185 return attr
->GetIntegerValue();
2192 nsTextEditorState::GetValue(nsAString
& aValue
, bool aIgnoreWrap
) const
2194 // While SetValue() is being called and requesting to commit composition to
2195 // IME, GetValue() may be called for appending text or something. Then, we
2196 // need to return the latest aValue of SetValue() since the value hasn't
2197 // been set to the editor yet.
2198 if (mIsCommittingComposition
) {
2199 aValue
= mValueBeingSet
;
2203 if (mTextEditor
&& mBoundFrame
&&
2204 (mEditorInitialized
|| !IsSingleLineTextControl())) {
2205 if (aIgnoreWrap
&& !mBoundFrame
->CachedValue().IsVoid()) {
2206 aValue
= mBoundFrame
->CachedValue();
2210 aValue
.Truncate(); // initialize out param
2212 uint32_t flags
= (nsIDocumentEncoder::OutputLFLineBreak
|
2213 nsIDocumentEncoder::OutputPreformatted
|
2214 nsIDocumentEncoder::OutputPersistNBSP
|
2215 nsIDocumentEncoder::OutputBodyOnly
);
2217 nsITextControlElement::nsHTMLTextWrap wrapProp
;
2218 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
2220 nsITextControlElement::GetWrapPropertyEnum(content
, wrapProp
) &&
2221 wrapProp
== nsITextControlElement::eHTMLTextWrap_Hard
) {
2222 flags
|= nsIDocumentEncoder::OutputWrap
;
2226 // What follows is a bit of a hack. The problem is that we could be in
2227 // this method because we're being destroyed for whatever reason while
2228 // script is executing. If that happens, editor will run with the
2229 // privileges of the executing script, which means it may not be able to
2230 // access its own DOM nodes! Let's try to deal with that by pushing a null
2231 // JSContext on the JSContext stack to make it clear that we're native
2232 // code. Note that any script that's directly trying to access our value
2233 // has to be going through some scriptable object to do that and that
2234 // already does the relevant security checks.
2235 // XXXbz if we could just get the textContent of our anonymous content (eg
2236 // if plaintext editor didn't create <br> nodes all over), we wouldn't need
2238 { /* Scope for AutoNoJSAPI. */
2239 AutoNoJSAPI nojsapi
;
2241 DebugOnly
<nsresult
> rv
= mTextEditor
->ComputeTextValue(flags
, aValue
);
2242 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
), "Failed to get value");
2244 // Only when the result doesn't include line breaks caused by hard-wrap,
2245 // mCacheValue should cache the value.
2246 if (!(flags
& nsIDocumentEncoder::OutputWrap
)) {
2247 mBoundFrame
->CacheValue(aValue
);
2249 mBoundFrame
->ClearCachedValue();
2252 if (!mTextCtrlElement
->ValueChanged() || !mValue
) {
2253 mTextCtrlElement
->GetDefaultValueFromContent(aValue
);
2261 nsTextEditorState::SetValue(const nsAString
& aValue
, const nsAString
* aOldValue
,
2264 nsAutoString
newValue(aValue
);
2266 // While mIsCommittingComposition is true (that means that some event
2267 // handlers which are fired during committing composition are the caller of
2268 // this method), GetValue() uses mValueBeingSet for its result because the
2269 // first calls of this methods hasn't set the value yet. So, when it's true,
2270 // we need to modify mValueBeingSet. In this case, we will back to the first
2271 // call of this method, then, mValueBeingSet will be truncated when
2272 // mIsCommittingComposition is set false. See below.
2273 if (mIsCommittingComposition
) {
2274 mValueBeingSet
= aValue
;
2275 // GetValue doesn't return current text frame's content during committing.
2276 // So we cannot trust this old value
2277 aOldValue
= nullptr;
2280 // Note that if this may be called during reframe of the editor. In such
2281 // case, we shouldn't commit composition. Therefore, when this is called
2282 // for internal processing, we shouldn't commit the composition.
2283 if (aFlags
& (eSetValue_BySetUserInput
| eSetValue_ByContent
)) {
2284 if (EditorHasComposition()) {
2285 // When this is called recursively, there shouldn't be composition.
2286 if (NS_WARN_IF(mIsCommittingComposition
)) {
2287 // Don't request to commit composition again. But if it occurs,
2288 // we should skip to set the new value to the editor here. It should
2289 // be set later with the updated mValueBeingSet.
2292 if (NS_WARN_IF(!mBoundFrame
)) {
2293 // We're not sure if this case is possible.
2295 // If setting value won't change current value, we shouldn't commit
2296 // composition for compatibility with the other browsers.
2297 nsAutoString currentValue
;
2300 mBoundFrame
->GetText(currentValue
);
2301 MOZ_ASSERT(currentValue
.Equals(*aOldValue
));
2303 currentValue
.Assign(*aOldValue
);
2305 mBoundFrame
->GetText(currentValue
);
2307 if (newValue
== currentValue
) {
2308 // Note that in this case, we shouldn't fire any events with setting
2309 // value because event handlers may try to set value recursively but
2310 // we cannot commit composition at that time due to unsafe to run
2311 // script (see below).
2314 // IME might commit composition, then change value, so we cannot
2315 // trust old value from parameter.
2316 aOldValue
= nullptr;
2318 // If there is composition, need to commit composition first because
2319 // other browsers do that.
2320 // NOTE: We don't need to block nested calls of this because input nor
2321 // other events won't be fired by setting values and script blocker
2322 // is used during setting the value to the editor. IE also allows
2323 // to set the editor value on the input event which is caused by
2324 // forcibly committing composition.
2325 if (nsContentUtils::IsSafeToRunScript()) {
2326 WeakPtr
<nsTextEditorState
> self(this);
2327 // WARNING: During this call, compositionupdate, compositionend, input
2328 // events will be fired. Therefore, everything can occur. E.g., the
2329 // document may be unloaded.
2330 mValueBeingSet
= aValue
;
2331 mIsCommittingComposition
= true;
2332 RefPtr
<TextEditor
> textEditor
= mTextEditor
;
2333 nsresult rv
= textEditor
->CommitComposition();
2337 mIsCommittingComposition
= false;
2338 // If this is called recursively during committing composition and
2339 // some of them may be skipped above. Therefore, we need to set
2340 // value to the editor with the aValue of the latest call.
2341 newValue
= mValueBeingSet
;
2342 // When mIsCommittingComposition is false, mValueBeingSet won't be
2343 // used. Therefore, let's clear it.
2344 mValueBeingSet
.Truncate();
2345 if (NS_FAILED(rv
)) {
2346 NS_WARNING("nsTextEditorState failed to commit composition");
2350 NS_WARNING("SetValue() is called when there is composition but "
2351 "it's not safe to request to commit the composition");
2356 // \r is an illegal character in the dom, but people use them,
2357 // so convert windows and mac platform linebreaks to \n:
2358 if (!nsContentUtils::PlatformToDOMLineBreaks(newValue
, fallible
)) {
2362 if (mTextEditor
&& mBoundFrame
) {
2363 // The InsertText call below might flush pending notifications, which
2364 // could lead into a scheduled PrepareEditor to be called. That will
2365 // lead to crashes (or worse) because we'd be initializing the editor
2366 // before InsertText returns. This script blocker makes sure that
2367 // PrepareEditor cannot be called prematurely.
2368 nsAutoScriptBlocker scriptBlocker
;
2371 if (IsSingleLineTextControl()) {
2372 NS_ASSERTION(mEditorInitialized
|| mInitializing
,
2373 "We should never try to use the editor if we're not initialized unless we're being initialized");
2377 nsAutoString currentValue
;
2380 mBoundFrame
->GetText(currentValue
);
2381 MOZ_ASSERT(currentValue
.Equals(*aOldValue
));
2383 currentValue
.Assign(*aOldValue
);
2385 mBoundFrame
->GetText(currentValue
);
2388 AutoWeakFrame
weakFrame(mBoundFrame
);
2390 // this is necessary to avoid infinite recursion
2391 if (!currentValue
.Equals(newValue
)) {
2392 RefPtr
<TextEditor
> textEditor
= mTextEditor
;
2393 ValueSetter
valueSetter(textEditor
);
2395 nsCOMPtr
<nsIDocument
> document
= textEditor
->GetDocument();
2396 if (NS_WARN_IF(!document
)) {
2400 // Time to mess with our security context... See comments in GetValue()
2401 // for why this is needed. Note that we have to do this up here, because
2402 // otherwise SelectAll() will fail.
2404 AutoNoJSAPI nojsapi
;
2406 // FYI: It's safe to use raw pointer for selection here because
2407 // SelectionBatcher will grab it with RefPtr.
2408 Selection
* selection
=
2409 mSelCon
->GetSelection(SelectionType::eNormal
);
2410 SelectionBatcher
selectionBatcher(selection
);
2412 if (NS_WARN_IF(!weakFrame
.IsAlive())) {
2418 // get the flags, remove readonly, disabled and max-length,
2419 // set the value, restore flags
2421 AutoRestoreEditorState
restoreState(textEditor
);
2423 mTextListener
->SettingValue(true);
2424 bool notifyValueChanged
= !!(aFlags
& eSetValue_Notify
);
2425 mTextListener
->SetValueChanged(notifyValueChanged
);
2427 if (aFlags
& eSetValue_BySetUserInput
) {
2428 // If the caller inserts text as part of user input, for example,
2429 // autocomplete, we need to replace the text as "insert string"
2430 // because undo should cancel only this operation (i.e., previous
2431 // transactions typed by user shouldn't be merged with this).
2432 DebugOnly
<nsresult
> rv
= textEditor
->ReplaceTextAsAction(newValue
);
2433 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
2434 "Failed to set the new value");
2435 } else if (aFlags
& eSetValue_ForXUL
) {
2436 // On XUL <textbox> element, we need to preserve existing undo
2438 // XXX Do we really need to do such complicated optimization?
2439 // This was landed for web pages which set <textarea> value
2440 // per line (bug 518122). For example:
2442 // textarea.value += oneLineText + "\n";
2444 // However, this path won't be used in web content anymore.
2445 nsCOMPtr
<nsISelectionController
> kungFuDeathGrip
= mSelCon
.get();
2446 uint32_t currentLength
= currentValue
.Length();
2447 uint32_t newlength
= newValue
.Length();
2448 if (!currentLength
||
2449 !StringBeginsWith(newValue
, currentValue
)) {
2450 // Replace the whole text.
2452 kungFuDeathGrip
->SelectAll();
2454 // Collapse selection to the end so that we can append data.
2455 mBoundFrame
->SelectAllOrCollapseToEndOfText(false);
2457 const nsAString
& insertValue
=
2458 StringTail(newValue
, newlength
- currentLength
);
2460 if (insertValue
.IsEmpty()) {
2461 DebugOnly
<nsresult
> rv
=
2462 textEditor
->DeleteSelectionAsAction(nsIEditor::eNone
,
2464 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
2465 "Failed to remove the text");
2467 DebugOnly
<nsresult
> rv
=
2468 textEditor
->InsertTextAsAction(insertValue
);
2469 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv
),
2470 "Failed to insert the new value");
2473 // On <input> or <textarea>, we shouldn't preserve existing undo
2474 // transactions because other browsers do not preserve them too
2475 // and not preserving transactions makes setting value faster.
2476 AutoDisableUndo
disableUndo(textEditor
);
2478 // Since we don't use undo transaction, we don't need to store
2479 // selection state. SetText will set selection to tail.
2480 // Note that textEditor will collapse selection to the end.
2481 // Therefore, it's safe to use RemoveAllRangesTemporarily() here.
2482 selection
->RemoveAllRangesTemporarily();
2485 textEditor
->SetText(newValue
);
2487 // Call the listener's HandleValueChanged() callback manually, since
2488 // we don't use the transaction manager in this path and it could be
2489 // that the editor would bypass calling the listener for that reason.
2490 mTextListener
->HandleValueChanged();
2493 mTextListener
->SetValueChanged(true);
2494 mTextListener
->SettingValue(false);
2496 if (!notifyValueChanged
) {
2497 // Listener doesn't update frame, but it is required for placeholder
2498 ValueWasChanged(true);
2502 if (!weakFrame
.IsAlive()) {
2503 // If the frame was destroyed because of a flush somewhere inside
2504 // InsertText, mBoundFrame here will be false. But it's also possible
2505 // for the frame to go away because of another reason (such as deleting
2506 // the existing selection -- see bug 574558), in which case we don't
2507 // need to reset the value here.
2509 return SetValue(newValue
, aFlags
& eSetValue_Notify
);
2514 // The new value never includes line breaks caused by hard-wrap.
2515 // So, mCachedValue can always cache the new value.
2516 if (!mBoundFrame
->CacheValue(newValue
, fallible
)) {
2526 // We can't just early-return here if mValue->Equals(newValue), because
2527 // ValueWasChanged and OnValueChanged below still need to be called.
2528 if (!mValue
->Equals(newValue
) ||
2529 !nsContentUtils::SkipCursorMoveForSameValueSet()) {
2530 if (!mValue
->Assign(newValue
, fallible
)) {
2534 // Since we have no editor we presumably have cached selection state.
2535 if (IsSelectionCached()) {
2536 SelectionProperties
& props
= GetSelectionProperties();
2537 if (aFlags
& eSetValue_MoveCursorToEndIfValueChanged
) {
2538 props
.SetStart(newValue
.Length());
2539 props
.SetEnd(newValue
.Length());
2540 props
.SetDirection(nsITextControlFrame::eForward
);
2542 // Make sure our cached selection position is not outside the new value.
2543 props
.SetStart(std::min(props
.GetStart(), newValue
.Length()));
2544 props
.SetEnd(std::min(props
.GetEnd(), newValue
.Length()));
2548 // Update the frame display if needed
2550 mBoundFrame
->UpdateValueDisplay(true);
2553 // Even if our value is not actually changing, apparently we need to mark
2554 // our SelectionProperties dirty to make accessibility tests happy.
2555 // Probably because they depend on the SetSelectionRange() call we make on
2556 // our frame in RestoreSelectionState, but I have no idea why they do.
2557 if (IsSelectionCached()) {
2558 SelectionProperties
& props
= GetSelectionProperties();
2563 // If we've reached the point where the root node has been created, we
2564 // can assume that it's safe to notify.
2565 ValueWasChanged(!!mBoundFrame
);
2568 mTextCtrlElement
->OnValueChanged(/* aNotify = */ !!mBoundFrame
,
2569 /* aWasInteractiveUserChange = */ false);
2575 nsTextEditorState::HasNonEmptyValue()
2577 if (mTextEditor
&& mBoundFrame
&& mEditorInitialized
&&
2578 !mIsCommittingComposition
) {
2580 nsresult rv
= mTextEditor
->IsEmpty(&empty
);
2581 if (NS_SUCCEEDED(rv
)) {
2587 GetValue(value
, true);
2588 return !value
.IsEmpty();
2592 nsTextEditorState::InitializeKeyboardEventListeners()
2594 //register key listeners
2595 nsCOMPtr
<EventTarget
> target
= do_QueryInterface(mTextCtrlElement
);
2596 EventListenerManager
* manager
= target
->GetOrCreateListenerManager();
2598 manager
->AddEventListenerByType(mTextListener
,
2599 NS_LITERAL_STRING("keydown"),
2600 TrustedEventsAtSystemGroupBubble());
2601 manager
->AddEventListenerByType(mTextListener
,
2602 NS_LITERAL_STRING("keypress"),
2603 TrustedEventsAtSystemGroupBubble());
2604 manager
->AddEventListenerByType(mTextListener
,
2605 NS_LITERAL_STRING("keyup"),
2606 TrustedEventsAtSystemGroupBubble());
2609 mSelCon
->SetScrollableFrame(do_QueryFrame(mBoundFrame
->PrincipalChildList().FirstChild()));
2613 nsTextEditorState::ValueWasChanged(bool aNotify
)
2615 UpdateOverlayTextVisibility(aNotify
);
2619 nsTextEditorState::SetPreviewText(const nsAString
& aValue
, bool aNotify
)
2621 // If we don't have a preview div, there's nothing to do.
2622 Element
* previewDiv
= GetPreviewNode();
2626 nsAutoString
previewValue(aValue
);
2628 nsContentUtils::RemoveNewlines(previewValue
);
2629 MOZ_ASSERT(previewDiv
->GetFirstChild(), "preview div has no child");
2630 previewDiv
->GetFirstChild()->AsText()->SetText(previewValue
, aNotify
);
2632 UpdateOverlayTextVisibility(aNotify
);
2636 nsTextEditorState::GetPreviewText(nsAString
& aValue
)
2638 // If we don't have a preview div, there's nothing to do.
2639 Element
* previewDiv
= GetPreviewNode();
2643 MOZ_ASSERT(previewDiv
->GetFirstChild(), "preview div has no child");
2644 const nsTextFragment
*text
= previewDiv
->GetFirstChild()->GetText();
2647 text
->AppendTo(aValue
);
2651 nsTextEditorState::UpdateOverlayTextVisibility(bool aNotify
)
2653 nsAutoString value
, previewValue
;
2654 bool valueIsEmpty
= !HasNonEmptyValue();
2655 GetPreviewText(previewValue
);
2657 mPreviewVisibility
= valueIsEmpty
&& !previewValue
.IsEmpty();
2658 mPlaceholderVisibility
= valueIsEmpty
&& previewValue
.IsEmpty();
2660 if (mPlaceholderVisibility
&&
2661 !nsContentUtils::ShowInputPlaceholderOnFocus()) {
2662 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
2663 mPlaceholderVisibility
= !nsContentUtils::IsFocusedContent(content
);
2666 if (mBoundFrame
&& aNotify
) {
2667 mBoundFrame
->InvalidateFrame();
2672 nsTextEditorState::HideSelectionIfBlurred()
2674 MOZ_ASSERT(mSelCon
, "Should have a selection controller if we have a frame!");
2675 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(mTextCtrlElement
);
2676 if (!nsContentUtils::IsFocusedContent(content
)) {
2677 mSelCon
->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN
);
2682 nsTextEditorState::EditorHasComposition()
2684 return mTextEditor
&& mTextEditor
->IsIMEComposing();