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

Side by Side Diff: chrome/browser/ui/views/location_bar/location_bar_view.cc

Issue 200783003: [OriginChip] Add animations for the hiding and showing of the chip. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Respond to comments, merge Created 6 years, 9 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 | Annotate | Revision Log
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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 "chrome/browser/ui/views/location_bar/location_bar_view.h" 5 #include "chrome/browser/ui/views/location_bar/location_bar_view.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 #include <map> 8 #include <map>
9 9
10 #include "base/command_line.h" 10 #include "base/command_line.h"
11 #include "base/i18n/rtl.h" 11 #include "base/i18n/rtl.h"
12 #include "base/prefs/pref_service.h" 12 #include "base/prefs/pref_service.h"
13 #include "base/stl_util.h" 13 #include "base/stl_util.h"
14 #include "base/strings/string_split.h"
14 #include "base/strings/utf_string_conversions.h" 15 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/app/chrome_command_ids.h" 16 #include "chrome/app/chrome_command_ids.h"
16 #include "chrome/browser/chrome_notification_types.h" 17 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/command_updater.h" 18 #include "chrome/browser/command_updater.h"
18 #include "chrome/browser/defaults.h" 19 #include "chrome/browser/defaults.h"
19 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h" 20 #include "chrome/browser/extensions/api/omnibox/omnibox_api.h"
20 #include "chrome/browser/extensions/extension_service.h" 21 #include "chrome/browser/extensions/extension_service.h"
21 #include "chrome/browser/extensions/location_bar_controller.h" 22 #include "chrome/browser/extensions/location_bar_controller.h"
22 #include "chrome/browser/extensions/tab_helper.h" 23 #include "chrome/browser/extensions/tab_helper.h"
23 #include "chrome/browser/favicon/favicon_tab_helper.h" 24 #include "chrome/browser/favicon/favicon_tab_helper.h"
24 #include "chrome/browser/profiles/profile.h" 25 #include "chrome/browser/profiles/profile.h"
25 #include "chrome/browser/search/instant_service.h" 26 #include "chrome/browser/search/instant_service.h"
26 #include "chrome/browser/search/instant_service_factory.h" 27 #include "chrome/browser/search/instant_service_factory.h"
27 #include "chrome/browser/search/search.h" 28 #include "chrome/browser/search/search.h"
28 #include "chrome/browser/search_engines/template_url.h" 29 #include "chrome/browser/search_engines/template_url.h"
29 #include "chrome/browser/search_engines/template_url_service.h" 30 #include "chrome/browser/search_engines/template_url_service.h"
30 #include "chrome/browser/search_engines/template_url_service_factory.h" 31 #include "chrome/browser/search_engines/template_url_service_factory.h"
31 #include "chrome/browser/translate/translate_service.h" 32 #include "chrome/browser/translate/translate_service.h"
32 #include "chrome/browser/translate/translate_tab_helper.h" 33 #include "chrome/browser/translate/translate_tab_helper.h"
33 #include "chrome/browser/ui/browser.h" 34 #include "chrome/browser/ui/browser.h"
34 #include "chrome/browser/ui/browser_finder.h" 35 #include "chrome/browser/ui/browser_finder.h"
35 #include "chrome/browser/ui/browser_instant_controller.h" 36 #include "chrome/browser/ui/browser_instant_controller.h"
36 #include "chrome/browser/ui/browser_window.h" 37 #include "chrome/browser/ui/browser_window.h"
37 #include "chrome/browser/ui/omnibox/location_bar_util.h" 38 #include "chrome/browser/ui/omnibox/location_bar_util.h"
38 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" 39 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
39 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h" 40 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
40 #include "chrome/browser/ui/passwords/manage_passwords_bubble_ui_controller.h" 41 #include "chrome/browser/ui/passwords/manage_passwords_bubble_ui_controller.h"
41 #include "chrome/browser/ui/tabs/tab_strip_model.h" 42 #include "chrome/browser/ui/tabs/tab_strip_model.h"
43 #include "chrome/browser/ui/toolbar/origin_chip_info.h"
42 #include "chrome/browser/ui/view_ids.h" 44 #include "chrome/browser/ui/view_ids.h"
43 #include "chrome/browser/ui/views/bookmarks/bookmark_prompt_view.h" 45 #include "chrome/browser/ui/views/bookmarks/bookmark_prompt_view.h"
44 #include "chrome/browser/ui/views/browser_dialogs.h" 46 #include "chrome/browser/ui/views/browser_dialogs.h"
45 #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h" 47 #include "chrome/browser/ui/views/location_bar/content_setting_image_view.h"
46 #include "chrome/browser/ui/views/location_bar/ev_bubble_view.h" 48 #include "chrome/browser/ui/views/location_bar/ev_bubble_view.h"
47 #include "chrome/browser/ui/views/location_bar/generated_credit_card_view.h" 49 #include "chrome/browser/ui/views/location_bar/generated_credit_card_view.h"
48 #include "chrome/browser/ui/views/location_bar/keyword_hint_view.h" 50 #include "chrome/browser/ui/views/location_bar/keyword_hint_view.h"
49 #include "chrome/browser/ui/views/location_bar/location_bar_layout.h" 51 #include "chrome/browser/ui/views/location_bar/location_bar_layout.h"
50 #include "chrome/browser/ui/views/location_bar/location_icon_view.h" 52 #include "chrome/browser/ui/views/location_bar/location_icon_view.h"
51 #include "chrome/browser/ui/views/location_bar/open_pdf_in_reader_view.h" 53 #include "chrome/browser/ui/views/location_bar/open_pdf_in_reader_view.h"
(...skipping 19 matching lines...) Expand all
71 #include "extensions/common/permissions/permissions_data.h" 73 #include "extensions/common/permissions/permissions_data.h"
72 #include "grit/generated_resources.h" 74 #include "grit/generated_resources.h"
73 #include "grit/theme_resources.h" 75 #include "grit/theme_resources.h"
74 #include "ui/accessibility/ax_view_state.h" 76 #include "ui/accessibility/ax_view_state.h"
75 #include "ui/base/dragdrop/drag_drop_types.h" 77 #include "ui/base/dragdrop/drag_drop_types.h"
76 #include "ui/base/l10n/l10n_util.h" 78 #include "ui/base/l10n/l10n_util.h"
77 #include "ui/base/layout.h" 79 #include "ui/base/layout.h"
78 #include "ui/base/resource/resource_bundle.h" 80 #include "ui/base/resource/resource_bundle.h"
79 #include "ui/base/theme_provider.h" 81 #include "ui/base/theme_provider.h"
80 #include "ui/events/event.h" 82 #include "ui/events/event.h"
83 #include "ui/gfx/animation/slide_animation.h"
81 #include "ui/gfx/canvas.h" 84 #include "ui/gfx/canvas.h"
82 #include "ui/gfx/color_utils.h" 85 #include "ui/gfx/color_utils.h"
83 #include "ui/gfx/image/image.h" 86 #include "ui/gfx/image/image.h"
84 #include "ui/gfx/image/image_skia_operations.h" 87 #include "ui/gfx/image/image_skia_operations.h"
85 #include "ui/gfx/skia_util.h" 88 #include "ui/gfx/skia_util.h"
86 #include "ui/gfx/text_utils.h" 89 #include "ui/gfx/text_utils.h"
87 #include "ui/native_theme/native_theme.h" 90 #include "ui/native_theme/native_theme.h"
88 #include "ui/views/background.h" 91 #include "ui/views/background.h"
89 #include "ui/views/border.h" 92 #include "ui/views/border.h"
90 #include "ui/views/button_drag_utils.h" 93 #include "ui/views/button_drag_utils.h"
(...skipping 122 matching lines...) Expand 10 before | Expand all | Expand 10 after
213 generated_credit_card_view_(NULL), 216 generated_credit_card_view_(NULL),
214 open_pdf_in_reader_view_(NULL), 217 open_pdf_in_reader_view_(NULL),
215 manage_passwords_icon_view_(NULL), 218 manage_passwords_icon_view_(NULL),
216 translate_icon_view_(NULL), 219 translate_icon_view_(NULL),
217 star_view_(NULL), 220 star_view_(NULL),
218 search_button_(NULL), 221 search_button_(NULL),
219 is_popup_mode_(is_popup_mode), 222 is_popup_mode_(is_popup_mode),
220 show_focus_rect_(false), 223 show_focus_rect_(false),
221 template_url_service_(NULL), 224 template_url_service_(NULL),
222 animation_offset_(0), 225 animation_offset_(0),
226 animated_host_label_(NULL),
223 weak_ptr_factory_(this) { 227 weak_ptr_factory_(this) {
224 edit_bookmarks_enabled_.Init( 228 edit_bookmarks_enabled_.Init(
225 prefs::kEditBookmarksEnabled, profile->GetPrefs(), 229 prefs::kEditBookmarksEnabled, profile->GetPrefs(),
226 base::Bind(&LocationBarView::Update, base::Unretained(this), 230 base::Bind(&LocationBarView::Update, base::Unretained(this),
227 static_cast<content::WebContents*>(NULL))); 231 static_cast<content::WebContents*>(NULL)));
228 232
229 if (browser_) 233 if (browser_)
230 browser_->search_model()->AddObserver(this); 234 browser_->search_model()->AddObserver(this);
231 } 235 }
232 236
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after
310 ime_inline_autocomplete_view_->SetAutoColorReadabilityEnabled(false); 314 ime_inline_autocomplete_view_->SetAutoColorReadabilityEnabled(false);
311 ime_inline_autocomplete_view_->set_background( 315 ime_inline_autocomplete_view_->set_background(
312 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor( 316 views::Background::CreateSolidBackground(GetNativeTheme()->GetSystemColor(
313 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused))); 317 ui::NativeTheme::kColorId_TextfieldSelectionBackgroundFocused)));
314 ime_inline_autocomplete_view_->SetEnabledColor( 318 ime_inline_autocomplete_view_->SetEnabledColor(
315 GetNativeTheme()->GetSystemColor( 319 GetNativeTheme()->GetSystemColor(
316 ui::NativeTheme::kColorId_TextfieldSelectionColor)); 320 ui::NativeTheme::kColorId_TextfieldSelectionColor));
317 ime_inline_autocomplete_view_->SetVisible(false); 321 ime_inline_autocomplete_view_->SetVisible(false);
318 AddChildView(ime_inline_autocomplete_view_); 322 AddChildView(ime_inline_autocomplete_view_);
319 323
324 animated_host_label_ = new views::Label(base::string16(), font_list);
325 animated_host_label_->SetVisible(false);
326 AddChildView(animated_host_label_);
327
320 origin_chip_view_ = new OriginChipView(this, profile(), font_list); 328 origin_chip_view_ = new OriginChipView(this, profile(), font_list);
321 origin_chip_view_->Init(); 329 origin_chip_view_->Init();
322 origin_chip_view_->SetFocusable(false); 330 origin_chip_view_->SetFocusable(false);
323 origin_chip_view_->set_drag_controller(this); 331 origin_chip_view_->set_drag_controller(this);
324 AddChildView(origin_chip_view_); 332 AddChildView(origin_chip_view_);
325 333
326 const SkColor text_color = GetColor(ToolbarModel::NONE, TEXT); 334 const SkColor text_color = GetColor(ToolbarModel::NONE, TEXT);
327 selected_keyword_view_ = new SelectedKeywordView( 335 selected_keyword_view_ = new SelectedKeywordView(
328 bubble_font_list, text_color, background_color, profile()); 336 bubble_font_list, text_color, background_color, profile());
329 AddChildView(selected_keyword_view_); 337 AddChildView(selected_keyword_view_);
(...skipping 86 matching lines...) Expand 10 before | Expand all | Expand 10 after
416 search_button_border->SetPainter(true, views::Button::STATE_NORMAL, NULL); 424 search_button_border->SetPainter(true, views::Button::STATE_NORMAL, NULL);
417 search_button_border->SetPainter(true, views::Button::STATE_HOVERED, NULL); 425 search_button_border->SetPainter(true, views::Button::STATE_HOVERED, NULL);
418 search_button_border->SetPainter(true, views::Button::STATE_PRESSED, NULL); 426 search_button_border->SetPainter(true, views::Button::STATE_PRESSED, NULL);
419 search_button_border->SetPainter(true, views::Button::STATE_DISABLED, NULL); 427 search_button_border->SetPainter(true, views::Button::STATE_DISABLED, NULL);
420 search_button_->SetBorder(search_button_border.PassAs<views::Border>()); 428 search_button_->SetBorder(search_button_border.PassAs<views::Border>());
421 const int kSearchButtonWidth = 56; 429 const int kSearchButtonWidth = 56;
422 search_button_->set_min_size(gfx::Size(kSearchButtonWidth, 0)); 430 search_button_->set_min_size(gfx::Size(kSearchButtonWidth, 0));
423 search_button_->SetVisible(false); 431 search_button_->SetVisible(false);
424 AddChildView(search_button_); 432 AddChildView(search_button_);
425 433
434 show_url_animation_.reset(new gfx::SlideAnimation(this));
435 show_url_animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
436 show_url_animation_->SetSlideDuration(200);
437
438 hide_url_animation_.reset(new gfx::SlideAnimation(this));
439 hide_url_animation_->SetTweenType(gfx::Tween::FAST_OUT_SLOW_IN);
440 hide_url_animation_->SetSlideDuration(200);
441
426 content::Source<Profile> profile_source = content::Source<Profile>(profile()); 442 content::Source<Profile> profile_source = content::Source<Profile>(profile());
427 registrar_.Add(this, 443 registrar_.Add(this,
428 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, 444 chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED,
429 profile_source); 445 profile_source);
430 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, profile_source); 446 registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_LOADED, profile_source);
431 registrar_.Add(this, 447 registrar_.Add(this,
432 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED, 448 chrome::NOTIFICATION_EXTENSION_UNLOADED_DEPRECATED,
433 profile_source); 449 profile_source);
434 450
435 // Initialize the location entry. We do this to avoid a black flash which is 451 // Initialize the location entry. We do this to avoid a black flash which is
(...skipping 265 matching lines...) Expand 10 before | Expand all | Expand 10 after
701 min_size.set_width(origin_chip_view_min_size.width() + 717 min_size.set_width(origin_chip_view_min_size.width() +
702 background_min_size.width() + 718 background_min_size.width() +
703 search_button_min_size.width()); 719 search_button_min_size.width());
704 return min_size; 720 return min_size;
705 } 721 }
706 722
707 void LocationBarView::Layout() { 723 void LocationBarView::Layout() {
708 if (!IsInitialized()) 724 if (!IsInitialized())
709 return; 725 return;
710 726
727 animated_host_label_->SetVisible(false);
711 origin_chip_view_->SetVisible(origin_chip_view_->ShouldShow()); 728 origin_chip_view_->SetVisible(origin_chip_view_->ShouldShow());
712 selected_keyword_view_->SetVisible(false); 729 selected_keyword_view_->SetVisible(false);
713 location_icon_view_->SetVisible(false); 730 location_icon_view_->SetVisible(false);
714 ev_bubble_view_->SetVisible(false); 731 ev_bubble_view_->SetVisible(false);
715 keyword_hint_view_->SetVisible(false); 732 keyword_hint_view_->SetVisible(false);
716 733
717 const int item_padding = GetItemPadding(); 734 const int item_padding = GetItemPadding();
718 735
719 // The textfield has 1 px of whitespace before the text in the RTL case only. 736 // The textfield has 1 px of whitespace before the text in the RTL case only.
720 const int kEditLeadingInternalSpace = base::i18n::IsRTL() ? 1 : 0; 737 const int kEditLeadingInternalSpace = base::i18n::IsRTL() ? 1 : 0;
721 LocationBarLayout leading_decorations( 738 LocationBarLayout leading_decorations(
722 LocationBarLayout::LEFT_EDGE, item_padding - kEditLeadingInternalSpace); 739 LocationBarLayout::LEFT_EDGE, item_padding - kEditLeadingInternalSpace);
723 LocationBarLayout trailing_decorations(LocationBarLayout::RIGHT_EDGE, 740 LocationBarLayout trailing_decorations(LocationBarLayout::RIGHT_EDGE,
724 item_padding); 741 item_padding);
725 742
743 // Show and position the animated host label used in the show and hide URL
744 // animations.
745 if (show_url_animation_->is_animating() ||
746 hide_url_animation_->is_animating()) {
747 const GURL url = GetWebContents()->GetURL();
748 const base::string16 host =
749 OriginChip::LabelFromURLForProfile(url, profile());
750 animated_host_label_->SetText(host);
751
752 const base::string16 formatted_url = GetToolbarModel()->GetFormattedURL();
753
754 // Split the formatted URL on the host name in order to determine the size
755 // of the text leading up to it.
756 std::vector<base::string16> substrings;
757 base::SplitStringUsingSubstr(formatted_url, host, &substrings);
758 const base::string16 text_leading_host = substrings[0];
759
760 gfx::FontList font_list = origin_chip_view_->GetFontList();
761 int w = std::numeric_limits<int>::max();
762 int h = font_list.GetHeight();
763 gfx::Canvas::SizeStringInt(text_leading_host, font_list, &w, &h, 0, 0);
764
765 const int position_of_host_name_in_chip =
766 origin_chip_view_->LabelPosition();
767 const int postition_of_host_name_in_url = position_of_host_name_in_chip + w;
768
769 int host_label_x = position_of_host_name_in_chip;
770 if (show_url_animation_->is_animating()) {
771 host_label_x = show_url_animation_->
772 CurrentValueBetween(position_of_host_name_in_chip,
773 postition_of_host_name_in_url);
774 } else if (hide_url_animation_->is_animating()) {
775 host_label_x = hide_url_animation_->
776 CurrentValueBetween(postition_of_host_name_in_url,
777 position_of_host_name_in_chip);
778 }
779 animated_host_label_->SetBounds(host_label_x, 0,
780 animated_host_label_->GetPreferredSize().width(), height());
781 animated_host_label_->SetVisible(true);
782 }
783
726 const int origin_chip_width = origin_chip_view_->visible() ? 784 const int origin_chip_width = origin_chip_view_->visible() ?
727 origin_chip_view_->GetPreferredSize().width() : 0; 785 origin_chip_view_->GetPreferredSize().width() : 0;
728 origin_chip_view_->SetBounds(0, 0, origin_chip_width, height()); 786 origin_chip_view_->SetBounds(0, 0, origin_chip_width, height());
729 787
730 const base::string16 keyword(omnibox_view_->model()->keyword()); 788 const base::string16 keyword(omnibox_view_->model()->keyword());
731 const bool is_keyword_hint(omnibox_view_->model()->is_keyword_hint()); 789 const bool is_keyword_hint(omnibox_view_->model()->is_keyword_hint());
732 const int bubble_location_y = vertical_edge_thickness() + kBubblePadding; 790 const int bubble_location_y = vertical_edge_thickness() + kBubblePadding;
733 // In some cases (e.g. fullscreen mode) we may have 0 height. We still want 791 // In some cases (e.g. fullscreen mode) we may have 0 height. We still want
734 // to position our child views in this case, because other things may be 792 // to position our child views in this case, because other things may be
735 // positioned relative to them (e.g. the "bookmark added" bubble if the user 793 // positioned relative to them (e.g. the "bookmark added" bubble if the user
(...skipping 358 matching lines...) Expand 10 before | Expand all | Expand 10 after
1094 origin_chip_view_->OnChanged(); 1152 origin_chip_view_->OnChanged();
1095 1153
1096 Layout(); 1154 Layout();
1097 SchedulePaint(); 1155 SchedulePaint();
1098 } 1156 }
1099 1157
1100 void LocationBarView::OnSetFocus() { 1158 void LocationBarView::OnSetFocus() {
1101 GetFocusManager()->SetFocusedView(this); 1159 GetFocusManager()->SetFocusedView(this);
1102 } 1160 }
1103 1161
1162 void LocationBarView::ShowURL() {
1163 omnibox_view_->SetVisible(false);
1164 omnibox_view_->ShowURL();
1165 show_url_animation_->Show();
1166 }
1167
1168 void LocationBarView::OnShowURLAnimationEnded() {
1169 animated_host_label_->SetVisible(false);
1170 omnibox_view_->SetVisible(true);
1171 omnibox_view_->FadeIn();
1172 omnibox_view_->SetFocus();
1173 }
1174
1175 void LocationBarView::HideURL() {
1176 omnibox_view_->SetVisible(false);
1177 hide_url_animation_->Show();
1178 }
1179
1180 void LocationBarView::OnHideURLAnimationEnded() {
1181 animated_host_label_->SetVisible(false);
1182 omnibox_view_->HideURL();
1183 omnibox_view_->SetVisible(true);
1184 origin_chip_view_->FadeIn();
1185 }
1186
1187 void LocationBarView::AnimationProgressed(const gfx::Animation* animation) {
1188 if (animation == show_url_animation_.get() ||
1189 animation == hide_url_animation_.get()) {
1190 Layout();
1191 SchedulePaint();
1192 }
1193 }
1194
1195 void LocationBarView::AnimationEnded(const gfx::Animation* animation) {
1196 if (animation == show_url_animation_.get()) {
1197 show_url_animation_->Reset();
1198 OnShowURLAnimationEnded();
1199 } else if (animation == hide_url_animation_.get()) {
1200 hide_url_animation_->Reset();
1201 OnHideURLAnimationEnded();
1202 }
1203 }
1204
1104 InstantController* LocationBarView::GetInstant() { 1205 InstantController* LocationBarView::GetInstant() {
1105 return delegate_->GetInstant(); 1206 return delegate_->GetInstant();
1106 } 1207 }
1107 1208
1108 WebContents* LocationBarView::GetWebContents() { 1209 WebContents* LocationBarView::GetWebContents() {
1109 return delegate_->GetWebContents(); 1210 return delegate_->GetWebContents();
1110 } 1211 }
1111 1212
1112 ToolbarModel* LocationBarView::GetToolbarModel() { 1213 ToolbarModel* LocationBarView::GetToolbarModel() {
1113 return delegate_->GetToolbarModel(); 1214 return delegate_->GetToolbarModel();
(...skipping 436 matching lines...) Expand 10 before | Expand all | Expand 10 after
1550 if (!browser) 1651 if (!browser)
1551 return; // Possible when browser is shutting down. 1652 return; // Possible when browser is shutting down.
1552 1653
1553 FirstRunBubble::ShowBubble(browser, GetLocationBarAnchor()); 1654 FirstRunBubble::ShowBubble(browser, GetLocationBarAnchor());
1554 #endif 1655 #endif
1555 } 1656 }
1556 1657
1557 void LocationBarView::AccessibilitySetValue(const base::string16& new_value) { 1658 void LocationBarView::AccessibilitySetValue(const base::string16& new_value) {
1558 omnibox_view_->SetUserText(new_value, new_value, true); 1659 omnibox_view_->SetUserText(new_value, new_value, true);
1559 } 1660 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698