Line data Source code
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 AccessibleCaret_h__
8 : #define AccessibleCaret_h__
9 :
10 : #include "mozilla/Attributes.h"
11 : #include "mozilla/dom/AnonymousContent.h"
12 : #include "mozilla/dom/Element.h"
13 : #include "nsCOMPtr.h"
14 : #include "nsIDOMEventListener.h"
15 : #include "nsISupportsBase.h"
16 : #include "nsISupportsImpl.h"
17 : #include "nsLiteralString.h"
18 : #include "nsRect.h"
19 : #include "mozilla/RefPtr.h"
20 : #include "nsString.h"
21 :
22 : class nsIDocument;
23 : class nsIFrame;
24 : class nsIPresShell;
25 : struct nsPoint;
26 :
27 : namespace mozilla {
28 :
29 : // -----------------------------------------------------------------------------
30 : // Upon the creation of AccessibleCaret, it will insert DOM Element as an
31 : // anonymous content containing the caret image. The caret appearance and
32 : // position can be controlled by SetAppearance() and SetPosition().
33 : //
34 : // All the rect or point are relative to root frame except being specified
35 : // explicitly.
36 : //
37 : // None of the methods in AccessibleCaret will flush layout or style. To ensure
38 : // that SetPosition() works correctly, the caller must make sure the layout is
39 : // up to date.
40 : //
41 : // Please see the wiki page for more information.
42 : // https://wiki.mozilla.org/AccessibleCaret
43 : //
44 : class AccessibleCaret
45 : {
46 : public:
47 : explicit AccessibleCaret(nsIPresShell* aPresShell);
48 : virtual ~AccessibleCaret();
49 :
50 : // This enumeration representing the visibility and visual style of an
51 : // AccessibleCaret.
52 : //
53 : // Use SetAppearance() to change the appearance, and use GetAppearance() to
54 : // get the current appearance.
55 : enum class Appearance : uint8_t {
56 : // Do not display the caret at all.
57 : None,
58 :
59 : // Display the caret in default style.
60 : Normal,
61 :
62 : // The caret should be displayed logically but it is kept invisible to the
63 : // user. This enum is the only difference between "logically visible" and
64 : // "visually visible". It can be used for reasons such as:
65 : // 1. Out of scroll port.
66 : // 2. For UX requirement such as hide a caret in an empty text area.
67 : NormalNotShown,
68 :
69 : // Display the caret which is tilted to the left.
70 : Left,
71 :
72 : // Display the caret which is tilted to the right.
73 : Right
74 : };
75 :
76 : friend std::ostream& operator<<(std::ostream& aStream,
77 : const Appearance& aAppearance);
78 :
79 0 : Appearance GetAppearance() const
80 : {
81 0 : return mAppearance;
82 : }
83 :
84 : virtual void SetAppearance(Appearance aAppearance);
85 :
86 : // Return true if current appearance is either Normal, NormalNotShown, Left,
87 : // or Right.
88 0 : bool IsLogicallyVisible() const
89 : {
90 0 : return mAppearance != Appearance::None;
91 : }
92 :
93 : // Return true if current appearance is either Normal, Left, or Right.
94 0 : bool IsVisuallyVisible() const
95 : {
96 0 : return (mAppearance != Appearance::None) &&
97 0 : (mAppearance != Appearance::NormalNotShown);
98 : }
99 :
100 : // Set true to enable the "Text Selection Bar" described in "Text Selection
101 : // Visual Spec" in bug 921965.
102 : virtual void SetSelectionBarEnabled(bool aEnabled);
103 :
104 : // This enumeration representing the result returned by SetPosition().
105 : enum class PositionChangedResult : uint8_t {
106 : // Position is not changed.
107 : NotChanged,
108 :
109 : // Position or zoom level is changed.
110 : Changed,
111 :
112 : // Position is out of scroll port.
113 : Invisible
114 : };
115 :
116 : friend std::ostream& operator<<(std::ostream& aStream,
117 : const PositionChangedResult& aResult);
118 :
119 : virtual PositionChangedResult SetPosition(nsIFrame* aFrame, int32_t aOffset);
120 :
121 : // Does two AccessibleCarets overlap?
122 : bool Intersects(const AccessibleCaret& aCaret) const;
123 :
124 : // Is the point within the caret's rect? The point should be relative to root
125 : // frame.
126 : enum class TouchArea {
127 : Full, // Contains both text overlay and caret image.
128 : CaretImage
129 : };
130 : bool Contains(const nsPoint& aPoint, TouchArea aTouchArea) const;
131 :
132 : // The geometry center of the imaginary caret (nsCaret) to which this
133 : // AccessibleCaret is attached. It is needed when dragging the caret.
134 0 : nsPoint LogicalPosition() const
135 : {
136 0 : return mImaginaryCaretRect.Center();
137 : }
138 :
139 : // Element for 'Intersects' test. Container of image and bar elements.
140 0 : dom::Element* CaretElement() const
141 : {
142 0 : return mCaretElementHolder->GetContentNode();
143 : }
144 :
145 : // Ensures that the caret element is made "APZ aware" so that the APZ code
146 : // doesn't scroll the page when the user is trying to drag the caret.
147 : void EnsureApzAware();
148 :
149 : protected:
150 : // Argument aRect should be relative to CustomContentContainerFrame().
151 : void SetCaretElementStyle(const nsRect& aRect, float aZoomLevel);
152 : void SetTextOverlayElementStyle(const nsRect& aRect, float aZoomLevel);
153 : void SetCaretImageElementStyle(const nsRect& aRect, float aZoomLevel);
154 : void SetSelectionBarElementStyle(const nsRect& aRect, float aZoomLevel);
155 :
156 : // Get current zoom level.
157 : float GetZoomLevel();
158 :
159 : // Element which contains the text overly for the 'Contains' test.
160 0 : dom::Element* TextOverlayElement() const
161 : {
162 0 : return mCaretElementHolder->GetElementById(sTextOverlayElementId);
163 : }
164 :
165 : // Element which contains the caret image for 'Contains' test.
166 0 : dom::Element* CaretImageElement() const
167 : {
168 0 : return mCaretElementHolder->GetElementById(sCaretImageElementId);
169 : }
170 :
171 : // Element which represents the text selection bar.
172 0 : dom::Element* SelectionBarElement() const
173 : {
174 0 : return mCaretElementHolder->GetElementById(sSelectionBarElementId);
175 : }
176 :
177 0 : nsIFrame* RootFrame() const
178 : {
179 0 : return mPresShell->GetRootFrame();
180 : }
181 :
182 : nsIFrame* CustomContentContainerFrame() const;
183 :
184 : // Transform Appearance to CSS id used in ua.css.
185 : static nsAutoString AppearanceString(Appearance aAppearance);
186 :
187 : already_AddRefed<dom::Element> CreateCaretElement(nsIDocument* aDocument) const;
188 :
189 : // Inject caret element into custom content container.
190 : void InjectCaretElement(nsIDocument* aDocument);
191 :
192 : // Remove caret element from custom content container.
193 : void RemoveCaretElement(nsIDocument* aDocument);
194 :
195 : // The top-center of the imaginary caret to which this AccessibleCaret is
196 : // attached.
197 0 : static nsPoint CaretElementPosition(const nsRect& aRect)
198 : {
199 0 : return aRect.TopLeft() + nsPoint(aRect.width / 2, 0);
200 : }
201 :
202 0 : class DummyTouchListener final : public nsIDOMEventListener
203 : {
204 : public:
205 : NS_DECL_ISUPPORTS
206 0 : NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent) override
207 : {
208 0 : return NS_OK;
209 : }
210 :
211 : private:
212 0 : virtual ~DummyTouchListener() {};
213 : };
214 :
215 : // Member variables
216 : Appearance mAppearance = Appearance::None;
217 :
218 : bool mSelectionBarEnabled = false;
219 :
220 : // AccessibleCaretManager owns us by a UniquePtr. When it's terminated by
221 : // AccessibleCaretEventHub::Terminate() which is called in
222 : // PresShell::Destroy(), it frees us automatically. No need to worry if we
223 : // outlive mPresShell.
224 : nsIPresShell* MOZ_NON_OWNING_REF const mPresShell = nullptr;
225 :
226 : RefPtr<dom::AnonymousContent> mCaretElementHolder;
227 :
228 : // mImaginaryCaretRect is relative to root frame.
229 : nsRect mImaginaryCaretRect;
230 :
231 : // Cache current zoom level to determine whether position is changed.
232 : float mZoomLevel = 0.0f;
233 :
234 : // A no-op touch-start listener which prevents APZ from panning when dragging
235 : // the caret.
236 0 : RefPtr<DummyTouchListener> mDummyTouchListener{new DummyTouchListener()};
237 :
238 : // Static class variables
239 : static float sWidth;
240 : static float sHeight;
241 : static float sMarginLeft;
242 : static float sBarWidth;
243 : static const nsLiteralString sTextOverlayElementId;
244 : static const nsLiteralString sCaretImageElementId;
245 : static const nsLiteralString sSelectionBarElementId;
246 :
247 : }; // class AccessibleCaret
248 :
249 : std::ostream& operator<<(std::ostream& aStream,
250 : const AccessibleCaret::Appearance& aAppearance);
251 :
252 : std::ostream& operator<<(std::ostream& aStream,
253 : const AccessibleCaret::PositionChangedResult& aResult);
254 :
255 : } // namespace mozilla
256 :
257 : #endif // AccessibleCaret_h__
|