Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "LayerSorter.h"
7 #include <math.h> // for fabs
8 #include <stdint.h> // for uint32_t
9 #include <stdio.h> // for fprintf, stderr, FILE
10 #include <stdlib.h> // for getenv
11 #include "DirectedGraph.h" // for DirectedGraph
12 #include "Layers.h" // for Layer
13 #include "gfx3DMatrix.h" // for gfx3DMatrix
14 #include "gfxLineSegment.h" // for gfxLineSegment
15 #include "gfxPoint.h" // for gfxPoint
16 #include "gfxPoint3D.h" // for gfxPoint3D
17 #include "gfxQuad.h" // for gfxQuad
18 #include "gfxRect.h" // for gfxRect
19 #include "gfxTypes.h" // for gfxFloat
20 #include "mozilla/gfx/BasePoint3D.h" // for BasePoint3D
21 #include "nsRegion.h" // for nsIntRegion
22 #include "nsTArray.h" // for nsTArray, etc
23 #include "limits.h"
24 #include "mozilla/Assertions.h"
26 using namespace mozilla::gfx;
28 namespace mozilla {
29 namespace layers {
31 enum LayerSortOrder {
32 Undefined,
33 ABeforeB,
34 BBeforeA,
35 };
37 /**
38 * Recover the z component from a 2d transformed point by finding the intersection
39 * of a line through the point in the z direction and the transformed plane.
40 *
41 * We want to solve:
42 *
43 * point = normal . (p0 - l0) / normal . l
44 */
45 static gfxFloat RecoverZDepth(const gfx3DMatrix& aTransform, const gfxPoint& aPoint)
46 {
47 const gfxPoint3D l(0, 0, 1);
48 gfxPoint3D l0 = gfxPoint3D(aPoint.x, aPoint.y, 0);
49 gfxPoint3D p0 = aTransform.Transform3D(gfxPoint3D(0, 0, 0));
50 gfxPoint3D normal = aTransform.GetNormalVector();
52 gfxFloat n = normal.DotProduct(p0 - l0);
53 gfxFloat d = normal.DotProduct(l);
55 if (!d) {
56 return 0;
57 }
59 return n/d;
60 }
62 /**
63 * Determine if this transform layer should be drawn before another when they
64 * are both preserve-3d children.
65 *
66 * We want to find the relative z depths of the 2 layers at points where they
67 * intersect when projected onto the 2d screen plane. Intersections are defined
68 * as corners that are positioned within the other quad, as well as intersections
69 * of the lines.
70 *
71 * We then choose the intersection point with the greatest difference in Z
72 * depths and use this point to determine an ordering for the two layers.
73 * For layers that are intersecting in 3d space, this essentially guesses an
74 * order. In a lot of cases we only intersect right at the edge point (3d cubes
75 * in particular) and this generates the 'correct' looking ordering. For planes
76 * that truely intersect, then there is no correct ordering and this remains
77 * unsolved without changing our rendering code.
78 */
79 static LayerSortOrder CompareDepth(Layer* aOne, Layer* aTwo) {
80 gfxRect ourRect = aOne->GetEffectiveVisibleRegion().GetBounds();
81 gfxRect otherRect = aTwo->GetEffectiveVisibleRegion().GetBounds();
83 gfx3DMatrix ourTransform;
84 To3DMatrix(aOne->GetTransform(), ourTransform);
85 gfx3DMatrix otherTransform;
86 To3DMatrix(aTwo->GetTransform(), otherTransform);
88 // Transform both rectangles and project into 2d space.
89 gfxQuad ourTransformedRect = ourTransform.TransformRect(ourRect);
90 gfxQuad otherTransformedRect = otherTransform.TransformRect(otherRect);
92 gfxRect ourBounds = ourTransformedRect.GetBounds();
93 gfxRect otherBounds = otherTransformedRect.GetBounds();
95 if (!ourBounds.Intersects(otherBounds)) {
96 return Undefined;
97 }
99 // Make a list of all points that are within the other rect.
100 // Could we just check Contains() on the bounds rects. ie, is it possible
101 // for layers to overlap without intersections (in 2d space) and yet still
102 // have their bounds rects not completely enclose each other?
103 nsTArray<gfxPoint> points;
104 for (uint32_t i = 0; i < 4; i++) {
105 if (ourTransformedRect.Contains(otherTransformedRect.mPoints[i])) {
106 points.AppendElement(otherTransformedRect.mPoints[i]);
107 }
108 if (otherTransformedRect.Contains(ourTransformedRect.mPoints[i])) {
109 points.AppendElement(ourTransformedRect.mPoints[i]);
110 }
111 }
113 // Look for intersections between lines (in 2d space) and use these as
114 // depth testing points.
115 for (uint32_t i = 0; i < 4; i++) {
116 for (uint32_t j = 0; j < 4; j++) {
117 gfxPoint intersection;
118 gfxLineSegment one(ourTransformedRect.mPoints[i],
119 ourTransformedRect.mPoints[(i + 1) % 4]);
120 gfxLineSegment two(otherTransformedRect.mPoints[j],
121 otherTransformedRect.mPoints[(j + 1) % 4]);
122 if (one.Intersects(two, intersection)) {
123 points.AppendElement(intersection);
124 }
125 }
126 }
128 // No intersections, no defined order between these layers.
129 if (points.IsEmpty()) {
130 return Undefined;
131 }
133 // Find the relative Z depths of each intersection point and check that the layers are in the same order.
134 gfxFloat highest = 0;
135 for (uint32_t i = 0; i < points.Length(); i++) {
136 gfxFloat ourDepth = RecoverZDepth(ourTransform, points.ElementAt(i));
137 gfxFloat otherDepth = RecoverZDepth(otherTransform, points.ElementAt(i));
139 gfxFloat difference = otherDepth - ourDepth;
141 if (fabs(difference) > fabs(highest)) {
142 highest = difference;
143 }
144 }
145 // If layers have the same depth keep the original order
146 if (fabs(highest) < 0.1 || highest >= 0) {
147 return ABeforeB;
148 } else {
149 return BBeforeA;
150 }
151 }
153 #ifdef DEBUG
154 static bool gDumpLayerSortList = getenv("MOZ_DUMP_LAYER_SORT_LIST") != 0;
156 static const int BLACK = 0;
157 static const int RED = 1;
158 static const int GREEN = 2;
159 static const int YELLOW = 3;
160 static const int BLUE = 4;
161 static const int MAGENTA = 5;
162 static const int CYAN = 6;
163 static const int WHITE = 7;
165 //#define USE_XTERM_COLORING
166 #ifdef USE_XTERM_COLORING
168 static const int RESET = 0;
169 static const int BRIGHT = 1;
170 static const int DIM = 2;
171 static const int UNDERLINE = 3;
172 static const int BLINK = 4;
173 static const int REVERSE = 7;
174 static const int HIDDEN = 8;
176 static void SetTextColor(uint32_t aColor)
177 {
178 char command[13];
180 /* Command is the control command to the terminal */
181 sprintf(command, "%c[%d;%d;%dm", 0x1B, RESET, aColor + 30, BLACK + 40);
182 printf("%s", command);
183 }
185 static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
186 {
187 SetTextColor(aColor);
188 fprintf(aFile, "%p", aLayer);
189 SetTextColor(GREEN);
190 }
191 #else
193 const char *colors[] = { "Black", "Red", "Green", "Yellow", "Blue", "Magenta", "Cyan", "White" };
195 static void print_layer_internal(FILE* aFile, Layer* aLayer, uint32_t aColor)
196 {
197 fprintf(aFile, "%p(%s)", aLayer, colors[aColor]);
198 }
199 #endif
201 static void print_layer(FILE* aFile, Layer* aLayer)
202 {
203 print_layer_internal(aFile, aLayer, aLayer->GetDebugColorIndex());
204 }
206 static void DumpLayerList(nsTArray<Layer*>& aLayers)
207 {
208 for (uint32_t i = 0; i < aLayers.Length(); i++) {
209 print_layer(stderr, aLayers.ElementAt(i));
210 fprintf(stderr, " ");
211 }
212 fprintf(stderr, "\n");
213 }
215 static void DumpEdgeList(DirectedGraph<Layer*>& aGraph)
216 {
217 const nsTArray<DirectedGraph<Layer*>::Edge>& edges = aGraph.GetEdgeList();
219 for (uint32_t i = 0; i < edges.Length(); i++) {
220 fprintf(stderr, "From: ");
221 print_layer(stderr, edges.ElementAt(i).mFrom);
222 fprintf(stderr, ", To: ");
223 print_layer(stderr, edges.ElementAt(i).mTo);
224 fprintf(stderr, "\n");
225 }
226 }
227 #endif
229 // The maximum number of layers that we will attempt to sort. Anything
230 // greater than this will be left unsorted. We should consider enabling
231 // depth buffering for the scene in this case.
232 #define MAX_SORTABLE_LAYERS 100
235 uint32_t gColorIndex = 1;
237 void SortLayersBy3DZOrder(nsTArray<Layer*>& aLayers)
238 {
239 uint32_t nodeCount = aLayers.Length();
240 if (nodeCount > MAX_SORTABLE_LAYERS) {
241 return;
242 }
243 DirectedGraph<Layer*> graph;
245 #ifdef DEBUG
246 if (gDumpLayerSortList) {
247 for (uint32_t i = 0; i < nodeCount; i++) {
248 if (aLayers.ElementAt(i)->GetDebugColorIndex() == 0) {
249 aLayers.ElementAt(i)->SetDebugColorIndex(gColorIndex++);
250 if (gColorIndex > 7) {
251 gColorIndex = 1;
252 }
253 }
254 }
255 fprintf(stderr, " --- Layers before sorting: --- \n");
256 DumpLayerList(aLayers);
257 }
258 #endif
260 // Iterate layers and determine edges.
261 for (uint32_t i = 0; i < nodeCount; i++) {
262 for (uint32_t j = i + 1; j < nodeCount; j++) {
263 Layer* a = aLayers.ElementAt(i);
264 Layer* b = aLayers.ElementAt(j);
265 LayerSortOrder order = CompareDepth(a, b);
266 if (order == ABeforeB) {
267 graph.AddEdge(a, b);
268 } else if (order == BBeforeA) {
269 graph.AddEdge(b, a);
270 }
271 }
272 }
274 #ifdef DEBUG
275 if (gDumpLayerSortList) {
276 fprintf(stderr, " --- Edge List: --- \n");
277 DumpEdgeList(graph);
278 }
279 #endif
281 // Build a new array using the graph.
282 nsTArray<Layer*> noIncoming;
283 nsTArray<Layer*> sortedList;
285 // Make a list of all layers with no incoming edges.
286 noIncoming.AppendElements(aLayers);
287 const nsTArray<DirectedGraph<Layer*>::Edge>& edges = graph.GetEdgeList();
288 for (uint32_t i = 0; i < edges.Length(); i++) {
289 noIncoming.RemoveElement(edges.ElementAt(i).mTo);
290 }
292 // Move each item without incoming edges into the sorted list,
293 // and remove edges from it.
294 do {
295 if (!noIncoming.IsEmpty()) {
296 uint32_t last = noIncoming.Length() - 1;
298 Layer* layer = noIncoming.ElementAt(last);
299 MOZ_ASSERT(layer); // don't let null layer pointers sneak into sortedList
301 noIncoming.RemoveElementAt(last);
302 sortedList.AppendElement(layer);
304 nsTArray<DirectedGraph<Layer*>::Edge> outgoing;
305 graph.GetEdgesFrom(layer, outgoing);
306 for (uint32_t i = 0; i < outgoing.Length(); i++) {
307 DirectedGraph<Layer*>::Edge edge = outgoing.ElementAt(i);
308 graph.RemoveEdge(edge);
309 if (!graph.NumEdgesTo(edge.mTo)) {
310 // If this node also has no edges now, add it to the list
311 noIncoming.AppendElement(edge.mTo);
312 }
313 }
314 }
316 // If there are no nodes without incoming edges, but there
317 // are still edges, then we have a cycle.
318 if (noIncoming.IsEmpty() && graph.GetEdgeCount()) {
319 // Find the node with the least incoming edges.
320 uint32_t minEdges = UINT_MAX;
321 Layer* minNode = nullptr;
322 for (uint32_t i = 0; i < aLayers.Length(); i++) {
323 uint32_t edgeCount = graph.NumEdgesTo(aLayers.ElementAt(i));
324 if (edgeCount && edgeCount < minEdges) {
325 minEdges = edgeCount;
326 minNode = aLayers.ElementAt(i);
327 if (minEdges == 1) {
328 break;
329 }
330 }
331 }
333 if (minNode) {
334 // Remove all of them!
335 graph.RemoveEdgesTo(minNode);
336 noIncoming.AppendElement(minNode);
337 }
338 }
339 } while (!noIncoming.IsEmpty());
340 NS_ASSERTION(!graph.GetEdgeCount(), "Cycles detected!");
341 #ifdef DEBUG
342 if (gDumpLayerSortList) {
343 fprintf(stderr, " --- Layers after sorting: --- \n");
344 DumpLayerList(sortedList);
345 }
346 #endif
348 aLayers.Clear();
349 aLayers.AppendElements(sortedList);
350 }
352 }
353 }