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

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

Issue 1973083002: Use element id's for animations (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: respond to reviewer feedback. Created 4 years, 6 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 2011 The Chromium Authors. All rights reserved. 1 // Copyright 2011 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_impl.h" 5 #include "cc/trees/layer_tree_impl.h"
6 6
7 #include <stddef.h> 7 #include <stddef.h>
8 #include <stdint.h> 8 #include <stdint.h>
9 9
10 #include <algorithm> 10 #include <algorithm>
(...skipping 442 matching lines...) Expand 10 before | Expand all | Expand 10 after
453 } 453 }
454 454
455 LayerListReverseIterator<LayerImpl> LayerTreeImpl::rbegin() { 455 LayerListReverseIterator<LayerImpl> LayerTreeImpl::rbegin() {
456 return LayerListReverseIterator<LayerImpl>(root_layer_); 456 return LayerListReverseIterator<LayerImpl>(root_layer_);
457 } 457 }
458 458
459 LayerListReverseIterator<LayerImpl> LayerTreeImpl::rend() { 459 LayerListReverseIterator<LayerImpl> LayerTreeImpl::rend() {
460 return LayerListReverseIterator<LayerImpl>(nullptr); 460 return LayerListReverseIterator<LayerImpl>(nullptr);
461 } 461 }
462 462
463 LayerImpl* LayerTreeImpl::LayerByElementId(ElementId element_id) const {
464 auto iter = element_layers_map_.find(element_id);
465 if (iter == element_layers_map_.end())
466 return nullptr;
467
468 return iter->second;
469 }
470
463 void LayerTreeImpl::AddToElementMap(LayerImpl* layer) { 471 void LayerTreeImpl::AddToElementMap(LayerImpl* layer) {
464 if (!layer->element_id() || !layer->mutable_properties()) 472 if (!layer->element_id())
465 return; 473 return;
466 474
467 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("compositor-worker"), 475 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("compositor-worker"),
468 "LayerTreeImpl::AddToElementMap", "element_id", 476 "LayerTreeImpl::AddToElementMap", "element",
469 layer->element_id(), "layer_id", layer->id()); 477 layer->element_id().AsValue().release(), "layer_id",
478 layer->id());
470 479
471 ElementLayers& layers = element_layers_map_[layer->element_id()]; 480 element_layers_map_[layer->element_id()] = layer;
472 if ((!layers.main || layer->IsActive()) && !layer->scrollable()) { 481
473 layers.main = layer; 482 layer_tree_host_impl_->animation_host()->RegisterElement(
474 } else if ((!layers.scroll || layer->IsActive()) && layer->scrollable()) { 483 layer->element_id(),
475 TRACE_EVENT2("compositor-worker", "LayerTreeImpl::AddToElementMap scroll", 484 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING);
476 "element_id", layer->element_id(), "layer_id", layer->id());
477 layers.scroll = layer;
478 }
479 } 485 }
480 486
481 void LayerTreeImpl::RemoveFromElementMap(LayerImpl* layer) { 487 void LayerTreeImpl::RemoveFromElementMap(LayerImpl* layer) {
482 if (!layer->element_id()) 488 if (!layer->element_id())
483 return; 489 return;
484 490
485 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("compositor-worker"), 491 TRACE_EVENT2(TRACE_DISABLED_BY_DEFAULT("compositor-worker"),
486 "LayerTreeImpl::RemoveFromElementMap", "element_id", 492 "LayerTreeImpl::RemoveFromElementMap", "element",
487 layer->element_id(), "layer_id", layer->id()); 493 layer->element_id().AsValue().release(), "layer_id",
494 layer->id());
488 495
489 ElementLayers& layers = element_layers_map_[layer->element_id()]; 496 layer_tree_host_impl_->animation_host()->UnregisterElement(
490 if (!layer->scrollable()) 497 layer->element_id(),
491 layers.main = nullptr; 498 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING);
492 if (layer->scrollable())
493 layers.scroll = nullptr;
494 499
495 if (!layers.main && !layers.scroll) 500 element_layers_map_.erase(layer->element_id());
496 element_layers_map_.erase(layer->element_id());
497 } 501 }
498 502
499 void LayerTreeImpl::AddToOpacityAnimationsMap(int id, float opacity) { 503 void LayerTreeImpl::AddToOpacityAnimationsMap(int id, float opacity) {
500 opacity_animations_map_[id] = opacity; 504 opacity_animations_map_[id] = opacity;
501 } 505 }
502 506
503 void LayerTreeImpl::AddToTransformAnimationsMap(int id, 507 void LayerTreeImpl::AddToTransformAnimationsMap(int id,
504 gfx::Transform transform) { 508 gfx::Transform transform) {
505 transform_animations_map_[id] = transform; 509 transform_animations_map_[id] = transform;
506 } 510 }
507 511
508 LayerTreeImpl::ElementLayers LayerTreeImpl::GetMutableLayers(
509 uint64_t element_id) {
510 auto iter = element_layers_map_.find(element_id);
511 if (iter == element_layers_map_.end())
512 return ElementLayers();
513
514 return iter->second;
515 }
516
517 LayerImpl* LayerTreeImpl::InnerViewportContainerLayer() const { 512 LayerImpl* LayerTreeImpl::InnerViewportContainerLayer() const {
518 return InnerViewportScrollLayer() 513 return InnerViewportScrollLayer()
519 ? InnerViewportScrollLayer()->scroll_clip_layer() 514 ? InnerViewportScrollLayer()->scroll_clip_layer()
520 : NULL; 515 : NULL;
521 } 516 }
522 517
523 LayerImpl* LayerTreeImpl::OuterViewportContainerLayer() const { 518 LayerImpl* LayerTreeImpl::OuterViewportContainerLayer() const {
524 return OuterViewportScrollLayer() 519 return OuterViewportScrollLayer()
525 ? OuterViewportScrollLayer()->scroll_clip_layer() 520 ? OuterViewportScrollLayer()->scroll_clip_layer()
526 : NULL; 521 : NULL;
(...skipping 278 matching lines...) Expand 10 before | Expand all | Expand 10 after
805 outer_viewport_scroll_layer_id_ = outer_viewport_scroll_layer_id; 800 outer_viewport_scroll_layer_id_ = outer_viewport_scroll_layer_id;
806 } 801 }
807 802
808 void LayerTreeImpl::ClearViewportLayers() { 803 void LayerTreeImpl::ClearViewportLayers() {
809 overscroll_elasticity_layer_id_ = Layer::INVALID_ID; 804 overscroll_elasticity_layer_id_ = Layer::INVALID_ID;
810 page_scale_layer_id_ = Layer::INVALID_ID; 805 page_scale_layer_id_ = Layer::INVALID_ID;
811 inner_viewport_scroll_layer_id_ = Layer::INVALID_ID; 806 inner_viewport_scroll_layer_id_ = Layer::INVALID_ID;
812 outer_viewport_scroll_layer_id_ = Layer::INVALID_ID; 807 outer_viewport_scroll_layer_id_ = Layer::INVALID_ID;
813 } 808 }
814 809
810 // For unit tests, we use the layer's id as its element id.
811 static void SetElementIdForTesting(LayerImpl* layer) {
812 layer->SetElementId(LayerIdToElementIdForTesting(layer->id()));
813 }
814
815 void LayerTreeImpl::SetElementIdsForTesting() {
816 LayerTreeHostCommon::CallFunctionForEveryLayer(this, SetElementIdForTesting);
817 }
818
815 bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) { 819 bool LayerTreeImpl::UpdateDrawProperties(bool update_lcd_text) {
816 if (!needs_update_draw_properties_) 820 if (!needs_update_draw_properties_)
817 return true; 821 return true;
818 822
819 // Calling UpdateDrawProperties must clear this flag, so there can be no 823 // Calling UpdateDrawProperties must clear this flag, so there can be no
820 // early outs before this. 824 // early outs before this.
821 needs_update_draw_properties_ = false; 825 needs_update_draw_properties_ = false;
822 826
823 // For max_texture_size. When the renderer is re-created in 827 // For max_texture_size. When the renderer is re-created in
824 // CreateAndSetRenderer, the needs update draw properties flag is set 828 // CreateAndSetRenderer, the needs update draw properties flag is set
(...skipping 212 matching lines...) Expand 10 before | Expand all | Expand 10 after
1037 } 1041 }
1038 1042
1039 bool LayerTreeImpl::LayerNeedsPushPropertiesForTesting(LayerImpl* layer) { 1043 bool LayerTreeImpl::LayerNeedsPushPropertiesForTesting(LayerImpl* layer) {
1040 return layers_that_should_push_properties_.find(layer) != 1044 return layers_that_should_push_properties_.find(layer) !=
1041 layers_that_should_push_properties_.end(); 1045 layers_that_should_push_properties_.end();
1042 } 1046 }
1043 1047
1044 void LayerTreeImpl::RegisterLayer(LayerImpl* layer) { 1048 void LayerTreeImpl::RegisterLayer(LayerImpl* layer) {
1045 DCHECK(!LayerById(layer->id())); 1049 DCHECK(!LayerById(layer->id()));
1046 layer_id_map_[layer->id()] = layer; 1050 layer_id_map_[layer->id()] = layer;
1047 layer_tree_host_impl_->animation_host()->RegisterElement(
1048 layer->id(),
1049 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING);
1050 } 1051 }
1051 1052
1052 void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) { 1053 void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) {
1053 DCHECK(LayerById(layer->id())); 1054 DCHECK(LayerById(layer->id()));
1054 layer_tree_host_impl_->animation_host()->UnregisterElement(
1055 layer->id(),
1056 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING);
1057 layer_id_map_.erase(layer->id()); 1055 layer_id_map_.erase(layer->id());
1058 DCHECK_NE(root_layer_, layer); 1056 DCHECK_NE(root_layer_, layer);
1059 } 1057 }
1060 1058
1061 // These manage ownership of the LayerImpl. 1059 // These manage ownership of the LayerImpl.
1062 void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) { 1060 void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) {
1063 DCHECK(std::find(layers_->begin(), layers_->end(), layer) == layers_->end()); 1061 DCHECK(std::find(layers_->begin(), layers_->end(), layer) == layers_->end());
1064 layers_->push_back(std::move(layer)); 1062 layers_->push_back(std::move(layer));
1065 set_needs_update_draw_properties(); 1063 set_needs_update_draw_properties();
1066 } 1064 }
(...skipping 861 matching lines...) Expand 10 before | Expand all | Expand 10 after
1928 1926
1929 std::unique_ptr<PendingPageScaleAnimation> 1927 std::unique_ptr<PendingPageScaleAnimation>
1930 LayerTreeImpl::TakePendingPageScaleAnimation() { 1928 LayerTreeImpl::TakePendingPageScaleAnimation() {
1931 return std::move(pending_page_scale_animation_); 1929 return std::move(pending_page_scale_animation_);
1932 } 1930 }
1933 1931
1934 bool LayerTreeImpl::IsAnimatingFilterProperty(const LayerImpl* layer) const { 1932 bool LayerTreeImpl::IsAnimatingFilterProperty(const LayerImpl* layer) const {
1935 ElementListType list_type = 1933 ElementListType list_type =
1936 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1934 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1937 return layer_tree_host_impl_->animation_host()->IsAnimatingFilterProperty( 1935 return layer_tree_host_impl_->animation_host()->IsAnimatingFilterProperty(
1938 layer->id(), list_type); 1936 layer->element_id(), list_type);
1939 } 1937 }
1940 1938
1941 bool LayerTreeImpl::IsAnimatingOpacityProperty(const LayerImpl* layer) const { 1939 bool LayerTreeImpl::IsAnimatingOpacityProperty(const LayerImpl* layer) const {
1942 ElementListType list_type = 1940 ElementListType list_type =
1943 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1941 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1944 return layer_tree_host_impl_->animation_host()->IsAnimatingOpacityProperty( 1942 return layer_tree_host_impl_->animation_host()->IsAnimatingOpacityProperty(
1945 layer->id(), list_type); 1943 layer->element_id(), list_type);
1946 } 1944 }
1947 1945
1948 bool LayerTreeImpl::IsAnimatingTransformProperty(const LayerImpl* layer) const { 1946 bool LayerTreeImpl::IsAnimatingTransformProperty(const LayerImpl* layer) const {
1949 ElementListType list_type = 1947 ElementListType list_type =
1950 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1948 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1951 return layer_tree_host_impl_->animation_host()->IsAnimatingTransformProperty( 1949 return layer_tree_host_impl_->animation_host()->IsAnimatingTransformProperty(
1952 layer->id(), list_type); 1950 layer->element_id(), list_type);
1953 } 1951 }
1954 1952
1955 bool LayerTreeImpl::HasPotentiallyRunningFilterAnimation( 1953 bool LayerTreeImpl::HasPotentiallyRunningFilterAnimation(
1956 const LayerImpl* layer) const { 1954 const LayerImpl* layer) const {
1957 ElementListType list_type = 1955 ElementListType list_type =
1958 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1956 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1959 return layer_tree_host_impl_->animation_host() 1957 return layer_tree_host_impl_->animation_host()
1960 ->HasPotentiallyRunningFilterAnimation(layer->id(), list_type); 1958 ->HasPotentiallyRunningFilterAnimation(layer->element_id(), list_type);
1961 } 1959 }
1962 1960
1963 bool LayerTreeImpl::HasPotentiallyRunningOpacityAnimation( 1961 bool LayerTreeImpl::HasPotentiallyRunningOpacityAnimation(
1964 const LayerImpl* layer) const { 1962 const LayerImpl* layer) const {
1965 ElementListType list_type = 1963 ElementListType list_type =
1966 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1964 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1967 return layer_tree_host_impl_->animation_host() 1965 return layer_tree_host_impl_->animation_host()
1968 ->HasPotentiallyRunningOpacityAnimation(layer->id(), list_type); 1966 ->HasPotentiallyRunningOpacityAnimation(layer->element_id(), list_type);
1969 } 1967 }
1970 1968
1971 bool LayerTreeImpl::HasPotentiallyRunningTransformAnimation( 1969 bool LayerTreeImpl::HasPotentiallyRunningTransformAnimation(
1972 const LayerImpl* layer) const { 1970 const LayerImpl* layer) const {
1973 ElementListType list_type = 1971 ElementListType list_type =
1974 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1972 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1975 return layer_tree_host_impl_->animation_host() 1973 return layer_tree_host_impl_->animation_host()
1976 ->HasPotentiallyRunningTransformAnimation(layer->id(), list_type); 1974 ->HasPotentiallyRunningTransformAnimation(layer->element_id(), list_type);
1977 } 1975 }
1978 1976
1979 bool LayerTreeImpl::HasAnyAnimationTargetingProperty( 1977 bool LayerTreeImpl::HasAnyAnimationTargetingProperty(
1980 const LayerImpl* layer, 1978 const LayerImpl* layer,
1981 TargetProperty::Type property) const { 1979 TargetProperty::Type property) const {
1982 return layer_tree_host_impl_->animation_host() 1980 return layer_tree_host_impl_->animation_host()
1983 ->HasAnyAnimationTargetingProperty(layer->id(), property); 1981 ->HasAnyAnimationTargetingProperty(layer->element_id(), property);
1984 } 1982 }
1985 1983
1986 bool LayerTreeImpl::AnimationsPreserveAxisAlignment( 1984 bool LayerTreeImpl::AnimationsPreserveAxisAlignment(
1987 const LayerImpl* layer) const { 1985 const LayerImpl* layer) const {
1988 return layer_tree_host_impl_->animation_host() 1986 return layer_tree_host_impl_->animation_host()
1989 ->AnimationsPreserveAxisAlignment(layer->id()); 1987 ->AnimationsPreserveAxisAlignment(layer->element_id());
1990 } 1988 }
1991 1989
1992 bool LayerTreeImpl::HasOnlyTranslationTransforms(const LayerImpl* layer) const { 1990 bool LayerTreeImpl::HasOnlyTranslationTransforms(const LayerImpl* layer) const {
1993 ElementListType list_type = 1991 ElementListType list_type =
1994 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1992 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1995 return layer_tree_host_impl_->animation_host()->HasOnlyTranslationTransforms( 1993 return layer_tree_host_impl_->animation_host()->HasOnlyTranslationTransforms(
1996 layer->id(), list_type); 1994 layer->element_id(), list_type);
1997 } 1995 }
1998 1996
1999 bool LayerTreeImpl::MaximumTargetScale(const LayerImpl* layer, 1997 bool LayerTreeImpl::MaximumTargetScale(const LayerImpl* layer,
2000 float* max_scale) const { 1998 float* max_scale) const {
2001 *max_scale = 0.f; 1999 *max_scale = 0.f;
2002 ElementListType list_type = 2000 ElementListType list_type =
2003 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 2001 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
2004 return layer_tree_host_impl_->animation_host()->MaximumTargetScale( 2002 return layer_tree_host_impl_->animation_host()->MaximumTargetScale(
2005 layer->id(), list_type, max_scale); 2003 layer->element_id(), list_type, max_scale);
2006 } 2004 }
2007 2005
2008 bool LayerTreeImpl::AnimationStartScale(const LayerImpl* layer, 2006 bool LayerTreeImpl::AnimationStartScale(const LayerImpl* layer,
2009 float* start_scale) const { 2007 float* start_scale) const {
2010 *start_scale = 0.f; 2008 *start_scale = 0.f;
2011 ElementListType list_type = 2009 ElementListType list_type =
2012 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 2010 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
2013 return layer_tree_host_impl_->animation_host()->AnimationStartScale( 2011 return layer_tree_host_impl_->animation_host()->AnimationStartScale(
2014 layer->id(), list_type, start_scale); 2012 layer->element_id(), list_type, start_scale);
2015 } 2013 }
2016 2014
2017 bool LayerTreeImpl::HasFilterAnimationThatInflatesBounds( 2015 bool LayerTreeImpl::HasFilterAnimationThatInflatesBounds(
2018 const LayerImpl* layer) const { 2016 const LayerImpl* layer) const {
2019 return layer_tree_host_impl_->animation_host() 2017 return layer_tree_host_impl_->animation_host()
2020 ->HasFilterAnimationThatInflatesBounds(layer->id()); 2018 ->HasFilterAnimationThatInflatesBounds(layer->element_id());
2021 } 2019 }
2022 2020
2023 bool LayerTreeImpl::HasTransformAnimationThatInflatesBounds( 2021 bool LayerTreeImpl::HasTransformAnimationThatInflatesBounds(
2024 const LayerImpl* layer) const { 2022 const LayerImpl* layer) const {
2025 return layer_tree_host_impl_->animation_host() 2023 return layer_tree_host_impl_->animation_host()
2026 ->HasTransformAnimationThatInflatesBounds(layer->id()); 2024 ->HasTransformAnimationThatInflatesBounds(layer->element_id());
2027 } 2025 }
2028 2026
2029 bool LayerTreeImpl::HasAnimationThatInflatesBounds( 2027 bool LayerTreeImpl::HasAnimationThatInflatesBounds(
2030 const LayerImpl* layer) const { 2028 const LayerImpl* layer) const {
2031 return layer_tree_host_impl_->animation_host() 2029 return layer_tree_host_impl_->animation_host()
2032 ->HasAnimationThatInflatesBounds(layer->id()); 2030 ->HasAnimationThatInflatesBounds(layer->element_id());
2033 } 2031 }
2034 2032
2035 bool LayerTreeImpl::FilterAnimationBoundsForBox(const LayerImpl* layer, 2033 bool LayerTreeImpl::FilterAnimationBoundsForBox(const LayerImpl* layer,
2036 const gfx::BoxF& box, 2034 const gfx::BoxF& box,
2037 gfx::BoxF* bounds) const { 2035 gfx::BoxF* bounds) const {
2038 return layer_tree_host_impl_->animation_host()->FilterAnimationBoundsForBox( 2036 return layer_tree_host_impl_->animation_host()->FilterAnimationBoundsForBox(
2039 layer->id(), box, bounds); 2037 layer->element_id(), box, bounds);
2040 } 2038 }
2041 2039
2042 bool LayerTreeImpl::TransformAnimationBoundsForBox(const LayerImpl* layer, 2040 bool LayerTreeImpl::TransformAnimationBoundsForBox(const LayerImpl* layer,
2043 const gfx::BoxF& box, 2041 const gfx::BoxF& box,
2044 gfx::BoxF* bounds) const { 2042 gfx::BoxF* bounds) const {
2045 *bounds = gfx::BoxF(); 2043 *bounds = gfx::BoxF();
2046 return layer_tree_host_impl_->animation_host() 2044 return layer_tree_host_impl_->animation_host()
2047 ->TransformAnimationBoundsForBox(layer->id(), box, bounds); 2045 ->TransformAnimationBoundsForBox(layer->element_id(), box, bounds);
2048 } 2046 }
2049 2047
2050 void LayerTreeImpl::ScrollAnimationAbort(bool needs_completion) { 2048 void LayerTreeImpl::ScrollAnimationAbort(bool needs_completion) {
2051 layer_tree_host_impl_->animation_host()->ScrollAnimationAbort( 2049 layer_tree_host_impl_->animation_host()->ScrollAnimationAbort(
2052 needs_completion); 2050 needs_completion);
2053 } 2051 }
2054 2052
2055 void LayerTreeImpl::ResetAllChangeTracking() { 2053 void LayerTreeImpl::ResetAllChangeTracking() {
2056 layers_that_should_push_properties_.clear(); 2054 layers_that_should_push_properties_.clear();
2057 for (auto* layer : *this) 2055 for (auto* layer : *this)
2058 layer->ResetChangeTracking(); 2056 layer->ResetChangeTracking();
2059 property_trees_.ResetAllChangeTracking(); 2057 property_trees_.ResetAllChangeTracking();
2060 } 2058 }
2061 2059
2062 } // namespace cc 2060 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698