summaryrefslogtreecommitdiffstats
path: root/dom/html/nsTextEditorState.h
diff options
context:
space:
mode:
Diffstat (limited to 'dom/html/nsTextEditorState.h')
-rw-r--r--dom/html/nsTextEditorState.h364
1 files changed, 364 insertions, 0 deletions
diff --git a/dom/html/nsTextEditorState.h b/dom/html/nsTextEditorState.h
new file mode 100644
index 000000000..11494f155
--- /dev/null
+++ b/dom/html/nsTextEditorState.h
@@ -0,0 +1,364 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef nsTextEditorState_h__
+#define nsTextEditorState_h__
+
+#include "nsString.h"
+#include "nsITextControlElement.h"
+#include "nsITextControlFrame.h"
+#include "nsCycleCollectionParticipant.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/Attributes.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/WeakPtr.h"
+
+class nsTextInputListener;
+class nsTextControlFrame;
+class nsTextInputSelectionImpl;
+class nsAnonDivObserver;
+class nsISelectionController;
+class nsFrameSelection;
+class nsIEditor;
+class nsITextControlElement;
+class nsFrame;
+
+namespace mozilla {
+namespace dom {
+class HTMLInputElement;
+} // namespace dom
+} // namespace mozilla
+
+/**
+ * nsTextEditorState is a class which is responsible for managing the state of
+ * plaintext controls. This currently includes the following HTML elements:
+ * <input type=text>
+ * <input type=password>
+ * <textarea>
+ * and also XUL controls such as <textbox> which use one of these elements behind
+ * the scenes.
+ *
+ * This class is held as a member of HTMLInputElement and nsHTMLTextAreaElement.
+ * The public functions in this class include the public APIs which content/ uses.
+ * Layout code uses the nsITextControlElement interface to invoke functions on this
+ * class.
+ *
+ * The design motivation behind this class is maintaining all of the things which
+ * collectively are considered the "state" of the text control in a single location.
+ * This state includes several things:
+ *
+ * * The control's value. This value is stored in the mValue member, and is only
+ * used when there is no frame for the control, or when the editor object has
+ * not been initialized yet.
+ *
+ * * The control's associated frame. This value is stored in the mBoundFrame member.
+ * A text control might never have an associated frame during its life cycle,
+ * or might have several different ones, but at any given moment in time there is
+ * a maximum of 1 bound frame to each text control.
+ *
+ * * The control's associated editor. This value is stored in the mEditor member.
+ * An editor is initilized for the control only when necessary (that is, when either
+ * the user is about to interact with the text control, or when some other code
+ * needs to access the editor object. Without a frame bound to the control, an
+ * editor is never initialzied. Once initialized, the editor might outlive the frame,
+ * in which case the same editor will be used if a new frame gets bound to the
+ * text control.
+ *
+ * * The anonymous content associated with the text control's frame, including the
+ * value div (the DIV element responsible for holding the value of the text control)
+ * and the placeholder div (the DIV element responsible for holding the placeholder
+ * value of the text control.) These values are stored in the mRootNode and
+ * mPlaceholderDiv members, respectively. They will be created when a
+ * frame is bound to the text control. They will be destroyed when the frame is
+ * unbound from the object. We could try and hold on to the anonymous content
+ * between different frames, but unfortunately that is not currently possible
+ * because they are not unbound from the document in time.
+ *
+ * * The frame selection controller. This value is stored in the mSelCon member.
+ * The frame selection controller is responsible for maintaining the selection state
+ * on a frame. It is created when a frame is bound to the text control element,
+ * and will be destroy when the frame is being unbound from the text control element.
+ * It is created alongside with the frame selection object which is stored in the
+ * mFrameSel member.
+ *
+ * * The editor text listener. This value is stored in the mTextListener member.
+ * Its job is to listen to selection and keyboard events, and act accordingly.
+ * It is created when an a frame is first bound to the control, and will be destroyed
+ * when the frame is unbound from the text control element.
+ *
+ * * The editor's cached value. This value is stored in the mCachedValue member.
+ * It is used to improve the performance of append operations to the text
+ * control. A mutation observer stored in the mMutationObserver has the job of
+ * invalidating this cache when the anonymous contect containing the value is
+ * changed.
+ *
+ * * The editor's cached selection properties. These vales are stored in the
+ * mSelectionProperties member, and include the selection's start, end and
+ * direction. They are only used when there is no frame available for the
+ * text field.
+ *
+ *
+ * As a general rule, nsTextEditorState objects own the value of the text control, and any
+ * attempt to retrieve or set the value must be made through those objects. Internally,
+ * the value can be represented in several different ways, based on the state the control is
+ * in.
+ *
+ * * When the control is first initialized, its value is equal to the default value of
+ * the DOM node. For <input> text controls, this default value is the value of the
+ * value attribute. For <textarea> elements, this default value is the value of the
+ * text node children of the element.
+ *
+ * * If the value has been changed through the DOM node (before the editor for the object
+ * is initialized), the value is stored as a simple string inside the mValue member of
+ * the nsTextEditorState object.
+ *
+ * * If an editor has been initialized for the control, the value is set and retrievd via
+ * the nsIPlaintextEditor interface, and is internally managed by the editor as the
+ * native anonymous content tree attached to the control's frame.
+ *
+ * * If the text editor state object is unbound from the control's frame, the value is
+ * transferred to the mValue member variable, and will be managed there until a new
+ * frame is bound to the text editor state object.
+ */
+
+class RestoreSelectionState;
+
+class nsTextEditorState : public mozilla::SupportsWeakPtr<nsTextEditorState> {
+public:
+ MOZ_DECLARE_WEAKREFERENCE_TYPENAME(nsTextEditorState)
+ explicit nsTextEditorState(nsITextControlElement* aOwningElement);
+ ~nsTextEditorState();
+
+ void Traverse(nsCycleCollectionTraversalCallback& cb);
+ void Unlink();
+
+ nsIEditor* GetEditor();
+ nsISelectionController* GetSelectionController() const;
+ nsFrameSelection* GetConstFrameSelection();
+ nsresult BindToFrame(nsTextControlFrame* aFrame);
+ void UnbindFromFrame(nsTextControlFrame* aFrame);
+ nsresult PrepareEditor(const nsAString *aValue = nullptr);
+ void InitializeKeyboardEventListeners();
+
+ enum SetValueFlags
+ {
+ // The call is for internal processing.
+ eSetValue_Internal = 0,
+ // The value is changed by a call of setUserInput() from chrome.
+ eSetValue_BySetUserInput = 1 << 0,
+ // The value is changed by changing value attribute of the element or
+ // something like setRangeText().
+ eSetValue_ByContent = 1 << 1,
+ // Whether the value change should be notified to the frame/contet nor not.
+ eSetValue_Notify = 1 << 2
+ };
+ MOZ_MUST_USE bool SetValue(const nsAString& aValue, uint32_t aFlags);
+ void GetValue(nsAString& aValue, bool aIgnoreWrap) const;
+ void EmptyValue() { if (mValue) mValue->Truncate(); }
+ bool IsEmpty() const { return mValue ? mValue->IsEmpty() : true; }
+
+ nsresult CreatePlaceholderNode();
+
+ mozilla::dom::Element* GetRootNode() {
+ return mRootNode;
+ }
+ mozilla::dom::Element* GetPlaceholderNode() {
+ return mPlaceholderDiv;
+ }
+
+ bool IsSingleLineTextControl() const {
+ return mTextCtrlElement->IsSingleLineTextControl();
+ }
+ bool IsTextArea() const {
+ return mTextCtrlElement->IsTextArea();
+ }
+ bool IsPlainTextControl() const {
+ return mTextCtrlElement->IsPlainTextControl();
+ }
+ bool IsPasswordTextControl() const {
+ return mTextCtrlElement->IsPasswordTextControl();
+ }
+ int32_t GetCols() {
+ return mTextCtrlElement->GetCols();
+ }
+ int32_t GetWrapCols() {
+ return mTextCtrlElement->GetWrapCols();
+ }
+ int32_t GetRows() {
+ return mTextCtrlElement->GetRows();
+ }
+
+ // placeholder methods
+ void UpdatePlaceholderVisibility(bool aNotify);
+ bool GetPlaceholderVisibility() {
+ return mPlaceholderVisibility;
+ }
+ void UpdatePlaceholderText(bool aNotify);
+
+ /**
+ * Get the maxlength attribute
+ * @param aMaxLength the value of the max length attr
+ * @returns false if attr not defined
+ */
+ bool GetMaxLength(int32_t* aMaxLength);
+
+ void ClearValueCache() { mCachedValue.Truncate(); }
+
+ void HideSelectionIfBlurred();
+
+ struct SelectionProperties {
+ public:
+ SelectionProperties() : mStart(0), mEnd(0),
+ mDirection(nsITextControlFrame::eForward) {}
+ bool IsDefault() const
+ {
+ return mStart == 0 && mEnd == 0 &&
+ mDirection == nsITextControlFrame::eForward;
+ }
+ int32_t GetStart() const
+ {
+ return mStart;
+ }
+ void SetStart(int32_t value)
+ {
+ mIsDirty = true;
+ mStart = value;
+ }
+ int32_t GetEnd() const
+ {
+ return mEnd;
+ }
+ void SetEnd(int32_t value)
+ {
+ mIsDirty = true;
+ mEnd = value;
+ }
+ nsITextControlFrame::SelectionDirection GetDirection() const
+ {
+ return mDirection;
+ }
+ void SetDirection(nsITextControlFrame::SelectionDirection value)
+ {
+ mIsDirty = true;
+ mDirection = value;
+ }
+ // return true only if mStart, mEnd, or mDirection have been modified
+ bool IsDirty() const
+ {
+ return mIsDirty;
+ }
+ private:
+ int32_t mStart, mEnd;
+ bool mIsDirty = false;
+ nsITextControlFrame::SelectionDirection mDirection;
+ };
+
+ bool IsSelectionCached() const;
+ SelectionProperties& GetSelectionProperties();
+ void SetSelectionProperties(SelectionProperties& aProps);
+ void WillInitEagerly() { mSelectionRestoreEagerInit = true; }
+ bool HasNeverInitializedBefore() const { return !mEverInited; }
+
+ void UpdateEditableState(bool aNotify) {
+ if (mRootNode) {
+ mRootNode->UpdateEditableState(aNotify);
+ }
+ }
+
+private:
+ friend class RestoreSelectionState;
+
+ // not copy constructible
+ nsTextEditorState(const nsTextEditorState&);
+ // not assignable
+ void operator= (const nsTextEditorState&);
+
+ nsresult CreateRootNode();
+
+ void ValueWasChanged(bool aNotify);
+
+ void DestroyEditor();
+ void Clear();
+
+ nsresult InitializeRootNode();
+
+ void FinishedRestoringSelection();
+
+ mozilla::dom::HTMLInputElement* GetParentNumberControl(nsFrame* aFrame) const;
+
+ bool EditorHasComposition();
+
+ class InitializationGuard {
+ public:
+ explicit InitializationGuard(nsTextEditorState& aState) :
+ mState(aState),
+ mGuardSet(false)
+ {
+ if (!mState.mInitializing) {
+ mGuardSet = true;
+ mState.mInitializing = true;
+ }
+ }
+ ~InitializationGuard() {
+ if (mGuardSet) {
+ mState.mInitializing = false;
+ }
+ }
+ bool IsInitializingRecursively() const {
+ return !mGuardSet;
+ }
+ private:
+ nsTextEditorState& mState;
+ bool mGuardSet;
+ };
+ friend class InitializationGuard;
+ friend class PrepareEditorEvent;
+
+ // The text control element owns this object, and ensures that this object
+ // has a smaller lifetime.
+ nsITextControlElement* const MOZ_NON_OWNING_REF mTextCtrlElement;
+ RefPtr<nsTextInputSelectionImpl> mSelCon;
+ RefPtr<RestoreSelectionState> mRestoringSelection;
+ nsCOMPtr<nsIEditor> mEditor;
+ nsCOMPtr<mozilla::dom::Element> mRootNode;
+ nsCOMPtr<mozilla::dom::Element> mPlaceholderDiv;
+ nsTextControlFrame* mBoundFrame;
+ RefPtr<nsTextInputListener> mTextListener;
+ mozilla::Maybe<nsString> mValue;
+ RefPtr<nsAnonDivObserver> mMutationObserver;
+ mutable nsString mCachedValue; // Caches non-hard-wrapped value on a multiline control.
+ // mValueBeingSet is available only while SetValue() is requesting to commit
+ // composition. I.e., this is valid only while mIsCommittingComposition is
+ // true. While active composition is being committed, GetValue() needs
+ // the latest value which is set by SetValue(). So, this is cache for that.
+ nsString mValueBeingSet;
+ SelectionProperties mSelectionProperties;
+ bool mEverInited; // Have we ever been initialized?
+ bool mEditorInitialized;
+ bool mInitializing; // Whether we're in the process of initialization
+ bool mValueTransferInProgress; // Whether a value is being transferred to the frame
+ bool mSelectionCached; // Whether mSelectionProperties is valid
+ mutable bool mSelectionRestoreEagerInit; // Whether we're eager initing because of selection restore
+ bool mPlaceholderVisibility;
+ bool mIsCommittingComposition;
+};
+
+inline void
+ImplCycleCollectionUnlink(nsTextEditorState& aField)
+{
+ aField.Unlink();
+}
+
+inline void
+ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
+ nsTextEditorState& aField,
+ const char* aName,
+ uint32_t aFlags = 0)
+{
+ aField.Traverse(aCallback);
+}
+
+#endif