tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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__