Bug 684759 - Part 3a - Sort preserve-3d layers using their z depth at points where they intersect in 2d space. r=roc
authorMatt Woodrow <mwoodrow@mozilla.com>
Fri, 07 Oct 2011 10:23:18 +1300
changeset 78734 39d6e79b44ad645d6050d79877aca414b40dbeb8
parent 78733 3ef0db7dc18dd42295595630c7536a8c979fbc85
child 78735 c17d89a1c4aa80f55c27eab247bc81eec129fb54
push idunknown
push userunknown
push dateunknown
reviewersroc
bugs684759
milestone10.0a1
Bug 684759 - Part 3a - Sort preserve-3d layers using their z depth at points where they intersect in 2d space. r=roc
gfx/layers/LayerSorter.cpp
gfx/layers/LayerSorter.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/Makefile.in
layout/base/nsDisplayList.cpp
layout/base/nsDisplayList.h
new file mode 100644
--- /dev/null
+++ b/gfx/layers/LayerSorter.cpp
@@ -0,0 +1,258 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "LayerSorter.h"
+#include "DirectedGraph.h"
+#include "limits.h"
+
+namespace mozilla {
+namespace layers {
+
+enum LayerSortOrder {
+  Undefined,
+  ABeforeB,
+  BBeforeA,
+};
+
+/**
+ * Recover the z component from a 2d transformed point by finding the intersection
+ * of a line through the point in the z direction and the transformed plane.
+ *
+ * We want to solve:
+ *
+ * point = normal . (p0 - l0) / normal . l
+ */
+static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint)
+{
+    const gfxPoint3D l(0, 0, 1);
+    gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0);
+    gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0));
+    gfxPoint3D normal = aTransform.GetNormalVector();
+
+    gfxFloat n = normal.DotProduct(p0 - l0); 
+    gfxFloat d = normal.DotProduct(l);
+
+    if (!d) {
+        return 0;
+    }
+
+    return n/d;
+}
+
+/**
+ * Determine if this transform layer should be drawn before another when they 
+ * are both preserve-3d children.
+ *
+ * We want to find the relative z depths of the 2 layers at points where they
+ * intersect when projected onto the 2d screen plane.
+ *
+ * If the ordering is consistent at all intersection points, then we have
+ * a definitive order, otherwise the 2 layers must actually intersect in 3d
+ * space, and we just order these arbitrarily.
+ */
+static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
+  gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
+  gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
+
+  gfx3DMatrix ourTransform = aOne->GetTransform();
+  gfx3DMatrix otherTransform = aTwo->GetTransform();
+
+  // Transform both rectangles and project into 2d space.
+  gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
+  gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
+
+  // Make a list of all points that are within the other rect.
+  nsTArray<gfxPoint> points;
+  for (PRUint32 i=0; i<4; i++) {
+    if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
+      points.AppendElement(otherTransformedRect.mPoints[i]);
+    }
+    if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
+      points.AppendElement(ourTransformedRect.mPoints[i]);
+    }
+  }
+
+  // No intersections, no defined order between these layers.
+  if (points.IsEmpty()) {
+    return Undefined;
+  }
+
+  // Find the relative Z depths of each intersection point and check that the layers are in the same order.
+  bool drawBefore = false;
+  for (PRUint32 i = 0; i < points.Length(); i++) {
+    bool temp = RecoverZDepth(ourTransform, points.ElementAt(i)) <= RecoverZDepth(otherTransform, points.ElementAt(i));
+    if (i == 0) {
+      drawBefore = temp; 
+    } else if (drawBefore != temp) {
+      // Mixed ordering means an intersection in 3d space that we can't resolve without plane splitting
+      // or depth buffering. Store this as having no defined order for now.
+      return Undefined;
+    }
+  }
+  if (drawBefore) {
+    return ABeforeB;
+  }
+  return BBeforeA;
+}
+
+#ifdef DEBUG
+static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0;
+
+static void DumpLayerList(nsTArray<Layer*>& aLayers)
+{
+  for (PRUint32 i = 0; i < aLayers.Length(); i++) {
+    fprintf(stderr, "%p, ", aLayers.ElementAt(i));
+  }
+  fprintf(stderr, "\n");
+}
+
+static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
+{
+  nsTArray<DirectedGraph<Layer*>::Edge> edges = aGraph.GetEdgeList();
+  
+  for (PRUint32 i = 0; i < edges.Length(); i++) {
+    fprintf(stderr, "From: %p, To: %p\n", edges.ElementAt(i).mFrom, edges.ElementAt(i).mTo);
+  }
+}
+#endif
+
+// The maximum number of layers that we will attempt to sort. Anything
+// greater than this will be left unsorted. We should consider enabling
+// depth buffering for the scene in this case.
+#define MAX_SORTABLE_LAYERS 100
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
+{
+  PRUint32 nodeCount = aLayers.Length();
+  if (nodeCount > MAX_SORTABLE_LAYERS) {
+    return;
+  }
+  DirectedGraph<Layer*> graph;
+
+#ifdef DEBUG
+  if (gDumpLayerSortList) {
+    fprintf(stderr, " --- Layers before sorting: --- \n");
+    DumpLayerList(aLayers);
+  }
+#endif
+
+  // Iterate layers and determine edges.
+  for (PRUint32 i = 0; i < nodeCount; i++) {
+    for (PRUint32 j = i + 1; j < nodeCount; j++) {
+      Layer* a = aLayers.ElementAt(i);
+      Layer* b = aLayers.ElementAt(j);
+      LayerSortOrder order = CompareDepth(a, b);
+      if (order == ABeforeB) {
+        graph.AddEdge(a, b);
+      } else if (order == BBeforeA) {
+        graph.AddEdge(b, a);
+      }
+    }
+  }
+
+#ifdef DEBUG
+  if (gDumpLayerSortList) {
+    fprintf(stderr, " --- Edge List: --- \n");
+    DumpEdgeList(graph);
+  }
+#endif
+
+  // Build a new array using the graph.
+  nsTArray<Layer*> noIncoming;
+  nsTArray<Layer*> sortedList;
+
+  // Make a list of all layers with no incoming edges.
+  noIncoming.AppendElements(aLayers);
+  const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
+  for (PRUint32 i = 0; i < edges.Length(); i++) {
+    noIncoming.RemoveElement(edges.ElementAt(i).mTo);
+  }
+
+  // Move each item without incoming edges into the sorted list,
+  // and remove edges from it.
+  while (!noIncoming.IsEmpty()) {
+    PRUint32 last = noIncoming.Length() - 1;
+
+    Layer* layer = noIncoming.ElementAt(last);
+
+    noIncoming.RemoveElementAt(last);
+    sortedList.AppendElement(layer);
+
+    nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
+    graph.GetEdgesFrom(layer, outgoing);
+    for (PRUint32 i = 0; i < outgoing.Length(); i++) {
+      DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
+      graph.RemoveEdge(edge);
+      if (!graph.NumEdgesTo(edge.mTo)) {
+        // If this node also has no edges now, add it to the list
+        noIncoming.AppendElement(edge.mTo);
+      }
+    }
+
+    // If there are no nodes without incoming edges, but there
+    // are still edges, then we have a cycle.
+    if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
+      // Find the node with the least incoming edges.
+      PRUint32 minEdges = UINT_MAX;
+      Layer* minNode = nsnull;
+      for (PRUint32 i = 0; i < aLayers.Length(); i++) {
+        PRUint32 edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
+        if (edgeCount && edgeCount < minEdges) {
+          minEdges = edgeCount;
+          minNode = aLayers.ElementAt(i);
+        }
+      }
+
+      // Remove all of them!
+      graph.RemoveEdgesTo(minNode);
+      noIncoming.AppendElement(minNode);
+    }
+  }
+  NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
+#ifdef DEBUG
+  if (gDumpLayerSortList) {
+    fprintf(stderr, " --- Layers after sorting: --- \n");
+    DumpLayerList(sortedList);
+  }
+#endif
+
+  aLayers.Clear();
+  aLayers.AppendElements(sortedList);
+}
+
+}
+}
new file mode 100644
--- /dev/null
+++ b/gfx/layers/LayerSorter.h
@@ -0,0 +1,50 @@
+/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
+ * ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is Mozilla Corporation code.
+ *
+ * The Initial Developer of the Original Code is Mozilla Foundation.
+ * Portions created by the Initial Developer are Copyright (C) 2011
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Matt Woodrow <mwoodrow@mozilla.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef GFX_LAYERSORTER_H
+#define GFX_LAYERSORTER_H
+
+#include "Layers.h"
+
+namespace mozilla {
+namespace layers {
+
+void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers);
+
+}
+}
+#endif /* GFX_LAYERSORTER_H */
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -43,16 +43,17 @@
 
 #include "ImageLayers.h"
 #include "Layers.h"
 #include "gfxPlatform.h"
 #include "ReadbackLayer.h"
 #include "gfxUtils.h"
 #include "nsPrintfCString.h"
 #include "mozilla/Util.h"
