Bug 1744573: Cache the transform matrix r=eeejay,Jamie

Differential Revision: https://phabricator.services.mozilla.com/D133134
This commit is contained in:
Morgan Reschenberg 2022-02-25 22:23:13 +00:00
parent ac056a416f
commit 618acab981
9 changed files with 177 additions and 62 deletions

View File

@ -5,6 +5,7 @@
#include "AccAttributes.h"
#include "StyleInfo.h"
#include "mozilla/ToString.h"
using namespace mozilla::a11y;
@ -65,6 +66,9 @@ void AccAttributes::StringFromValueAndName(nsAtom* aAttrName,
[&aValueString](const uint64_t& val) { aValueString.AppendInt(val); },
[&aValueString](const UniquePtr<AccGroupInfo>& val) {
aValueString.Assign(u"AccGroupInfo{...}");
},
[&aValueString](const UniquePtr<gfx::Matrix4x4>& val) {
aValueString.AppendPrintf("Matrix4x4=%s", ToString(*val).c_str());
});
}
@ -157,6 +161,10 @@ void AccAttributes::CopyTo(AccAttributes* aDest) const {
[](const UniquePtr<AccGroupInfo>& val) {
MOZ_ASSERT_UNREACHABLE(
"Trying to copy an AccAttributes containing an AccGroupInfo");
},
[](const UniquePtr<gfx::Matrix4x4>& val) {
MOZ_ASSERT_UNREACHABLE(
"Trying to copy an AccAttributes containing a matrix");
});
}
}

View File

