Bug 1494333 - index crons just like artifacts r=Callek
[gecko.git] / dom / html / nsTextEditorState.h
blob9c4a713cd8df59d8673592ce2548d2c6ecea0a2b
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsTextEditorState_h__
8 #define nsTextEditorState_h__
10 #include "nsString.h"
11 #include "nsITextControlElement.h"
12 #include "nsITextControlFrame.h"
13 #include "nsCycleCollectionParticipant.h"
14 #include "mozilla/dom/Element.h"
15 #include "mozilla/Attributes.h"
16 #include "mozilla/Maybe.h"
17 #include "mozilla/TextEditor.h"
18 #include "mozilla/WeakPtr.h"
19 #include "mozilla/dom/HTMLInputElementBinding.h"
20 #include "mozilla/dom/Nullable.h"
22 class nsTextControlFrame;
23 class nsTextInputSelectionImpl;
24 class nsAnonDivObserver;
25 class nsISelectionController;
26 class nsFrameSelection;
27 class nsITextControlElement;
28 class nsFrame;
30 namespace mozilla {
32 class ErrorResult;
33 class TextInputListener;
35 namespace dom {
36 class HTMLInputElement;
37 } // namespace dom
38 } // namespace mozilla
40 /**
41 * nsTextEditorState is a class which is responsible for managing the state of
42 * plaintext controls. This currently includes the following HTML elements:
43 * <input type=text>
44 * <input type=password>
45 * <textarea>
46 * and also XUL controls such as <textbox> which use one of these elements behind
47 * the scenes.
49 * This class is held as a member of HTMLInputElement and nsHTMLTextAreaElement.
50 * The public functions in this class include the public APIs which content/ uses.
51 * Layout code uses the nsITextControlElement interface to invoke functions on this
52 * class.
54 * The design motivation behind this class is maintaining all of the things which
55 * collectively are considered the "state" of the text control in a single location.
56 * This state includes several things:
58 * * The control's value. This value is stored in the mValue member, and is only
59 * used when there is no frame for the control, or when the editor object has
60 * not been initialized yet.
62 * * The control's associated frame. This value is stored in the mBoundFrame member.
63 * A text control might never have an associated frame during its life cycle,
64 * or might have several different ones, but at any given moment in time there is
65 * a maximum of 1 bound frame to each text control.
67 * * The control's associated editor. This value is stored in the mEditor member.
68 * An editor is initilized for the control only when necessary (that is, when either
69 * the user is about to interact with the text control, or when some other code
70 * needs to access the editor object. Without a frame bound to the control, an
71 * editor is never initialzied. Once initialized, the editor might outlive the frame,
72 * in which case the same editor will be used if a new frame gets bound to the
73 * text control.
75 * * The anonymous content associated with the text control's frame, including the
76 * value div (the DIV element responsible for holding the value of the text control)
77 * and the placeholder div (the DIV element responsible for holding the placeholder
78 * value of the text control.) These values are stored in the mRootNode and
79 * mPlaceholderDiv members, respectively. They will be created when a
80 * frame is bound to the text control. They will be destroyed when the frame is
81 * unbound from the object. We could try and hold on to the anonymous content
82 * between different frames, but unfortunately that is not currently possible
83 * because they are not unbound from the document in time.
85 * * The frame selection controller. This value is stored in the mSelCon member.
86 * The frame selection controller is responsible for maintaining the selection state
87 * on a frame. It is created when a frame is bound to the text control element,
88 * and will be destroy when the frame is being unbound from the text control element.
89 * It is created alongside with the frame selection object which is stored in the
90 * mFrameSel member.
92 * * The editor text listener. This value is stored in the mTextListener member.
93 * Its job is to listen to selection and keyboard events, and act accordingly.
94 * It is created when an a frame is first bound to the control, and will be destroyed
95 * when the frame is unbound from the text control element.
97 * * The editor's cached value. This value is stored in the mCachedValue member.
98 * It is used to improve the performance of append operations to the text
99 * control. A mutation observer stored in the mMutationObserver has the job of
100 * invalidating this cache when the anonymous contect containing the value is
101 * changed.
103 * * The editor's cached selection properties. These vales are stored in the
104 * mSelectionProperties member, and include the selection's start, end and
105 * direction. They are only used when there is no frame available for the
106 * text field.
109 * As a general rule, nsTextEditorState objects own the value of the text control, and any
110 * attempt to retrieve or set the value must be made through those objects. Internally,
111 * the value can be represented in several different ways, based on the state the control is
112 * in.
114 * * When the control is first initialized, its value is equal to the default value of
115 * the DOM node. For <input> text controls, this default value is the value of the
116 * value attribute. For <textarea> elements, this default value is the value of the
117 * text node children of the element.
119 * * If the value has been changed through the DOM node (before the editor for the object
120 * is initialized), the value is stored as a simple string inside the mValue member of
121 * the nsTextEditorState object.
123 * * If an editor has been initialized for the control, the value is set and retrievd via
124 * the nsIPlaintextEditor interface, and is internally managed by the editor as the
125 * native anonymous content tree attached to the control's frame.
127 * * If the text editor state object is unbound from the control's frame, the value is
128 * transferred to the mValue member variable, and will be managed there until a new
129 * frame is bound to the text editor state object.
132 class RestoreSelectionState;
134 class nsTextEditorState : public mozilla::SupportsWeakPtr<nsTextEditorState> {
135 public:
136 MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsTextEditorState)
137 explicit nsTextEditorState(nsITextControlElement* aOwningElement);
138 static nsTextEditorState*
139 Construct(nsITextControlElement* aOwningElement,
140 nsTextEditorState** aReusedState);
141 ~nsTextEditorState();
143 void Traverse(nsCycleCollectionTraversalCallback& cb);
144 void Unlink();
146 void PrepareForReuse()
148 Unlink();
149 mValue.reset();
150 mValueBeingSet.Truncate();
151 mTextCtrlElement = nullptr;
154 mozilla::TextEditor* GetTextEditor();
155 nsISelectionController* GetSelectionController() const;
156 nsFrameSelection* GetConstFrameSelection();
157 nsresult BindToFrame(nsTextControlFrame* aFrame);
158 void UnbindFromFrame(nsTextControlFrame* aFrame);
159 nsresult PrepareEditor(const nsAString *aValue = nullptr);
160 void InitializeKeyboardEventListeners();
162 enum SetValueFlags
164 // The call is for internal processing.
165 eSetValue_Internal = 0,
166 // The value is changed by a call of setUserInput() from chrome.
167 eSetValue_BySetUserInput = 1 << 0,
168 // The value is changed by changing value attribute of the element or
169 // something like setRangeText().
170 eSetValue_ByContent = 1 << 1,
171 // Whether the value change should be notified to the frame/contet nor not.
172 eSetValue_Notify = 1 << 2,
173 // Whether to move the cursor to end of the value (in the case when we have
174 // cached selection offsets), in the case when the value has changed. If
175 // this is not set, the cached selection offsets will simply be clamped to
176 // be within the length of the new value. In either case, if the value has
177 // not changed the cursor won't move.
178 eSetValue_MoveCursorToEndIfValueChanged = 1 << 3,
179 // The value is changed for a XUL text control as opposed to for an HTML
180 // text control. Such value changes are different in that they preserve the
181 // undo history.
182 eSetValue_ForXUL = 1 << 4,
184 MOZ_MUST_USE bool SetValue(const nsAString& aValue,
185 const nsAString* aOldValue,
186 uint32_t aFlags);
187 MOZ_MUST_USE bool SetValue(const nsAString& aValue,
188 uint32_t aFlags)
190 return SetValue(aValue, nullptr, aFlags);
192 void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
193 bool HasNonEmptyValue();
194 // The following methods are for textarea element to use whether default
195 // value or not.
196 // XXX We might have to add assertion when it is into editable,
197 // or reconsider fixing bug 597525 to remove these.
198 void EmptyValue() { if (mValue) mValue->Truncate(); }
199 bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
201 mozilla::dom::Element* GetRootNode();
202 mozilla::dom::Element* GetPreviewNode();
204 bool IsSingleLineTextControl() const {
205 return mTextCtrlElement->IsSingleLineTextControl();
207 bool IsTextArea() const {
208 return mTextCtrlElement->IsTextArea();
210 bool IsPasswordTextControl() const {
211 return mTextCtrlElement->IsPasswordTextControl();
213 int32_t GetCols() {
214 return mTextCtrlElement->GetCols();
216 int32_t GetWrapCols() {
217 return mTextCtrlElement->GetWrapCols();
219 int32_t GetRows() {
220 return mTextCtrlElement->GetRows();
223 void UpdateOverlayTextVisibility(bool aNotify);
225 // placeholder methods
226 bool GetPlaceholderVisibility() {
227 return mPlaceholderVisibility;
230 // preview methods
231 void SetPreviewText(const nsAString& aValue, bool aNotify);
232 void GetPreviewText(nsAString& aValue);
233 bool GetPreviewVisibility() {
234 return mPreviewVisibility;
238 * Get the maxlength attribute
239 * @param aMaxLength the value of the max length attr
240 * @returns false if attr not defined
242 int32_t GetMaxLength();
244 void HideSelectionIfBlurred();
246 struct SelectionProperties {
247 public:
248 SelectionProperties() : mStart(0), mEnd(0),
249 mDirection(nsITextControlFrame::eForward) {}
250 bool IsDefault() const
252 return mStart == 0 && mEnd == 0 &&
253 mDirection == nsITextControlFrame::eForward;
255 uint32_t GetStart() const
257 return mStart;
259 void SetStart(uint32_t value)
261 mIsDirty = true;
262 mStart = value;
264 uint32_t GetEnd() const
266 return mEnd;
268 void SetEnd(uint32_t value)
270 mIsDirty = true;
271 mEnd = value;
273 nsITextControlFrame::SelectionDirection GetDirection() const
275 return mDirection;
277 void SetDirection(nsITextControlFrame::SelectionDirection value)
279 mIsDirty = true;
280 mDirection = value;
282 // return true only if mStart, mEnd, or mDirection have been modified,
283 // or if SetIsDirty() was explicitly called.
284 bool IsDirty() const
286 return mIsDirty;
288 void SetIsDirty()
290 mIsDirty = true;
292 private:
293 uint32_t mStart, mEnd;
294 bool mIsDirty = false;
295 nsITextControlFrame::SelectionDirection mDirection;
298 bool IsSelectionCached() const;
299 SelectionProperties& GetSelectionProperties();
300 void SetSelectionProperties(SelectionProperties& aProps);
301 void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
302 bool HasNeverInitializedBefore() const { return !mEverInited; }
303 // Sync up our selection properties with our editor prior to being destroyed.
304 // This will invoke UnbindFromFrame() to ensure that we grab whatever
305 // selection state may be at the moment.
306 void SyncUpSelectionPropertiesBeforeDestruction();
308 // Get the selection range start and end points in our text.
309 void GetSelectionRange(uint32_t* aSelectionStart, uint32_t* aSelectionEnd,
310 mozilla::ErrorResult& aRv);
312 // Get the selection direction
313 nsITextControlFrame::SelectionDirection
314 GetSelectionDirection(mozilla::ErrorResult& aRv);
316 // Set the selection range (start, end, direction). aEnd is allowed to be
317 // smaller than aStart; in that case aStart will be reset to the same value as
318 // aEnd. This basically implements
319 // https://html.spec.whatwg.org/multipage/forms.html#set-the-selection-range
320 // but with the start/end already coerced to zero if null (and without the
321 // special infinity value), and the direction already converted to a
322 // SelectionDirection.
324 // If we have a frame, this method will scroll the selection into view.
326 // XXXbz This should really take uint32_t, but none of our guts (either the
327 // frame or our cached selection state) work with uint32_t at the moment...
328 void SetSelectionRange(uint32_t aStart, uint32_t aEnd,
329 nsITextControlFrame::SelectionDirection aDirection,
330 mozilla::ErrorResult& aRv);
332 // Set the selection range, but with an optional string for the direction.
333 // This will convert aDirection to an nsITextControlFrame::SelectionDirection
334 // and then call our other SetSelectionRange overload.
335 void SetSelectionRange(uint32_t aSelectionStart,
336 uint32_t aSelectionEnd,
337 const mozilla::dom::Optional<nsAString>& aDirection,
338 mozilla::ErrorResult& aRv);
340 // Set the selection start. This basically implements the
341 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionstart
342 // setter.
343 void SetSelectionStart(const mozilla::dom::Nullable<uint32_t>& aStart,
344 mozilla::ErrorResult& aRv);
346 // Set the selection end. This basically implements the
347 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectionend
348 // setter.
349 void SetSelectionEnd(const mozilla::dom::Nullable<uint32_t>& aEnd,
350 mozilla::ErrorResult& aRv);
352 // Get the selection direction as a string. This implements the
353 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
354 // getter.
355 void GetSelectionDirectionString(nsAString& aDirection,
356 mozilla::ErrorResult& aRv);
358 // Set the selection direction. This basically implements the
359 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-selectiondirection
360 // setter.
361 void SetSelectionDirection(const nsAString& aDirection,
362 mozilla::ErrorResult& aRv);
364 // Set the range text. This basically implements
365 // https://html.spec.whatwg.org/multipage/forms.html#dom-textarea/input-setrangetext
366 void SetRangeText(const nsAString& aReplacement, mozilla::ErrorResult& aRv);
367 // The last two arguments are -1 if we don't know our selection range;
368 // otherwise they're the start and end of our selection range.
369 void SetRangeText(const nsAString& aReplacement, uint32_t aStart,
370 uint32_t aEnd, mozilla::dom::SelectionMode aSelectMode,
371 mozilla::ErrorResult& aRv,
372 const mozilla::Maybe<uint32_t>& aSelectionStart =
373 mozilla::Nothing(),
374 const mozilla::Maybe<uint32_t>& aSelectionEnd =
375 mozilla::Nothing());
377 void UpdateEditableState(bool aNotify) {
378 if (auto* root = GetRootNode()) {
379 root->UpdateEditableState(aNotify);
383 private:
384 friend class RestoreSelectionState;
386 // not copy constructible
387 nsTextEditorState(const nsTextEditorState&);
388 // not assignable
389 void operator= (const nsTextEditorState&);
391 void ValueWasChanged(bool aNotify);
393 void DestroyEditor();
394 void Clear();
396 nsresult InitializeRootNode();
398 void FinishedRestoringSelection();
400 mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
402 bool EditorHasComposition();
404 class InitializationGuard {
405 public:
406 explicit InitializationGuard(nsTextEditorState& aState) :
407 mState(aState),
408 mGuardSet(false)
410 if (!mState.mInitializing) {
411 mGuardSet = true;
412 mState.mInitializing = true;
415 ~InitializationGuard() {
416 if (mGuardSet) {
417 mState.mInitializing = false;
420 bool IsInitializingRecursively() const {
421 return !mGuardSet;
423 private:
424 nsTextEditorState& mState;
425 bool mGuardSet;
427 friend class InitializationGuard;
428 friend class PrepareEditorEvent;
430 // The text control element owns this object, and ensures that this object
431 // has a smaller lifetime.
432 nsITextControlElement* MOZ_NON_OWNING_REF mTextCtrlElement;
433 RefPtr<nsTextInputSelectionImpl> mSelCon;
434 RefPtr<RestoreSelectionState> mRestoringSelection;
435 RefPtr<mozilla::TextEditor> mTextEditor;
436 nsTextControlFrame* mBoundFrame;
437 RefPtr<mozilla::TextInputListener> mTextListener;
438 mozilla::Maybe<nsString> mValue;
439 // mValueBeingSet is available only while SetValue() is requesting to commit
440 // composition. I.e., this is valid only while mIsCommittingComposition is
441 // true. While active composition is being committed, GetValue() needs
442 // the latest value which is set by SetValue(). So, this is cache for that.
443 nsString mValueBeingSet;
444 SelectionProperties mSelectionProperties;
445 bool mEverInited; // Have we ever been initialized?
446 bool mEditorInitialized;
447 bool mInitializing; // Whether we're in the process of initialization
448 bool mValueTransferInProgress; // Whether a value is being transferred to the frame
449 bool mSelectionCached; // Whether mSelectionProperties is valid
450 mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
451 bool mPlaceholderVisibility;
452 bool mPreviewVisibility;
453 bool mIsCommittingComposition;
456 inline void
457 ImplCycleCollectionUnlink(nsTextEditorState& aField)
459 aField.Unlink();
462 inline void
463 ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
464 nsTextEditorState& aField,
465 const char* aName,
466 uint32_t aFlags = 0)
468 aField.Traverse(aCallback);
471 #endif