Bug 957668. Add and implement plumbing for a display port margins api. r=kats
authorTimothy Nikkel <tnikkel@gmail.com>
Wed, 26 Mar 2014 21:46:23 -0400
changeset 175590 8b0da7b8afc61140412d00ffa14c98c5f502f5c7
parent 175589 40651e2d3cbc94cfef28e23e1d0e63bf94c37b4a
child 175591 4a75c46797d127d92ae8bbe0293d7219239c03a6
push id26494
push usercbook@mozilla.com
push dateThu, 27 Mar 2014 13:09:48 +0000
treeherdermozilla-central@d2ecc6d31622 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs957668
milestone31.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 957668. Add and implement plumbing for a display port margins api. r=kats
content/base/src/nsGkAtomList.h
dom/base/nsDOMWindowUtils.cpp
dom/interfaces/base/nsIDOMWindowUtils.idl
layout/base/nsLayoutUtils.cpp
layout/base/nsLayoutUtils.h
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -2017,16 +2017,18 @@ GK_ATOM(el, "el")
 GK_ATOM(TypingTxnName, "Typing")
 GK_ATOM(IMETxnName, "IME")
 GK_ATOM(DeleteTxnName, "Deleting")
 
 // IPC stuff
 GK_ATOM(Remote, "remote")
 GK_ATOM(RemoteId, "_remote_id")
 GK_ATOM(DisplayPort, "_displayport")
+GK_ATOM(DisplayPortMargins, "_displayportmargins")
+GK_ATOM(DisplayPortBase, "_displayportbase")
 GK_ATOM(CriticalDisplayPort, "_critical_displayport")
 
 // Names for system metrics
 GK_ATOM(color_picker_available, "color-picker-available")
 GK_ATOM(scrollbar_start_backward, "scrollbar-start-backward")
 GK_ATOM(scrollbar_start_forward, "scrollbar-start-forward")
 GK_ATOM(scrollbar_end_backward, "scrollbar-end-backward")
 GK_ATOM(scrollbar_end_forward, "scrollbar-end-forward")
