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

Side by Side Diff: chrome/browser/managed_mode/managed_mode.cc

Issue 11826059: Add ManagedUserService for profile-specific managed user data. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: android test Created 7 years, 11 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/managed_mode/managed_mode.h" 5 #include "chrome/browser/managed_mode/managed_mode.h"
6 6
7 #include "base/command_line.h" 7 #include "base/command_line.h"
8 #include "base/prefs/public/pref_change_registrar.h" 8 #include "base/prefs/public/pref_change_registrar.h"
9 #include "base/sequenced_task_runner.h" 9 #include "base/sequenced_task_runner.h"
10 #include "chrome/browser/browser_process.h" 10 #include "chrome/browser/browser_process.h"
11 #include "chrome/browser/extensions/extension_service.h"
12 #include "chrome/browser/extensions/extension_system.h"
13 #include "chrome/browser/managed_mode/managed_mode_site_list.h" 11 #include "chrome/browser/managed_mode/managed_mode_site_list.h"
14 #include "chrome/browser/managed_mode/managed_mode_url_filter.h" 12 #include "chrome/browser/managed_mode/managed_mode_url_filter.h"
15 #include "chrome/browser/policy/url_blacklist_manager.h" 13 #include "chrome/browser/policy/url_blacklist_manager.h"
16 #include "chrome/browser/prefs/pref_service.h" 14 #include "chrome/browser/prefs/pref_service.h"
17 #include "chrome/browser/prefs/scoped_user_pref_update.h"
18 #include "chrome/browser/profiles/profile.h" 15 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/browser/ui/browser.h" 16 #include "chrome/browser/ui/browser.h"
20 #include "chrome/browser/ui/browser_list.h" 17 #include "chrome/browser/ui/browser_list.h"
21 #include "chrome/browser/ui/browser_window.h" 18 #include "chrome/browser/ui/browser_window.h"
22 #include "chrome/common/chrome_notification_types.h" 19 #include "chrome/common/chrome_notification_types.h"
23 #include "chrome/common/chrome_switches.h" 20 #include "chrome/common/chrome_switches.h"
24 #include "chrome/common/extensions/extension_set.h"
25 #include "chrome/common/pref_names.h" 21 #include "chrome/common/pref_names.h"
26 #include "content/public/browser/browser_thread.h" 22 #include "content/public/browser/browser_thread.h"
27 #include "content/public/browser/notification_service.h" 23 #include "content/public/browser/notification_service.h"
28 #include "grit/generated_resources.h" 24 #include "grit/generated_resources.h"
29 #include "ui/base/l10n/l10n_util.h"
30 25
31 using content::BrowserThread; 26 using content::BrowserThread;
32 27
33 // A bridge from ManagedMode (which lives on the UI thread) to
34 // ManagedModeURLFilter (which might live on a different thread).
35 class ManagedMode::URLFilterContext {
36 public:
37 explicit URLFilterContext(
38 scoped_refptr<base::SequencedTaskRunner> task_runner)
39 : task_runner_(task_runner) {}
40 ~URLFilterContext() {}
41
42 const ManagedModeURLFilter* url_filter() const {
43 DCHECK(task_runner_->RunsTasksOnCurrentThread());
44 return &url_filter_;
45 }
46
47 void SetDefaultFilteringBehavior(
48 ManagedModeURLFilter::FilteringBehavior behavior) {
49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
50 // Because ManagedMode is a singleton, we can pass the pointer to
51 // |url_filter_| unretained.
52 task_runner_->PostTask(
53 FROM_HERE,
54 base::Bind(&ManagedModeURLFilter::SetDefaultFilteringBehavior,
55 base::Unretained(&url_filter_),
56 behavior));
57 }
58
59 void LoadWhitelists(ScopedVector<ManagedModeSiteList> site_lists) {
60 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
61 task_runner_->PostTask(FROM_HERE,
62 base::Bind(&ManagedModeURLFilter::LoadWhitelists,
63 base::Unretained(&url_filter_),
64 base::Passed(&site_lists),
65 base::Bind(&base::DoNothing)));
66 }
67
68 void SetManualLists(scoped_ptr<ListValue> whitelist,
69 scoped_ptr<ListValue> blacklist) {
70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
71 task_runner_->PostTask(FROM_HERE,
72 base::Bind(&ManagedModeURLFilter::SetManualLists,
73 base::Unretained(&url_filter_),
74 base::Passed(&whitelist),
75 base::Passed(&blacklist)));
76 }
77
78 void AddURLPatternToManualList(bool is_whitelist,
79 const std::string& url) {
80 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
81 task_runner_->PostTask(FROM_HERE,
82 base::Bind(&ManagedModeURLFilter::AddURLPatternToManualList,
83 base::Unretained(&url_filter_),
84 is_whitelist,
85 url));
86 }
87
88 void ShutdownOnUIThread() {
89 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90 bool result = task_runner_->DeleteSoon(FROM_HERE, this);
91 DCHECK(result);
92 }
93
94 private:
95 ManagedModeURLFilter url_filter_;
96 scoped_refptr<base::SequencedTaskRunner> task_runner_;
97
98 DISALLOW_COPY_AND_ASSIGN(URLFilterContext);
99 };
100
101 // static 28 // static
102 ManagedMode* ManagedMode::GetInstance() { 29 ManagedMode* ManagedMode::GetInstance() {
103 return Singleton<ManagedMode, LeakySingletonTraits<ManagedMode> >::get(); 30 return Singleton<ManagedMode, LeakySingletonTraits<ManagedMode> >::get();
104 } 31 }
105 32
106 // static 33 // static
107 void ManagedMode::RegisterPrefs(PrefServiceSimple* prefs) { 34 void ManagedMode::RegisterPrefs(PrefServiceSimple* prefs) {
108 prefs->RegisterBooleanPref(prefs::kInManagedMode, false); 35 prefs->RegisterBooleanPref(prefs::kInManagedMode, false);
109 } 36 }
110 37
111 // static 38 // static
112 void ManagedMode::RegisterUserPrefs(PrefServiceSyncable* prefs) {
113 prefs->RegisterIntegerPref(prefs::kDefaultManagedModeFilteringBehavior,
114 2,
115 PrefServiceSyncable::UNSYNCABLE_PREF);
116 prefs->RegisterListPref(prefs::kManagedModeWhitelist,
117 PrefServiceSyncable::UNSYNCABLE_PREF);
118 prefs->RegisterListPref(prefs::kManagedModeBlacklist,
119 PrefServiceSyncable::UNSYNCABLE_PREF);
120 }
121
122 // static
123 void ManagedMode::Init(Profile* profile) { 39 void ManagedMode::Init(Profile* profile) {
124 GetInstance()->InitImpl(profile); 40 GetInstance()->InitImpl(profile);
125 } 41 }
126 42
127 void ManagedMode::InitImpl(Profile* profile) { 43 void ManagedMode::InitImpl(Profile* profile) {
128 DCHECK(g_browser_process); 44 DCHECK(g_browser_process);
129 DCHECK(g_browser_process->local_state()); 45 DCHECK(g_browser_process->local_state());
130 46
131 Profile* original_profile = profile->GetOriginalProfile(); 47 Profile* original_profile = profile->GetOriginalProfile();
132 // Set the value directly in the PrefService instead of using 48 // Set the value directly in the PrefService instead of using
(...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after
214 void ManagedMode::LeaveManagedMode() { 130 void ManagedMode::LeaveManagedMode() {
215 GetInstance()->LeaveManagedModeImpl(); 131 GetInstance()->LeaveManagedModeImpl();
216 } 132 }
217 133
218 void ManagedMode::LeaveManagedModeImpl() { 134 void ManagedMode::LeaveManagedModeImpl() {
219 bool confirmed = PlatformConfirmLeave(); 135 bool confirmed = PlatformConfirmLeave();
220 if (confirmed) 136 if (confirmed)
221 SetInManagedMode(NULL); 137 SetInManagedMode(NULL);
222 } 138 }
223 139
224 // static
225 const ManagedModeURLFilter* ManagedMode::GetURLFilterForIOThread() {
226 return GetInstance()->GetURLFilterForIOThreadImpl();
227 }
228
229 // static
230 const ManagedModeURLFilter* ManagedMode::GetURLFilterForUIThread() {
231 return GetInstance()->GetURLFilterForUIThreadImpl();
232 }
233
234 const ManagedModeURLFilter* ManagedMode::GetURLFilterForIOThreadImpl() {
235 return io_url_filter_context_->url_filter();
236 }
237
238 const ManagedModeURLFilter* ManagedMode::GetURLFilterForUIThreadImpl() {
239 return ui_url_filter_context_->url_filter();
240 }
241
242 // static
243 void ManagedMode::AddToManualList(bool is_whitelist,
244 const base::ListValue& list) {
245 GetInstance()->AddToManualListImpl(is_whitelist, list);
246 }
247
248 void ManagedMode::AddToManualListImpl(bool is_whitelist,
249 const base::ListValue& list) {
250 if (!managed_profile_)
251 return;
252
253 ListPrefUpdate pref_update(managed_profile_->GetPrefs(),
254 is_whitelist ? prefs::kManagedModeWhitelist :
255 prefs::kManagedModeBlacklist);
256 ListValue* pref_list = pref_update.Get();
257
258 for (size_t i = 0; i < list.GetSize(); ++i) {
259 std::string url_pattern;
260 list.GetString(i, &url_pattern);
261
262 if (!IsInManualList(is_whitelist, url_pattern)) {
263 pref_list->AppendString(url_pattern);
264 AddURLPatternToManualList(is_whitelist, url_pattern);
265 }
266 }
267 }
268
269 // static
270 void ManagedMode::RemoveFromManualList(bool is_whitelist,
271 const base::ListValue& list) {
272 GetInstance()->RemoveFromManualListImpl(is_whitelist, list);
273 }
274
275 void ManagedMode::RemoveFromManualListImpl(bool is_whitelist,
276 const base::ListValue& list) {
277 ListPrefUpdate pref_update(managed_profile_->GetPrefs(),
278 is_whitelist ? prefs::kManagedModeWhitelist :
279 prefs::kManagedModeBlacklist);
280 ListValue* pref_list = pref_update.Get();
281
282 for (size_t i = 0; i < list.GetSize(); ++i) {
283 std::string pattern;
284 size_t out_index;
285 list.GetString(i, &pattern);
286 StringValue value_to_remove(pattern);
287
288 pref_list->Remove(value_to_remove, &out_index);
289 }
290 }
291
292 // static
293 bool ManagedMode::IsInManualList(bool is_whitelist,
294 const std::string& url_pattern) {
295 return GetInstance()->IsInManualListImpl(is_whitelist, url_pattern);
296 }
297
298 bool ManagedMode::IsInManualListImpl(bool is_whitelist,
299 const std::string& url_pattern) {
300 StringValue pattern(url_pattern);
301 const ListValue* list = managed_profile_->GetPrefs()->GetList(
302 is_whitelist ? prefs::kManagedModeWhitelist :
303 prefs::kManagedModeBlacklist);
304 return list->Find(pattern) != list->end();
305 }
306
307 // static
308 scoped_ptr<base::ListValue> ManagedMode::GetBlacklist() {
309 return scoped_ptr<base::ListValue>(
310 GetInstance()->managed_profile_->GetPrefs()->GetList(
311 prefs::kManagedModeBlacklist)->DeepCopy()).Pass();
312 }
313
314 std::string ManagedMode::GetDebugPolicyProviderName() const {
315 // Save the string space in official builds.
316 #ifdef NDEBUG
317 NOTREACHED();
318 return std::string();
319 #else
320 return "Managed Mode";
321 #endif
322 }
323
324 bool ManagedMode::UserMayLoad(const extensions::Extension* extension,
325 string16* error) const {
326 string16 tmp_error;
327 if (ExtensionManagementPolicyImpl(&tmp_error))
328 return true;
329
330 // If the extension is already loaded, we allow it, otherwise we'd unload
331 // all existing extensions.
332 ExtensionService* extension_service =
333 extensions::ExtensionSystem::Get(managed_profile_)->extension_service();
334
335 // |extension_service| can be NULL in a unit test.
336 if (extension_service &&
337 extension_service->GetInstalledExtension(extension->id()))
338 return true;
339
340 if (error)
341 *error = tmp_error;
342 return false;
343 }
344
345 bool ManagedMode::UserMayModifySettings(const extensions::Extension* extension,
346 string16* error) const {
347 return ExtensionManagementPolicyImpl(error);
348 }
349
350 bool ManagedMode::ExtensionManagementPolicyImpl(string16* error) const {
351 if (!IsInManagedModeImpl())
352 return true;
353
354 if (error)
355 *error = l10n_util::GetStringUTF16(IDS_EXTENSIONS_LOCKED_MANAGED_MODE);
356 return false;
357 }
358
359 void ManagedMode::OnBrowserAdded(Browser* browser) { 140 void ManagedMode::OnBrowserAdded(Browser* browser) {
360 // Return early if we don't have any queued callbacks. 141 // Return early if we don't have any queued callbacks.
361 if (callbacks_.empty()) 142 if (callbacks_.empty())
362 return; 143 return;
363 144
364 DCHECK(managed_profile_); 145 DCHECK(managed_profile_);
365 if (browser->profile()->GetOriginalProfile() != managed_profile_) 146 if (browser->profile()->GetOriginalProfile() != managed_profile_)
366 FinalizeEnter(false); 147 FinalizeEnter(false);
367 } 148 }
368 149
369 void ManagedMode::OnBrowserRemoved(Browser* browser) { 150 void ManagedMode::OnBrowserRemoved(Browser* browser) {
370 // Return early if we don't have any queued callbacks. 151 // Return early if we don't have any queued callbacks.
371 if (callbacks_.empty()) 152 if (callbacks_.empty())
372 return; 153 return;
373 154
374 DCHECK(managed_profile_); 155 DCHECK(managed_profile_);
375 if (browser->profile()->GetOriginalProfile() == managed_profile_) { 156 if (browser->profile()->GetOriginalProfile() == managed_profile_) {
376 // Ignore closing browser windows that are in managed mode. 157 // Ignore closing browser windows that are in managed mode.
377 return; 158 return;
378 } 159 }
379 size_t count = browsers_to_close_.erase(browser); 160 size_t count = browsers_to_close_.erase(browser);
380 DCHECK_EQ(1u, count); 161 DCHECK_EQ(1u, count);
381 if (browsers_to_close_.empty()) 162 if (browsers_to_close_.empty())
382 FinalizeEnter(true); 163 FinalizeEnter(true);
383 } 164 }
384 165
385 ManagedMode::ManagedMode() 166 ManagedMode::ManagedMode() : managed_profile_(NULL) {
386 : managed_profile_(NULL),
387 io_url_filter_context_(
388 new URLFilterContext(
389 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO))),
390 ui_url_filter_context_(
391 new URLFilterContext(
392 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI))) {
393 BrowserList::AddObserver(this); 167 BrowserList::AddObserver(this);
394 } 168 }
395 169
396 ManagedMode::~ManagedMode() { 170 ManagedMode::~ManagedMode() {
397 // This class usually is a leaky singleton, so this destructor shouldn't be 171 // This class usually is a leaky singleton, so this destructor shouldn't be
398 // called. We still do some cleanup, in case we're owned by a unit test. 172 // called. We still do some cleanup, in case we're owned by a unit test.
399 BrowserList::RemoveObserver(this); 173 BrowserList::RemoveObserver(this);
400 DCHECK_EQ(0u, callbacks_.size()); 174 DCHECK_EQ(0u, callbacks_.size());
401 DCHECK_EQ(0u, browsers_to_close_.size()); 175 DCHECK_EQ(0u, browsers_to_close_.size());
402 io_url_filter_context_.release()->ShutdownOnUIThread();
403 ui_url_filter_context_.release()->ShutdownOnUIThread();
404 } 176 }
405 177
406 void ManagedMode::Observe(int type, 178 void ManagedMode::Observe(int type,
407 const content::NotificationSource& source, 179 const content::NotificationSource& source,
408 const content::NotificationDetails& details) { 180 const content::NotificationDetails& details) {
409 // Return early if we don't have any queued callbacks. 181 // Return early if we don't have any queued callbacks.
410 if (callbacks_.empty()) 182 if (callbacks_.empty())
411 return; 183 return;
412 184
413 switch (type) { 185 switch (type) {
414 case chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST: { 186 case chrome::NOTIFICATION_CLOSE_ALL_BROWSERS_REQUEST: {
415 FinalizeEnter(false); 187 FinalizeEnter(false);
416 return; 188 return;
417 } 189 }
418 case chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED: { 190 case chrome::NOTIFICATION_BROWSER_CLOSE_CANCELLED: {
419 Browser* browser = content::Source<Browser>(source).ptr(); 191 Browser* browser = content::Source<Browser>(source).ptr();
420 if (browsers_to_close_.find(browser) != browsers_to_close_.end()) 192 if (browsers_to_close_.find(browser) != browsers_to_close_.end())
421 FinalizeEnter(false); 193 FinalizeEnter(false);
422 return; 194 return;
423 } 195 }
424 default: 196 default:
425 NOTREACHED(); 197 NOTREACHED();
426 } 198 }
427 } 199 }
428 200
429 void ManagedMode::FinalizeEnter(bool result) { 201 void ManagedMode::FinalizeEnter(bool result) {
430 if (result) 202 if (result)
431 SetInManagedMode(managed_profile_); 203 SetInManagedMode(managed_profile_);
204
432 for (std::vector<EnterCallback>::iterator it = callbacks_.begin(); 205 for (std::vector<EnterCallback>::iterator it = callbacks_.begin();
433 it != callbacks_.end(); ++it) { 206 it != callbacks_.end(); ++it) {
434 it->Run(result); 207 it->Run(result);
435 } 208 }
436 callbacks_.clear(); 209 callbacks_.clear();
437 browsers_to_close_.clear(); 210 browsers_to_close_.clear();
438 registrar_.RemoveAll(); 211 registrar_.RemoveAll();
439 } 212 }
440 213
441 bool ManagedMode::PlatformConfirmEnter() { 214 bool ManagedMode::PlatformConfirmEnter() {
442 // TODO(bauerb): Show platform-specific confirmation dialog. 215 // TODO(bauerb): Show platform-specific confirmation dialog.
443 return true; 216 return true;
444 } 217 }
445 218
446 bool ManagedMode::PlatformConfirmLeave() { 219 bool ManagedMode::PlatformConfirmLeave() {
447 // TODO(bauerb): Show platform-specific confirmation dialog. 220 // TODO(bauerb): Show platform-specific confirmation dialog.
448 return true; 221 return true;
449 } 222 }
450 223
451 void ManagedMode::SetInManagedMode(Profile* newly_managed_profile) { 224 void ManagedMode::SetInManagedMode(Profile* newly_managed_profile) {
452 // Register the ManagementPolicy::Provider before changing the pref when
453 // setting it, and unregister it after changing the pref when clearing it,
454 // so pref observers see the correct ManagedMode state.
455 bool in_managed_mode = !!newly_managed_profile;
456 if (in_managed_mode) {
457 DCHECK(!managed_profile_ || managed_profile_ == newly_managed_profile);
458 extensions::ExtensionSystem::Get(
459 newly_managed_profile)->management_policy()->RegisterProvider(this);
460 pref_change_registrar_.reset(new PrefChangeRegistrar());
461 pref_change_registrar_->Init(newly_managed_profile->GetPrefs());
462 pref_change_registrar_->Add(
463 prefs::kDefaultManagedModeFilteringBehavior,
464 base::Bind(
465 &ManagedMode::OnDefaultFilteringBehaviorChanged,
466 base::Unretained(this)));
467 } else {
468 extensions::ExtensionSystem::Get(
469 managed_profile_)->management_policy()->UnregisterProvider(this);
470 pref_change_registrar_.reset();
471 }
472
473 managed_profile_ = newly_managed_profile; 225 managed_profile_ = newly_managed_profile;
474 ManagedModeURLFilter::FilteringBehavior behavior =
475 ManagedModeURLFilter::ALLOW;
476 if (in_managed_mode) {
477 int behavior_value = managed_profile_->GetPrefs()->GetInteger(
478 prefs::kDefaultManagedModeFilteringBehavior);
479 behavior = ManagedModeURLFilter::BehaviorFromInt(behavior_value);
480 }
481 io_url_filter_context_->SetDefaultFilteringBehavior(behavior);
482 ui_url_filter_context_->SetDefaultFilteringBehavior(behavior);
483 g_browser_process->local_state()->SetBoolean(prefs::kInManagedMode, 226 g_browser_process->local_state()->SetBoolean(prefs::kInManagedMode,
484 in_managed_mode); 227 !!newly_managed_profile);
485 if (in_managed_mode)
486 UpdateManualListsImpl();
487 228
488 // This causes the avatar and the profile menu to get updated. 229 // This causes the avatar and the profile menu to get updated.
489 content::NotificationService::current()->Notify( 230 content::NotificationService::current()->Notify(
490 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED, 231 chrome::NOTIFICATION_PROFILE_CACHED_INFO_CHANGED,
491 content::NotificationService::AllBrowserContextsAndSources(), 232 content::NotificationService::AllBrowserContextsAndSources(),
492 content::NotificationService::NoDetails()); 233 content::NotificationService::NoDetails());
493 } 234 }
494
495 ScopedVector<ManagedModeSiteList> ManagedMode::GetActiveSiteLists() {
496 DCHECK(managed_profile_);
497 ScopedVector<ManagedModeSiteList> site_lists;
498 // TODO(bauerb): Get site lists from all extensions.
499 return site_lists.Pass();
500 }
501
502 void ManagedMode::OnDefaultFilteringBehaviorChanged() {
503 DCHECK(IsInManagedModeImpl());
504
505 int behavior_value = managed_profile_->GetPrefs()->GetInteger(
506 prefs::kDefaultManagedModeFilteringBehavior);
507 ManagedModeURLFilter::FilteringBehavior behavior =
508 ManagedModeURLFilter::BehaviorFromInt(behavior_value);
509 io_url_filter_context_->SetDefaultFilteringBehavior(behavior);
510 ui_url_filter_context_->SetDefaultFilteringBehavior(behavior);
511 }
512
513 // Static
514 void ManagedMode::UpdateManualLists() {
515 GetInstance()->UpdateManualListsImpl();
516 }
517
518 void ManagedMode::UpdateManualListsImpl() {
519 io_url_filter_context_->LoadWhitelists(GetActiveSiteLists());
520 ui_url_filter_context_->LoadWhitelists(GetActiveSiteLists());
521 io_url_filter_context_->SetManualLists(GetWhitelist(), GetBlacklist());
522 ui_url_filter_context_->SetManualLists(GetWhitelist(), GetBlacklist());
523 }
524
525 scoped_ptr<base::ListValue> ManagedMode::GetWhitelist() {
526 return make_scoped_ptr(
527 managed_profile_->GetPrefs()->GetList(
528 prefs::kManagedModeWhitelist)->DeepCopy());
529 }
530
531 void ManagedMode::AddURLPatternToManualList(
532 bool is_whitelist,
533 const std::string& url_pattern) {
534 io_url_filter_context_->AddURLPatternToManualList(true, url_pattern);
535 ui_url_filter_context_->AddURLPatternToManualList(true, url_pattern);
536 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698