172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Use of this source code is governed by a BSD-style license that can be
3c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// found in the LICENSE file.
4c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
5c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_updater.h"
6c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
7c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <algorithm>
8c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include <set>
9c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
10ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "base/compiler_specific.h"
11c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/logging.h"
12c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/file_util.h"
13731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick#include "base/metrics/histogram.h"
14c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/rand_util.h"
15c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/stl_util-inl.h"
163345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_number_conversions.h"
173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "base/string_split.h"
18c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/string_util.h"
19c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/time.h"
203f50c38dc070f4bb515c1b64450dae14f316474eKristian Monsen#include "base/threading/thread.h"
21c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/version.h"
22ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "crypto/sha2.h"
23ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "content/common/notification_service.h"
24c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/browser_process.h"
25c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/extensions/extension_error_reporter.h"
2621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/extensions/extension_service.h"
273345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#include "chrome/browser/prefs/pref_service.h"
2821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen#include "chrome/browser/profiles/profile.h"
29c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/browser/utility_process_host.h"
30c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_switches.h"
31c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/chrome_version_info.h"
32c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension.h"
33c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/extensions/extension_constants.h"
34ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#include "chrome/common/extensions/extension_file_util.h"
35c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "chrome/common/pref_names.h"
36c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "googleurl/src/gurl.h"
37c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/escape.h"
38c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/base/load_flags.h"
39c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "net/url_request/url_request_status.h"
40c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick#if defined(OS_MACOSX)
42c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#include "base/sys_string_conversions.h"
43c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
44c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
45ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#define SEND_ACTIVE_PINGS 1
46ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
47c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::RandDouble;
48c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::RandInt;
49c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::Time;
50c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing base::TimeDelta;
51c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing prefs::kExtensionBlacklistUpdateVersion;
52c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing prefs::kLastExtensionsUpdateCheck;
53c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochusing prefs::kNextExtensionsUpdateCheck;
54c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
55c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Update AppID for extension blacklist.
56c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst char* ExtensionUpdater::kBlacklistAppID = "com.google.crx.blacklist";
57c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
58c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Wait at least 5 minutes after browser startup before we do any checks. If you
59c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// change this value, make sure to update comments where it is used.
60c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochconst int kStartupWaitSeconds = 60 * 5;
61c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
62c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// For sanity checking on update frequency - enforced in release mode only.
63c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMinUpdateFrequencySeconds = 30;
64c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7;  // 7 days
65c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
66c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Maximum length of an extension manifest update check url, since it is a GET
67c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// request. We want to stay under 2K because of proxies, etc.
68c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic const int kExtensionsManifestMaxURLSize = 2000;
69c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70513209b27ff55e2841eac0e4120199c23acce758Ben MurdochManifestFetchData::ManifestFetchData(const GURL& update_url)
71731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    : base_url_(update_url),
72731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick      full_url_(update_url) {
73731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
74731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
75731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickManifestFetchData::~ManifestFetchData() {}
76c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
77c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The format for request parameters in update checks is:
78c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
79c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   ?x=EXT1_INFO&x=EXT2_INFO
80c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
81c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
82c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
83c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   id=EXTENSION_ID&v=VERSION&uc
84c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
85c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
86ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// looks like r=DAYS or a=DAYS for extensions in the Chrome extensions gallery.
87ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// ('r' refers to 'roll call' ie installation, and 'a' refers to 'active').
88ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// These values will each be present at most once every 24 hours, and indicate
89ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// the number of days since the last time it was present in an update check.
90c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
91c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// So for two extensions like:
92c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   Extension 1- id:aaaa version:1.1
93c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   Extension 2- id:bbbb version:2.0
94c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
95c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// the full update url would be:
96c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//   http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
97c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch//
98c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// (Note that '=' is %3D and '&' is %26 when urlencoded.)
99c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool ManifestFetchData::AddExtension(std::string id, std::string version,
100ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                     const PingData& ping_data,
1014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                     const std::string& update_url_data) {
102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (extension_ids_.find(id) != extension_ids_.end()) {
103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Duplicate extension id " << id;
104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
105c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
106c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Compute the string we'd append onto the full_url_, and see if it fits.
108c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<std::string> parts;
109c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  parts.push_back("id=" + id);
110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  parts.push_back("v=" + version);
111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  parts.push_back("uc");
112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!update_url_data.empty()) {
1144a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // Make sure the update_url_data string is escaped before using it so that
1154a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // there is no chance of overriding the id or v other parameter value
1164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    // we place into the x= value.
1174a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    parts.push_back("ap=" + EscapeQueryParamValue(update_url_data, true));
1184a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  }
1194a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
120ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Append rollcall and active ping parameters.
121ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (base_url_.DomainIs("google.com")) {
122ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    std::string ping_value;
123ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    pings_[id] = PingData(0, 0);
124ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
125ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (ping_data.rollcall_days == kNeverPinged ||
126ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ping_data.rollcall_days > 0) {
127ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ping_value += "r=" + base::IntToString(ping_data.rollcall_days);
128ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      pings_[id].rollcall_days = ping_data.rollcall_days;
129ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
130ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#if SEND_ACTIVE_PINGS
131ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (ping_data.active_days == kNeverPinged || ping_data.active_days > 0) {
132ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!ping_value.empty())
133ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        ping_value += "&";
134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      ping_value += "a=" + base::IntToString(ping_data.active_days);
135ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      pings_[id].active_days = ping_data.active_days;
136ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
137ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen#endif  // SEND_ACTIVE_PINGS
138ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!ping_value.empty())
139ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      parts.push_back("ping=" + EscapeQueryParamValue(ping_value, true));
140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
142c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::string extra = full_url_.has_query() ? "&" : "?";
143c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  extra += "x=" + EscapeQueryParamValue(JoinString(parts, '&'), true);
144c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
145c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Check against our max url size, exempting the first extension added.
146c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
147dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!extension_ids_.empty() && new_size > kExtensionsManifestMaxURLSize) {
148c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
149c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
150c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
151c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
152c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
153c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We have room so go ahead and add the extension.
154c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  extension_ids_.insert(id);
155c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
156c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
157c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
158c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ManifestFetchData::Includes(const std::string& extension_id) const {
160731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  return extension_ids_.find(extension_id) != extension_ids_.end();
161731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick}
162731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ManifestFetchData::DidPing(std::string extension_id, PingType type) const {
164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::map<std::string, PingData>::const_iterator i = pings_.find(extension_id);
165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (i == pings_.end())
166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return false;
167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  int value = 0;
168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (type == ROLLCALL)
169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    value = i->second.rollcall_days;
170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  else if (type == ACTIVE)
171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    value = i->second.active_days;
172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  else
173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    NOTREACHED();
174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return value == kNeverPinged || value > 0;
175c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
176c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
177c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochnamespace {
178c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
179ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// When we've computed a days value, we want to make sure we don't send a
180ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// negative value (due to the system clock being set backwards, etc.), since -1
181ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// is a special sentinel value that means "never pinged", and other negative
182ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen// values don't make sense.
183ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenstatic int SanitizeDays(int days) {
184ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (days < 0)
185ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return 0;
186ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return days;
187ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
188ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
189c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Calculates the value to use for the ping days parameter.
190c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic int CalculatePingDays(const Time& last_ping_day) {
191c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int days = ManifestFetchData::kNeverPinged;
192c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!last_ping_day.is_null()) {
193ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    days = SanitizeDays((Time::Now() - last_ping_day).InDays());
194c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
195c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return days;
196c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
197c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
198ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenstatic int CalculateActivePingDays(const Time& last_active_ping_day,
199ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   bool hasActiveBit) {
200ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!hasActiveBit)
201ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return 0;
202ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (last_active_ping_day.is_null())
203ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return ManifestFetchData::kNeverPinged;
204ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
205ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
206ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
207c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}  // namespace
208c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
209c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochManifestFetchesBuilder::ManifestFetchesBuilder(
210ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ExtensionServiceInterface* service,
211ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ExtensionPrefs* prefs)
212ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    : service_(service), prefs_(prefs) {
213c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(service_);
214ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(prefs_);
215c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
216c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
217731df977c0511bca2206b5f333555b1205ff1f43Iain MerrickManifestFetchesBuilder::~ManifestFetchesBuilder() {}
218731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick
219c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ManifestFetchesBuilder::AddExtension(const Extension& extension) {
2203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // Skip extensions with empty update URLs converted from user
2213345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  // scripts.
2223345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (extension.converted_from_user_script() &&
2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      extension.update_url().is_empty()) {
2243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
2253345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  }
2263345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
2274a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // If the extension updates itself from the gallery, ignore any update URL
2284a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // data.  At the moment there is no extra data that an extension can
2294a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // communicate to the the gallery update servers.
2304a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  std::string update_url_data;
2314a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  if (!extension.UpdatesFromGallery())
232ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    update_url_data = prefs_->GetUpdateUrlData(extension.id());
2334a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
234c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  AddExtensionData(extension.location(),
235c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   extension.id(),
236c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                   *extension.version(),
23721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                   extension.GetType(),
2384a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                   extension.update_url(), update_url_data);
239c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
240c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
241c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ManifestFetchesBuilder::AddPendingExtension(
242c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& id,
243c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const PendingExtensionInfo& info) {
244c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Use a zero version to ensure that a pending extension will always
245c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // be updated, and thus installed (assuming all extensions have
246c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // non-zero versions).
247c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<Version> version(
248c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      Version::GetVersionFromString("0.0.0.0"));
2493345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
25021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  AddExtensionData(
251dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      info.install_source(), id, *version,
252dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen      Extension::TYPE_UNKNOWN, info.update_url(), "");
253c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
254c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
255c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ManifestFetchesBuilder::ReportStats() const {
25621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtension",
25721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                           url_stats_.extension_count);
258c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckTheme",
259c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           url_stats_.theme_count);
26021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckApp",
26121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                           url_stats_.app_count);
26221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckPending",
26321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen                           url_stats_.pending_count);
264c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckGoogleUrl",
265c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           url_stats_.google_url_count);
266c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckOtherUrl",
267c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           url_stats_.other_url_count);
268c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckNoUrl",
269c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           url_stats_.no_url_count);
270c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
271c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
272c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::vector<ManifestFetchData*> ManifestFetchesBuilder::GetFetches() {
273c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<ManifestFetchData*> fetches;
274c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  fetches.reserve(fetches_.size());
275c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::multimap<GURL, ManifestFetchData*>::iterator it =
276c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           fetches_.begin(); it != fetches_.end(); ++it) {
277c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    fetches.push_back(it->second);
278c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
279c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  fetches_.clear();
280c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  url_stats_ = URLStats();
281c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return fetches;
282c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
283c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
284c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ManifestFetchesBuilder::AddExtensionData(
285c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Extension::Location location,
286c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& id,
287c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const Version& version,
28821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    Extension::Type extension_type,
2894a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    GURL update_url,
2904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    const std::string& update_url_data) {
291513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  if (!Extension::IsAutoUpdateableLocation(location)) {
292c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
293c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
294c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
295c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Skip extensions with non-empty invalid update URLs.
296c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!update_url.is_empty() && !update_url.is_valid()) {
297c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "Extension " << id << " has invalid update url "
298c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                 << update_url;
299c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
300c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
301c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
302c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Skip extensions with empty IDs.
303c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (id.empty()) {
304c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "Found extension with empty ID";
305c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
306c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
307c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
308c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (update_url.DomainIs("google.com")) {
309c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    url_stats_.google_url_count++;
310c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (update_url.is_empty()) {
311c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    url_stats_.no_url_count++;
312c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Fill in default update URL.
313c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    //
314c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(akalin): Figure out if we should use the HTTPS version.
315201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    update_url = Extension::GalleryUpdateUrl(false);
316c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
317c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    url_stats_.other_url_count++;
318c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
319c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
32021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  switch (extension_type) {
32121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    case Extension::TYPE_THEME:
32221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      ++url_stats_.theme_count;
32321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      break;
32421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    case Extension::TYPE_EXTENSION:
32521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    case Extension::TYPE_USER_SCRIPT:
32621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      ++url_stats_.extension_count;
32721d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      break;
32821d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    case Extension::TYPE_HOSTED_APP:
32921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    case Extension::TYPE_PACKAGED_APP:
33021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      ++url_stats_.app_count;
331ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      break;
33221d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    case Extension::TYPE_UNKNOWN:
33321d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    default:
33421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      ++url_stats_.pending_count;
33521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      break;
336c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
337c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
338c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!update_url.is_empty());
339c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(update_url.is_valid());
340c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
341c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ManifestFetchData* fetch = NULL;
342c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::multimap<GURL, ManifestFetchData*>::iterator existing_iter =
343c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      fetches_.find(update_url);
344c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
345c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Find or create a ManifestFetchData to add this extension to.
346ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ManifestFetchData::PingData ping_data;
347ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ping_data.rollcall_days = CalculatePingDays(prefs_->LastPingDay(id));
348ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ping_data.active_days =
349ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      CalculateActivePingDays(prefs_->LastActivePingDay(id),
350ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                              prefs_->GetActiveBit(id));
351c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  while (existing_iter != fetches_.end()) {
352c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (existing_iter->second->AddExtension(id, version.GetString(),
353ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            ping_data, update_url_data)) {
354c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      fetch = existing_iter->second;
355c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      break;
356c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
357c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    existing_iter++;
358c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
359c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!fetch) {
360c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    fetch = new ManifestFetchData(update_url);
361c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    fetches_.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch));
362ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bool added = fetch->AddExtension(id, version.GetString(), ping_data,
3634a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch                                     update_url_data);
364c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    DCHECK(added);
365c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
366c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
367c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
368c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// A utility class to do file handling on the file I/O thread.
369c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass ExtensionUpdaterFileHandler
370c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    : public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> {
371c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
372ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  explicit ExtensionUpdaterFileHandler(
373ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      base::WeakPtr<ExtensionUpdater> updater)
374ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      : updater_(updater) {
375ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
376ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
377ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
378c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Writes crx file data into a tempfile, and calls back the updater.
379c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void WriteTempFile(const std::string& extension_id, const std::string& data,
380ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                     const GURL& download_url) {
381c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Make sure we're running in the right thread.
382731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
383c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
384ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    bool failed = false;
385c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FilePath path;
386c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!file_util::CreateTemporaryFile(&path)) {
387c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(WARNING) << "Failed to create temporary file path";
388ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      failed = true;
389ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else if (file_util::WriteFile(path, data.c_str(), data.length()) !=
390c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        static_cast<int>(data.length())) {
39172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      // TODO(asargent) - It would be nice to back off updating altogether if
392c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // the disk is full. (http://crbug.com/12763).
393c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      LOG(ERROR) << "Failed to write temporary file";
394c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      file_util::Delete(path, false);
395ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      failed = true;
396c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
397c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
398ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (failed) {
399ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!BrowserThread::PostTask(
400ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              BrowserThread::UI, FROM_HERE,
401ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              NewRunnableMethod(
402ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  this, &ExtensionUpdaterFileHandler::OnCRXFileWriteError,
403ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  extension_id))) {
404ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NOTREACHED();
405ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
406ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    } else {
407ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!BrowserThread::PostTask(
408ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              BrowserThread::UI, FROM_HERE,
409ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              NewRunnableMethod(
410ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  this, &ExtensionUpdaterFileHandler::OnCRXFileWritten,
411ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  extension_id, path, download_url))) {
412ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NOTREACHED();
413ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        // Delete |path| since we couldn't post.
414ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        extension_file_util::DeleteFile(path, false);
415ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
416ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
417c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
418c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
419c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
420c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  friend class base::RefCountedThreadSafe<ExtensionUpdaterFileHandler>;
421c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
422ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ~ExtensionUpdaterFileHandler() {
423ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI) ||
424ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen           BrowserThread::CurrentlyOn(BrowserThread::FILE));
425ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
426ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
427ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void OnCRXFileWritten(const std::string& id,
428ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                        const FilePath& path,
429ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                        const GURL& download_url) {
430ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
431ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!updater_) {
432ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      // Delete |path| since we don't have an updater anymore.
433ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!BrowserThread::PostTask(
434ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              BrowserThread::FILE, FROM_HERE,
435ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              NewRunnableFunction(
436ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  extension_file_util::DeleteFile, path, false))) {
437ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NOTREACHED();
438ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
439ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
440ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
441ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // The ExtensionUpdater now owns the temp file.
442ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    updater_->OnCRXFileWritten(id, path, download_url);
443ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
444ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
445ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  void OnCRXFileWriteError(const std::string& id) {
446ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
447ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!updater_) {
448ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
449ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
450ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    updater_->OnCRXFileWriteError(id);
451ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
452ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
453ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Should be accessed only on UI thread.
454ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::WeakPtr<ExtensionUpdater> updater_;
455c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
456c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
45772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionUpdater::ExtensionFetch::ExtensionFetch()
45872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : id(""),
45972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      url(),
46072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      package_hash(""),
46172a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      version("") {}
46272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
46372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionUpdater::ExtensionFetch::ExtensionFetch(const std::string& i,
46472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                 const GURL& u,
46572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                 const std::string& h,
46672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                                 const std::string& v)
46772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    : id(i), url(u), package_hash(h), version(v) {}
46872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
46972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian MonsenExtensionUpdater::ExtensionFetch::~ExtensionFetch() {}
47072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen
471ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian MonsenExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface* service,
472ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   ExtensionPrefs* extension_prefs,
473c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   PrefService* prefs,
474ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                   Profile* profile,
475c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                   int frequency_seconds)
476ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    : alive_(false),
477ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
478ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      service_(service), frequency_seconds_(frequency_seconds),
479ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      method_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)),
480ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      will_check_soon_(false), extension_prefs_(extension_prefs),
481ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      prefs_(prefs), profile_(profile), blacklist_checks_enabled_(true) {
482c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Init();
483c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
484c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
485c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::Init() {
486c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK_GE(frequency_seconds_, 5);
487c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(frequency_seconds_ <= kMaxUpdateFrequencySeconds);
488c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#ifdef NDEBUG
489c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // In Release mode we enforce that update checks don't happen too often.
490c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
491c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch#endif
492c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
493c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
494c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
495c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochExtensionUpdater::~ExtensionUpdater() {
4964a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  Stop();
497c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
498c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
499c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void EnsureInt64PrefRegistered(PrefService* prefs,
5003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                      const char name[]) {
501c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!prefs->FindPreference(name))
502c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    prefs->RegisterInt64Pref(name, 0);
503c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
504c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
505c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstatic void EnsureBlacklistVersionPrefRegistered(PrefService* prefs) {
506c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!prefs->FindPreference(kExtensionBlacklistUpdateVersion))
507c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    prefs->RegisterStringPref(kExtensionBlacklistUpdateVersion, "0");
508c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
509c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
510c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// The overall goal here is to balance keeping clients up to date while
511c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// avoiding a thundering herd against update servers.
512c407dc5cd9bdc5668497f21b26b09d988ab439deBen MurdochTimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
5134a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
514c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If someone's testing with a quick frequency, just allow it.
515c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (frequency_seconds_ < kStartupWaitSeconds)
516c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TimeDelta::FromSeconds(frequency_seconds_);
517c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
518c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we've never scheduled a check before, start at frequency_seconds_.
519c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!prefs_->HasPrefPath(kNextExtensionsUpdateCheck))
520c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TimeDelta::FromSeconds(frequency_seconds_);
521c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
522c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If it's been a long time since our last actual check, we want to do one
523c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // relatively soon.
524c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Time now = Time::Now();
525c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Time last = Time::FromInternalValue(prefs_->GetInt64(
526c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kLastExtensionsUpdateCheck));
527c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int days = (now - last).InDays();
528c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (days >= 30) {
529c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Wait 5-10 minutes.
530c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
531c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          kStartupWaitSeconds * 2));
532c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (days >= 14) {
533c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Wait 10-20 minutes.
534c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
535c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          kStartupWaitSeconds * 4));
536c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (days >= 3) {
537c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // Wait 20-40 minutes.
538c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
539c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          kStartupWaitSeconds * 8));
540c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
541c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
542c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Read the persisted next check time, and use that if it isn't too soon.
543c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Otherwise pick something random.
544c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
545c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      kNextExtensionsUpdateCheck));
546c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
547c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (saved_next >= earliest) {
548c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return saved_next - now;
549c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
550c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
551c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          frequency_seconds_));
552c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
553c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
554c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
555c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::Start() {
5564a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(!alive_);
5574a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // If these are NULL, then that means we've been called after Stop()
5584a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // has been called.
5594a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(service_);
560ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(extension_prefs_);
5614a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(prefs_);
562ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(profile_);
563ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(!weak_ptr_factory_.HasWeakPtrs());
564ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  file_handler_ =
565ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      new ExtensionUpdaterFileHandler(weak_ptr_factory_.GetWeakPtr());
5664a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  alive_ = true;
567c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Make sure our prefs are registered, then schedule the first check.
568c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EnsureInt64PrefRegistered(prefs_, kLastExtensionsUpdateCheck);
569c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EnsureInt64PrefRegistered(prefs_, kNextExtensionsUpdateCheck);
570c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  EnsureBlacklistVersionPrefRegistered(prefs_);
571c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ScheduleNextCheck(DetermineFirstCheckDelay());
572c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
573c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
574c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::Stop() {
575ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  weak_ptr_factory_.InvalidateWeakPtrs();
5764a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  alive_ = false;
577ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  file_handler_ = NULL;
5784a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  service_ = NULL;
579ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  extension_prefs_ = NULL;
5804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  prefs_ = NULL;
581ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  profile_ = NULL;
582c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  timer_.Stop();
583ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  will_check_soon_ = false;
584ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  method_factory_.RevokeAll();
585c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  manifest_fetcher_.reset();
586c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  extension_fetcher_.reset();
5874a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  STLDeleteElements(&manifests_pending_);
588c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  manifests_pending_.clear();
589c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  extensions_pending_.clear();
590c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
591c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
592c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::OnURLFetchComplete(
59372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const URLFetcher* source,
59472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const GURL& url,
59572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const net::URLRequestStatus& status,
59672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    int response_code,
59772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const ResponseCookies& cookies,
598c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::string& data) {
5994a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // Stop() destroys all our URLFetchers, which means we shouldn't be
6004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  // called after Stop() is called.
6014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
6024a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch
603c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (source == manifest_fetcher_.get()) {
604c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    OnManifestFetchComplete(url, status, response_code, data);
605c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else if (source == extension_fetcher_.get()) {
606c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    OnCRXFetchComplete(url, status, response_code, data);
607c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
608c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED();
609c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
610ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotifyIfFinished();
611c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
612c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
613c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch// Utility class to handle doing xml parsing in a sandboxed utility process.
614c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochclass SafeManifestParser : public UtilityProcessHost::Client {
615c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch public:
616c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Takes ownership of |fetch_data|.
617c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data,
618ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                     base::WeakPtr<ExtensionUpdater> updater)
619c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      : xml_(xml), updater_(updater) {
620ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
621c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    fetch_data_.reset(fetch_data);
622c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
623c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
624c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Posts a task over to the IO loop to start the parsing of xml_ in a
625c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // utility process.
626c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void Start() {
627731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
628ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!BrowserThread::PostTask(
629ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            BrowserThread::IO, FROM_HERE,
630ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen            NewRunnableMethod(
631ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                this, &SafeManifestParser::ParseInSandbox,
632ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                g_browser_process->resource_dispatcher_host()))) {
633ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NOTREACHED();
634ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
635c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
636c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
637c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Creates the sandboxed utility process and tells it to start parsing.
638c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  void ParseInSandbox(ResourceDispatcherHost* rdh) {
639731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
640c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
641c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(asargent) we shouldn't need to do this branch here - instead
642c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // UtilityProcessHost should handle it for us. (http://crbug.com/19192)
643c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    bool use_utility_process = rdh &&
644c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        !CommandLine::ForCurrentProcess()->HasSwitch(switches::kSingleProcess);
645c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (use_utility_process) {
646c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UtilityProcessHost* host = new UtilityProcessHost(
647ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          this, BrowserThread::UI);
648c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      host->StartUpdateManifestParse(xml_);
649c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
650c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UpdateManifest manifest;
651c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (manifest.Parse(xml_)) {
652ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (!BrowserThread::PostTask(
653ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                BrowserThread::UI, FROM_HERE,
654ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                NewRunnableMethod(
655ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    this, &SafeManifestParser::OnParseUpdateManifestSucceeded,
656ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    manifest.results()))) {
657ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          NOTREACHED();
658ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
659c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      } else {
660ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        if (!BrowserThread::PostTask(
661ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                BrowserThread::UI, FROM_HERE,
662ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                NewRunnableMethod(
663ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    this, &SafeManifestParser::OnParseUpdateManifestFailed,
664ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                    manifest.errors()))) {
665ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          NOTREACHED();
666ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        }
667c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
668c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
669c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
670c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
671c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Callback from the utility process when parsing succeeded.
672c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void OnParseUpdateManifestSucceeded(
673c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      const UpdateManifest::Results& results) {
674731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
675ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!updater_) {
676ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
677ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
678ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    updater_->HandleManifestResults(*fetch_data_, &results);
679c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
680c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
681c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Callback from the utility process when parsing failed.
682c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  virtual void OnParseUpdateManifestFailed(const std::string& error_message) {
683731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick    DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
684ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!updater_) {
685ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      return;
686ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    }
687c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    LOG(WARNING) << "Error parsing update manifest:\n" << error_message;
688ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    updater_->HandleManifestResults(*fetch_data_, NULL);
689c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
690c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
691c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch private:
692ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ~SafeManifestParser() {
693ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // If we're using UtilityProcessHost, we may not be destroyed on
694ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // the UI or IO thread.
695ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
696c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
697ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const std::string xml_;
698c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
699ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Should be accessed only on UI thread.
700ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  scoped_ptr<ManifestFetchData> fetch_data_;
701ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  base::WeakPtr<ExtensionUpdater> updater_;
702c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch};
703c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
704c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
70572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsenvoid ExtensionUpdater::OnManifestFetchComplete(
70672a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const GURL& url,
70772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const net::URLRequestStatus& status,
70872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    int response_code,
70972a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    const std::string& data) {
710c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We want to try parsing the manifest, and if it indicates updates are
711c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // available, we want to fire off requests to fetch those updates.
71272a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (status.status() == net::URLRequestStatus::SUCCESS &&
71372a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
714513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    scoped_refptr<SafeManifestParser> safe_parser(
715ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        new SafeManifestParser(data, current_manifest_fetch_.release(),
716ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                               weak_ptr_factory_.GetWeakPtr()));
717c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    safe_parser->Start();
718c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
719c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
720513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    VLOG(1) << "Failed to fetch manifest '" << url.possibly_invalid_spec()
721513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch            << "' response code:" << response_code;
722ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    RemoveFromInProgress(current_manifest_fetch_->extension_ids());
723c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
724c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  manifest_fetcher_.reset();
725c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_manifest_fetch_.reset();
726c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
727c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If we have any pending manifest requests, fire off the next one.
728c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!manifests_pending_.empty()) {
729c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ManifestFetchData* manifest_fetch = manifests_pending_.front();
730c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manifests_pending_.pop_front();
731c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartUpdateCheck(manifest_fetch);
732c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
733c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
734c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
735c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::HandleManifestResults(
736c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const ManifestFetchData& fetch_data,
737ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const UpdateManifest::Results* results) {
738ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(alive_);
739ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
740ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // Remove all the ids's from in_progress_ids_ (we will add them back in
741ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // below if they actually have updates we need to fetch and install).
742ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  RemoveFromInProgress(fetch_data.extension_ids());
743ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
744ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (!results) {
745ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    NotifyIfFinished();
7464a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    return;
747ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
748c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
749c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Examine the parsed manifest and kick off fetches of any new crx files.
750ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::vector<int> updates = DetermineUpdates(fetch_data, *results);
751c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < updates.size(); i++) {
752ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const UpdateManifest::Result* update = &(results->list.at(updates[i]));
753ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    const std::string& id = update->extension_id;
754ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    in_progress_ids_.insert(id);
755ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (id != std::string(kBlacklistAppID))
756ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NotifyUpdateFound(update->extension_id);
757c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FetchUpdatedExtension(update->extension_id, update->crx_url,
758c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        update->package_hash, update->version);
759c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
760c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
761c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the manifest response included a <daystart> element, we want to save
762ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  // that value for any extensions which had sent a ping in the request.
763c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (fetch_data.base_url().DomainIs("google.com") &&
764ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      results->daystart_elapsed_seconds >= 0) {
765c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Time daystart =
766ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Time::Now() - TimeDelta::FromSeconds(results->daystart_elapsed_seconds);
767c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
768c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const std::set<std::string>& extension_ids = fetch_data.extension_ids();
769c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::set<std::string>::const_iterator i;
770c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
771ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (fetch_data.DidPing(*i, ManifestFetchData::ROLLCALL)) {
772c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        if (*i == kBlacklistAppID) {
773ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          extension_prefs_->SetBlacklistLastPingDay(daystart);
774c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        } else if (service_->GetExtensionById(*i, true) != NULL) {
775ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          extension_prefs_->SetLastPingDay(*i, daystart);
776c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
777c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
778ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (extension_prefs_->GetActiveBit(*i)) {
779ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        extension_prefs_->SetActiveBit(*i, false);
780ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        extension_prefs_->SetLastActivePingDay(*i, daystart);
781ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
782c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
783c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
784ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotifyIfFinished();
785c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
786c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
787c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::ProcessBlacklist(const std::string& data) {
7884a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
789c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Verify sha256 hash value.
790ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  char sha256_hash_value[crypto::SHA256_LENGTH];
791ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  crypto::SHA256HashString(data, sha256_hash_value, crypto::SHA256_LENGTH);
7923345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  std::string hash_in_hex = base::HexEncode(sha256_hash_value,
793ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                                            crypto::SHA256_LENGTH);
794c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
795c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (current_extension_fetch_.package_hash != hash_in_hex) {
796c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    NOTREACHED() << "Fetched blacklist checksum is not as expected. "
797c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << "Expected: " << current_extension_fetch_.package_hash
798c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      << " Actual: " << hash_in_hex;
799c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return;
800c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
801c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<std::string> blacklist;
802731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick  base::SplitString(data, '\n', &blacklist);
803c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
804c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Tell ExtensionService to update prefs.
805c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  service_->UpdateExtensionBlacklist(blacklist);
806c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
807c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Update the pref value for blacklist version
808c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs_->SetString(kExtensionBlacklistUpdateVersion,
809c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                    current_extension_fetch_.version);
810c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs_->ScheduleSavePersistentPrefs();
811c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
812c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
813c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::OnCRXFetchComplete(const GURL& url,
81472a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen                                          const net::URLRequestStatus& status,
815c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          int response_code,
816c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          const std::string& data) {
81772a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen  if (status.status() == net::URLRequestStatus::SUCCESS &&
81872a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen      (response_code == 200 || (url.SchemeIsFile() && data.length() > 0))) {
819c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (current_extension_fetch_.id == kBlacklistAppID) {
820c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      ProcessBlacklist(data);
821ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      in_progress_ids_.erase(current_extension_fetch_.id);
822c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    } else {
823c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Successfully fetched - now write crx to a file so we can have the
82421d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      // ExtensionService install it.
825ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      if (!BrowserThread::PostTask(
826ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              BrowserThread::FILE, FROM_HERE,
827ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              NewRunnableMethod(
828ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  file_handler_.get(),
829ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  &ExtensionUpdaterFileHandler::WriteTempFile,
830ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                  current_extension_fetch_.id, data, url))) {
831ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NOTREACHED();
832ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      }
833c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
834c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
835c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // TODO(asargent) do things like exponential backoff, handling
836c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // 503 Service Unavailable / Retry-After headers, etc. here.
837c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // (http://crbug.com/12546).
838513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch    VLOG(1) << "Failed to fetch extension '" << url.possibly_invalid_spec()
839513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch            << "' response code:" << response_code;
840c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
841c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  extension_fetcher_.reset();
842c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  current_extension_fetch_ = ExtensionFetch();
843c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
844c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If there are any pending downloads left, start one.
845dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen  if (!extensions_pending_.empty()) {
846c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ExtensionFetch next = extensions_pending_.front();
847c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    extensions_pending_.pop_front();
848c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    FetchUpdatedExtension(next.id, next.url, next.package_hash, next.version);
849c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
850c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
851c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
852c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::OnCRXFileWritten(const std::string& id,
853c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        const FilePath& path,
854c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                        const GURL& download_url) {
855ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(alive_);
85621d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  // The ExtensionService is now responsible for cleaning up the temp file
857c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // at |path|.
858c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  service_->UpdateExtension(id, path, download_url);
859ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  in_progress_ids_.erase(id);
860ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotifyIfFinished();
861c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
862c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
863ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::OnCRXFileWriteError(const std::string& id) {
864ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(alive_);
865ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  in_progress_ids_.erase(id);
866ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotifyIfFinished();
867ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
868c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
869c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
8704a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
871c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(!timer_.IsRunning());
872c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  DCHECK(target_delay >= TimeDelta::FromSeconds(1));
873c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
874c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Add +/- 10% random jitter.
875c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double delay_ms = target_delay.InMillisecondsF();
876c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  double jitter_factor = (RandDouble() * .2) - 0.1;
877c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  delay_ms += delay_ms * jitter_factor;
878c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  TimeDelta actual_delay = TimeDelta::FromMilliseconds(
879c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      static_cast<int64>(delay_ms));
880c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
881c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Save the time of next check.
882c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  Time next = Time::Now() + actual_delay;
883c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs_->SetInt64(kNextExtensionsUpdateCheck, next.ToInternalValue());
884c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs_->ScheduleSavePersistentPrefs();
885c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
886c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  timer_.Start(actual_delay, this, &ExtensionUpdater::TimerFired);
887c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
888c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
889c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::TimerFired() {
8904a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
891c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  CheckNow();
892c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
893c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // If the user has overridden the update frequency, don't bother reporting
894c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // this.
89521d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen  if (frequency_seconds_ == ExtensionService::kDefaultUpdateFrequencySeconds) {
896c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    Time last = Time::FromInternalValue(prefs_->GetInt64(
897c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        kLastExtensionsUpdateCheck));
898c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (last.ToInternalValue() != 0) {
899c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // Use counts rather than time so we can use minutes rather than millis.
900c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
901c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          (Time::Now() - last).InMinutes(),
902c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          base::TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
903c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          base::TimeDelta::FromDays(40).InMinutes(),
904c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          50);  // 50 buckets seems to be the default.
905c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
906c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
907c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
908c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Save the last check time, and schedule the next check.
909c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  int64 now = Time::Now().ToInternalValue();
910c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  prefs_->SetInt64(kLastExtensionsUpdateCheck, now);
911c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
912c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
913c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
914ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::CheckSoon() {
915ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(alive_);
916ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (will_check_soon_) {
917ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    return;
918ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
919ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (BrowserThread::PostTask(
920ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          BrowserThread::UI, FROM_HERE,
921ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen          method_factory_.NewRunnableMethod(
922ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen              &ExtensionUpdater::DoCheckSoon))) {
923ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    will_check_soon_ = true;
924ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  } else {
925ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    NOTREACHED();
926ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
927ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
928ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
929ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenbool ExtensionUpdater::WillCheckSoon() const {
930ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  return will_check_soon_;
931ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
932ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
933ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::DoCheckSoon() {
934ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  DCHECK(will_check_soon_);
935ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  CheckNow();
936ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  will_check_soon_ = false;
937ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
938ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
939c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::CheckNow() {
9404a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
941ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotifyStarted();
942ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  ManifestFetchesBuilder fetches_builder(service_, extension_prefs_);
943c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
944c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  const ExtensionList* extensions = service_->extensions();
945c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (ExtensionList::const_iterator iter = extensions->begin();
946c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       iter != extensions->end(); ++iter) {
947c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    fetches_builder.AddExtension(**iter);
948c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
949c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
950ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  const PendingExtensionManager* pending_extension_manager =
951ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      service_->pending_extension_manager();
952ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
953ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PendingExtensionManager::const_iterator iter;
954ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (iter = pending_extension_manager->begin();
955ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen       iter != pending_extension_manager->end(); iter++) {
956ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // TODO(skerner): Move the determination of what gets fetched into
957ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    // class PendingExtensionManager.
958dc0f95d653279beabeb9817299e2902918ba123eKristian Monsen    Extension::Location location = iter->second.install_source();
95921d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen    if (location != Extension::EXTERNAL_PREF &&
96021d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen        location != Extension::EXTERNAL_REGISTRY)
96121d179b334e59e9a3bfcaed4c4430bef1bc5759dKristian Monsen      fetches_builder.AddPendingExtension(iter->first, iter->second);
962c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
963c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
964c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  fetches_builder.ReportStats();
965c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
966c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<ManifestFetchData*> fetches(fetches_builder.GetFetches());
967c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
968c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Start a fetch of the blacklist if needed.
969ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (blacklist_checks_enabled_) {
97072a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    // Note: it is very important that we use  the https version of the update
971201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // url here to avoid DNS hijacking of the blacklist, which is not validated
972201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch    // by a public key signature like .crx files are.
973c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    ManifestFetchData* blacklist_fetch =
974201ade2fbba22bfb27ae029f4d23fca6ded109a0Ben Murdoch        new ManifestFetchData(Extension::GalleryUpdateUrl(true));
975c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    std::string version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
976ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ManifestFetchData::PingData ping_data;
977ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    ping_data.rollcall_days =
978ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        CalculatePingDays(extension_prefs_->BlacklistLastPingDay());
979ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    blacklist_fetch->AddExtension(kBlacklistAppID, version, ping_data, "");
980c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartUpdateCheck(blacklist_fetch);
981c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
982c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
983c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // Now start fetching regular extension updates
984c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::vector<ManifestFetchData*>::const_iterator it = fetches.begin();
985c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       it != fetches.end(); ++it) {
986c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // StartUpdateCheck makes sure the url isn't already downloading or
987c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // scheduled, so we don't need to check before calling it. Ownership of
988c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // fetch is transferred here.
989c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    StartUpdateCheck(*it);
990c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
991c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // We don't want to use fetches after this since StartUpdateCheck()
992c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // takes ownership of its argument.
993c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  fetches.clear();
994ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
995ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotifyIfFinished();
996c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
997c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
998c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochbool ExtensionUpdater::GetExistingVersion(const std::string& id,
999c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                          std::string* version) {
10004a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
1001c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (id == kBlacklistAppID) {
1002c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    *version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
1003c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return true;
1004c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1005513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch  const Extension* extension = service_->GetExtensionById(id, false);
1006c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (!extension) {
1007c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    return false;
1008c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1009c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  *version = extension->version()->GetString();
1010c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return true;
1011c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1012c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1013c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochstd::vector<int> ExtensionUpdater::DetermineUpdates(
1014c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const ManifestFetchData& fetch_data,
1015c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const UpdateManifest::Results& possible_updates) {
10164a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  DCHECK(alive_);
1017c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::vector<int> result;
1018c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1019c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // This will only get set if one of possible_updates specifies
1020c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  // browser_min_version.
1021c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  scoped_ptr<Version> browser_version;
1022ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  PendingExtensionManager* pending_extension_manager =
1023ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      service_->pending_extension_manager();
1024c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1025c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (size_t i = 0; i < possible_updates.list.size(); i++) {
1026c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    const UpdateManifest::Result* update = &possible_updates.list[i];
1027c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1028c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (!fetch_data.Includes(update->extension_id))
1029c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      continue;
1030c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1031ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    if (!pending_extension_manager->IsIdPending(update->extension_id)) {
1032c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // If we're not installing pending extension, and the update
1033c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // version is the same or older than what's already installed,
1034c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // we don't want it.
1035c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      std::string version;
1036c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!GetExistingVersion(update->extension_id, &version))
1037c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
1038c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1039c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      scoped_ptr<Version> existing_version(
1040c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          Version::GetVersionFromString(version));
1041c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      scoped_ptr<Version> update_version(
1042c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          Version::GetVersionFromString(update->version));
1043c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1044c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!update_version.get() ||
1045c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          update_version->CompareTo(*(existing_version.get())) <= 0) {
1046c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
1047c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
1048c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1049c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1050c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    // If the update specifies a browser minimum version, do we qualify?
1051c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (update->browser_min_version.length() > 0) {
1052c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // First determine the browser version if we haven't already.
1053c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (!browser_version.get()) {
10543345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        chrome::VersionInfo version_info;
10553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick        if (version_info.is_valid()) {
1056c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          browser_version.reset(Version::GetVersionFromString(
10573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick                                    version_info.Version()));
1058c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        }
1059c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
1060c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      scoped_ptr<Version> browser_min_version(
1061c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          Version::GetVersionFromString(update->browser_min_version));
1062c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      if (browser_version.get() && browser_min_version.get() &&
1063c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch          browser_min_version->CompareTo(*browser_version.get()) > 0) {
1064c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // TODO(asargent) - We may want this to show up in the extensions UI
1065c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        // eventually. (http://crbug.com/12547).
1066c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        LOG(WARNING) << "Updated version of extension " << update->extension_id
1067ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                     << " available, but requires chrome version "
1068ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen                     << update->browser_min_version;
1069c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        continue;
1070c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      }
1071c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1072c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    result.push_back(i);
1073c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1074c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  return result;
1075c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1076c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1077c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) {
1078ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  AddToInProgress(fetch_data->extension_ids());
1079ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
10804a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch  scoped_ptr<ManifestFetchData> scoped_fetch_data(fetch_data);
10813345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick  if (CommandLine::ForCurrentProcess()->HasSwitch(
10823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick      switches::kDisableBackgroundNetworking))
10833345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick    return;
10843345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick
1085c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  std::deque<ManifestFetchData*>::const_iterator i;
1086c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
1087c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (fetch_data->full_url() == (*i)->full_url()) {
1088c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      // This url is already scheduled to be fetched.
1089c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;
1090c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1091c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1092c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1093c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (manifest_fetcher_.get() != NULL) {
1094c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (manifest_fetcher_->url() != fetch_data->full_url()) {
10954a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch      manifests_pending_.push_back(scoped_fetch_data.release());
1096c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1097c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1098c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
1099c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        fetch_data->full_url().possibly_invalid_spec().length());
1100c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
11014a5e2dc747d50c653511c68ccb2cfbfb740bd5a7Ben Murdoch    current_manifest_fetch_.swap(scoped_fetch_data);
1102c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manifest_fetcher_.reset(
1103c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        URLFetcher::Create(kManifestFetcherId, fetch_data->full_url(),
1104c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                           URLFetcher::GET, this));
110572a454cd3513ac24fbdd0e0cb9ad70b86a99b801Kristian Monsen    manifest_fetcher_->set_request_context(
1106ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        profile_->GetRequestContext());
1107c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
1108513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                      net::LOAD_DO_NOT_SAVE_COOKIES |
1109513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                      net::LOAD_DISABLE_CACHE);
1110c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    manifest_fetcher_->Start();
1111c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1112c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1113c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1114c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdochvoid ExtensionUpdater::FetchUpdatedExtension(const std::string& id,
1115c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             const GURL& url,
1116c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             const std::string& hash,
1117c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch                                             const std::string& version) {
1118c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  for (std::deque<ExtensionFetch>::const_iterator iter =
1119c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch           extensions_pending_.begin();
1120c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch       iter != extensions_pending_.end(); ++iter) {
1121c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (iter->id == id || iter->url == url) {
1122c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      return;  // already scheduled
1123c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1124c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1125c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch
1126c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  if (extension_fetcher_.get() != NULL) {
1127c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    if (extension_fetcher_->url() != url) {
1128c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch      extensions_pending_.push_back(ExtensionFetch(id, url, hash, version));
1129c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    }
1130c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  } else {
1131c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    extension_fetcher_.reset(
1132c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch        URLFetcher::Create(kExtensionFetcherId, url, URLFetcher::GET, this));
1133c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    extension_fetcher_->set_request_context(
1134ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        profile_->GetRequestContext());
1135c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    extension_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
1136513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                       net::LOAD_DO_NOT_SAVE_COOKIES |
1137513209b27ff55e2841eac0e4120199c23acce758Ben Murdoch                                       net::LOAD_DISABLE_CACHE);
1138c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    extension_fetcher_->Start();
1139c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch    current_extension_fetch_ = ExtensionFetch(id, url, hash, version);
1140c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch  }
1141c407dc5cd9bdc5668497f21b26b09d988ab439deBen Murdoch}
1142ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1143ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::NotifyStarted() {
1144ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotificationService::current()->Notify(
1145ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NotificationType::EXTENSION_UPDATING_STARTED,
1146ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Source<Profile>(profile_),
1147ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NotificationService::NoDetails());
1148ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
1149ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1150ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::NotifyUpdateFound(const std::string& extension_id) {
1151ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  NotificationService::current()->Notify(
1152ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      NotificationType::EXTENSION_UPDATE_FOUND,
1153ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Source<Profile>(profile_),
1154ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen      Details<const std::string>(&extension_id));
1155ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
1156ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1157ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::NotifyIfFinished() {
1158ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  if (in_progress_ids_.empty()) {
1159ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    NotificationService::current()->Notify(
1160ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NotificationType::EXTENSION_UPDATING_FINISHED,
1161ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        Source<Profile>(profile_),
1162ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen        NotificationService::NoDetails());
1163ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    VLOG(1) << "Sending EXTENSION_UPDATING_FINISHED";
1164ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  }
1165ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
1166ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1167ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::AddToInProgress(const std::set<std::string>& ids) {
1168ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::set<std::string>::const_iterator i;
1169ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (i = ids.begin(); i != ids.end(); ++i)
1170ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    in_progress_ids_.insert(*i);
1171ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
1172ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen
1173ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsenvoid ExtensionUpdater::RemoveFromInProgress(const std::set<std::string>& ids) {
1174ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  std::set<std::string>::const_iterator i;
1175ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen  for (i = ids.begin(); i != ids.end(); ++i)
1176ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen    in_progress_ids_.erase(*i);
1177ddb351dbec246cf1fab5ec20d2d5520909041de1Kristian Monsen}
1178