diff options
Diffstat (limited to 'layout/base/AccessibleCaret.h')
-rw-r--r-- | layout/base/AccessibleCaret.h | 241 |
1 files changed, 241 insertions, 0 deletions
diff --git a/layout/base/AccessibleCaret.h b/layout/base/AccessibleCaret.h new file mode 100644 index 0000000000..979a528639 --- /dev/null +++ b/layout/base/AccessibleCaret.h @@ -0,0 +1,241 @@ +/* -*- 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 "nsCOMPtr.h" +#include "nsIDOMEventListener.h" +#include "nsIFrame.h" // for WeakFrame only +#include "nsISupports.h" +#include "nsISupportsImpl.h" +#include "nsLiteralString.h" +#include "nsRect.h" +#include "mozilla/RefPtr.h" +#include "nsString.h" + +class nsIFrame; +struct nsPoint; + +namespace mozilla { +class PresShell; +namespace dom { +class Element; +class Event; +} // namespace dom + +// ----------------------------------------------------------------------------- +// 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(PresShell* 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); + } + + // This enum represents the result returned by SetPosition(). + enum class PositionChangedResult : uint8_t { + // Both position and the zoom level are not changed. + NotChanged, + + // The position is changed. (The zoom level may or may not be changed.) + Position, + + // Only the zoom level is changed. The position is *not* changed. + Zoom, + + // The 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. This is the container of the caret image + // and text-overlay elements. See CreateCaretElement() for the content + // structure. + dom::Element& CaretElement() const { + return mCaretElementHolder->ContentNode(); + } + + // 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(); + + bool IsInPositionFixedSubtree() const; + + 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); + + // 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); + } + + nsIFrame* RootFrame() const; + + nsIFrame* CustomContentContainerFrame() const; + + // Transform Appearance to CSS id used in ua.css. + static nsAutoString AppearanceString(Appearance aAppearance); + + already_AddRefed<dom::Element> CreateCaretElement(dom::Document*) const; + + // Inject caret element into custom content container. + void InjectCaretElement(dom::Document*); + + // Remove caret element from custom content container. + void RemoveCaretElement(dom::Document*); + + // Clear the cached rects and zoom level. + void ClearCachedData(); + + // 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(mozilla::dom::Event* aEvent) override { + return NS_OK; + } + + private: + virtual ~DummyTouchListener() = default; + }; + + // Member variables + Appearance mAppearance = Appearance::None; + + // 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. + PresShell* const MOZ_NON_OWNING_REF mPresShell = nullptr; + + RefPtr<dom::AnonymousContent> mCaretElementHolder; + + // This cached rect is relative to the root frame, and is used in + // LogicalPosition() when dragging a caret. + nsRect mImaginaryCaretRect; + + // This cached rect is relative to the custom content container, and is used + // in SetPosition() to check whether the caret position has changed. + nsRect mImaginaryCaretRectInContainerFrame; + + // The reference frame we used to calculate mImaginaryCaretRect and + // mImaginaryCaretRectInContainerFrame. + WeakFrame mImaginaryCaretReferenceFrame; + + // 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 const nsLiteralString sTextOverlayElementId; + static const nsLiteralString sCaretImageElementId; + +}; // 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__ |