Bug 1450927 - add getBoundsInCSSPixels XPCOM method. r=surkov r=jld
☠☠ backed out by 89bc67f5eaaf ☠ ☠
authorYura Zenevich <yura.zenevich@gmail.com>
Tue, 24 Apr 2018 15:07:03 -0400
changeset 469178 d7dd8b0d2473d38abcc425e583c377eac734d14c
parent 469177 8c74001d3808e8b385c60da11f6ca99053bd816b
child 469179 ca285aed3926966562f9444b912eb24d757d2657
push id9165
push userasasaki@mozilla.com
push dateThu, 26 Apr 2018 21:04:54 +0000
treeherdermozilla-beta@064c3804de2e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssurkov, jld
bugs1450927
milestone61.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1450927 - add getBoundsInCSSPixels XPCOM method. r=surkov r=jld MozReview-Commit-ID: CTANPVQw1sG
accessible/generic/Accessible-inl.h
accessible/generic/Accessible.cpp
accessible/generic/Accessible.h
accessible/generic/ApplicationAccessible.cpp
accessible/generic/ApplicationAccessible.h
accessible/html/HTMLListAccessible.cpp
accessible/html/HTMLListAccessible.h
accessible/interfaces/gecko/IGeckoCustom.idl
accessible/interfaces/nsIAccessible.idl
accessible/ipc/ProxyAccessibleShared.h
accessible/ipc/other/DocAccessibleChild.cpp
accessible/ipc/other/DocAccessibleChild.h
accessible/ipc/other/PDocAccessible.ipdl
accessible/ipc/other/ProxyAccessible.cpp
accessible/ipc/win/ProxyAccessible.cpp
accessible/tests/browser/bounds/browser_test_zoom.js
accessible/tests/browser/bounds/browser_test_zoom_text.js
accessible/tests/browser/bounds/head.js
accessible/tests/mochitest/bounds/test_list.html
accessible/tests/mochitest/layout.js
accessible/windows/msaa/GeckoCustom.cpp
accessible/windows/msaa/GeckoCustom.h
accessible/xpcom/xpcAccessible.cpp
accessible/xpcom/xpcAccessible.h
accessible/xul/XULTreeAccessible.cpp
accessible/xul/XULTreeAccessible.h
accessible/xul/XULTreeGridAccessible.cpp
accessible/xul/XULTreeGridAccessible.h
ipc/ipdl/sync-messages.ini
--- a/accessible/generic/Accessible-inl.h
+++ b/accessible/generic/Accessible-inl.h
@@ -139,12 +139,18 @@ Accessible::InsertAfter(Accessible* aNew
     mDoc->UnbindFromDocument(aNewChild);
     return false;
   }
 
   return InsertChildAt(aRefChild ? aRefChild->IndexInParent() + 1 : 0,
                        aNewChild);
 }
 
+inline nsIntRect
+Accessible::Bounds() const
+{
+  return BoundsInAppUnits().ToNearestPixels(mDoc->PresContext()->AppUnitsPerDevPixel());
+}
+
 } // namespace a11y
 } // namespace mozilla
 
 #endif
--- a/accessible/generic/Accessible.cpp
+++ b/accessible/generic/Accessible.cpp
@@ -665,42 +665,41 @@ Accessible::RelativeBounds(nsIFrame** aB
     return nsLayoutUtils::
       GetAllInFlowRectsUnion(frame, *aBoundingFrame,
                              nsLayoutUtils::RECTS_ACCOUNT_FOR_TRANSFORMS);
   }
 
   return nsRect();
 }
 
