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