Windows should animate when they are about to get docked at screen edges.
[chromium-blink-merge.git] / net / proxy / dhcp_proxy_script_fetcher_win.cc
blob9e34f5122e46752ac97c9ab55a431488d182ac22
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
3 // found in the LICENSE file.
5 #include "net/proxy/dhcp_proxy_script_fetcher_win.h"
7 #include "base/bind.h"
8 #include "base/bind_helpers.h"
9 #include "base/metrics/histogram.h"
10 #include "base/perftimer.h"
11 #include "base/threading/worker_pool.h"
12 #include "net/base/net_errors.h"
13 #include "net/proxy/dhcp_proxy_script_adapter_fetcher_win.h"
15 #include <winsock2.h>
16 #include <iphlpapi.h>
17 #pragma comment(lib, "iphlpapi.lib")
19 namespace {
21 // How long to wait at maximum after we get results (a PAC file or
22 // knowledge that no PAC file is configured) from whichever network
23 // adapter finishes first.
24 const int kMaxWaitAfterFirstResultMs = 400;
26 const int kGetAdaptersAddressesErrors[] = {
27 ERROR_ADDRESS_NOT_ASSOCIATED,
28 ERROR_BUFFER_OVERFLOW,
29 ERROR_INVALID_PARAMETER,
30 ERROR_NOT_ENOUGH_MEMORY,
31 ERROR_NO_DATA,
34 } // namespace
36 namespace net {
38 DhcpProxyScriptFetcherWin::DhcpProxyScriptFetcherWin(
39 URLRequestContext* url_request_context)
40 : state_(STATE_START),
41 num_pending_fetchers_(0),
42 destination_string_(NULL),
43 url_request_context_(url_request_context) {
44 DCHECK(url_request_context_);
47 DhcpProxyScriptFetcherWin::~DhcpProxyScriptFetcherWin() {
48 // Count as user-initiated if we are not yet in STATE_DONE.
49 Cancel();
52 int DhcpProxyScriptFetcherWin::Fetch(base::string16* utf16_text,
53 const CompletionCallback& callback) {
54 DCHECK(CalledOnValidThread());
55 if (state_ != STATE_START && state_ != STATE_DONE) {
56 NOTREACHED();
57 return ERR_UNEXPECTED;
60 fetch_start_time_ = base::TimeTicks::Now();
62 state_ = STATE_WAIT_ADAPTERS;
63 callback_ = callback;
64 destination_string_ = utf16_text;
66 last_query_ = ImplCreateAdapterQuery();
67 base::WorkerPool::PostTaskAndReply(
68 FROM_HERE,
69 base::Bind(
70 &DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames,
71 last_query_.get()),
72 base::Bind(
73 &DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone,
74 AsWeakPtr(),
75 last_query_),
76 true);
78 return ERR_IO_PENDING;
81 void DhcpProxyScriptFetcherWin::Cancel() {
82 DCHECK(CalledOnValidThread());
84 if (state_ != STATE_DONE) {
85 // We only count this stat if the cancel was explicitly initiated by
86 // our client, and if we weren't already in STATE_DONE.
87 UMA_HISTOGRAM_TIMES("Net.DhcpWpadCancelTime",
88 base::TimeTicks::Now() - fetch_start_time_);
91 CancelImpl();
94 void DhcpProxyScriptFetcherWin::CancelImpl() {
95 DCHECK(CalledOnValidThread());
97 if (state_ != STATE_DONE) {
98 callback_.Reset();
99 wait_timer_.Stop();
100 state_ = STATE_DONE;
102 for (FetcherVector::iterator it = fetchers_.begin();
103 it != fetchers_.end();
104 ++it) {
105 (*it)->Cancel();
108 fetchers_.clear();
112 void DhcpProxyScriptFetcherWin::OnGetCandidateAdapterNamesDone(
113 scoped_refptr<AdapterQuery> query) {
114 DCHECK(CalledOnValidThread());
116 // This can happen if this object is reused for multiple queries,
117 // and a previous query was cancelled before it completed.
118 if (query.get() != last_query_.get())
119 return;
120 last_query_ = NULL;
122 // Enable unit tests to wait for this to happen; in production this function
123 // call is a no-op.
124 ImplOnGetCandidateAdapterNamesDone();
126 // We may have been cancelled.
127 if (state_ != STATE_WAIT_ADAPTERS)
128 return;
130 state_ = STATE_NO_RESULTS;
132 const std::set<std::string>& adapter_names = query->adapter_names();
134 if (adapter_names.empty()) {
135 TransitionToDone();
136 return;
139 for (std::set<std::string>::const_iterator it = adapter_names.begin();
140 it != adapter_names.end();
141 ++it) {
142 DhcpProxyScriptAdapterFetcher* fetcher(ImplCreateAdapterFetcher());
143 fetcher->Fetch(
144 *it, base::Bind(&DhcpProxyScriptFetcherWin::OnFetcherDone,
145 base::Unretained(this)));
146 fetchers_.push_back(fetcher);
148 num_pending_fetchers_ = fetchers_.size();
151 std::string DhcpProxyScriptFetcherWin::GetFetcherName() const {
152 DCHECK(CalledOnValidThread());
153 return "win";
156 const GURL& DhcpProxyScriptFetcherWin::GetPacURL() const {
157 DCHECK(CalledOnValidThread());
158 DCHECK_EQ(state_, STATE_DONE);
160 return pac_url_;
163 void DhcpProxyScriptFetcherWin::OnFetcherDone(int result) {
164 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
166 if (--num_pending_fetchers_ == 0) {
167 TransitionToDone();
168 return;
171 // If the only pending adapters are those less preferred than one
172 // with a valid PAC script, we do not need to wait any longer.
173 for (FetcherVector::iterator it = fetchers_.begin();
174 it != fetchers_.end();
175 ++it) {
176 bool did_finish = (*it)->DidFinish();
177 int result = (*it)->GetResult();
178 if (did_finish && result == OK) {
179 TransitionToDone();
180 return;
182 if (!did_finish || result != ERR_PAC_NOT_IN_DHCP) {
183 break;
187 // Once we have a single result, we set a maximum on how long to wait
188 // for the rest of the results.
189 if (state_ == STATE_NO_RESULTS) {
190 state_ = STATE_SOME_RESULTS;
191 wait_timer_.Start(FROM_HERE,
192 ImplGetMaxWait(), this, &DhcpProxyScriptFetcherWin::OnWaitTimer);
196 void DhcpProxyScriptFetcherWin::OnWaitTimer() {
197 DCHECK_EQ(state_, STATE_SOME_RESULTS);
199 // These are intended to help us understand whether our timeout may
200 // be too aggressive or not aggressive enough.
201 UMA_HISTOGRAM_COUNTS_100("Net.DhcpWpadNumAdaptersAtWaitTimer",
202 fetchers_.size());
203 UMA_HISTOGRAM_COUNTS_100("Net.DhcpWpadNumPendingAdaptersAtWaitTimer",
204 num_pending_fetchers_);
206 TransitionToDone();
209 void DhcpProxyScriptFetcherWin::TransitionToDone() {
210 DCHECK(state_ == STATE_NO_RESULTS || state_ == STATE_SOME_RESULTS);
212 int result = ERR_PAC_NOT_IN_DHCP; // Default if no fetchers.
213 if (!fetchers_.empty()) {
214 // Scan twice for the result; once through the whole list for success,
215 // then if no success, return result for most preferred network adapter,
216 // preferring "real" network errors to the ERR_PAC_NOT_IN_DHCP error.
217 // Default to ERR_ABORTED if no fetcher completed.
218 result = ERR_ABORTED;
219 for (FetcherVector::iterator it = fetchers_.begin();
220 it != fetchers_.end();
221 ++it) {
222 if ((*it)->DidFinish() && (*it)->GetResult() == OK) {
223 result = OK;
224 *destination_string_ = (*it)->GetPacScript();
225 pac_url_ = (*it)->GetPacURL();
226 break;
229 if (result != OK) {
230 destination_string_->clear();
231 for (FetcherVector::iterator it = fetchers_.begin();
232 it != fetchers_.end();
233 ++it) {
234 if ((*it)->DidFinish()) {
235 result = (*it)->GetResult();
236 if (result != ERR_PAC_NOT_IN_DHCP) {
237 break;
244 CompletionCallback callback = callback_;
245 CancelImpl();
246 DCHECK_EQ(state_, STATE_DONE);
247 DCHECK(fetchers_.empty());
248 DCHECK(callback_.is_null()); // Invariant of data.
250 UMA_HISTOGRAM_TIMES("Net.DhcpWpadCompletionTime",
251 base::TimeTicks::Now() - fetch_start_time_);
253 if (result != OK) {
254 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
255 "Net.DhcpWpadFetchError", std::abs(result), GetAllErrorCodesForUma());
258 // We may be deleted re-entrantly within this outcall.
259 callback.Run(result);
262 int DhcpProxyScriptFetcherWin::num_pending_fetchers() const {
263 return num_pending_fetchers_;
266 URLRequestContext* DhcpProxyScriptFetcherWin::url_request_context() const {
267 return url_request_context_;
270 DhcpProxyScriptAdapterFetcher*
271 DhcpProxyScriptFetcherWin::ImplCreateAdapterFetcher() {
272 return new DhcpProxyScriptAdapterFetcher(url_request_context_);
275 DhcpProxyScriptFetcherWin::AdapterQuery*
276 DhcpProxyScriptFetcherWin::ImplCreateAdapterQuery() {
277 return new AdapterQuery();
280 base::TimeDelta DhcpProxyScriptFetcherWin::ImplGetMaxWait() {
281 return base::TimeDelta::FromMilliseconds(kMaxWaitAfterFirstResultMs);
284 bool DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(
285 std::set<std::string>* adapter_names) {
286 DCHECK(adapter_names);
287 adapter_names->clear();
289 // The GetAdaptersAddresses MSDN page recommends using a size of 15000 to
290 // avoid reallocation.
291 ULONG adapters_size = 15000;
292 scoped_ptr_malloc<IP_ADAPTER_ADDRESSES> adapters;
293 ULONG error = ERROR_SUCCESS;
294 int num_tries = 0;
296 PerfTimer time_api_access;
297 do {
298 adapters.reset(
299 reinterpret_cast<IP_ADAPTER_ADDRESSES*>(malloc(adapters_size)));
300 // Return only unicast addresses, and skip information we do not need.
301 error = GetAdaptersAddresses(AF_UNSPEC,
302 GAA_FLAG_SKIP_ANYCAST |
303 GAA_FLAG_SKIP_MULTICAST |
304 GAA_FLAG_SKIP_DNS_SERVER |
305 GAA_FLAG_SKIP_FRIENDLY_NAME,
306 NULL,
307 adapters.get(),
308 &adapters_size);
309 ++num_tries;
310 } while (error == ERROR_BUFFER_OVERFLOW && num_tries <= 3);
312 // This is primarily to validate our belief that the GetAdaptersAddresses API
313 // function is fast enough to call synchronously from the network thread.
314 UMA_HISTOGRAM_TIMES("Net.DhcpWpadGetAdaptersAddressesTime",
315 time_api_access.Elapsed());
317 if (error != ERROR_SUCCESS) {
318 UMA_HISTOGRAM_CUSTOM_ENUMERATION(
319 "Net.DhcpWpadGetAdaptersAddressesError",
320 error,
321 base::CustomHistogram::ArrayToCustomRanges(
322 kGetAdaptersAddressesErrors,
323 arraysize(kGetAdaptersAddressesErrors)));
326 if (error == ERROR_NO_DATA) {
327 // There are no adapters that we care about.
328 return true;
331 if (error != ERROR_SUCCESS) {
332 LOG(WARNING) << "Unexpected error retrieving WPAD configuration from DHCP.";
333 return false;
336 IP_ADAPTER_ADDRESSES* adapter = NULL;
337 for (adapter = adapters.get(); adapter; adapter = adapter->Next) {
338 if (adapter->IfType == IF_TYPE_SOFTWARE_LOOPBACK)
339 continue;
340 if ((adapter->Flags & IP_ADAPTER_DHCP_ENABLED) == 0)
341 continue;
343 DCHECK(adapter->AdapterName);
344 adapter_names->insert(adapter->AdapterName);
347 return true;
350 DhcpProxyScriptFetcherWin::AdapterQuery::AdapterQuery() {
353 DhcpProxyScriptFetcherWin::AdapterQuery::~AdapterQuery() {
356 void DhcpProxyScriptFetcherWin::AdapterQuery::GetCandidateAdapterNames() {
357 ImplGetCandidateAdapterNames(&adapter_names_);
360 const std::set<std::string>&
361 DhcpProxyScriptFetcherWin::AdapterQuery::adapter_names() const {
362 return adapter_names_;
365 bool DhcpProxyScriptFetcherWin::AdapterQuery::ImplGetCandidateAdapterNames(
366 std::set<std::string>* adapter_names) {
367 return DhcpProxyScriptFetcherWin::GetCandidateAdapterNames(adapter_names);
370 } // namespace net