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

Side by Side Diff: net/proxy/proxy_service.cc

Issue 10534132: NetLogEventParameter to Callback refactoring 7. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Remove unneeded variable 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 | Annotate | Revision Log
« no previous file with comments | « net/proxy/proxy_script_decider.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 "net/proxy/proxy_service.h" 5 #include "net/proxy/proxy_service.h"
6 6
7 #include <algorithm> 7 #include <algorithm>
8 8
9 #include "base/bind.h" 9 #include "base/bind.h"
10 #include "base/bind_helpers.h" 10 #include "base/bind_helpers.h"
(...skipping 315 matching lines...) Expand 10 before | Expand all | Expand 10 after
326 326
327 static bool IsSupported() { 327 static bool IsSupported() {
328 #if defined(OS_WIN) || defined(OS_MACOSX) 328 #if defined(OS_WIN) || defined(OS_MACOSX)
329 return true; 329 return true;
330 #else 330 #else
331 return false; 331 return false;
332 #endif 332 #endif
333 } 333 }
334 }; 334 };
335 335
336 // NetLog parameter to describe a proxy configuration change. 336 // Returns NetLog parameters describing a proxy configuration change.
337 class ProxyConfigChangedNetLogParam : public NetLog::EventParameters { 337 Value* NetLogProxyConfigChangedCallback(const ProxyConfig* old_config,
338 public: 338 const ProxyConfig* new_config,
339 ProxyConfigChangedNetLogParam(const ProxyConfig& old_config, 339 NetLog::LogLevel /* log_level */) {
340 const ProxyConfig& new_config) 340 DictionaryValue* dict = new DictionaryValue();
341 : old_config_(old_config), 341 // The "old_config" is optional -- the first notification will not have
342 new_config_(new_config) { 342 // any "previous" configuration.
343 if (old_config->is_valid())
344 dict->Set("old_config", old_config->ToValue());
345 dict->Set("new_config", new_config->ToValue());
346 return dict;
347 }
348
349 Value* NetLogBadProxyListCallback(const ProxyRetryInfoMap* retry_info,
350 NetLog::LogLevel /* log_level */) {
351 DictionaryValue* dict = new DictionaryValue();
352 ListValue* list = new ListValue();
353
354 for (ProxyRetryInfoMap::const_iterator iter = retry_info->begin();
355 iter != retry_info->end(); ++iter) {
356 list->Append(Value::CreateStringValue(iter->first));
343 } 357 }
358 dict->Set("bad_proxy_list", list);
359 return dict;
360 }
344 361
345 virtual Value* ToValue() const OVERRIDE { 362 // Returns NetLog parameters on a successfuly proxy resolution.
346 DictionaryValue* dict = new DictionaryValue(); 363 Value* NetLogFinishedResolvingProxyCallback(ProxyInfo* result,
347 // The "old_config" is optional -- the first notification will not have 364 NetLog::LogLevel /* log_level */) {
348 // any "previous" configuration. 365 DictionaryValue* dict = new DictionaryValue();
349 if (old_config_.is_valid()) 366 dict->SetString("pac_string", result->ToPacString());
350 dict->Set("old_config", old_config_.ToValue()); 367 return dict;
351 dict->Set("new_config", new_config_.ToValue()); 368 }
352 return dict;
353 }
354
355 protected:
356 virtual ~ProxyConfigChangedNetLogParam() {}
357
358 private:
359 const ProxyConfig old_config_;
360 const ProxyConfig new_config_;
361 DISALLOW_COPY_AND_ASSIGN(ProxyConfigChangedNetLogParam);
362 };
363
364 class BadProxyListNetLogParam : public NetLog::EventParameters {
365 public:
366 BadProxyListNetLogParam(const ProxyRetryInfoMap& retry_info) {
367 proxy_list_.reserve(retry_info.size());
368 for (ProxyRetryInfoMap::const_iterator iter = retry_info.begin();
369 iter != retry_info.end(); ++iter) {
370 proxy_list_.push_back(iter->first);
371 }
372 }
373
374 virtual Value* ToValue() const OVERRIDE {
375 DictionaryValue* dict = new DictionaryValue();
376 ListValue* list = new ListValue();
377 for (std::vector<std::string>::const_iterator iter = proxy_list_.begin();
378 iter != proxy_list_.end(); ++iter)
379 list->Append(Value::CreateStringValue(*iter));
380 dict->Set("bad_proxy_list", list);
381 return dict;
382 }
383
384 protected:
385 virtual ~BadProxyListNetLogParam() {}
386
387 private:
388 std::vector<std::string> proxy_list_;
389 DISALLOW_COPY_AND_ASSIGN(BadProxyListNetLogParam);
390 };
391 369
392 #if defined(OS_CHROMEOS) 370 #if defined(OS_CHROMEOS)
393 class UnsetProxyConfigService : public ProxyConfigService { 371 class UnsetProxyConfigService : public ProxyConfigService {
394 public: 372 public:
395 UnsetProxyConfigService() {} 373 UnsetProxyConfigService() {}
396 virtual ~UnsetProxyConfigService() {} 374 virtual ~UnsetProxyConfigService() {}
397 375
398 virtual void AddObserver(Observer* observer) {} 376 virtual void AddObserver(Observer* observer) {}
399 virtual void RemoveObserver(Observer* observer) {} 377 virtual void RemoveObserver(Observer* observer) {}
400 virtual ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) { 378 virtual ConfigAvailability GetLatestProxyConfig(ProxyConfig* config) {
(...skipping 438 matching lines...) Expand 10 before | Expand all | Expand 10 after
839 817
840 void CancelResolveJob() { 818 void CancelResolveJob() {
841 DCHECK(is_started()); 819 DCHECK(is_started());
842 // The request may already be running in the resolver. 820 // The request may already be running in the resolver.
843 resolver()->CancelRequest(resolve_job_); 821 resolver()->CancelRequest(resolve_job_);
844 resolve_job_ = NULL; 822 resolve_job_ = NULL;
845 DCHECK(!is_started()); 823 DCHECK(!is_started());
846 } 824 }
847 825
848 void Cancel() { 826 void Cancel() {
849 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL); 827 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
850 828
851 if (is_started()) 829 if (is_started())
852 CancelResolveJob(); 830 CancelResolveJob();
853 831
854 // Mark as cancelled, to prevent accessing this again later. 832 // Mark as cancelled, to prevent accessing this again later.
855 service_ = NULL; 833 service_ = NULL;
856 user_callback_.Reset(); 834 user_callback_.Reset();
857 results_ = NULL; 835 results_ = NULL;
858 836
859 net_log_.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL); 837 net_log_.EndEvent(NetLog::TYPE_PROXY_SERVICE);
860 } 838 }
861 839
862 // Returns true if Cancel() has been called. 840 // Returns true if Cancel() has been called.
863 bool was_cancelled() const { 841 bool was_cancelled() const {
864 return user_callback_.is_null(); 842 return user_callback_.is_null();
865 } 843 }
866 844
867 // Helper to call after ProxyResolver completion (both synchronous and 845 // Helper to call after ProxyResolver completion (both synchronous and
868 // asynchronous). Fixes up the result that is to be returned to user. 846 // asynchronous). Fixes up the result that is to be returned to user.
869 int QueryDidComplete(int result_code) { 847 int QueryDidComplete(int result_code) {
(...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after
1052 } 1030 }
1053 1031
1054 int ProxyService::ResolveProxy(const GURL& raw_url, 1032 int ProxyService::ResolveProxy(const GURL& raw_url,
1055 ProxyInfo* result, 1033 ProxyInfo* result,
1056 const net::CompletionCallback& callback, 1034 const net::CompletionCallback& callback,
1057 PacRequest** pac_request, 1035 PacRequest** pac_request,
1058 const BoundNetLog& net_log) { 1036 const BoundNetLog& net_log) {
1059 DCHECK(CalledOnValidThread()); 1037 DCHECK(CalledOnValidThread());
1060 DCHECK(!callback.is_null()); 1038 DCHECK(!callback.is_null());
1061 1039
1062 net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE, NULL); 1040 net_log.BeginEvent(NetLog::TYPE_PROXY_SERVICE);
1063 1041
1064 // Notify our polling-based dependencies that a resolve is taking place. 1042 // Notify our polling-based dependencies that a resolve is taking place.
1065 // This way they can schedule their polls in response to network activity. 1043 // This way they can schedule their polls in response to network activity.
1066 config_service_->OnLazyPoll(); 1044 config_service_->OnLazyPoll();
1067 if (script_poller_.get()) 1045 if (script_poller_.get())
1068 script_poller_->OnLazyPoll(); 1046 script_poller_->OnLazyPoll();
1069 1047
1070 if (current_state_ == STATE_NONE) 1048 if (current_state_ == STATE_NONE)
1071 ApplyProxyConfigIfAvailable(); 1049 ApplyProxyConfigIfAvailable();
1072 1050
1073 // Strip away any reference fragments and the username/password, as they 1051 // Strip away any reference fragments and the username/password, as they
1074 // are not relevant to proxy resolution. 1052 // are not relevant to proxy resolution.
1075 GURL url = SimplifyUrlForRequest(raw_url); 1053 GURL url = SimplifyUrlForRequest(raw_url);
1076 1054
1077 // Check if the request can be completed right away. (This is the case when 1055 // Check if the request can be completed right away. (This is the case when
1078 // using a direct connection for example). 1056 // using a direct connection for example).
1079 int rv = TryToCompleteSynchronously(url, result); 1057 int rv = TryToCompleteSynchronously(url, result);
1080 if (rv != ERR_IO_PENDING) 1058 if (rv != ERR_IO_PENDING)
1081 return DidFinishResolvingProxy(result, rv, net_log); 1059 return DidFinishResolvingProxy(result, rv, net_log);
1082 1060
1083 scoped_refptr<PacRequest> req( 1061 scoped_refptr<PacRequest> req(
1084 new PacRequest(this, url, result, callback, net_log)); 1062 new PacRequest(this, url, result, callback, net_log));
1085 1063
1086 if (current_state_ == STATE_READY) { 1064 if (current_state_ == STATE_READY) {
1087 // Start the resolve request. 1065 // Start the resolve request.
1088 rv = req->Start(); 1066 rv = req->Start();
1089 if (rv != ERR_IO_PENDING) 1067 if (rv != ERR_IO_PENDING)
1090 return req->QueryDidComplete(rv); 1068 return req->QueryDidComplete(rv);
1091 } else { 1069 } else {
1092 req->net_log()->BeginEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, 1070 req->net_log()->BeginEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC);
1093 NULL);
1094 } 1071 }
1095 1072
1096 DCHECK_EQ(ERR_IO_PENDING, rv); 1073 DCHECK_EQ(ERR_IO_PENDING, rv);
1097 DCHECK(!ContainsPendingRequest(req)); 1074 DCHECK(!ContainsPendingRequest(req));
1098 pending_requests_.push_back(req); 1075 pending_requests_.push_back(req);
1099 1076
1100 // Completion will be notified through |callback|, unless the caller cancels 1077 // Completion will be notified through |callback|, unless the caller cancels
1101 // the request using |pac_request|. 1078 // the request using |pac_request|.
1102 if (pac_request) 1079 if (pac_request)
1103 *pac_request = req.get(); 1080 *pac_request = req.get();
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after
1142 1119
1143 void ProxyService::SuspendAllPendingRequests() { 1120 void ProxyService::SuspendAllPendingRequests() {
1144 for (PendingRequests::iterator it = pending_requests_.begin(); 1121 for (PendingRequests::iterator it = pending_requests_.begin();
1145 it != pending_requests_.end(); 1122 it != pending_requests_.end();
1146 ++it) { 1123 ++it) {
1147 PacRequest* req = it->get(); 1124 PacRequest* req = it->get();
1148 if (req->is_started()) { 1125 if (req->is_started()) {
1149 req->CancelResolveJob(); 1126 req->CancelResolveJob();
1150 1127
1151 req->net_log()->BeginEvent( 1128 req->net_log()->BeginEvent(
1152 NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, NULL); 1129 NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC);
1153 } 1130 }
1154 } 1131 }
1155 } 1132 }
1156 1133
1157 void ProxyService::SetReady() { 1134 void ProxyService::SetReady() {
1158 DCHECK(!init_proxy_resolver_.get()); 1135 DCHECK(!init_proxy_resolver_.get());
1159 current_state_ = STATE_READY; 1136 current_state_ = STATE_READY;
1160 1137
1161 // Make a copy in case |this| is deleted during the synchronous completion 1138 // Make a copy in case |this| is deleted during the synchronous completion
1162 // of one of the requests. If |this| is deleted then all of the PacRequest 1139 // of one of the requests. If |this| is deleted then all of the PacRequest
1163 // instances will be Cancel()-ed. 1140 // instances will be Cancel()-ed.
1164 PendingRequests pending_copy = pending_requests_; 1141 PendingRequests pending_copy = pending_requests_;
1165 1142
1166 for (PendingRequests::iterator it = pending_copy.begin(); 1143 for (PendingRequests::iterator it = pending_copy.begin();
1167 it != pending_copy.end(); 1144 it != pending_copy.end();
1168 ++it) { 1145 ++it) {
1169 PacRequest* req = it->get(); 1146 PacRequest* req = it->get();
1170 if (!req->is_started() && !req->was_cancelled()) { 1147 if (!req->is_started() && !req->was_cancelled()) {
1171 req->net_log()->EndEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC, 1148 req->net_log()->EndEvent(NetLog::TYPE_PROXY_SERVICE_WAITING_FOR_INIT_PAC);
1172 NULL);
1173 1149
1174 // Note that we re-check for synchronous completion, in case we are 1150 // Note that we re-check for synchronous completion, in case we are
1175 // no longer using a ProxyResolver (can happen if we fell-back to manual). 1151 // no longer using a ProxyResolver (can happen if we fell-back to manual).
1176 req->StartAndCompleteCheckingForSynchronous(); 1152 req->StartAndCompleteCheckingForSynchronous();
1177 } 1153 }
1178 } 1154 }
1179 } 1155 }
1180 1156
1181 void ProxyService::ApplyProxyConfigIfAvailable() { 1157 void ProxyService::ApplyProxyConfigIfAvailable() {
1182 DCHECK_EQ(STATE_NONE, current_state_); 1158 DCHECK_EQ(STATE_NONE, current_state_);
(...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after
1292 iter != new_retry_info.end(); ++iter) { 1268 iter != new_retry_info.end(); ++iter) {
1293 ProxyRetryInfoMap::iterator existing = proxy_retry_info_.find(iter->first); 1269 ProxyRetryInfoMap::iterator existing = proxy_retry_info_.find(iter->first);
1294 if (existing == proxy_retry_info_.end()) 1270 if (existing == proxy_retry_info_.end())
1295 proxy_retry_info_[iter->first] = iter->second; 1271 proxy_retry_info_[iter->first] = iter->second;
1296 else if (existing->second.bad_until < iter->second.bad_until) 1272 else if (existing->second.bad_until < iter->second.bad_until)
1297 existing->second.bad_until = iter->second.bad_until; 1273 existing->second.bad_until = iter->second.bad_until;
1298 } 1274 }
1299 if (net_log_) { 1275 if (net_log_) {
1300 net_log_->AddGlobalEntry( 1276 net_log_->AddGlobalEntry(
1301 NetLog::TYPE_BAD_PROXY_LIST_REPORTED, 1277 NetLog::TYPE_BAD_PROXY_LIST_REPORTED,
1302 make_scoped_refptr( 1278 base::Bind(&NetLogBadProxyListCallback, &new_retry_info));
1303 new BadProxyListNetLogParam(new_retry_info)));
1304 } 1279 }
1305 } 1280 }
1306 1281
1307 void ProxyService::CancelPacRequest(PacRequest* req) { 1282 void ProxyService::CancelPacRequest(PacRequest* req) {
1308 DCHECK(CalledOnValidThread()); 1283 DCHECK(CalledOnValidThread());
1309 DCHECK(req); 1284 DCHECK(req);
1310 req->Cancel(); 1285 req->Cancel();
1311 RemovePendingRequest(req); 1286 RemovePendingRequest(req);
1312 } 1287 }
1313 1288
(...skipping 17 matching lines...) Expand all
1331 1306
1332 int ProxyService::DidFinishResolvingProxy(ProxyInfo* result, 1307 int ProxyService::DidFinishResolvingProxy(ProxyInfo* result,
1333 int result_code, 1308 int result_code,
1334 const BoundNetLog& net_log) { 1309 const BoundNetLog& net_log) {
1335 // Log the result of the proxy resolution. 1310 // Log the result of the proxy resolution.
1336 if (result_code == OK) { 1311 if (result_code == OK) {
1337 // When logging all events is enabled, dump the proxy list. 1312 // When logging all events is enabled, dump the proxy list.
1338 if (net_log.IsLoggingAllEvents()) { 1313 if (net_log.IsLoggingAllEvents()) {
1339 net_log.AddEvent( 1314 net_log.AddEvent(
1340 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, 1315 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST,
1341 make_scoped_refptr(new NetLogStringParameter( 1316 base::Bind(&NetLogFinishedResolvingProxyCallback, result));
1342 "pac_string", result->ToPacString())));
1343 } 1317 }
1344 result->DeprioritizeBadProxies(proxy_retry_info_); 1318 result->DeprioritizeBadProxies(proxy_retry_info_);
1345 } else { 1319 } else {
1346 net_log.AddEvent( 1320 net_log.AddEventWithNetErrorCode(
1347 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, 1321 NetLog::TYPE_PROXY_SERVICE_RESOLVED_PROXY_LIST, result_code);
1348 make_scoped_refptr(new NetLogIntegerParameter(
1349 "net_error", result_code)));
1350 1322
1351 if (!config_.pac_mandatory()) { 1323 if (!config_.pac_mandatory()) {
1352 // Fall-back to direct when the proxy resolver fails. This corresponds 1324 // Fall-back to direct when the proxy resolver fails. This corresponds
1353 // with a javascript runtime error in the PAC script. 1325 // with a javascript runtime error in the PAC script.
1354 // 1326 //
1355 // This implicit fall-back to direct matches Firefox 3.5 and 1327 // This implicit fall-back to direct matches Firefox 3.5 and
1356 // Internet Explorer 8. For more information, see: 1328 // Internet Explorer 8. For more information, see:
1357 // 1329 //
1358 // http://www.chromium.org/developers/design-documents/proxy-settings-fall back 1330 // http://www.chromium.org/developers/design-documents/proxy-settings-fall back
1359 result->UseDirect(); 1331 result->UseDirect();
1360 result_code = OK; 1332 result_code = OK;
1361 } else { 1333 } else {
1362 result_code = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED; 1334 result_code = ERR_MANDATORY_PROXY_CONFIGURATION_FAILED;
1363 } 1335 }
1364 } 1336 }
1365 1337
1366 net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE, NULL); 1338 net_log.EndEvent(NetLog::TYPE_PROXY_SERVICE);
1367 return result_code; 1339 return result_code;
1368 } 1340 }
1369 1341
1370 void ProxyService::SetProxyScriptFetchers( 1342 void ProxyService::SetProxyScriptFetchers(
1371 ProxyScriptFetcher* proxy_script_fetcher, 1343 ProxyScriptFetcher* proxy_script_fetcher,
1372 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher) { 1344 DhcpProxyScriptFetcher* dhcp_proxy_script_fetcher) {
1373 DCHECK(CalledOnValidThread()); 1345 DCHECK(CalledOnValidThread());
1374 State previous_state = ResetProxyConfig(false); 1346 State previous_state = ResetProxyConfig(false);
1375 proxy_script_fetcher_.reset(proxy_script_fetcher); 1347 proxy_script_fetcher_.reset(proxy_script_fetcher);
1376 dhcp_proxy_script_fetcher_.reset(dhcp_proxy_script_fetcher); 1348 dhcp_proxy_script_fetcher_.reset(dhcp_proxy_script_fetcher);
(...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after
1502 case ProxyConfigService::CONFIG_VALID: 1474 case ProxyConfigService::CONFIG_VALID:
1503 effective_config = config; 1475 effective_config = config;
1504 break; 1476 break;
1505 case ProxyConfigService::CONFIG_UNSET: 1477 case ProxyConfigService::CONFIG_UNSET:
1506 effective_config = ProxyConfig::CreateDirect(); 1478 effective_config = ProxyConfig::CreateDirect();
1507 break; 1479 break;
1508 } 1480 }
1509 1481
1510 // Emit the proxy settings change to the NetLog stream. 1482 // Emit the proxy settings change to the NetLog stream.
1511 if (net_log_) { 1483 if (net_log_) {
1512 scoped_refptr<NetLog::EventParameters> params( 1484 net_log_->AddGlobalEntry(
1513 new ProxyConfigChangedNetLogParam(fetched_config_, effective_config)); 1485 net::NetLog::TYPE_PROXY_CONFIG_CHANGED,
1514 net_log_->AddGlobalEntry(net::NetLog::TYPE_PROXY_CONFIG_CHANGED, 1486 base::Bind(&NetLogProxyConfigChangedCallback,
1515 params); 1487 &fetched_config_, &effective_config));
1516 } 1488 }
1517 1489
1518 // Set the new configuration as the most recently fetched one. 1490 // Set the new configuration as the most recently fetched one.
1519 fetched_config_ = effective_config; 1491 fetched_config_ = effective_config;
1520 fetched_config_.set_id(1); // Needed for a later DCHECK of is_valid(). 1492 fetched_config_.set_id(1); // Needed for a later DCHECK of is_valid().
1521 1493
1522 InitializeUsingLastFetchedConfig(); 1494 InitializeUsingLastFetchedConfig();
1523 } 1495 }
1524 1496
1525 void ProxyService::InitializeUsingLastFetchedConfig() { 1497 void ProxyService::InitializeUsingLastFetchedConfig() {
(...skipping 131 matching lines...) Expand 10 before | Expand all | Expand 10 after
1657 OnCompletion(result_); 1629 OnCompletion(result_);
1658 } 1630 }
1659 } 1631 }
1660 1632
1661 void SyncProxyServiceHelper::OnCompletion(int rv) { 1633 void SyncProxyServiceHelper::OnCompletion(int rv) {
1662 result_ = rv; 1634 result_ = rv;
1663 event_.Signal(); 1635 event_.Signal();
1664 } 1636 }
1665 1637
1666 } // namespace net 1638 } // namespace net
OLDNEW
« no previous file with comments | « net/proxy/proxy_script_decider.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698