-nsIntRect
-Accessible::Bounds() const
+nsRect
+Accessible::BoundsInAppUnits() const
 {
   nsIFrame* boundingFrame = nullptr;
   nsRect unionRectTwips = RelativeBounds(&boundingFrame);
-  if (!boundingFrame)
-    return nsIntRect();
-
-  nsIntRect screenRect;
-  nsPresContext* presContext = mDoc->PresContext();
-  screenRect.SetRect(presContext->AppUnitsToDevPixels(unionRectTwips.X()),
-                     presContext->AppUnitsToDevPixels(unionRectTwips.Y()),
-                     presContext->AppUnitsToDevPixels(unionRectTwips.Width()),
-                     presContext->AppUnitsToDevPixels(unionRectTwips.Height()));
+  if (!boundingFrame) {
+    return nsRect();
+  }
 
   // We need to take into account a non-1 resolution set on the presshell.
   // This happens in mobile platforms with async pinch zooming. Here we
   // scale the bounds before adding the screen-relative offset.
-  screenRect.ScaleRoundOut(presContext->PresShell()->GetResolution());
+  unionRectTwips.ScaleRoundOut(mDoc->PresContext()->PresShell()->GetResolution());
   // We have the union of the rectangle, now we need to put it in absolute
   // screen coords.
-  nsIntRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits().
-    ToNearestPixels(presContext->AppUnitsPerDevPixel());
-  screenRect.MoveBy(orgRectPixels.X(), orgRectPixels.Y());
-
-  return screenRect;
+  nsRect orgRectPixels = boundingFrame->GetScreenRectInAppUnits();
+  unionRectTwips.MoveBy(orgRectPixels.X(), orgRectPixels.Y());
+
+  return unionRectTwips;
+}
+
+nsIntRect
+Accessible::BoundsInCSSPixels() const
+{
+  return BoundsInAppUnits().ToNearestPixels(mDoc->PresContext()->AppUnitsPerCSSPixel());
 }
 
 void
 Accessible::SetSelected(bool aSelect)
 {
   if (!HasOwnContent())
     return;
 
--- a/accessible/generic/Accessible.h
+++ b/accessible/generic/Accessible.h
@@ -505,19 +505,29 @@ public:
    *                        if missed entire text is appended
    * @param aLength       [in, optional] required length of text, if missed
    *                        then text form start offset till the end is appended
    */
   virtual void AppendTextTo(nsAString& aText, uint32_t aStartOffset = 0,
                             uint32_t aLength = UINT32_MAX);
 
   /**
+   * Return boundaries in screen coordinates in app units.
+   */
+  virtual nsRect BoundsInAppUnits() const;
+
+  /**
    * Return boundaries in screen coordinates.
    */
-  virtual nsIntRect Bounds() const;
+  nsIntRect Bounds() const;
+
+  /**
+   * Return boundaries in screen coordinates in CSS pixels.
+   */
+  virtual nsIntRect BoundsInCSSPixels() const;
 
   /**
    * Return boundaries rect relative the bounding frame.
    */
   virtual nsRect RelativeBounds(nsIFrame** aRelativeFrame) const;
 
   /**
    * Selects the accessible within its container if applicable.
--- a/accessible/generic/ApplicationAccessible.cpp
+++ b/accessible/generic/ApplicationAccessible.cpp
@@ -111,20 +111,20 @@ ApplicationAccessible::FocusedChild()
 }
 
 Relation
 ApplicationAccessible::RelationByType(RelationType aRelationType)
 {
   return Relation();
 }
 
-nsIntRect
-ApplicationAccessible::Bounds() const
+nsRect
+ApplicationAccessible::BoundsInAppUnits() const
 {
-  return nsIntRect();
+  return nsRect();
 }
 
 ////////////////////////////////////////////////////////////////////////////////
 // Accessible public methods
 
 void
 ApplicationAccessible::Shutdown()
 {
--- a/accessible/generic/ApplicationAccessible.h
+++ b/accessible/generic/ApplicationAccessible.h
@@ -31,17 +31,17 @@ class ApplicationAccessible : public Acc
 public:
 
   ApplicationAccessible();
 
   NS_INLINE_DECL_REFCOUNTING_INHERITED(ApplicationAccessible, AccessibleWrap)
 
   // Accessible
   virtual void Shutdown() override;
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
   virtual GroupPos GroupPosition() override;
   virtual ENameValueFlag Name(nsString& aName) override;
   virtual void ApplyARIAState(uint64_t* aState) const override;
   virtual void Description(nsString& aDescription) override;
   virtual void Value(nsString& aValue) override;
   virtual mozilla::a11y::role NativeRole() override;
   virtual uint64_t State() override;
--- a/accessible/html/HTMLListAccessible.cpp
+++ b/accessible/html/HTMLListAccessible.cpp
@@ -69,25 +69,25 @@ HTMLLIAccessible::NativeRole()
 }
 
 uint64_t
 HTMLLIAccessible::NativeState()
 {
   return HyperTextAccessibleWrap::NativeState() | states::READONLY;
 }
 
-nsIntRect
-HTMLLIAccessible::Bounds() const
+nsRect
+HTMLLIAccessible::BoundsInAppUnits() const
 {
-  nsIntRect rect = AccessibleWrap::Bounds();
-  if (rect.IsEmpty() || !mBullet || mBullet->IsInside())
+  nsRect rect = AccessibleWrap::BoundsInAppUnits();
+  if (rect.IsEmpty() || !mBullet || mBullet->IsInside()) {
     return rect;
+  }
 
-  nsIntRect bulletRect = mBullet->Bounds();
-
+  nsRect bulletRect = mBullet->BoundsInAppUnits();
   // Move x coordinate of list item over to cover bullet as well
   rect.SetLeftEdge(bulletRect.X());
   return rect;
 }
 
 bool
 HTMLLIAccessible::InsertChildAt(uint32_t aIndex, Accessible* aChild)
 {
--- a/accessible/html/HTMLListAccessible.h
+++ b/accessible/html/HTMLListAccessible.h
@@ -46,17 +46,17 @@ public:
   HTMLLIAccessible(nsIContent* aContent, DocAccessible* aDoc);
 
   // nsISupports
   NS_INLINE_DECL_REFCOUNTING_INHERITED(HTMLLIAccessible,
                                        HyperTextAccessibleWrap)
 
   // Accessible
   virtual void Shutdown() override;
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
 
   virtual bool InsertChildAt(uint32_t aIndex, Accessible* aChild) override;
 
   // HTMLLIAccessible
   HTMLListBulletAccessible* Bullet() const { return mBullet; }
   void UpdateBullet(bool aHasBullet);
--- a/accessible/interfaces/gecko/IGeckoCustom.idl
+++ b/accessible/interfaces/gecko/IGeckoCustom.idl
@@ -7,16 +7,20 @@
 import "objidl.idl";
 import "oaidl.idl";
 
 [object, uuid(7510360f-cdae-4de9-88c8-d167eda62afc)]
 interface IGeckoCustom : IUnknown
 {
   [propget] HRESULT ID([out, retval] unsigned __int64* aID);
   [propget] HRESULT anchorCount([out, retval] long* aCount);
+  [propget] HRESULT boundsInCSSPixels([out] long* aX,
+                                      [out] long* aY,
+                                      [out] long* aWidth,
+                                      [out, retval] long* aHeight);
   [propget] HRESULT DOMNodeID([out, retval] BSTR* aID);
   [propget] HRESULT minimumIncrement([out, retval] double* aIncrement);
   [propget] HRESULT mozState([out, retval] unsigned __int64* aState);
 }
 
 
 [
     uuid(55769d85-f830-4d76-9e39-3670914a28f7),
--- a/accessible/interfaces/nsIAccessible.idl
+++ b/accessible/interfaces/nsIAccessible.idl
@@ -15,21 +15,21 @@ interface nsIAccessibleRelation;
 namespace mozilla {
 namespace a11y {
 class Accessible;
 }
 }
 %}
 
 /**
- * A cross-platform interface that supports platform-specific 
+ * A cross-platform interface that supports platform-specific
  * accessibility APIs like MSAA and ATK. Contains the sum of what's needed
  * to support IAccessible as well as ATK's generic accessibility objects.
  * Can also be used by in-process accessibility clients to get information
- * about objects in the accessible tree. The accessible tree is a subset of 
+ * about objects in the accessible tree. The accessible tree is a subset of
  * nodes in the DOM tree -- such as documents, focusable elements and text.
  * Mozilla creates the implementations of nsIAccessible on demand.
  * See http://www.mozilla.org/projects/ui/accessibility for more information.
  */
 [scriptable, builtinclass, uuid(de2869d9-563c-4943-996b-31a4daa4d097)]
 interface nsIAccessible : nsISupports
 {
   /**
@@ -51,17 +51,17 @@ interface nsIAccessible : nsISupports
    * First child in accessible tree
    */
   readonly attribute nsIAccessible firstChild;
 
   /**
    * Last child in accessible tree
    */
   readonly attribute nsIAccessible lastChild;
-  
+
   /**
    * Array of all this element's children.
    */
   readonly attribute nsIArray children;
 
   /**
    * Number of accessible children
    */
@@ -119,17 +119,17 @@ interface nsIAccessible : nsISupports
   /**
    * Accessible description -- long text associated with this node
    */
   readonly attribute AString description;
 
   /**
    * Provides localized string of accesskey name, such as Alt+D.
    * The modifier may be affected by user and platform preferences.
-   * Usually alt+letter, or just the letter alone for menu items. 
+   * Usually alt+letter, or just the letter alone for menu items.
    */
   readonly attribute AString accessKey;
 
   /**
    * Provides localized string of global keyboard accelerator for default
    * action, such as Ctrl+O for Open file
    */
   readonly attribute AString keyboardShortcut;
@@ -221,21 +221,27 @@ interface nsIAccessible : nsISupports
 
   /**
    * Returns multiple accessible relations for this object.
    */
   nsIArray getRelations();
 
   /**
    * Return accessible's x and y coordinates relative to the screen and
-   * accessible's width and height.
+   * accessible's width and height in Dev pixels.
    */
   void getBounds(out long x, out long y, out long width, out long height);
 
   /**
+   * Return accessible's x and y coordinates relative to the screen and
+   * accessible's width and height in CSS pixels.
+   */
+  void getBoundsInCSSPixels(out long aX, out long aY, out long aWidth, out long aHeight);
+
+  /**
    * Add or remove this accessible to the current selection
    */
   void setSelected(in boolean isSelected);
 
   /**
    * Select this accessible node only
    */
   void takeSelection();
--- a/accessible/ipc/ProxyAccessibleShared.h
+++ b/accessible/ipc/ProxyAccessibleShared.h
@@ -248,16 +248,17 @@ double MinValue();
 double MaxValue();
 double Step();
 
 void TakeFocus();
 ProxyAccessible* FocusedChild();
 ProxyAccessible* ChildAtPoint(int32_t aX, int32_t aY,
     Accessible::EWhichChildAtPoint aWhichChild);
 nsIntRect Bounds();
+nsIntRect BoundsInCSSPixels();
 
 void Language(nsString& aLocale);
 void DocType(nsString& aType);
 void Title(nsString& aTitle);
 void URL(nsString& aURL);
 void MimeType(nsString aMime);
 void URLDocTypeMimeType(nsString& aURL, nsString& aDocType,
     nsString& aMimeType);
--- a/accessible/ipc/other/DocAccessibleChild.cpp
+++ b/accessible/ipc/other/DocAccessibleChild.cpp
@@ -1979,16 +1979,40 @@ DocAccessibleChild::RecvExtents(const ui
       *aWidth = screenRect.width;
       *aHeight = screenRect.height;
     }
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
+DocAccessibleChild::RecvExtentsInCSSPixels(const uint64_t& aID,
+                                           int32_t* aX,
+                                           int32_t* aY,
+                                           int32_t* aWidth,
+                                           int32_t* aHeight)
+{
+  *aX = 0;
+  *aY = 0;
+  *aWidth = 0;
+  *aHeight = 0;
+  Accessible* acc = IdToAccessible(aID);
+  if (acc && !acc->IsDefunct()) {
+    nsIntRect screenRect = acc->BoundsInCSSPixels();
+    if (!screenRect.IsEmpty()) {
+      *aX = screenRect.x;
+      *aY = screenRect.y;
+      *aWidth = screenRect.width;
+      *aHeight = screenRect.height;
+    }
+  }
+  return IPC_OK();
+}
+
+mozilla::ipc::IPCResult
 DocAccessibleChild::RecvDOMNodeID(const uint64_t& aID, nsString* aDOMNodeID)
 {
   Accessible* acc = IdToAccessible(aID);
   if (!acc) {
     return IPC_OK();
   }
 
   nsIContent* content = acc->GetContent();
--- a/accessible/ipc/other/DocAccessibleChild.h
+++ b/accessible/ipc/other/DocAccessibleChild.h
@@ -465,16 +465,21 @@ public:
                                                         bool* aOk) override;
 
   virtual mozilla::ipc::IPCResult RecvExtents(const uint64_t& aID,
                                               const bool& aNeedsScreenCoords,
                                               int32_t* aX,
                                               int32_t* aY,
                                               int32_t* aWidth,
                                               int32_t* aHeight) override;
+  virtual mozilla::ipc::IPCResult RecvExtentsInCSSPixels(const uint64_t& aID,
+                                                         int32_t* aX,
+                                                         int32_t* aY,
+                                                         int32_t* aWidth,
+                                                         int32_t* aHeight) override;
   virtual mozilla::ipc::IPCResult RecvDOMNodeID(const uint64_t& aID, nsString* aDOMNodeID) override;
 private:
 
   Accessible* IdToAccessible(const uint64_t& aID) const;
   Accessible* IdToAccessibleLink(const uint64_t& aID) const;
   Accessible* IdToAccessibleSelect(const uint64_t& aID) const;
   HyperTextAccessible* IdToHyperTextAccessible(const uint64_t& aID) const;
   TextLeafAccessible* IdToTextLeafAccessible(const uint64_t& aID) const;
--- a/accessible/ipc/other/PDocAccessible.ipdl
+++ b/accessible/ipc/other/PDocAccessible.ipdl
@@ -260,13 +260,15 @@ child:
   nested(inside_sync) sync MimeType(uint64_t aID) returns(nsString aMime);
   nested(inside_sync) sync URLDocTypeMimeType(uint64_t aID) returns(nsString aURL, nsString aDocType, nsString aMimeType);
 
   nested(inside_sync) sync AccessibleAtPoint(uint64_t aID, int32_t aX, int32_t aY, bool aNeedsScreenCoords, uint32_t aWhich)
     returns(uint64_t aResult, bool aOk);
 
   nested(inside_sync) sync Extents(uint64_t aID, bool aNeedsScreenCoords)
     returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
+  nested(inside_sync) sync ExtentsInCSSPixels(uint64_t aID)
+    returns(int32_t aX, int32_t aY, int32_t aWidth, int32_t aHeight);
   nested(inside_sync) sync DOMNodeID(uint64_t aID) returns(nsString aDOMNodeID);
 };
 
 }
 }
