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

Side by Side Diff: cc/trees/layer_tree.cc

Issue 2251143002: cc: Reland Move data to LayerTree from LayerTreeHost. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: better fix? Created 4 years, 4 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 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "cc/trees/layer_tree.h"
6
5 #include "base/auto_reset.h" 7 #include "base/auto_reset.h"
8 #include "base/time/time.h"
6 #include "cc/animation/animation_host.h" 9 #include "cc/animation/animation_host.h"
10 #include "cc/input/page_scale_animation.h"
11 #include "cc/layers/heads_up_display_layer.h"
12 #include "cc/layers/heads_up_display_layer_impl.h"
7 #include "cc/layers/layer.h" 13 #include "cc/layers/layer.h"
14 #include "cc/layers/layer_proto_converter.h"
15 #include "cc/proto/gfx_conversions.h"
8 #include "cc/proto/layer_tree.pb.h" 16 #include "cc/proto/layer_tree.pb.h"
9 #include "cc/trees/layer_tree.h" 17 #include "cc/trees/layer_tree_host.h"
18 #include "cc/trees/layer_tree_host_common.h"
19 #include "cc/trees/layer_tree_impl.h"
10 20
11 namespace cc { 21 namespace cc {
12 22
13 LayerTree::LayerTree(std::unique_ptr<AnimationHost> animation_host) 23 namespace {
14 : in_paint_layer_contents_(false), 24
15 animation_host_(std::move(animation_host)) { 25 Layer* UpdateAndGetLayer(Layer* current_layer,
26 int layer_id,
27 LayerTree* layer_tree) {
28 if (layer_id == Layer::INVALID_ID) {
29 if (current_layer)
30 current_layer->SetLayerTreeHost(nullptr);
31
32 return nullptr;
33 }
34 Layer* layer = layer_tree->LayerById(layer_id);
35 DCHECK(layer);
36 if (current_layer && current_layer != layer)
37 current_layer->SetLayerTreeHost(nullptr);
38
39 return layer;
40 }
41
42 } // namespace
43
44 LayerTree::Inputs::Inputs()
45 : top_controls_height(0.f),
46 top_controls_shown_ratio(0.f),
47 top_controls_shrink_blink_size(false),
48 device_scale_factor(1.f),
49 painted_device_scale_factor(1.f),
50 page_scale_factor(1.f),
51 min_page_scale_factor(1.f),
52 max_page_scale_factor(1.f),
53 background_color(SK_ColorWHITE),
54 has_transparent_background(false),
55 have_scroll_event_handlers(false),
56 event_listener_properties() {}
57
58 LayerTree::Inputs::~Inputs() = default;
59
60 LayerTree::LayerTree(std::unique_ptr<AnimationHost> animation_host,
61 LayerTreeHost* layer_tree_host)
62 : needs_full_tree_sync_(true),
63 needs_meta_info_recomputation_(true),
64 in_paint_layer_contents_(false),
65 animation_host_(std::move(animation_host)),
66 layer_tree_host_(layer_tree_host) {
16 DCHECK(animation_host_); 67 DCHECK(animation_host_);
17 } 68 DCHECK(layer_tree_host_);
18 69 }
19 LayerTree::~LayerTree() {} 70
71 LayerTree::~LayerTree() {
72 animation_host_->SetMutatorHostClient(nullptr);
73
74 // We must clear any pointers into the layer tree prior to destroying it.
75 RegisterViewportLayers(nullptr, nullptr, nullptr, nullptr);
76
77 if (inputs_.root_layer) {
78 inputs_.root_layer->SetLayerTreeHost(nullptr);
79
80 // The root layer must be destroyed before the layer tree. We've made a
81 // contract with our animation controllers that the animation_host will
82 // outlive them, and we must make good.
83 inputs_.root_layer = nullptr;
84 }
85 }
86
87 void LayerTree::SetRootLayer(scoped_refptr<Layer> root_layer) {
88 if (inputs_.root_layer.get() == root_layer.get())
89 return;
90
91 if (inputs_.root_layer.get())
92 inputs_.root_layer->SetLayerTreeHost(nullptr);
93 inputs_.root_layer = root_layer;
94 if (inputs_.root_layer.get()) {
95 DCHECK(!inputs_.root_layer->parent());
96 inputs_.root_layer->SetLayerTreeHost(layer_tree_host_);
97 }
98
99 if (hud_layer_.get())
100 hud_layer_->RemoveFromParent();
101
102 // Reset gpu rasterization tracking.
103 // This flag is sticky until a new tree comes along.
104 layer_tree_host_->ResetGpuRasterizationTracking();
105
106 SetNeedsFullTreeSync();
107 }
108
109 void LayerTree::RegisterViewportLayers(
110 scoped_refptr<Layer> overscroll_elasticity_layer,
111 scoped_refptr<Layer> page_scale_layer,
112 scoped_refptr<Layer> inner_viewport_scroll_layer,
113 scoped_refptr<Layer> outer_viewport_scroll_layer) {
114 DCHECK(!inner_viewport_scroll_layer ||
115 inner_viewport_scroll_layer != outer_viewport_scroll_layer);
116 inputs_.overscroll_elasticity_layer = overscroll_elasticity_layer;
117 inputs_.page_scale_layer = page_scale_layer;
118 inputs_.inner_viewport_scroll_layer = inner_viewport_scroll_layer;
119 inputs_.outer_viewport_scroll_layer = outer_viewport_scroll_layer;
120 }
121
122 void LayerTree::RegisterSelection(const LayerSelection& selection) {
123 if (inputs_.selection == selection)
124 return;
125
126 inputs_.selection = selection;
127 SetNeedsCommit();
128 }
129
130 void LayerTree::SetHaveScrollEventHandlers(bool have_event_handlers) {
131 if (inputs_.have_scroll_event_handlers == have_event_handlers)
132 return;
133
134 inputs_.have_scroll_event_handlers = have_event_handlers;
135 SetNeedsCommit();
136 }
137
138 void LayerTree::SetEventListenerProperties(EventListenerClass event_class,
139 EventListenerProperties properties) {
140 const size_t index = static_cast<size_t>(event_class);
141 if (inputs_.event_listener_properties[index] == properties)
142 return;
143
144 inputs_.event_listener_properties[index] = properties;
145 SetNeedsCommit();
146 }
147
148 void LayerTree::SetViewportSize(const gfx::Size& device_viewport_size) {
149 if (inputs_.device_viewport_size == device_viewport_size)
150 return;
151
152 inputs_.device_viewport_size = device_viewport_size;
153
154 SetPropertyTreesNeedRebuild();
155 SetNeedsCommit();
156 }
157
158 void LayerTree::SetTopControlsHeight(float height, bool shrink) {
159 if (inputs_.top_controls_height == height &&
160 inputs_.top_controls_shrink_blink_size == shrink)
161 return;
162
163 inputs_.top_controls_height = height;
164 inputs_.top_controls_shrink_blink_size = shrink;
165 SetNeedsCommit();
166 }
167
168 void LayerTree::SetTopControlsShownRatio(float ratio) {
169 if (inputs_.top_controls_shown_ratio == ratio)
170 return;
171
172 inputs_.top_controls_shown_ratio = ratio;
173 SetNeedsCommit();
174 }
175
176 void LayerTree::SetPageScaleFactorAndLimits(float page_scale_factor,
177 float min_page_scale_factor,
178 float max_page_scale_factor) {
179 if (inputs_.page_scale_factor == page_scale_factor &&
180 inputs_.min_page_scale_factor == min_page_scale_factor &&
181 inputs_.max_page_scale_factor == max_page_scale_factor)
182 return;
183
184 inputs_.page_scale_factor = page_scale_factor;
185 inputs_.min_page_scale_factor = min_page_scale_factor;
186 inputs_.max_page_scale_factor = max_page_scale_factor;
187 SetPropertyTreesNeedRebuild();
188 SetNeedsCommit();
189 }
190
191 void LayerTree::StartPageScaleAnimation(const gfx::Vector2d& target_offset,
192 bool use_anchor,
193 float scale,
194 base::TimeDelta duration) {
195 inputs_.pending_page_scale_animation.reset(new PendingPageScaleAnimation(
196 target_offset, use_anchor, scale, duration));
197
198 SetNeedsCommit();
199 }
200
201 bool LayerTree::HasPendingPageScaleAnimation() const {
202 return !!inputs_.pending_page_scale_animation.get();
203 }
204
205 void LayerTree::SetDeviceScaleFactor(float device_scale_factor) {
206 if (inputs_.device_scale_factor == device_scale_factor)
207 return;
208 inputs_.device_scale_factor = device_scale_factor;
209
210 property_trees_.needs_rebuild = true;
211 SetNeedsCommit();
212 }
213
214 void LayerTree::SetPaintedDeviceScaleFactor(float painted_device_scale_factor) {
215 if (inputs_.painted_device_scale_factor == painted_device_scale_factor)
216 return;
217 inputs_.painted_device_scale_factor = painted_device_scale_factor;
218
219 SetNeedsCommit();
220 }
20 221
21 void LayerTree::RegisterLayer(Layer* layer) { 222 void LayerTree::RegisterLayer(Layer* layer) {
22 DCHECK(!LayerById(layer->id())); 223 DCHECK(!LayerById(layer->id()));
23 DCHECK(!in_paint_layer_contents_); 224 DCHECK(!in_paint_layer_contents_);
24 layer_id_map_[layer->id()] = layer; 225 layer_id_map_[layer->id()] = layer;
25 if (layer->element_id()) { 226 if (layer->element_id()) {
26 animation_host_->RegisterElement(layer->element_id(), 227 animation_host_->RegisterElement(layer->element_id(),
27 ElementListType::ACTIVE); 228 ElementListType::ACTIVE);
28 } 229 }
29 } 230 }
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after
65 266
66 std::unordered_set<Layer*>& LayerTree::LayersThatShouldPushProperties() { 267 std::unordered_set<Layer*>& LayerTree::LayersThatShouldPushProperties() {
67 return layers_that_should_push_properties_; 268 return layers_that_should_push_properties_;
68 } 269 }
69 270
70 bool LayerTree::LayerNeedsPushPropertiesForTesting(Layer* layer) const { 271 bool LayerTree::LayerNeedsPushPropertiesForTesting(Layer* layer) const {
71 return layers_that_should_push_properties_.find(layer) != 272 return layers_that_should_push_properties_.find(layer) !=
72 layers_that_should_push_properties_.end(); 273 layers_that_should_push_properties_.end();
73 } 274 }
74 275
276 void LayerTree::SetNeedsMetaInfoRecomputation(bool needs_recomputation) {
277 needs_meta_info_recomputation_ = needs_recomputation;
278 }
279
280 void LayerTree::SetPageScaleFromImplSide(float page_scale) {
281 DCHECK(layer_tree_host_->CommitRequested());
282 inputs_.page_scale_factor = page_scale;
283 SetPropertyTreesNeedRebuild();
284 }
285
286 void LayerTree::SetElasticOverscrollFromImplSide(
287 gfx::Vector2dF elastic_overscroll) {
288 DCHECK(layer_tree_host_->CommitRequested());
289 elastic_overscroll_ = elastic_overscroll;
290 }
291
292 void LayerTree::UpdateHudLayer(bool show_hud_info) {
293 if (show_hud_info) {
294 if (!hud_layer_.get()) {
295 hud_layer_ = HeadsUpDisplayLayer::Create();
296 }
297
298 if (inputs_.root_layer.get() && !hud_layer_->parent())
299 inputs_.root_layer->AddChild(hud_layer_);
300 } else if (hud_layer_.get()) {
301 hud_layer_->RemoveFromParent();
302 hud_layer_ = nullptr;
303 }
304 }
305
306 void LayerTree::SetNeedsFullTreeSync() {
307 needs_full_tree_sync_ = true;
308 needs_meta_info_recomputation_ = true;
309
310 property_trees_.needs_rebuild = true;
311 SetNeedsCommit();
312 }
313
314 void LayerTree::SetNeedsCommit() {
315 layer_tree_host_->SetNeedsCommit();
316 }
317
318 void LayerTree::SetPropertyTreesNeedRebuild() {
319 property_trees_.needs_rebuild = true;
320 layer_tree_host_->SetNeedsUpdateLayers();
321 }
322
323 void LayerTree::PushPropertiesTo(LayerTreeImpl* tree_impl) {
324 tree_impl->set_needs_full_tree_sync(needs_full_tree_sync_);
325 needs_full_tree_sync_ = false;
326
327 if (hud_layer_.get()) {
328 LayerImpl* hud_impl = tree_impl->LayerById(hud_layer_->id());
329 tree_impl->set_hud_layer(static_cast<HeadsUpDisplayLayerImpl*>(hud_impl));
330 } else {
331 tree_impl->set_hud_layer(nullptr);
332 }
333
334 tree_impl->set_background_color(inputs_.background_color);
335 tree_impl->set_has_transparent_background(inputs_.has_transparent_background);
336 tree_impl->set_have_scroll_event_handlers(inputs_.have_scroll_event_handlers);
337 tree_impl->set_event_listener_properties(
338 EventListenerClass::kTouchStartOrMove,
339 event_listener_properties(EventListenerClass::kTouchStartOrMove));
340 tree_impl->set_event_listener_properties(
341 EventListenerClass::kMouseWheel,
342 event_listener_properties(EventListenerClass::kMouseWheel));
343 tree_impl->set_event_listener_properties(
344 EventListenerClass::kTouchEndOrCancel,
345 event_listener_properties(EventListenerClass::kTouchEndOrCancel));
346
347 if (inputs_.page_scale_layer && inputs_.inner_viewport_scroll_layer) {
348 tree_impl->SetViewportLayersFromIds(
349 inputs_.overscroll_elasticity_layer
350 ? inputs_.overscroll_elasticity_layer->id()
351 : Layer::INVALID_ID,
352 inputs_.page_scale_layer->id(),
353 inputs_.inner_viewport_scroll_layer->id(),
354 inputs_.outer_viewport_scroll_layer
355 ? inputs_.outer_viewport_scroll_layer->id()
356 : Layer::INVALID_ID);
357 DCHECK(inputs_.inner_viewport_scroll_layer
358 ->IsContainerForFixedPositionLayers());
359 } else {
360 tree_impl->ClearViewportLayers();
361 }
362
363 tree_impl->RegisterSelection(inputs_.selection);
364
365 bool property_trees_changed_on_active_tree =
366 tree_impl->IsActiveTree() && tree_impl->property_trees()->changed;
367 // Property trees may store damage status. We preserve the sync tree damage
368 // status by pushing the damage status from sync tree property trees to main
369 // thread property trees or by moving it onto the layers.
370 if (inputs_.root_layer && property_trees_changed_on_active_tree) {
371 if (property_trees_.sequence_number ==
372 tree_impl->property_trees()->sequence_number)
373 tree_impl->property_trees()->PushChangeTrackingTo(&property_trees_);
374 else
375 tree_impl->MoveChangeTrackingToLayers();
376 }
377 // Setting property trees must happen before pushing the page scale.
378 tree_impl->SetPropertyTrees(&property_trees_);
379
380 tree_impl->PushPageScaleFromMainThread(inputs_.page_scale_factor,
381 inputs_.min_page_scale_factor,
382 inputs_.max_page_scale_factor);
383
384 tree_impl->set_top_controls_shrink_blink_size(
385 inputs_.top_controls_shrink_blink_size);
386 tree_impl->set_top_controls_height(inputs_.top_controls_height);
387 tree_impl->PushTopControlsFromMainThread(inputs_.top_controls_shown_ratio);
388 tree_impl->elastic_overscroll()->PushFromMainThread(elastic_overscroll_);
389 if (tree_impl->IsActiveTree())
390 tree_impl->elastic_overscroll()->PushPendingToActive();
391
392 tree_impl->set_painted_device_scale_factor(
393 inputs_.painted_device_scale_factor);
394
395 if (inputs_.pending_page_scale_animation) {
396 tree_impl->SetPendingPageScaleAnimation(
397 std::move(inputs_.pending_page_scale_animation));
398 }
399
400 DCHECK(!tree_impl->ViewportSizeInvalid());
401
402 tree_impl->set_has_ever_been_drawn(false);
403 }
404
75 void LayerTree::ToProtobuf(proto::LayerTree* proto) { 405 void LayerTree::ToProtobuf(proto::LayerTree* proto) {
406 LayerProtoConverter::SerializeLayerHierarchy(inputs_.root_layer,
407 proto->mutable_root_layer());
408
76 for (auto* layer : layers_that_should_push_properties_) { 409 for (auto* layer : layers_that_should_push_properties_) {
77 proto->add_layers_that_should_push_properties(layer->id()); 410 proto->add_layers_that_should_push_properties(layer->id());
78 } 411 }
79 proto->set_in_paint_layer_contents(in_paint_layer_contents()); 412 proto->set_in_paint_layer_contents(in_paint_layer_contents());
413
414 proto->set_needs_full_tree_sync(needs_full_tree_sync_);
415 proto->set_needs_meta_info_recomputation(needs_meta_info_recomputation_);
416 proto->set_hud_layer_id(hud_layer_ ? hud_layer_->id() : Layer::INVALID_ID);
417
418 // Viewport layers.
419 proto->set_overscroll_elasticity_layer_id(
420 inputs_.overscroll_elasticity_layer
421 ? inputs_.overscroll_elasticity_layer->id()
422 : Layer::INVALID_ID);
423 proto->set_page_scale_layer_id(inputs_.page_scale_layer
424 ? inputs_.page_scale_layer->id()
425 : Layer::INVALID_ID);
426 proto->set_inner_viewport_scroll_layer_id(
427 inputs_.inner_viewport_scroll_layer
428 ? inputs_.inner_viewport_scroll_layer->id()
429 : Layer::INVALID_ID);
430 proto->set_outer_viewport_scroll_layer_id(
431 inputs_.outer_viewport_scroll_layer
432 ? inputs_.outer_viewport_scroll_layer->id()
433 : Layer::INVALID_ID);
434
435 SizeToProto(inputs_.device_viewport_size,
436 proto->mutable_device_viewport_size());
437 proto->set_top_controls_shrink_blink_size(
438 inputs_.top_controls_shrink_blink_size);
439 proto->set_top_controls_height(inputs_.top_controls_height);
440 proto->set_top_controls_shown_ratio(inputs_.top_controls_shown_ratio);
441 proto->set_device_scale_factor(inputs_.device_scale_factor);
442 proto->set_painted_device_scale_factor(inputs_.painted_device_scale_factor);
443 proto->set_page_scale_factor(inputs_.page_scale_factor);
444 proto->set_min_page_scale_factor(inputs_.min_page_scale_factor);
445 proto->set_max_page_scale_factor(inputs_.max_page_scale_factor);
446
447 proto->set_background_color(inputs_.background_color);
448 proto->set_has_transparent_background(inputs_.has_transparent_background);
449 proto->set_have_scroll_event_handlers(inputs_.have_scroll_event_handlers);
450 proto->set_wheel_event_listener_properties(static_cast<uint32_t>(
451 event_listener_properties(EventListenerClass::kMouseWheel)));
452 proto->set_touch_start_or_move_event_listener_properties(
453 static_cast<uint32_t>(
454 event_listener_properties(EventListenerClass::kTouchStartOrMove)));
455 proto->set_touch_end_or_cancel_event_listener_properties(
456 static_cast<uint32_t>(
457 event_listener_properties(EventListenerClass::kTouchEndOrCancel)));
458
459 LayerSelectionToProtobuf(inputs_.selection, proto->mutable_selection());
460 property_trees_.ToProtobuf(proto->mutable_property_trees());
80 } 461 }
81 462
82 void LayerTree::FromProtobuf(const proto::LayerTree& proto) { 463 void LayerTree::FromProtobuf(const proto::LayerTree& proto) {
464 // Layer hierarchy.
465 scoped_refptr<Layer> new_root_layer =
466 LayerProtoConverter::DeserializeLayerHierarchy(
467 inputs_.root_layer, proto.root_layer(), layer_tree_host_);
468 if (inputs_.root_layer != new_root_layer) {
469 inputs_.root_layer = new_root_layer;
470 }
471
83 for (auto layer_id : proto.layers_that_should_push_properties()) { 472 for (auto layer_id : proto.layers_that_should_push_properties()) {
84 AddLayerShouldPushProperties(layer_id_map_[layer_id]); 473 AddLayerShouldPushProperties(layer_id_map_[layer_id]);
85 } 474 }
86 in_paint_layer_contents_ = proto.in_paint_layer_contents(); 475 in_paint_layer_contents_ = proto.in_paint_layer_contents();
476
477 needs_full_tree_sync_ = proto.needs_full_tree_sync();
478 needs_meta_info_recomputation_ = proto.needs_meta_info_recomputation();
479
480 inputs_.overscroll_elasticity_layer =
481 UpdateAndGetLayer(inputs_.overscroll_elasticity_layer.get(),
482 proto.overscroll_elasticity_layer_id(), this);
483 inputs_.page_scale_layer = UpdateAndGetLayer(
484 inputs_.page_scale_layer.get(), proto.page_scale_layer_id(), this);
485 inputs_.inner_viewport_scroll_layer =
486 UpdateAndGetLayer(inputs_.inner_viewport_scroll_layer.get(),
487 proto.inner_viewport_scroll_layer_id(), this);
488 inputs_.outer_viewport_scroll_layer =
489 UpdateAndGetLayer(inputs_.outer_viewport_scroll_layer.get(),
490 proto.outer_viewport_scroll_layer_id(), this);
491
492 inputs_.device_viewport_size = ProtoToSize(proto.device_viewport_size());
493 inputs_.top_controls_shrink_blink_size =
494 proto.top_controls_shrink_blink_size();
495 inputs_.top_controls_height = proto.top_controls_height();
496 inputs_.top_controls_shown_ratio = proto.top_controls_shown_ratio();
497 inputs_.device_scale_factor = proto.device_scale_factor();
498 inputs_.painted_device_scale_factor = proto.painted_device_scale_factor();
499 inputs_.page_scale_factor = proto.page_scale_factor();
500 inputs_.min_page_scale_factor = proto.min_page_scale_factor();
501 inputs_.max_page_scale_factor = proto.max_page_scale_factor();
502 inputs_.background_color = proto.background_color();
503 inputs_.has_transparent_background = proto.has_transparent_background();
504 inputs_.have_scroll_event_handlers = proto.have_scroll_event_handlers();
505 inputs_.event_listener_properties[static_cast<size_t>(
506 EventListenerClass::kMouseWheel)] =
507 static_cast<EventListenerProperties>(
508 proto.wheel_event_listener_properties());
509 inputs_.event_listener_properties[static_cast<size_t>(
510 EventListenerClass::kTouchStartOrMove)] =
511 static_cast<EventListenerProperties>(
512 proto.touch_start_or_move_event_listener_properties());
513 inputs_.event_listener_properties[static_cast<size_t>(
514 EventListenerClass::kTouchEndOrCancel)] =
515 static_cast<EventListenerProperties>(
516 proto.touch_end_or_cancel_event_listener_properties());
517
518 hud_layer_ = static_cast<HeadsUpDisplayLayer*>(
519 UpdateAndGetLayer(hud_layer_.get(), proto.hud_layer_id(), this));
520
521 LayerSelectionFromProtobuf(&inputs_.selection, proto.selection());
522
523 // It is required to create new PropertyTrees before deserializing it.
524 property_trees_ = PropertyTrees();
525 property_trees_.FromProtobuf(proto.property_trees());
526
527 // Forcefully override the sequence number of all layers in the tree to have
528 // a valid sequence number. Changing the sequence number for a layer does not
529 // need a commit, so the value will become out of date for layers that are not
530 // updated for other reasons. All layers that at this point are part of the
531 // layer tree are valid, so it is OK that they have a valid sequence number.
532 int seq_num = property_trees_.sequence_number;
533 LayerTreeHostCommon::CallFunctionForEveryLayer(
534 layer_tree_host_, [seq_num](Layer* layer) {
535 layer->set_property_tree_sequence_number(seq_num);
536 });
87 } 537 }
88 538
89 } // namespace cc 539 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698