4918dfc854b8a72599163ee4e0f610fe099e1b58
[platform/framework/web/crosswalk.git] / src / chrome / browser / extensions / updater / extension_downloader.cc
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.
4
5 #include "chrome/browser/extensions/updater/extension_downloader.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/command_line.h"
11 #include "base/files/file_path.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/metrics/histogram.h"
15 #include "base/metrics/sparse_histogram.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_util.h"
19 #include "base/strings/stringprintf.h"
20 #include "base/time/time.h"
21 #include "base/version.h"
22 #include "chrome/browser/chrome_notification_types.h"
23 #include "chrome/browser/extensions/updater/extension_cache.h"
24 #include "chrome/browser/extensions/updater/request_queue_impl.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/chrome_version_info.h"
27 #include "chrome/common/extensions/manifest_url_handler.h"
28 #include "content/public/browser/browser_thread.h"
29 #include "content/public/browser/notification_details.h"
30 #include "content/public/browser/notification_service.h"
31 #include "extensions/browser/updater/safe_manifest_parser.h"
32 #include "extensions/common/extension_urls.h"
33 #include "google_apis/gaia/identity_provider.h"
34 #include "net/base/backoff_entry.h"
35 #include "net/base/load_flags.h"
36 #include "net/base/net_errors.h"
37 #include "net/http/http_request_headers.h"
38 #include "net/http/http_status_code.h"
39 #include "net/url_request/url_fetcher.h"
40 #include "net/url_request/url_request_context_getter.h"
41 #include "net/url_request/url_request_status.h"
42
43 using base::Time;
44 using base::TimeDelta;
45 using content::BrowserThread;
46
47 namespace extensions {
48
49 const char ExtensionDownloader::kBlacklistAppID[] = "com.google.crx.blacklist";
50
51 namespace {
52
53 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
54   // Number of initial errors (in sequence) to ignore before applying
55   // exponential back-off rules.
56   0,
57
58   // Initial delay for exponential back-off in ms.
59   2000,
60
61   // Factor by which the waiting time will be multiplied.
62   2,
63
64   // Fuzzing percentage. ex: 10% will spread requests randomly
65   // between 90%-100% of the calculated time.
66   0.1,
67
68   // Maximum amount of time we are willing to delay our request in ms.
69   -1,
70
71   // Time to keep an entry from being discarded even when it
72   // has no significant state, -1 to never discard.
73   -1,
74
75   // Don't use initial delay unless the last request was an error.
76   false,
77 };
78
79 const char kAuthUserQueryKey[] = "authuser";
80
81 const int kMaxAuthUserValue = 10;
82 const int kMaxOAuth2Attempts = 3;
83
84 const char kNotFromWebstoreInstallSource[] = "notfromwebstore";
85 const char kDefaultInstallSource[] = "";
86
87 const char kGoogleDotCom[] = "google.com";
88 const char kTokenServiceConsumerId[] = "extension_downloader";
89 const char kWebstoreOAuth2Scope[] =
90     "https://www.googleapis.com/auth/chromewebstore.readonly";
91
92 #define RETRY_HISTOGRAM(name, retry_count, url)                           \
93   if ((url).DomainIs(kGoogleDotCom)) {                                    \
94     UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountGoogleUrl", \
95                                 retry_count,                              \
96                                 1,                                        \
97                                 kMaxRetries,                              \
98                                 kMaxRetries + 1);                         \
99   } else {                                                                \
100     UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountOtherUrl",  \
101                                 retry_count,                              \
102                                 1,                                        \
103                                 kMaxRetries,                              \
104                                 kMaxRetries + 1);                         \
105   }
106
107 bool ShouldRetryRequest(const net::URLRequestStatus& status,
108                         int response_code) {
109   // Retry if the response code is a server error, or the request failed because
110   // of network errors as opposed to file errors.
111   return ((response_code >= 500 && status.is_success()) ||
112           status.status() == net::URLRequestStatus::FAILED);
113 }
114
115 // This parses and updates a URL query such that the value of the |authuser|
116 // query parameter is incremented by 1. If parameter was not present in the URL,
117 // it will be added with a value of 1. All other query keys and values are
118 // preserved as-is. Returns |false| if the user index exceeds a hard-coded
119 // maximum.
120 bool IncrementAuthUserIndex(GURL* url) {
121   int user_index = 0;
122   std::string old_query = url->query();
123   std::vector<std::string> new_query_parts;
124   url::Component query(0, old_query.length());
125   url::Component key, value;
126   while (url::ExtractQueryKeyValue(old_query.c_str(), &query, &key, &value)) {
127     std::string key_string = old_query.substr(key.begin, key.len);
128     std::string value_string = old_query.substr(value.begin, value.len);
129     if (key_string == kAuthUserQueryKey) {
130       base::StringToInt(value_string, &user_index);
131     } else {
132       new_query_parts.push_back(base::StringPrintf(
133           "%s=%s", key_string.c_str(), value_string.c_str()));
134     }
135   }
136   if (user_index >= kMaxAuthUserValue)
137     return false;
138   new_query_parts.push_back(
139       base::StringPrintf("%s=%d", kAuthUserQueryKey, user_index + 1));
140   std::string new_query_string = JoinString(new_query_parts, '&');
141   url::Component new_query(0, new_query_string.size());
142   url::Replacements<char> replacements;
143   replacements.SetQuery(new_query_string.c_str(), new_query);
144   *url = url->ReplaceComponents(replacements);
145   return true;
146 }
147
148 }  // namespace
149
150 UpdateDetails::UpdateDetails(const std::string& id, const Version& version)
151     : id(id), version(version) {}
152
153 UpdateDetails::~UpdateDetails() {}
154
155 ExtensionDownloader::ExtensionFetch::ExtensionFetch()
156     : url(), credentials(CREDENTIALS_NONE) {
157 }
158
159 ExtensionDownloader::ExtensionFetch::ExtensionFetch(
160     const std::string& id,
161     const GURL& url,
162     const std::string& package_hash,
163     const std::string& version,
164     const std::set<int>& request_ids)
165     : id(id),
166       url(url),
167       package_hash(package_hash),
168       version(version),
169       request_ids(request_ids),
170       credentials(CREDENTIALS_NONE),
171       oauth2_attempt_count(0) {
172 }
173
174 ExtensionDownloader::ExtensionFetch::~ExtensionFetch() {}
175
176 ExtensionDownloader::ExtensionDownloader(
177     ExtensionDownloaderDelegate* delegate,
178     net::URLRequestContextGetter* request_context)
179     : OAuth2TokenService::Consumer(kTokenServiceConsumerId),
180       delegate_(delegate),
181       request_context_(request_context),
182       manifests_queue_(&kDefaultBackoffPolicy,
183                        base::Bind(&ExtensionDownloader::CreateManifestFetcher,
184                                   base::Unretained(this))),
185       extensions_queue_(&kDefaultBackoffPolicy,
186                         base::Bind(&ExtensionDownloader::CreateExtensionFetcher,
187                                    base::Unretained(this))),
188       extension_cache_(NULL),
189       enable_extra_update_metrics_(false),
190       weak_ptr_factory_(this) {
191   DCHECK(delegate_);
192   DCHECK(request_context_.get());
193 }
194
195 ExtensionDownloader::~ExtensionDownloader() {}
196
197 bool ExtensionDownloader::AddExtension(const Extension& extension,
198                                        int request_id) {
199   // Skip extensions with empty update URLs converted from user
200   // scripts.
201   if (extension.converted_from_user_script() &&
202       ManifestURL::GetUpdateURL(&extension).is_empty()) {
203     return false;
204   }
205
206   // If the extension updates itself from the gallery, ignore any update URL
207   // data.  At the moment there is no extra data that an extension can
208   // communicate to the the gallery update servers.
209   std::string update_url_data;
210   if (!ManifestURL::UpdatesFromGallery(&extension))
211     update_url_data = delegate_->GetUpdateUrlData(extension.id());
212
213   std::string install_source;
214   bool force_update = delegate_->ShouldForceUpdate(extension.id(),
215                                                    &install_source);
216   return AddExtensionData(extension.id(),
217                           *extension.version(),
218                           extension.GetType(),
219                           ManifestURL::GetUpdateURL(&extension),
220                           update_url_data,
221                           request_id,
222                           force_update,
223                           install_source);
224 }
225
226 bool ExtensionDownloader::AddPendingExtension(const std::string& id,
227                                               const GURL& update_url,
228                                               int request_id) {
229   // Use a zero version to ensure that a pending extension will always
230   // be updated, and thus installed (assuming all extensions have
231   // non-zero versions).
232   Version version("0.0.0.0");
233   DCHECK(version.IsValid());
234
235   return AddExtensionData(id,
236                           version,
237                           Manifest::TYPE_UNKNOWN,
238                           update_url,
239                           std::string(),
240                           request_id,
241                           false,
242                           std::string());
243 }
244
245 void ExtensionDownloader::StartAllPending(ExtensionCache* cache) {
246   if (cache) {
247     extension_cache_ = cache;
248     extension_cache_->Start(base::Bind(
249         &ExtensionDownloader::DoStartAllPending,
250         weak_ptr_factory_.GetWeakPtr()));
251   } else {
252     DoStartAllPending();
253   }
254 }
255
256 void ExtensionDownloader::DoStartAllPending() {
257   ReportStats();
258   url_stats_ = URLStats();
259
260   for (FetchMap::iterator it = fetches_preparing_.begin();
261        it != fetches_preparing_.end(); ++it) {
262     std::vector<linked_ptr<ManifestFetchData> >& list = it->second;
263     for (size_t i = 0; i < list.size(); ++i) {
264       StartUpdateCheck(scoped_ptr<ManifestFetchData>(list[i].release()));
265     }
266   }
267   fetches_preparing_.clear();
268 }
269
270 void ExtensionDownloader::StartBlacklistUpdate(
271     const std::string& version,
272     const ManifestFetchData::PingData& ping_data,
273     int request_id) {
274   // Note: it is very important that we use the https version of the update
275   // url here to avoid DNS hijacking of the blacklist, which is not validated
276   // by a public key signature like .crx files are.
277   scoped_ptr<ManifestFetchData> blacklist_fetch(CreateManifestFetchData(
278       extension_urls::GetWebstoreUpdateUrl(), request_id));
279   DCHECK(blacklist_fetch->base_url().SchemeIsSecure());
280   blacklist_fetch->AddExtension(kBlacklistAppID,
281                                 version,
282                                 &ping_data,
283                                 std::string(),
284                                 kDefaultInstallSource,
285                                 false);
286   StartUpdateCheck(blacklist_fetch.Pass());
287 }
288
289 void ExtensionDownloader::SetWebstoreIdentityProvider(
290     scoped_ptr<IdentityProvider> identity_provider) {
291   identity_provider_.swap(identity_provider);
292 }
293
294 bool ExtensionDownloader::AddExtensionData(
295     const std::string& id,
296     const Version& version,
297     Manifest::Type extension_type,
298     const GURL& extension_update_url,
299     const std::string& update_url_data,
300     int request_id,
301     bool force_update,
302     const std::string& install_source_override) {
303   GURL update_url(extension_update_url);
304   // Skip extensions with non-empty invalid update URLs.
305   if (!update_url.is_empty() && !update_url.is_valid()) {
306     LOG(WARNING) << "Extension " << id << " has invalid update url "
307                  << update_url;
308     return false;
309   }
310
311   // Make sure we use SSL for store-hosted extensions.
312   if (extension_urls::IsWebstoreUpdateUrl(update_url) &&
313       !update_url.SchemeIsSecure())
314     update_url = extension_urls::GetWebstoreUpdateUrl();
315
316   // Skip extensions with empty IDs.
317   if (id.empty()) {
318     LOG(WARNING) << "Found extension with empty ID";
319     return false;
320   }
321
322   if (update_url.DomainIs(kGoogleDotCom)) {
323     url_stats_.google_url_count++;
324   } else if (update_url.is_empty()) {
325     url_stats_.no_url_count++;
326     // Fill in default update URL.
327     update_url = extension_urls::GetWebstoreUpdateUrl();
328   } else {
329     url_stats_.other_url_count++;
330   }
331
332   switch (extension_type) {
333     case Manifest::TYPE_THEME:
334       ++url_stats_.theme_count;
335       break;
336     case Manifest::TYPE_EXTENSION:
337     case Manifest::TYPE_USER_SCRIPT:
338       ++url_stats_.extension_count;
339       break;
340     case Manifest::TYPE_HOSTED_APP:
341     case Manifest::TYPE_LEGACY_PACKAGED_APP:
342       ++url_stats_.app_count;
343       break;
344     case Manifest::TYPE_PLATFORM_APP:
345       ++url_stats_.platform_app_count;
346       break;
347     case Manifest::TYPE_UNKNOWN:
348     default:
349       ++url_stats_.pending_count;
350       break;
351   }
352
353   std::vector<GURL> update_urls;
354   update_urls.push_back(update_url);
355   // If metrics are enabled, also add to ManifestFetchData for the
356   // webstore update URL.
357   if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
358       enable_extra_update_metrics_) {
359     update_urls.push_back(extension_urls::GetWebstoreUpdateUrl());
360   }
361
362   for (size_t i = 0; i < update_urls.size(); ++i) {
363     DCHECK(!update_urls[i].is_empty());
364     DCHECK(update_urls[i].is_valid());
365
366     std::string install_source = i == 0 ?
367         kDefaultInstallSource : kNotFromWebstoreInstallSource;
368     if (!install_source_override.empty()) {
369       install_source = install_source_override;
370     }
371
372     ManifestFetchData::PingData ping_data;
373     ManifestFetchData::PingData* optional_ping_data = NULL;
374     if (delegate_->GetPingDataForExtension(id, &ping_data))
375       optional_ping_data = &ping_data;
376
377     // Find or create a ManifestFetchData to add this extension to.
378     bool added = false;
379     FetchMap::iterator existing_iter = fetches_preparing_.find(
380         std::make_pair(request_id, update_urls[i]));
381     if (existing_iter != fetches_preparing_.end() &&
382         !existing_iter->second.empty()) {
383       // Try to add to the ManifestFetchData at the end of the list.
384       ManifestFetchData* existing_fetch = existing_iter->second.back().get();
385       if (existing_fetch->AddExtension(id, version.GetString(),
386                                        optional_ping_data, update_url_data,
387                                        install_source,
388                                        force_update)) {
389         added = true;
390       }
391     }
392     if (!added) {
393       // Otherwise add a new element to the list, if the list doesn't exist or
394       // if its last element is already full.
395       linked_ptr<ManifestFetchData> fetch(
396           CreateManifestFetchData(update_urls[i], request_id));
397       fetches_preparing_[std::make_pair(request_id, update_urls[i])].
398           push_back(fetch);
399       added = fetch->AddExtension(id, version.GetString(),
400                                   optional_ping_data,
401                                   update_url_data,
402                                   install_source,
403                                   force_update);
404       DCHECK(added);
405     }
406   }
407
408   return true;
409 }
410
411 void ExtensionDownloader::ReportStats() const {
412   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
413                            url_stats_.extension_count);
414   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
415                            url_stats_.theme_count);
416   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
417                            url_stats_.app_count);
418   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPackagedApp",
419                            url_stats_.platform_app_count);
420   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
421                            url_stats_.pending_count);
422   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
423                            url_stats_.google_url_count);
424   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
425                            url_stats_.other_url_count);
426   UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
427                            url_stats_.no_url_count);
428 }
429
430 void ExtensionDownloader::StartUpdateCheck(
431     scoped_ptr<ManifestFetchData> fetch_data) {
432   const std::set<std::string>& id_set(fetch_data->extension_ids());
433
434   if (CommandLine::ForCurrentProcess()->HasSwitch(
435       switches::kDisableBackgroundNetworking)) {
436     NotifyExtensionsDownloadFailed(id_set,
437                                    fetch_data->request_ids(),
438                                    ExtensionDownloaderDelegate::DISABLED);
439     return;
440   }
441
442   RequestQueue<ManifestFetchData>::iterator i;
443   for (i = manifests_queue_.begin(); i != manifests_queue_.end(); ++i) {
444     if (fetch_data->full_url() == i->full_url()) {
445       // This url is already scheduled to be fetched.
446       i->Merge(*fetch_data);
447       return;
448     }
449   }
450
451   if (manifests_queue_.active_request() &&
452       manifests_queue_.active_request()->full_url() == fetch_data->full_url()) {
453     manifests_queue_.active_request()->Merge(*fetch_data);
454   } else {
455     UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
456         fetch_data->full_url().possibly_invalid_spec().length());
457
458     manifests_queue_.ScheduleRequest(fetch_data.Pass());
459   }
460 }
461
462 void ExtensionDownloader::CreateManifestFetcher() {
463   if (VLOG_IS_ON(2)) {
464     std::vector<std::string> id_vector(
465         manifests_queue_.active_request()->extension_ids().begin(),
466         manifests_queue_.active_request()->extension_ids().end());
467     std::string id_list = JoinString(id_vector, ',');
468     VLOG(2) << "Fetching " << manifests_queue_.active_request()->full_url()
469             << " for " << id_list;
470   }
471
472   manifest_fetcher_.reset(net::URLFetcher::Create(
473       kManifestFetcherId, manifests_queue_.active_request()->full_url(),
474       net::URLFetcher::GET, this));
475   manifest_fetcher_->SetRequestContext(request_context_.get());
476   manifest_fetcher_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
477                                   net::LOAD_DO_NOT_SAVE_COOKIES |
478                                   net::LOAD_DISABLE_CACHE);
479   // Update checks can be interrupted if a network change is detected; this is
480   // common for the retail mode AppPack on ChromeOS. Retrying once should be
481   // enough to recover in those cases; let the fetcher retry up to 3 times
482   // just in case. http://crosbug.com/130602
483   manifest_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
484   manifest_fetcher_->Start();
485 }
486
487 void ExtensionDownloader::OnURLFetchComplete(
488     const net::URLFetcher* source) {
489   VLOG(2) << source->GetResponseCode() << " " << source->GetURL();
490
491   if (source == manifest_fetcher_.get()) {
492     std::string data;
493     source->GetResponseAsString(&data);
494     OnManifestFetchComplete(source->GetURL(),
495                             source->GetStatus(),
496                             source->GetResponseCode(),
497                             source->GetBackoffDelay(),
498                             data);
499   } else if (source == extension_fetcher_.get()) {
500     OnCRXFetchComplete(source,
501                        source->GetURL(),
502                        source->GetStatus(),
503                        source->GetResponseCode(),
504                        source->GetBackoffDelay());
505   } else {
506     NOTREACHED();
507   }
508 }
509
510 void ExtensionDownloader::OnManifestFetchComplete(
511     const GURL& url,
512     const net::URLRequestStatus& status,
513     int response_code,
514     const base::TimeDelta& backoff_delay,
515     const std::string& data) {
516   // We want to try parsing the manifest, and if it indicates updates are
517   // available, we want to fire off requests to fetch those updates.
518   if (status.status() == net::URLRequestStatus::SUCCESS &&
519       (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
520     RETRY_HISTOGRAM("ManifestFetchSuccess",
521                     manifests_queue_.active_request_failure_count(), url);
522     VLOG(2) << "beginning manifest parse for " << url;
523     scoped_refptr<SafeManifestParser> safe_parser(
524         new SafeManifestParser(
525             data,
526             manifests_queue_.reset_active_request().release(),
527             base::Bind(&ExtensionDownloader::HandleManifestResults,
528                        weak_ptr_factory_.GetWeakPtr())));
529     safe_parser->Start();
530   } else {
531     VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
532             << "' response code:" << response_code;
533     if (ShouldRetryRequest(status, response_code) &&
534         manifests_queue_.active_request_failure_count() < kMaxRetries) {
535       manifests_queue_.RetryRequest(backoff_delay);
536     } else {
537       RETRY_HISTOGRAM("ManifestFetchFailure",
538                       manifests_queue_.active_request_failure_count(), url);
539       NotifyExtensionsDownloadFailed(
540           manifests_queue_.active_request()->extension_ids(),
541           manifests_queue_.active_request()->request_ids(),
542           ExtensionDownloaderDelegate::MANIFEST_FETCH_FAILED);
543     }
544   }
545   manifest_fetcher_.reset();
546   manifests_queue_.reset_active_request();
547
548   // If we have any pending manifest requests, fire off the next one.
549   manifests_queue_.StartNextRequest();
550 }
551
552 void ExtensionDownloader::HandleManifestResults(
553     const ManifestFetchData& fetch_data,
554     const UpdateManifest::Results* results) {
555   // Keep a list of extensions that will not be updated, so that the |delegate_|
556   // can be notified once we're done here.
557   std::set<std::string> not_updated(fetch_data.extension_ids());
558
559   if (!results) {
560     NotifyExtensionsDownloadFailed(
561         not_updated,
562         fetch_data.request_ids(),
563         ExtensionDownloaderDelegate::MANIFEST_INVALID);
564     return;
565   }
566
567   // Examine the parsed manifest and kick off fetches of any new crx files.
568   std::vector<int> updates;
569   DetermineUpdates(fetch_data, *results, &updates);
570   for (size_t i = 0; i < updates.size(); i++) {
571     const UpdateManifest::Result* update = &(results->list.at(updates[i]));
572     const std::string& id = update->extension_id;
573     not_updated.erase(id);
574
575     GURL crx_url = update->crx_url;
576     if (id != kBlacklistAppID) {
577       NotifyUpdateFound(update->extension_id, update->version);
578     } else {
579       // The URL of the blacklist file is returned by the server and we need to
580       // be sure that we continue to be able to reliably detect whether a URL
581       // references a blacklist file.
582       DCHECK(extension_urls::IsBlacklistUpdateUrl(crx_url)) << crx_url;
583
584       // Force https (crbug.com/129587).
585       if (!crx_url.SchemeIsSecure()) {
586         url::Replacements<char> replacements;
587         std::string scheme("https");
588         replacements.SetScheme(scheme.c_str(),
589                                url::Component(0, scheme.size()));
590         crx_url = crx_url.ReplaceComponents(replacements);
591       }
592     }
593     scoped_ptr<ExtensionFetch> fetch(new ExtensionFetch(
594         update->extension_id, crx_url, update->package_hash,
595         update->version, fetch_data.request_ids()));
596     FetchUpdatedExtension(fetch.Pass());
597   }
598
599   // If the manifest response included a <daystart> element, we want to save
600   // that value for any extensions which had sent a ping in the request.
601   if (fetch_data.base_url().DomainIs(kGoogleDotCom) &&
602       results->daystart_elapsed_seconds >= 0) {
603     Time day_start =
604         Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
605
606     const std::set<std::string>& extension_ids = fetch_data.extension_ids();
607     std::set<std::string>::const_iterator i;
608     for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
609       const std::string& id = *i;
610       ExtensionDownloaderDelegate::PingResult& result = ping_results_[id];
611       result.did_ping = fetch_data.DidPing(id, ManifestFetchData::ROLLCALL);
612       result.day_start = day_start;
613     }
614   }
615
616   NotifyExtensionsDownloadFailed(
617       not_updated,
618       fetch_data.request_ids(),
619       ExtensionDownloaderDelegate::NO_UPDATE_AVAILABLE);
620 }
621
622 void ExtensionDownloader::DetermineUpdates(
623     const ManifestFetchData& fetch_data,
624     const UpdateManifest::Results& possible_updates,
625     std::vector<int>* result) {
626   // This will only be valid if one of possible_updates specifies
627   // browser_min_version.
628   Version browser_version;
629
630   for (size_t i = 0; i < possible_updates.list.size(); i++) {
631     const UpdateManifest::Result* update = &possible_updates.list[i];
632     const std::string& id = update->extension_id;
633
634     if (!fetch_data.Includes(id)) {
635       VLOG(2) << "Ignoring " << id << " from this manifest";
636       continue;
637     }
638
639     if (VLOG_IS_ON(2)) {
640       if (update->version.empty())
641         VLOG(2) << "manifest indicates " << id << " has no update";
642       else
643         VLOG(2) << "manifest indicates " << id
644                 << " latest version is '" << update->version << "'";
645     }
646
647     if (!delegate_->IsExtensionPending(id)) {
648       // If we're not installing pending extension, and the update
649       // version is the same or older than what's already installed,
650       // we don't want it.
651       std::string version;
652       if (!delegate_->GetExtensionExistingVersion(id, &version)) {
653         VLOG(2) << id << " is not installed";
654         continue;
655       }
656
657       VLOG(2) << id << " is at '" << version << "'";
658
659       // We should skip the version check if update was forced.
660       if (!fetch_data.DidForceUpdate(id)) {
661         Version existing_version(version);
662         Version update_version(update->version);
663         if (!update_version.IsValid() ||
664             update_version.CompareTo(existing_version) <= 0) {
665           continue;
666         }
667       }
668     }
669
670     // If the update specifies a browser minimum version, do we qualify?
671     if (update->browser_min_version.length() > 0) {
672       // First determine the browser version if we haven't already.
673       if (!browser_version.IsValid()) {
674         chrome::VersionInfo version_info;
675         if (version_info.is_valid())
676           browser_version = Version(version_info.Version());
677       }
678       Version browser_min_version(update->browser_min_version);
679       if (browser_version.IsValid() && browser_min_version.IsValid() &&
680           browser_min_version.CompareTo(browser_version) > 0) {
681         // TODO(asargent) - We may want this to show up in the extensions UI
682         // eventually. (http://crbug.com/12547).
683         LOG(WARNING) << "Updated version of extension " << id
684                      << " available, but requires chrome version "
685                      << update->browser_min_version;
686         continue;
687       }
688     }
689     VLOG(2) << "will try to update " << id;
690     result->push_back(i);
691   }
692 }
693
694   // Begins (or queues up) download of an updated extension.
695 void ExtensionDownloader::FetchUpdatedExtension(
696     scoped_ptr<ExtensionFetch> fetch_data) {
697   if (!fetch_data->url.is_valid()) {
698     // TODO(asargent): This can sometimes be invalid. See crbug.com/130881.
699     LOG(ERROR) << "Invalid URL: '" << fetch_data->url.possibly_invalid_spec()
700                << "' for extension " << fetch_data->id;
701     return;
702   }
703
704   for (RequestQueue<ExtensionFetch>::iterator iter =
705            extensions_queue_.begin();
706        iter != extensions_queue_.end(); ++iter) {
707     if (iter->id == fetch_data->id || iter->url == fetch_data->url) {
708       iter->request_ids.insert(fetch_data->request_ids.begin(),
709                                fetch_data->request_ids.end());
710       return;  // already scheduled
711     }
712   }
713
714   if (extensions_queue_.active_request() &&
715       extensions_queue_.active_request()->url == fetch_data->url) {
716     extensions_queue_.active_request()->request_ids.insert(
717         fetch_data->request_ids.begin(), fetch_data->request_ids.end());
718   } else {
719     std::string version;
720     if (extension_cache_ &&
721         extension_cache_->GetExtension(fetch_data->id, NULL, &version) &&
722         version == fetch_data->version) {
723       base::FilePath crx_path;
724       // Now get .crx file path and mark extension as used.
725       extension_cache_->GetExtension(fetch_data->id, &crx_path, &version);
726       NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, false);
727     } else {
728       extensions_queue_.ScheduleRequest(fetch_data.Pass());
729     }
730   }
731 }
732
733 void ExtensionDownloader::NotifyDelegateDownloadFinished(
734     scoped_ptr<ExtensionFetch> fetch_data,
735     const base::FilePath& crx_path,
736     bool file_ownership_passed) {
737   delegate_->OnExtensionDownloadFinished(fetch_data->id, crx_path,
738       file_ownership_passed, fetch_data->url, fetch_data->version,
739       ping_results_[fetch_data->id], fetch_data->request_ids);
740   ping_results_.erase(fetch_data->id);
741 }
742
743 void ExtensionDownloader::CreateExtensionFetcher() {
744   const ExtensionFetch* fetch = extensions_queue_.active_request();
745   extension_fetcher_.reset(net::URLFetcher::Create(
746       kExtensionFetcherId, fetch->url, net::URLFetcher::GET, this));
747   extension_fetcher_->SetRequestContext(request_context_.get());
748   extension_fetcher_->SetAutomaticallyRetryOnNetworkChanges(3);
749
750   int load_flags = net::LOAD_DISABLE_CACHE;
751   bool is_secure = fetch->url.SchemeIsSecure();
752   if (fetch->credentials != ExtensionFetch::CREDENTIALS_COOKIES || !is_secure) {
753       load_flags |= net::LOAD_DO_NOT_SEND_COOKIES |
754                     net::LOAD_DO_NOT_SAVE_COOKIES;
755   }
756   extension_fetcher_->SetLoadFlags(load_flags);
757
758   // Download CRX files to a temp file. The blacklist is small and will be
759   // processed in memory, so it is fetched into a string.
760   if (fetch->id != kBlacklistAppID) {
761     extension_fetcher_->SaveResponseToTemporaryFile(
762         BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE));
763   }
764
765   if (fetch->credentials == ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN &&
766       is_secure) {
767     if (access_token_.empty()) {
768       // We should try OAuth2, but we have no token cached. This
769       // ExtensionFetcher will be started once the token fetch is complete,
770       // in either OnTokenFetchSuccess or OnTokenFetchFailure.
771       DCHECK(identity_provider_.get());
772       OAuth2TokenService::ScopeSet webstore_scopes;
773       webstore_scopes.insert(kWebstoreOAuth2Scope);
774       access_token_request_ =
775           identity_provider_->GetTokenService()->StartRequest(
776               identity_provider_->GetActiveAccountId(),
777               webstore_scopes,
778               this);
779       return;
780     }
781     extension_fetcher_->AddExtraRequestHeader(
782         base::StringPrintf("%s: Bearer %s",
783             net::HttpRequestHeaders::kAuthorization,
784             access_token_.c_str()));
785   }
786
787   VLOG(2) << "Starting fetch of " << fetch->url << " for " << fetch->id;
788   extension_fetcher_->Start();
789 }
790
791 void ExtensionDownloader::OnCRXFetchComplete(
792     const net::URLFetcher* source,
793     const GURL& url,
794     const net::URLRequestStatus& status,
795     int response_code,
796     const base::TimeDelta& backoff_delay) {
797   ExtensionFetch& active_request = *extensions_queue_.active_request();
798   const std::string& id = active_request.id;
799   if (status.status() == net::URLRequestStatus::SUCCESS &&
800       (response_code == 200 || url.SchemeIsFile())) {
801     RETRY_HISTOGRAM("CrxFetchSuccess",
802                     extensions_queue_.active_request_failure_count(), url);
803     base::FilePath crx_path;
804     // Take ownership of the file at |crx_path|.
805     CHECK(source->GetResponseAsFilePath(true, &crx_path));
806     scoped_ptr<ExtensionFetch> fetch_data =
807         extensions_queue_.reset_active_request();
808     if (extension_cache_) {
809       const std::string& version = fetch_data->version;
810       extension_cache_->PutExtension(id, crx_path, version,
811           base::Bind(&ExtensionDownloader::NotifyDelegateDownloadFinished,
812                      weak_ptr_factory_.GetWeakPtr(),
813                      base::Passed(&fetch_data)));
814     } else {
815       NotifyDelegateDownloadFinished(fetch_data.Pass(), crx_path, true);
816     }
817   } else if (IterateFetchCredentialsAfterFailure(
818                 &active_request,
819                 status,
820                 response_code)) {
821     extensions_queue_.RetryRequest(backoff_delay);
822   } else {
823     const std::set<int>& request_ids = active_request.request_ids;
824     const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[id];
825     VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
826             << "' response code:" << response_code;
827     if (ShouldRetryRequest(status, response_code) &&
828         extensions_queue_.active_request_failure_count() < kMaxRetries) {
829       extensions_queue_.RetryRequest(backoff_delay);
830     } else {
831       RETRY_HISTOGRAM("CrxFetchFailure",
832                       extensions_queue_.active_request_failure_count(), url);
833       // status.error() is 0 (net::OK) or negative. (See net/base/net_errors.h)
834       UMA_HISTOGRAM_SPARSE_SLOWLY("Extensions.CrxFetchError", -status.error());
835       delegate_->OnExtensionDownloadFailed(
836           id, ExtensionDownloaderDelegate::CRX_FETCH_FAILED, ping, request_ids);
837     }
838     ping_results_.erase(id);
839     extensions_queue_.reset_active_request();
840   }
841
842   extension_fetcher_.reset();
843
844   // If there are any pending downloads left, start the next one.
845   extensions_queue_.StartNextRequest();
846 }
847
848 void ExtensionDownloader::NotifyExtensionsDownloadFailed(
849     const std::set<std::string>& extension_ids,
850     const std::set<int>& request_ids,
851     ExtensionDownloaderDelegate::Error error) {
852   for (std::set<std::string>::const_iterator it = extension_ids.begin();
853        it != extension_ids.end(); ++it) {
854     const ExtensionDownloaderDelegate::PingResult& ping = ping_results_[*it];
855     delegate_->OnExtensionDownloadFailed(*it, error, ping, request_ids);
856     ping_results_.erase(*it);
857   }
858 }
859
860 void ExtensionDownloader::NotifyUpdateFound(const std::string& id,
861                                             const std::string& version) {
862   UpdateDetails updateInfo(id, Version(version));
863   content::NotificationService::current()->Notify(
864       extensions::NOTIFICATION_EXTENSION_UPDATE_FOUND,
865       content::NotificationService::AllBrowserContextsAndSources(),
866       content::Details<UpdateDetails>(&updateInfo));
867 }
868
869 bool ExtensionDownloader::IterateFetchCredentialsAfterFailure(
870     ExtensionFetch* fetch,
871     const net::URLRequestStatus& status,
872     int response_code) {
873   bool auth_failure = status.status() == net::URLRequestStatus::CANCELED ||
874                       (status.status() == net::URLRequestStatus::SUCCESS &&
875                        (response_code == net::HTTP_UNAUTHORIZED ||
876                         response_code == net::HTTP_FORBIDDEN));
877   if (!auth_failure) {
878     return false;
879   }
880   // Here we decide what to do next if the server refused to authorize this
881   // fetch.
882   switch (fetch->credentials) {
883     case ExtensionFetch::CREDENTIALS_NONE:
884       if (fetch->url.DomainIs(kGoogleDotCom) && identity_provider_) {
885         fetch->credentials = ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN;
886       } else {
887         fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES;
888       }
889       return true;
890     case ExtensionFetch::CREDENTIALS_OAUTH2_TOKEN:
891       fetch->oauth2_attempt_count++;
892       // OAuth2 may fail due to an expired access token, in which case we
893       // should invalidate the token and try again.
894       if (response_code == net::HTTP_UNAUTHORIZED &&
895           fetch->oauth2_attempt_count <= kMaxOAuth2Attempts) {
896         DCHECK(identity_provider_.get());
897         OAuth2TokenService::ScopeSet webstore_scopes;
898         webstore_scopes.insert(kWebstoreOAuth2Scope);
899         identity_provider_->GetTokenService()->InvalidateToken(
900             identity_provider_->GetActiveAccountId(),
901             webstore_scopes,
902             access_token_);
903         access_token_.clear();
904         return true;
905       }
906       // Either there is no Gaia identity available, the active identity
907       // doesn't have access to this resource, or the server keeps returning
908       // 401s and we've retried too many times. Fall back on cookies.
909       if (access_token_.empty() ||
910           response_code == net::HTTP_FORBIDDEN ||
911           fetch->oauth2_attempt_count > kMaxOAuth2Attempts) {
912         fetch->credentials = ExtensionFetch::CREDENTIALS_COOKIES;
913         return true;
914       }
915       // Something else is wrong. Time to give up.
916       return false;
917     case ExtensionFetch::CREDENTIALS_COOKIES:
918       if (response_code == net::HTTP_FORBIDDEN) {
919         // Try the next session identity, up to some maximum.
920         return IncrementAuthUserIndex(&fetch->url);
921       }
922       return false;
923     default:
924       NOTREACHED();
925   }
926   NOTREACHED();
927   return false;
928 }
929
930 void ExtensionDownloader::OnGetTokenSuccess(
931     const OAuth2TokenService::Request* request,
932     const std::string& access_token,
933     const base::Time& expiration_time) {
934   access_token_ = access_token;
935   extension_fetcher_->AddExtraRequestHeader(
936       base::StringPrintf("%s: Bearer %s",
937           net::HttpRequestHeaders::kAuthorization,
938           access_token_.c_str()));
939   extension_fetcher_->Start();
940 }
941
942 void ExtensionDownloader::OnGetTokenFailure(
943     const OAuth2TokenService::Request* request,
944     const GoogleServiceAuthError& error) {
945   // If we fail to get an access token, kick the pending fetch and let it fall
946   // back on cookies.
947   extension_fetcher_->Start();
948 }
949
950 ManifestFetchData* ExtensionDownloader::CreateManifestFetchData(
951     const GURL& update_url,
952     int request_id) {
953   ManifestFetchData::PingMode ping_mode = ManifestFetchData::NO_PING;
954   if (update_url.DomainIs(ping_enabled_domain_.c_str())) {
955     if (enable_extra_update_metrics_) {
956       ping_mode = ManifestFetchData::PING_WITH_METRICS;
957     } else {
958       ping_mode = ManifestFetchData::PING;
959     }
960   }
961   return new ManifestFetchData(
962       update_url, request_id, brand_code_, manifest_query_params_, ping_mode);
963 }
964
965 }  // namespace extensions