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

Side by Side Diff: content/browser/renderer_host/resource_dispatcher_host.cc

Issue 9369009: Make content::ResourceContext be a real interface like the rest of the Content API (i.e. don't ha... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: sync Created 8 years, 10 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 // See http://dev.chromium.org/developers/design-documents/multi-process-resourc e-loading 5 // See http://dev.chromium.org/developers/design-documents/multi-process-resourc e-loading
6 6
7 #include "content/browser/renderer_host/resource_dispatcher_host.h" 7 #include "content/browser/renderer_host/resource_dispatcher_host.h"
8 8
9 #include <set> 9 #include <set>
10 #include <vector> 10 #include <vector>
(...skipping 347 matching lines...) Expand 10 before | Expand all | Expand 10 after
358 } 358 }
359 359
360 void ResourceDispatcherHost::AddResourceQueueDelegate( 360 void ResourceDispatcherHost::AddResourceQueueDelegate(
361 ResourceQueueDelegate* delegate) { 361 ResourceQueueDelegate* delegate) {
362 temporarily_delegate_set_->insert(delegate); 362 temporarily_delegate_set_->insert(delegate);
363 } 363 }
364 364
365 scoped_refptr<ResourceHandler> 365 scoped_refptr<ResourceHandler>
366 ResourceDispatcherHost::CreateResourceHandlerForDownload( 366 ResourceDispatcherHost::CreateResourceHandlerForDownload(
367 net::URLRequest* request, 367 net::URLRequest* request,
368 const content::ResourceContext& context, 368 content::ResourceContext* context,
369 int child_id, 369 int child_id,
370 int route_id, 370 int route_id,
371 int request_id, 371 int request_id,
372 const DownloadSaveInfo& save_info, 372 const DownloadSaveInfo& save_info,
373 const DownloadResourceHandler::OnStartedCallback& started_cb) { 373 const DownloadResourceHandler::OnStartedCallback& started_cb) {
374 scoped_refptr<ResourceHandler> handler( 374 scoped_refptr<ResourceHandler> handler(
375 new DownloadResourceHandler(this, child_id, route_id, request_id, 375 new DownloadResourceHandler(this, child_id, route_id, request_id,
376 request->url(), download_file_manager_.get(), 376 request->url(), download_file_manager_.get(),
377 request, started_cb, save_info)); 377 request, started_cb, save_info));
378 if (delegate_) { 378 if (delegate_) {
(...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after
528 GlobalRequestID old_request_id(request_data.transferred_request_child_id, 528 GlobalRequestID old_request_id(request_data.transferred_request_child_id,
529 request_data.transferred_request_request_id); 529 request_data.transferred_request_request_id);
530 TransferredNavigations::iterator iter = 530 TransferredNavigations::iterator iter =
531 transferred_navigations_.find(old_request_id); 531 transferred_navigations_.find(old_request_id);
532 if (iter != transferred_navigations_.end()) { 532 if (iter != transferred_navigations_.end()) {
533 deferred_request = iter->second; 533 deferred_request = iter->second;
534 pending_requests_.erase(old_request_id); 534 pending_requests_.erase(old_request_id);
535 transferred_navigations_.erase(iter); 535 transferred_navigations_.erase(iter);
536 } 536 }
537 537
538 const content::ResourceContext& resource_context = 538 content::ResourceContext* resource_context = filter_->resource_context();
539 filter_->resource_context();
540 539
541 // Might need to resolve the blob references in the upload data. 540 // Might need to resolve the blob references in the upload data.
542 if (request_data.upload_data) { 541 if (request_data.upload_data) {
543 resource_context.blob_storage_context()->controller()-> 542 resource_context->GetBlobStorageContext()->controller()->
544 ResolveBlobReferencesInUploadData(request_data.upload_data.get()); 543 ResolveBlobReferencesInUploadData(request_data.upload_data.get());
545 } 544 }
546 545
547 if (is_shutdown_ || 546 if (is_shutdown_ ||
548 !ShouldServiceRequest(process_type, child_id, request_data)) { 547 !ShouldServiceRequest(process_type, child_id, request_data)) {
549 AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id); 548 AbortRequestBeforeItStarts(filter_, sync_result, route_id, request_id);
550 return; 549 return;
551 } 550 }
552 551
553 const content::Referrer referrer(MaybeStripReferrer(request_data.referrer), 552 const content::Referrer referrer(MaybeStripReferrer(request_data.referrer),
(...skipping 19 matching lines...) Expand all
573 } else { 572 } else {
574 handler = new AsyncResourceHandler( 573 handler = new AsyncResourceHandler(
575 filter_, route_id, request_data.url, this); 574 filter_, route_id, request_data.url, this);
576 } 575 }
577 576
578 // The RedirectToFileResourceHandler depends on being next in the chain. 577 // The RedirectToFileResourceHandler depends on being next in the chain.
579 if (request_data.download_to_file) 578 if (request_data.download_to_file)
580 handler = new content::RedirectToFileResourceHandler(handler, child_id, 579 handler = new content::RedirectToFileResourceHandler(handler, child_id,
581 this); 580 this);
582 581
583 if (HandleExternalProtocol(request_id, child_id, route_id, 582 if (HandleExternalProtocol(
584 request_data.url, request_data.resource_type, 583 request_id, child_id, route_id, request_data.url,
585 *resource_context.request_context()->job_factory(), 584 request_data.resource_type,
586 handler)) { 585 *resource_context->GetRequestContext()->job_factory(), handler)) {
587 return; 586 return;
588 } 587 }
589 588
590 // Construct the request. 589 // Construct the request.
591 net::URLRequest* request; 590 net::URLRequest* request;
592 if (deferred_request) { 591 if (deferred_request) {
593 request = deferred_request; 592 request = deferred_request;
594 } else { 593 } else {
595 request = new net::URLRequest(request_data.url, this); 594 request = new net::URLRequest(request_data.url, this);
596 request->set_method(request_data.method); 595 request->set_method(request_data.method);
(...skipping 100 matching lines...) Expand 10 before | Expand all | Expand 10 after
697 request_data.frame_id, 696 request_data.frame_id,
698 request_data.parent_is_main_frame, 697 request_data.parent_is_main_frame,
699 request_data.parent_frame_id, 698 request_data.parent_frame_id,
700 request_data.resource_type, 699 request_data.resource_type,
701 request_data.transition_type, 700 request_data.transition_type,
702 upload_size, 701 upload_size,
703 false, // is download 702 false, // is download
704 ResourceType::IsFrame(request_data.resource_type), // allow_download 703 ResourceType::IsFrame(request_data.resource_type), // allow_download
705 request_data.has_user_gesture, 704 request_data.has_user_gesture,
706 request_data.referrer_policy, 705 request_data.referrer_policy,
707 &resource_context); 706 resource_context);
708 SetRequestInfo(request, extra_info); // Request takes ownership. 707 SetRequestInfo(request, extra_info); // Request takes ownership.
709 708
710 if (request->url().SchemeIs(chrome::kBlobScheme)) { 709 if (request->url().SchemeIs(chrome::kBlobScheme)) {
711 // Hang on to a reference to ensure the blob is not released prior 710 // Hang on to a reference to ensure the blob is not released prior
712 // to the job being started. 711 // to the job being started.
713 webkit_blob::BlobStorageController* controller = 712 webkit_blob::BlobStorageController* controller =
714 resource_context.blob_storage_context()->controller(); 713 resource_context->GetBlobStorageContext()->controller();
715 extra_info->set_requested_blob_data( 714 extra_info->set_requested_blob_data(
716 controller->GetBlobDataFromUrl(request->url())); 715 controller->GetBlobDataFromUrl(request->url()));
717 } 716 }
718 717
719 // Have the appcache associate its extra info with the request. 718 // Have the appcache associate its extra info with the request.
720 appcache::AppCacheInterceptor::SetExtraRequestInfo( 719 appcache::AppCacheInterceptor::SetExtraRequestInfo(
721 request, resource_context.appcache_service(), child_id, 720 request, resource_context->GetAppCacheService(), child_id,
722 request_data.appcache_host_id, request_data.resource_type); 721 request_data.appcache_host_id, request_data.resource_type);
723 722
724 if (deferred_request) { 723 if (deferred_request) {
725 // This is a request that has been transferred from another process, so 724 // This is a request that has been transferred from another process, so
726 // resume it rather than continuing the regular procedure for starting a 725 // resume it rather than continuing the regular procedure for starting a
727 // request. Currently this is only done for redirects. 726 // request. Currently this is only done for redirects.
728 GlobalRequestID global_id(extra_info->child_id(), extra_info->request_id()); 727 GlobalRequestID global_id(extra_info->child_id(), extra_info->request_id());
729 pending_requests_[global_id] = request; 728 pending_requests_[global_id] = request;
730 request->FollowDeferredRedirect(); 729 request->FollowDeferredRedirect();
731 } else { 730 } else {
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after
848 FollowDeferredRedirect(filter_->child_id(), request_id, 847 FollowDeferredRedirect(filter_->child_id(), request_id,
849 has_new_first_party_for_cookies, 848 has_new_first_party_for_cookies,
850 new_first_party_for_cookies); 849 new_first_party_for_cookies);
851 } 850 }
852 851
853 ResourceDispatcherHostRequestInfo* ResourceDispatcherHost::CreateRequestInfo( 852 ResourceDispatcherHostRequestInfo* ResourceDispatcherHost::CreateRequestInfo(
854 ResourceHandler* handler, 853 ResourceHandler* handler,
855 int child_id, 854 int child_id,
856 int route_id, 855 int route_id,
857 bool download, 856 bool download,
858 const content::ResourceContext& context) { 857 content::ResourceContext* context) {
859 return new ResourceDispatcherHostRequestInfo( 858 return new ResourceDispatcherHostRequestInfo(
860 handler, 859 handler,
861 content::PROCESS_TYPE_RENDERER, 860 content::PROCESS_TYPE_RENDERER,
862 child_id, 861 child_id,
863 route_id, 862 route_id,
864 0, 863 0,
865 request_id_, 864 request_id_,
866 false, // is_main_frame 865 false, // is_main_frame
867 -1, // frame_id 866 -1, // frame_id
868 false, // parent_is_main_frame 867 false, // parent_is_main_frame
869 -1, // parent_frame_id 868 -1, // parent_frame_id
870 ResourceType::SUB_RESOURCE, 869 ResourceType::SUB_RESOURCE,
871 content::PAGE_TRANSITION_LINK, 870 content::PAGE_TRANSITION_LINK,
872 0, // upload_size 871 0, // upload_size
873 download, // is_download 872 download, // is_download
874 download, // allow_download 873 download, // allow_download
875 false, // has_user_gesture 874 false, // has_user_gesture
876 WebKit::WebReferrerPolicyDefault, 875 WebKit::WebReferrerPolicyDefault,
877 &context); 876 context);
878 } 877 }
879 878
880 void ResourceDispatcherHost::OnSwapOutACK( 879 void ResourceDispatcherHost::OnSwapOutACK(
881 const ViewMsg_SwapOut_Params& params) { 880 const ViewMsg_SwapOut_Params& params) {
882 // Closes for cross-site transitions are handled such that the cross-site 881 // Closes for cross-site transitions are handled such that the cross-site
883 // transition continues. 882 // transition continues.
884 GlobalRequestID global_id(params.new_render_process_host_id, 883 GlobalRequestID global_id(params.new_render_process_host_id,
885 params.new_request_id); 884 params.new_request_id);
886 PendingRequestList::iterator i = pending_requests_.find(global_id); 885 PendingRequestList::iterator i = pending_requests_.find(global_id);
887 if (i != pending_requests_.end()) { 886 if (i != pending_requests_.end()) {
(...skipping 24 matching lines...) Expand all
912 } 911 }
913 912
914 // We are explicitly forcing the download of 'url'. 913 // We are explicitly forcing the download of 'url'.
915 net::Error ResourceDispatcherHost::BeginDownload( 914 net::Error ResourceDispatcherHost::BeginDownload(
916 scoped_ptr<net::URLRequest> request, 915 scoped_ptr<net::URLRequest> request,
917 bool prefer_cache, 916 bool prefer_cache,
918 const DownloadSaveInfo& save_info, 917 const DownloadSaveInfo& save_info,
919 const DownloadResourceHandler::OnStartedCallback& started_cb, 918 const DownloadResourceHandler::OnStartedCallback& started_cb,
920 int child_id, 919 int child_id,
921 int route_id, 920 int route_id,
922 const content::ResourceContext& context) { 921 content::ResourceContext* context) {
923 if (is_shutdown_) 922 if (is_shutdown_)
924 return net::ERR_INSUFFICIENT_RESOURCES; 923 return net::ERR_INSUFFICIENT_RESOURCES;
925 924
926 const GURL& url = request->original_url(); 925 const GURL& url = request->original_url();
927 const net::URLRequestContext* request_context = context.request_context(); 926 const net::URLRequestContext* request_context = context->GetRequestContext();
928 request->set_referrer(MaybeStripReferrer(GURL(request->referrer())).spec()); 927 request->set_referrer(MaybeStripReferrer(GURL(request->referrer())).spec());
929 request->set_context(request_context); 928 request->set_context(request_context);
930 int extra_load_flags = net::LOAD_IS_DOWNLOAD; 929 int extra_load_flags = net::LOAD_IS_DOWNLOAD;
931 if (prefer_cache) { 930 if (prefer_cache) {
932 // If there is upload data attached, only retrieve from cache because there 931 // If there is upload data attached, only retrieve from cache because there
933 // is no current mechanism to prompt the user for their consent for a 932 // is no current mechanism to prompt the user for their consent for a
934 // re-post. For GETs, try to retrieve data from the cache and skip 933 // re-post. For GETs, try to retrieve data from the cache and skip
935 // validating the entry if present. 934 // validating the entry if present.
936 if (request->get_upload() != NULL) 935 if (request->get_upload() != NULL)
937 extra_load_flags |= net::LOAD_ONLY_FROM_CACHE; 936 extra_load_flags |= net::LOAD_ONLY_FROM_CACHE;
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
970 969
971 return net::OK; 970 return net::OK;
972 } 971 }
973 972
974 // This function is only used for saving feature. 973 // This function is only used for saving feature.
975 void ResourceDispatcherHost::BeginSaveFile( 974 void ResourceDispatcherHost::BeginSaveFile(
976 const GURL& url, 975 const GURL& url,
977 const GURL& referrer, 976 const GURL& referrer,
978 int child_id, 977 int child_id,
979 int route_id, 978 int route_id,
980 const content::ResourceContext& context) { 979 content::ResourceContext* context) {
981 if (is_shutdown_) 980 if (is_shutdown_)
982 return; 981 return;
983 982
984 scoped_refptr<ResourceHandler> handler( 983 scoped_refptr<ResourceHandler> handler(
985 new SaveFileResourceHandler(child_id, 984 new SaveFileResourceHandler(child_id,
986 route_id, 985 route_id,
987 url, 986 url,
988 save_file_manager_.get())); 987 save_file_manager_.get()));
989 request_id_--; 988 request_id_--;
990 989
991 const net::URLRequestContext* request_context = context.request_context(); 990 const net::URLRequestContext* request_context = context->GetRequestContext();
992 bool known_proto = 991 bool known_proto =
993 request_context->job_factory()->IsHandledURL(url); 992 request_context->job_factory()->IsHandledURL(url);
994 if (!known_proto) { 993 if (!known_proto) {
995 // Since any URLs which have non-standard scheme have been filtered 994 // Since any URLs which have non-standard scheme have been filtered
996 // by save manager(see GURL::SchemeIsStandard). This situation 995 // by save manager(see GURL::SchemeIsStandard). This situation
997 // should not happen. 996 // should not happen.
998 NOTREACHED(); 997 NOTREACHED();
999 return; 998 return;
1000 } 999 }
1001 1000
1002 net::URLRequest* request = new net::URLRequest(url, this); 1001 net::URLRequest* request = new net::URLRequest(url, this);
1003 request->set_method("GET"); 1002 request->set_method("GET");
1004 request->set_referrer(MaybeStripReferrer(referrer).spec()); 1003 request->set_referrer(MaybeStripReferrer(referrer).spec());
1005 // So far, for saving page, we need fetch content from cache, in the 1004 // So far, for saving page, we need fetch content from cache, in the
1006 // future, maybe we can use a configuration to configure this behavior. 1005 // future, maybe we can use a configuration to configure this behavior.
1007 request->set_load_flags(net::LOAD_PREFERRING_CACHE); 1006 request->set_load_flags(net::LOAD_PREFERRING_CACHE);
1008 request->set_context(context.request_context()); 1007 request->set_context(context->GetRequestContext());
1009 1008
1010 // Since we're just saving some resources we need, disallow downloading. 1009 // Since we're just saving some resources we need, disallow downloading.
1011 ResourceDispatcherHostRequestInfo* extra_info = 1010 ResourceDispatcherHostRequestInfo* extra_info =
1012 CreateRequestInfo(handler, child_id, route_id, false, context); 1011 CreateRequestInfo(handler, child_id, route_id, false, context);
1013 SetRequestInfo(request, extra_info); // Request takes ownership. 1012 SetRequestInfo(request, extra_info); // Request takes ownership.
1014 1013
1015 BeginRequestInternal(request); 1014 BeginRequestInternal(request);
1016 } 1015 }
1017 1016
1018 void ResourceDispatcherHost::FollowDeferredRedirect( 1017 void ResourceDispatcherHost::FollowDeferredRedirect(
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after
1181 route_ids.insert(iter->first.second); 1180 route_ids.insert(iter->first.second);
1182 } 1181 }
1183 for (std::set<int>::const_iterator iter = route_ids.begin(); 1182 for (std::set<int>::const_iterator iter = route_ids.begin();
1184 iter != route_ids.end(); ++iter) { 1183 iter != route_ids.end(); ++iter) {
1185 CancelBlockedRequestsForRoute(child_id, *iter); 1184 CancelBlockedRequestsForRoute(child_id, *iter);
1186 } 1185 }
1187 } 1186 }
1188 } 1187 }
1189 1188
1190 void ResourceDispatcherHost::CancelRequestsForContext( 1189 void ResourceDispatcherHost::CancelRequestsForContext(
1191 const content::ResourceContext* context) { 1190 content::ResourceContext* context) {
1192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 1191 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
1193 DCHECK(context); 1192 DCHECK(context);
1194 1193
1195 // Note that request cancellation has side effects. Therefore, we gather all 1194 // Note that request cancellation has side effects. Therefore, we gather all
1196 // the requests to cancel first, and then we start cancelling. We assert at 1195 // the requests to cancel first, and then we start cancelling. We assert at
1197 // the end that there are no more to cancel since the context is about to go 1196 // the end that there are no more to cancel since the context is about to go
1198 // away. 1197 // away.
1199 std::vector<net::URLRequest*> requests_to_cancel; 1198 std::vector<net::URLRequest*> requests_to_cancel;
1200 for (PendingRequestList::iterator i = pending_requests_.begin(); 1199 for (PendingRequestList::iterator i = pending_requests_.begin();
1201 i != pending_requests_.end();) { 1200 i != pending_requests_.end();) {
(...skipping 233 matching lines...) Expand 10 before | Expand all | Expand 10 after
1435 const net::CookieList& cookie_list) const { 1434 const net::CookieList& cookie_list) const {
1436 VLOG(1) << "OnGetCookies: " << request->url().spec(); 1435 VLOG(1) << "OnGetCookies: " << request->url().spec();
1437 int render_process_id, render_view_id; 1436 int render_process_id, render_view_id;
1438 if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) 1437 if (!RenderViewForRequest(request, &render_process_id, &render_view_id))
1439 return false; 1438 return false;
1440 1439
1441 const ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); 1440 const ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
1442 1441
1443 return content::GetContentClient()->browser()->AllowGetCookie( 1442 return content::GetContentClient()->browser()->AllowGetCookie(
1444 request->url(), request->first_party_for_cookies(), cookie_list, 1443 request->url(), request->first_party_for_cookies(), cookie_list,
1445 *info->context(), render_process_id, render_view_id); 1444 info->context(), render_process_id, render_view_id);
1446 } 1445 }
1447 1446
1448 bool ResourceDispatcherHost::CanSetCookie(const net::URLRequest* request, 1447 bool ResourceDispatcherHost::CanSetCookie(const net::URLRequest* request,
1449 const std::string& cookie_line, 1448 const std::string& cookie_line,
1450 net::CookieOptions* options) const { 1449 net::CookieOptions* options) const {
1451 VLOG(1) << "OnSetCookie: " << request->url().spec(); 1450 VLOG(1) << "OnSetCookie: " << request->url().spec();
1452 1451
1453 int render_process_id, render_view_id; 1452 int render_process_id, render_view_id;
1454 if (!RenderViewForRequest(request, &render_process_id, &render_view_id)) 1453 if (!RenderViewForRequest(request, &render_process_id, &render_view_id))
1455 return false; 1454 return false;
1456 1455
1457 const ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); 1456 const ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
1458 return content::GetContentClient()->browser()->AllowSetCookie( 1457 return content::GetContentClient()->browser()->AllowSetCookie(
1459 request->url(), request->first_party_for_cookies(), cookie_line, 1458 request->url(), request->first_party_for_cookies(), cookie_line,
1460 *info->context(), render_process_id, render_view_id, options); 1459 info->context(), render_process_id, render_view_id, options);
1461 } 1460 }
1462 1461
1463 void ResourceDispatcherHost::OnResponseStarted(net::URLRequest* request) { 1462 void ResourceDispatcherHost::OnResponseStarted(net::URLRequest* request) {
1464 VLOG(1) << "OnResponseStarted: " << request->url().spec(); 1463 VLOG(1) << "OnResponseStarted: " << request->url().spec();
1465 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request); 1464 ResourceDispatcherHostRequestInfo* info = InfoForRequest(request);
1466 1465
1467 if (request->status().is_success()) { 1466 if (request->status().is_success()) {
1468 if (PauseRequestIfNeeded(info)) { 1467 if (PauseRequestIfNeeded(info)) {
1469 VLOG(1) << "OnResponseStarted pausing: " << request->url().spec(); 1468 VLOG(1) << "OnResponseStarted pausing: " << request->url().spec();
1470 return; 1469 return;
(...skipping 829 matching lines...) Expand 10 before | Expand all | Expand 10 after
2300 scoped_refptr<ResourceHandler> transferred_resource_handler( 2299 scoped_refptr<ResourceHandler> transferred_resource_handler(
2301 new DoomedResourceHandler(info->resource_handler())); 2300 new DoomedResourceHandler(info->resource_handler()));
2302 info->set_resource_handler(transferred_resource_handler.get()); 2301 info->set_resource_handler(transferred_resource_handler.get());
2303 } 2302 }
2304 2303
2305 bool ResourceDispatcherHost::IsTransferredNavigation( 2304 bool ResourceDispatcherHost::IsTransferredNavigation(
2306 const content::GlobalRequestID& transferred_request_id) const { 2305 const content::GlobalRequestID& transferred_request_id) const {
2307 return transferred_navigations_.find(transferred_request_id) != 2306 return transferred_navigations_.find(transferred_request_id) !=
2308 transferred_navigations_.end(); 2307 transferred_navigations_.end();
2309 } 2308 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698