Bug 1744573: Apply single-level transforms in RemoteAccessibleBase::Bounds r=eeejay,Jamie,emilio

Differential Revision: https://phabricator.services.mozilla.com/D131554
This commit is contained in:
Morgan Reschenberg 2022-02-25 22:23:13 +00:00
Родитель 618acab981
Коммит 7aae56705c
6 изменённых файлов: 155 добавлений и 10 удалений

Просмотреть файл

@ -670,8 +670,8 @@ nsRect LocalAccessible::ParentRelativeBounds() {
} }
nsIFrame* boundingFrame = FindNearestAccessibleAncestorFrame(); nsIFrame* boundingFrame = FindNearestAccessibleAncestorFrame();
nsRect unionRect = nsLayoutUtils::GetAllInFlowRectsUnion( nsRect unionRect =
frame, boundingFrame, nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS); nsLayoutUtils::GetAllInFlowRectsUnion(frame, boundingFrame);
if (unionRect.IsEmpty()) { if (unionRect.IsEmpty()) {
// If we end up with a 0x0 rect from above (or one with negative // If we end up with a 0x0 rect from above (or one with negative
@ -3377,7 +3377,6 @@ void LocalAccessible::MaybeQueueCacheUpdateForStyleChanges() {
if (nsIFrame* frame = GetFrame()) { if (nsIFrame* frame = GetFrame()) {
const ComputedStyle* newStyle = frame->Style(); const ComputedStyle* newStyle = frame->Style();
MOZ_ASSERT(newStyle != mOldComputedStyle, "New style matches old style!");
nsAutoCString oldVal, newVal; nsAutoCString oldVal, newVal;
mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_display, oldVal); mOldComputedStyle->GetComputedPropertyValue(eCSSProperty_display, oldVal);

Просмотреть файл

@ -16,6 +16,7 @@
#include "mozilla/dom/BrowserParent.h" #include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/CanonicalBrowsingContext.h" #include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/DocumentInlines.h" #include "mozilla/dom/DocumentInlines.h"
#include "mozilla/gfx/Matrix.h"
#include "mozilla/Unused.h" #include "mozilla/Unused.h"
#include "nsAccUtils.h" #include "nsAccUtils.h"
#include "nsTextEquivUtils.h" #include "nsTextEquivUtils.h"
@ -313,13 +314,39 @@ Maybe<nsRect> RemoteAccessibleBase<Derived>::RetrieveCachedBounds() const {
return Nothing(); return Nothing();
} }
template <class Derived>
bool RemoteAccessibleBase<Derived>::ApplyTransform(nsRect& aBounds) const {
// First, attempt to retrieve the transform from the cache.
Maybe<const UniquePtr<gfx::Matrix4x4>&> maybeTransform =
mCachedFields->GetAttribute<UniquePtr<gfx::Matrix4x4>>(
nsGkAtoms::transform);
if (!maybeTransform) {
return false;
}
// The transform matrix we cache is meant to operate on rects
// within the coordinate space of the frame to which the
// transform is applied (self-relative rects). We cache bounds
// relative to some ancestor. Remove the relative offset before
// transforming. The transform matrix will add it back in.
aBounds.MoveTo(0, 0);
auto mtxInPixels = gfx::Matrix4x4Typed<CSSPixel, CSSPixel>::FromUnknownMatrix(
*(*maybeTransform));
// Our matrix is in CSS Pixels, so we need our rect to be in CSS
// Pixels too. Convert before applying.
auto boundsInPixels = CSSRect::FromAppUnits(aBounds);
boundsInPixels = mtxInPixels.TransformBounds(boundsInPixels);
aBounds = CSSRect::ToAppUnits(boundsInPixels);
return true;
}
template <class Derived> template <class Derived>
LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const { LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
if (mCachedFields) { if (mCachedFields) {
Maybe<nsRect> maybeBounds = RetrieveCachedBounds(); Maybe<nsRect> maybeBounds = RetrieveCachedBounds();
if (maybeBounds) { if (maybeBounds) {
nsRect bounds = *maybeBounds; nsRect bounds = *maybeBounds;
LayoutDeviceIntRect devPxBounds;
dom::CanonicalBrowsingContext* cbc = dom::CanonicalBrowsingContext* cbc =
static_cast<dom::BrowserParent*>(mDoc->Manager()) static_cast<dom::BrowserParent*>(mDoc->Manager())
->GetBrowsingContext() ->GetBrowsingContext()
@ -328,7 +355,11 @@ LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
nsPresContext* presContext = nsPresContext* presContext =
bp->GetOwnerElement()->OwnerDoc()->GetPresContext(); bp->GetOwnerElement()->OwnerDoc()->GetPresContext();
Unused << ApplyTransform(bounds);
LayoutDeviceIntRect devPxBounds;
const Accessible* acc = this; const Accessible* acc = this;
while (acc) { while (acc) {
if (LocalAccessible* localAcc = if (LocalAccessible* localAcc =
const_cast<Accessible*>(acc)->AsLocal()) { const_cast<Accessible*>(acc)->AsLocal()) {
@ -349,7 +380,6 @@ LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
// by GetFullZoom because LocalAccessible::Bounds already does // by GetFullZoom because LocalAccessible::Bounds already does
// that. // that.
devPxBounds.MoveBy(localBounds.X(), localBounds.Y()); devPxBounds.MoveBy(localBounds.X(), localBounds.Y());
break; break;
} }
@ -360,6 +390,7 @@ LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
(remoteAcc == this) ? Nothing() : remoteAcc->RetrieveCachedBounds(); (remoteAcc == this) ? Nothing() : remoteAcc->RetrieveCachedBounds();
if (maybeRemoteBounds) { if (maybeRemoteBounds) {
nsRect remoteBounds = *maybeRemoteBounds;
// We need to take into account a non-1 resolution set on the // We need to take into account a non-1 resolution set on the
// presshell. This happens with async pinch zooming, among other // presshell. This happens with async pinch zooming, among other
// things. We can't reliably query this value in the parent process, // things. We can't reliably query this value in the parent process,
@ -378,11 +409,10 @@ LayoutDeviceIntRect RemoteAccessibleBase<Derived>::Bounds() const {
bounds.ScaleRoundOut(res.valueOr(1.0f)); bounds.ScaleRoundOut(res.valueOr(1.0f));
} }
// Regardless of whether this is a doc, we should offset `bounds` // We should offset `bounds` by the bounds retrieved above.
// by the bounds retrieved here. This is how we build screen // This is how we build screen coordinates from relative coordinates.
// coordinates from relative coordinates.
nsRect remoteBounds = *maybeRemoteBounds;
bounds.MoveBy(remoteBounds.X(), remoteBounds.Y()); bounds.MoveBy(remoteBounds.X(), remoteBounds.Y());
Unused << remoteAcc->ApplyTransform(bounds);
} }
acc = acc->Parent(); acc = acc->Parent();

Просмотреть файл

@ -320,6 +320,7 @@ class RemoteAccessibleBase : public Accessible, public HyperTextAccessibleBase {
protected: protected:
void SetParent(Derived* aParent); void SetParent(Derived* aParent);
Maybe<nsRect> RetrieveCachedBounds() const; Maybe<nsRect> RetrieveCachedBounds() const;
bool ApplyTransform(nsRect& aBounds) const;
virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize, virtual void ARIAGroupPosition(int32_t* aLevel, int32_t* aSetSize,
int32_t* aPosInSet) const override; int32_t* aPosInSet) const override;

Просмотреть файл

@ -17,3 +17,4 @@ https_first_disabled = true
skip-if = e10s && os == 'win' # bug 1372296 skip-if = e10s && os == 'win' # bug 1372296
[browser_zero_area.js] [browser_zero_area.js]
[browser_test_display_contents.js] [browser_test_display_contents.js]
[browser_test_simple_transform.js]

Просмотреть файл

@ -0,0 +1,110 @@
/* 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/. */
"use strict";
async function finishContentPaint(browser) {
await SpecialPowers.spawn(browser, [], () => {
return new Promise(function(r) {
content.requestAnimationFrame(() => content.setTimeout(r));
});
});
}
async function testContentBoundsInIframe(iframeDocAcc, id, browser) {
const acc = findAccessibleChildByID(iframeDocAcc, id);
const x = {};
const y = {};
const width = {};
const height = {};
acc.getBounds(x, y, width, height);
await invokeContentTask(
browser,
[id, x.value, y.value, width.value, height.value],
(_id, _x, _y, _width, _height) => {
const { Layout: LayoutUtils } = ChromeUtils.import(
"chrome://mochitests/content/browser/accessible/tests/browser/Layout.jsm"
);
let [
expectedX,
expectedY,
expectedWidth,
expectedHeight,
] = LayoutUtils.getBoundsForDOMElm(_id, content.document);
ok(
_x >= expectedX - 5 || _x <= expectedX + 5,
"Got " + _x + ", accurate x for " + _id
);
ok(
_y >= expectedY - 5 || _y <= expectedY + 5,
"Got " + _y + ", accurate y for " + _id
);
ok(
_width >= expectedWidth - 5 || _width <= expectedWidth + 5,
"Got " + _width + ", accurate width for " + _id
);
ok(
_height >= expectedHeight - 5 || _height <= expectedHeight + 5,
"Got " + _height + ", accurate height for " + _id
);
}
);
}
// test basic translation
addAccessibleTask(
`<p id="translate">hello world</p>`,
async function(browser, iframeDocAcc, contentDocAcc) {
ok(iframeDocAcc, "IFRAME document accessible is present");
await testContentBoundsInIframe(iframeDocAcc, "translate", browser);
await invokeContentTask(browser, [], () => {
let p = content.document.getElementById("translate");
p.style = "transform: translate(100px, 100px);";
});
await finishContentPaint(browser);
await testContentBoundsInIframe(iframeDocAcc, "translate", browser);
},
{ topLevel: true, iframe: true, remoteIframe: true }
);
// test basic rotation
addAccessibleTask(
`<p id="rotate">hello world</p>`,
async function(browser, iframeDocAcc, contentDocAcc) {
ok(iframeDocAcc, "IFRAME document accessible is present");
await testContentBoundsInIframe(iframeDocAcc, "rotate", browser);
await invokeContentTask(browser, [], () => {
let p = content.document.getElementById("rotate");
p.style = "transform: rotate(-40deg);";
});
await finishContentPaint(browser);
await testContentBoundsInIframe(iframeDocAcc, "rotate", browser);
},
{ topLevel: true, iframe: true, remoteIframe: true }
);
// test basic scale
addAccessibleTask(
`<p id="scale">hello world</p>`,
async function(browser, iframeDocAcc, contentDocAcc) {
ok(iframeDocAcc, "IFRAME document accessible is present");
await testContentBoundsInIframe(iframeDocAcc, "scale", browser);
await invokeContentTask(browser, [], () => {
let p = content.document.getElementById("scale");
p.style = "transform: scale(2);";
});
await finishContentPaint(browser);
await testContentBoundsInIframe(iframeDocAcc, "scale", browser);
},
{ topLevel: true, iframe: true, remoteIframe: true }
);

Просмотреть файл

@ -3680,7 +3680,11 @@ struct BoxToRect : public nsLayoutUtils::BoxCallback {
nsLayoutUtils::TransformRect(outer, mRelativeTo, r); nsLayoutUtils::TransformRect(outer, mRelativeTo, r);
} }
} else { } else {
r += outer->GetOffsetTo(mRelativeTo); if (aFrame->PresContext() != mRelativeTo->PresContext()) {
r += outer->GetOffsetToCrossDoc(mRelativeTo);
} else {
r += outer->GetOffsetTo(mRelativeTo);
}
} }
mCallback->AddRect(r); mCallback->AddRect(r);
} }