--- a/accessible/ipc/other/ProxyAccessible.cpp
+++ b/accessible/ipc/other/ProxyAccessible.cpp
@@ -1008,16 +1008,26 @@ ProxyAccessible::Bounds()
 {
   nsIntRect rect;
   Unused << mDoc->SendExtents(mID, false,
                               &(rect.x), &(rect.y),
                               &(rect.width), &(rect.height));
   return rect;
 }
 
+nsIntRect
+ProxyAccessible::BoundsInCSSPixels()
+{
+  nsIntRect rect;
+  Unused << mDoc->SendExtentsInCSSPixels(mID,
+                                         &rect.x, &rect.y,
+                                         &rect.width, &rect.height);
+  return rect;
+}
+
 void
 ProxyAccessible::Language(nsString& aLocale)
 {
   Unused << mDoc->SendLanguage(mID, &aLocale);
 }
 
 void
 ProxyAccessible::DocType(nsString& aType)
--- a/accessible/ipc/win/ProxyAccessible.cpp
+++ b/accessible/ipc/win/ProxyAccessible.cpp
@@ -230,16 +230,29 @@ ProxyAccessible::Bounds()
   HRESULT hr = acc->accLocation(&left, &top, &width, &height, kChildIdSelf);
   if (FAILED(hr)) {
     return rect;
   }
   rect.SetRect(left, top, width, height);
   return rect;
 }
 