+#include "LayerSorter.h"
 
 using namespace mozilla::layers;
 using namespace mozilla::gfx;
 
 typedef FrameMetrics::ViewID ViewID;
 const ViewID FrameMetrics::NULL_SCROLL_ID = 0;
 const ViewID FrameMetrics::ROOT_SCROLL_ID = 1;
 const ViewID FrameMetrics::START_SCROLL_ID = 2;
@@ -411,16 +412,39 @@ ContainerLayer::HasMultipleChildren()
     if (count > 1)
       return PR_TRUE;
   }
 
   return PR_FALSE;
 }
 
 void
+ContainerLayer::SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray)
+{
+  nsAutoTArray<Layer*, 10> toSort;
+
+  for (Layer* l = GetFirstChild(); l; l = l->GetNextSibling()) {
+    ContainerLayer* container = l->AsContainerLayer();
+    if (container && container->GetContentFlags() & CONTENT_PRESERVE_3D) {
+      toSort.AppendElement(l);
+    } else {
+      if (toSort.Length() > 0) {
+        SortLayersBy3DZOrder(toSort);
+        aArray.MoveElementsFrom(toSort);
+      }
+      aArray.AppendElement(l);
+    }
+  }
+  if (toSort.Length() > 0) {
+    SortLayersBy3DZOrder(toSort);
+    aArray.MoveElementsFrom(toSort);
+  }
+}
+
+void
 ContainerLayer::DefaultComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface)
 {
   gfxMatrix residual;
   gfx3DMatrix idealTransform = GetLocalTransform()*aTransformToSurface;
   mEffectiveTransform = SnapTransform(idealTransform, gfxRect(0, 0, 0, 0), &residual);
 
   bool useIntermediateSurface;
   float opacity = GetEffectiveOpacity();
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -43,16 +43,17 @@
 #include "nsRegion.h"
 #include "nsPoint.h"
 #include "nsRect.h"
 #include "nsISupportsImpl.h"
 #include "nsAutoPtr.h"
 #include "gfx3DMatrix.h"
 #include "gfxColor.h"
 #include "gfxPattern.h"
+#include "nsTArray.h"
 
 #include "mozilla/gfx/2D.h"
 
 #if defined(DEBUG) || defined(PR_LOGGING)
 #  include <stdio.h>            // FILE
 #  include "prlog.h"
 #  define MOZ_LAYERS_HAVE_LOG
 #  define MOZ_LAYERS_LOG(_args)                             \
@@ -572,17 +573,23 @@ public:
     CONTENT_OPAQUE = 0x01,
     /**
      * If this is set, the caller is notifying that the contents of this layer
      * require per-component alpha for optimal fidelity. However, there is no
      * guarantee that component alpha will be supported for this layer at
      * paint time.
      * This should never be set at the same time as CONTENT_OPAQUE.
      */
-    CONTENT_COMPONENT_ALPHA = 0x02
+    CONTENT_COMPONENT_ALPHA = 0x02,
+
+    /**
+     * If this is set then this layer is part of a preserve-3d group, and should
+     * be sorted with sibling layers that are also part of the same group.
+     */
+    CONTENT_PRESERVE_3D = 0x04
   };
   /**
    * CONSTRUCTION PHASE ONLY
    * This lets layout make some promises about what will be drawn into the
    * visible region of the ThebesLayer. This enables internal quality
    * and performance optimizations.
    */
   void SetContentFlags(PRUint32 aFlags)
