Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(355)

Side by Side Diff: third_party/WebKit/Source/platform/graphics/Canvas2DLayerBridgeTest.cpp

Issue 2212163002: Add some plumbing for the color management of canvases (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: rebase again Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 /* 1 /*
2 * Copyright (C) 2011 Google Inc. All rights reserved. 2 * Copyright (C) 2011 Google Inc. All rights reserved.
3 * 3 *
4 * Redistribution and use in source and binary forms, with or without 4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions 5 * modification, are permitted provided that the following conditions
6 * are met: 6 * are met:
7 * 1. Redistributions of source code must retain the above copyright 7 * 1. Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer. 8 * notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright 9 * 2. Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the 10 * notice, this list of conditions and the following disclaimer in the
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after
94 private: 94 private:
95 RefPtr<Canvas2DLayerBridge> m_layerBridge; 95 RefPtr<Canvas2DLayerBridge> m_layerBridge;
96 }; 96 };
97 97
98 } // anonymous namespace 98 } // anonymous namespace
99 99
100 class Canvas2DLayerBridgeTest : public Test { 100 class Canvas2DLayerBridgeTest : public Test {
101 public: 101 public:
102 PassRefPtr<Canvas2DLayerBridge> makeBridge(std::unique_ptr<FakeWebGraphicsCo ntext3DProvider> provider, const IntSize& size, Canvas2DLayerBridge::Acceleratio nMode accelerationMode) 102 PassRefPtr<Canvas2DLayerBridge> makeBridge(std::unique_ptr<FakeWebGraphicsCo ntext3DProvider> provider, const IntSize& size, Canvas2DLayerBridge::Acceleratio nMode accelerationMode)
103 { 103 {
104 return adoptRef(new Canvas2DLayerBridge(std::move(provider), size, 0, No nOpaque, accelerationMode)); 104 return adoptRef(new Canvas2DLayerBridge(std::move(provider), size, 0, No nOpaque, accelerationMode, nullptr));
105 } 105 }
106 106
107 protected: 107 protected:
108 void fullLifecycleTest() 108 void fullLifecycleTest()
109 { 109 {
110 FakeGLES2Interface gl; 110 FakeGLES2Interface gl;
111 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl)); 111 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl));
112 112
113 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::Disable Acceleration))); 113 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::Disable Acceleration, nullptr)));
114 114
115 const GrGLTextureInfo* textureInfo = skia::GrBackendObjectToGrGLTextureI nfo(bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests)->getTe xtureHandle(true)); 115 const GrGLTextureInfo* textureInfo = skia::GrBackendObjectToGrGLTextureI nfo(bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests)->getTe xtureHandle(true));
116 EXPECT_EQ(textureInfo, nullptr); 116 EXPECT_EQ(textureInfo, nullptr);
117 bridge.clear(); 117 bridge.clear();
118 } 118 }
119 119
120 void fallbackToSoftwareIfContextLost() 120 void fallbackToSoftwareIfContextLost()
121 { 121 {
122 FakeGLES2Interface gl; 122 FakeGLES2Interface gl;
123 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl)); 123 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl));
124 124
125 gl.setIsContextLost(true); 125 gl.setIsContextLost(true);
126 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::EnableA cceleration))); 126 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::EnableA cceleration, nullptr)));
127 EXPECT_TRUE(bridge->checkSurfaceValid()); 127 EXPECT_TRUE(bridge->checkSurfaceValid());
128 EXPECT_FALSE(bridge->isAccelerated()); 128 EXPECT_FALSE(bridge->isAccelerated());
129 } 129 }
130 130
131 void fallbackToSoftwareOnFailedTextureAlloc() 131 void fallbackToSoftwareOnFailedTextureAlloc()
132 { 132 {
133 { 133 {
134 // No fallback case. 134 // No fallback case.
135 FakeGLES2Interface gl; 135 FakeGLES2Interface gl;
136 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); 136 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
137 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration))); 137 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration, nullptr)));
138 EXPECT_TRUE(bridge->checkSurfaceValid()); 138 EXPECT_TRUE(bridge->checkSurfaceValid());
139 EXPECT_TRUE(bridge->isAccelerated()); 139 EXPECT_TRUE(bridge->isAccelerated());
140 RefPtr<SkImage> snapshot = bridge->newImageSnapshot(PreferAccelerati on, SnapshotReasonUnitTests); 140 RefPtr<SkImage> snapshot = bridge->newImageSnapshot(PreferAccelerati on, SnapshotReasonUnitTests);
141 EXPECT_TRUE(bridge->isAccelerated()); 141 EXPECT_TRUE(bridge->isAccelerated());
142 EXPECT_TRUE(snapshot->isTextureBacked()); 142 EXPECT_TRUE(snapshot->isTextureBacked());
143 } 143 }
144 144
145 { 145 {
146 // Fallback case. 146 // Fallback case.
147 FakeGLES2Interface gl; 147 FakeGLES2Interface gl;
148 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); 148 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
149 GrContext* gr = contextProvider->grContext(); 149 GrContext* gr = contextProvider->grContext();
150 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration))); 150 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration, nullptr)));
151 EXPECT_TRUE(bridge->checkSurfaceValid()); 151 EXPECT_TRUE(bridge->checkSurfaceValid());
152 EXPECT_TRUE(bridge->isAccelerated()); // We don't yet know that allo cation will fail 152 EXPECT_TRUE(bridge->isAccelerated()); // We don't yet know that allo cation will fail
153 // This will cause SkSurface_Gpu creation to fail without 153 // This will cause SkSurface_Gpu creation to fail without
154 // Canvas2DLayerBridge otherwise detecting that anything was disable d. 154 // Canvas2DLayerBridge otherwise detecting that anything was disable d.
155 gr->abandonContext(); 155 gr->abandonContext();
156 RefPtr<SkImage> snapshot = bridge->newImageSnapshot(PreferAccelerati on, SnapshotReasonUnitTests); 156 RefPtr<SkImage> snapshot = bridge->newImageSnapshot(PreferAccelerati on, SnapshotReasonUnitTests);
157 EXPECT_FALSE(bridge->isAccelerated()); 157 EXPECT_FALSE(bridge->isAccelerated());
158 EXPECT_FALSE(snapshot->isTextureBacked()); 158 EXPECT_FALSE(snapshot->isTextureBacked());
159 } 159 }
160 } 160 }
161 161
162 void noDrawOnContextLostTest() 162 void noDrawOnContextLostTest()
163 { 163 {
164 FakeGLES2Interface gl; 164 FakeGLES2Interface gl;
165 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl)); 165 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl));
166 166
167 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::ForceAc celerationForTesting))); 167 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::ForceAc celerationForTesting, nullptr)));
168 EXPECT_TRUE(bridge->checkSurfaceValid()); 168 EXPECT_TRUE(bridge->checkSurfaceValid());
169 SkPaint paint; 169 SkPaint paint;
170 uint32_t genID = bridge->getOrCreateSurface()->generationID(); 170 uint32_t genID = bridge->getOrCreateSurface()->generationID();
171 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint); 171 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
172 EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID()); 172 EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID());
173 gl.setIsContextLost(true); 173 gl.setIsContextLost(true);
174 EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID()); 174 EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID());
175 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint); 175 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
176 EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID()); 176 EXPECT_EQ(genID, bridge->getOrCreateSurface()->generationID());
177 // This results in the internal surface being torn down in response to t he context loss. 177 // This results in the internal surface being torn down in response to t he context loss.
178 EXPECT_FALSE(bridge->checkSurfaceValid()); 178 EXPECT_FALSE(bridge->checkSurfaceValid());
179 EXPECT_EQ(nullptr, bridge->getOrCreateSurface()); 179 EXPECT_EQ(nullptr, bridge->getOrCreateSurface());
180 // The following passes by not crashing 180 // The following passes by not crashing
181 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint); 181 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
182 bridge->flush(); 182 bridge->flush();
183 } 183 }
184 184
185 void prepareMailboxWhenContextIsLost() 185 void prepareMailboxWhenContextIsLost()
186 { 186 {
187 FakeGLES2Interface gl; 187 FakeGLES2Interface gl;
188 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl)); 188 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrap Unique(new FakeWebGraphicsContext3DProvider(&gl));
189 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::ForceAc celerationForTesting))); 189 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std::move (contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::ForceAc celerationForTesting, nullptr)));
190 190
191 // TODO(junov): The PrepareTextureMailbox() method will fail a DCHECK if we don't 191 // TODO(junov): The PrepareTextureMailbox() method will fail a DCHECK if we don't
192 // do this before calling it the first time when the context is lost. 192 // do this before calling it the first time when the context is lost.
193 bridge->prepareSurfaceForPaintingIfNeeded(); 193 bridge->prepareSurfaceForPaintingIfNeeded();
194 194
195 // When the context is lost we are not sure if we should be producing GL frames for the 195 // When the context is lost we are not sure if we should be producing GL frames for the
196 // compositor still or not, so fail to generate frames. 196 // compositor still or not, so fail to generate frames.
197 gl.setIsContextLost(true); 197 gl.setIsContextLost(true);
198 198
199 cc::TextureMailbox textureMailbox; 199 cc::TextureMailbox textureMailbox;
200 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; 200 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback;
201 EXPECT_FALSE(bridge->PrepareTextureMailbox(&textureMailbox, &releaseCall back)); 201 EXPECT_FALSE(bridge->PrepareTextureMailbox(&textureMailbox, &releaseCall back));
202 } 202 }
203 203
204 void prepareMailboxAndLoseResourceTest() 204 void prepareMailboxAndLoseResourceTest()
205 { 205 {
206 // Prepare a mailbox, then report the resource as lost. 206 // Prepare a mailbox, then report the resource as lost.
207 // This test passes by not crashing and not triggering assertions. 207 // This test passes by not crashing and not triggering assertions.
208 { 208 {
209 FakeGLES2Interface gl; 209 FakeGLES2Interface gl;
210 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); 210 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
211 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::For ceAccelerationForTesting))); 211 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge::For ceAccelerationForTesting, nullptr)));
212 212
213 cc::TextureMailbox textureMailbox; 213 cc::TextureMailbox textureMailbox;
214 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; 214 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback;
215 EXPECT_TRUE(bridge->PrepareTextureMailbox(&textureMailbox, &releaseC allback)); 215 EXPECT_TRUE(bridge->PrepareTextureMailbox(&textureMailbox, &releaseC allback));
216 216
217 bool lostResource = true; 217 bool lostResource = true;
218 releaseCallback->Run(gpu::SyncToken(), lostResource); 218 releaseCallback->Run(gpu::SyncToken(), lostResource);
219 } 219 }
220 220
221 // Retry with mailbox released while bridge destruction is in progress. 221 // Retry with mailbox released while bridge destruction is in progress.
222 { 222 {
223 FakeGLES2Interface gl; 223 FakeGLES2Interface gl;
224 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); 224 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
225 225
226 cc::TextureMailbox textureMailbox; 226 cc::TextureMailbox textureMailbox;
227 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; 227 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback;
228 228
229 { 229 {
230 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(s td::move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge: :ForceAccelerationForTesting))); 230 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(s td::move(contextProvider), IntSize(300, 150), 0, NonOpaque, Canvas2DLayerBridge: :ForceAccelerationForTesting, nullptr)));
231 bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback) ; 231 bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback) ;
232 // |bridge| goes out of scope and would normally be destroyed, b ut object is kept alive by self references. 232 // |bridge| goes out of scope and would normally be destroyed, b ut object is kept alive by self references.
233 } 233 }
234 234
235 // Before fixing crbug.com/411864, the following line you cause a me mory use after free 235 // Before fixing crbug.com/411864, the following line you cause a me mory use after free
236 // that sometimes causes a crash in normal builds and crashes consis tently with ASAN. 236 // that sometimes causes a crash in normal builds and crashes consis tently with ASAN.
237 // This should cause the bridge to be destroyed. 237 // This should cause the bridge to be destroyed.
238 bool lostResource = true; 238 bool lostResource = true;
239 releaseCallback->Run(gpu::SyncToken(), lostResource); 239 releaseCallback->Run(gpu::SyncToken(), lostResource);
240 } 240 }
241 } 241 }
242 242
243 void accelerationHintTest() 243 void accelerationHintTest()
244 { 244 {
245 { 245 {
246 FakeGLES2Interface gl; 246 FakeGLES2Interface gl;
247 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); 247 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
248 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 300), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration))); 248 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 300), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration, nullptr)));
249 SkPaint paint; 249 SkPaint paint;
250 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint); 250 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
251 RefPtr<SkImage> image = bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests); 251 RefPtr<SkImage> image = bridge->newImageSnapshot(PreferAcceleration, SnapshotReasonUnitTests);
252 EXPECT_TRUE(bridge->checkSurfaceValid()); 252 EXPECT_TRUE(bridge->checkSurfaceValid());
253 EXPECT_TRUE(bridge->isAccelerated()); 253 EXPECT_TRUE(bridge->isAccelerated());
254 } 254 }
255 255
256 { 256 {
257 FakeGLES2Interface gl; 257 FakeGLES2Interface gl;
258 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl)); 258 std::unique_ptr<FakeWebGraphicsContext3DProvider> contextProvider = wrapUnique(new FakeWebGraphicsContext3DProvider(&gl));
259 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 300), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration))); 259 Canvas2DLayerBridgePtr bridge(adoptRef(new Canvas2DLayerBridge(std:: move(contextProvider), IntSize(300, 300), 0, NonOpaque, Canvas2DLayerBridge::Ena bleAcceleration, nullptr)));
260 SkPaint paint; 260 SkPaint paint;
261 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint); 261 bridge->canvas()->drawRect(SkRect::MakeXYWH(0, 0, 1, 1), paint);
262 RefPtr<SkImage> image = bridge->newImageSnapshot(PreferNoAcceleratio n, SnapshotReasonUnitTests); 262 RefPtr<SkImage> image = bridge->newImageSnapshot(PreferNoAcceleratio n, SnapshotReasonUnitTests);
263 EXPECT_TRUE(bridge->checkSurfaceValid()); 263 EXPECT_TRUE(bridge->checkSurfaceValid());
264 EXPECT_FALSE(bridge->isAccelerated()); 264 EXPECT_FALSE(bridge->isAccelerated());
265 } 265 }
266 } 266 }
267 }; 267 };
268 268
269 TEST_F(Canvas2DLayerBridgeTest, FullLifecycleSingleThreaded) 269 TEST_F(Canvas2DLayerBridgeTest, FullLifecycleSingleThreaded)
(...skipping 790 matching lines...) Expand 10 before | Expand all | Expand 10 after
1060 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback; 1060 std::unique_ptr<cc::SingleReleaseCallback> releaseCallback;
1061 EXPECT_FALSE(bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback )); 1061 EXPECT_FALSE(bridge->PrepareTextureMailbox(&textureMailbox, &releaseCallback ));
1062 EXPECT_TRUE(bridge->checkSurfaceValid()); 1062 EXPECT_TRUE(bridge->checkSurfaceValid());
1063 1063
1064 // Tear down the bridge on the thread so that 'bridge' can go out of scope 1064 // Tear down the bridge on the thread so that 'bridge' can go out of scope
1065 // without crashing due to thread checks 1065 // without crashing due to thread checks
1066 postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge); 1066 postAndWaitDestroyBridgeTask(BLINK_FROM_HERE, testThread.get(), &bridge);
1067 } 1067 }
1068 1068
1069 } // namespace blink 1069 } // namespace blink
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698