+nsIntRect
+ProxyAccessible::BoundsInCSSPixels()
+{
+  RefPtr<IGeckoCustom> custom = QueryInterface<IGeckoCustom>(this);
+  if (!custom) {
+    return nsIntRect();
+  }
+
+  nsIntRect rect;
+  HRESULT hr = custom->get_boundsInCSSPixels(&rect.x, &rect.y, &rect.width, &rect.height);
+  return rect;
+}
+
 void
 ProxyAccessible::Language(nsString& aLocale)
 {
   aLocale.Truncate();
 
   RefPtr<IAccessible> acc;
   if (!GetCOMInterface((void**)getter_AddRefs(acc))) {
     return;
--- a/accessible/tests/browser/bounds/browser_test_zoom.js
+++ b/accessible/tests/browser/bounds/browser_test_zoom.js
@@ -1,33 +1,36 @@
 /* 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";
 
 /* import-globals-from ../../mochitest/layout.js */
 
+/* global getContentDPR */
+
 async function getContentBoundsForDOMElm(browser, id) {
   return ContentTask.spawn(browser, id, contentId => {
     this.ok = ok;
     return getBoundsForDOMElm(contentId);
   });
 }
 
 async function testContentBounds(browser, acc) {
   let [expectedX, expectedY, expectedWidth, expectedHeight] =
     await getContentBoundsForDOMElm(browser, getAccessibleDOMNodeID(acc));
 
-  let [x, y, width, height] = getBounds(acc);
+  let contentDPR = await getContentDPR(browser);
+  let [x, y, width, height] = getBounds(acc, contentDPR);
   let prettyAccName = prettyName(acc);
   is(x, expectedX, "Wrong x coordinate of " + prettyAccName);
   is(y, expectedY, "Wrong y coordinate of " + prettyAccName);
   is(width, expectedWidth, "Wrong width of " + prettyAccName);
-  is(height, expectedHeight, "Wrong height of " + prettyAccName);
+  ok(height >= expectedHeight, "Wrong height of " + prettyAccName);
 }
 
 async function runTests(browser, accDoc) {
   loadFrameScripts(browser, { name: "layout.js", dir: MOCHITESTS_DIR });
 
   let p1 = findAccessibleChildByID(accDoc, "p1");
   let p2 = findAccessibleChildByID(accDoc, "p2");
   let imgmap = findAccessibleChildByID(accDoc, "imgmap");
--- a/accessible/tests/browser/bounds/browser_test_zoom_text.js
+++ b/accessible/tests/browser/bounds/browser_test_zoom_text.js
@@ -1,36 +1,39 @@
 /* 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";
 
 /* import-globals-from ../../mochitest/layout.js */
 
+/* global getContentDPR */
+
 async function runTests(browser, accDoc) {
-  function testTextNode(id) {
+  async function testTextNode(id) {
     let hyperTextNode = findAccessibleChildByID(accDoc, id);
     let textNode = hyperTextNode.firstChild;
 
-    let [x, y, width, height] = getBounds(textNode);
+    let contentDPR = await getContentDPR(browser);
+    let [x, y, width, height] = getBounds(textNode, contentDPR);
     testTextBounds(hyperTextNode, 0, -1, [x, y, width, height],
                    COORDTYPE_SCREEN_RELATIVE);
   }
 
   loadFrameScripts(browser, { name: "layout.js", dir: MOCHITESTS_DIR });
 
-  testTextNode("p1");
-  testTextNode("p2");
+  await testTextNode("p1");
+  await testTextNode("p2");
 
   await ContentTask.spawn(browser, {}, () => {
     zoomDocument(document, 2.0);
   });
 
-  testTextNode("p1");
+  await testTextNode("p1");
 
   await ContentTask.spawn(browser, {}, () => {
     zoomDocument(document, 1.0);
   });
 }
 
 /**
  * Test the text range boundary when page is zoomed
--- a/accessible/tests/browser/bounds/head.js
+++ b/accessible/tests/browser/bounds/head.js
@@ -1,16 +1,26 @@
 /* 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";
 
 // Load the shared-head file first.
 /* import-globals-from ../shared-head.js */
+
+/* exported getContentDPR */
+
 Services.scriptloader.loadSubScript(
   "chrome://mochitests/content/browser/accessible/tests/browser/shared-head.js",
   this);
 
 // Loading and common.js from accessible/tests/mochitest/ for all tests, as
 // well as events.js.
 loadScripts({ name: "common.js", dir: MOCHITESTS_DIR },
             { name: "layout.js", dir: MOCHITESTS_DIR }, "events.js");
+
+/**
+ * Get content window DPR that can be different from parent window DPR.
+ */
+async function getContentDPR(browser) {
+  return ContentTask.spawn(browser, null, () => content.window.devicePixelRatio);
+}
--- a/accessible/tests/mochitest/bounds/test_list.html
+++ b/accessible/tests/mochitest/bounds/test_list.html
@@ -41,18 +41,18 @@
       [xLI, yLI, widthLI, heightLI] = getBounds(li);
 
       ok(xLI < xLIElm,
          "Outside list item x=" + xLI + " should be lesser than list item element x=" + xLIElm);
       is(yLI, yLIElm,
          "Outside list item y should match to list item element y");
       ok(widthLI > widthLIElm,
          "Outside list item width=" + widthLI + " should be greater than list item element width=" + widthLIElm);
-      is(heightLI, heightLIElm,
-         "Outside list item height should match to list item element height");
+      ok(heightLI > heightLIElm,
+         "Outside list item height=" + heightLI + " should be greater than list item element height=" + heightLIElm);
 
       SimpleTest.finish();
     }
 
     SimpleTest.waitForExplicitFinish();
     addA11yLoadEvent(doTest);
   </script>
 </head>
--- a/accessible/tests/mochitest/layout.js
+++ b/accessible/tests/mochitest/layout.js
@@ -194,22 +194,35 @@ function getPos(aID) {
   var accessible = getAccessible(aID);
   var x = {}, y = {};
   accessible.getBounds(x, y, {}, {});
   return [x.value, y.value];
 }
 
 /**
  * Return the accessible coordinates and size relative to the screen in device
- * pixels.
+ * pixels. This methods also retrieves coordinates in CSS pixels and ensures that they
+ * match Dev pixels with a given device pixel ratio.
  */
-function getBounds(aID) {
-  var accessible = getAccessible(aID);
-  var x = {}, y = {}, width = {}, height = {};
+function getBounds(aID, aDPR = window.devicePixelRatio) {
+  const accessible = getAccessible(aID);
+  let x = {}, y = {}, width = {}, height = {};
+  let xInCSS = {}, yInCSS = {}, widthInCSS = {}, heightInCSS = {};
   accessible.getBounds(x, y, width, height);
+  accessible.getBoundsInCSSPixels(xInCSS, yInCSS, widthInCSS, heightInCSS);
+
+  ok(Math.trunc(x.value / aDPR) <= xInCSS.value,
+    "X in CSS pixels is calculated correctly");
+  ok(Math.trunc(y.value / aDPR) <= yInCSS.value,
+    "Y in CSS pixels is calculated correctly");
+  ok(Math.trunc(width.value / aDPR) <= widthInCSS.value,
+    "Width in CSS pixels is calculated correctly");
+  ok(Math.trunc(height.value / aDPR) <= heightInCSS.value,
+    "Heights in CSS pixels is calculated correctly");
+
   return [x.value, y.value, width.value, height.value];
 }
 
 function getRangeExtents(aID, aStartOffset, aEndOffset, aCoordOrigin) {
   var hyperText = getAccessible(aID, [nsIAccessibleText]);
   var x = {}, y = {}, width = {}, height = {};
   hyperText.getRangeExtents(aStartOffset, aEndOffset,
                             x, y, width, height, aCoordOrigin);
--- a/accessible/windows/msaa/GeckoCustom.cpp
+++ b/accessible/windows/msaa/GeckoCustom.cpp
@@ -16,16 +16,30 @@ IMPL_IUNKNOWN_QUERY_TAIL_AGGREGATED(mAcc
 HRESULT
 GeckoCustom::get_anchorCount(long* aCount)
 {
   *aCount = mAcc->AnchorCount();
   return S_OK;
 }
 
 HRESULT
+GeckoCustom::get_boundsInCSSPixels(long* aX, long* aY, long* aWidth, long* aHeight)
+{
+  nsIntRect bounds = mAcc->BoundsInCSSPixels();
+  if (!bounds.IsEmpty()) {
+    *aX = bounds.X();
+    *aY = bounds.Y();
+    *aWidth = bounds.Width();
+    *aHeight = bounds.Height();
+  }
+
+  return S_OK;
+}
+
+HRESULT
 GeckoCustom::get_DOMNodeID(BSTR* aID)
 {
   nsIContent* content = mAcc->GetContent();
   if (!content) {
     return S_OK;
   }
 
   nsAtom* id = content->GetID();
--- a/accessible/windows/msaa/GeckoCustom.h
+++ b/accessible/windows/msaa/GeckoCustom.h
@@ -22,16 +22,17 @@ class GeckoCustom final : public IGeckoC
 {
 public:
   explicit GeckoCustom(AccessibleWrap* aAcc) : mAcc(aAcc) {}
 
   // IUnknown
   DECL_IUNKNOWN
 
   virtual STDMETHODIMP get_anchorCount(long* aCount);
+  virtual STDMETHODIMP get_boundsInCSSPixels(long* aX, long* aY, long* aWidth, long* aHeight);
   virtual STDMETHODIMP get_DOMNodeID(BSTR* aID);
   virtual STDMETHODIMP get_ID(uint64_t* aID);
   virtual STDMETHODIMP get_minimumIncrement(double* aIncrement);
   virtual STDMETHODIMP get_mozState(uint64_t* aState);
 
 private:
   GeckoCustom() = delete;
   GeckoCustom& operator =(const GeckoCustom&) = delete;
--- a/accessible/xpcom/xpcAccessible.cpp
+++ b/accessible/xpcom/xpcAccessible.cpp
@@ -452,16 +452,44 @@ xpcAccessible::GetBounds(int32_t* aX, in
     rect = IntlGeneric().AsProxy()->Bounds();
   }
 
   rect.GetRect(aX, aY, aWidth, aHeight);
   return NS_OK;
 }
 
 NS_IMETHODIMP
+xpcAccessible::GetBoundsInCSSPixels(int32_t* aX, int32_t* aY,
+                                  int32_t* aWidth, int32_t* aHeight)
+{
+  NS_ENSURE_ARG_POINTER(aX);
+  *aX = 0;
+  NS_ENSURE_ARG_POINTER(aY);
+  *aY = 0;
+  NS_ENSURE_ARG_POINTER(aWidth);
+  *aWidth = 0;
+  NS_ENSURE_ARG_POINTER(aHeight);
+  *aHeight = 0;
+
+  if (IntlGeneric().IsNull()) {
+    return NS_ERROR_FAILURE;
+  }
+
+  nsIntRect rect;
+  if (Accessible* acc = IntlGeneric().AsAccessible()) {
+    rect = acc->BoundsInCSSPixels();
+  } else {
+    rect = IntlGeneric().AsProxy()->BoundsInCSSPixels();
+  }
+
+  rect.GetRect(aX, aY, aWidth, aHeight);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 xpcAccessible::GroupPosition(int32_t* aGroupLevel,
                              int32_t* aSimilarItemsInGroup,
                              int32_t* aPositionInGroup)
 {
   NS_ENSURE_ARG_POINTER(aGroupLevel);
   *aGroupLevel = 0;
 
   NS_ENSURE_ARG_POINTER(aSimilarItemsInGroup);
--- a/accessible/xpcom/xpcAccessible.h
+++ b/accessible/xpcom/xpcAccessible.h
@@ -55,16 +55,18 @@ public:
 
   NS_IMETHOD GetAccessKey(nsAString& aAccessKey) final;
   NS_IMETHOD GetKeyboardShortcut(nsAString& aKeyBinding) final;
 
   NS_IMETHOD GetAttributes(nsIPersistentProperties** aAttributes)
     final;
   NS_IMETHOD GetBounds(int32_t* aX, int32_t* aY,
                        int32_t* aWidth, int32_t* aHeight) final;
+  NS_IMETHOD GetBoundsInCSSPixels(int32_t* aX, int32_t* aY,
+                                  int32_t* aWidth, int32_t* aHeight) final;
   NS_IMETHOD GroupPosition(int32_t* aGroupLevel, int32_t* aSimilarItemsInGroup,
                            int32_t* aPositionInGroup) final;
   NS_IMETHOD GetRelationByType(uint32_t aType,
                                nsIAccessibleRelation** aRelation)
     final;
   NS_IMETHOD GetRelations(nsIArray** aRelations) final;
 
   NS_IMETHOD GetFocusedChild(nsIAccessible** aChild) final;
--- a/accessible/xul/XULTreeAccessible.cpp
+++ b/accessible/xul/XULTreeAccessible.cpp
@@ -718,47 +718,56 @@ NS_IMPL_ISUPPORTS_CYCLE_COLLECTION_INHER
 
 Accessible*
 XULTreeItemAccessibleBase::FocusedChild()
 {
   return FocusMgr()->FocusedAccessible() == this ? this : nullptr;
 }
 
 nsIntRect
-XULTreeItemAccessibleBase::Bounds() const
+XULTreeItemAccessibleBase::BoundsInCSSPixels() const
 {
   // Get x coordinate and width from treechildren element, get y coordinate and
   // height from tree cell.
 
   nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
-  if (!boxObj)
+  if (!boxObj) {
     return nsIntRect();
+  }
 
   nsCOMPtr<nsITreeColumn> column = nsCoreUtils::GetFirstSensibleColumn(mTree);
 
   int32_t x = 0, y = 0, width = 0, height = 0;
   nsresult rv = mTree->GetCoordsForCellItem(mRow, column, EmptyString(),
                                             &x, &y, &width, &height);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return nsIntRect();
+  }
 
   boxObj->GetWidth(&width);
 
   int32_t tcX = 0, tcY = 0;
   boxObj->GetScreenX(&tcX);
   boxObj->GetScreenY(&tcY);
 
   x = tcX;
   y += tcY;
 
+  return nsIntRect(x, y, width, height);
+}
+
+nsRect
+XULTreeItemAccessibleBase::BoundsInAppUnits() const
+{
+  nsIntRect bounds = BoundsInCSSPixels();
   nsPresContext* presContext = mDoc->PresContext();
-  return nsIntRect(presContext->CSSPixelsToDevPixels(x),
-                   presContext->CSSPixelsToDevPixels(y),
-                   presContext->CSSPixelsToDevPixels(width),
-                   presContext->CSSPixelsToDevPixels(height));
+  return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
+                presContext->CSSPixelsToAppUnits(bounds.Y()),
+                presContext->CSSPixelsToAppUnits(bounds.Width()),
+                presContext->CSSPixelsToAppUnits(bounds.Height()));
 }
 
 void
 XULTreeItemAccessibleBase::SetSelected(bool aSelect)
 {
   nsCOMPtr<nsITreeSelection> selection;
   mTreeView->GetSelection(getter_AddRefs(selection));
   if (selection) {
--- a/accessible/xul/XULTreeAccessible.h
+++ b/accessible/xul/XULTreeAccessible.h
@@ -141,17 +141,18 @@ public:
 
   // nsISupports and cycle collection
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeItemAccessibleBase,
                                            AccessibleWrap)
 
   // Accessible
   virtual void Shutdown() override;
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
+  virtual nsIntRect BoundsInCSSPixels() const override;
   virtual GroupPos GroupPosition() override;
   virtual uint64_t NativeState() override;
   virtual uint64_t NativeInteractiveState() const override;
   virtual int32_t IndexInParent() const override;
   virtual Relation RelationByType(RelationType aType) override;
   virtual Accessible* FocusedChild() override;
   virtual void SetSelected(bool aSelect) override;
   virtual void TakeFocus() override;
--- a/accessible/xul/XULTreeGridAccessible.cpp
+++ b/accessible/xul/XULTreeGridAccessible.cpp
@@ -490,42 +490,51 @@ XULTreeGridCellAccessible::Name(nsString
   // GetCellValue for that cell would return "starred" or "flagged" for example.
   if (aName.IsEmpty())
     mTreeView->GetCellValue(mRow, mColumn, aName);
 
   return eNameOK;
 }
 
 nsIntRect
-XULTreeGridCellAccessible::Bounds() const
+XULTreeGridCellAccessible::BoundsInCSSPixels() const
 {
   // Get bounds for tree cell and add x and y of treechildren element to
   // x and y of the cell.
   nsCOMPtr<nsIBoxObject> boxObj = nsCoreUtils::GetTreeBodyBoxObject(mTree);
-  if (!boxObj)
+  if (!boxObj) {
     return nsIntRect();
+  }
 
   int32_t x = 0, y = 0, width = 0, height = 0;
   nsresult rv = mTree->GetCoordsForCellItem(mRow, mColumn,
                                             NS_LITERAL_STRING("cell"),
                                             &x, &y, &width, &height);
-  if (NS_FAILED(rv))
+  if (NS_FAILED(rv)) {
     return nsIntRect();
+  }
 
   int32_t tcX = 0, tcY = 0;
   boxObj->GetScreenX(&tcX);
   boxObj->GetScreenY(&tcY);
   x += tcX;
   y += tcY;
 
+  return nsIntRect(x, y, width, height);
+}
+
+nsRect
+XULTreeGridCellAccessible::BoundsInAppUnits() const
+{
+  nsIntRect bounds = BoundsInCSSPixels();
   nsPresContext* presContext = mDoc->PresContext();
-  return nsIntRect(presContext->CSSPixelsToDevPixels(x),
-                   presContext->CSSPixelsToDevPixels(y),
-                   presContext->CSSPixelsToDevPixels(width),
-                   presContext->CSSPixelsToDevPixels(height));
+  return nsRect(presContext->CSSPixelsToAppUnits(bounds.X()),
+                presContext->CSSPixelsToAppUnits(bounds.Y()),
+                presContext->CSSPixelsToAppUnits(bounds.Width()),
+                presContext->CSSPixelsToAppUnits(bounds.Height()));
 }
 
 uint8_t
 XULTreeGridCellAccessible::ActionCount()
 {
   bool isCycler = false;
   mColumn->GetCycler(&isCycler);
   if (isCycler)
--- a/accessible/xul/XULTreeGridAccessible.h
+++ b/accessible/xul/XULTreeGridAccessible.h
@@ -121,17 +121,18 @@ public:
   // nsISupports
   NS_DECL_ISUPPORTS_INHERITED
   NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XULTreeGridCellAccessible,
                                            LeafAccessible)
 
   // Accessible
   virtual void Shutdown() override;
   virtual TableCellAccessible* AsTableCell() override { return this; }
-  virtual nsIntRect Bounds() const override;
+  virtual nsRect BoundsInAppUnits() const override;
+  virtual nsIntRect BoundsInCSSPixels() const override;
   virtual ENameValueFlag Name(nsString& aName) override;
   virtual Accessible* FocusedChild() override;
   virtual already_AddRefed<nsIPersistentProperties> NativeAttributes() override;
   virtual int32_t IndexInParent() const override;
   virtual Relation RelationByType(RelationType aType) override;
   virtual a11y::role NativeRole() override;
   virtual uint64_t NativeState() override;
   virtual uint64_t NativeInteractiveState() const override;
--- a/ipc/ipdl/sync-messages.ini
+++ b/ipc/ipdl/sync-messages.ini
@@ -589,16 +589,19 @@ platform = notwin
 description =
 platform = notwin
 [PDocAccessible::AccessibleAtPoint]
 description =
 platform = notwin
 [PDocAccessible::Extents]
 description =
 platform = notwin
+[PDocAccessible::ExtentsInCSSPixels]
+description =
+platform = notwin
 [PDocAccessible::DOMNodeID]
 description =
 platform = notwin
 [PDocAccessible::GetWindowedPluginIAccessible]
 description =
 platform = win
 [PDocAccessible::SyncTextChangeEvent]
 description =