gecko-dev/layout/base/AccessibleCaret.h
"Emilio Cobos Álvarez" f3d30ca484 Bug 1472020 - Make AccessibleCaret a bit saner. r=bz,TYLin
Avoid processing anon content in nsCanvasFrame, then getting more anon content
via AccessibleCaretEventHub::Init. Instead call Init before creating the custom
content container. We could also throw a script runner at it I guess, but this
prevents the reentrancy issue.

Avoid cloning nodes during layout, just use the same node (already cloned in
InsertAnonymousContent) instead.

The RemoveChild in GetAnonymousContent to handle the reframes instead of cloning
around is a bit hacky, but I don't think it's really worth extending
PostDestroyData for this special case.

Differential Revision: https://phabricator.services.mozilla.com/D1889
2018-08-13 11:56:48 +02:00

262 lines
8.0 KiB
C++

/* -*- 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 "mozilla/dom/Element.h"
#include "nsCOMPtr.h"
#include "nsIDOMEventListener.h"
#include "nsISupportsBase.h"
#include "nsISupportsImpl.h"
#include "nsLiteralString.h"
#include "nsRect.h"
#include "mozilla/RefPtr.h"
#include "nsString.h"
class nsIDocument;
class nsIFrame;
class nsIPresShell;
struct nsPoint;
namespace mozilla {
namespace dom {
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(nsIPresShell* 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);
}
// Set true to enable the "Text Selection Bar" described in "Text Selection
// Visual Spec" in bug 921965.
virtual void SetSelectionBarEnabled(bool aEnabled);
// This enumeration representing the result returned by SetPosition().
enum class PositionChangedResult : uint8_t {
// Position is not changed.
NotChanged,
// Position or zoom level is changed.
Changed,
// 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. Container of image and bar elements.
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();
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);
void SetSelectionBarElementStyle(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);
}
// Element which represents the text selection bar.
dom::Element* SelectionBarElement() const
{
return mCaretElementHolder->GetElementById(sSelectionBarElementId);
}
nsIFrame* RootFrame() const
{
return mPresShell->GetRootFrame();
}
nsIFrame* CustomContentContainerFrame() const;
// Transform Appearance to CSS id used in ua.css.
static nsAutoString AppearanceString(Appearance aAppearance);
already_AddRefed<dom::Element> CreateCaretElement(nsIDocument* aDocument) const;
// Inject caret element into custom content container.
void InjectCaretElement(nsIDocument* aDocument);
// Remove caret element from custom content container.
void RemoveCaretElement(nsIDocument* aDocument);
// 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() {};
};
// Member variables
Appearance mAppearance = Appearance::None;
bool mSelectionBarEnabled = false;
// 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.
nsIPresShell* MOZ_NON_OWNING_REF const mPresShell = nullptr;
RefPtr<dom::AnonymousContent> mCaretElementHolder;
// mImaginaryCaretRect is relative to root frame.
nsRect mImaginaryCaretRect;
// 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 float sWidth;
static float sHeight;
static float sMarginLeft;
static float sBarWidth;
static const nsLiteralString sTextOverlayElementId;
static const nsLiteralString sCaretImageElementId;
static const nsLiteralString sSelectionBarElementId;
}; // 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__