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