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

Side by Side Diff: trunk/src/chrome/browser/sync/profile_sync_components_factory_impl.cc

Issue 465113002: Revert 288557 "[Sync] Use OnSingleDataTypeUnrecoverableError for..." (Closed) Base URL: svn://svn.chromium.org/chrome/
Patch Set: Created 6 years, 4 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 "base/command_line.h" 5 #include "base/command_line.h"
6 #include "build/build_config.h" 6 #include "build/build_config.h"
7 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 7 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
8 #include "chrome/browser/bookmarks/enhanced_bookmarks_features.h" 8 #include "chrome/browser/bookmarks/enhanced_bookmarks_features.h"
9 #include "chrome/browser/dom_distiller/dom_distiller_service_factory.h" 9 #include "chrome/browser/dom_distiller/dom_distiller_service_factory.h"
10 #include "chrome/browser/history/history_service.h" 10 #include "chrome/browser/history/history_service.h"
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after
172 syncer::ModelTypeSet disabled_types = 172 syncer::ModelTypeSet disabled_types =
173 GetDisabledTypesFromCommandLine(*command_line_); 173 GetDisabledTypesFromCommandLine(*command_line_);
174 syncer::ModelTypeSet enabled_types = 174 syncer::ModelTypeSet enabled_types =
175 GetEnabledTypesFromCommandLine(*command_line_); 175 GetEnabledTypesFromCommandLine(*command_line_);
176 RegisterCommonDataTypes(disabled_types, enabled_types, pss); 176 RegisterCommonDataTypes(disabled_types, enabled_types, pss);
177 #if !defined(OS_ANDROID) 177 #if !defined(OS_ANDROID)
178 RegisterDesktopDataTypes(disabled_types, enabled_types, pss); 178 RegisterDesktopDataTypes(disabled_types, enabled_types, pss);
179 #endif 179 #endif
180 } 180 }
181 181
182 void ProfileSyncComponentsFactoryImpl::DisableBrokenType(
183 syncer::ModelType type,
184 const tracked_objects::Location& from_here,
185 const std::string& message) {
186 ProfileSyncService* p = ProfileSyncServiceFactory::GetForProfile(profile_);
187 syncer::SyncError error(
188 from_here, syncer::SyncError::DATATYPE_ERROR, message, type);
189 p->DisableDatatype(error);
190 }
191
192 DataTypeController::DisableTypeCallback
193 ProfileSyncComponentsFactoryImpl::MakeDisableCallbackFor(
194 syncer::ModelType type) {
195 return base::Bind(&ProfileSyncComponentsFactoryImpl::DisableBrokenType,
196 weak_factory_.GetWeakPtr(),
197 type);
198 }
199
182 void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes( 200 void ProfileSyncComponentsFactoryImpl::RegisterCommonDataTypes(
183 syncer::ModelTypeSet disabled_types, 201 syncer::ModelTypeSet disabled_types,
184 syncer::ModelTypeSet enabled_types, 202 syncer::ModelTypeSet enabled_types,
185 ProfileSyncService* pss) { 203 ProfileSyncService* pss) {
186 // Autofill sync is enabled by default. Register unless explicitly 204 // Autofill sync is enabled by default. Register unless explicitly
187 // disabled. 205 // disabled.
188 if (!disabled_types.Has(syncer::AUTOFILL)) { 206 if (!disabled_types.Has(syncer::AUTOFILL)) {
189 pss->RegisterDataTypeController( 207 pss->RegisterDataTypeController(
190 new AutofillDataTypeController(this, profile_)); 208 new AutofillDataTypeController(
209 this, profile_, MakeDisableCallbackFor(syncer::AUTOFILL)));
191 } 210 }
192 211
193 // Autofill profile sync is enabled by default. Register unless explicitly 212 // Autofill profile sync is enabled by default. Register unless explicitly
194 // disabled. 213 // disabled.
195 if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) { 214 if (!disabled_types.Has(syncer::AUTOFILL_PROFILE)) {
196 pss->RegisterDataTypeController( 215 pss->RegisterDataTypeController(
197 new AutofillProfileDataTypeController(this, profile_)); 216 new AutofillProfileDataTypeController(
217 this, profile_, MakeDisableCallbackFor(syncer::AUTOFILL_PROFILE)));
198 } 218 }
199 219
200 // Bookmark sync is enabled by default. Register unless explicitly 220 // Bookmark sync is enabled by default. Register unless explicitly
201 // disabled. 221 // disabled.
202 if (!disabled_types.Has(syncer::BOOKMARKS)) { 222 if (!disabled_types.Has(syncer::BOOKMARKS)) {
203 pss->RegisterDataTypeController( 223 pss->RegisterDataTypeController(
204 new BookmarkDataTypeController(this, profile_, pss)); 224 new BookmarkDataTypeController(this, profile_, pss));
205 } 225 }
206 226
207 // TypedUrl sync is enabled by default. Register unless explicitly disabled, 227 // TypedUrl sync is enabled by default. Register unless explicitly disabled,
208 // or if saving history is disabled. 228 // or if saving history is disabled.
209 if (!profile_->GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled) && 229 if (!profile_->GetPrefs()->GetBoolean(prefs::kSavingBrowserHistoryDisabled) &&
210 !disabled_types.Has(syncer::TYPED_URLS)) { 230 !disabled_types.Has(syncer::TYPED_URLS)) {
211 pss->RegisterDataTypeController( 231 pss->RegisterDataTypeController(
212 new TypedUrlDataTypeController(this, profile_, pss)); 232 new TypedUrlDataTypeController(this, profile_, pss));
213 } 233 }
214 234
215 // Delete directive sync is enabled by default. Register unless full history 235 // Delete directive sync is enabled by default. Register unless full history
216 // sync is disabled. 236 // sync is disabled.
217 if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) { 237 if (!disabled_types.Has(syncer::HISTORY_DELETE_DIRECTIVES)) {
218 pss->RegisterDataTypeController( 238 pss->RegisterDataTypeController(
219 new UIDataTypeController( 239 new UIDataTypeController(
220 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 240 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
221 base::Bind(&ChromeReportUnrecoverableError), 241 base::Bind(&ChromeReportUnrecoverableError),
242 MakeDisableCallbackFor(syncer::HISTORY_DELETE_DIRECTIVES),
222 syncer::HISTORY_DELETE_DIRECTIVES, 243 syncer::HISTORY_DELETE_DIRECTIVES,
223 this)); 244 this));
224 } 245 }
225 246
226 // Session sync is enabled by default. Register unless explicitly disabled. 247 // Session sync is enabled by default. Register unless explicitly disabled.
227 if (!disabled_types.Has(syncer::PROXY_TABS)) { 248 if (!disabled_types.Has(syncer::PROXY_TABS)) {
228 pss->RegisterDataTypeController(new ProxyDataTypeController( 249 pss->RegisterDataTypeController(new ProxyDataTypeController(
229 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 250 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
230 syncer::PROXY_TABS)); 251 syncer::PROXY_TABS));
231 pss->RegisterDataTypeController( 252 pss->RegisterDataTypeController(
232 new SessionDataTypeController(this, 253 new SessionDataTypeController(
233 profile_, 254 this,
234 pss->GetSyncedWindowDelegatesGetter(), 255 profile_,
235 pss->GetLocalDeviceInfoProvider())); 256 pss->GetSyncedWindowDelegatesGetter(),
257 pss->GetLocalDeviceInfoProvider(),
258 MakeDisableCallbackFor(syncer::SESSIONS)));
236 } 259 }
237 260
238 // Favicon sync is enabled by default. Register unless explicitly disabled. 261 // Favicon sync is enabled by default. Register unless explicitly disabled.
239 if (!disabled_types.Has(syncer::FAVICON_IMAGES) && 262 if (!disabled_types.Has(syncer::FAVICON_IMAGES) &&
240 !disabled_types.Has(syncer::FAVICON_TRACKING)) { 263 !disabled_types.Has(syncer::FAVICON_TRACKING)) {
241 pss->RegisterDataTypeController( 264 pss->RegisterDataTypeController(
242 new UIDataTypeController( 265 new UIDataTypeController(
243 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 266 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
244 base::Bind(&ChromeReportUnrecoverableError), 267 base::Bind(&ChromeReportUnrecoverableError),
268 MakeDisableCallbackFor(syncer::FAVICON_IMAGES),
245 syncer::FAVICON_IMAGES, 269 syncer::FAVICON_IMAGES,
246 this)); 270 this));
247 pss->RegisterDataTypeController( 271 pss->RegisterDataTypeController(
248 new UIDataTypeController( 272 new UIDataTypeController(
249 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 273 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
250 base::Bind(&ChromeReportUnrecoverableError), 274 base::Bind(&ChromeReportUnrecoverableError),
275 MakeDisableCallbackFor(syncer::FAVICON_TRACKING),
251 syncer::FAVICON_TRACKING, 276 syncer::FAVICON_TRACKING,
252 this)); 277 this));
253 } 278 }
254 279
255 // Password sync is enabled by default. Register unless explicitly 280 // Password sync is enabled by default. Register unless explicitly
256 // disabled. 281 // disabled.
257 if (!disabled_types.Has(syncer::PASSWORDS)) { 282 if (!disabled_types.Has(syncer::PASSWORDS)) {
258 pss->RegisterDataTypeController( 283 pss->RegisterDataTypeController(
259 new PasswordDataTypeController(this, profile_)); 284 new PasswordDataTypeController(
285 this, profile_, MakeDisableCallbackFor(syncer::PASSWORDS)));
260 } 286 }
261 287
262 // Article sync is disabled by default. Register only if explicitly enabled. 288 // Article sync is disabled by default. Register only if explicitly enabled.
263 if (IsEnableSyncArticlesSet()) { 289 if (IsEnableSyncArticlesSet()) {
264 pss->RegisterDataTypeController( 290 pss->RegisterDataTypeController(
265 new UIDataTypeController( 291 new UIDataTypeController(
266 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 292 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
267 base::Bind(&ChromeReportUnrecoverableError), 293 base::Bind(&ChromeReportUnrecoverableError),
294 MakeDisableCallbackFor(syncer::ARTICLES),
268 syncer::ARTICLES, 295 syncer::ARTICLES,
269 this)); 296 this));
270 } 297 }
271 298
272 #if defined(ENABLE_MANAGED_USERS) 299 #if defined(ENABLE_MANAGED_USERS)
273 pss->RegisterDataTypeController( 300 pss->RegisterDataTypeController(
274 new SupervisedUserSyncDataTypeController( 301 new SupervisedUserSyncDataTypeController(
302 MakeDisableCallbackFor(syncer::SUPERVISED_USER_SETTINGS),
275 syncer::SUPERVISED_USER_SETTINGS, 303 syncer::SUPERVISED_USER_SETTINGS,
276 this, 304 this,
277 profile_)); 305 profile_));
278 pss->RegisterDataTypeController( 306 pss->RegisterDataTypeController(
279 new SupervisedUserSyncDataTypeController( 307 new SupervisedUserSyncDataTypeController(
308 MakeDisableCallbackFor(syncer::SUPERVISED_USERS),
280 syncer::SUPERVISED_USERS, 309 syncer::SUPERVISED_USERS,
281 this, 310 this,
282 profile_)); 311 profile_));
283 pss->RegisterDataTypeController( 312 pss->RegisterDataTypeController(
284 new SupervisedUserSyncDataTypeController( 313 new SupervisedUserSyncDataTypeController(
314 MakeDisableCallbackFor(syncer::SUPERVISED_USER_SHARED_SETTINGS),
285 syncer::SUPERVISED_USER_SHARED_SETTINGS, 315 syncer::SUPERVISED_USER_SHARED_SETTINGS,
286 this, 316 this,
287 profile_)); 317 profile_));
288 #endif 318 #endif
289 } 319 }
290 320
291 void ProfileSyncComponentsFactoryImpl::RegisterDesktopDataTypes( 321 void ProfileSyncComponentsFactoryImpl::RegisterDesktopDataTypes(
292 syncer::ModelTypeSet disabled_types, 322 syncer::ModelTypeSet disabled_types,
293 syncer::ModelTypeSet enabled_types, 323 syncer::ModelTypeSet enabled_types,
294 ProfileSyncService* pss) { 324 ProfileSyncService* pss) {
295 // App sync is enabled by default. Register unless explicitly 325 // App sync is enabled by default. Register unless explicitly
296 // disabled. 326 // disabled.
297 if (!disabled_types.Has(syncer::APPS)) { 327 if (!disabled_types.Has(syncer::APPS)) {
298 pss->RegisterDataTypeController( 328 pss->RegisterDataTypeController(
299 new ExtensionDataTypeController(syncer::APPS, this, profile_)); 329 new ExtensionDataTypeController(syncer::APPS, this, profile_,
330 MakeDisableCallbackFor(syncer::APPS)));
300 } 331 }
301 332
302 // Extension sync is enabled by default. Register unless explicitly 333 // Extension sync is enabled by default. Register unless explicitly
303 // disabled. 334 // disabled.
304 if (!disabled_types.Has(syncer::EXTENSIONS)) { 335 if (!disabled_types.Has(syncer::EXTENSIONS)) {
305 pss->RegisterDataTypeController( 336 pss->RegisterDataTypeController(
306 new ExtensionDataTypeController(syncer::EXTENSIONS, this, profile_)); 337 new ExtensionDataTypeController(
338 syncer::EXTENSIONS, this, profile_,
339 MakeDisableCallbackFor(syncer::EXTENSIONS)));
307 } 340 }
308 341
309 // Preference sync is enabled by default. Register unless explicitly 342 // Preference sync is enabled by default. Register unless explicitly
310 // disabled. 343 // disabled.
311 if (!disabled_types.Has(syncer::PREFERENCES)) { 344 if (!disabled_types.Has(syncer::PREFERENCES)) {
312 pss->RegisterDataTypeController( 345 pss->RegisterDataTypeController(
313 new UIDataTypeController( 346 new UIDataTypeController(
314 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 347 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
315 base::Bind(&ChromeReportUnrecoverableError), 348 base::Bind(&ChromeReportUnrecoverableError),
349 MakeDisableCallbackFor(syncer::PREFERENCES),
316 syncer::PREFERENCES, 350 syncer::PREFERENCES,
317 this)); 351 this));
318 } 352 }
319 353
320 if (!disabled_types.Has(syncer::PRIORITY_PREFERENCES)) { 354 if (!disabled_types.Has(syncer::PRIORITY_PREFERENCES)) {
321 pss->RegisterDataTypeController( 355 pss->RegisterDataTypeController(
322 new UIDataTypeController( 356 new UIDataTypeController(
323 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 357 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
324 base::Bind(&ChromeReportUnrecoverableError), 358 base::Bind(&ChromeReportUnrecoverableError),
359 MakeDisableCallbackFor(syncer::PRIORITY_PREFERENCES),
325 syncer::PRIORITY_PREFERENCES, 360 syncer::PRIORITY_PREFERENCES,
326 this)); 361 this));
327 } 362 }
328 363
329 #if defined(ENABLE_THEMES) 364 #if defined(ENABLE_THEMES)
330 // Theme sync is enabled by default. Register unless explicitly disabled. 365 // Theme sync is enabled by default. Register unless explicitly disabled.
331 if (!disabled_types.Has(syncer::THEMES)) { 366 if (!disabled_types.Has(syncer::THEMES)) {
332 pss->RegisterDataTypeController( 367 pss->RegisterDataTypeController(
333 new ThemeDataTypeController(this, profile_)); 368 new ThemeDataTypeController(this, profile_,
369 MakeDisableCallbackFor(syncer::THEMES)));
334 } 370 }
335 #endif 371 #endif
336 372
337 // Search Engine sync is enabled by default. Register unless explicitly 373 // Search Engine sync is enabled by default. Register unless explicitly
338 // disabled. 374 // disabled.
339 if (!disabled_types.Has(syncer::SEARCH_ENGINES)) { 375 if (!disabled_types.Has(syncer::SEARCH_ENGINES)) {
340 pss->RegisterDataTypeController( 376 pss->RegisterDataTypeController(
341 new SearchEngineDataTypeController(this, profile_)); 377 new SearchEngineDataTypeController(
378 this, profile_, MakeDisableCallbackFor(syncer::SEARCH_ENGINES)));
342 } 379 }
343 380
344 // Extension setting sync is enabled by default. Register unless explicitly 381 // Extension setting sync is enabled by default. Register unless explicitly
345 // disabled. 382 // disabled.
346 if (!disabled_types.Has(syncer::EXTENSION_SETTINGS)) { 383 if (!disabled_types.Has(syncer::EXTENSION_SETTINGS)) {
347 pss->RegisterDataTypeController(new ExtensionSettingDataTypeController( 384 pss->RegisterDataTypeController(
348 syncer::EXTENSION_SETTINGS, this, profile_)); 385 new ExtensionSettingDataTypeController(
386 syncer::EXTENSION_SETTINGS, this, profile_,
387 MakeDisableCallbackFor(syncer::EXTENSION_SETTINGS)));
349 } 388 }
350 389
351 // App setting sync is enabled by default. Register unless explicitly 390 // App setting sync is enabled by default. Register unless explicitly
352 // disabled. 391 // disabled.
353 if (!disabled_types.Has(syncer::APP_SETTINGS)) { 392 if (!disabled_types.Has(syncer::APP_SETTINGS)) {
354 pss->RegisterDataTypeController(new ExtensionSettingDataTypeController( 393 pss->RegisterDataTypeController(
355 syncer::APP_SETTINGS, this, profile_)); 394 new ExtensionSettingDataTypeController(
395 syncer::APP_SETTINGS, this, profile_,
396 MakeDisableCallbackFor(syncer::APP_SETTINGS)));
356 } 397 }
357 398
358 #if defined(ENABLE_APP_LIST) 399 #if defined(ENABLE_APP_LIST)
359 if (app_list::switches::IsAppListSyncEnabled()) { 400 if (app_list::switches::IsAppListSyncEnabled()) {
360 pss->RegisterDataTypeController( 401 pss->RegisterDataTypeController(
361 new UIDataTypeController( 402 new UIDataTypeController(
362 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 403 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
363 base::Bind(&ChromeReportUnrecoverableError), 404 base::Bind(&ChromeReportUnrecoverableError),
405 MakeDisableCallbackFor(syncer::APP_LIST),
364 syncer::APP_LIST, 406 syncer::APP_LIST,
365 this)); 407 this));
366 } 408 }
367 #endif 409 #endif
368 410
369 // Synced Notifications are disabled by default. 411 // Synced Notifications are disabled by default.
370 #if defined(ENABLE_EXTENSIONS) && defined(ENABLE_NOTIFICATIONS) 412 #if defined(ENABLE_EXTENSIONS) && defined(ENABLE_NOTIFICATIONS)
371 if (enabled_types.Has(syncer::SYNCED_NOTIFICATIONS)) { 413 if (enabled_types.Has(syncer::SYNCED_NOTIFICATIONS)) {
372 pss->RegisterDataTypeController( 414 pss->RegisterDataTypeController(
373 new ExtensionBackedDataTypeController( 415 new ExtensionBackedDataTypeController(
416 MakeDisableCallbackFor(syncer::SYNCED_NOTIFICATIONS),
374 syncer::SYNCED_NOTIFICATIONS, 417 syncer::SYNCED_NOTIFICATIONS,
375 "", // TODO(dewittj): pass the extension hash here. 418 "", // TODO(dewittj): pass the extension hash here.
376 this, 419 this,
377 profile_)); 420 profile_));
378 421
379 pss->RegisterDataTypeController( 422 pss->RegisterDataTypeController(
380 new ExtensionBackedDataTypeController( 423 new ExtensionBackedDataTypeController(
424 MakeDisableCallbackFor(syncer::SYNCED_NOTIFICATION_APP_INFO),
381 syncer::SYNCED_NOTIFICATION_APP_INFO, 425 syncer::SYNCED_NOTIFICATION_APP_INFO,
382 "", // TODO(dewittj): pass the extension hash here. 426 "", // TODO(dewittj): pass the extension hash here.
383 this, 427 this,
384 profile_)); 428 profile_));
385 } 429 }
386 #endif 430 #endif
387 431
388 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_CHROMEOS) 432 #if defined(OS_LINUX) || defined(OS_WIN) || defined(OS_CHROMEOS)
389 // Dictionary sync is enabled by default. 433 // Dictionary sync is enabled by default.
390 if (!disabled_types.Has(syncer::DICTIONARY)) { 434 if (!disabled_types.Has(syncer::DICTIONARY)) {
391 pss->RegisterDataTypeController( 435 pss->RegisterDataTypeController(
392 new UIDataTypeController( 436 new UIDataTypeController(
393 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI), 437 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
394 base::Bind(&ChromeReportUnrecoverableError), 438 base::Bind(&ChromeReportUnrecoverableError),
439 MakeDisableCallbackFor(syncer::DICTIONARY),
395 syncer::DICTIONARY, 440 syncer::DICTIONARY,
396 this)); 441 this));
397 } 442 }
398 #endif 443 #endif
399 } 444 }
400 445
401 DataTypeManager* ProfileSyncComponentsFactoryImpl::CreateDataTypeManager( 446 DataTypeManager* ProfileSyncComponentsFactoryImpl::CreateDataTypeManager(
402 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>& 447 const syncer::WeakHandle<syncer::DataTypeDebugInfoListener>&
403 debug_info_listener, 448 debug_info_listener,
404 const DataTypeController::TypeMap* controllers, 449 const DataTypeController::TypeMap* controllers,
(...skipping 267 matching lines...) Expand 10 before | Expand all | Expand 10 after
672 new TypedUrlModelAssociator(profile_sync_service, 717 new TypedUrlModelAssociator(profile_sync_service,
673 history_backend, 718 history_backend,
674 error_handler); 719 error_handler);
675 TypedUrlChangeProcessor* change_processor = 720 TypedUrlChangeProcessor* change_processor =
676 new TypedUrlChangeProcessor(profile_, 721 new TypedUrlChangeProcessor(profile_,
677 model_associator, 722 model_associator,
678 history_backend, 723 history_backend,
679 error_handler); 724 error_handler);
680 return SyncComponents(model_associator, change_processor); 725 return SyncComponents(model_associator, change_processor);
681 } 726 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698