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