summaryrefslogtreecommitdiffstats
path: root/layout/base/AccessibleCaret.h
blob: 979a5286399764b4291fd5918983efa59598444c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/* -*- 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 AccessibleCaret_h__
#define AccessibleCaret_h__

#include "mozilla/Attributes.h"
#include "mozilla/dom/AnonymousContent.h"
#include "nsCOMPtr.h"
#include "nsIDOMEventListener.h"
#include "nsIFrame.h"  // for WeakFrame only
#include "nsISupports.h"
#include "nsISupportsImpl.h"
#include "nsLiteralString.h"
#include "nsRect.h"
#include "mozilla/RefPtr.h"
#include "nsString.h"

class nsIFrame;
struct nsPoint;

namespace mozilla {
class PresShell;
namespace dom {
class Element;
class Event;
}  // namespace dom

// -----------------------------------------------------------------------------
// Upon the creation of AccessibleCaret, it will insert DOM Element as an
// anonymous content containing the caret image. The caret appearance and
// position can be controlled by SetAppearance() and SetPosition().
//
// All the rect or point are relative to root frame except being specified
// explicitly.
//
// None of the methods in AccessibleCaret will flush layout or style. To ensure
// that SetPosition() works correctly, the caller must make sure the layout is
// up to date.
//
// Please see the wiki page for more information.
// https://wiki.mozilla.org/AccessibleCaret
//
class AccessibleCaret {
 public:
  explicit AccessibleCaret(PresShell* aPresShell);
  virtual ~AccessibleCaret();

  // This enumeration representing the visibility and visual style of an
  // AccessibleCaret.
  //
  // Use SetAppearance() to change the appearance, and use GetAppearance() to
  // get the current appearance.
  enum class Appearance : uint8_t {
    // Do not display the caret at all.
    None,

    // Display the caret in default style.
    Normal,

    // The caret should be displayed logically but it is kept invisible to the
    // user. This enum is the only difference between "logically visible" and
    // "visually visible". It can be used for reasons such as:
    // 1. Out of scroll port.
    // 2. For UX requirement such as hide a caret in an empty text area.
    NormalNotShown,

    // Display the caret which is tilted to the left.
    Left,

    // Display the caret which is tilted to the right.
    Right
  };

  friend std::ostream& operator<<(std::ostream& aStream,
                                  const Appearance& aAppearance);

  Appearance GetAppearance() const { return mAppearance; }

  virtual void SetAppearance(Appearance aAppearance);

  // Return true if current appearance is either Normal, NormalNotShown, Left,
  // or Right.
  bool IsLogicallyVisible() const { return mAppearance != Appearance::None; }

  // Return true if current appearance is either Normal, Left, or Right.
  bool IsVisuallyVisible() const {
    return (mAppearance != Appearance::None) &&
           (mAppearance != Appearance::NormalNotShown);
  }

  // This enum represents the result returned by SetPosition().
  enum class PositionChangedResult : uint8_t {
    // Both position and the zoom level are not changed.
    NotChanged,

    // The position is changed. (The zoom level may or may not be changed.)
    Position,

    // Only the zoom level is changed. The position is *not* changed.
    Zoom,

    // The position is out of scroll port.
    Invisible
  };

  friend std::ostream& operator<<(std::ostream& aStream,
                                  const PositionChangedResult& aResult);

  virtual PositionChangedResult SetPosition(nsIFrame* aFrame, int32_t aOffset);

  // Does two AccessibleCarets overlap?
  bool Intersects(const AccessibleCaret& aCaret) const;

  // Is the point within the caret's rect? The point should be relative to root
  // frame.
  enum class TouchArea {
    Full,  // Contains both text overlay and caret image.
    CaretImage
  };
  bool Contains(const nsPoint& aPoint, TouchArea aTouchArea) const;

  // The geometry center of the imaginary caret (nsCaret) to which this
  // AccessibleCaret is attached. It is needed when dragging the caret.
  nsPoint LogicalPosition() const { return mImaginaryCaretRect.Center(); }

  // Element for 'Intersects' test. This is the container of the caret image
  // and text-overlay elements. See CreateCaretElement() for the content
  // structure.
  dom::Element& CaretElement() const {
    return mCaretElementHolder->ContentNode();
  }

  // Ensures that the caret element is made "APZ aware" so that the APZ code
  // doesn't scroll the page when the user is trying to drag the caret.
  void EnsureApzAware();

  bool IsInPositionFixedSubtree() const;

 protected:
  // Argument aRect should be relative to CustomContentContainerFrame().
  void SetCaretElementStyle(const nsRect& aRect, float aZoomLevel);
  void SetTextOverlayElementStyle(const nsRect& aRect, float aZoomLevel);
  void SetCaretImageElementStyle(const nsRect& aRect, float aZoomLevel);

  // Get current zoom level.
  float GetZoomLevel();

  // Element which contains the text overly for the 'Contains' test.
  dom::Element* TextOverlayElement() const {
    return mCaretElementHolder->GetElementById(sTextOverlayElementId);
  }

  // Element which contains the caret image for 'Contains' test.
  dom::Element* CaretImageElement() const {
    return mCaretElementHolder->GetElementById(sCaretImageElementId);
  }

  nsIFrame* RootFrame() const;

  nsIFrame* CustomContentContainerFrame() const;

  // Transform Appearance to CSS id used in ua.css.
  static nsAutoString AppearanceString(Appearance aAppearance);

  already_AddRefed<dom::Element> CreateCaretElement(dom::Document*) const;

  // Inject caret element into custom content container.
  void InjectCaretElement(dom::Document*);

  // Remove caret element from custom content container.
  void RemoveCaretElement(dom::Document*);

  // Clear the cached rects and zoom level.
  void ClearCachedData();

  // The top-center of the imaginary caret to which this AccessibleCaret is
  // attached.
  static nsPoint CaretElementPosition(const nsRect& aRect) {
    return aRect.TopLeft() + nsPoint(aRect.width / 2, 0);
  }

  class DummyTouchListener final : public nsIDOMEventListener {
   public:
    NS_DECL_ISUPPORTS
    NS_IMETHOD HandleEvent(mozilla::dom::Event* aEvent) override {
      return NS_OK;
    }

   private:
    virtual ~DummyTouchListener() = default;
  };

  // Member variables
  Appearance mAppearance = Appearance::None;

  // AccessibleCaretManager owns us by a UniquePtr. When it's terminated by
  // AccessibleCaretEventHub::Terminate() which is called in
  // PresShell::Destroy(), it frees us automatically. No need to worry if we
  // outlive mPresShell.
  PresShell* const MOZ_NON_OWNING_REF mPresShell = nullptr;

  RefPtr<dom::AnonymousContent> mCaretElementHolder;

  // This cached rect is relative to the root frame, and is used in
  // LogicalPosition() when dragging a caret.
  nsRect mImaginaryCaretRect;

  // This cached rect is relative to the custom content container, and is used
  // in SetPosition() to check whether the caret position has changed.
  nsRect mImaginaryCaretRectInContainerFrame;

  // The reference frame we used to calculate mImaginaryCaretRect and
  // mImaginaryCaretRectInContainerFrame.
  WeakFrame mImaginaryCaretReferenceFrame;

  // Cache current zoom level to determine whether position is changed.
  float mZoomLevel = 0.0f;

  // A no-op touch-start listener which prevents APZ from panning when dragging
  // the caret.
  RefPtr<DummyTouchListener> mDummyTouchListener{new DummyTouchListener()};

  // Static class variables
  static const nsLiteralString sTextOverlayElementId;
  static const nsLiteralString sCaretImageElementId;

};  // class AccessibleCaret

std::ostream& operator<<(std::ostream& aStream,
                         const AccessibleCaret::Appearance& aAppearance);

std::ostream& operator<<(std::ostream& aStream,
                         const AccessibleCaret::PositionChangedResult& aResult);

}  // namespace mozilla

#endif  // AccessibleCaret_h__