1326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek// Use of this source code is governed by a BSD-style license that can be
3326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek// found in the LICENSE file.
4326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
5326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "chrome/browser/extensions/updater/extension_downloader.h"
6326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
7326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include <utility>
8326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
9326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/bind.h"
10ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie#include "base/command_line.h"
11ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie#include "base/files/file_path.h"
12326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/location.h"
13326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/logging.h"
14326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/metrics/histogram.h"
15326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/metrics/sparse_histogram.h"
16326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/stl_util.h"
17326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/strings/string_number_conversions.h"
18326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "base/strings/string_util.h"
19892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek#include "base/strings/stringprintf.h"
20b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek#include "base/time/time.h"
2130a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "base/version.h"
22326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "chrome/browser/chrome_notification_types.h"
2330a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/extensions/updater/extension_cache.h"
2430a2e16f6c27f888dd11eba6bbbae1e980078fcbChandler Carruth#include "chrome/browser/extensions/updater/request_queue_impl.h"
25326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "chrome/common/chrome_switches.h"
26326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "chrome/common/chrome_version_info.h"
27326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "chrome/common/extensions/manifest_url_handler.h"
28326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "content/public/browser/browser_thread.h"
29326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "content/public/browser/notification_details.h"
30326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "content/public/browser/notification_service.h"
31326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "extensions/browser/updater/safe_manifest_parser.h"
32af13d5b25b360e698cc1cf1055ad7d14e008e505Ted Kremenek#include "extensions/common/extension_urls.h"
33283a358aecb75e30fcd486f2206f6c03c5e7f11dTed Kremenek#include "google_apis/gaia/identity_provider.h"
34326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "net/base/backoff_entry.h"
35a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek#include "net/base/load_flags.h"
36326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "net/base/net_errors.h"
37db34ab70961ca4b24b600eb47053d7af304659f5Tom Care#include "net/http/http_request_headers.h"
38326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "net/http/http_status_code.h"
39326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "net/url_request/url_fetcher.h"
40326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek#include "net/url_request/url_request_context_getter.h"
417fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek#include "net/url_request/url_request_status.h"
421d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek
43ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikieusing base::Time;
44b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenekusing base::TimeDelta;
45c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xuusing content::BrowserThread;
46c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
47a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremeneknamespace extensions {
481d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek
49a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenekconst char ExtensionDownloader::kBlacklistAppID[] = "com.google.crx.blacklist";
50a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
51a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremeneknamespace {
52a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
53a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenekconst net::BackoffEntry::Policy kDefaultBackoffPolicy = {
54ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  // Number of initial errors (in sequence) to ignore before applying
55a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  // exponential back-off rules.
56a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  0,
57a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
58a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  // Initial delay for exponential back-off in ms.
59a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  2000,
60a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
61a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  // Factor by which the waiting time will be multiplied.
621d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  2,
63a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
641d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  // Fuzzing percentage. ex: 10% will spread requests randomly
65a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  // between 90%-100% of the calculated time.
66ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  0.1,
67ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie
68ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  // Maximum amount of time we are willing to delay our request in ms.
69ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  -1,
701d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek
71ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  // Time to keep an entry from being discarded even when it
72ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  // has no significant state, -1 to never discard.
731d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  -1,
74ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie
75cca300a91966df70c9c320e477a3c26ba622673dTed Kremenek  // Don't use initial delay unless the last request was an error.
76326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  false,
776f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmith};
786f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmith
79b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenekconst char kAuthUserQueryKey[] = "authuser";
80b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek
81b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenekconst int kMaxAuthUserValue = 10;
82ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikieconst int kMaxOAuth2Attempts = 3;
83ad5a894df1841698c824381b414630799adc26caTed Kremenek
846f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmithconst char kNotFromWebstoreInstallSource[] = "notfromwebstore";
856f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmithconst char kDefaultInstallSource[] = "";
866f42b62b6194f53bcbc349f5d17388e1936535d7Dylan Noblesmith
87b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenekconst char kGoogleDotCom[] = "google.com";
88326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekconst char kTokenServiceConsumerId[] = "extension_downloader";
89b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenekconst char kWebstoreOAuth2Scope[] =
90b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek    "https://www.googleapis.com/auth/chromewebstore.readonly";
91b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek
92a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek#define RETRY_HISTOGRAM(name, retry_count, url)                           \
93a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek  if ((url).DomainIs(kGoogleDotCom)) {                                    \
94326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountGoogleUrl", \
951d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                retry_count,                              \
96d200187bd27f9ad68699693a6e57f9ee3ff260faJordy Rose                                1,                                        \
97bc5cb8a5fe2b88f917d47ceb58b53696a121e57eTed Kremenek                                kMaxRetries,                              \
981d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                kMaxRetries + 1);                         \
99b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  } else {                                                                \
100b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek    UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions." name "RetryCountOtherUrl",  \
101326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                                retry_count,                              \
1021d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                1,                                        \
103326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                                kMaxRetries,                              \
104445895a97ae3f1d7bad3480839d31ed3ebcc9c83Ted Kremenek                                kMaxRetries + 1);                         \
105c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu  }
106c6238d2786cfd961b94580b3d3675a1b3ff0721cZhongxing Xu
107ddc0c4814788dda4ef224cd4d22d07154a6ede49Ted Kremenekbool ShouldRetryRequest(const net::URLRequestStatus& status,
108ddc0c4814788dda4ef224cd4d22d07154a6ede49Ted Kremenek                        int response_code) {
109ddc0c4814788dda4ef224cd4d22d07154a6ede49Ted Kremenek  // Retry if the response code is a server error, or the request failed because
110ddc0c4814788dda4ef224cd4d22d07154a6ede49Ted Kremenek  // of network errors as opposed to file errors.
111ddc0c4814788dda4ef224cd4d22d07154a6ede49Ted Kremenek  return ((response_code >= 500 && status.is_success()) ||
112ddc0c4814788dda4ef224cd4d22d07154a6ede49Ted Kremenek          status.status() == net::URLRequestStatus::FAILED);
11374fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek}
11474fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek
11574fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek// This parses and updates a URL query such that the value of the |authuser|
11674fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek// query parameter is incremented by 1. If parameter was not present in the URL,
11774fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek// it will be added with a value of 1. All other query keys and values are
11874fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek// preserved as-is. Returns |false| if the user index exceeds a hard-coded
11974fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenek// maximum.
12074fb1a493cf5d2dd0fb51a4eadf74e85e10a3457Ted Kremenekbool IncrementAuthUserIndex(GURL* url) {
121ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  int user_index = 0;
122f3477c13eeaf11b32a41f181398fb5deffd0dd73Sylvestre Ledru  std::string old_query = url->query();
123326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  std::vector<std::string> new_query_parts;
124326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  url::Component query(0, old_query.length());
125326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  url::Component key, value;
126ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  while (url::ExtractQueryKeyValue(old_query.c_str(), &query, &key, &value)) {
127b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek    std::string key_string = old_query.substr(key.begin, key.len);
128bc5cb8a5fe2b88f917d47ceb58b53696a121e57eTed Kremenek    std::string value_string = old_query.substr(value.begin, value.len);
129b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek    if (key_string == kAuthUserQueryKey) {
130b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek      base::StringToInt(value_string, &user_index);
131b8ad5ee345fa1fdd1fa9253f2d01f69becc88a04Ted Kremenek    } else {
1329b823e8e1ccb8a2cb49923bad22a80ca96f41f92Ted Kremenek      new_query_parts.push_back(base::StringPrintf(
1330d28d360b5559abda755e50b855ba5e59727d9cdTed Kremenek          "%s=%s", key_string.c_str(), value_string.c_str()));
1340d28d360b5559abda755e50b855ba5e59727d9cdTed Kremenek    }
135ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  }
136a2d7e6511a8767dc67381c210601b895a8ebae39Anna Zaks  if (user_index >= kMaxAuthUserValue)
137326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    return false;
138ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  new_query_parts.push_back(
139283a358aecb75e30fcd486f2206f6c03c5e7f11dTed Kremenek      base::StringPrintf("%s=%d", kAuthUserQueryKey, user_index + 1));
14004eeba43040969c05cfcb563195ef5b199297b62Anders Carlsson  std::string new_query_string = JoinString(new_query_parts, '&');
141af13d5b25b360e698cc1cf1055ad7d14e008e505Ted Kremenek  url::Component new_query(0, new_query_string.size());
142ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  url::Replacements<char> replacements;
143ad5a894df1841698c824381b414630799adc26caTed Kremenek  replacements.SetQuery(new_query_string.c_str(), new_query);
144ad5a894df1841698c824381b414630799adc26caTed Kremenek  *url = url->ReplaceComponents(replacements);
145ad5a894df1841698c824381b414630799adc26caTed Kremenek  return true;
146682060c5d95f6e4f79536013781ab0870cdd3850Ted Kremenek}
14704eeba43040969c05cfcb563195ef5b199297b62Anders Carlsson
1485d98994c7749312a43ce6adf45537979a98e7afdChandler Carruth}  // namespace
1495d98994c7749312a43ce6adf45537979a98e7afdChandler Carruth
1505d98994c7749312a43ce6adf45537979a98e7afdChandler CarruthUpdateDetails::UpdateDetails(const std::string& id, const Version& version)
1515d98994c7749312a43ce6adf45537979a98e7afdChandler Carruth    : id(id), version(version) {}
1525d98994c7749312a43ce6adf45537979a98e7afdChandler Carruth
153326be568e2cb04285c84e6e26a3e6b3822607361Ted KremenekUpdateDetails::~UpdateDetails() {}
154db34ab70961ca4b24b600eb47053d7af304659f5Tom Care
155326be568e2cb04285c84e6e26a3e6b3822607361Ted KremenekExtensionDownloader::ExtensionFetch::ExtensionFetch()
156326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    : url(), credentials(CREDENTIALS_NONE) {
157326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek}
158326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
159326be568e2cb04285c84e6e26a3e6b3822607361Ted KremenekExtensionDownloader::ExtensionFetch::ExtensionFetch(
160d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek    const std::string& id,
161326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const GURL& url,
1621d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    const std::string& package_hash,
163326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const std::string& version,
164ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie    const std::set<int>& request_ids)
165b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek    : id(id),
166b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      url(url),
167b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      package_hash(package_hash),
168ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie      version(version),
1697fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      request_ids(request_ids),
1707fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      credentials(CREDENTIALS_NONE),
1717fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      oauth2_attempt_count(0) {
1727fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek}
1737fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek
174ba243b59a1074e0962f6abfa3bb9aa984eac1245David BlaikieExtensionDownloader::ExtensionFetch::~ExtensionFetch() {}
175a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
176a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted KremenekExtensionDownloader::ExtensionDownloader(
177a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek    ExtensionDownloaderDelegate* delegate,
178a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek    net::URLRequestContextGetter* request_context)
179a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek    : OAuth2TokenService::Consumer(kTokenServiceConsumerId),
180a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      delegate_(delegate),
181a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      request_context_(request_context),
182a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      manifests_queue_(&kDefaultBackoffPolicy,
183a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek                       base::Bind(&ExtensionDownloader::CreateManifestFetcher,
184a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek                                  base::Unretained(this))),
185a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek      extensions_queue_(&kDefaultBackoffPolicy,
186a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek                        base::Bind(&ExtensionDownloader::CreateExtensionFetcher,
187a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek                                   base::Unretained(this))),
188ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie      extension_cache_(NULL),
189b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      enable_extra_update_metrics_(false),
190326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      weak_ptr_factory_(this) {
191326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  DCHECK(delegate_);
192326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  DCHECK(request_context_.get());
193326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek}
194326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
195326be568e2cb04285c84e6e26a3e6b3822607361Ted KremenekExtensionDownloader::~ExtensionDownloader() {}
196326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
197326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekbool ExtensionDownloader::AddExtension(const Extension& extension,
198a02d893f15d4663bdba3bd92ade10070bf0510e4Zhongxing Xu                                       int request_id) {
199ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  // Skip extensions with empty update URLs converted from user
200ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  // scripts.
2011d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  if (extension.converted_from_user_script() &&
202a02d893f15d4663bdba3bd92ade10070bf0510e4Zhongxing Xu      ManifestURL::GetUpdateURL(&extension).is_empty()) {
203326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    return false;
204326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  }
205326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
2061d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  // If the extension updates itself from the gallery, ignore any update URL
207326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // data.  At the moment there is no extra data that an extension can
208326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // communicate to the the gallery update servers.
209326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  std::string update_url_data;
210326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  if (!ManifestURL::UpdatesFromGallery(&extension))
211326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    update_url_data = delegate_->GetUpdateUrlData(extension.id());
212d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek
213326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  std::string install_source;
214326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  bool force_update = delegate_->ShouldForceUpdate(extension.id(),
2151d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                                   &install_source);
216326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  return AddExtensionData(extension.id(),
217326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                          *extension.version(),
218326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                          extension.GetType(),
2198ddf7cead8a67342a4584a203e0bf736b7efedbeZhongxing Xu                          ManifestURL::GetUpdateURL(&extension),
2208ddf7cead8a67342a4584a203e0bf736b7efedbeZhongxing Xu                          update_url_data,
2211d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                          request_id,
222326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                          force_update,
2231d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                          install_source);
224326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek}
225a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenek
226a5937bbfd19e61d651a58b0f0ffeef68457902a5Ted Kremenekbool ExtensionDownloader::AddPendingExtension(const std::string& id,
2271d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                              const GURL& update_url,
228326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                                              int request_id) {
229326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // Use a zero version to ensure that a pending extension will always
230d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek  // be updated, and thus installed (assuming all extensions have
2311d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  // non-zero versions).
232326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  Version version("0.0.0.0");
233326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  DCHECK(version.IsValid());
234326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
235326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  return AddExtensionData(id,
236326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                          version,
237d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek                          Manifest::TYPE_UNKNOWN,
238326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                          update_url,
239326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                          std::string(),
240fadcd5d5bbe1bfc1c6b8d819cc2242f780a49fecAnna Zaks                          request_id,
241fadcd5d5bbe1bfc1c6b8d819cc2242f780a49fecAnna Zaks                          false,
242fadcd5d5bbe1bfc1c6b8d819cc2242f780a49fecAnna Zaks                          std::string());
243326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek}
244326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
245326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekvoid ExtensionDownloader::StartAllPending(ExtensionCache* cache) {
246326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  if (cache) {
247326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    extension_cache_ = cache;
2481d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    extension_cache_->Start(base::Bind(
249326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek        &ExtensionDownloader::DoStartAllPending,
2509c378f705405d37f49795d5e915989de774fe11fTed Kremenek        weak_ptr_factory_.GetWeakPtr()));
251326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  } else {
252326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    DoStartAllPending();
253326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  }
25426c9cb55cb96643c0759c08d037c16c309864087Zhongxing Xu}
255892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek
256326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekvoid ExtensionDownloader::DoStartAllPending() {
257326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  ReportStats();
258326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  url_stats_ = URLStats();
259326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
260326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  for (FetchMap::iterator it = fetches_preparing_.begin();
261326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek       it != fetches_preparing_.end(); ++it) {
262326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    std::vector<linked_ptr<ManifestFetchData> >& list = it->second;
263326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    for (size_t i = 0; i < list.size(); ++i) {
2641d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek      StartUpdateCheck(scoped_ptr<ManifestFetchData>(list[i].release()));
265ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie    }
266d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu  }
267892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek  fetches_preparing_.clear();
268d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu}
269326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
270326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekvoid ExtensionDownloader::StartBlacklistUpdate(
271326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const std::string& version,
272326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const ManifestFetchData::PingData& ping_data,
273892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek    int request_id) {
274d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu  // Note: it is very important that we use the https version of the update
275326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // url here to avoid DNS hijacking of the blacklist, which is not validated
276326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // by a public key signature like .crx files are.
277fadcd5d5bbe1bfc1c6b8d819cc2242f780a49fecAnna Zaks  scoped_ptr<ManifestFetchData> blacklist_fetch(CreateManifestFetchData(
278fadcd5d5bbe1bfc1c6b8d819cc2242f780a49fecAnna Zaks      extension_urls::GetWebstoreUpdateUrl(), request_id));
279fadcd5d5bbe1bfc1c6b8d819cc2242f780a49fecAnna Zaks  DCHECK(blacklist_fetch->base_url().SchemeIsSecure());
280326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  blacklist_fetch->AddExtension(kBlacklistAppID,
281326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                                version,
282326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                                &ping_data,
283d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek                                std::string(),
2841d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                kDefaultInstallSource,
285d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek                                false);
286892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek  StartUpdateCheck(blacklist_fetch.Pass());
287326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek}
288326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
289326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekvoid ExtensionDownloader::SetWebstoreIdentityProvider(
290326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    scoped_ptr<IdentityProvider> identity_provider) {
291326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  identity_provider_.swap(identity_provider);
2929c378f705405d37f49795d5e915989de774fe11fTed Kremenek}
293326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
294326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenekbool ExtensionDownloader::AddExtensionData(
295326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const std::string& id,
296326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const Version& version,
297326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    Manifest::Type extension_type,
298326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const GURL& extension_update_url,
299d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek    const std::string& update_url_data,
300326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    int request_id,
3011d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    bool force_update,
302326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    const std::string& install_source_override) {
303326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  GURL update_url(extension_update_url);
304326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // Skip extensions with non-empty invalid update URLs.
305326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  if (!update_url.is_empty() && !update_url.is_valid()) {
306326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    LOG(WARNING) << "Extension " << id << " has invalid update url "
307326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek                 << update_url;
308326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    return false;
309326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  }
3101d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek
311326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // Make sure we use SSL for store-hosted extensions.
312326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  if (extension_urls::IsWebstoreUpdateUrl(update_url) &&
313326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      !update_url.SchemeIsSecure())
314326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    update_url = extension_urls::GetWebstoreUpdateUrl();
3159c378f705405d37f49795d5e915989de774fe11fTed Kremenek
316326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // Skip extensions with empty IDs.
317326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  if (id.empty()) {
318326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    LOG(WARNING) << "Found extension with empty ID";
319326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    return false;
320326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  }
321326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
3227fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek  if (update_url.DomainIs(kGoogleDotCom)) {
3237fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek    url_stats_.google_url_count++;
3247fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek  } else if (update_url.is_empty()) {
325326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    url_stats_.no_url_count++;
326326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    // Fill in default update URL.
327326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    update_url = extension_urls::GetWebstoreUpdateUrl();
328ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  } else {
329ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie    url_stats_.other_url_count++;
3307fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek  }
3317fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek
332326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  switch (extension_type) {
333326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    case Manifest::TYPE_THEME:
334326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      ++url_stats_.theme_count;
335326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      break;
336326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    case Manifest::TYPE_EXTENSION:
3377fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek    case Manifest::TYPE_USER_SCRIPT:
3387fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      ++url_stats_.extension_count;
339326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      break;
340326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    case Manifest::TYPE_HOSTED_APP:
341326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    case Manifest::TYPE_LEGACY_PACKAGED_APP:
3421d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek      ++url_stats_.app_count;
3437fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      break;
3447fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek    case Manifest::TYPE_PLATFORM_APP:
345326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      ++url_stats_.platform_app_count;
3467fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      break;
347326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    case Manifest::TYPE_UNKNOWN:
348d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek    default:
3499c378f705405d37f49795d5e915989de774fe11fTed Kremenek      ++url_stats_.pending_count;
350326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      break;
351326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  }
352326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
353326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  std::vector<GURL> update_urls;
354326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  update_urls.push_back(update_url);
355326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // If metrics are enabled, also add to ManifestFetchData for the
356326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  // webstore update URL.
357326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  if (!extension_urls::IsWebstoreUpdateUrl(update_url) &&
358d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek      enable_extra_update_metrics_) {
3591d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    update_urls.push_back(extension_urls::GetWebstoreUpdateUrl());
360326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  }
361892697dd2287caf7c29aaaa82909b0e90b8b63feTed Kremenek
362d706434b0231c76fd9acf30060646a7aa8f69aefZhongxing Xu  for (size_t i = 0; i < update_urls.size(); ++i) {
363326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    DCHECK(!update_urls[i].is_empty());
3641d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    DCHECK(update_urls[i].is_valid());
365326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
366326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    std::string install_source = i == 0 ?
3677fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek        kDefaultInstallSource : kNotFromWebstoreInstallSource;
3687fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek    if (!install_source_override.empty()) {
3697fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek      install_source = install_source_override;
3707fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek    }
3717fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek
3727fa9b4f258636d89342eda28f21a986c8ac353b1Ted Kremenek    ManifestFetchData::PingData ping_data;
373d064fdc4b7b64ca55b40b70490c79d6f569df78eTed Kremenek    ManifestFetchData::PingData* optional_ping_data = NULL;
374326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    if (delegate_->GetPingDataForExtension(id, &ping_data))
375326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek      optional_ping_data = &ping_data;
376326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
377326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    // Find or create a ManifestFetchData to add this extension to.
3781d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    bool added = false;
379326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    FetchMap::iterator existing_iter = fetches_preparing_.find(
380326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek        std::make_pair(request_id, update_urls[i]));
381326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek    if (existing_iter != fetches_preparing_.end() &&
382326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek        !existing_iter->second.empty()) {
3831d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek      // Try to add to the ManifestFetchData at the end of the list.
3841d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek      ManifestFetchData* existing_fetch = existing_iter->second.back().get();
385ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie      if (existing_fetch->AddExtension(id, version.GetString(),
386b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                                       optional_ping_data, update_url_data,
387b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                                       install_source,
388b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                                       force_update)) {
389a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek        added = true;
390a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek      }
391a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek    }
392a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek    if (!added) {
393ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie      // Otherwise add a new element to the list, if the list doesn't exist or
394b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      // if its last element is already full.
3951d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek      linked_ptr<ManifestFetchData> fetch(
3965a1ffe98b04120846a15f7105905b5f363b08635Jordan Rose          CreateManifestFetchData(update_urls[i], request_id));
3975a1ffe98b04120846a15f7105905b5f363b08635Jordan Rose      fetches_preparing_[std::make_pair(request_id, update_urls[i])].
398a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek          push_back(fetch);
399a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek      added = fetch->AddExtension(id, version.GetString(),
400ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie                                  optional_ping_data,
4011d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                                  update_url_data,
402ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie                                  install_source,
403d200187bd27f9ad68699693a6e57f9ee3ff260faJordy Rose                                  force_update);
404ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie      DCHECK(added);
405b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek    }
406b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  }
407b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek
408ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  return true;
409b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek}
410b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek
411b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenekvoid ExtensionDownloader::ReportStats() const {
412a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
413a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek                           url_stats_.extension_count);
414a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
415a43df9539644bf1c258e12710cd69d79b0b078cdTed Kremenek                           url_stats_.theme_count);
416ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
4171d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek                           url_stats_.app_count);
418b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPackagedApp",
419b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                           url_stats_.platform_app_count);
420b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
421b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                           url_stats_.pending_count);
422b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
423b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                           url_stats_.google_url_count);
424ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
425b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                           url_stats_.other_url_count);
426d200187bd27f9ad68699693a6e57f9ee3ff260faJordy Rose  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
427d200187bd27f9ad68699693a6e57f9ee3ff260faJordy Rose                           url_stats_.no_url_count);
428b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek}
429ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie
430b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenekvoid ExtensionDownloader::StartUpdateCheck(
431ba243b59a1074e0962f6abfa3bb9aa984eac1245David Blaikie    scoped_ptr<ManifestFetchData> fetch_data) {
432b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  const std::set<std::string>& id_set(fetch_data->extension_ids());
433b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek
434b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  if (CommandLine::ForCurrentProcess()->HasSwitch(
435b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      switches::kDisableBackgroundNetworking)) {
436b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek    NotifyExtensionsDownloadFailed(id_set,
437b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                                   fetch_data->request_ids(),
438b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek                                   ExtensionDownloaderDelegate::DISABLED);
4391d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek    return;
440b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  }
441b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek
442b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  RequestQueue<ManifestFetchData>::iterator i;
4431d26f48dc2eea1c07431ca1519d7034a21b9bcffTed Kremenek  for (i = manifests_queue_.begin(); i != manifests_queue_.end(); ++i) {
444b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek    if (fetch_data->full_url() == i->full_url()) {
445b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      // This url is already scheduled to be fetched.
446b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      i->Merge(*fetch_data);
447b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek      return;
448b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek    }
449b1b5daf30d2597e066936772bd206500232d7d65Ted Kremenek  }
450326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek
451326be568e2cb04285c84e6e26a3e6b3822607361Ted Kremenek  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
462void 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
487void 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
510void 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
552void 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
622void 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.
695void 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
733void 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
743void 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
791void 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
848void 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
860void 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
869bool 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
930void 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
942void 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
950ManifestFetchData* 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
966