summaryrefslogtreecommitdiffstats
path: root/layout/base/AccessibleCaret.h
blob: 42e5bb38643e7f3822b8a03431edffd5223b5acb (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
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
/* -*- 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 "mozilla/dom/Element.h"
#include "nsCOMPtr.h"
#include "nsIDOMEventListener.h"
#include "nsISupportsBase.h"
#include "nsISupportsImpl.h"
#include "nsLiteralString.h"
#include "nsRect.h"
#include "mozilla/RefPtr.h"
#include "nsString.h"

class nsIDocument;
class nsIFrame;
class nsIPresShell;
struct nsPoint;

namespace mozilla {

// -----------------------------------------------------------------------------
// 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(nsIPresShell* 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);
  }

  // Set true to enable the "Text Selection Bar" described in "Text Selection
  // Visual Spec" in bug 921965.
  virtual void SetSelectionBarEnabled(bool aEnabled);

  // This enumeration representing the result returned by SetPosition().
  enum class PositionChangedResult : uint8_t {
    // Position is not changed.
    NotChanged,

    // Position or zoom level is changed.
    Changed,

    // 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. Container of image and bar elements.
  dom::Element* CaretElement() const
  {
    return mCaretElementHolder->GetContentNode();
  }

  // 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();

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);
  void SetSelectionBarElementStyle(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);
  }

  // Element which represents the text selection bar.
  dom::Element* SelectionBarElement() const
  {
    return mCaretElementHolder->GetElementById(sSelectionBarElementId);
  }

  nsIFrame* RootFrame() const
  {
    return mPresShell->GetRootFrame();
  }

  nsIFrame* CustomContentContainerFrame() const;

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

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

  // Inject caret element into custom content container.
  void InjectCaretElement(nsIDocument* aDocument);

  // Remove caret element from custom content container.
  void RemoveCaretElement(nsIDocument* aDocument);

  // 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(nsIDOMEvent* aEvent) override
    {
      return NS_OK;
    }

  private:
    virtual ~DummyTouchListener() {};
  };

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

  bool mSelectionBarEnabled = false;

  // 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.
  nsIPresShell* MOZ_NON_OWNING_REF const mPresShell = nullptr;

  RefPtr<dom::AnonymousContent> mCaretElementHolder;

  // mImaginaryCaretRect is relative to root frame.
  nsRect mImaginaryCaretRect;

  // 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 float sWidth;
  static float sHeight;
  static float sMarginLeft;
  static float sBarWidth;
  static const nsLiteralString sTextOverlayElementId;
  static const nsLiteralString sCaretImageElementId;
  static const nsLiteralString sSelectionBarElementId;

}; // 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__