Coverage Report

Created: 2018-09-25 14:53

/work/obj-fuzz/dist/include/nsTextEditorState.h
Line
Count
Source (jump to first uncovered line)
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
#ifndef nsTextEditorState_h__
8
#define nsTextEditorState_h__
9
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"
21
22
class nsTextControlFrame;
23
class nsTextInputSelectionImpl;
24
class nsAnonDivObserver;
25
class nsISelectionController;
26
class nsFrameSelection;
27
class nsITextControlElement;
28
class nsFrame;
29
30
namespace mozilla {
31
32
class ErrorResult;
33
class TextInputListener;
34
35
namespace dom {
36
class HTMLInputElement;
37
} // namespace dom
38
} // namespace mozilla
39
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.
48
 *
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.
53
 *
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:
57
 *
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.
61
 *
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.
66
 *
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.
74
 *
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.
84
 *
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.
91
 *
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.
96
 *
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.
102
 *
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.
107
 *
108
 *
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.
113
 *
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.
118
 *
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.
122
 *
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.
126
 *
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.
130
 */
131
132
class RestoreSelectionState;
133
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();
142
143
  void Traverse(nsCycleCollectionTraversalCallback& cb);
144
  void Unlink();
145
146
  void PrepareForReuse()
147
  {
148
    Unlink();
149
    mValue.reset();
150
    mValueBeingSet.Truncate();
151
    mTextCtrlElement = nullptr;
152
  }
153
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();
161
162
  enum SetValueFlags
163
  {
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,
183
  };
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)
189
  {
190
    return SetValue(aValue, nullptr, aFlags);
191
  }
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; }
200
201
  mozilla::dom::Element* GetRootNode();
202
  mozilla::dom::Element* GetPreviewNode();
203
204
  bool IsSingleLineTextControl() const {
205
    return mTextCtrlElement->IsSingleLineTextControl();
206
  }
207
0
  bool IsTextArea() const {
208
0
    return mTextCtrlElement->IsTextArea();
209
0
  }
210
  bool IsPasswordTextControl() const {
211
    return mTextCtrlElement->IsPasswordTextControl();
212
  }
213
0
  int32_t GetCols() {
214
0
    return mTextCtrlElement->GetCols();
215
0
  }
216
  int32_t GetWrapCols() {
217
    return mTextCtrlElement->GetWrapCols();
218
  }
219
0
  int32_t GetRows() {
220
0
    return mTextCtrlElement->GetRows();
221
0
  }
222
223
  void UpdateOverlayTextVisibility(bool aNotify);
224
225
  // placeholder methods
226
  bool GetPlaceholderVisibility() {
227
    return mPlaceholderVisibility;
228
  }
229
230
  // preview methods
231
  void SetPreviewText(const nsAString& aValue, bool aNotify);
232
  void GetPreviewText(nsAString& aValue);
233
  bool GetPreviewVisibility() {
234
    return mPreviewVisibility;
235
  }
236
237
  /**
238
   * Get the maxlength attribute
239
   * @param aMaxLength the value of the max length attr
240
   * @returns false if attr not defined
241
   */
242
  int32_t GetMaxLength();
243
244
  void HideSelectionIfBlurred();
245
246
  struct SelectionProperties {
247
    public:
248
      SelectionProperties() : mStart(0), mEnd(0),
249
        mDirection(nsITextControlFrame::eForward) {}
250
      bool IsDefault() const
251
      {
252
        return mStart == 0 && mEnd == 0 &&
253
               mDirection == nsITextControlFrame::eForward;
254
      }
255
      uint32_t GetStart() const
256
      {
257
        return mStart;
258
      }
259
      void SetStart(uint32_t value)
260
      {
261
        mIsDirty = true;
262
        mStart = value;
263
      }
264
      uint32_t GetEnd() const
265
      {
266
        return mEnd;
267
      }
268
      void SetEnd(uint32_t value)
269
      {
270
        mIsDirty = true;
271
        mEnd = value;
272
      }
273
      nsITextControlFrame::SelectionDirection GetDirection() const
274
      {
275
        return mDirection;
276
      }
277
      void SetDirection(nsITextControlFrame::SelectionDirection value)
278
      {
279
        mIsDirty = true;
280
        mDirection = value;
281
      }
282
      // return true only if mStart, mEnd, or mDirection have been modified,
283
      // or if SetIsDirty() was explicitly called.
284
      bool IsDirty() const
285
      {
286
        return mIsDirty;
287
      }
288
      void SetIsDirty()
289
      {
290
        mIsDirty = true;
291
      }
292
    private:
293
      uint32_t mStart, mEnd;
294
      bool mIsDirty = false;
295
      nsITextControlFrame::SelectionDirection mDirection;
296
  };
297
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();
307
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);
311
312
  // Get the selection direction
313
  nsITextControlFrame::SelectionDirection
314
    GetSelectionDirection(mozilla::ErrorResult& aRv);
315
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.
323
  //
324
  // If we have a frame, this method will scroll the selection into view.
325
  //
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);
331
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);
339
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);
345
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);
351
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);
357
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);
363
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());
376
377
0
  void UpdateEditableState(bool aNotify) {
378
0
    if (auto* root = GetRootNode()) {
379
0
      root->UpdateEditableState(aNotify);
380
0
    }
381
0
  }
382
383
private:
384
  friend class RestoreSelectionState;
385
386
  // not copy constructible
387
  nsTextEditorState(const nsTextEditorState&);
388
  // not assignable
389
  void operator= (const nsTextEditorState&);
390
391
  void ValueWasChanged(bool aNotify);
392
393
  void DestroyEditor();
394
  void Clear();
395
396
  nsresult InitializeRootNode();
397
398
  void FinishedRestoringSelection();
399
400
  mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
401
402
  bool EditorHasComposition();
403
404
  class InitializationGuard {
405
  public:
406
    explicit InitializationGuard(nsTextEditorState& aState) :
407
      mState(aState),
408
      mGuardSet(false)
409
    {
410
      if (!mState.mInitializing) {
411
        mGuardSet = true;
412
        mState.mInitializing = true;
413
      }
414
    }
415
    ~InitializationGuard() {
416
      if (mGuardSet) {
417
        mState.mInitializing = false;
418
      }
419
    }
420
    bool IsInitializingRecursively() const {
421
      return !mGuardSet;
422
    }
423
  private:
424
    nsTextEditorState& mState;
425
    bool mGuardSet;
426
  };
427
  friend class InitializationGuard;
428
  friend class PrepareEditorEvent;
429
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;
454
};
455
456
inline void
457
ImplCycleCollectionUnlink(nsTextEditorState& aField)
458
{
459
  aField.Unlink();
460
}
461
462
inline void
463
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
464
                            nsTextEditorState& aField,
465
                            const char* aName,
466
                            uint32_t aFlags = 0)
467
{
468
  aField.Traverse(aCallback);
469
}
470
471
#endif