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

Side by Side Diff: chrome/browser/autofill/autofill_external_delegate.cc

Issue 10443084: Add Datalist Support to New Autofill UI (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Created 8 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 (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 "base/utf_string_conversions.h" 5 #include "base/utf_string_conversions.h"
6 #include "chrome/browser/autocomplete_history_manager.h" 6 #include "chrome/browser/autocomplete_history_manager.h"
7 #include "chrome/browser/autofill/autofill_external_delegate.h" 7 #include "chrome/browser/autofill/autofill_external_delegate.h"
8 #include "chrome/browser/autofill/autofill_manager.h" 8 #include "chrome/browser/autofill/autofill_manager.h"
9 #include "chrome/common/autofill_messages.h" 9 #include "chrome/common/autofill_messages.h"
10 #include "chrome/common/chrome_constants.h" 10 #include "chrome/common/chrome_constants.h"
(...skipping 12 matching lines...) Expand all
23 23
24 AutofillExternalDelegate::AutofillExternalDelegate( 24 AutofillExternalDelegate::AutofillExternalDelegate(
25 TabContentsWrapper* tab_contents_wrapper, 25 TabContentsWrapper* tab_contents_wrapper,
26 AutofillManager* autofill_manager) 26 AutofillManager* autofill_manager)
27 : tab_contents_wrapper_(tab_contents_wrapper), 27 : tab_contents_wrapper_(tab_contents_wrapper),
28 autofill_manager_(autofill_manager), 28 autofill_manager_(autofill_manager),
29 password_autofill_manager_( 29 password_autofill_manager_(
30 tab_contents_wrapper ? tab_contents_wrapper->web_contents() : NULL), 30 tab_contents_wrapper ? tab_contents_wrapper->web_contents() : NULL),
31 autofill_query_id_(0), 31 autofill_query_id_(0),
32 display_warning_if_disabled_(false), 32 display_warning_if_disabled_(false),
33 has_shown_autofill_popup_for_current_edit_(false) { 33 has_shown_autofill_popup_for_current_edit_(false),
34 popup_visible_(false) {
34 } 35 }
35 36
36 void AutofillExternalDelegate::SelectAutofillSuggestionAtIndex(int unique_id) { 37 void AutofillExternalDelegate::SelectAutofillSuggestionAtIndex(int unique_id) {
37 if (unique_id == WebAutofillClient::MenuItemIDAutofillOptions ||
38 unique_id == WebAutofillClient::MenuItemIDClearForm ||
39 unique_id == WebAutofillClient::MenuItemIDSeparator ||
40 unique_id == WebAutofillClient::MenuItemIDWarningMessage)
41 return;
42
43 ClearPreviewedForm(); 38 ClearPreviewedForm();
44 39
45 if (unique_id == WebAutofillClient::MenuItemIDPasswordEntry) 40 // Only preview the data if it is a profile.
46 return; 41 if (unique_id > 0)
47 42 FillAutofillFormData(unique_id, true);
48 FillAutofillFormData(unique_id, true);
49 } 43 }
50 44
51 void AutofillExternalDelegate::OnQuery(int query_id, 45 void AutofillExternalDelegate::OnQuery(int query_id,
52 const webkit::forms::FormData& form, 46 const webkit::forms::FormData& form,
53 const webkit::forms::FormField& field, 47 const webkit::forms::FormField& field,
54 const gfx::Rect& bounds, 48 const gfx::Rect& bounds,
55 bool display_warning_if_disabled) { 49 bool display_warning_if_disabled) {
56 autofill_query_form_ = form; 50 autofill_query_form_ = form;
57 autofill_query_field_ = field; 51 autofill_query_field_ = field;
58 display_warning_if_disabled_ = display_warning_if_disabled; 52 display_warning_if_disabled_ = display_warning_if_disabled;
59 autofill_query_id_ = query_id; 53 autofill_query_id_ = query_id;
60 54
61 OnQueryPlatformSpecific(query_id, form, field, bounds); 55 OnQueryPlatformSpecific(query_id, form, field, bounds);
62 } 56 }
63 57
64 void AutofillExternalDelegate::OnSuggestionsReturned( 58 void AutofillExternalDelegate::OnSuggestionsReturned(
65 int query_id, 59 int query_id,
66 const std::vector<string16>& values, 60 const std::vector<string16>& values,
67 const std::vector<string16>& labels, 61 const std::vector<string16>& labels,
68 const std::vector<string16>& icons, 62 const std::vector<string16>& icons,
69 const std::vector<int>& unique_ids) { 63 const std::vector<int>& unique_ids) {
Ilya Sherman 2012/06/06 22:25:45 nit: It's always bothered me that we use "v", "l",
csharp 2012/06/07 14:57:43 Done.
70 if (query_id != autofill_query_id_) 64 if (query_id != autofill_query_id_)
71 return; 65 return;
72 66
73 if (values.empty()) {
74 // No suggestions, any popup currently showing is obsolete.
75 HideAutofillPopup();
76 return;
77 }
78
79 std::vector<string16> v(values); 67 std::vector<string16> v(values);
80 std::vector<string16> l(labels); 68 std::vector<string16> l(labels);
81 std::vector<string16> i(icons); 69 std::vector<string16> i(icons);
82 std::vector<int> ids(unique_ids); 70 std::vector<int> ids(unique_ids);
83 71
84 // Add a separator to go between the values and menu items. 72 // Add a separator to go between the values and menu items.
85 v.push_back(string16()); 73 v.push_back(string16());
86 l.push_back(string16()); 74 l.push_back(string16());
87 i.push_back(string16()); 75 i.push_back(string16());
88 ids.push_back(WebAutofillClient::MenuItemIDSeparator); 76 ids.push_back(WebAutofillClient::MenuItemIDSeparator);
89 77
90 DCHECK_GT(ids.size(), 0U); 78 ApplyAutofillWarnings(&v, &l, &i, &ids);
91 if (!autofill_query_field_.should_autocomplete) {
92 // If autofill is disabled and we had suggestions, show a warning instead.
93 v.assign(1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED));
94 l.assign(1, string16());
95 i.assign(1, string16());
96 ids.assign(1, WebAutofillClient::MenuItemIDWarningMessage);
97 } else if (ids[0] < 0 && ids.size() > 1) {
98 // If we received a warning instead of suggestions from autofill but regular
99 // suggestions from autocomplete, don't show the autofill warning.
100 v.erase(v.begin());
101 l.erase(l.begin());
102 i.erase(i.begin());
103 ids.erase(ids.begin());
104 }
105
106 // If we were about to show a warning and we shouldn't, don't.
107 if (ids[0] < 0 && !display_warning_if_disabled_)
108 return;
109 79
110 // Only include "Autofill Options" special menu item if we have Autofill 80 // Only include "Autofill Options" special menu item if we have Autofill
111 // items, identified by |unique_ids| having at least one valid value. 81 // items, identified by |unique_ids| having at least one valid value.
112 bool has_autofill_item = false; 82 bool has_autofill_item = false;
113 for (size_t i = 0; i < ids.size(); ++i) { 83 for (size_t i = 0; i < ids.size(); ++i) {
114 if (ids[i] > 0) { 84 if (ids[i] > 0) {
115 has_autofill_item = true; 85 has_autofill_item = true;
116 break; 86 break;
117 } 87 }
118 } 88 }
119 89
120 // The form has been auto-filled, so give the user the chance to clear the 90 if (has_autofill_item)
121 // form. Append the 'Clear form' menu item. 91 ApplyAutofillOptions(&v, &l, &i, &ids);
122 if (has_autofill_item && autofill_query_field_.is_autofilled) {
123 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
124 l.push_back(string16());
125 i.push_back(string16());
126 ids.push_back(WebAutofillClient::MenuItemIDClearForm);
127 }
128 92
129 if (has_autofill_item) { 93 InsertDataListValues(&v, &l, &i, &ids);
130 // Append the 'Chrome Autofill options' menu item;
131 v.push_back(l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP));
132 l.push_back(string16());
133 i.push_back(string16());
134 ids.push_back(WebAutofillClient::MenuItemIDAutofillOptions);
135 }
136 94
137 // Remove the separator if it is the last element. 95 // Remove the separator if it is the last element.
138 if (*(ids.rbegin()) == WebAutofillClient::MenuItemIDSeparator) { 96 if (*(ids.rbegin()) == WebAutofillClient::MenuItemIDSeparator) {
139 v.pop_back(); 97 v.pop_back();
140 l.pop_back(); 98 l.pop_back();
141 i.pop_back(); 99 i.pop_back();
142 ids.pop_back(); 100 ids.pop_back();
143 } 101 }
144 102
103 if (v.empty()) {
104 // No suggestions, any popup currently showing is obsolete.
105 HideAutofillPopup();
106 return;
107 }
108
145 // Send to display. 109 // Send to display.
146 if (!v.empty() && autofill_query_field_.is_focusable) 110 if (!v.empty() && autofill_query_field_.is_focusable) {
Ilya Sherman 2012/06/06 22:25:45 nit: v.empty() is checked on line 103, so no need
csharp 2012/06/07 14:57:43 Done.
111 popup_visible_ = true;
147 ApplyAutofillSuggestions(v, l, i, ids); 112 ApplyAutofillSuggestions(v, l, i, ids);
113 }
148 114
149 tab_contents_wrapper_->autofill_manager()->OnDidShowAutofillSuggestions( 115 tab_contents_wrapper_->autofill_manager()->OnDidShowAutofillSuggestions(
150 has_autofill_item && !has_shown_autofill_popup_for_current_edit_); 116 has_autofill_item && !has_shown_autofill_popup_for_current_edit_);
151 has_shown_autofill_popup_for_current_edit_ |= has_autofill_item; 117 has_shown_autofill_popup_for_current_edit_ |= has_autofill_item;
Ilya Sherman 2012/06/06 22:25:45 Should these lines also be part of the if-stmt on
csharp 2012/06/07 14:57:43 I'm not sure. The old autofill_agent logic didn't
Ilya Sherman 2012/06/07 22:56:05 I think it was an oversight on my part previously
csharp 2012/06/08 14:44:53 Done.
152 } 118 }
153 119
154 void AutofillExternalDelegate::OnShowPasswordSuggestions( 120 void AutofillExternalDelegate::OnShowPasswordSuggestions(
155 const std::vector<string16>& suggestions, 121 const std::vector<string16>& suggestions,
156 const webkit::forms::FormField& field, 122 const webkit::forms::FormField& field,
157 const gfx::Rect& bounds) { 123 const gfx::Rect& bounds) {
158 autofill_query_field_ = field; 124 autofill_query_field_ = field;
159 125
160 if (suggestions.empty()) { 126 if (suggestions.empty()) {
161 HideAutofillPopup(); 127 HideAutofillPopup();
162 return; 128 return;
163 } 129 }
164 130
165 SetBounds(bounds); 131 SetBounds(bounds);
166 132
167 std::vector<string16> empty(suggestions.size()); 133 std::vector<string16> empty(suggestions.size());
168 std::vector<int> password_ids(suggestions.size(), 134 std::vector<int> password_ids(suggestions.size(),
169 WebAutofillClient::MenuItemIDPasswordEntry); 135 WebAutofillClient::MenuItemIDPasswordEntry);
170 ApplyAutofillSuggestions(suggestions, empty, empty, password_ids); 136 ApplyAutofillSuggestions(suggestions, empty, empty, password_ids);
171 } 137 }
172 138
139 void AutofillExternalDelegate::SetCurrentDataListValues(
140 const std::vector<string16>& data_list_values,
141 const std::vector<string16>& data_list_labels,
142 const std::vector<string16>& data_list_icons,
143 const std::vector<int>& data_list_unique_ids) {
144 // TODO(csharp): Modify the code to allow the data list values to change
145 // even if the popup is visible.
146 // http://crbug.com/131003
147 if (!popup_visible_) {
148 data_list_values_ = data_list_values;
149 data_list_labels_ = data_list_labels;
150 data_list_icons_ = data_list_icons;
151 data_list_unique_ids_ = data_list_unique_ids;
152 }
153 }
154
173 void AutofillExternalDelegate::RemoveAutocompleteEntry(const string16& value) { 155 void AutofillExternalDelegate::RemoveAutocompleteEntry(const string16& value) {
174 if (tab_contents_wrapper_) { 156 if (tab_contents_wrapper_) {
175 tab_contents_wrapper_->autocomplete_history_manager()-> 157 tab_contents_wrapper_->autocomplete_history_manager()->
176 OnRemoveAutocompleteEntry(autofill_query_field_.name, value); 158 OnRemoveAutocompleteEntry(autofill_query_field_.name, value);
177 } 159 }
178 } 160 }
179 161
180 void AutofillExternalDelegate::RemoveAutofillProfileOrCreditCard( 162 void AutofillExternalDelegate::RemoveAutofillProfileOrCreditCard(
181 int unique_id) { 163 int unique_id) {
182 autofill_manager_->RemoveAutofillProfileOrCreditCard(unique_id); 164 autofill_manager_->RemoveAutofillProfileOrCreditCard(unique_id);
183 } 165 }
184 166
185 167
186 void AutofillExternalDelegate::DidEndTextFieldEditing() { 168 void AutofillExternalDelegate::DidEndTextFieldEditing() {
187 HideAutofillPopup(); 169 HideAutofillPopup();
188 170
189 has_shown_autofill_popup_for_current_edit_ = false; 171 has_shown_autofill_popup_for_current_edit_ = false;
190 } 172 }
191 173
192 bool AutofillExternalDelegate::DidAcceptAutofillSuggestions( 174 bool AutofillExternalDelegate::DidAcceptAutofillSuggestions(
193 const string16& value, 175 const string16& value,
194 int unique_id, 176 int unique_id,
195 unsigned index) { 177 unsigned index) {
196 // If the selected element is a warning we don't want to do anything. 178 // If the selected element is a warning we don't want to do anything.
197 if (unique_id == WebAutofillClient::MenuItemIDWarningMessage) 179 if (unique_id == WebAutofillClient::MenuItemIDWarningMessage)
198 return false; 180 return false;
199 181
182 RenderViewHost* host =
183 tab_contents_wrapper_->web_contents()->GetRenderViewHost();
184
200 if (unique_id == WebAutofillClient::MenuItemIDAutofillOptions) { 185 if (unique_id == WebAutofillClient::MenuItemIDAutofillOptions) {
201 // User selected 'Autofill Options'. 186 // User selected 'Autofill Options'.
202 autofill_manager_->OnShowAutofillDialog(); 187 autofill_manager_->OnShowAutofillDialog();
203 } else if (unique_id == WebAutofillClient::MenuItemIDClearForm) { 188 } else if (unique_id == WebAutofillClient::MenuItemIDClearForm) {
204 // User selected 'Clear form'. 189 // User selected 'Clear form'.
205 RenderViewHost* host =
206 tab_contents_wrapper_->web_contents()->GetRenderViewHost();
207 host->Send(new AutofillMsg_ClearForm(host->GetRoutingID())); 190 host->Send(new AutofillMsg_ClearForm(host->GetRoutingID()));
208 } else if (unique_id == WebAutofillClient::MenuItemIDPasswordEntry && 191 } else if (unique_id == WebAutofillClient::MenuItemIDPasswordEntry &&
209 password_autofill_manager_.DidAcceptAutofillSuggestion( 192 password_autofill_manager_.DidAcceptAutofillSuggestion(
210 autofill_query_field_, value)) { 193 autofill_query_field_, value)) {
211 // DidAcceptAutofillSuggestion has already handled the work to fill in 194 // DidAcceptAutofillSuggestion has already handled the work to fill in
212 // the page as required. 195 // the page as required.
196 } else if (unique_id == WebAutofillClient::MenuItemIDDataListEntry) {
197 host->Send(new AutofillMsg_AcceptDataListSuggestion(host->GetRoutingID(),
198 value));
213 } else if (unique_id == WebAutofillClient::MenuItemIDAutocompleteEntry) { 199 } else if (unique_id == WebAutofillClient::MenuItemIDAutocompleteEntry) {
214 // User selected an Autocomplete, so we fill directly. 200 // User selected an Autocomplete, so we fill directly.
215 RenderViewHost* host =
216 tab_contents_wrapper_->web_contents()->GetRenderViewHost();
217 host->Send(new AutofillMsg_SetNodeText( 201 host->Send(new AutofillMsg_SetNodeText(
218 host->GetRoutingID(), 202 host->GetRoutingID(),
219 value)); 203 value));
220 } else { 204 } else {
221 FillAutofillFormData(unique_id, false); 205 FillAutofillFormData(unique_id, false);
222 } 206 }
223 207
224 HideAutofillPopup(); 208 HideAutofillPopup();
225 209
226 return true; 210 return true;
227 } 211 }
228 212
229 void AutofillExternalDelegate::ClearPreviewedForm() { 213 void AutofillExternalDelegate::ClearPreviewedForm() {
230 RenderViewHost* host = 214 RenderViewHost* host =
231 tab_contents_wrapper_->web_contents()->GetRenderViewHost(); 215 tab_contents_wrapper_->web_contents()->GetRenderViewHost();
232 host->Send(new AutofillMsg_ClearPreviewedForm(host->GetRoutingID())); 216 host->Send(new AutofillMsg_ClearPreviewedForm(host->GetRoutingID()));
233 } 217 }
234 218
235 void AutofillExternalDelegate::HideAutofillPopup() { 219 void AutofillExternalDelegate::HideAutofillPopup() {
220 popup_visible_ = false;
221
236 HideAutofillPopupInternal(); 222 HideAutofillPopupInternal();
237 } 223 }
238 224
239 void AutofillExternalDelegate::Reset() { 225 void AutofillExternalDelegate::Reset() {
240 HideAutofillPopup(); 226 HideAutofillPopup();
241 227
242 password_autofill_manager_.Reset(); 228 password_autofill_manager_.Reset();
243 } 229 }
244 230
245 void AutofillExternalDelegate::AddPasswordFormMapping( 231 void AutofillExternalDelegate::AddPasswordFormMapping(
(...skipping 15 matching lines...) Expand all
261 host->GetRoutingID())); 247 host->GetRoutingID()));
262 } 248 }
263 249
264 // Fill the values for the whole form. 250 // Fill the values for the whole form.
265 autofill_manager_->OnFillAutofillFormData(autofill_query_id_, 251 autofill_manager_->OnFillAutofillFormData(autofill_query_id_,
266 autofill_query_form_, 252 autofill_query_form_,
267 autofill_query_field_, 253 autofill_query_field_,
268 unique_id); 254 unique_id);
269 } 255 }
270 256
257 void AutofillExternalDelegate::ApplyAutofillWarnings(
258 std::vector<string16>* autofill_values,
259 std::vector<string16>* autofill_labels,
260 std::vector<string16>* autofill_icons,
261 std::vector<int>* autofill_unique_ids) {
262 if (!autofill_query_field_.should_autocomplete) {
263 // If autofill is disabled and we had suggestions, show a warning instead.
264 autofill_values->assign(
265 1, l10n_util::GetStringUTF16(IDS_AUTOFILL_WARNING_FORM_DISABLED));
266 autofill_labels->assign(1, string16());
267 autofill_icons->assign(1, string16());
268 autofill_unique_ids->assign(1, WebAutofillClient::MenuItemIDWarningMessage);
269 } else if (autofill_unique_ids->size() > 1 &&
270 (*autofill_unique_ids)[0] ==
271 WebAutofillClient::MenuItemIDWarningMessage) {
Ilya Sherman 2012/06/06 22:25:45 nit: Looks like this still needs to be indented by
csharp 2012/06/07 14:57:43 Done.
272 // If we received a warning instead of suggestions from autofill but regular
273 // suggestions from autocomplete, don't show the autofill warning.
274 autofill_values->erase(autofill_values->begin());
275 autofill_labels->erase(autofill_labels->begin());
276 autofill_icons->erase(autofill_icons->begin());
277 autofill_unique_ids->erase(autofill_unique_ids->begin());
278 }
279
280 // If we were about to show a warning and we shouldn't, don't.
281 if (autofill_unique_ids->size() > 0 &&
Ilya Sherman 2012/06/06 22:25:45 nit: !autofill_unique_ids->empty()
csharp 2012/06/07 14:57:43 Done.
282 (*autofill_unique_ids)[0] ==
283 WebAutofillClient::MenuItemIDWarningMessage &&
284 !display_warning_if_disabled_) {
285 autofill_values->clear();
286 autofill_labels->clear();
287 autofill_icons->clear();
288 autofill_unique_ids->clear();
289 }
290 }
291
292 void AutofillExternalDelegate::ApplyAutofillOptions(
293 std::vector<string16>* autofill_values,
294 std::vector<string16>* autofill_labels,
295 std::vector<string16>* autofill_icons,
296 std::vector<int>* autofill_unique_ids) {
297 // The form has been auto-filled, so give the user the chance to clear the
298 // form. Append the 'Clear form' menu item.
299 if (autofill_query_field_.is_autofilled) {
300 autofill_values->push_back(
301 l10n_util::GetStringUTF16(IDS_AUTOFILL_CLEAR_FORM_MENU_ITEM));
302 autofill_labels->push_back(string16());
303 autofill_icons->push_back(string16());
304 autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDClearForm);
305 }
306
307 // Append the 'Chrome Autofill options' menu item;
308 autofill_values->push_back(
309 l10n_util::GetStringUTF16(IDS_AUTOFILL_OPTIONS_POPUP));
310 autofill_labels->push_back(string16());
311 autofill_icons->push_back(string16());
312 autofill_unique_ids->push_back(WebAutofillClient::MenuItemIDAutofillOptions);
313 }
314
315 void AutofillExternalDelegate::InsertDataListValues(
316 std::vector<string16>* autofill_values,
317 std::vector<string16>* autofill_labels,
318 std::vector<string16>* autofill_icons,
319 std::vector<int>* autofill_unique_ids) {
320 if (data_list_values_.empty())
321 return;
322
323 // Insert the separator between the datalist and Autofill values (if there
324 // are any).
325 if (!autofill_values->empty()) {
326 autofill_values->insert(autofill_values->begin(), string16());
327 autofill_labels->insert(autofill_labels->begin(), string16());
328 autofill_icons->insert(autofill_icons->begin(), string16());
329 autofill_unique_ids->insert(autofill_unique_ids->begin(),
330 WebAutofillClient::MenuItemIDSeparator);
331 }
332
333 // Insert the datalist elements.
334 autofill_values->insert(autofill_values->begin(),
335 data_list_values_.begin(),
336 data_list_values_.end());
337 autofill_labels->insert(autofill_labels->begin(),
338 data_list_labels_.begin(),
339 data_list_labels_.end());
340 autofill_icons->insert(autofill_icons->begin(),
341 data_list_icons_.begin(),
342 data_list_icons_.end());
343 autofill_unique_ids->insert(autofill_unique_ids->begin(),
344 data_list_unique_ids_.begin(),
345 data_list_unique_ids_.end());
346 }
347
271 // Add a "!defined(OS_YOUROS) for each platform that implements this 348 // Add a "!defined(OS_YOUROS) for each platform that implements this
272 // in an autofill_external_delegate_YOUROS.cc. Currently there are 349 // in an autofill_external_delegate_YOUROS.cc. Currently there are
273 // none, so all platforms use the default. 350 // none, so all platforms use the default.
274 351
275 #if !defined(OS_ANDROID) && !defined(TOOLKIT_GTK) 352 #if !defined(OS_ANDROID) && !defined(TOOLKIT_GTK)
276 353
277 AutofillExternalDelegate* AutofillExternalDelegate::Create( 354 AutofillExternalDelegate* AutofillExternalDelegate::Create(
278 TabContentsWrapper*, AutofillManager*) { 355 TabContentsWrapper*, AutofillManager*) {
279 return NULL; 356 return NULL;
280 } 357 }
281 358
282 #endif 359 #endif
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698