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

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: updated CompositorMutableStateTest. 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 211 matching lines...) Expand 10 before | Expand all | Expand 10 after
1036 } 1040 }
1037 1041
1038 bool LayerTreeImpl::LayerNeedsPushPropertiesForTesting(LayerImpl* layer) { 1042 bool LayerTreeImpl::LayerNeedsPushPropertiesForTesting(LayerImpl* layer) {
1039 return layers_that_should_push_properties_.find(layer) != 1043 return layers_that_should_push_properties_.find(layer) !=
1040 layers_that_should_push_properties_.end(); 1044 layers_that_should_push_properties_.end();
1041 } 1045 }
1042 1046
1043 void LayerTreeImpl::RegisterLayer(LayerImpl* layer) { 1047 void LayerTreeImpl::RegisterLayer(LayerImpl* layer) {
1044 DCHECK(!LayerById(layer->id())); 1048 DCHECK(!LayerById(layer->id()));
1045 layer_id_map_[layer->id()] = layer; 1049 layer_id_map_[layer->id()] = layer;
1046 layer_tree_host_impl_->animation_host()->RegisterElement(
1047 layer->id(),
1048 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING);
1049 } 1050 }
1050 1051
1051 void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) { 1052 void LayerTreeImpl::UnregisterLayer(LayerImpl* layer) {
1052 DCHECK(LayerById(layer->id())); 1053 DCHECK(LayerById(layer->id()));
1053 layer_tree_host_impl_->animation_host()->UnregisterElement(
1054 layer->id(),
1055 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING);
1056 layer_id_map_.erase(layer->id()); 1054 layer_id_map_.erase(layer->id());
1057 DCHECK_NE(root_layer_, layer); 1055 DCHECK_NE(root_layer_, layer);
1058 } 1056 }
1059 1057
1060 // These manage ownership of the LayerImpl. 1058 // These manage ownership of the LayerImpl.
1061 void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) { 1059 void LayerTreeImpl::AddLayer(std::unique_ptr<LayerImpl> layer) {
1062 DCHECK(std::find(layers_->begin(), layers_->end(), layer) == layers_->end()); 1060 DCHECK(std::find(layers_->begin(), layers_->end(), layer) == layers_->end());
1063 layers_->push_back(std::move(layer)); 1061 layers_->push_back(std::move(layer));
1064 set_needs_update_draw_properties(); 1062 set_needs_update_draw_properties();
1065 } 1063 }
(...skipping 868 matching lines...) Expand 10 before | Expand all | Expand 10 after
1934 1932
1935 std::unique_ptr<PendingPageScaleAnimation> 1933 std::unique_ptr<PendingPageScaleAnimation>
1936 LayerTreeImpl::TakePendingPageScaleAnimation() { 1934 LayerTreeImpl::TakePendingPageScaleAnimation() {
1937 return std::move(pending_page_scale_animation_); 1935 return std::move(pending_page_scale_animation_);
1938 } 1936 }
1939 1937
1940 bool LayerTreeImpl::IsAnimatingFilterProperty(const LayerImpl* layer) const { 1938 bool LayerTreeImpl::IsAnimatingFilterProperty(const LayerImpl* layer) const {
1941 ElementListType list_type = 1939 ElementListType list_type =
1942 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1940 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1943 return layer_tree_host_impl_->animation_host()->IsAnimatingFilterProperty( 1941 return layer_tree_host_impl_->animation_host()->IsAnimatingFilterProperty(
1944 layer->id(), list_type); 1942 layer->element_id(), list_type);
1945 } 1943 }
1946 1944
1947 bool LayerTreeImpl::IsAnimatingOpacityProperty(const LayerImpl* layer) const { 1945 bool LayerTreeImpl::IsAnimatingOpacityProperty(const LayerImpl* layer) const {
1948 ElementListType list_type = 1946 ElementListType list_type =
1949 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1947 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1950 return layer_tree_host_impl_->animation_host()->IsAnimatingOpacityProperty( 1948 return layer_tree_host_impl_->animation_host()->IsAnimatingOpacityProperty(
1951 layer->id(), list_type); 1949 layer->element_id(), list_type);
1952 } 1950 }
1953 1951
1954 bool LayerTreeImpl::IsAnimatingTransformProperty(const LayerImpl* layer) const { 1952 bool LayerTreeImpl::IsAnimatingTransformProperty(const LayerImpl* layer) const {
1955 ElementListType list_type = 1953 ElementListType list_type =
1956 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1954 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1957 return layer_tree_host_impl_->animation_host()->IsAnimatingTransformProperty( 1955 return layer_tree_host_impl_->animation_host()->IsAnimatingTransformProperty(
1958 layer->id(), list_type); 1956 layer->element_id(), list_type);
1959 } 1957 }
1960 1958
1961 bool LayerTreeImpl::HasPotentiallyRunningFilterAnimation( 1959 bool LayerTreeImpl::HasPotentiallyRunningFilterAnimation(
1962 const LayerImpl* layer) const { 1960 const LayerImpl* layer) const {
1963 ElementListType list_type = 1961 ElementListType list_type =
1964 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1962 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1965 return layer_tree_host_impl_->animation_host() 1963 return layer_tree_host_impl_->animation_host()
1966 ->HasPotentiallyRunningFilterAnimation(layer->id(), list_type); 1964 ->HasPotentiallyRunningFilterAnimation(layer->element_id(), list_type);
1967 } 1965 }
1968 1966
1969 bool LayerTreeImpl::HasPotentiallyRunningOpacityAnimation( 1967 bool LayerTreeImpl::HasPotentiallyRunningOpacityAnimation(
1970 const LayerImpl* layer) const { 1968 const LayerImpl* layer) const {
1971 ElementListType list_type = 1969 ElementListType list_type =
1972 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1970 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1973 return layer_tree_host_impl_->animation_host() 1971 return layer_tree_host_impl_->animation_host()
1974 ->HasPotentiallyRunningOpacityAnimation(layer->id(), list_type); 1972 ->HasPotentiallyRunningOpacityAnimation(layer->element_id(), list_type);
1975 } 1973 }
1976 1974
1977 bool LayerTreeImpl::HasPotentiallyRunningTransformAnimation( 1975 bool LayerTreeImpl::HasPotentiallyRunningTransformAnimation(
1978 const LayerImpl* layer) const { 1976 const LayerImpl* layer) const {
1979 ElementListType list_type = 1977 ElementListType list_type =
1980 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1978 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
1981 return layer_tree_host_impl_->animation_host() 1979 return layer_tree_host_impl_->animation_host()
1982 ->HasPotentiallyRunningTransformAnimation(layer->id(), list_type); 1980 ->HasPotentiallyRunningTransformAnimation(layer->element_id(), list_type);
1983 } 1981 }
1984 1982
1985 bool LayerTreeImpl::HasAnyAnimationTargetingProperty( 1983 bool LayerTreeImpl::HasAnyAnimationTargetingProperty(
1986 const LayerImpl* layer, 1984 const LayerImpl* layer,
1987 TargetProperty::Type property) const { 1985 TargetProperty::Type property) const {
1988 return layer_tree_host_impl_->animation_host() 1986 return layer_tree_host_impl_->animation_host()
1989 ->HasAnyAnimationTargetingProperty(layer->id(), property); 1987 ->HasAnyAnimationTargetingProperty(layer->element_id(), property);
1990 } 1988 }
1991 1989
1992 bool LayerTreeImpl::AnimationsPreserveAxisAlignment( 1990 bool LayerTreeImpl::AnimationsPreserveAxisAlignment(
1993 const LayerImpl* layer) const { 1991 const LayerImpl* layer) const {
1994 return layer_tree_host_impl_->animation_host() 1992 return layer_tree_host_impl_->animation_host()
1995 ->AnimationsPreserveAxisAlignment(layer->id()); 1993 ->AnimationsPreserveAxisAlignment(layer->element_id());
1996 } 1994 }
1997 1995
1998 bool LayerTreeImpl::HasOnlyTranslationTransforms(const LayerImpl* layer) const { 1996 bool LayerTreeImpl::HasOnlyTranslationTransforms(const LayerImpl* layer) const {
1999 ElementListType list_type = 1997 ElementListType list_type =
2000 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 1998 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
2001 return layer_tree_host_impl_->animation_host()->HasOnlyTranslationTransforms( 1999 return layer_tree_host_impl_->animation_host()->HasOnlyTranslationTransforms(
2002 layer->id(), list_type); 2000 layer->element_id(), list_type);
2003 } 2001 }
2004 2002
2005 bool LayerTreeImpl::MaximumTargetScale(const LayerImpl* layer, 2003 bool LayerTreeImpl::MaximumTargetScale(const LayerImpl* layer,
2006 float* max_scale) const { 2004 float* max_scale) const {
2007 *max_scale = 0.f; 2005 *max_scale = 0.f;
2008 ElementListType list_type = 2006 ElementListType list_type =
2009 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 2007 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
2010 return layer_tree_host_impl_->animation_host()->MaximumTargetScale( 2008 return layer_tree_host_impl_->animation_host()->MaximumTargetScale(
2011 layer->id(), list_type, max_scale); 2009 layer->element_id(), list_type, max_scale);
2012 } 2010 }
2013 2011
2014 bool LayerTreeImpl::AnimationStartScale(const LayerImpl* layer, 2012 bool LayerTreeImpl::AnimationStartScale(const LayerImpl* layer,
2015 float* start_scale) const { 2013 float* start_scale) const {
2016 *start_scale = 0.f; 2014 *start_scale = 0.f;
2017 ElementListType list_type = 2015 ElementListType list_type =
2018 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING; 2016 IsActiveTree() ? ElementListType::ACTIVE : ElementListType::PENDING;
2019 return layer_tree_host_impl_->animation_host()->AnimationStartScale( 2017 return layer_tree_host_impl_->animation_host()->AnimationStartScale(
2020 layer->id(), list_type, start_scale); 2018 layer->element_id(), list_type, start_scale);
2021 } 2019 }
2022 2020
2023 bool LayerTreeImpl::HasFilterAnimationThatInflatesBounds( 2021 bool LayerTreeImpl::HasFilterAnimationThatInflatesBounds(
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 ->HasFilterAnimationThatInflatesBounds(layer->id()); 2024 ->HasFilterAnimationThatInflatesBounds(layer->element_id());
2027 } 2025 }
2028 2026
2029 bool LayerTreeImpl::HasTransformAnimationThatInflatesBounds( 2027 bool LayerTreeImpl::HasTransformAnimationThatInflatesBounds(
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 ->HasTransformAnimationThatInflatesBounds(layer->id()); 2030 ->HasTransformAnimationThatInflatesBounds(layer->element_id());
2033 } 2031 }
2034 2032
2035 bool LayerTreeImpl::HasAnimationThatInflatesBounds( 2033 bool LayerTreeImpl::HasAnimationThatInflatesBounds(
2036 const LayerImpl* layer) const { 2034 const LayerImpl* layer) const {
2037 return layer_tree_host_impl_->animation_host() 2035 return layer_tree_host_impl_->animation_host()
2038 ->HasAnimationThatInflatesBounds(layer->id()); 2036 ->HasAnimationThatInflatesBounds(layer->element_id());
2039 } 2037 }
2040 2038
2041 bool LayerTreeImpl::FilterAnimationBoundsForBox(const LayerImpl* layer, 2039 bool LayerTreeImpl::FilterAnimationBoundsForBox(const LayerImpl* layer,
2042 const gfx::BoxF& box, 2040 const gfx::BoxF& box,
2043 gfx::BoxF* bounds) const { 2041 gfx::BoxF* bounds) const {
2044 return layer_tree_host_impl_->animation_host()->FilterAnimationBoundsForBox( 2042 return layer_tree_host_impl_->animation_host()->FilterAnimationBoundsForBox(
2045 layer->id(), box, bounds); 2043 layer->element_id(), box, bounds);
2046 } 2044 }
2047 2045
2048 bool LayerTreeImpl::TransformAnimationBoundsForBox(const LayerImpl* layer, 2046 bool LayerTreeImpl::TransformAnimationBoundsForBox(const LayerImpl* layer,
2049 const gfx::BoxF& box, 2047 const gfx::BoxF& box,
2050 gfx::BoxF* bounds) const { 2048 gfx::BoxF* bounds) const {
2051 *bounds = gfx::BoxF(); 2049 *bounds = gfx::BoxF();
2052 return layer_tree_host_impl_->animation_host() 2050 return layer_tree_host_impl_->animation_host()
2053 ->TransformAnimationBoundsForBox(layer->id(), box, bounds); 2051 ->TransformAnimationBoundsForBox(layer->element_id(), box, bounds);
2054 } 2052 }
2055 2053
2056 void LayerTreeImpl::ScrollAnimationAbort(bool needs_completion) { 2054 void LayerTreeImpl::ScrollAnimationAbort(bool needs_completion) {
2057 layer_tree_host_impl_->animation_host()->ScrollAnimationAbort( 2055 layer_tree_host_impl_->animation_host()->ScrollAnimationAbort(
2058 needs_completion); 2056 needs_completion);
2059 } 2057 }
2060 2058
2061 void LayerTreeImpl::ResetAllChangeTracking() { 2059 void LayerTreeImpl::ResetAllChangeTracking() {
2062 layers_that_should_push_properties_.clear(); 2060 layers_that_should_push_properties_.clear();
2063 for (auto* layer : *this) 2061 for (auto* layer : *this)
2064 layer->ResetChangeTracking(); 2062 layer->ResetChangeTracking();
2065 property_trees_.ResetAllChangeTracking(); 2063 property_trees_.ResetAllChangeTracking();
2066 } 2064 }
2067 2065
2068 } // namespace cc 2066 } // namespace cc
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698