--- a/dom/base/nsDOMWindowUtils.cpp
+++ b/dom/base/nsDOMWindowUtils.cpp
@@ -448,16 +448,119 @@ nsDOMWindowUtils::SetDisplayPortForEleme
       }
     }
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsDOMWindowUtils::SetDisplayPortMarginsForElement(float aLeftMargin,
+                                                  float aTopMargin,
+                                                  float aRightMargin,
+                                                  float aBottomMargin,
+                                                  uint32_t aAlignment,
+                                                  nsIDOMElement* aElement,
+                                                  uint32_t aPriority)
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsIPresShell* presShell = GetPresShell();
+  if (!presShell) {
+    return NS_ERROR_FAILURE;
+  }
+
+  // Note order change of arguments between our function signature and
+  // LayerMargin constructor.
+  LayerMargin displayportMargins(aTopMargin,
+                                 aRightMargin,
+                                 aBottomMargin,
+                                 aLeftMargin);
+
+  if (!aElement) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
+
+  if (!content) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (content->GetCurrentDoc() != presShell->GetDocument()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  DisplayPortMarginsPropertyData* currentData =
+    static_cast<DisplayPortMarginsPropertyData*>(content->GetProperty(nsGkAtoms::DisplayPortMargins));
+  if (currentData && currentData->mPriority > aPriority) {
+    return NS_OK;
+  }
+
+  content->SetProperty(nsGkAtoms::DisplayPortMargins,
+                       new DisplayPortMarginsPropertyData(displayportMargins, aAlignment, aPriority),
+                       nsINode::DeleteProperty<DisplayPortMarginsPropertyData>);
+
+  nsIFrame* rootScrollFrame = presShell->GetRootScrollFrame();
+  if (rootScrollFrame) {
+    if (content == rootScrollFrame->GetContent()) {
+      // We are setting a root displayport for a document.
+      // The pres shell needs a special flag set.
+      presShell->SetIgnoreViewportScrolling(true);
+    }
+  }
+
+  nsIFrame* rootFrame = presShell->FrameManager()->GetRootFrame();
+  if (rootFrame) {
+    rootFrame->SchedulePaint();
+  }
+
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsDOMWindowUtils::SetDisplayPortBaseForElement(int32_t aX,
+                                               int32_t aY,
+                                               int32_t aWidth,
+                                               int32_t aHeight,
+                                               nsIDOMElement* aElement)
+{
+  if (!nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  nsIPresShell* presShell = GetPresShell();
+  if (!presShell) {
+    return NS_ERROR_FAILURE;
+  }
+
+  if (!aElement) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
+
+  if (!content) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  if (content->GetCurrentDoc() != presShell->GetDocument()) {
+    return NS_ERROR_INVALID_ARG;
+  }
+
+  content->SetProperty(nsGkAtoms::DisplayPortBase, new nsRect(aX, aY, aWidth, aHeight),
+                       nsINode::DeleteProperty<nsRect>);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsDOMWindowUtils::SetCriticalDisplayPortForElement(float aXPx, float aYPx,
                                                    float aWidthPx, float aHeightPx,
                                                    nsIDOMElement* aElement)
 {
   if (!nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
--- a/dom/interfaces/base/nsIDOMWindowUtils.idl
+++ b/dom/interfaces/base/nsIDOMWindowUtils.idl
@@ -38,17 +38,17 @@ interface nsIDOMFile;
 interface nsIFile;
 interface nsIDOMTouch;
 interface nsIDOMClientRect;
 interface nsIURI;
 interface nsIDOMEventTarget;
 interface nsIRunnable;
 interface nsICompositionStringSynthesizer;
 
-[scriptable, uuid(ef70a299-033c-4adc-b214-6649aed9d828)]
+[scriptable, uuid(f3148b3e-6db8-4a49-aa5c-de726449054d)]
 interface nsIDOMWindowUtils : nsISupports {
 
   /**
    * Image animation mode of the window. When this attribute's value
    * is changed, the implementation should set all images in the window
    * to the given value. That is, when set to kDontAnimMode, all images
    * will stop animating. The attribute's value must be one of the
    * animationMode values from imgIContainer.
@@ -147,16 +147,48 @@ interface nsIDOMWindowUtils : nsISupport
    * aPriority is recorded along with the displayport rectangle. If this
    * method is called with a lower priority than the current priority, the
    * call is ignored.
    */
   void setDisplayPortForElement(in float aXPx, in float aYPx,
                                 in float aWidthPx, in float aHeightPx,
                                 in nsIDOMElement aElement,
                                 in uint32_t aPriority);
+  /**
+   * An alternate way to represent a displayport rect as a set of margins and a
+   * base rect to apply those margins to. A consumer of pixels may ask for as
+   * many extra pixels as it would like in each direction. Layout then sets
+   * the base rect to the "visible rect" of the element, which is just the
+   * subrect of the element that is drawn (it does not take in account content
+   * covering the element).
+   *
+   * If both a displayport rect and displayport margins with corresponding base
+   * rect are set with the same priority then the margins will take precendence.
+   *
+   * Specifying an alignment value will ensure that after the base rect has
+   * been expanded by the displayport margins, it will be further expanded so
+   * that each edge is located at a multiple of the "alignment" value.
+   *
+   * Note that both the margin values and alignment are treated as values in
+   * LayerPixels. Refer to layout/base/Units.h for a description of this unit.
+   * The base rect values are in app units.
+   */
+  void setDisplayPortMarginsForElement(in float aLeftMargin,
+                                       in float aTopMargin,
+                                       in float aRightMargin,
+                                       in float aBottomMargin,
+                                       in uint32_t aAlignment,
+                                       in nsIDOMElement aElement,
+                                       in uint32_t aPriority);
+
+  void setDisplayPortBaseForElement(in int32_t aX,
+                                    in int32_t aY,
+                                    in int32_t aWidth,
+                                    in int32_t aHeight,
+                                    in nsIDOMElement aElement);
 
   /**
    * When a display port is set, this allows a sub-section of that
    * display port to be marked as 'critical'. In this scenario, the
    * area outside of this rectangle may be rendered at a lower
    * detail (for example, by reducing its resolution), or not rendered
    * at all under some circumstances.
    * This call will have no effect if a display port has not been set.
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -651,24 +651,117 @@ nsLayoutUtils::FindScrollableFrameFor(Vi
     scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame();
   }
   return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr;
 }
 
 bool
 nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult)
 {
-  void* property = aContent->GetProperty(nsGkAtoms::DisplayPort);
-  if (!property) {
+  DisplayPortPropertyData* rectData =
+    static_cast<DisplayPortPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPort));
+  DisplayPortMarginsPropertyData* marginsData =
+    static_cast<DisplayPortMarginsPropertyData*>(aContent->GetProperty(nsGkAtoms::DisplayPortMargins));
+  if (!rectData && !marginsData) {
     return false;
   }
 
   if (aResult) {
-    *aResult = (static_cast<DisplayPortPropertyData*>(property))->mRect;
-  }
+    if (rectData && marginsData) {
+      // choose margins if equal priority
+      if (rectData->mPriority > marginsData->mPriority) {
+        marginsData = nullptr;
+      } else {
+        rectData = nullptr;
+      }
+    }
+
+    if (rectData) {
+      *aResult = rectData->mRect;
+    } else {
+      nsRect* baseData =
+        static_cast<nsRect*>(aContent->GetProperty(nsGkAtoms::DisplayPortBase));
+      nsRect base;
+      if (baseData) {
+        base = *baseData;
+      }
+
+      nsIFrame* frame = aContent->GetPrimaryFrame();
+      bool isRoot = false;
+      if (aContent->OwnerDoc()->GetRootElement() == aContent) {
+        // We want the scroll frame, the root scroll frame differs from all
+        // others in that the primary frame is not the scroll frame.
+        frame = frame->PresContext()->PresShell()->GetRootScrollFrame();
+        isRoot = true;
+      }
+      if (frame) {
+        // first convert the base rect to layer pixels
+        nsPresContext* presContext = frame->PresContext();
+        int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
+        gfxSize res = presContext->PresShell()->GetCumulativeResolution();
+        gfxSize parentRes = res;
+        if (isRoot) {
+          gfxSize localRes = presContext->PresShell()->GetResolution();
+          parentRes.width /= localRes.width;
+          parentRes.height /= localRes.height;
+        }
+        LayerRect rect;
+        rect.x = parentRes.width * NSAppUnitsToFloatPixels(base.x, auPerDevPixel);
+        rect.y = parentRes.height * NSAppUnitsToFloatPixels(base.y, auPerDevPixel);
+        rect.width =
+          parentRes.width * NSAppUnitsToFloatPixels(base.width, auPerDevPixel);
+        rect.height =
+          parentRes.height * NSAppUnitsToFloatPixels(base.height, auPerDevPixel);
+
+        rect.Inflate(marginsData->mMargins);
+
+        nsIScrollableFrame* scrollableFrame = frame->GetScrollTargetFrame();
+        nsPoint scrollPos(
+          scrollableFrame ? scrollableFrame->GetScrollPosition() : nsPoint(0,0));
+        if (marginsData->mAlignment > 0) {
+          LayerPoint scrollPosLayer(
+            res.width * NSAppUnitsToFloatPixels(scrollPos.x, auPerDevPixel),
+            res.height * NSAppUnitsToFloatPixels(scrollPos.y, auPerDevPixel));
+          rect += scrollPosLayer;
+
+          // Inflate the rectangle by 1 so that we always push to the next tile
+          // boundary. This is desirable to stop from having a rectangle with a
+          // moving origin occasionally being smaller when it coincidentally lines
+          // up to tile boundaries.
+          rect.Inflate(1);
+
+          float left =
+            marginsData->mAlignment * floor(rect.x / marginsData->mAlignment);
+          float top =
+            marginsData->mAlignment * floor(rect.y / marginsData->mAlignment);
+          float right =
+            marginsData->mAlignment * ceil(rect.XMost() / marginsData->mAlignment);
+          float bottom =
+            marginsData->mAlignment * ceil(rect.YMost() / marginsData->mAlignment);
+          rect = LayerRect(left, top, right - left, bottom - top);
+          rect -= scrollPosLayer;
+        }
+
+        nsRect result;
+        result.x = NSFloatPixelsToAppUnits(rect.x / res.width, auPerDevPixel);
+        result.y = NSFloatPixelsToAppUnits(rect.y / res.height, auPerDevPixel);
+        result.width =
+          NSFloatPixelsToAppUnits(rect.width / res.width, auPerDevPixel);
+        result.height =
+          NSFloatPixelsToAppUnits(rect.height / res.height, auPerDevPixel);
+
+        // Finally, clamp the display port to the expanded scrollable rect.
+        nsRect expandedScrollableRect = CalculateExpandedScrollableRect(frame);
+        result = expandedScrollableRect.Intersect(result + scrollPos) - scrollPos;
+
+        *aResult = result;
+      }
+    }
+  }
+
   return true;
 }
 
 bool
 nsLayoutUtils::GetCriticalDisplayPort(nsIContent* aContent, nsRect* aResult)
 {
   void* property = aContent->GetProperty(nsGkAtoms::CriticalDisplayPort);
   if (!property) {
--- a/layout/base/nsLayoutUtils.h
+++ b/layout/base/nsLayoutUtils.h
@@ -80,16 +80,28 @@ struct DisplayPortPropertyData {
   DisplayPortPropertyData(const nsRect& aRect, uint32_t aPriority)
     : mRect(aRect)
     , mPriority(aPriority)
   {}
   nsRect mRect;
   uint32_t mPriority;
 };
 
+struct DisplayPortMarginsPropertyData {
+  DisplayPortMarginsPropertyData(const LayerMargin& aMargins,
+                                 uint32_t aAlignment, uint32_t aPriority)
+    : mMargins(aMargins)
+    , mAlignment(aAlignment)
+    , mPriority(aPriority)
+  {}
+  LayerMargin mMargins;
+  uint32_t mAlignment;
+  uint32_t mPriority;
+};
+
 template <class AnimationsOrTransitions>
 extern AnimationsOrTransitions* HasAnimationOrTransition(nsIContent* aContent,
                                                          nsIAtom* aAnimationProperty,
                                                          nsCSSProperty aProperty);
 
 } // namespace mozilla
 
 /**