@ -12,6 +12,7 @@
#include "nsTHashMap.h"
#include "nsAtom.h"
#include "nsStringFwd.h"
#include "mozilla/gfx/Matrix.h"
class nsVariant;
@ -68,7 +69,8 @@ class AccAttributes {
using AttrValueType =
Variant<bool, float, double, int32_t, RefPtr<nsAtom>, nsTArray<int32_t>,
CSSCoord, FontSize, Color, DeleteEntry, UniquePtr<nsString>,
RefPtr<AccAttributes>, uint64_t, UniquePtr<AccGroupInfo>>;
RefPtr<AccAttributes>, uint64_t, UniquePtr<AccGroupInfo>,
UniquePtr<gfx::Matrix4x4>>;
static_assert(sizeof(AttrValueType) <= 16);
using AtomVariantMap = nsTHashMap<nsRefPtrHashKey<nsAtom>, AttrValueType>;
@ -87,6 +89,7 @@ class AccAttributes {
template <typename T, typename std::enable_if<
!std::is_convertible_v<T, nsString> &&
!std::is_convertible_v<T, AccGroupInfo*> &&
!std::is_convertible_v<T, gfx::Matrix4x4> &&
!std::is_convertible_v<T, nsAtom*>,
bool>::type = true>
void SetAttribute(nsAtom* aAttrName, T&& aAttrValue) {
@ -101,6 +104,11 @@ class AccAttributes {
void SetAttribute(nsAtom* aAttrName, AccGroupInfo* aAttrValue) {
UniquePtr<AccGroupInfo> value(aAttrValue);
}
void SetAttribute(nsAtom* aAttrName, gfx::Matrix4x4&& aAttrValue) {
UniquePtr<gfx::Matrix4x4> value = MakeUnique<gfx::Matrix4x4>();
*value = std::forward<gfx::Matrix4x4>(aAttrValue);
mData.InsertOrUpdate(aAttrName, AsVariant(std::move(value)));
}
@ -120,6 +128,11 @@ class AccAttributes {
const T& val = *(value->as<UniquePtr<nsString>>().get());
return SomeRef(val);
}
} else if constexpr (std::is_same_v<gfx::Matrix4x4, T>) {
if (value->is<UniquePtr<gfx::Matrix4x4>>()) {
const T& val = *(value->as<UniquePtr<gfx::Matrix4x4>>());
return SomeRef(val);
}
} else {
if (value->is<T>()) {
const T& val = value->as<T>();
@ -184,6 +197,11 @@ class AccAttributes {
const T& val = *(mValue->as<UniquePtr<nsString>>().get());
return SomeRef(val);
}
} else if constexpr (std::is_same_v<gfx::Matrix4x4, T>) {
if (mValue->is<UniquePtr<gfx::Matrix4x4>>()) {
const T& val = *(mValue->as<UniquePtr<gfx::Matrix4x4>>());
return SomeRef(val);
}
} else {
if (mValue->is<T>()) {
const T& val = mValue->as<T>();

View File

@ -22,6 +22,7 @@ class CacheDomain {
static constexpr uint64_t GroupInfo = ((uint64_t)0x1) << 7;
static constexpr uint64_t Actions = ((uint64_t)0x1) << 8;
static constexpr uint64_t Style = ((uint64_t)0x1) << 9;
static constexpr uint64_t TransformMatrix = ((uint64_t)0x1) << 10;
static constexpr uint64_t All = ~((uint64_t)0x0);
};

View File

@ -44,6 +44,7 @@
#include "mozilla/dom/Document.h"
#include "mozilla/dom/HTMLFormElement.h"
#include "mozilla/dom/HTMLAnchorElement.h"
#include "mozilla/gfx/Matrix.h"
#include "nsIContent.h"
#include "nsIFormControl.h"
@ -606,8 +607,44 @@ LocalAccessible* LocalAccessible::LocalChildAtPoint(
return accessible;
}
nsRect LocalAccessible::ParentRelativeBounds() {
nsIFrame* LocalAccessible::FindNearestAccessibleAncestorFrame() {
nsIFrame* frame = GetFrame();
nsIFrame* boundingFrame = nullptr;
if (IsDoc() &&
nsCoreUtils::IsTopLevelContentDocInProcess(AsDoc()->DocumentNode())) {
// Tab documents and OOP iframe docs won't have ancestor accessibles
// with frames. Instead, bound by their presshell's root frame.
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
}
// Iterate through accessible's ancestors to find one with a frame.
LocalAccessible* ancestor = mParent;
while (ancestor && !boundingFrame) {
if (ancestor->IsDoc()) {
// If we find a doc accessible, use its presshell's root frame
// (since doc accessibles themselves don't have frames).
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
break;
}
if ((boundingFrame = ancestor->GetFrame())) {
// Otherwise, if the ancestor has a frame, use that
break;
}
ancestor = ancestor->LocalParent();
}
if (!boundingFrame) {
MOZ_ASSERT_UNREACHABLE("No ancestor with frame?");
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
}
return boundingFrame;
}
nsRect LocalAccessible::ParentRelativeBounds() {
nsIFrame* frame = GetFrame();
if (frame && mContent) {
if (mContent->GetProperty(nsGkAtoms::hitregion) && mContent->IsElement()) {
@ -632,43 +669,7 @@ nsRect LocalAccessible::ParentRelativeBounds() {
}
}
// We need to find a frame to make our bounds relative to. We'll store this
// in `boundingFrame`. Ultimately, we'll create screen-relative coordinates
// by summing the x, y offsets of our ancestors' bounds in
// RemoteAccessibleBase::Bounds(), so it is important that our bounding
// frame have a corresponding accessible.
if (IsDoc() &&
nsCoreUtils::IsTopLevelContentDocInProcess(AsDoc()->DocumentNode())) {
// Tab documents and OOP iframe docs won't have ancestor accessibles with
// frames. We'll use their presshell root frame instead.
// XXX bug 1736635: Should DocAccessibles return their presShell frame on
// GetFrame()?
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
}
// Iterate through ancestors to find one with a frame.
LocalAccessible* parent = mParent;
while (parent && !boundingFrame) {
if (parent->IsDoc()) {
// If we find a doc accessible, use its presshell's root frame
// (since doc accessibles themselves don't have frames).
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
break;
}
if ((boundingFrame = parent->GetFrame())) {
// Otherwise, if the parent has a frame, use that
break;
}
parent = parent->LocalParent();
}
if (!boundingFrame) {
MOZ_ASSERT_UNREACHABLE("No ancestor with frame?");
boundingFrame = nsLayoutUtils::GetContainingBlockForClientRect(frame);
}
nsIFrame* boundingFrame = FindNearestAccessibleAncestorFrame();
nsRect unionRect = nsLayoutUtils::GetAllInFlowRectsUnion(
frame, boundingFrame, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
@ -3246,6 +3247,34 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
}
}
if (aCacheDomain & CacheDomain::TransformMatrix) {
nsIFrame* frame = GetFrame();
if (frame && frame->IsTransformed()) {
// We need to find a frame to make our transform relative to.
// It's important this frame have a corresponding accessible,
// because this transform is applied while walking the accessibility
// tree (in the parent process), not the frame tree.
nsIFrame* boundingFrame = FindNearestAccessibleAncestorFrame();
// This matrix is only valid when applied to CSSPixel points/rects
// in the coordinate space of `frame`. It also includes the translation
// to the parent space.
gfx::Matrix4x4 mtx = nsLayoutUtils::GetTransformToAncestor(
RelativeTo{frame}, RelativeTo{boundingFrame},
nsIFrame::IN_CSS_UNITS)
.GetMatrix();
UniquePtr<gfx::Matrix4x4> ptr = MakeUnique<gfx::Matrix4x4>(mtx);
fields->SetAttribute(nsGkAtoms::transform, std::move(ptr));
} else {
// Otherwise, if we're bundling a transform update but this
// frame isn't transformed (or doesn't exist), we need
// to send a DeleteEntry() to remove any
// transform that was previously cached for this frame.
fields->SetAttribute(nsGkAtoms::transform, DeleteEntry());
}
}
if (aCacheDomain & CacheDomain::DOMNodeID && mContent) {
nsAtom* id = mContent->GetID();
if (id) {
@ -3327,6 +3356,11 @@ already_AddRefed<AccAttributes> LocalAccessible::BundleFieldsForCache(
// Note our frame's current computed style so we can track style changes
// later on.
mOldComputedStyle = frame->Style();
if (frame->IsTransformed()) {
mStateFlags |= eOldFrameHasValidTransformStyle;
} else {
mStateFlags &= ~eOldFrameHasValidTransformStyle;
}
}
}
@ -3352,7 +3386,39 @@ void LocalAccessible::MaybeQueueCacheUpdateForStyleChanges() {
mDoc->QueueCacheUpdate(this, CacheDomain::Style);
}
bool newHasValidTransformStyle = frame->IsTransformed();
bool oldHasValidTransformStyle =
(mStateFlags & eOldFrameHasValidTransformStyle) != 0;
// We should send a transform update if we're adding or
// removing transform styling altogether.
bool sendTransformUpdate =
newHasValidTransformStyle || oldHasValidTransformStyle;
if (newHasValidTransformStyle && oldHasValidTransformStyle) {
// If we continue to have transform styling, verify
// our transform has actually changed.
nsChangeHint transformHint =
newStyle->StyleDisplay()->CalcTransformPropertyDifference(
*mOldComputedStyle->StyleDisplay());
// If this hint exists, it implies we found a property difference
sendTransformUpdate = !!transformHint;
}
if (sendTransformUpdate) {
// Queuing a cache update for the TransformMatrix domain doesn't
// necessarily mean we'll send the matrix itself, we may
// send a DeleteEntry() instead. See BundleFieldsForCache for
// more information.
mDoc->QueueCacheUpdate(this, CacheDomain::TransformMatrix);
}
mOldComputedStyle = newStyle;
if (newHasValidTransformStyle) {
mStateFlags |= eOldFrameHasValidTransformStyle;
} else {
mStateFlags &= ~eOldFrameHasValidTransformStyle;
}
}
}

View File

@ -861,8 +861,11 @@ class LocalAccessible : public nsISupports, public Accessible {
eRelocated = 1 << 7, // accessible was moved in tree
eNoKidsFromDOM = 1 << 8, // accessible doesn't allow children from DOM
eHasTextKids = 1 << 9, // accessible have a text leaf in children
eOldFrameHasValidTransformStyle =
1 << 10, // frame prior to most recent style change both has transform
// styling and supports transforms
eLastStateFlag = eHasTextKids
eLastStateFlag = eOldFrameHasValidTransformStyle
};
/**
@ -1045,6 +1048,13 @@ class LocalAccessible : public nsISupports, public Accessible {
LocalAccessible() = delete;
LocalAccessible(const LocalAccessible&) = delete;
LocalAccessible& operator=(const LocalAccessible&) = delete;
/**
* Traverses the accessible's parent chain in search of an accessible with
* a frame. Returns the frame when found. Includes special handling for
* OOP iframe docs and tab documents.
*/
nsIFrame* FindNearestAccessibleAncestorFrame();
};
NS_DEFINE_STATIC_IID_ACCESSOR(LocalAccessible, NS_ACCESSIBLE_IMPL_IID)

View File

@ -20,6 +20,7 @@ using mozilla::a11y::AccType from "mozilla/a11y/IPCTypes.h";
using mozilla::a11y::AccGenericType from "mozilla/a11y/IPCTypes.h";
[RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
using mozilla::a11y::CacheUpdateType from "mozilla/a11y/IPCTypes.h";
using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
namespace mozilla {
namespace a11y {

View File

@ -20,6 +20,7 @@ using mozilla::a11y::CacheUpdateType from "mozilla/a11y/IPCTypes.h";
[RefCounted] using mozilla::a11y::AccAttributes from "mozilla/a11y/IPCTypes.h";
using mozilla::WindowsHandle from "mozilla/ipc/IPCTypes.h";
using mozilla::LayoutDeviceIntRect from "Units.h";
using mozilla::gfx::Matrix4x4 from "mozilla/gfx/Matrix.h";
namespace mozilla {
namespace a11y {

View File

@ -2560,28 +2560,7 @@ nsChangeHint nsStyleDisplay::CalcDifference(
* comparisons but turn all the resulting change hints into
* nsChangeHint_NeutralChange.
*/
nsChangeHint transformHint = nsChangeHint(0);
transformHint |= CompareTransformValues(mTransform, aNewData.mTransform);
transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
transformHint |= CompareTransformValues(mScale, aNewData.mScale);
transformHint |= CompareMotionValues(*this, aNewData);
if (mTransformOrigin != aNewData.mTransformOrigin) {
transformHint |= nsChangeHint_UpdateTransformLayer |
nsChangeHint_UpdatePostTransformOverflow;
}
if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
mTransformStyle != aNewData.mTransformStyle ||
mTransformBox != aNewData.mTransformBox) {
transformHint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
}
if (mBackfaceVisibility != aNewData.mBackfaceVisibility) {
transformHint |= nsChangeHint_RepaintFrame;
}
nsChangeHint transformHint = CalcTransformPropertyDifference(aNewData);
if (transformHint) {
if (HasTransformStyle()) {
@ -2692,6 +2671,34 @@ nsChangeHint nsStyleDisplay::CalcDifference(
return hint;
}
nsChangeHint nsStyleDisplay::CalcTransformPropertyDifference(
const nsStyleDisplay& aNewData) const {
nsChangeHint transformHint = nsChangeHint(0);
transformHint |= CompareTransformValues(mTransform, aNewData.mTransform);
transformHint |= CompareTransformValues(mRotate, aNewData.mRotate);
transformHint |= CompareTransformValues(mTranslate, aNewData.mTranslate);
transformHint |= CompareTransformValues(mScale, aNewData.mScale);
transformHint |= CompareMotionValues(*this, aNewData);
if (mTransformOrigin != aNewData.mTransformOrigin) {
transformHint |= nsChangeHint_UpdateTransformLayer |
nsChangeHint_UpdatePostTransformOverflow;
}
if (mPerspectiveOrigin != aNewData.mPerspectiveOrigin ||
mTransformStyle != aNewData.mTransformStyle ||
mTransformBox != aNewData.mTransformBox) {
transformHint |= nsChangeHint_UpdateOverflow | nsChangeHint_RepaintFrame;
}
if (mBackfaceVisibility != aNewData.mBackfaceVisibility) {
transformHint |= nsChangeHint_RepaintFrame;
}
return transformHint;
}
// --------------------
// nsStyleVisibility
//

View File

@ -1191,6 +1191,9 @@ struct MOZ_NEEDS_MEMMOVABLE_MEMBERS nsStyleDisplay {
nsChangeHint CalcDifference(const nsStyleDisplay& aNewData,
const nsStylePosition& aOldPosition) const;
nsChangeHint CalcTransformPropertyDifference(
const nsStyleDisplay& aNewData) const;
nsStyleAutoArray<mozilla::StyleTransition> mTransitions;
// The number of elements in mTransitions that are not from repeating
// a list due to another property being longer.