Line data Source code
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/. */
6 :
7 : #include "nsTextEditorState.h"
8 :
9 : #include "nsCOMPtr.h"
10 : #include "nsIPresShell.h"
11 : #include "nsView.h"
12 : #include "nsCaret.h"
13 : #include "nsEditorCID.h"
14 : #include "nsLayoutCID.h"
15 : #include "nsITextControlFrame.h"
16 : #include "nsIDOMCharacterData.h"
17 : #include "nsIDOMDocument.h"
18 : #include "nsContentCreatorFunctions.h"
19 : #include "nsTextControlFrame.h"
20 : #include "nsIControllers.h"
21 : #include "nsIDOMHTMLInputElement.h"
22 : #include "nsIDOMHTMLTextAreaElement.h"
23 : #include "nsITransactionManager.h"
24 : #include "nsIControllerContext.h"
25 : #include "nsAttrValue.h"
26 : #include "nsAttrValueInlines.h"
27 : #include "nsGenericHTMLElement.h"
28 : #include "nsIDOMEventListener.h"
29 : #include "nsIEditorObserver.h"
30 : #include "nsIWidget.h"
31 : #include "nsIDocumentEncoder.h"
32 : #include "nsISelectionPrivate.h"
33 : #include "nsPIDOMWindow.h"
34 : #include "nsServiceManagerUtils.h"
35 : #include "mozilla/dom/Selection.h"
36 : #include "mozilla/TextEditRules.h"
37 : #include "mozilla/EventListenerManager.h"
38 : #include "nsContentUtils.h"
39 : #include "mozilla/Preferences.h"
40 : #include "nsTextNode.h"
41 : #include "nsIController.h"
42 : #include "mozilla/AutoRestore.h"
43 : #include "mozilla/TextEvents.h"
44 : #include "mozilla/dom/ScriptSettings.h"
45 : #include "mozilla/dom/HTMLInputElement.h"
46 : #include "nsNumberControlFrame.h"
47 : #include "nsFrameSelection.h"
48 : #include "mozilla/ErrorResult.h"
49 : #include "mozilla/Telemetry.h"
50 : #include "mozilla/layers/ScrollInputMethods.h"
51 :
52 : using namespace mozilla;
53 : using namespace mozilla::dom;
54 : using mozilla::layers::ScrollInputMethod;
55 :
56 : static NS_DEFINE_CID(kTextEditorCID, NS_TEXTEDITOR_CID);
57 :
58 : class MOZ_STACK_CLASS ValueSetter
59 : {
60 : public:
61 1 : explicit ValueSetter(TextEditor* aTextEditor)
62 1 : : mTextEditor(aTextEditor)
63 : // To protect against a reentrant call to SetValue, we check whether
64 : // another SetValue is already happening for this editor. If it is,
65 : // we must wait until we unwind to re-enable oninput events.
66 1 : , mOuterTransaction(aTextEditor->IsSuppressingDispatchingInputEvent())
67 : {
68 1 : MOZ_ASSERT(aTextEditor);
69 1 : }
70 1 : ~ValueSetter()
71 1 : {
72 1 : mTextEditor->SetSuppressDispatchingInputEvent(mOuterTransaction);
73 1 : }
74 1 : void Init()
75 : {
76 1 : mTextEditor->SetSuppressDispatchingInputEvent(true);
77 1 : }
78 :
79 : private:
80 : RefPtr<TextEditor> mTextEditor;
81 : bool mOuterTransaction;
82 : };
83 :
84 6 : class RestoreSelectionState : public Runnable {
85 : public:
86 2 : RestoreSelectionState(nsTextEditorState* aState, nsTextControlFrame* aFrame)
87 2 : : mozilla::Runnable("RestoreSelectionState")
88 : , mFrame(aFrame)
89 2 : , mTextEditorState(aState)
90 : {
91 2 : }
92 :
93 2 : NS_IMETHOD Run() override {
94 2 : if (!mTextEditorState) {
95 0 : return NS_OK;
96 : }
97 :
98 : AutoHideSelectionChanges hideSelectionChanges
99 4 : (mFrame->GetConstFrameSelection());
100 :
101 2 : if (mFrame) {
102 : // SetSelectionRange leads to Selection::AddRange which flushes Layout -
103 : // need to block script to avoid nested PrepareEditor calls (bug 642800).
104 4 : nsAutoScriptBlocker scriptBlocker;
105 : nsTextEditorState::SelectionProperties& properties =
106 2 : mTextEditorState->GetSelectionProperties();
107 2 : if (properties.IsDirty()) {
108 2 : mFrame->SetSelectionRange(properties.GetStart(),
109 : properties.GetEnd(),
110 2 : properties.GetDirection());
111 : }
112 2 : if (!mTextEditorState->mSelectionRestoreEagerInit) {
113 2 : mTextEditorState->HideSelectionIfBlurred();
114 : }
115 2 : mTextEditorState->mSelectionRestoreEagerInit = false;
116 : }
117 :
118 2 : if (mTextEditorState) {
119 2 : mTextEditorState->FinishedRestoringSelection();
120 : }
121 2 : return NS_OK;
122 : }
123 :
124 : // Let the text editor tell us we're no longer relevant - avoids use of AutoWeakFrame
125 0 : void Revoke() {
126 0 : mFrame = nullptr;
127 0 : mTextEditorState = nullptr;
128 0 : }
129 :
130 : private:
131 : nsTextControlFrame* mFrame;
132 : nsTextEditorState* mTextEditorState;
133 : };
134 :
135 : class MOZ_RAII AutoRestoreEditorState final
136 : {
137 : public:
138 1 : explicit AutoRestoreEditorState(TextEditor* aTextEditor
139 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
140 1 : : mTextEditor(aTextEditor)
141 1 : , mSavedFlags(mTextEditor->Flags())
142 2 : , mSavedMaxLength(mTextEditor->MaxTextLength())
143 : {
144 1 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
145 1 : MOZ_ASSERT(mTextEditor);
146 :
147 1 : uint32_t flags = mSavedFlags;
148 1 : flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
149 1 : flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
150 1 : flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
151 1 : mTextEditor->SetFlags(flags);
152 :
153 1 : mTextEditor->SetMaxTextLength(-1);
154 1 : }
155 :
156 1 : ~AutoRestoreEditorState()
157 1 : {
158 1 : mTextEditor->SetMaxTextLength(mSavedMaxLength);
159 1 : mTextEditor->SetFlags(mSavedFlags);
160 1 : }
161 :
162 : private:
163 : TextEditor* mTextEditor;
164 : uint32_t mSavedFlags;
165 : int32_t mSavedMaxLength;
166 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
167 : };
168 :
169 : class MOZ_RAII AutoDisableUndo final
170 : {
171 : public:
172 1 : explicit AutoDisableUndo(TextEditor* aTextEditor
173 : MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
174 1 : : mTextEditor(aTextEditor)
175 1 : , mPreviousEnabled(true)
176 : {
177 1 : MOZ_GUARD_OBJECT_NOTIFIER_INIT;
178 1 : MOZ_ASSERT(mTextEditor);
179 :
180 : bool canUndo;
181 2 : DebugOnly<nsresult> rv = mTextEditor->CanUndo(&mPreviousEnabled, &canUndo);
182 1 : MOZ_ASSERT(NS_SUCCEEDED(rv));
183 :
184 1 : mTextEditor->EnableUndo(false);
185 1 : }
186 :
187 1 : ~AutoDisableUndo()
188 1 : {
189 1 : mTextEditor->EnableUndo(mPreviousEnabled);
190 1 : }
191 :
192 : private:
193 : TextEditor* mTextEditor;
194 : bool mPreviousEnabled;
195 : MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
196 : };
197 :
198 : /*static*/
199 : bool
200 0 : nsITextControlElement::GetWrapPropertyEnum(nsIContent* aContent,
201 : nsITextControlElement::nsHTMLTextWrap& aWrapProp)
202 : {
203 : // soft is the default; "physical" defaults to soft as well because all other
204 : // browsers treat it that way and there is no real reason to maintain physical
205 : // and virtual as separate entities if no one else does. Only hard and off
206 : // do anything different.
207 0 : aWrapProp = eHTMLTextWrap_Soft; // the default
208 :
209 0 : nsAutoString wrap;
210 0 : if (aContent->IsHTMLElement()) {
211 : static nsIContent::AttrValuesArray strings[] =
212 : {&nsGkAtoms::HARD, &nsGkAtoms::OFF, nullptr};
213 :
214 0 : switch (aContent->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::wrap,
215 0 : strings, eIgnoreCase)) {
216 0 : case 0: aWrapProp = eHTMLTextWrap_Hard; break;
217 0 : case 1: aWrapProp = eHTMLTextWrap_Off; break;
218 : }
219 :
220 0 : return true;
221 : }
222 :
223 0 : return false;
224 : }
225 :
226 : /*static*/
227 : already_AddRefed<nsITextControlElement>
228 0 : nsITextControlElement::GetTextControlElementFromEditingHost(nsIContent* aHost)
229 : {
230 0 : if (!aHost) {
231 0 : return nullptr;
232 : }
233 :
234 : nsCOMPtr<nsITextControlElement> parent =
235 0 : do_QueryInterface(aHost->GetParent());
236 :
237 0 : return parent.forget();
238 : }
239 :
240 : static bool
241 4 : SuppressEventHandlers(nsPresContext* aPresContext)
242 : {
243 4 : bool suppressHandlers = false;
244 :
245 4 : if (aPresContext)
246 : {
247 : // Right now we only suppress event handlers and controller manipulation
248 : // when in a print preview or print context!
249 :
250 : // In the current implementation, we only paginate when
251 : // printing or in print preview.
252 :
253 4 : suppressHandlers = aPresContext->IsPaginated();
254 : }
255 :
256 4 : return suppressHandlers;
257 : }
258 :
259 : class nsAnonDivObserver final : public nsStubMutationObserver
260 : {
261 : public:
262 0 : explicit nsAnonDivObserver(nsTextEditorState* aTextEditorState)
263 0 : : mTextEditorState(aTextEditorState) {}
264 : NS_DECL_ISUPPORTS
265 : NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
266 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
267 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
268 : NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
269 :
270 : private:
271 0 : ~nsAnonDivObserver() {}
272 : nsTextEditorState* mTextEditorState;
273 : };
274 :
275 : class nsTextInputSelectionImpl final : public nsSupportsWeakReference
276 : , public nsISelectionController
277 : {
278 1 : ~nsTextInputSelectionImpl(){}
279 :
280 : public:
281 : NS_DECL_CYCLE_COLLECTING_ISUPPORTS
282 197 : NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsTextInputSelectionImpl, nsISelectionController)
283 :
284 : nsTextInputSelectionImpl(nsFrameSelection *aSel, nsIPresShell *aShell, nsIContent *aLimiter);
285 :
286 : void SetScrollableFrame(nsIScrollableFrame *aScrollableFrame);
287 8 : nsFrameSelection* GetConstFrameSelection()
288 8 : { return mFrameSelection; }
289 : // Will return null if !mFrameSelection.
290 : Selection* GetSelection(SelectionType aSelectionType);
291 :
292 : //NSISELECTIONCONTROLLER INTERFACES
293 : NS_IMETHOD SetDisplaySelection(int16_t toggle) override;
294 : NS_IMETHOD GetDisplaySelection(int16_t* _retval) override;
295 : NS_IMETHOD SetSelectionFlags(int16_t aInEnable) override;
296 : NS_IMETHOD GetSelectionFlags(int16_t *aOutEnable) override;
297 : NS_IMETHOD GetSelection(RawSelectionType aRawSelectionType,
298 : nsISelection** aSelection) override;
299 : NS_IMETHOD ScrollSelectionIntoView(RawSelectionType aRawSelectionType,
300 : int16_t aRegion, int16_t aFlags) override;
301 : NS_IMETHOD RepaintSelection(RawSelectionType aRawSelectionType) override;
302 : nsresult RepaintSelection(nsPresContext* aPresContext,
303 : SelectionType aSelectionType);
304 : NS_IMETHOD SetCaretEnabled(bool enabled) override;
305 : NS_IMETHOD SetCaretReadOnly(bool aReadOnly) override;
306 : NS_IMETHOD GetCaretEnabled(bool* _retval) override;
307 : NS_IMETHOD GetCaretVisible(bool* _retval) override;
308 : NS_IMETHOD SetCaretVisibilityDuringSelection(bool aVisibility) override;
309 : NS_IMETHOD PhysicalMove(int16_t aDirection, int16_t aAmount, bool aExtend) override;
310 : NS_IMETHOD CharacterMove(bool aForward, bool aExtend) override;
311 : NS_IMETHOD CharacterExtendForDelete() override;
312 : NS_IMETHOD CharacterExtendForBackspace() override;
313 : NS_IMETHOD WordMove(bool aForward, bool aExtend) override;
314 : NS_IMETHOD WordExtendForDelete(bool aForward) override;
315 : NS_IMETHOD LineMove(bool aForward, bool aExtend) override;
316 : NS_IMETHOD IntraLineMove(bool aForward, bool aExtend) override;
317 : NS_IMETHOD PageMove(bool aForward, bool aExtend) override;
318 : NS_IMETHOD CompleteScroll(bool aForward) override;
319 : NS_IMETHOD CompleteMove(bool aForward, bool aExtend) override;
320 : NS_IMETHOD ScrollPage(bool aForward) override;
321 : NS_IMETHOD ScrollLine(bool aForward) override;
322 : NS_IMETHOD ScrollCharacter(bool aRight) override;
323 : NS_IMETHOD SelectAll(void) override;
324 : NS_IMETHOD CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool* _retval) override;
325 : virtual nsresult CheckVisibilityContent(nsIContent* aNode, int16_t aStartOffset, int16_t aEndOffset, bool* aRetval) override;
326 :
327 : private:
328 : RefPtr<nsFrameSelection> mFrameSelection;
329 : nsCOMPtr<nsIContent> mLimiter;
330 : nsIScrollableFrame *mScrollFrame;
331 : nsWeakPtr mPresShellWeak;
332 : };
333 :
334 95 : NS_IMPL_CYCLE_COLLECTING_ADDREF(nsTextInputSelectionImpl)
335 91 : NS_IMPL_CYCLE_COLLECTING_RELEASE(nsTextInputSelectionImpl)
336 83 : NS_INTERFACE_TABLE_HEAD(nsTextInputSelectionImpl)
337 83 : NS_INTERFACE_TABLE(nsTextInputSelectionImpl,
338 : nsISelectionController,
339 : nsISelectionDisplay,
340 : nsISupportsWeakReference)
341 83 : NS_INTERFACE_TABLE_TO_MAP_SEGUE_CYCLE_COLLECTION(nsTextInputSelectionImpl)
342 14 : NS_INTERFACE_MAP_END
343 :
344 0 : NS_IMPL_CYCLE_COLLECTION(nsTextInputSelectionImpl, mFrameSelection, mLimiter)
345 :
346 :
347 : // BEGIN nsTextInputSelectionImpl
348 :
349 4 : nsTextInputSelectionImpl::nsTextInputSelectionImpl(nsFrameSelection *aSel,
350 : nsIPresShell *aShell,
351 4 : nsIContent *aLimiter)
352 4 : : mScrollFrame(nullptr)
353 : {
354 4 : if (aSel && aShell)
355 : {
356 4 : mFrameSelection = aSel;//we are the owner now!
357 4 : mLimiter = aLimiter;
358 4 : mFrameSelection->Init(aShell, mLimiter);
359 4 : mPresShellWeak = do_GetWeakReference(aShell);
360 : }
361 4 : }
362 :
363 : void
364 6 : nsTextInputSelectionImpl::SetScrollableFrame(nsIScrollableFrame *aScrollableFrame)
365 : {
366 6 : mScrollFrame = aScrollableFrame;
367 6 : if (!mScrollFrame && mFrameSelection) {
368 2 : mFrameSelection->DisconnectFromPresShell();
369 2 : mFrameSelection = nullptr;
370 : }
371 6 : }
372 :
373 : Selection*
374 6 : nsTextInputSelectionImpl::GetSelection(SelectionType aSelectionType)
375 : {
376 6 : if (!mFrameSelection) {
377 0 : return nullptr;
378 : }
379 :
380 6 : return mFrameSelection->GetSelection(aSelectionType);
381 : }
382 :
383 : NS_IMETHODIMP
384 8 : nsTextInputSelectionImpl::SetDisplaySelection(int16_t aToggle)
385 : {
386 8 : if (!mFrameSelection)
387 0 : return NS_ERROR_NULL_POINTER;
388 :
389 8 : mFrameSelection->SetDisplaySelection(aToggle);
390 8 : return NS_OK;
391 : }
392 :
393 : NS_IMETHODIMP
394 0 : nsTextInputSelectionImpl::GetDisplaySelection(int16_t *aToggle)
395 : {
396 0 : if (!mFrameSelection)
397 0 : return NS_ERROR_NULL_POINTER;
398 :
399 0 : *aToggle = mFrameSelection->GetDisplaySelection();
400 0 : return NS_OK;
401 : }
402 :
403 : NS_IMETHODIMP
404 2 : nsTextInputSelectionImpl::SetSelectionFlags(int16_t aToggle)
405 : {
406 2 : return NS_OK;//stub this out. not used in input
407 : }
408 :
409 : NS_IMETHODIMP
410 0 : nsTextInputSelectionImpl::GetSelectionFlags(int16_t *aOutEnable)
411 : {
412 0 : *aOutEnable = nsISelectionDisplay::DISPLAY_TEXT;
413 0 : return NS_OK;
414 : }
415 :
416 : NS_IMETHODIMP
417 41 : nsTextInputSelectionImpl::GetSelection(RawSelectionType aRawSelectionType,
418 : nsISelection** aSelection)
419 : {
420 41 : if (!mFrameSelection)
421 0 : return NS_ERROR_NULL_POINTER;
422 :
423 41 : *aSelection =
424 41 : mFrameSelection->GetSelection(ToSelectionType(aRawSelectionType));
425 :
426 : // GetSelection() fails only when aRawSelectionType is invalid value.
427 41 : if (!(*aSelection)) {
428 0 : return NS_ERROR_INVALID_ARG;
429 : }
430 :
431 41 : NS_ADDREF(*aSelection);
432 41 : return NS_OK;
433 : }
434 :
435 : NS_IMETHODIMP
436 3 : nsTextInputSelectionImpl::ScrollSelectionIntoView(
437 : RawSelectionType aRawSelectionType,
438 : int16_t aRegion,
439 : int16_t aFlags)
440 : {
441 3 : if (!mFrameSelection)
442 0 : return NS_ERROR_FAILURE;
443 :
444 6 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
445 3 : return frameSelection->ScrollSelectionIntoView(
446 : ToSelectionType(aRawSelectionType),
447 3 : aRegion, aFlags);
448 : }
449 :
450 : NS_IMETHODIMP
451 0 : nsTextInputSelectionImpl::RepaintSelection(RawSelectionType aRawSelectionType)
452 : {
453 0 : if (!mFrameSelection)
454 0 : return NS_ERROR_FAILURE;
455 :
456 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
457 0 : return frameSelection->RepaintSelection(ToSelectionType(aRawSelectionType));
458 : }
459 :
460 : nsresult
461 0 : nsTextInputSelectionImpl::RepaintSelection(nsPresContext* aPresContext,
462 : SelectionType aSelectionType)
463 : {
464 0 : if (!mFrameSelection)
465 0 : return NS_ERROR_FAILURE;
466 :
467 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
468 0 : return frameSelection->RepaintSelection(aSelectionType);
469 : }
470 :
471 : NS_IMETHODIMP
472 0 : nsTextInputSelectionImpl::SetCaretEnabled(bool enabled)
473 : {
474 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
475 :
476 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak);
477 0 : if (!shell) return NS_ERROR_FAILURE;
478 :
479 : // tell the pres shell to enable the caret, rather than settings its visibility directly.
480 : // this way the presShell's idea of caret visibility is maintained.
481 0 : nsCOMPtr<nsISelectionController> selCon = do_QueryInterface(shell);
482 0 : if (!selCon) return NS_ERROR_NO_INTERFACE;
483 0 : selCon->SetCaretEnabled(enabled);
484 :
485 0 : return NS_OK;
486 : }
487 :
488 : NS_IMETHODIMP
489 2 : nsTextInputSelectionImpl::SetCaretReadOnly(bool aReadOnly)
490 : {
491 2 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
492 : nsresult result;
493 4 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
494 2 : if (shell)
495 : {
496 2 : RefPtr<nsCaret> caret = shell->GetCaret();
497 2 : if (caret) {
498 : nsISelection* domSel =
499 2 : mFrameSelection->GetSelection(SelectionType::eNormal);
500 2 : if (domSel)
501 2 : caret->SetCaretReadOnly(aReadOnly);
502 2 : return NS_OK;
503 : }
504 : }
505 0 : return NS_ERROR_FAILURE;
506 : }
507 :
508 : NS_IMETHODIMP
509 0 : nsTextInputSelectionImpl::GetCaretEnabled(bool *_retval)
510 : {
511 0 : return GetCaretVisible(_retval);
512 : }
513 :
514 : NS_IMETHODIMP
515 0 : nsTextInputSelectionImpl::GetCaretVisible(bool *_retval)
516 : {
517 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
518 : nsresult result;
519 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
520 0 : if (shell)
521 : {
522 0 : RefPtr<nsCaret> caret = shell->GetCaret();
523 0 : if (caret) {
524 0 : *_retval = caret->IsVisible();
525 0 : return NS_OK;
526 : }
527 : }
528 0 : return NS_ERROR_FAILURE;
529 : }
530 :
531 : NS_IMETHODIMP
532 0 : nsTextInputSelectionImpl::SetCaretVisibilityDuringSelection(bool aVisibility)
533 : {
534 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
535 : nsresult result;
536 0 : nsCOMPtr<nsIPresShell> shell = do_QueryReferent(mPresShellWeak, &result);
537 0 : if (shell)
538 : {
539 0 : RefPtr<nsCaret> caret = shell->GetCaret();
540 0 : if (caret) {
541 : nsISelection* domSel =
542 0 : mFrameSelection->GetSelection(SelectionType::eNormal);
543 0 : if (domSel)
544 0 : caret->SetVisibilityDuringSelection(aVisibility);
545 0 : return NS_OK;
546 : }
547 : }
548 0 : return NS_ERROR_FAILURE;
549 : }
550 :
551 : NS_IMETHODIMP
552 0 : nsTextInputSelectionImpl::PhysicalMove(int16_t aDirection, int16_t aAmount,
553 : bool aExtend)
554 : {
555 0 : if (mFrameSelection) {
556 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
557 0 : return frameSelection->PhysicalMove(aDirection, aAmount, aExtend);
558 : }
559 0 : return NS_ERROR_NULL_POINTER;
560 : }
561 :
562 : NS_IMETHODIMP
563 0 : nsTextInputSelectionImpl::CharacterMove(bool aForward, bool aExtend)
564 : {
565 0 : if (mFrameSelection) {
566 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
567 0 : return frameSelection->CharacterMove(aForward, aExtend);
568 : }
569 0 : return NS_ERROR_NULL_POINTER;
570 : }
571 :
572 : NS_IMETHODIMP
573 0 : nsTextInputSelectionImpl::CharacterExtendForDelete()
574 : {
575 0 : if (mFrameSelection) {
576 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
577 0 : return frameSelection->CharacterExtendForDelete();
578 : }
579 0 : return NS_ERROR_NULL_POINTER;
580 : }
581 :
582 : NS_IMETHODIMP
583 0 : nsTextInputSelectionImpl::CharacterExtendForBackspace()
584 : {
585 0 : if (mFrameSelection) {
586 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
587 0 : return frameSelection->CharacterExtendForBackspace();
588 : }
589 0 : return NS_ERROR_NULL_POINTER;
590 : }
591 :
592 : NS_IMETHODIMP
593 0 : nsTextInputSelectionImpl::WordMove(bool aForward, bool aExtend)
594 : {
595 0 : if (mFrameSelection) {
596 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
597 0 : return frameSelection->WordMove(aForward, aExtend);
598 : }
599 0 : return NS_ERROR_NULL_POINTER;
600 : }
601 :
602 : NS_IMETHODIMP
603 0 : nsTextInputSelectionImpl::WordExtendForDelete(bool aForward)
604 : {
605 0 : if (mFrameSelection) {
606 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
607 0 : return frameSelection->WordExtendForDelete(aForward);
608 : }
609 0 : return NS_ERROR_NULL_POINTER;
610 : }
611 :
612 : NS_IMETHODIMP
613 0 : nsTextInputSelectionImpl::LineMove(bool aForward, bool aExtend)
614 : {
615 0 : if (mFrameSelection)
616 : {
617 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
618 0 : nsresult result = frameSelection->LineMove(aForward, aExtend);
619 0 : if (NS_FAILED(result))
620 0 : result = CompleteMove(aForward,aExtend);
621 0 : return result;
622 : }
623 0 : return NS_ERROR_NULL_POINTER;
624 : }
625 :
626 :
627 : NS_IMETHODIMP
628 0 : nsTextInputSelectionImpl::IntraLineMove(bool aForward, bool aExtend)
629 : {
630 0 : if (mFrameSelection) {
631 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
632 0 : return frameSelection->IntraLineMove(aForward, aExtend);
633 : }
634 0 : return NS_ERROR_NULL_POINTER;
635 : }
636 :
637 :
638 : NS_IMETHODIMP
639 0 : nsTextInputSelectionImpl::PageMove(bool aForward, bool aExtend)
640 : {
641 : // expected behavior for PageMove is to scroll AND move the caret
642 : // and to remain relative position of the caret in view. see Bug 4302.
643 0 : if (mScrollFrame)
644 : {
645 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
646 0 : frameSelection->CommonPageMove(aForward, aExtend, mScrollFrame);
647 : }
648 : // After ScrollSelectionIntoView(), the pending notifications might be
649 : // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
650 : return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
651 : nsISelectionController::SELECTION_FOCUS_REGION,
652 : nsISelectionController::SCROLL_SYNCHRONOUS |
653 0 : nsISelectionController::SCROLL_FOR_CARET_MOVE);
654 : }
655 :
656 : NS_IMETHODIMP
657 0 : nsTextInputSelectionImpl::CompleteScroll(bool aForward)
658 : {
659 0 : if (!mScrollFrame)
660 0 : return NS_ERROR_NOT_INITIALIZED;
661 :
662 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
663 0 : (uint32_t) ScrollInputMethod::MainThreadCompleteScroll);
664 :
665 0 : mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
666 : nsIScrollableFrame::WHOLE,
667 0 : nsIScrollableFrame::INSTANT);
668 0 : return NS_OK;
669 : }
670 :
671 : NS_IMETHODIMP
672 0 : nsTextInputSelectionImpl::CompleteMove(bool aForward, bool aExtend)
673 : {
674 0 : NS_ENSURE_STATE(mFrameSelection);
675 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
676 :
677 : // grab the parent / root DIV for this text widget
678 0 : nsIContent* parentDIV = frameSelection->GetLimiter();
679 0 : if (!parentDIV)
680 0 : return NS_ERROR_UNEXPECTED;
681 :
682 : // make the caret be either at the very beginning (0) or the very end
683 0 : int32_t offset = 0;
684 0 : CaretAssociationHint hint = CARET_ASSOCIATE_BEFORE;
685 0 : if (aForward)
686 : {
687 0 : offset = parentDIV->GetChildCount();
688 :
689 : // Prevent the caret from being placed after the last
690 : // BR node in the content tree!
691 :
692 0 : if (offset > 0)
693 : {
694 0 : nsIContent *child = parentDIV->GetLastChild();
695 :
696 0 : if (child->IsHTMLElement(nsGkAtoms::br))
697 : {
698 0 : --offset;
699 0 : hint = CARET_ASSOCIATE_AFTER; // for Bug 106855
700 : }
701 : }
702 : }
703 :
704 0 : frameSelection->HandleClick(parentDIV, offset, offset, aExtend,
705 0 : false, hint);
706 :
707 : // if we got this far, attempt to scroll no matter what the above result is
708 0 : return CompleteScroll(aForward);
709 : }
710 :
711 : NS_IMETHODIMP
712 0 : nsTextInputSelectionImpl::ScrollPage(bool aForward)
713 : {
714 0 : if (!mScrollFrame)
715 0 : return NS_ERROR_NOT_INITIALIZED;
716 :
717 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
718 0 : (uint32_t) ScrollInputMethod::MainThreadScrollPage);
719 :
720 0 : mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
721 : nsIScrollableFrame::PAGES,
722 0 : nsIScrollableFrame::SMOOTH);
723 0 : return NS_OK;
724 : }
725 :
726 : NS_IMETHODIMP
727 0 : nsTextInputSelectionImpl::ScrollLine(bool aForward)
728 : {
729 0 : if (!mScrollFrame)
730 0 : return NS_ERROR_NOT_INITIALIZED;
731 :
732 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
733 0 : (uint32_t) ScrollInputMethod::MainThreadScrollLine);
734 :
735 0 : mScrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
736 : nsIScrollableFrame::LINES,
737 0 : nsIScrollableFrame::SMOOTH);
738 0 : return NS_OK;
739 : }
740 :
741 : NS_IMETHODIMP
742 0 : nsTextInputSelectionImpl::ScrollCharacter(bool aRight)
743 : {
744 0 : if (!mScrollFrame)
745 0 : return NS_ERROR_NOT_INITIALIZED;
746 :
747 : mozilla::Telemetry::Accumulate(mozilla::Telemetry::SCROLL_INPUT_METHODS,
748 0 : (uint32_t) ScrollInputMethod::MainThreadScrollCharacter);
749 :
750 0 : mScrollFrame->ScrollBy(nsIntPoint(aRight ? 1 : -1, 0),
751 : nsIScrollableFrame::LINES,
752 0 : nsIScrollableFrame::SMOOTH);
753 0 : return NS_OK;
754 : }
755 :
756 : NS_IMETHODIMP
757 0 : nsTextInputSelectionImpl::SelectAll()
758 : {
759 0 : if (mFrameSelection) {
760 0 : RefPtr<nsFrameSelection> frameSelection = mFrameSelection;
761 0 : return frameSelection->SelectAll();
762 : }
763 0 : return NS_ERROR_NULL_POINTER;
764 : }
765 :
766 : NS_IMETHODIMP
767 0 : nsTextInputSelectionImpl::CheckVisibility(nsIDOMNode *node, int16_t startOffset, int16_t EndOffset, bool *_retval)
768 : {
769 0 : if (!mPresShellWeak) return NS_ERROR_NOT_INITIALIZED;
770 : nsresult result;
771 0 : nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak, &result);
772 0 : if (shell)
773 : {
774 0 : return shell->CheckVisibility(node,startOffset,EndOffset, _retval);
775 : }
776 0 : return NS_ERROR_FAILURE;
777 :
778 : }
779 :
780 : nsresult
781 0 : nsTextInputSelectionImpl::CheckVisibilityContent(nsIContent* aNode,
782 : int16_t aStartOffset,
783 : int16_t aEndOffset,
784 : bool* aRetval)
785 : {
786 0 : if (!mPresShellWeak) {
787 0 : return NS_ERROR_NOT_INITIALIZED;
788 : }
789 :
790 0 : nsCOMPtr<nsISelectionController> shell = do_QueryReferent(mPresShellWeak);
791 0 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
792 :
793 0 : return shell->CheckVisibilityContent(aNode, aStartOffset, aEndOffset, aRetval);
794 : }
795 :
796 : class nsTextInputListener : public nsISelectionListener,
797 : public nsIDOMEventListener,
798 : public nsIEditorObserver,
799 : public nsSupportsWeakReference
800 : {
801 : public:
802 : /** the default constructor
803 : */
804 : explicit nsTextInputListener(nsITextControlElement* aTxtCtrlElement);
805 :
806 : /** SetEditor gives an address to the editor that will be accessed
807 : * @param aEditor the editor this listener calls for editing operations
808 : */
809 6 : void SetFrame(nsTextControlFrame *aFrame){mFrame = aFrame;}
810 :
811 2 : void SettingValue(bool aValue) { mSettingValue = aValue; }
812 2 : void SetValueChanged(bool aSetValueChanged) { mSetValueChanged = aSetValueChanged; }
813 :
814 : NS_DECL_ISUPPORTS
815 :
816 : NS_DECL_NSISELECTIONLISTENER
817 :
818 : NS_DECL_NSIDOMEVENTLISTENER
819 :
820 : NS_DECL_NSIEDITOROBSERVER
821 :
822 : protected:
823 : /** the default destructor. virtual due to the possibility of derivation.
824 : */
825 : virtual ~nsTextInputListener();
826 :
827 : nsresult UpdateTextInputCommands(const nsAString& commandsToUpdate,
828 : nsISelection* sel = nullptr,
829 : int16_t reason = 0);
830 :
831 : protected:
832 :
833 : nsIFrame* mFrame;
834 :
835 : nsITextControlElement* const mTxtCtrlElement;
836 :
837 : bool mSelectionWasCollapsed;
838 : /**
839 : * Whether we had undo items or not the last time we got EditAction()
840 : * notification (when this state changes we update undo and redo menus)
841 : */
842 : bool mHadUndoItems;
843 : /**
844 : * Whether we had redo items or not the last time we got EditAction()
845 : * notification (when this state changes we update undo and redo menus)
846 : */
847 : bool mHadRedoItems;
848 : /**
849 : * Whether we're in the process of a SetValue call, and should therefore
850 : * refrain from calling OnValueChanged.
851 : */
852 : bool mSettingValue;
853 : /**
854 : * Whether we are in the process of a SetValue call that doesn't want
855 : * |SetValueChanged| to be called.
856 : */
857 : bool mSetValueChanged;
858 : };
859 :
860 :
861 : /*
862 : * nsTextInputListener implementation
863 : */
864 :
865 4 : nsTextInputListener::nsTextInputListener(nsITextControlElement* aTxtCtrlElement)
866 : : mFrame(nullptr)
867 : , mTxtCtrlElement(aTxtCtrlElement)
868 : , mSelectionWasCollapsed(true)
869 : , mHadUndoItems(false)
870 : , mHadRedoItems(false)
871 : , mSettingValue(false)
872 4 : , mSetValueChanged(true)
873 : {
874 4 : }
875 :
876 4 : nsTextInputListener::~nsTextInputListener()
877 : {
878 6 : }
879 :
880 107 : NS_IMPL_ISUPPORTS(nsTextInputListener,
881 : nsISelectionListener,
882 : nsIEditorObserver,
883 : nsISupportsWeakReference,
884 : nsIDOMEventListener)
885 :
886 : // BEGIN nsIDOMSelectionListener
887 :
888 : NS_IMETHODIMP
889 17 : nsTextInputListener::NotifySelectionChanged(nsIDOMDocument* aDoc, nsISelection* aSel, int16_t aReason)
890 : {
891 : bool collapsed;
892 34 : AutoWeakFrame weakFrame = mFrame;
893 :
894 17 : if (!aDoc || !aSel || NS_FAILED(aSel->GetIsCollapsed(&collapsed)))
895 0 : return NS_OK;
896 :
897 : // Fire the select event
898 : // The specs don't exactly say when we should fire the select event.
899 : // IE: Whenever you add/remove a character to/from the selection. Also
900 : // each time for select all. Also if you get to the end of the text
901 : // field you will get new event for each keypress or a continuous
902 : // stream of events if you use the mouse. IE will fire select event
903 : // when the selection collapses to nothing if you are holding down
904 : // the shift or mouse button.
905 : // Mozilla: If we have non-empty selection we will fire a new event for each
906 : // keypress (or mouseup) if the selection changed. Mozilla will also
907 : // create the event each time select all is called, even if everything
908 : // was previously selected, becase technically select all will first collapse
909 : // and then extend. Mozilla will never create an event if the selection
910 : // collapses to nothing.
911 17 : if (!collapsed && (aReason & (nsISelectionListener::MOUSEUP_REASON |
912 : nsISelectionListener::KEYPRESS_REASON |
913 : nsISelectionListener::SELECTALL_REASON)))
914 : {
915 0 : nsIContent* content = mFrame->GetContent();
916 0 : if (content)
917 : {
918 0 : nsCOMPtr<nsIDocument> doc = content->GetComposedDoc();
919 0 : if (doc)
920 : {
921 0 : nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
922 0 : if (presShell)
923 : {
924 0 : nsEventStatus status = nsEventStatus_eIgnore;
925 0 : WidgetEvent event(true, eFormSelect);
926 :
927 0 : presShell->HandleEventWithTarget(&event, mFrame, content, &status);
928 : }
929 : }
930 : }
931 : }
932 :
933 : // if the collapsed state did not change, don't fire notifications
934 17 : if (collapsed == mSelectionWasCollapsed)
935 17 : return NS_OK;
936 :
937 0 : mSelectionWasCollapsed = collapsed;
938 :
939 0 : if (!weakFrame.IsAlive() || !nsContentUtils::IsFocusedContent(mFrame->GetContent()))
940 0 : return NS_OK;
941 :
942 0 : return UpdateTextInputCommands(NS_LITERAL_STRING("select"), aSel, aReason);
943 : }
944 :
945 : // END nsIDOMSelectionListener
946 :
947 : static void
948 0 : DoCommandCallback(Command aCommand, void* aData)
949 : {
950 0 : nsTextControlFrame *frame = static_cast<nsTextControlFrame*>(aData);
951 0 : nsIContent *content = frame->GetContent();
952 :
953 0 : nsCOMPtr<nsIControllers> controllers;
954 0 : nsCOMPtr<nsIDOMHTMLInputElement> input = do_QueryInterface(content);
955 0 : if (input) {
956 0 : input->GetControllers(getter_AddRefs(controllers));
957 : } else {
958 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textArea =
959 0 : do_QueryInterface(content);
960 :
961 0 : if (textArea) {
962 0 : textArea->GetControllers(getter_AddRefs(controllers));
963 : }
964 : }
965 :
966 0 : if (!controllers) {
967 0 : NS_WARNING("Could not get controllers");
968 0 : return;
969 : }
970 :
971 0 : const char* commandStr = WidgetKeyboardEvent::GetCommandStr(aCommand);
972 :
973 0 : nsCOMPtr<nsIController> controller;
974 0 : controllers->GetControllerForCommand(commandStr, getter_AddRefs(controller));
975 0 : if (!controller) {
976 0 : return;
977 : }
978 :
979 : bool commandEnabled;
980 0 : nsresult rv = controller->IsCommandEnabled(commandStr, &commandEnabled);
981 0 : NS_ENSURE_SUCCESS_VOID(rv);
982 0 : if (commandEnabled) {
983 0 : controller->DoCommand(commandStr);
984 : }
985 : }
986 :
987 : NS_IMETHODIMP
988 0 : nsTextInputListener::HandleEvent(nsIDOMEvent* aEvent)
989 : {
990 0 : bool defaultPrevented = false;
991 0 : nsresult rv = aEvent->GetDefaultPrevented(&defaultPrevented);
992 0 : NS_ENSURE_SUCCESS(rv, rv);
993 0 : if (defaultPrevented) {
994 0 : return NS_OK;
995 : }
996 :
997 0 : bool isTrusted = false;
998 0 : rv = aEvent->GetIsTrusted(&isTrusted);
999 0 : NS_ENSURE_SUCCESS(rv, rv);
1000 0 : if (!isTrusted) {
1001 0 : return NS_OK;
1002 : }
1003 :
1004 : WidgetKeyboardEvent* keyEvent =
1005 0 : aEvent->WidgetEventPtr()->AsKeyboardEvent();
1006 0 : if (!keyEvent) {
1007 0 : return NS_ERROR_UNEXPECTED;
1008 : }
1009 :
1010 0 : if (keyEvent->mMessage != eKeyPress) {
1011 0 : return NS_OK;
1012 : }
1013 :
1014 : nsIWidget::NativeKeyBindingsType nativeKeyBindingsType =
1015 0 : mTxtCtrlElement->IsTextArea() ?
1016 : nsIWidget::NativeKeyBindingsForMultiLineEditor :
1017 0 : nsIWidget::NativeKeyBindingsForSingleLineEditor;
1018 :
1019 0 : nsIWidget* widget = keyEvent->mWidget;
1020 : // If the event is created by chrome script, the widget is nullptr.
1021 0 : if (!widget) {
1022 0 : widget = mFrame->GetNearestWidget();
1023 0 : NS_ENSURE_TRUE(widget, NS_OK);
1024 : }
1025 :
1026 : // WidgetKeyboardEvent::ExecuteEditCommands() requires non-nullptr mWidget.
1027 : // If the event is created by chrome script, it is nullptr but we need to
1028 : // execute native key bindings. Therefore, we need to set widget to
1029 : // WidgetEvent::mWidget temporarily.
1030 0 : AutoRestore<nsCOMPtr<nsIWidget>> saveWidget(keyEvent->mWidget);
1031 0 : keyEvent->mWidget = widget;
1032 0 : if (keyEvent->ExecuteEditCommands(nativeKeyBindingsType,
1033 0 : DoCommandCallback, mFrame)) {
1034 0 : aEvent->PreventDefault();
1035 : }
1036 0 : return NS_OK;
1037 : }
1038 :
1039 : // BEGIN nsIEditorObserver
1040 :
1041 : NS_IMETHODIMP
1042 1 : nsTextInputListener::EditAction()
1043 : {
1044 1 : if (!mFrame) {
1045 : // We've been disconnected from the nsTextEditorState object, nothing to do
1046 : // here.
1047 0 : return NS_OK;
1048 : }
1049 :
1050 2 : AutoWeakFrame weakFrame = mFrame;
1051 :
1052 1 : nsITextControlFrame* frameBase = do_QueryFrame(mFrame);
1053 1 : nsTextControlFrame* frame = static_cast<nsTextControlFrame*> (frameBase);
1054 1 : NS_ASSERTION(frame, "Where is our frame?");
1055 : //
1056 : // Update the undo / redo menus
1057 : //
1058 2 : RefPtr<TextEditor> textEditor = frame->GetTextEditor();
1059 :
1060 : // Get the number of undo / redo items
1061 1 : int32_t numUndoItems = textEditor->NumberOfUndoItems();
1062 1 : int32_t numRedoItems = textEditor->NumberOfRedoItems();
1063 1 : if ((numUndoItems && !mHadUndoItems) || (!numUndoItems && mHadUndoItems) ||
1064 0 : (numRedoItems && !mHadRedoItems) || (!numRedoItems && mHadRedoItems)) {
1065 : // Modify the menu if undo or redo items are different
1066 0 : UpdateTextInputCommands(NS_LITERAL_STRING("undo"));
1067 :
1068 0 : mHadUndoItems = numUndoItems != 0;
1069 0 : mHadRedoItems = numRedoItems != 0;
1070 : }
1071 :
1072 1 : if (!weakFrame.IsAlive()) {
1073 0 : return NS_OK;
1074 : }
1075 :
1076 : // Make sure we know we were changed (do NOT set this to false if there are
1077 : // no undo items; JS could change the value and we'd still need to save it)
1078 1 : if (mSetValueChanged) {
1079 1 : frame->SetValueChanged(true);
1080 : }
1081 :
1082 1 : if (!mSettingValue) {
1083 0 : mTxtCtrlElement->OnValueChanged(/* aNotify = */ true,
1084 0 : /* aWasInteractiveUserChange = */ true);
1085 : }
1086 :
1087 1 : return NS_OK;
1088 : }
1089 :
1090 : NS_IMETHODIMP
1091 1 : nsTextInputListener::BeforeEditAction()
1092 : {
1093 1 : return NS_OK;
1094 : }
1095 :
1096 : NS_IMETHODIMP
1097 0 : nsTextInputListener::CancelEditAction()
1098 : {
1099 0 : return NS_OK;
1100 : }
1101 :
1102 : // END nsIEditorObserver
1103 :
1104 :
1105 : nsresult
1106 0 : nsTextInputListener::UpdateTextInputCommands(const nsAString& commandsToUpdate,
1107 : nsISelection* sel,
1108 : int16_t reason)
1109 : {
1110 0 : nsIContent* content = mFrame->GetContent();
1111 0 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
1112 :
1113 0 : nsCOMPtr<nsIDocument> doc = content->GetComposedDoc();
1114 0 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
1115 :
1116 0 : nsPIDOMWindowOuter *domWindow = doc->GetWindow();
1117 0 : NS_ENSURE_TRUE(domWindow, NS_ERROR_FAILURE);
1118 :
1119 0 : return domWindow->UpdateCommands(commandsToUpdate, sel, reason);
1120 : }
1121 :
1122 : // END nsTextInputListener
1123 :
1124 : // nsTextEditorState
1125 :
1126 8 : nsTextEditorState::nsTextEditorState(nsITextControlElement* aOwningElement)
1127 : : mTextCtrlElement(aOwningElement)
1128 : , mBoundFrame(nullptr)
1129 : , mEverInited(false)
1130 : , mEditorInitialized(false)
1131 : , mInitializing(false)
1132 : , mValueTransferInProgress(false)
1133 : , mSelectionCached(true)
1134 : , mSelectionRestoreEagerInit(false)
1135 : , mPlaceholderVisibility(false)
1136 : , mPreviewVisibility(false)
1137 8 : , mIsCommittingComposition(false)
1138 : // When adding more member variable initializations here, add the same
1139 : // also to ::Construct.
1140 : {
1141 8 : MOZ_COUNT_CTOR(nsTextEditorState);
1142 8 : }
1143 :
1144 : nsTextEditorState*
1145 8 : nsTextEditorState::Construct(nsITextControlElement* aOwningElement,
1146 : nsTextEditorState** aReusedState)
1147 : {
1148 8 : if (*aReusedState) {
1149 1 : nsTextEditorState* state = *aReusedState;
1150 1 : *aReusedState = nullptr;
1151 1 : state->mTextCtrlElement = aOwningElement;
1152 1 : state->mBoundFrame = nullptr;
1153 1 : state->mSelectionProperties = SelectionProperties();
1154 1 : state->mEverInited = false;
1155 1 : state->mEditorInitialized = false;
1156 1 : state->mInitializing = false;
1157 1 : state->mValueTransferInProgress = false;
1158 1 : state->mSelectionCached = true;
1159 1 : state->mSelectionRestoreEagerInit = false;
1160 1 : state->mPlaceholderVisibility = false;
1161 1 : state->mPreviewVisibility = false;
1162 1 : state->mIsCommittingComposition = false;
1163 : // When adding more member variable initializations here, add the same
1164 : // also to the constructor.
1165 1 : return state;
1166 : }
1167 :
1168 7 : return new nsTextEditorState(aOwningElement);
1169 : }
1170 :
1171 0 : nsTextEditorState::~nsTextEditorState()
1172 : {
1173 0 : MOZ_COUNT_DTOR(nsTextEditorState);
1174 0 : Clear();
1175 0 : }
1176 :
1177 : void
1178 1 : nsTextEditorState::Clear()
1179 : {
1180 1 : if (mBoundFrame) {
1181 : // Oops, we still have a frame!
1182 : // This should happen when the type of a text input control is being changed
1183 : // to something which is not a text control. In this case, we should pretend
1184 : // that a frame is being destroyed, and clean up after ourselves properly.
1185 0 : UnbindFromFrame(mBoundFrame);
1186 0 : mTextEditor = nullptr;
1187 : } else {
1188 : // If we have a bound frame around, UnbindFromFrame will call DestroyEditor
1189 : // for us.
1190 1 : DestroyEditor();
1191 : }
1192 1 : mTextListener = nullptr;
1193 1 : }
1194 :
1195 1 : void nsTextEditorState::Unlink()
1196 : {
1197 1 : nsTextEditorState* tmp = this;
1198 1 : tmp->Clear();
1199 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelCon)
1200 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mTextEditor)
1201 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mRootNode)
1202 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPlaceholderDiv)
1203 1 : NS_IMPL_CYCLE_COLLECTION_UNLINK(mPreviewDiv)
1204 1 : }
1205 :
1206 2 : void nsTextEditorState::Traverse(nsCycleCollectionTraversalCallback& cb)
1207 : {
1208 2 : nsTextEditorState* tmp = this;
1209 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mSelCon)
1210 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTextEditor)
1211 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mRootNode)
1212 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPlaceholderDiv)
1213 2 : NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPreviewDiv)
1214 2 : }
1215 :
1216 : nsFrameSelection*
1217 8 : nsTextEditorState::GetConstFrameSelection() {
1218 8 : if (mSelCon)
1219 8 : return mSelCon->GetConstFrameSelection();
1220 0 : return nullptr;
1221 : }
1222 :
1223 : TextEditor*
1224 8 : nsTextEditorState::GetTextEditor()
1225 : {
1226 8 : if (!mTextEditor) {
1227 1 : nsresult rv = PrepareEditor();
1228 1 : NS_ENSURE_SUCCESS(rv, nullptr);
1229 : }
1230 8 : return mTextEditor;
1231 : }
1232 :
1233 : nsISelectionController*
1234 14 : nsTextEditorState::GetSelectionController() const
1235 : {
1236 14 : return mSelCon;
1237 : }
1238 :
1239 : // Helper class, used below in BindToFrame().
1240 3 : class PrepareEditorEvent : public Runnable {
1241 : public:
1242 1 : PrepareEditorEvent(nsTextEditorState& aState,
1243 : nsIContent* aOwnerContent,
1244 : const nsAString& aCurrentValue)
1245 1 : : mozilla::Runnable("PrepareEditorEvent")
1246 : , mState(&aState)
1247 : , mOwnerContent(aOwnerContent)
1248 1 : , mCurrentValue(aCurrentValue)
1249 : {
1250 1 : aState.mValueTransferInProgress = true;
1251 1 : }
1252 :
1253 1 : NS_IMETHOD Run() override {
1254 1 : NS_ENSURE_TRUE(mState, NS_ERROR_NULL_POINTER);
1255 :
1256 : // Transfer the saved value to the editor if we have one
1257 1 : const nsAString *value = nullptr;
1258 1 : if (!mCurrentValue.IsEmpty()) {
1259 0 : value = &mCurrentValue;
1260 : }
1261 :
1262 2 : nsAutoScriptBlocker scriptBlocker;
1263 :
1264 1 : mState->PrepareEditor(value);
1265 :
1266 1 : mState->mValueTransferInProgress = false;
1267 :
1268 1 : return NS_OK;
1269 : }
1270 :
1271 : private:
1272 : WeakPtr<nsTextEditorState> mState;
1273 : nsCOMPtr<nsIContent> mOwnerContent; // strong reference
1274 : nsAutoString mCurrentValue;
1275 : };
1276 :
1277 : nsresult
1278 4 : nsTextEditorState::BindToFrame(nsTextControlFrame* aFrame)
1279 : {
1280 4 : NS_ASSERTION(aFrame, "The frame to bind to should be valid");
1281 4 : NS_ENSURE_ARG_POINTER(aFrame);
1282 :
1283 4 : NS_ASSERTION(!mBoundFrame, "Cannot bind twice, need to unbind first");
1284 4 : NS_ENSURE_TRUE(!mBoundFrame, NS_ERROR_FAILURE);
1285 :
1286 : // If we'll need to transfer our current value to the editor, save it before
1287 : // binding to the frame.
1288 8 : nsAutoString currentValue;
1289 4 : if (mTextEditor) {
1290 1 : GetValue(currentValue, true);
1291 : }
1292 :
1293 4 : mBoundFrame = aFrame;
1294 :
1295 4 : nsresult rv = CreateRootNode();
1296 4 : NS_ENSURE_SUCCESS(rv, rv);
1297 :
1298 4 : nsIContent *rootNode = GetRootNode();
1299 4 : rv = InitializeRootNode();
1300 4 : NS_ENSURE_SUCCESS(rv, rv);
1301 :
1302 4 : nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
1303 4 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
1304 :
1305 : // Create selection
1306 8 : RefPtr<nsFrameSelection> frameSel = new nsFrameSelection();
1307 :
1308 : // Create a SelectionController
1309 8 : mSelCon = new nsTextInputSelectionImpl(frameSel, shell, rootNode);
1310 4 : MOZ_ASSERT(!mTextListener, "Should not overwrite the object");
1311 4 : mTextListener = new nsTextInputListener(mTextCtrlElement);
1312 :
1313 4 : mTextListener->SetFrame(mBoundFrame);
1314 4 : mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_ON);
1315 :
1316 : // Get the caret and make it a selection listener.
1317 8 : RefPtr<nsISelection> domSelection;
1318 12 : if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
1319 16 : getter_AddRefs(domSelection))) &&
1320 4 : domSelection) {
1321 8 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
1322 8 : RefPtr<nsCaret> caret = shell->GetCaret();
1323 8 : nsCOMPtr<nsISelectionListener> listener;
1324 4 : if (caret) {
1325 4 : listener = do_QueryInterface(caret);
1326 4 : if (listener) {
1327 4 : selPriv->AddSelectionListener(listener);
1328 : }
1329 : }
1330 :
1331 4 : selPriv->AddSelectionListener(static_cast<nsISelectionListener*>
1332 4 : (mTextListener));
1333 : }
1334 :
1335 : // If an editor exists from before, prepare it for usage
1336 4 : if (mTextEditor) {
1337 2 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1338 1 : NS_ENSURE_TRUE(content, NS_ERROR_FAILURE);
1339 :
1340 : // Set the correct direction on the newly created root node
1341 1 : if (mTextEditor->IsRightToLeft()) {
1342 0 : rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("rtl"), false);
1343 1 : } else if (mTextEditor->IsLeftToRight()) {
1344 0 : rootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::dir, NS_LITERAL_STRING("ltr"), false);
1345 : } else {
1346 : // otherwise, inherit the content node's direction
1347 : }
1348 :
1349 : nsContentUtils::AddScriptRunner(
1350 2 : new PrepareEditorEvent(*this, content, currentValue));
1351 : }
1352 :
1353 4 : return NS_OK;
1354 : }
1355 :
1356 2 : struct PreDestroyer
1357 : {
1358 1 : void Init(TextEditor* aTextEditor) { mTextEditor = aTextEditor; }
1359 2 : ~PreDestroyer()
1360 2 : {
1361 2 : if (mTextEditor) {
1362 0 : mTextEditor->PreDestroy(true);
1363 : }
1364 2 : }
1365 1 : void Swap(RefPtr<TextEditor>& aTextEditor)
1366 : {
1367 1 : return mTextEditor.swap(aTextEditor);
1368 : }
1369 : private:
1370 : RefPtr<TextEditor> mTextEditor;
1371 : };
1372 :
1373 : nsresult
1374 4 : nsTextEditorState::PrepareEditor(const nsAString *aValue)
1375 : {
1376 4 : if (!mBoundFrame) {
1377 : // Cannot create an editor without a bound frame.
1378 : // Don't return a failure code, because js callers can't handle that.
1379 0 : return NS_OK;
1380 : }
1381 :
1382 4 : if (mEditorInitialized) {
1383 : // Do not initialize the editor multiple times.
1384 2 : return NS_OK;
1385 : }
1386 :
1387 4 : AutoHideSelectionChanges hideSelectionChanges(GetConstFrameSelection());
1388 :
1389 : // Don't attempt to initialize recursively!
1390 4 : InitializationGuard guard(*this);
1391 2 : if (guard.IsInitializingRecursively()) {
1392 0 : return NS_ERROR_NOT_INITIALIZED;
1393 : }
1394 :
1395 : // Note that we don't check mTextEditor here, because we might already have
1396 : // one around, in which case we don't create a new one, and we'll just tie
1397 : // the required machinery to it.
1398 :
1399 2 : nsPresContext *presContext = mBoundFrame->PresContext();
1400 2 : nsIPresShell *shell = presContext->GetPresShell();
1401 :
1402 : // Setup the editor flags
1403 2 : uint32_t editorFlags = 0;
1404 2 : if (IsPlainTextControl())
1405 2 : editorFlags |= nsIPlaintextEditor::eEditorPlaintextMask;
1406 2 : if (IsSingleLineTextControl())
1407 2 : editorFlags |= nsIPlaintextEditor::eEditorSingleLineMask;
1408 2 : if (IsPasswordTextControl())
1409 0 : editorFlags |= nsIPlaintextEditor::eEditorPasswordMask;
1410 :
1411 : // All nsTextControlFrames are widgets
1412 2 : editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
1413 :
1414 : // Spell check is diabled at creation time. It is enabled once
1415 : // the editor comes into focus.
1416 2 : editorFlags |= nsIPlaintextEditor::eEditorSkipSpellCheck;
1417 :
1418 2 : bool shouldInitializeEditor = false;
1419 4 : RefPtr<TextEditor> newTextEditor; // the editor that we might create
1420 2 : nsresult rv = NS_OK;
1421 4 : PreDestroyer preDestroyer;
1422 2 : if (!mTextEditor) {
1423 1 : shouldInitializeEditor = true;
1424 :
1425 : // Create an editor
1426 1 : newTextEditor = new TextEditor();
1427 1 : preDestroyer.Init(newTextEditor);
1428 :
1429 : // Make sure we clear out the non-breaking space before we initialize the editor
1430 1 : rv = mBoundFrame->UpdateValueDisplay(true, true);
1431 1 : NS_ENSURE_SUCCESS(rv, rv);
1432 : } else {
1433 1 : if (aValue || !mEditorInitialized) {
1434 : // Set the correct value in the root node
1435 1 : rv = mBoundFrame->UpdateValueDisplay(true, !mEditorInitialized, aValue);
1436 1 : NS_ENSURE_SUCCESS(rv, rv);
1437 : }
1438 :
1439 1 : newTextEditor = mTextEditor; // just pretend that we have a new editor!
1440 :
1441 : // Don't lose application flags in the process.
1442 1 : if (newTextEditor->IsMailEditor()) {
1443 0 : editorFlags |= nsIPlaintextEditor::eEditorMailMask;
1444 : }
1445 : }
1446 :
1447 : // Get the current value of the textfield from the content.
1448 : // Note that if we've created a new editor, mTextEditor is null at this stage,
1449 : // so we will get the real value from the content.
1450 4 : nsAutoString defaultValue;
1451 2 : if (aValue) {
1452 0 : defaultValue = *aValue;
1453 : } else {
1454 2 : GetValue(defaultValue, true);
1455 : }
1456 :
1457 2 : if (!mEditorInitialized) {
1458 : // Now initialize the editor.
1459 : //
1460 : // NOTE: Conversion of '\n' to <BR> happens inside the
1461 : // editor's Init() call.
1462 :
1463 : // Get the DOM document
1464 4 : nsCOMPtr<nsIDOMDocument> domdoc = do_QueryInterface(shell->GetDocument());
1465 2 : if (!domdoc)
1466 0 : return NS_ERROR_FAILURE;
1467 :
1468 : // What follows is a bit of a hack. The editor uses the public DOM APIs
1469 : // for its content manipulations, and it causes it to fail some security
1470 : // checks deep inside when initializing. So we explictly make it clear that
1471 : // we're native code.
1472 : // Note that any script that's directly trying to access our value
1473 : // has to be going through some scriptable object to do that and that
1474 : // already does the relevant security checks.
1475 4 : AutoNoJSAPI nojsapi;
1476 :
1477 4 : rv = newTextEditor->Init(domdoc, GetRootNode(), mSelCon, editorFlags,
1478 4 : defaultValue);
1479 2 : NS_ENSURE_SUCCESS(rv, rv);
1480 : }
1481 :
1482 : // Initialize the controller for the editor
1483 :
1484 2 : if (!SuppressEventHandlers(presContext)) {
1485 4 : nsCOMPtr<nsIControllers> controllers;
1486 : nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
1487 4 : do_QueryInterface(mTextCtrlElement);
1488 2 : if (inputElement) {
1489 2 : rv = inputElement->GetControllers(getter_AddRefs(controllers));
1490 : } else {
1491 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement =
1492 0 : do_QueryInterface(mTextCtrlElement);
1493 :
1494 0 : if (!textAreaElement)
1495 0 : return NS_ERROR_FAILURE;
1496 :
1497 0 : rv = textAreaElement->GetControllers(getter_AddRefs(controllers));
1498 : }
1499 :
1500 2 : NS_ENSURE_SUCCESS(rv, rv);
1501 :
1502 2 : if (controllers) {
1503 : uint32_t numControllers;
1504 2 : bool found = false;
1505 2 : rv = controllers->GetControllerCount(&numControllers);
1506 8 : for (uint32_t i = 0; i < numControllers; i ++) {
1507 12 : nsCOMPtr<nsIController> controller;
1508 6 : rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
1509 6 : if (NS_SUCCEEDED(rv) && controller) {
1510 : nsCOMPtr<nsIControllerContext> editController =
1511 12 : do_QueryInterface(controller);
1512 6 : if (editController) {
1513 4 : editController->SetCommandContext(
1514 4 : static_cast<nsIEditor*>(newTextEditor));
1515 4 : found = true;
1516 : }
1517 : }
1518 : }
1519 2 : if (!found)
1520 0 : rv = NS_ERROR_FAILURE;
1521 : }
1522 : }
1523 :
1524 : // Initialize the plaintext editor
1525 2 : if (shouldInitializeEditor) {
1526 : // Set up wrapping
1527 1 : newTextEditor->SetWrapColumn(GetWrapCols());
1528 : }
1529 :
1530 : // Set max text field length
1531 2 : newTextEditor->SetMaxTextLength(GetMaxLength());
1532 :
1533 4 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
1534 2 : if (content) {
1535 2 : editorFlags = newTextEditor->Flags();
1536 :
1537 : // Check if the readonly attribute is set.
1538 2 : if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::readonly))
1539 0 : editorFlags |= nsIPlaintextEditor::eEditorReadonlyMask;
1540 :
1541 : // Check if the disabled attribute is set.
1542 : // TODO: call IsDisabled() here!
1543 2 : if (content->HasAttr(kNameSpaceID_None, nsGkAtoms::disabled))
1544 0 : editorFlags |= nsIPlaintextEditor::eEditorDisabledMask;
1545 :
1546 : // Disable the selection if necessary.
1547 2 : if (editorFlags & nsIPlaintextEditor::eEditorDisabledMask)
1548 0 : mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_OFF);
1549 :
1550 2 : newTextEditor->SetFlags(editorFlags);
1551 : }
1552 :
1553 2 : if (shouldInitializeEditor) {
1554 : // Hold on to the newly created editor
1555 1 : preDestroyer.Swap(mTextEditor);
1556 : }
1557 :
1558 : // If we have a default value, insert it under the div we created
1559 : // above, but be sure to use the editor so that '*' characters get
1560 : // displayed for password fields, etc. SetValue() will call the
1561 : // editor for us.
1562 :
1563 2 : if (!defaultValue.IsEmpty()) {
1564 0 : rv = newTextEditor->SetFlags(editorFlags);
1565 0 : NS_ENSURE_SUCCESS(rv, rv);
1566 :
1567 : // Now call SetValue() which will make the necessary editor calls to set
1568 : // the default value. Make sure to turn off undo before setting the default
1569 : // value, and turn it back on afterwards. This will make sure we can't undo
1570 : // past the default value.
1571 : // So, we use eSetValue_Internal flag only that it will turn off undo.
1572 :
1573 0 : bool success = SetValue(defaultValue, eSetValue_Internal);
1574 0 : NS_ENSURE_TRUE(success, NS_ERROR_OUT_OF_MEMORY);
1575 :
1576 : // Now restore the original editor flags.
1577 0 : rv = newTextEditor->SetFlags(editorFlags);
1578 0 : NS_ENSURE_SUCCESS(rv, rv);
1579 : }
1580 :
1581 : nsCOMPtr<nsITransactionManager> transactionManager =
1582 4 : newTextEditor->GetTransactionManager();
1583 2 : if (NS_WARN_IF(!transactionManager)) {
1584 0 : return NS_ERROR_FAILURE;
1585 : }
1586 :
1587 2 : transactionManager->SetMaxTransactionCount(
1588 2 : nsITextControlElement::DEFAULT_UNDO_CAP);
1589 :
1590 2 : if (IsPasswordTextControl()) {
1591 : // Disable undo for password textfields. Note that we want to do this at
1592 : // the very end of InitEditor, so the calls to EnableUndo when setting the
1593 : // default value don't screw us up.
1594 : // Since changing the control type does a reframe, we don't have to worry
1595 : // about dynamic type changes here.
1596 0 : newTextEditor->EnableUndo(false);
1597 : }
1598 :
1599 2 : if (!mEditorInitialized) {
1600 2 : newTextEditor->PostCreate();
1601 2 : mEverInited = true;
1602 2 : mEditorInitialized = true;
1603 : }
1604 :
1605 2 : if (mTextListener) {
1606 2 : newTextEditor->AddEditorObserver(mTextListener);
1607 : }
1608 :
1609 : // Restore our selection after being bound to a new frame
1610 2 : HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
1611 2 : if (number ? number->IsSelectionCached() : mSelectionCached) {
1612 2 : if (mRestoringSelection) // paranoia
1613 0 : mRestoringSelection->Revoke();
1614 2 : mRestoringSelection = new RestoreSelectionState(this, mBoundFrame);
1615 2 : if (mRestoringSelection) {
1616 2 : nsContentUtils::AddScriptRunner(mRestoringSelection);
1617 : }
1618 : }
1619 :
1620 : // The selection cache is no longer going to be valid.
1621 : //
1622 : // XXXbz Shouldn't we do this at the point when we're actually about to
1623 : // restore the properties or something? As things stand, if UnbindFromFrame
1624 : // happens before our RestoreSelectionState runs, it looks like we'll lose our
1625 : // selection info, because we will think we don't have it cached and try to
1626 : // read it from the selection controller, which will not have it yet.
1627 2 : if (number) {
1628 0 : number->ClearSelectionCached();
1629 : } else {
1630 2 : mSelectionCached = false;
1631 : }
1632 :
1633 2 : return rv;
1634 : }
1635 :
1636 : void
1637 2 : nsTextEditorState::FinishedRestoringSelection()
1638 : {
1639 2 : mRestoringSelection = nullptr;
1640 2 : }
1641 :
1642 : bool
1643 27 : nsTextEditorState::IsSelectionCached() const
1644 : {
1645 27 : if (mBoundFrame) {
1646 22 : HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
1647 22 : if (number) {
1648 0 : return number->IsSelectionCached();
1649 : }
1650 : }
1651 27 : return mSelectionCached;
1652 : }
1653 :
1654 : nsTextEditorState::SelectionProperties&
1655 15 : nsTextEditorState::GetSelectionProperties()
1656 : {
1657 15 : if (mBoundFrame) {
1658 9 : HTMLInputElement* number = GetParentNumberControl(mBoundFrame);
1659 9 : if (number) {
1660 0 : return number->GetSelectionProperties();
1661 : }
1662 : }
1663 15 : return mSelectionProperties;
1664 : }
1665 :
1666 : void
1667 1 : nsTextEditorState::SyncUpSelectionPropertiesBeforeDestruction()
1668 : {
1669 1 : if (mBoundFrame) {
1670 0 : UnbindFromFrame(mBoundFrame);
1671 : }
1672 1 : }
1673 :
1674 : void
1675 0 : nsTextEditorState::SetSelectionProperties(nsTextEditorState::SelectionProperties& aProps)
1676 : {
1677 0 : if (mBoundFrame) {
1678 0 : mBoundFrame->SetSelectionRange(aProps.GetStart(),
1679 : aProps.GetEnd(),
1680 0 : aProps.GetDirection());
1681 : } else {
1682 0 : mSelectionProperties = aProps;
1683 : }
1684 0 : }
1685 :
1686 : void
1687 3 : nsTextEditorState::GetSelectionRange(uint32_t* aSelectionStart,
1688 : uint32_t* aSelectionEnd,
1689 : ErrorResult& aRv)
1690 : {
1691 3 : MOZ_ASSERT(aSelectionStart);
1692 3 : MOZ_ASSERT(aSelectionEnd);
1693 3 : MOZ_ASSERT(IsSelectionCached() || GetSelectionController(),
1694 : "How can we not have a cached selection if we have no selection "
1695 : "controller?");
1696 :
1697 : // Note that we may have both IsSelectionCached() _and_
1698 : // GetSelectionController() if we haven't initialized our editor yet.
1699 3 : if (IsSelectionCached()) {
1700 0 : const SelectionProperties& props = GetSelectionProperties();
1701 0 : *aSelectionStart = props.GetStart();
1702 0 : *aSelectionEnd = props.GetEnd();
1703 0 : return;
1704 : }
1705 :
1706 3 : Selection* sel = mSelCon->GetSelection(SelectionType::eNormal);
1707 3 : if (NS_WARN_IF(!sel)) {
1708 0 : aRv.Throw(NS_ERROR_FAILURE);
1709 0 : return;
1710 : }
1711 :
1712 3 : mozilla::dom::Element* root = GetRootNode();
1713 3 : if (NS_WARN_IF(!root)) {
1714 0 : aRv.Throw(NS_ERROR_UNEXPECTED);
1715 0 : return;
1716 : }
1717 : nsContentUtils::GetSelectionInTextControl(sel, root,
1718 3 : *aSelectionStart, *aSelectionEnd);
1719 : }
1720 :
1721 : nsITextControlFrame::SelectionDirection
1722 3 : nsTextEditorState::GetSelectionDirection(ErrorResult& aRv)
1723 : {
1724 3 : MOZ_ASSERT(IsSelectionCached() || GetSelectionController(),
1725 : "How can we not have a cached selection if we have no selection "
1726 : "controller?");
1727 :
1728 : // Note that we may have both IsSelectionCached() _and_
1729 : // GetSelectionController() if we haven't initialized our editor yet.
1730 3 : if (IsSelectionCached()) {
1731 0 : return GetSelectionProperties().GetDirection();
1732 : }
1733 :
1734 3 : Selection* sel = mSelCon->GetSelection(SelectionType::eNormal);
1735 3 : if (NS_WARN_IF(!sel)) {
1736 0 : aRv.Throw(NS_ERROR_FAILURE);
1737 0 : return nsITextControlFrame::eForward; // Doesn't really matter
1738 : }
1739 :
1740 3 : nsDirection direction = sel->GetSelectionDirection();
1741 3 : if (direction == eDirNext) {
1742 3 : return nsITextControlFrame::eForward;
1743 : }
1744 :
1745 0 : MOZ_ASSERT(direction == eDirPrevious);
1746 0 : return nsITextControlFrame::eBackward;
1747 : }
1748 :
1749 : void
1750 2 : nsTextEditorState::SetSelectionRange(uint32_t aStart, uint32_t aEnd,
1751 : nsITextControlFrame::SelectionDirection aDirection,
1752 : ErrorResult& aRv)
1753 : {
1754 2 : MOZ_ASSERT(IsSelectionCached() || mBoundFrame,
1755 : "How can we have a non-cached selection but no frame?");
1756 :
1757 2 : if (aStart > aEnd) {
1758 1 : aStart = aEnd;
1759 : }
1760 :
1761 2 : bool changed = false;
1762 2 : nsresult rv = NS_OK; // For the ScrollSelectionIntoView() return value.
1763 2 : if (IsSelectionCached()) {
1764 0 : nsAutoString value;
1765 : // XXXbz is "false" the right thing to pass here? Hard to tell, given the
1766 : // various mismatches between our impl and the spec.
1767 0 : GetValue(value, false);
1768 0 : uint32_t length = value.Length();
1769 0 : if (aStart > length) {
1770 0 : aStart = length;
1771 : }
1772 0 : if (aEnd > length) {
1773 0 : aEnd = length;
1774 : }
1775 0 : SelectionProperties& props = GetSelectionProperties();
1776 0 : changed = props.GetStart() != aStart ||
1777 0 : props.GetEnd() != aEnd ||
1778 0 : props.GetDirection() != aDirection;
1779 0 : props.SetStart(aStart);
1780 0 : props.SetEnd(aEnd);
1781 0 : props.SetDirection(aDirection);
1782 : } else {
1783 2 : aRv = mBoundFrame->SetSelectionRange(aStart, aEnd, aDirection);
1784 2 : if (aRv.Failed()) {
1785 0 : return;
1786 : }
1787 2 : rv = mBoundFrame->ScrollSelectionIntoView();
1788 : // Press on to firing the event even if that failed, like our old code did.
1789 : // But is that really what we want? Firing the event _and_ throwing from
1790 : // here is weird. Maybe we should just ignore ScrollSelectionIntoView
1791 : // failures?
1792 :
1793 : // XXXbz This is preserving our current behavior of firing a "select" event
1794 : // on all mutations when we have an editor, but we should really consider
1795 : // fixing that...
1796 2 : changed = true;
1797 : }
1798 :
1799 2 : if (changed) {
1800 : // It sure would be nice if we had an existing Element* or so to work with.
1801 4 : nsCOMPtr<nsINode> node = do_QueryInterface(mTextCtrlElement);
1802 : RefPtr<AsyncEventDispatcher> asyncDispatcher =
1803 6 : new AsyncEventDispatcher(node, NS_LITERAL_STRING("select"), true, false);
1804 2 : asyncDispatcher->PostDOMEvent();
1805 : }
1806 :
1807 2 : if (NS_FAILED(rv)) {
1808 0 : aRv.Throw(rv);
1809 : }
1810 : }
1811 :
1812 : void
1813 1 : nsTextEditorState::SetSelectionStart(const Nullable<uint32_t>& aStart,
1814 : ErrorResult& aRv)
1815 : {
1816 1 : uint32_t start = 0;
1817 1 : if (!aStart.IsNull()) {
1818 1 : start = aStart.Value();
1819 : }
1820 :
1821 : uint32_t ignored, end;
1822 1 : GetSelectionRange(&ignored, &end, aRv);
1823 1 : if (aRv.Failed()) {
1824 0 : return;
1825 : }
1826 :
1827 1 : nsITextControlFrame::SelectionDirection dir = GetSelectionDirection(aRv);
1828 1 : if (aRv.Failed()) {
1829 0 : return;
1830 : }
1831 :
1832 1 : if (end < start) {
1833 0 : end = start;
1834 : }
1835 :
1836 1 : SetSelectionRange(start, end, dir, aRv);
1837 : }
1838 :
1839 : void
1840 1 : nsTextEditorState::SetSelectionEnd(const Nullable<uint32_t>& aEnd,
1841 : ErrorResult& aRv)
1842 : {
1843 1 : uint32_t end = 0;
1844 1 : if (!aEnd.IsNull()) {
1845 1 : end = aEnd.Value();
1846 : }
1847 :
1848 : uint32_t start, ignored;
1849 1 : GetSelectionRange(&start, &ignored, aRv);
1850 1 : if (aRv.Failed()) {
1851 0 : return;
1852 : }
1853 :
1854 1 : nsITextControlFrame::SelectionDirection dir = GetSelectionDirection(aRv);
1855 1 : if (aRv.Failed()) {
1856 0 : return;
1857 : }
1858 :
1859 1 : SetSelectionRange(start, end, dir, aRv);
1860 : }
1861 :
1862 : static void
1863 0 : DirectionToName(nsITextControlFrame::SelectionDirection dir, nsAString& aDirection)
1864 : {
1865 0 : if (dir == nsITextControlFrame::eNone) {
1866 0 : NS_WARNING("We don't actually support this... how did we get it?");
1867 0 : aDirection.AssignLiteral("none");
1868 0 : } else if (dir == nsITextControlFrame::eForward) {
1869 0 : aDirection.AssignLiteral("forward");
1870 0 : } else if (dir == nsITextControlFrame::eBackward) {
1871 0 : aDirection.AssignLiteral("backward");
1872 : } else {
1873 0 : NS_NOTREACHED("Invalid SelectionDirection value");
1874 : }
1875 0 : }
1876 :
1877 : void
1878 0 : nsTextEditorState::GetSelectionDirectionString(nsAString& aDirection,
1879 : ErrorResult& aRv)
1880 : {
1881 0 : nsITextControlFrame::SelectionDirection dir = GetSelectionDirection(aRv);
1882 0 : if (aRv.Failed()) {
1883 0 : return;
1884 : }
1885 0 : DirectionToName(dir, aDirection);
1886 : }
1887 :
1888 : static nsITextControlFrame::SelectionDirection
1889 0 : DirectionStringToSelectionDirection(const nsAString& aDirection)
1890 : {
1891 0 : if (aDirection.EqualsLiteral("backward")) {
1892 0 : return nsITextControlFrame::eBackward;
1893 : }
1894 :
1895 : // We don't support directionless selections.
1896 0 : return nsITextControlFrame::eForward;
1897 : }
1898 :
1899 : void
1900 0 : nsTextEditorState::SetSelectionDirection(const nsAString& aDirection,
1901 : ErrorResult& aRv)
1902 : {
1903 : nsITextControlFrame::SelectionDirection dir =
1904 0 : DirectionStringToSelectionDirection(aDirection);
1905 :
1906 0 : if (IsSelectionCached()) {
1907 0 : GetSelectionProperties().SetDirection(dir);
1908 0 : return;
1909 : }
1910 :
1911 : uint32_t start, end;
1912 0 : GetSelectionRange(&start, &end, aRv);
1913 0 : if (aRv.Failed()) {
1914 0 : return;
1915 : }
1916 :
1917 0 : SetSelectionRange(start, end, dir, aRv);
1918 : }
1919 :
1920 : static nsITextControlFrame::SelectionDirection
1921 0 : DirectionStringToSelectionDirection(const Optional<nsAString>& aDirection)
1922 : {
1923 0 : if (!aDirection.WasPassed()) {
1924 : // We don't support directionless selections.
1925 0 : return nsITextControlFrame::eForward;
1926 : }
1927 :
1928 0 : return DirectionStringToSelectionDirection(aDirection.Value());
1929 : }
1930 :
1931 : void
1932 0 : nsTextEditorState::SetSelectionRange(uint32_t aSelectionStart,
1933 : uint32_t aSelectionEnd,
1934 : const Optional<nsAString>& aDirection,
1935 : ErrorResult& aRv)
1936 : {
1937 : nsITextControlFrame::SelectionDirection dir =
1938 0 : DirectionStringToSelectionDirection(aDirection);
1939 :
1940 0 : SetSelectionRange(aSelectionStart, aSelectionEnd, dir, aRv);
1941 0 : }
1942 :
1943 : void
1944 0 : nsTextEditorState::SetRangeText(const nsAString& aReplacement,
1945 : ErrorResult& aRv)
1946 : {
1947 : uint32_t start, end;
1948 0 : GetSelectionRange(&start, &end, aRv);
1949 0 : if (aRv.Failed()) {
1950 0 : return;
1951 : }
1952 :
1953 : SetRangeText(aReplacement, start, end, SelectionMode::Preserve,
1954 0 : aRv, Some(start), Some(end));
1955 : }
1956 :
1957 : void
1958 0 : nsTextEditorState::SetRangeText(const nsAString& aReplacement, uint32_t aStart,
1959 : uint32_t aEnd, SelectionMode aSelectMode,
1960 : ErrorResult& aRv,
1961 : const Maybe<uint32_t>& aSelectionStart,
1962 : const Maybe<uint32_t>& aSelectionEnd)
1963 : {
1964 0 : if (aStart > aEnd) {
1965 0 : aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
1966 0 : return;
1967 : }
1968 :
1969 0 : nsAutoString value;
1970 0 : mTextCtrlElement->GetValueFromSetRangeText(value);
1971 0 : uint32_t inputValueLength = value.Length();
1972 :
1973 0 : if (aStart > inputValueLength) {
1974 0 : aStart = inputValueLength;
1975 : }
1976 :
1977 0 : if (aEnd > inputValueLength) {
1978 0 : aEnd = inputValueLength;
1979 : }
1980 :
1981 : uint32_t selectionStart, selectionEnd;
1982 0 : if (!aSelectionStart) {
1983 0 : MOZ_ASSERT(!aSelectionEnd);
1984 0 : GetSelectionRange(&selectionStart, &selectionEnd, aRv);
1985 0 : if (aRv.Failed()) {
1986 0 : return;
1987 : }
1988 : } else {
1989 0 : MOZ_ASSERT(aSelectionEnd);
1990 0 : selectionStart = *aSelectionStart;
1991 0 : selectionEnd = *aSelectionEnd;
1992 : }
1993 :
1994 0 : MOZ_ASSERT(aStart <= aEnd);
1995 0 : value.Replace(aStart, aEnd - aStart, aReplacement);
1996 0 : nsresult rv = mTextCtrlElement->SetValueFromSetRangeText(value);
1997 0 : if (NS_FAILED(rv)) {
1998 0 : aRv.Throw(rv);
1999 0 : return;
2000 : }
2001 :
2002 0 : uint32_t newEnd = aStart + aReplacement.Length();
2003 0 : int32_t delta = aReplacement.Length() - (aEnd - aStart);
2004 :
2005 0 : switch (aSelectMode) {
2006 : case mozilla::dom::SelectionMode::Select:
2007 : {
2008 0 : selectionStart = aStart;
2009 0 : selectionEnd = newEnd;
2010 : }
2011 0 : break;
2012 : case mozilla::dom::SelectionMode::Start:
2013 : {
2014 0 : selectionStart = selectionEnd = aStart;
2015 : }
2016 0 : break;
2017 : case mozilla::dom::SelectionMode::End:
2018 : {
2019 0 : selectionStart = selectionEnd = newEnd;
2020 : }
2021 0 : break;
2022 : case mozilla::dom::SelectionMode::Preserve:
2023 : {
2024 0 : if (selectionStart > aEnd) {
2025 0 : selectionStart += delta;
2026 0 : } else if (selectionStart > aStart) {
2027 0 : selectionStart = aStart;
2028 : }
2029 :
2030 0 : if (selectionEnd > aEnd) {
2031 0 : selectionEnd += delta;
2032 0 : } else if (selectionEnd > aStart) {
2033 0 : selectionEnd = newEnd;
2034 : }
2035 : }
2036 0 : break;
2037 : default:
2038 0 : MOZ_CRASH("Unknown mode!");
2039 : }
2040 :
2041 0 : SetSelectionRange(selectionStart, selectionEnd, Optional<nsAString>(), aRv);
2042 : }
2043 :
2044 : HTMLInputElement*
2045 34 : nsTextEditorState::GetParentNumberControl(nsFrame* aFrame) const
2046 : {
2047 34 : MOZ_ASSERT(aFrame);
2048 34 : nsIContent* content = aFrame->GetContent();
2049 34 : MOZ_ASSERT(content);
2050 34 : nsIContent* parent = content->GetParent();
2051 34 : if (!parent) {
2052 0 : return nullptr;
2053 : }
2054 34 : nsIContent* parentOfParent = parent->GetParent();
2055 34 : if (!parentOfParent) {
2056 0 : return nullptr;
2057 : }
2058 34 : HTMLInputElement* input = HTMLInputElement::FromContent(parentOfParent);
2059 34 : if (input) {
2060 : // This function might be called during frame reconstruction as a result
2061 : // of changing the input control's type from number to something else. In
2062 : // that situation, the type of the control has changed, but its frame has
2063 : // not been reconstructed yet. So we need to check the type of the input
2064 : // control in addition to the type of the frame.
2065 0 : return (input->ControlType() == NS_FORM_INPUT_NUMBER) ? input : nullptr;
2066 : }
2067 :
2068 34 : return nullptr;
2069 : }
2070 :
2071 : void
2072 3 : nsTextEditorState::DestroyEditor()
2073 : {
2074 : // notify the editor that we are going away
2075 3 : if (mEditorInitialized) {
2076 1 : if (mTextListener) {
2077 1 : mTextEditor->RemoveEditorObserver(mTextListener);
2078 : }
2079 1 : mTextEditor->PreDestroy(true);
2080 1 : mEditorInitialized = false;
2081 : }
2082 3 : ClearValueCache();
2083 3 : }
2084 :
2085 : void
2086 2 : nsTextEditorState::UnbindFromFrame(nsTextControlFrame* aFrame)
2087 : {
2088 2 : NS_ENSURE_TRUE_VOID(mBoundFrame);
2089 :
2090 : // If it was, however, it should be unbounded from the same frame.
2091 2 : NS_ASSERTION(!aFrame || aFrame == mBoundFrame, "Unbinding from the wrong frame");
2092 2 : NS_ENSURE_TRUE_VOID(!aFrame || aFrame == mBoundFrame);
2093 :
2094 : // If the editor is modified but nsIEditorObserver::EditAction() hasn't been
2095 : // called yet, we need to notify it here because editor may be destroyed
2096 : // before EditAction() is called if selection listener causes flushing layout.
2097 3 : if (mTextListener && mTextEditor && mEditorInitialized &&
2098 1 : mTextEditor->IsInEditAction()) {
2099 0 : mTextListener->EditAction();
2100 : }
2101 :
2102 : // We need to start storing the value outside of the editor if we're not
2103 : // going to use it anymore, so retrieve it for now.
2104 4 : nsAutoString value;
2105 2 : GetValue(value, true);
2106 :
2107 2 : if (mRestoringSelection) {
2108 0 : mRestoringSelection->Revoke();
2109 0 : mRestoringSelection = nullptr;
2110 : }
2111 :
2112 : // Save our selection state if needed.
2113 : // Note that GetSelectionRange will attempt to work with our selection
2114 : // controller, so we should make sure we do it before we start doing things
2115 : // like destroying our editor (if we have one), tearing down the selection
2116 : // controller, and so forth.
2117 2 : if (!IsSelectionCached()) {
2118 : // Go ahead and cache it now.
2119 1 : uint32_t start = 0, end = 0;
2120 2 : IgnoredErrorResult rangeRv;
2121 1 : GetSelectionRange(&start, &end, rangeRv);
2122 :
2123 2 : IgnoredErrorResult dirRv;
2124 : nsITextControlFrame::SelectionDirection direction =
2125 1 : GetSelectionDirection(dirRv);
2126 :
2127 1 : MOZ_ASSERT(aFrame == mBoundFrame);
2128 1 : SelectionProperties& props = GetSelectionProperties();
2129 1 : props.SetStart(start);
2130 1 : props.SetEnd(end);
2131 1 : props.SetDirection(direction);
2132 1 : HTMLInputElement* number = GetParentNumberControl(aFrame);
2133 1 : if (number) {
2134 : // If we are inside a number control, cache the selection on the
2135 : // parent control, because this text editor state will be destroyed
2136 : // together with the native anonymous text control.
2137 0 : number->SetSelectionCached();
2138 : } else {
2139 1 : mSelectionCached = true;
2140 : }
2141 : }
2142 :
2143 : // Destroy our editor
2144 2 : DestroyEditor();
2145 :
2146 : // Clean up the controller
2147 2 : if (!SuppressEventHandlers(mBoundFrame->PresContext()))
2148 : {
2149 4 : nsCOMPtr<nsIControllers> controllers;
2150 : nsCOMPtr<nsIDOMHTMLInputElement> inputElement =
2151 4 : do_QueryInterface(mTextCtrlElement);
2152 2 : if (inputElement)
2153 2 : inputElement->GetControllers(getter_AddRefs(controllers));
2154 : else
2155 : {
2156 : nsCOMPtr<nsIDOMHTMLTextAreaElement> textAreaElement =
2157 0 : do_QueryInterface(mTextCtrlElement);
2158 0 : if (textAreaElement) {
2159 0 : textAreaElement->GetControllers(getter_AddRefs(controllers));
2160 : }
2161 : }
2162 :
2163 2 : if (controllers)
2164 : {
2165 : uint32_t numControllers;
2166 2 : nsresult rv = controllers->GetControllerCount(&numControllers);
2167 2 : NS_ASSERTION((NS_SUCCEEDED(rv)), "bad result in gfx text control destructor");
2168 9 : for (uint32_t i = 0; i < numControllers; i ++)
2169 : {
2170 14 : nsCOMPtr<nsIController> controller;
2171 7 : rv = controllers->GetControllerAt(i, getter_AddRefs(controller));
2172 7 : if (NS_SUCCEEDED(rv) && controller)
2173 : {
2174 14 : nsCOMPtr<nsIControllerContext> editController = do_QueryInterface(controller);
2175 7 : if (editController)
2176 : {
2177 4 : editController->SetCommandContext(nullptr);
2178 : }
2179 : }
2180 : }
2181 : }
2182 : }
2183 :
2184 2 : if (mSelCon) {
2185 2 : if (mTextListener) {
2186 4 : RefPtr<nsISelection> domSelection;
2187 6 : if (NS_SUCCEEDED(mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
2188 8 : getter_AddRefs(domSelection))) &&
2189 2 : domSelection) {
2190 4 : nsCOMPtr<nsISelectionPrivate> selPriv(do_QueryInterface(domSelection));
2191 :
2192 2 : selPriv->RemoveSelectionListener(static_cast<nsISelectionListener*>
2193 2 : (mTextListener));
2194 : }
2195 : }
2196 :
2197 2 : mSelCon->SetScrollableFrame(nullptr);
2198 2 : mSelCon = nullptr;
2199 : }
2200 :
2201 2 : if (mTextListener)
2202 : {
2203 2 : mTextListener->SetFrame(nullptr);
2204 :
2205 4 : nsCOMPtr<EventTarget> target = do_QueryInterface(mTextCtrlElement);
2206 2 : EventListenerManager* manager = target->GetExistingListenerManager();
2207 2 : if (manager) {
2208 4 : manager->RemoveEventListenerByType(mTextListener,
2209 4 : NS_LITERAL_STRING("keydown"),
2210 10 : TrustedEventsAtSystemGroupBubble());
2211 4 : manager->RemoveEventListenerByType(mTextListener,
2212 4 : NS_LITERAL_STRING("keypress"),
2213 10 : TrustedEventsAtSystemGroupBubble());
2214 4 : manager->RemoveEventListenerByType(mTextListener,
2215 4 : NS_LITERAL_STRING("keyup"),
2216 10 : TrustedEventsAtSystemGroupBubble());
2217 : }
2218 :
2219 2 : mTextListener = nullptr;
2220 : }
2221 :
2222 2 : mBoundFrame = nullptr;
2223 :
2224 : // Now that we don't have a frame any more, store the value in the text buffer.
2225 : // The only case where we don't do this is if a value transfer is in progress.
2226 2 : if (!mValueTransferInProgress) {
2227 2 : bool success = SetValue(value, eSetValue_Internal);
2228 : // TODO Find something better to do if this fails...
2229 2 : NS_ENSURE_TRUE_VOID(success);
2230 : }
2231 :
2232 2 : if (mRootNode && mMutationObserver) {
2233 0 : mRootNode->RemoveMutationObserver(mMutationObserver);
2234 0 : mMutationObserver = nullptr;
2235 : }
2236 :
2237 : // Unbind the anonymous content from the tree.
2238 : // We actually hold a reference to the content nodes so that
2239 : // they're not actually destroyed.
2240 2 : nsContentUtils::DestroyAnonymousContent(&mRootNode);
2241 2 : nsContentUtils::DestroyAnonymousContent(&mPlaceholderDiv);
2242 2 : nsContentUtils::DestroyAnonymousContent(&mPreviewDiv);
2243 : }
2244 :
2245 : nsresult
2246 4 : nsTextEditorState::CreateRootNode()
2247 : {
2248 4 : MOZ_ASSERT(!mRootNode);
2249 4 : MOZ_ASSERT(mBoundFrame);
2250 :
2251 4 : nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
2252 4 : NS_ENSURE_TRUE(shell, NS_ERROR_FAILURE);
2253 :
2254 4 : nsIDocument *doc = shell->GetDocument();
2255 4 : NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
2256 :
2257 : // Now create a DIV and add it to the anonymous content child list.
2258 8 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
2259 8 : nodeInfo = doc->NodeInfoManager()->GetNodeInfo(nsGkAtoms::div, nullptr,
2260 : kNameSpaceID_XHTML,
2261 4 : nsIDOMNode::ELEMENT_NODE);
2262 :
2263 8 : nsresult rv = NS_NewHTMLElement(getter_AddRefs(mRootNode), nodeInfo.forget(),
2264 4 : NOT_FROM_PARSER);
2265 4 : NS_ENSURE_SUCCESS(rv, rv);
2266 :
2267 4 : if (!IsSingleLineTextControl()) {
2268 0 : mMutationObserver = new nsAnonDivObserver(this);
2269 0 : mRootNode->AddMutationObserver(mMutationObserver);
2270 : }
2271 :
2272 4 : return rv;
2273 : }
2274 :
2275 : nsresult
2276 4 : nsTextEditorState::InitializeRootNode()
2277 : {
2278 : // Make our root node editable
2279 4 : mRootNode->SetFlags(NODE_IS_EDITABLE);
2280 :
2281 : // Set the necessary classes on the text control. We use class values
2282 : // instead of a 'style' attribute so that the style comes from a user-agent
2283 : // style sheet and is still applied even if author styles are disabled.
2284 8 : nsAutoString classValue;
2285 4 : classValue.AppendLiteral("anonymous-div");
2286 4 : int32_t wrapCols = GetWrapCols();
2287 4 : if (wrapCols > 0) {
2288 0 : classValue.AppendLiteral(" wrap");
2289 : }
2290 4 : if (!IsSingleLineTextControl()) {
2291 : // We can't just inherit the overflow because setting visible overflow will
2292 : // crash when the number of lines exceeds the height of the textarea and
2293 : // setting -moz-hidden-unscrollable overflow (NS_STYLE_OVERFLOW_CLIP)
2294 : // doesn't paint the caret for some reason.
2295 0 : const nsStyleDisplay* disp = mBoundFrame->StyleDisplay();
2296 0 : if (disp->mOverflowX != NS_STYLE_OVERFLOW_VISIBLE &&
2297 0 : disp->mOverflowX != NS_STYLE_OVERFLOW_CLIP) {
2298 0 : classValue.AppendLiteral(" inherit-overflow");
2299 : }
2300 0 : classValue.AppendLiteral(" inherit-scroll-behavior");
2301 : }
2302 4 : nsresult rv = mRootNode->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
2303 4 : classValue, false);
2304 4 : NS_ENSURE_SUCCESS(rv, rv);
2305 :
2306 4 : return mBoundFrame->UpdateValueDisplay(false);
2307 : }
2308 :
2309 : Element*
2310 4 : nsTextEditorState::CreateEmptyDivNode()
2311 : {
2312 4 : MOZ_ASSERT(mBoundFrame);
2313 :
2314 4 : nsIPresShell *shell = mBoundFrame->PresContext()->GetPresShell();
2315 4 : MOZ_ASSERT(shell);
2316 :
2317 4 : nsIDocument *doc = shell->GetDocument();
2318 4 : MOZ_ASSERT(doc);
2319 :
2320 4 : nsNodeInfoManager* pNodeInfoManager = doc->NodeInfoManager();
2321 4 : MOZ_ASSERT(pNodeInfoManager);
2322 :
2323 : Element *element;
2324 :
2325 : // Create a DIV
2326 8 : RefPtr<mozilla::dom::NodeInfo> nodeInfo;
2327 8 : nodeInfo = pNodeInfoManager->GetNodeInfo(nsGkAtoms::div, nullptr,
2328 : kNameSpaceID_XHTML,
2329 4 : nsIDOMNode::ELEMENT_NODE);
2330 :
2331 4 : element = NS_NewHTMLDivElement(nodeInfo.forget());
2332 :
2333 : // Create the text node for DIV
2334 8 : RefPtr<nsTextNode> textNode = new nsTextNode(pNodeInfoManager);
2335 :
2336 4 : element->AppendChildTo(textNode, false);
2337 :
2338 8 : return element;
2339 : }
2340 :
2341 : nsresult
2342 4 : nsTextEditorState::CreatePlaceholderNode()
2343 : {
2344 : #ifdef DEBUG
2345 : {
2346 8 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2347 4 : if (content) {
2348 8 : nsAutoString placeholderTxt;
2349 4 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder,
2350 8 : placeholderTxt);
2351 4 : nsContentUtils::RemoveNewlines(placeholderTxt);
2352 4 : NS_ASSERTION(!placeholderTxt.IsEmpty(), "CreatePlaceholderNode() shouldn't \
2353 : be called if @placeholder is the empty string when trimmed from line breaks");
2354 : }
2355 : }
2356 : #endif // DEBUG
2357 :
2358 4 : NS_ENSURE_TRUE(!mPlaceholderDiv, NS_ERROR_UNEXPECTED);
2359 :
2360 : // Create a DIV for the placeholder
2361 : // and add it to the anonymous content child list
2362 4 : mPlaceholderDiv = CreateEmptyDivNode();
2363 :
2364 : // initialize the text
2365 4 : UpdatePlaceholderText(false);
2366 :
2367 4 : return NS_OK;
2368 : }
2369 :
2370 : nsresult
2371 0 : nsTextEditorState::CreatePreviewNode()
2372 : {
2373 0 : NS_ENSURE_TRUE(!mPreviewDiv, NS_ERROR_UNEXPECTED);
2374 :
2375 : // Create a DIV for the preview
2376 : // and add it to the anonymous content child list
2377 0 : mPreviewDiv = CreateEmptyDivNode();
2378 :
2379 0 : mPreviewDiv->SetAttr(kNameSpaceID_None, nsGkAtoms::_class,
2380 0 : NS_LITERAL_STRING("preview-div"), false);
2381 :
2382 0 : return NS_OK;
2383 : }
2384 :
2385 : int32_t
2386 2 : nsTextEditorState::GetMaxLength()
2387 : {
2388 4 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2389 : nsGenericHTMLElement* element =
2390 2 : nsGenericHTMLElement::FromContentOrNull(content);
2391 2 : if (NS_WARN_IF(!element)) {
2392 0 : return -1;
2393 : }
2394 :
2395 2 : const nsAttrValue* attr = element->GetParsedAttr(nsGkAtoms::maxlength);
2396 2 : if (attr && attr->Type() == nsAttrValue::eInteger) {
2397 0 : return attr->GetIntegerValue();
2398 : }
2399 :
2400 2 : return -1;
2401 : }
2402 :
2403 : void
2404 34 : nsTextEditorState::GetValue(nsAString& aValue, bool aIgnoreWrap) const
2405 : {
2406 : // While SetValue() is being called and requesting to commit composition to
2407 : // IME, GetValue() may be called for appending text or something. Then, we
2408 : // need to return the latest aValue of SetValue() since the value hasn't
2409 : // been set to the editor yet.
2410 34 : if (mIsCommittingComposition) {
2411 0 : aValue = mValueBeingSet;
2412 0 : return;
2413 : }
2414 :
2415 47 : if (mTextEditor && mBoundFrame &&
2416 14 : (mEditorInitialized || !IsSingleLineTextControl())) {
2417 4 : bool canCache = aIgnoreWrap && !IsSingleLineTextControl();
2418 4 : if (canCache && !mCachedValue.IsEmpty()) {
2419 0 : aValue = mCachedValue;
2420 0 : return;
2421 : }
2422 :
2423 4 : aValue.Truncate(); // initialize out param
2424 :
2425 : uint32_t flags = (nsIDocumentEncoder::OutputLFLineBreak |
2426 : nsIDocumentEncoder::OutputPreformatted |
2427 4 : nsIDocumentEncoder::OutputPersistNBSP);
2428 :
2429 4 : if (IsPlainTextControl())
2430 : {
2431 4 : flags |= nsIDocumentEncoder::OutputBodyOnly;
2432 : }
2433 :
2434 4 : if (!aIgnoreWrap) {
2435 : nsITextControlElement::nsHTMLTextWrap wrapProp;
2436 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2437 0 : if (content &&
2438 0 : nsITextControlElement::GetWrapPropertyEnum(content, wrapProp) &&
2439 0 : wrapProp == nsITextControlElement::eHTMLTextWrap_Hard) {
2440 0 : flags |= nsIDocumentEncoder::OutputWrap;
2441 : }
2442 : }
2443 :
2444 : // What follows is a bit of a hack. The problem is that we could be in
2445 : // this method because we're being destroyed for whatever reason while
2446 : // script is executing. If that happens, editor will run with the
2447 : // privileges of the executing script, which means it may not be able to
2448 : // access its own DOM nodes! Let's try to deal with that by pushing a null
2449 : // JSContext on the JSContext stack to make it clear that we're native
2450 : // code. Note that any script that's directly trying to access our value
2451 : // has to be going through some scriptable object to do that and that
2452 : // already does the relevant security checks.
2453 : // XXXbz if we could just get the textContent of our anonymous content (eg
2454 : // if plaintext editor didn't create <br> nodes all over), we wouldn't need
2455 : // this.
2456 : { /* Scope for AutoNoJSAPI. */
2457 8 : AutoNoJSAPI nojsapi;
2458 :
2459 16 : mTextEditor->OutputToString(NS_LITERAL_STRING("text/plain"), flags,
2460 12 : aValue);
2461 : }
2462 4 : if (canCache) {
2463 0 : mCachedValue = aValue;
2464 : } else {
2465 4 : mCachedValue.Truncate();
2466 : }
2467 : } else {
2468 30 : if (!mTextCtrlElement->ValueChanged() || !mValue) {
2469 30 : mTextCtrlElement->GetDefaultValueFromContent(aValue);
2470 : } else {
2471 0 : aValue = *mValue;
2472 : }
2473 : }
2474 : }
2475 :
2476 : bool
2477 6 : nsTextEditorState::SetValue(const nsAString& aValue, const nsAString* aOldValue,
2478 : uint32_t aFlags)
2479 : {
2480 12 : nsAutoString newValue(aValue);
2481 :
2482 : // While mIsCommittingComposition is true (that means that some event
2483 : // handlers which are fired during committing composition are the caller of
2484 : // this method), GetValue() uses mValueBeingSet for its result because the
2485 : // first calls of this methods hasn't set the value yet. So, when it's true,
2486 : // we need to modify mValueBeingSet. In this case, we will back to the first
2487 : // call of this method, then, mValueBeingSet will be truncated when
2488 : // mIsCommittingComposition is set false. See below.
2489 6 : if (mIsCommittingComposition) {
2490 0 : mValueBeingSet = aValue;
2491 : // GetValue doesn't return current text frame's content during committing.
2492 : // So we cannot trust this old value
2493 0 : aOldValue = nullptr;
2494 : }
2495 :
2496 : // Note that if this may be called during reframe of the editor. In such
2497 : // case, we shouldn't commit composition. Therefore, when this is called
2498 : // for internal processing, we shouldn't commit the composition.
2499 6 : if (aFlags & (eSetValue_BySetUserInput | eSetValue_ByContent)) {
2500 1 : if (EditorHasComposition()) {
2501 : // When this is called recursively, there shouldn't be composition.
2502 0 : if (NS_WARN_IF(mIsCommittingComposition)) {
2503 : // Don't request to commit composition again. But if it occurs,
2504 : // we should skip to set the new value to the editor here. It should
2505 : // be set later with the updated mValueBeingSet.
2506 0 : return true;
2507 : }
2508 0 : if (NS_WARN_IF(!mBoundFrame)) {
2509 : // We're not sure if this case is possible.
2510 : } else {
2511 : // If setting value won't change current value, we shouldn't commit
2512 : // composition for compatibility with the other browsers.
2513 0 : nsAutoString currentValue;
2514 0 : if (aOldValue) {
2515 : #ifdef DEBUG
2516 0 : mBoundFrame->GetText(currentValue);
2517 0 : MOZ_ASSERT(currentValue.Equals(*aOldValue));
2518 : #endif
2519 0 : currentValue.Assign(*aOldValue);
2520 : } else {
2521 0 : mBoundFrame->GetText(currentValue);
2522 : }
2523 0 : if (newValue == currentValue) {
2524 : // Note that in this case, we shouldn't fire any events with setting
2525 : // value because event handlers may try to set value recursively but
2526 : // we cannot commit composition at that time due to unsafe to run
2527 : // script (see below).
2528 0 : return true;
2529 : }
2530 : // IME might commit composition, then change value, so we cannot
2531 : // trust old value from parameter.
2532 0 : aOldValue = nullptr;
2533 : }
2534 : // If there is composition, need to commit composition first because
2535 : // other browsers do that.
2536 : // NOTE: We don't need to block nested calls of this because input nor
2537 : // other events won't be fired by setting values and script blocker
2538 : // is used during setting the value to the editor. IE also allows
2539 : // to set the editor value on the input event which is caused by
2540 : // forcibly committing composition.
2541 0 : if (nsContentUtils::IsSafeToRunScript()) {
2542 0 : WeakPtr<nsTextEditorState> self(this);
2543 : // WARNING: During this call, compositionupdate, compositionend, input
2544 : // events will be fired. Therefore, everything can occur. E.g., the
2545 : // document may be unloaded.
2546 0 : mValueBeingSet = aValue;
2547 0 : mIsCommittingComposition = true;
2548 0 : RefPtr<TextEditor> textEditor = mTextEditor;
2549 0 : nsresult rv = textEditor->ForceCompositionEnd();
2550 0 : if (!self.get()) {
2551 0 : return true;
2552 : }
2553 0 : mIsCommittingComposition = false;
2554 : // If this is called recursively during committing composition and
2555 : // some of them may be skipped above. Therefore, we need to set
2556 : // value to the editor with the aValue of the latest call.
2557 0 : newValue = mValueBeingSet;
2558 : // When mIsCommittingComposition is false, mValueBeingSet won't be
2559 : // used. Therefore, let's clear it.
2560 0 : mValueBeingSet.Truncate();
2561 0 : if (NS_FAILED(rv)) {
2562 0 : NS_WARNING("nsTextEditorState failed to commit composition");
2563 0 : return true;
2564 : }
2565 : } else {
2566 : NS_WARNING("SetValue() is called when there is composition but "
2567 0 : "it's not safe to request to commit the composition");
2568 : }
2569 : }
2570 : }
2571 :
2572 : // \r is an illegal character in the dom, but people use them,
2573 : // so convert windows and mac platform linebreaks to \n:
2574 6 : if (!nsContentUtils::PlatformToDOMLineBreaks(newValue, fallible)) {
2575 0 : return false;
2576 : }
2577 :
2578 6 : if (mTextEditor && mBoundFrame) {
2579 : // The InsertText call below might flush pending notifications, which
2580 : // could lead into a scheduled PrepareEditor to be called. That will
2581 : // lead to crashes (or worse) because we'd be initializing the editor
2582 : // before InsertText returns. This script blocker makes sure that
2583 : // PrepareEditor cannot be called prematurely.
2584 2 : nsAutoScriptBlocker scriptBlocker;
2585 :
2586 : #ifdef DEBUG
2587 1 : if (IsSingleLineTextControl()) {
2588 1 : NS_ASSERTION(mEditorInitialized || mInitializing,
2589 : "We should never try to use the editor if we're not initialized unless we're being initialized");
2590 : }
2591 : #endif
2592 :
2593 2 : nsAutoString currentValue;
2594 1 : if (aOldValue) {
2595 : #ifdef DEBUG
2596 1 : mBoundFrame->GetText(currentValue);
2597 1 : MOZ_ASSERT(currentValue.Equals(*aOldValue));
2598 : #endif
2599 1 : currentValue.Assign(*aOldValue);
2600 : } else {
2601 0 : mBoundFrame->GetText(currentValue);
2602 : }
2603 :
2604 2 : AutoWeakFrame weakFrame(mBoundFrame);
2605 :
2606 : // this is necessary to avoid infinite recursion
2607 1 : if (!currentValue.Equals(newValue)) {
2608 2 : RefPtr<TextEditor> textEditor = mTextEditor;
2609 2 : ValueSetter valueSetter(textEditor);
2610 :
2611 2 : nsCOMPtr<nsIDocument> document = textEditor->GetDocument();
2612 1 : if (NS_WARN_IF(!document)) {
2613 0 : return true;
2614 : }
2615 :
2616 : // Time to mess with our security context... See comments in GetValue()
2617 : // for why this is needed. Note that we have to do this up here, because
2618 : // otherwise SelectAll() will fail.
2619 : {
2620 2 : AutoNoJSAPI nojsapi;
2621 :
2622 2 : nsCOMPtr<nsISelection> domSel;
2623 1 : mSelCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
2624 2 : getter_AddRefs(domSel));
2625 1 : SelectionBatcher selectionBatcher(domSel ? domSel->AsSelection() :
2626 3 : nullptr);
2627 :
2628 1 : if (NS_WARN_IF(!weakFrame.IsAlive())) {
2629 0 : return true;
2630 : }
2631 :
2632 1 : valueSetter.Init();
2633 :
2634 : // get the flags, remove readonly, disabled and max-length,
2635 : // set the value, restore flags
2636 : {
2637 2 : AutoRestoreEditorState restoreState(textEditor);
2638 :
2639 1 : mTextListener->SettingValue(true);
2640 1 : bool notifyValueChanged = !!(aFlags & eSetValue_Notify);
2641 1 : mTextListener->SetValueChanged(notifyValueChanged);
2642 :
2643 1 : if (aFlags & eSetValue_BySetUserInput) {
2644 0 : nsCOMPtr<nsISelectionController> kungFuDeathGrip = mSelCon.get();
2645 0 : uint32_t currentLength = currentValue.Length();
2646 0 : uint32_t newlength = newValue.Length();
2647 0 : if (!currentLength ||
2648 0 : !StringBeginsWith(newValue, currentValue)) {
2649 : // Replace the whole text.
2650 0 : currentLength = 0;
2651 0 : kungFuDeathGrip->SelectAll();
2652 : } else {
2653 : // Collapse selection to the end so that we can append data.
2654 0 : mBoundFrame->SelectAllOrCollapseToEndOfText(false);
2655 : }
2656 : const nsAString& insertValue =
2657 0 : StringTail(newValue, newlength - currentLength);
2658 :
2659 0 : if (insertValue.IsEmpty()) {
2660 0 : textEditor->DeleteSelection(nsIEditor::eNone, nsIEditor::eStrip);
2661 : } else {
2662 0 : textEditor->InsertText(insertValue);
2663 : }
2664 : } else {
2665 2 : AutoDisableUndo disableUndo(textEditor);
2666 1 : if (domSel) {
2667 : // Since we don't use undo transaction, we don't need to store
2668 : // selection state. SetText will set selection to tail.
2669 1 : domSel->RemoveAllRanges();
2670 : }
2671 :
2672 1 : textEditor->SetText(newValue);
2673 : }
2674 :
2675 1 : mTextListener->SetValueChanged(true);
2676 1 : mTextListener->SettingValue(false);
2677 :
2678 1 : if (!notifyValueChanged) {
2679 : // Listener doesn't update frame, but it is required for placeholder
2680 0 : ValueWasChanged(true);
2681 : }
2682 : }
2683 :
2684 1 : if (!weakFrame.IsAlive()) {
2685 : // If the frame was destroyed because of a flush somewhere inside
2686 : // InsertText, mBoundFrame here will be false. But it's also possible
2687 : // for the frame to go away because of another reason (such as deleting
2688 : // the existing selection -- see bug 574558), in which case we don't
2689 : // need to reset the value here.
2690 0 : if (!mBoundFrame) {
2691 0 : return SetValue(newValue, aFlags & eSetValue_Notify);
2692 : }
2693 0 : return true;
2694 : }
2695 :
2696 1 : if (!IsSingleLineTextControl()) {
2697 0 : if (!mCachedValue.Assign(newValue, fallible)) {
2698 0 : return false;
2699 : }
2700 : }
2701 : }
2702 : }
2703 : } else {
2704 5 : if (!mValue) {
2705 3 : mValue.emplace();
2706 : }
2707 :
2708 : // We can't just early-return here if mValue->Equals(newValue), because
2709 : // ValueWasChanged and OnValueChanged below still need to be called.
2710 10 : if (!mValue->Equals(newValue) ||
2711 5 : !nsContentUtils::SkipCursorMoveForSameValueSet()) {
2712 0 : if (!mValue->Assign(newValue, fallible)) {
2713 0 : return false;
2714 : }
2715 :
2716 : // Since we have no editor we presumably have cached selection state.
2717 0 : if (IsSelectionCached()) {
2718 0 : SelectionProperties& props = GetSelectionProperties();
2719 0 : if (aFlags & eSetValue_MoveCursorToEndIfValueChanged) {
2720 0 : props.SetStart(newValue.Length());
2721 0 : props.SetEnd(newValue.Length());
2722 0 : props.SetDirection(nsITextControlFrame::eForward);
2723 : } else {
2724 : // Make sure our cached selection position is not outside the new value.
2725 0 : props.SetStart(std::min(props.GetStart(), newValue.Length()));
2726 0 : props.SetEnd(std::min(props.GetEnd(), newValue.Length()));
2727 : }
2728 : }
2729 :
2730 : // Update the frame display if needed
2731 0 : if (mBoundFrame) {
2732 0 : mBoundFrame->UpdateValueDisplay(true);
2733 : }
2734 : } else {
2735 : // Even if our value is not actually changing, apparently we need to mark
2736 : // our SelectionProperties dirty to make accessibility tests happy.
2737 : // Probably because they depend on the SetSelectionRange() call we make on
2738 : // our frame in RestoreSelectionState, but I have no idea why they do.
2739 5 : if (IsSelectionCached()) {
2740 5 : SelectionProperties& props = GetSelectionProperties();
2741 5 : props.SetIsDirty();
2742 : }
2743 : }
2744 :
2745 : // If we've reached the point where the root node has been created, we
2746 : // can assume that it's safe to notify.
2747 5 : ValueWasChanged(!!mRootNode);
2748 : }
2749 :
2750 12 : mTextCtrlElement->OnValueChanged(/* aNotify = */ !!mRootNode,
2751 12 : /* aWasInteractiveUserChange = */ false);
2752 :
2753 6 : return true;
2754 : }
2755 :
2756 : bool
2757 17 : nsTextEditorState::HasNonEmptyValue()
2758 : {
2759 21 : if (mTextEditor && mBoundFrame && mEditorInitialized &&
2760 4 : !mIsCommittingComposition) {
2761 : bool empty;
2762 4 : nsresult rv = mTextEditor->GetDocumentIsEmpty(&empty);
2763 4 : if (NS_SUCCEEDED(rv)) {
2764 4 : return !empty;
2765 : }
2766 : }
2767 :
2768 26 : nsAutoString value;
2769 13 : GetValue(value, true);
2770 13 : return !value.IsEmpty();
2771 : }
2772 :
2773 : void
2774 4 : nsTextEditorState::InitializeKeyboardEventListeners()
2775 : {
2776 : //register key listeners
2777 8 : nsCOMPtr<EventTarget> target = do_QueryInterface(mTextCtrlElement);
2778 4 : EventListenerManager* manager = target->GetOrCreateListenerManager();
2779 4 : if (manager) {
2780 8 : manager->AddEventListenerByType(mTextListener,
2781 8 : NS_LITERAL_STRING("keydown"),
2782 20 : TrustedEventsAtSystemGroupBubble());
2783 8 : manager->AddEventListenerByType(mTextListener,
2784 8 : NS_LITERAL_STRING("keypress"),
2785 20 : TrustedEventsAtSystemGroupBubble());
2786 8 : manager->AddEventListenerByType(mTextListener,
2787 8 : NS_LITERAL_STRING("keyup"),
2788 20 : TrustedEventsAtSystemGroupBubble());
2789 : }
2790 :
2791 4 : mSelCon->SetScrollableFrame(do_QueryFrame(mBoundFrame->PrincipalChildList().FirstChild()));
2792 4 : }
2793 :
2794 : void
2795 5 : nsTextEditorState::ValueWasChanged(bool aNotify)
2796 : {
2797 5 : UpdateOverlayTextVisibility(aNotify);
2798 5 : }
2799 :
2800 : void
2801 4 : nsTextEditorState::UpdatePlaceholderText(bool aNotify)
2802 : {
2803 4 : NS_ASSERTION(mPlaceholderDiv, "This function should not be called if "
2804 : "mPlaceholderDiv isn't set");
2805 :
2806 : // If we don't have a placeholder div, there's nothing to do.
2807 4 : if (!mPlaceholderDiv)
2808 0 : return;
2809 :
2810 8 : nsAutoString placeholderValue;
2811 :
2812 8 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2813 4 : content->GetAttr(kNameSpaceID_None, nsGkAtoms::placeholder, placeholderValue);
2814 4 : nsContentUtils::RemoveNewlines(placeholderValue);
2815 4 : NS_ASSERTION(mPlaceholderDiv->GetFirstChild(), "placeholder div has no child");
2816 4 : mPlaceholderDiv->GetFirstChild()->SetText(placeholderValue, aNotify);
2817 : }
2818 :
2819 : void
2820 0 : nsTextEditorState::SetPreviewText(const nsAString& aValue, bool aNotify)
2821 : {
2822 : // If we don't have a preview div, there's nothing to do.
2823 0 : if (!mPreviewDiv)
2824 0 : return;
2825 :
2826 0 : nsAutoString previewValue(aValue);
2827 :
2828 0 : nsContentUtils::RemoveNewlines(previewValue);
2829 0 : MOZ_ASSERT(mPreviewDiv->GetFirstChild(), "preview div has no child");
2830 0 : mPreviewDiv->GetFirstChild()->SetText(previewValue, aNotify);
2831 :
2832 0 : UpdateOverlayTextVisibility(aNotify);
2833 : }
2834 :
2835 : void
2836 10 : nsTextEditorState::GetPreviewText(nsAString& aValue)
2837 : {
2838 : // If we don't have a preview div, there's nothing to do.
2839 10 : if (!mPreviewDiv)
2840 10 : return;
2841 :
2842 0 : MOZ_ASSERT(mPreviewDiv->GetFirstChild(), "preview div has no child");
2843 0 : const nsTextFragment *text = mPreviewDiv->GetFirstChild()->GetText();
2844 :
2845 0 : aValue.Truncate();
2846 0 : text->AppendTo(aValue);
2847 : }
2848 :
2849 : void
2850 10 : nsTextEditorState::UpdateOverlayTextVisibility(bool aNotify)
2851 : {
2852 20 : nsAutoString value, previewValue;
2853 10 : bool valueIsEmpty = !HasNonEmptyValue();
2854 10 : GetPreviewText(previewValue);
2855 :
2856 10 : mPreviewVisibility = valueIsEmpty && !previewValue.IsEmpty();
2857 10 : mPlaceholderVisibility = valueIsEmpty && previewValue.IsEmpty();
2858 :
2859 19 : if (mPlaceholderVisibility &&
2860 9 : !nsContentUtils::ShowInputPlaceholderOnFocus()) {
2861 0 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2862 0 : mPlaceholderVisibility = !nsContentUtils::IsFocusedContent(content);
2863 : }
2864 :
2865 10 : if (mBoundFrame && aNotify) {
2866 1 : mBoundFrame->InvalidateFrame();
2867 : }
2868 10 : }
2869 :
2870 : void
2871 2 : nsTextEditorState::HideSelectionIfBlurred()
2872 : {
2873 2 : MOZ_ASSERT(mSelCon, "Should have a selection controller if we have a frame!");
2874 4 : nsCOMPtr<nsIContent> content = do_QueryInterface(mTextCtrlElement);
2875 2 : if (!nsContentUtils::IsFocusedContent(content)) {
2876 2 : mSelCon->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
2877 : }
2878 2 : }
2879 :
2880 : bool
2881 1 : nsTextEditorState::EditorHasComposition()
2882 : {
2883 1 : return mTextEditor && mTextEditor->IsIMEComposing();
2884 : }
2885 :
2886 0 : NS_IMPL_ISUPPORTS(nsAnonDivObserver, nsIMutationObserver)
2887 :
2888 : void
2889 0 : nsAnonDivObserver::CharacterDataChanged(nsIDocument* aDocument,
2890 : nsIContent* aContent,
2891 : CharacterDataChangeInfo* aInfo)
2892 : {
2893 0 : mTextEditorState->ClearValueCache();
2894 0 : }
2895 :
2896 : void
2897 0 : nsAnonDivObserver::ContentAppended(nsIDocument* aDocument,
2898 : nsIContent* aContainer,
2899 : nsIContent* aFirstNewContent,
2900 : int32_t /* unused */)
2901 : {
2902 0 : mTextEditorState->ClearValueCache();
2903 0 : }
2904 :
2905 : void
2906 0 : nsAnonDivObserver::ContentInserted(nsIDocument* aDocument,
2907 : nsIContent* aContainer,
2908 : nsIContent* aChild,
2909 : int32_t /* unused */)
2910 : {
2911 0 : mTextEditorState->ClearValueCache();
2912 0 : }
2913 :
2914 : void
2915 0 : nsAnonDivObserver::ContentRemoved(nsIDocument* aDocument,
2916 : nsIContent* aContainer,
2917 : nsIContent* aChild,
2918 : int32_t aIndexInContainer,
2919 : nsIContent* aPreviousSibling)
2920 : {
2921 0 : mTextEditorState->ClearValueCache();
2922 0 : }
|