@@ -1090,16 +1097,18 @@ public:
   void SetFrameMetrics(const FrameMetrics& aFrameMetrics)
   {
     mFrameMetrics = aFrameMetrics;
     Mutated();
   }
 
   virtual void FillSpecificAttributes(SpecificLayerAttributes& aAttrs);
 
+  void SortChildrenBy3DZOrder(nsTArray<Layer*>& aArray);
+
   // These getters can be used anytime.
 
   virtual ContainerLayer* AsContainerLayer() { return this; }
 
   virtual Layer* GetFirstChild() { return mFirstChild; }
   virtual Layer* GetLastChild() { return mLastChild; }
   const FrameMetrics& GetFrameMetrics() { return mFrameMetrics; }
 
--- a/gfx/layers/Makefile.in
+++ b/gfx/layers/Makefile.in
@@ -61,30 +61,32 @@ endif
 
 EXPORTS = \
         BasicLayers.h \
         ImageLayers.h \
         Layers.h \
         LayerManagerOGL.h \
         LayerManagerOGLProgram.h \
         ReadbackLayer.h \
+        LayerSorter.h \
         $(NULL)
 
 CPPSRCS = \
         BasicImages.cpp \
         BasicLayers.cpp \
         Layers.cpp \
         ReadbackProcessor.cpp \
         ThebesLayerBuffer.cpp \
         CanvasLayerOGL.cpp \
         ColorLayerOGL.cpp \
         ContainerLayerOGL.cpp \
         ImageLayerOGL.cpp \
         LayerManagerOGL.cpp \
         ThebesLayerOGL.cpp \
+        LayerSorter.cpp \
         $(NULL)
 
 ifeq ($(MOZ_WIDGET_TOOLKIT),cocoa)
 CMMSRCS = \
         MacIOSurfaceImageOGL.mm \
         $(NULL)
 endif
 
--- a/layout/base/nsDisplayList.cpp
+++ b/layout/base/nsDisplayList.cpp
@@ -757,43 +757,25 @@ static bool IsContentLEQ(nsDisplayItem* 
   // These GetUnderlyingFrame calls return non-null because we're only used
   // in sorting
   return nsLayoutUtils::CompareTreePosition(
       aItem1->GetUnderlyingFrame()->GetContent(),
       aItem2->GetUnderlyingFrame()->GetContent(),
       static_cast<nsIContent*>(aClosure)) <= 0;
 }
 
-static bool IsZPositionLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
-                             void* aClosure) {
-  if (!aItem1->GetUnderlyingFrame()->Preserves3D() ||
-      !aItem1->GetUnderlyingFrame()->Preserves3D()) {
-    return IsContentLEQ(aItem1, aItem2, aClosure);
-  }
-
-  nsIFrame* ancestor;
-  gfx3DMatrix matrix1 = aItem1->GetUnderlyingFrame()->GetTransformMatrix(&ancestor);
-  gfx3DMatrix matrix2 = aItem2->GetUnderlyingFrame()->GetTransformMatrix(&ancestor);
-
-  if (matrix1._43 == matrix2._43) {
-    return IsContentLEQ(aItem1, aItem2, aClosure);
-  }
-
-  return matrix1._43 < matrix2._43;
-}
-
 static bool IsZOrderLEQ(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
                           void* aClosure) {
   // These GetUnderlyingFrame calls return non-null because we're only used
   // in sorting.  Note that we can't just take the difference of the two
   // z-indices here, because that might overflow a 32-bit int.
   PRInt32 index1 = nsLayoutUtils::GetZIndex(aItem1->GetUnderlyingFrame());
   PRInt32 index2 = nsLayoutUtils::GetZIndex(aItem2->GetUnderlyingFrame());
   if (index1 == index2)
-    return IsZPositionLEQ(aItem1, aItem2, aClosure);
+    return IsContentLEQ(aItem1, aItem2, aClosure);
   return index1 < index2;
 }
 
 void nsDisplayList::ExplodeAnonymousChildLists(nsDisplayListBuilder* aBuilder) {
   // See if there's anything to do
   bool anyAnonymousItems = false;
   nsDisplayItem* i;
   for (i = GetBottom(); i != nsnull; i = i->GetAbove()) {
@@ -830,21 +812,16 @@ void nsDisplayList::SortByZOrder(nsDispl
   Sort(aBuilder, IsZOrderLEQ, aCommonAncestor);
 }
 
 void nsDisplayList::SortByContentOrder(nsDisplayListBuilder* aBuilder,
                                        nsIContent* aCommonAncestor) {
   Sort(aBuilder, IsContentLEQ, aCommonAncestor);
 }
 
-void nsDisplayList::SortByZPosition(nsDisplayListBuilder* aBuilder,
-                                    nsIContent* aCommonAncestor) {
-  Sort(aBuilder, IsZPositionLEQ, aCommonAncestor);
-}
-
 void nsDisplayList::Sort(nsDisplayListBuilder* aBuilder,
                          SortLEQ aCmp, void* aClosure) {
   ExplodeAnonymousChildLists(aBuilder);
   ::Sort(this, Count(), aCmp, aClosure);
 }
 
 bool nsDisplayItem::RecomputeVisibility(nsDisplayListBuilder* aBuilder,
                                           nsRegion* aVisibleRegion) {
@@ -2531,27 +2508,34 @@ already_AddRefed<Layer> nsDisplayTransfo
     GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel());
 
   if (newTransformMatrix.IsSingular() ||
       (mFrame->GetStyleDisplay()->mBackfaceVisibility == NS_STYLE_BACKFACE_VISIBILITY_HIDDEN &&
        newTransformMatrix.GetNormalVector().z <= 0.0)) {
     return nsnull;
   }
 
-  return aBuilder->LayerBuilder()->
+  nsRefPtr<ContainerLayer> container = aBuilder->LayerBuilder()->
     BuildContainerLayerFor(aBuilder, aManager, mFrame, this, *mStoredList.GetList(),
                            aContainerParameters, &newTransformMatrix);
+
+  // Add the preserve-3d flag for this layer, BuildContainerLayerFor clears all flags,
+  // so we never need to explicitely unset this flag.
+  if (mFrame->Preserves3D()) {
+    container->SetContentFlags(container->GetContentFlags() | Layer::CONTENT_PRESERVE_3D);
+  }
+  return container.forget();
 }
 
 nsDisplayItem::LayerState
 nsDisplayTransform::GetLayerState(nsDisplayListBuilder* aBuilder,
                                   LayerManager* aManager) {
   if (mFrame->AreLayersMarkedActive(nsChangeHint_UpdateTransformLayer))
     return LAYER_ACTIVE;
-  if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D())
+  if (!GetTransform(mFrame->PresContext()->AppUnitsPerDevPixel()).Is2D() || mFrame->Preserves3D())
     return LAYER_ACTIVE;
   nsIFrame* activeScrolledRoot =
     nsLayoutUtils::GetActiveScrolledRootFor(mFrame, nsnull);
   return !mStoredList.ChildrenCanBeInactive(aBuilder, 
                                              aManager, 
                                              *mStoredList.GetList(), 
                                              activeScrolledRoot)
       ? LAYER_ACTIVE : LAYER_INACTIVE;
--- a/layout/base/nsDisplayList.h
+++ b/layout/base/nsDisplayList.h
@@ -1007,18 +1007,16 @@ public:
    * GetUnderlyingFrame() on each item. z-index is ignored.
    * @param aCommonAncestor a common ancestor of all the content elements
    * associated with the display items, for speeding up tree order
    * checks, or nsnull if not known; it's only a hint, if it is not an
    * ancestor of some elements, then we lose performance but not correctness
    */
   void SortByContentOrder(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor);
 
-  void SortByZPosition(nsDisplayListBuilder* aBuilder, nsIContent* aCommonAncestor);
-
   /**
    * Generic stable sort. Take care, because some of the items might be nsDisplayLists
    * themselves.
    * aCmp(item1, item2) should return true if item1 <= item2. We sort the items
    * into increasing order.
    */
   typedef bool (* SortLEQ)(nsDisplayItem* aItem1, nsDisplayItem* aItem2,
                              void* aClosure);