summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-28 05:58:05 +0000
committerasargent@chromium.org <asargent@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-01-28 05:58:05 +0000
commit5ef47ec0d8278b6ff2c663299a41fa28e474a896 (patch)
treed2308c829db73df9db6306a90ab5c363a35fe190 /chrome
parentfb40ec7a037ca4da04eb2d3eafe418bd18df59a1 (diff)
downloadchromium_src-5ef47ec0d8278b6ff2c663299a41fa28e474a896.zip
chromium_src-5ef47ec0d8278b6ff2c663299a41fa28e474a896.tar.gz
chromium_src-5ef47ec0d8278b6ff2c663299a41fa28e474a896.tar.bz2
Refactor extension autoupdater.
This includes two changes: 1) Stop sending the Omaha UID to the gallery in favor of a "ping" parameter included at most once per day in a update manifest fetch, indicating the number of days since the last time we sent the ping parameter. The calculation of this parameter is based on the offset from now to a value in the *previous* response from the server where it indicated it's notion of when that day had started. 2) Batch update manifest requests for extensions with the same update url. The server and protocol have supported this for a while but this is the first time we've added support in the client. BUG=b/2367191 TEST=Extension updates should still work normally. Review URL: http://codereview.chromium.org/558005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@37383 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/extensions/extension_prefs.cc31
-rw-r--r--chrome/browser/extensions/extension_prefs.h10
-rw-r--r--chrome/browser/extensions/extension_updater.cc339
-rw-r--r--chrome/browser/extensions/extension_updater.h100
-rw-r--r--chrome/browser/extensions/extension_updater_unittest.cc127
-rw-r--r--chrome/browser/extensions/extensions_service.cc12
-rw-r--r--chrome/browser/extensions/extensions_service.h11
-rw-r--r--chrome/browser/utility_process_host.h2
-rw-r--r--chrome/common/extensions/update_manifest.cc24
-rw-r--r--chrome/common/extensions/update_manifest.h18
-rw-r--r--chrome/common/extensions/update_manifest_unittest.cc38
-rw-r--r--chrome/common/utility_messages.h20
-rw-r--r--chrome/common/utility_messages_internal.h2
13 files changed, 506 insertions, 228 deletions
diff --git a/chrome/browser/extensions/extension_prefs.cc b/chrome/browser/extensions/extension_prefs.cc
index af58426..6d92ad2 100644
--- a/chrome/browser/extensions/extension_prefs.cc
+++ b/chrome/browser/extensions/extension_prefs.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "chrome/browser/extensions/extension_prefs.h"
-
#include "base/string_util.h"
+#include "chrome/browser/extensions/extension_prefs.h"
#include "chrome/common/extensions/extension.h"
+using base::Time;
+
namespace {
// Preferences keys
@@ -44,6 +45,10 @@ const wchar_t kExtensionShelf[] = L"extensions.shelf";
// object stored in the Preferences file. The extensions are stored by ID.
const wchar_t kExtensionToolbar[] = L"extensions.toolbar";
+// The key for a serialized Time value indicating the start of the day (from the
+// server's perspective) an extension last included a "ping" parameter during
+// its update check.
+const wchar_t kLastPingDay[] = L"lastpingday";
}
////////////////////////////////////////////////////////////////////////////////
@@ -231,6 +236,28 @@ void ExtensionPrefs::UpdateBlacklist(
return;
}
+Time ExtensionPrefs::LastPingDay(const std::string& extension_id) {
+ DictionaryValue* dictionary = GetExtensionPref(extension_id);
+ if (dictionary && dictionary->HasKey(kLastPingDay)) {
+ std::string string_value;
+ int64 value;
+ dictionary->GetString(kLastPingDay, &string_value);
+ if (StringToInt64(string_value, &value)) {
+ return Time::FromInternalValue(value);
+ }
+ }
+ return Time();
+}
+
+void ExtensionPrefs::SetLastPingDay(const std::string& extension_id,
+ const Time& time) {
+ std::string value = Int64ToString(time.ToInternalValue());
+ UpdateExtensionPref(extension_id, kLastPingDay,
+ Value::CreateStringValue(value));
+ prefs_->ScheduleSavePersistentPrefs();
+}
+
+
void ExtensionPrefs::GetKilledExtensionIds(std::set<std::string>* killed_ids) {
const DictionaryValue* dict = prefs_->GetDictionary(kExtensionsPref);
if (!dict || dict->empty())
diff --git a/chrome/browser/extensions/extension_prefs.h b/chrome/browser/extensions/extension_prefs.h
index e976b53..d8d3694 100644
--- a/chrome/browser/extensions/extension_prefs.h
+++ b/chrome/browser/extensions/extension_prefs.h
@@ -11,6 +11,7 @@
#include "base/linked_ptr.h"
#include "base/task.h"
+#include "base/time.h"
#include "chrome/common/extensions/extension.h"
#include "chrome/common/pref_service.h"
#include "googleurl/src/gurl.h"
@@ -85,6 +86,15 @@ class ExtensionPrefs {
// Did the extension ask to escalate its permission during an upgrade?
bool DidExtensionEscalatePermissions(const std::string& id);
+ // Returns the last value set via SetLastPingDay. If there isn't such a
+ // pref, the returned Time will return true for is_null().
+ base::Time LastPingDay(const std::string& extension_id);
+
+ // The time stored is based on the server's perspective of day start time, not
+ // the client's.
+ void SetLastPingDay(const std::string& extension_id, const base::Time& time);
+
+
// Saves ExtensionInfo for each installed extension with the path to the
// version directory and the location. Blacklisted extensions won't be saved
// and neither will external extensions the user has explicitly uninstalled.
diff --git a/chrome/browser/extensions/extension_updater.cc b/chrome/browser/extensions/extension_updater.cc
index da55735..7567039 100644
--- a/chrome/browser/extensions/extension_updater.cc
+++ b/chrome/browser/extensions/extension_updater.cc
@@ -11,8 +11,8 @@
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/rand_util.h"
-#include "base/scoped_vector.h"
#include "base/sha2.h"
+#include "base/stl_util-inl.h"
#include "base/string_util.h"
#include "base/time.h"
#include "base/thread.h"
@@ -54,8 +54,6 @@ const char* ExtensionUpdater::kBlacklistUpdateUrl =
// Update AppID for extension blacklist.
const char* ExtensionUpdater::kBlacklistAppID = "com.google.crx.blacklist";
-const char* ExtensionUpdater::kUidKey = "uid";
-
// Wait at least 5 minutes after browser startup before we do any checks. If you
// change this value, make sure to update comments where it is used.
const int kStartupWaitSeconds = 60 * 5;
@@ -64,6 +62,82 @@ const int kStartupWaitSeconds = 60 * 5;
static const int kMinUpdateFrequencySeconds = 30;
static const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
+// Maximum length of an extension manifest update check url, since it is a GET
+// request. We want to stay under 2K because of proxies, etc.
+static const int kExtensionsManifestMaxURLSize = 2000;
+
+
+// The format for request parameters in update checks is:
+//
+// ?x=EXT1_INFO&x=EXT2_INFO
+//
+// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
+//
+// id=EXTENSION_ID&v=VERSION&uc
+//
+// Additionally, we may include the parameter ping=PING_DATA where PING_DATA
+// looks like r=DAYS for extensions in the Chrome extensions gallery. This value
+// will be present at most once every 24 hours, and indicate the number of days
+// since the last time it was present in an update check.
+//
+// So for two extensions like:
+// Extension 1- id:aaaa version:1.1
+// Extension 2- id:bbbb version:2.0
+//
+// the full update url would be:
+// http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
+//
+// (Note that '=' is %3D and '&' is %26 when urlencoded.)
+bool ManifestFetchData::AddExtension(std::string id, std::string version,
+ int days) {
+ if (extension_ids_.find(id) != extension_ids_.end()) {
+ NOTREACHED() << "Duplicate extension id " << id;
+ return false;
+ }
+
+ // Compute the string we'd append onto the full_url_, and see if it fits.
+ std::vector<std::string> parts;
+ parts.push_back("id=" + id);
+ parts.push_back("v=" + version);
+ parts.push_back("uc");
+
+ if (ShouldPing(days)) {
+ parts.push_back("ping=" + EscapeQueryParamValue("r=" + IntToString(days),
+ true));
+ }
+
+ std::string extra = full_url_.has_query() ? "&" : "?";
+ extra += "x=" + EscapeQueryParamValue(JoinString(parts, '&'), true);
+
+ // Check against our max url size, exempting the first extension added.
+ int new_size = full_url_.possibly_invalid_spec().size() + extra.size();
+ if (extension_ids_.size() > 0 && new_size > kExtensionsManifestMaxURLSize) {
+ UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 1);
+ return false;
+ }
+ UMA_HISTOGRAM_PERCENTAGE("Extensions.UpdateCheckHitUrlSizeLimit", 0);
+
+ // We have room so go ahead and add the extension.
+ extension_ids_.insert(id);
+ ping_days_[id] = days;
+ full_url_ = GURL(full_url_.possibly_invalid_spec() + extra);
+ return true;
+}
+
+bool ManifestFetchData::DidPing(std::string extension_id) const {
+ std::map<std::string, int>::const_iterator i = ping_days_.find(extension_id);
+ if (i != ping_days_.end()) {
+ return ShouldPing(i->second);
+ }
+ return false;
+}
+
+bool ManifestFetchData::ShouldPing(int days) const {
+ return base_url_.DomainIs("google.com") &&
+ (days == kNeverPinged || days > 0);
+}
+
+
// A utility class to do file handling on the file I/O thread.
class ExtensionUpdaterFileHandler
: public base::RefCountedThreadSafe<ExtensionUpdaterFileHandler> {
@@ -110,55 +184,12 @@ class ExtensionUpdaterFileHandler
~ExtensionUpdaterFileHandler() {}
};
-class DefaultUidProvider : public ExtensionUpdater::UidProvider {
- public:
- DefaultUidProvider() {}
- virtual ~DefaultUidProvider() {}
-
- virtual std::string GetUidString() {
- std::string result;
-#if defined(OS_WIN)
- // First try looking in HKCU, then try HKLM.
- HKEY rootKeys[] = { HKEY_CURRENT_USER, HKEY_LOCAL_MACHINE };
- for (int i = 0; i < sizeof(rootKeys); i++) {
- RegKey key(rootKeys[i], L"Software\\Google\\Update");
- std::wstring value;
- if (key.ReadValue(L"ui", &value)) {
- if (IsStringASCII(value) &&
- value.length() <= UidProvider::maxUidLength) {
- result = WideToASCII(value);
- break;
- } else {
- NOTREACHED();
- }
- }
- }
-#elif defined(OS_MACOSX)
- CFStringRef guid = (CFStringRef)
- CFPreferencesCopyAppValue(CFSTR("GUID"),
- CFSTR("com.google.Keystone.Agent"));
- if (guid) {
- std::string value = base::SysCFStringRefToUTF8(guid);
- if (IsStringASCII(value) &&
- value.length() <= UidProvider::maxUidLength) {
- result = value;
- } else {
- NOTREACHED();
- }
- CFRelease(guid);
- }
-#endif
- return result;
- }
-};
-
-
ExtensionUpdater::ExtensionUpdater(ExtensionUpdateService* service,
PrefService* prefs,
int frequency_seconds)
: service_(service), frequency_seconds_(frequency_seconds),
prefs_(prefs), file_handler_(new ExtensionUpdaterFileHandler()),
- blacklist_checks_enabled_(true), uid_provider_(new DefaultUidProvider()) {
+ blacklist_checks_enabled_(true) {
Init();
}
@@ -172,7 +203,9 @@ void ExtensionUpdater::Init() {
frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
}
-ExtensionUpdater::~ExtensionUpdater() {}
+ExtensionUpdater::~ExtensionUpdater() {
+ STLDeleteElements(&manifests_pending_);
+}
static void EnsureInt64PrefRegistered(PrefService* prefs,
const wchar_t name[]) {
@@ -261,8 +294,11 @@ void ExtensionUpdater::OnURLFetchComplete(
// Utility class to handle doing xml parsing in a sandboxed utility process.
class SafeManifestParser : public UtilityProcessHost::Client {
public:
- SafeManifestParser(const std::string& xml, ExtensionUpdater* updater)
+ // Takes ownership of |fetch_data|.
+ SafeManifestParser(const std::string& xml, ManifestFetchData* fetch_data,
+ ExtensionUpdater* updater)
: xml_(xml), updater_(updater) {
+ fetch_data_.reset(fetch_data);
}
// Posts a task over to the IO loop to start the parsing of xml_ in a
@@ -308,9 +344,9 @@ class SafeManifestParser : public UtilityProcessHost::Client {
// Callback from the utility process when parsing succeeded.
virtual void OnParseUpdateManifestSucceeded(
- const UpdateManifest::ResultList& list) {
+ const UpdateManifest::Results& results) {
DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
- updater_->HandleManifestResults(list);
+ updater_->HandleManifestResults(*fetch_data_, results);
}
// Callback from the utility process when parsing failed.
@@ -323,6 +359,7 @@ class SafeManifestParser : public UtilityProcessHost::Client {
~SafeManifestParser() {}
const std::string& xml_;
+ scoped_ptr<ManifestFetchData> fetch_data_;
scoped_refptr<ExtensionUpdater> updater_;
};
@@ -336,7 +373,7 @@ void ExtensionUpdater::OnManifestFetchComplete(const GURL& url,
// available, we want to fire off requests to fetch those updates.
if (status.status() == URLRequestStatus::SUCCESS && response_code == 200) {
scoped_refptr<SafeManifestParser> safe_parser =
- new SafeManifestParser(data, this);
+ new SafeManifestParser(data, current_manifest_fetch_.release(), this);
safe_parser->Start();
} else {
// TODO(asargent) Do exponential backoff here. (http://crbug.com/12546).
@@ -344,23 +381,44 @@ void ExtensionUpdater::OnManifestFetchComplete(const GURL& url,
"' response code:" << response_code;
}
manifest_fetcher_.reset();
+ current_manifest_fetch_.reset();
// If we have any pending manifest requests, fire off the next one.
if (!manifests_pending_.empty()) {
- GURL url = manifests_pending_.front();
+ ManifestFetchData* manifest_fetch = manifests_pending_.front();
manifests_pending_.pop_front();
- StartUpdateCheck(url);
+ StartUpdateCheck(manifest_fetch);
}
}
void ExtensionUpdater::HandleManifestResults(
- const UpdateManifest::ResultList& results) {
- std::vector<int> updates = DetermineUpdates(results);
+ const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results& results) {
+
+ // Examine the parsed manifest and kick off fetches of any new crx files.
+ std::vector<int> updates = DetermineUpdates(fetch_data, results);
for (size_t i = 0; i < updates.size(); i++) {
- const UpdateManifest::Result* update = &(results.at(updates[i]));
+ const UpdateManifest::Result* update = &(results.list.at(updates[i]));
FetchUpdatedExtension(update->extension_id, update->crx_url,
update->package_hash, update->version);
}
+
+ // If the manifest response included a <daystart> element, we want to save
+ // that value for any extensions which had sent ping_days in the request.
+ if (fetch_data.base_url().DomainIs("google.com") &&
+ results.daystart_elapsed_seconds >= 0) {
+ Time daystart =
+ Time::Now() - TimeDelta::FromSeconds(results.daystart_elapsed_seconds);
+
+ const std::set<std::string>& extension_ids = fetch_data.extension_ids();
+ std::set<std::string>::const_iterator i;
+ for (i = extension_ids.begin(); i != extension_ids.end(); i++) {
+ bool did_ping = fetch_data.DidPing(*i);
+ if (did_ping && service_->GetExtensionById(*i, true)) {
+ service_->SetLastPingDay(*i, daystart);
+ }
+ }
+ }
}
void ExtensionUpdater::ProcessBlacklist(const std::string& data) {
@@ -440,49 +498,6 @@ void ExtensionUpdater::OnExtensionInstallFinished(const FilePath& path,
file_handler_.get(), &ExtensionUpdaterFileHandler::DeleteFile, path));
}
-
-// Helper function for building up request parameters in update check urls. It
-// appends information about one extension to a request parameter string. The
-// format for request parameters in update checks is:
-//
-// ?x=EXT1_INFO&x=EXT2_INFO
-//
-// where EXT1_INFO and EXT2_INFO are url-encoded strings of the form:
-//
-// id=EXTENSION_ID&v=VERSION&uc
-//
-// So for two extensions like:
-// Extension 1- id:aaaa version:1.1
-// Extension 2- id:bbbb version:2.0
-//
-// the full update url would be:
-// http://somehost/path?x=id%3Daaaa%26v%3D1.1%26uc&x=id%3Dbbbb%26v%3D2.0%26uc
-//
-// (Note that '=' is %3D and '&' is %26 when urlencoded.)
-//
-// Again, this function would just append one extension's worth of data, e.g.
-// "x=id%3Daaaa%26v%3D1.1%26uc"
-void AppendExtensionInfo(std::string* str, const Extension& extension) {
- const Version* version = extension.version();
- DCHECK(version);
- std::vector<std::string> parts;
-
- // Push extension id, version, and uc (indicates an update check to Omaha).
- parts.push_back("id=" + extension.id());
- parts.push_back("v=" + version->GetString());
- parts.push_back("uc");
-
- str->append("x=" + EscapeQueryParamValue(JoinString(parts, '&'), true));
-}
-
-// Creates a blacklist update url.
-GURL ExtensionUpdater::GetBlacklistUpdateUrl(const std::wstring& version) {
- std::string blklist_info = StringPrintf("id=%s&v=%s&uc", kBlacklistAppID,
- WideToASCII(version).c_str());
- return GURL(StringPrintf("%s?x=%s", kBlacklistUpdateUrl,
- EscapeQueryParamValue(blklist_info, true).c_str()));
-}
-
void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
DCHECK(!timer_.IsRunning());
DCHECK(target_delay >= TimeDelta::FromSeconds(1));
@@ -527,13 +542,11 @@ void ExtensionUpdater::TimerFired() {
}
void ExtensionUpdater::CheckNow() {
- // Generate a set of update urls for loaded extensions.
- std::set<GURL> urls;
-
- if (blacklist_checks_enabled_ && service_->HasInstalledExtensions()) {
- urls.insert(GetBlacklistUpdateUrl(
- prefs_->GetString(kExtensionBlacklistUpdateVersion)));
- }
+ // List of data on fetches we're going to do. We limit the number of
+ // extensions grouped together in one batch to avoid running into the limits
+ // on the length of http GET requests, so there might be multiple
+ // ManifestFetchData* objects with the same base_url.
+ std::multimap<GURL, ManifestFetchData*> fetches;
int no_url_count = 0;
int google_url_count = 0;
@@ -545,9 +558,9 @@ void ExtensionUpdater::CheckNow() {
iter != extensions->end(); ++iter) {
Extension* extension = (*iter);
const GURL& update_url = extension->update_url();
- const std::string google_suffix = ".google.com";
- size_t suffix_index = update_url.host().length() - google_suffix.length();
- if (update_url.host().find(google_suffix) == suffix_index) {
+
+ // Collect histogram data and skip extensions with no update url.
+ if (update_url.DomainIs("google.com")) {
google_url_count++;
} else if (update_url.is_empty() || extension->id().empty()) {
// TODO(asargent) when a default URL is added, make sure to update
@@ -563,36 +576,49 @@ void ExtensionUpdater::CheckNow() {
theme_count++;
DCHECK(update_url.is_valid());
- DCHECK(!update_url.has_ref());
- // Append extension information to the url.
- std::string full_url_string = update_url.spec();
- full_url_string.append(update_url.has_query() ? "&" : "?");
- AppendExtensionInfo(&full_url_string, *extension);
-
- // Send the Omaha uid when doing update checks to Omaha.
- if (update_url.DomainIs("google.com")) {
- std::string uid = uid_provider_->GetUidString();
- if (uid.length() > 0 && uid.length() <= UidProvider::maxUidLength) {
- full_url_string.append("&" + std::string(ExtensionUpdater::kUidKey) +
- "=" + EscapeQueryParamValue(uid, true));
+ ManifestFetchData* fetch = NULL;
+ std::multimap<GURL, ManifestFetchData*>::iterator existing_iter =
+ fetches.find(update_url);
+
+ // Find or create a ManifestFetchData to add this extension to.
+ std::string id = extension->id();
+ std::string version = extension->VersionString();
+ int ping_days = CalculatePingDays(extension->id());
+ while (existing_iter != fetches.end()) {
+ if (existing_iter->second->AddExtension(id, version, ping_days)) {
+ fetch = existing_iter->second;
+ break;
}
+ existing_iter++;
}
-
- GURL full_url(full_url_string);
- if (!full_url.is_valid()) {
- LOG(ERROR) << "invalid url: " << full_url.possibly_invalid_spec();
- NOTREACHED();
- } else {
- urls.insert(full_url);
+ if (!fetch) {
+ fetch = new ManifestFetchData(update_url);
+ fetches.insert(std::pair<GURL, ManifestFetchData*>(update_url, fetch));
+ bool added = fetch->AddExtension(id, version, ping_days);
+ DCHECK(added);
}
}
- // Now do an update check for each url we found.
- for (std::set<GURL>::iterator iter = urls.begin(); iter != urls.end();
- ++iter) {
+
+ // Start a fetch of the blacklist if needed.
+ if (blacklist_checks_enabled_ && service_->HasInstalledExtensions()) {
+ ManifestFetchData* blacklist_fetch =
+ new ManifestFetchData(GURL(kBlacklistUpdateUrl));
+ std::wstring version = prefs_->GetString(kExtensionBlacklistUpdateVersion);
+ blacklist_fetch->AddExtension(kBlacklistAppID, WideToASCII(version), 0);
+ StartUpdateCheck(blacklist_fetch);
+ }
+
+ // Now start fetching regular extension updates
+ std::multimap<GURL, ManifestFetchData*>::iterator i = fetches.begin();
+ while (i != fetches.end()) {
+ ManifestFetchData *fetch = i->second;
+
// StartUpdateCheck makes sure the url isn't already downloading or
- // scheduled, so we don't need to check before calling it.
- StartUpdateCheck(*iter);
+ // scheduled, so we don't need to check before calling it. Ownership of
+ // fetch is transferred here.
+ StartUpdateCheck(fetch);
+ fetches.erase(i++);
}
UMA_HISTOGRAM_COUNTS_100("Extensions.UpdateCheckExtensions",
@@ -607,6 +633,14 @@ void ExtensionUpdater::CheckNow() {
no_url_count);
}
+int ExtensionUpdater::CalculatePingDays(const std::string& extension_id) {
+ int days = ManifestFetchData::kNeverPinged;
+ Time last_ping_day = service_->LastPingDay(extension_id);
+ if (!last_ping_day.is_null()) {
+ days = (Time::Now() - last_ping_day).InDays();
+ }
+ return days;
+}
bool ExtensionUpdater::GetExistingVersion(const std::string& id,
std::string* version) {
@@ -624,21 +658,23 @@ bool ExtensionUpdater::GetExistingVersion(const std::string& id,
}
std::vector<int> ExtensionUpdater::DetermineUpdates(
- const std::vector<UpdateManifest::Result>& possible_updates) {
-
+ const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results& possible_updates) {
std::vector<int> result;
// This will only get set if one of possible_updates specifies
// browser_min_version.
scoped_ptr<Version> browser_version;
- for (size_t i = 0; i < possible_updates.size(); i++) {
- const UpdateManifest::Result* update = &possible_updates[i];
+ for (size_t i = 0; i < possible_updates.list.size(); i++) {
+ const UpdateManifest::Result* update = &possible_updates.list[i];
+
+ if (!fetch_data.Includes(update->extension_id))
+ continue;
std::string version;
- if (!GetExistingVersion(update->extension_id, &version)) {
+ if (!GetExistingVersion(update->extension_id, &version))
continue;
- }
// If the update version is the same or older than what's already installed,
// we don't want it.
@@ -681,19 +717,28 @@ std::vector<int> ExtensionUpdater::DetermineUpdates(
return result;
}
-void ExtensionUpdater::StartUpdateCheck(const GURL& url) {
- if (std::find(manifests_pending_.begin(), manifests_pending_.end(), url) !=
- manifests_pending_.end()) {
- return; // already scheduled
+void ExtensionUpdater::StartUpdateCheck(ManifestFetchData* fetch_data) {
+ std::deque<ManifestFetchData*>::const_iterator i;
+ for (i = manifests_pending_.begin(); i != manifests_pending_.end(); i++) {
+ if (fetch_data->full_url() == (*i)->full_url()) {
+ // This url is already scheduled to be fetched.
+ delete fetch_data;
+ return;
+ }
}
if (manifest_fetcher_.get() != NULL) {
- if (manifest_fetcher_->url() != url) {
- manifests_pending_.push_back(url);
+ if (manifest_fetcher_->url() != fetch_data->full_url()) {
+ manifests_pending_.push_back(fetch_data);
}
} else {
+ UMA_HISTOGRAM_COUNTS("Extensions.UpdateCheckUrlLength",
+ fetch_data->full_url().possibly_invalid_spec().length());
+
+ current_manifest_fetch_.reset(fetch_data);
manifest_fetcher_.reset(
- URLFetcher::Create(kManifestFetcherId, url, URLFetcher::GET, this));
+ URLFetcher::Create(kManifestFetcherId, fetch_data->full_url(),
+ URLFetcher::GET, this));
manifest_fetcher_->set_request_context(Profile::GetDefaultRequestContext());
manifest_fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES |
net::LOAD_DO_NOT_SAVE_COOKIES);
diff --git a/chrome/browser/extensions/extension_updater.h b/chrome/browser/extensions/extension_updater.h
index 8b2aed1..ae6d87c 100644
--- a/chrome/browser/extensions/extension_updater.h
+++ b/chrome/browser/extensions/extension_updater.h
@@ -6,6 +6,8 @@
#define CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_
#include <deque>
+#include <map>
+#include <set>
#include <string>
#include <vector>
@@ -25,6 +27,57 @@ class ExtensionUpdaterTest;
class ExtensionUpdaterFileHandler;
class PrefService;
+// To save on server resources we can request updates for multiple extensions
+// in one manifest check. This class helps us keep track of the id's for a
+// given fetch, building up the actual URL, and what if anything to include
+// in the ping parameter.
+class ManifestFetchData {
+ public:
+ static const int kNeverPinged = -1;
+
+ explicit ManifestFetchData(GURL update_url) : base_url_(update_url),
+ full_url_(update_url) {}
+
+ // Returns true if this extension information was successfully added. If the
+ // return value is false it means the full_url would have become too long, and
+ // this ManifestFetchData object remains unchanged.
+ bool AddExtension(std::string id, std::string version, int ping_days);
+
+ const GURL& base_url() const { return base_url_; }
+ const GURL& full_url() const { return full_url_; }
+ int extension_count() { return extension_ids_.size(); }
+ const std::set<std::string>& extension_ids() const { return extension_ids_; }
+
+ // Returns true if the given id is included in this manifest fetch.
+ bool Includes(std::string extension_id) const {
+ return extension_ids_.find(extension_id) != extension_ids_.end();
+ }
+
+ // Returns true if a ping parameter was added to full_url for this extension
+ // id.
+ bool DidPing(std::string extension_id) const;
+
+ private:
+ // Returns true if we should include a ping parameter for a given number of
+ // days.
+ bool ShouldPing(int days) const;
+
+ std::set<std::string> extension_ids_;
+
+ // Keeps track of the day value to use for the extensions where we want to
+ // send a 'days since last ping' parameter in the check.
+ std::map<std::string, int> ping_days_;
+
+ // The base update url without any arguments added.
+ GURL base_url_;
+
+ // The base update url plus arguments indicating the id, version, etc.
+ // information about each extension.
+ GURL full_url_;
+
+ DISALLOW_COPY_AND_ASSIGN(ManifestFetchData);
+};
+
// A class for doing auto-updates of installed Extensions. Used like this:
//
// ExtensionUpdater* updater = new ExtensionUpdater(my_extensions_service,
@@ -60,19 +113,6 @@ class ExtensionUpdater
blacklist_checks_enabled_ = enabled;
}
- // Interface for getting a uid to send for checks to the gallery.
- class UidProvider {
- public:
- static const unsigned int maxUidLength = 256;
- virtual ~UidProvider() {}
-
- // This should return a uid string no longer than maxUidLength, or the empty
- // string if there is no known uid (in which case nothing will be sent). It
- // will be url-escaped by the consumer so implementers of this interface
- // don't need to worry about it.
- virtual std::string GetUidString() = 0;
- };
-
private:
friend class base::RefCountedThreadSafe<ExtensionUpdater>;
friend class ExtensionUpdaterTest;
@@ -102,9 +142,6 @@ class ExtensionUpdater
static const char* kBlacklistUpdateUrl;
static const char* kBlacklistAppID;
- // Key used to denote Omaha uid in gallery update check urls.
- static const char* kUidKey;
-
// Does common work from constructors.
void Init();
@@ -149,15 +186,20 @@ class ExtensionUpdater
// BaseTimer::ReceiverMethod callback.
void TimerFired();
- // Begins an update check - called with url to fetch an update manifest.
- void StartUpdateCheck(const GURL& url);
+ // Begins an update check. Takes ownership of |fetch_data|.
+ void StartUpdateCheck(ManifestFetchData* fetch_data);
// Begins (or queues up) download of an updated extension.
void FetchUpdatedExtension(const std::string& id, const GURL& url,
const std::string& hash, const std::string& version);
// Once a manifest is parsed, this starts fetches of any relevant crx files.
- void HandleManifestResults(const UpdateManifest::ResultList& results);
+ void HandleManifestResults(const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results& results);
+
+ // Calculates the value to use for the ping days parameter in manifest
+ // fetches for a given extension.
+ int CalculatePingDays(const std::string& extension_id);
// Determines the version of an existing extension.
// Returns true on success and false on failures.
@@ -165,17 +207,8 @@ class ExtensionUpdater
// Given a list of potential updates, returns the indices of the ones that are
// applicable (are actually a new version, etc.) in |result|.
- std::vector<int> DetermineUpdates(
- const std::vector<UpdateManifest::Result>& possible_updates);
-
- // Creates a blacklist update url.
- static GURL GetBlacklistUpdateUrl(const std::wstring& version);
-
- // Registers a custom UidProvider, deleting the default one. Takes ownership
- // of |provider|. Useful mainly for testing.
- void set_uid_provider(UidProvider* provider) {
- uid_provider_.reset(provider);
- }
+ std::vector<int> DetermineUpdates(const ManifestFetchData& fetch_data,
+ const UpdateManifest::Results& possible_updates);
// Outstanding url fetch requests for manifests and updates.
scoped_ptr<URLFetcher> manifest_fetcher_;
@@ -183,9 +216,12 @@ class ExtensionUpdater
// Pending manifests and extensions to be fetched when the appropriate fetcher
// is available.
- std::deque<GURL> manifests_pending_;
+ std::deque<ManifestFetchData*> manifests_pending_;
std::deque<ExtensionFetch> extensions_pending_;
+ // The manifest currently being fetched (if any).
+ scoped_ptr<ManifestFetchData> current_manifest_fetch_;
+
// The extension currently being fetched (if any).
ExtensionFetch current_extension_fetch_;
@@ -200,8 +236,6 @@ class ExtensionUpdater
scoped_refptr<ExtensionUpdaterFileHandler> file_handler_;
bool blacklist_checks_enabled_;
- scoped_ptr<UidProvider> uid_provider_;
-
DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater);
};
diff --git a/chrome/browser/extensions/extension_updater_unittest.cc b/chrome/browser/extensions/extension_updater_unittest.cc
index 3f8a2c9..9439638 100644
--- a/chrome/browser/extensions/extension_updater_unittest.cc
+++ b/chrome/browser/extensions/extension_updater_unittest.cc
@@ -33,6 +33,9 @@
#define MAYBE_TestBlacklistUpdateCheckRequests TestBlacklistUpdateCheckRequests
#endif
+using base::Time;
+using base::TimeDelta;
+
static int expected_load_flags =
net::LOAD_DO_NOT_SEND_COOKIES | net::LOAD_DO_NOT_SAVE_COOKIES;
@@ -66,7 +69,23 @@ class MockService : public ExtensionUpdateService {
EXPECT_TRUE(false);
return false;
}
+
+ virtual void SetLastPingDay(const std::string& extension_id,
+ const Time& time) {
+ last_ping_days_[extension_id] = time;
+ }
+
+ virtual Time LastPingDay(const std::string& extension_id) {
+ std::map<std::string, Time>::iterator i =
+ last_ping_days_.find(extension_id);
+ if (i != last_ping_days_.end())
+ return i->second;
+
+ return Time();
+ }
+
private:
+ std::map<std::string, Time> last_ping_days_;
DISALLOW_COPY_AND_ASSIGN(MockService);
};
@@ -200,19 +219,6 @@ class ServiceForBlacklistTests : public MockService {
FilePath install_path_;
};
-
-// For testing gallery updates.
-class MockUidProvider : public ExtensionUpdater::UidProvider {
- public:
- explicit MockUidProvider(std::string uid) : uid_(uid) {}
- virtual ~MockUidProvider() {}
- virtual std::string GetUidString() {
- return uid_;
- }
- private:
- std::string uid_;
-};
-
static const int kUpdateFrequencySecs = 15;
// Takes a string with KEY=VALUE parameters separated by '&' in |params| and
@@ -240,8 +246,6 @@ static void ExtractParameters(const std::string& params,
// inside this class (which is a friend to ExtensionUpdater).
class ExtensionUpdaterTest : public testing::Test {
public:
-
-
static void SimulateTimerFired(ExtensionUpdater* updater) {
EXPECT_TRUE(updater->timer_.IsRunning());
updater->timer_.Stop();
@@ -253,12 +257,12 @@ class ExtensionUpdaterTest : public testing::Test {
const std::string& id,
const std::string& version,
const std::string& url,
- std::vector<UpdateManifest::Result>* results) {
+ UpdateManifest::Results* results) {
UpdateManifest::Result result;
result.extension_id = id;
result.version = version;
result.crx_url = GURL(url);
- results->push_back(result);
+ results->list.push_back(result);
}
static void TestExtensionUpdateCheckRequests() {
@@ -379,8 +383,10 @@ class ExtensionUpdaterTest : public testing::Test {
new ExtensionUpdater(&service, prefs.get(), kUpdateFrequencySecs);
// Check passing an empty list of parse results to DetermineUpdates
- std::vector<UpdateManifest::Result> updates;
- std::vector<int> updateable = updater->DetermineUpdates(updates);
+ ManifestFetchData fetch_data(GURL("http://localhost/foo"));
+ UpdateManifest::Results updates;
+ std::vector<int> updateable = updater->DetermineUpdates(fetch_data,
+ updates);
EXPECT_TRUE(updateable.empty());
// Create two updates - expect that DetermineUpdates will return the first
@@ -388,11 +394,15 @@ class ExtensionUpdaterTest : public testing::Test {
// installed and available at v2.0).
scoped_ptr<Version> one(Version::GetVersionFromString("1.0"));
EXPECT_TRUE(tmp[0]->version()->Equals(*one));
+ fetch_data.AddExtension(tmp[0]->id(), tmp[0]->VersionString(),
+ ManifestFetchData::kNeverPinged);
AddParseResult(tmp[0]->id(),
"1.1", "http://localhost/e1_1.1.crx", &updates);
+ fetch_data.AddExtension(tmp[1]->id(), tmp[1]->VersionString(),
+ ManifestFetchData::kNeverPinged);
AddParseResult(tmp[1]->id(),
tmp[1]->VersionString(), "http://localhost/e2_2.0.crx", &updates);
- updateable = updater->DetermineUpdates(updates);
+ updateable = updater->DetermineUpdates(fetch_data, updates);
EXPECT_EQ(1u, updateable.size());
EXPECT_EQ(0, updateable[0]);
STLDeleteElements(&tmp);
@@ -419,8 +429,12 @@ class ExtensionUpdaterTest : public testing::Test {
// Request 2 update checks - the first should begin immediately and the
// second one should be queued up.
- updater->StartUpdateCheck(url1);
- updater->StartUpdateCheck(url2);
+ ManifestFetchData* fetch1 = new ManifestFetchData(url1);
+ ManifestFetchData* fetch2 = new ManifestFetchData(url2);
+ fetch1->AddExtension("1111", "1.0", 0);
+ fetch2->AddExtension("12345", "2.0", ManifestFetchData::kNeverPinged);
+ updater->StartUpdateCheck(fetch1);
+ updater->StartUpdateCheck(fetch2);
std::string invalid_xml = "invalid xml";
fetcher = factory.GetFetcherByID(ExtensionUpdater::kManifestFetcherId);
@@ -622,7 +636,7 @@ class ExtensionUpdaterTest : public testing::Test {
file_util::Delete(service.install_path(), false);
}
- static void TestGalleryRequests() {
+ static void TestGalleryRequests(int ping_days) {
TestURLFetcherFactory factory;
URLFetcher::set_factory(&factory);
@@ -637,16 +651,21 @@ class ExtensionUpdaterTest : public testing::Test {
EXPECT_EQ(2u, tmp.size());
service.set_extensions(tmp);
+ Time now = Time::Now();
+ if (ping_days == 0) {
+ service.SetLastPingDay(tmp[0]->id(), now - TimeDelta::FromSeconds(15));
+ } else if (ping_days > 0) {
+ Time last_ping_day =
+ now - TimeDelta::FromDays(ping_days) - TimeDelta::FromSeconds(15);
+ service.SetLastPingDay(tmp[0]->id(), last_ping_day);
+ }
+
MessageLoop message_loop;
ScopedTempPrefService prefs;
scoped_refptr<ExtensionUpdater> updater =
new ExtensionUpdater(&service, prefs.get(), kUpdateFrequencySecs);
updater->set_blacklist_checks_enabled(false);
- // Create a mock uid provider and have the updater take ownership of it.
- MockUidProvider* uid_provider = new MockUidProvider("foobar");
- updater->set_uid_provider(uid_provider);
-
// Make the updater do manifest fetching, and note the urls it tries to
// fetch.
std::vector<GURL> fetched_urls;
@@ -675,11 +694,48 @@ class ExtensionUpdaterTest : public testing::Test {
NOTREACHED();
}
- // Now make sure the google query had the uid but the regular one did not.
- std::string search_string = std::string(ExtensionUpdater::kUidKey) + "=" +
- uid_provider->GetUidString();
- EXPECT_TRUE(url1_query.find(search_string) != std::string::npos);
+ // Now make sure the non-google query had no ping parameter, but the google
+ // one did (depending on ping_days).
+ std::string search_string = "ping%3Dr";
EXPECT_TRUE(url2_query.find(search_string) == std::string::npos);
+ if (ping_days == 0) {
+ EXPECT_TRUE(url1_query.find(search_string) == std::string::npos);
+ } else {
+ search_string += "%253D" + IntToString(ping_days);
+ size_t pos = url1_query.find(search_string);
+ EXPECT_TRUE(pos != std::string::npos);
+ }
+
+ STLDeleteElements(&tmp);
+ }
+
+ // This makes sure that the extension updater properly stores the results
+ // of a <daystart> tag from a manifest fetch in one of two cases: 1) This is
+ // the first time we fetched the extension, or 2) We sent a ping value of
+ // >= 1 day for the extension.
+ static void TestHandleManifestResults() {
+ ServiceForManifestTests service;
+ ScopedTempPrefService prefs;
+ scoped_refptr<ExtensionUpdater> updater =
+ new ExtensionUpdater(&service, prefs.get(), kUpdateFrequencySecs);
+
+ GURL update_url("http://www.google.com/manifest");
+ ExtensionList tmp;
+ CreateTestExtensions(1, &tmp, &update_url.spec());
+ service.set_extensions(tmp);
+
+ ManifestFetchData fetch_data(update_url);
+ Extension* extension = tmp[0];
+ fetch_data.AddExtension(extension->id(), extension->VersionString(),
+ ManifestFetchData::kNeverPinged);
+ UpdateManifest::Results results;
+ results.daystart_elapsed_seconds = 750;
+
+ updater->HandleManifestResults(fetch_data, results);
+ Time last_ping_day = service.LastPingDay(extension->id());
+ EXPECT_FALSE(last_ping_day.is_null());
+ int64 seconds_diff = (Time::Now() - last_ping_day).InSeconds();
+ EXPECT_LT(seconds_diff - results.daystart_elapsed_seconds, 5);
STLDeleteElements(&tmp);
}
@@ -720,7 +776,14 @@ TEST(ExtensionUpdaterTest, TestMultipleExtensionDownloading) {
}
TEST(ExtensionUpdaterTest, TestGalleryRequests) {
- ExtensionUpdaterTest::TestGalleryRequests();
+ ExtensionUpdaterTest::TestGalleryRequests(ManifestFetchData::kNeverPinged);
+ ExtensionUpdaterTest::TestGalleryRequests(0);
+ ExtensionUpdaterTest::TestGalleryRequests(1);
+ ExtensionUpdaterTest::TestGalleryRequests(5);
+}
+
+TEST(ExtensionUpdaterTest, TestHandleManifestResults) {
+ ExtensionUpdaterTest::TestHandleManifestResults();
}
// TODO(asargent) - (http://crbug.com/12780) add tests for:
diff --git a/chrome/browser/extensions/extensions_service.cc b/chrome/browser/extensions/extensions_service.cc
index de75aba..a62799f 100644
--- a/chrome/browser/extensions/extensions_service.cc
+++ b/chrome/browser/extensions/extensions_service.cc
@@ -42,6 +42,8 @@
#include "chrome/browser/extensions/external_registry_extension_provider_win.h"
#endif
+using base::Time;
+
namespace errors = extension_manifest_errors;
namespace {
@@ -433,7 +435,6 @@ void ExtensionsService::LoadInstalledExtension(const ExtensionInfo& info,
info.extension_id,
info.extension_location));
}
-
}
void ExtensionsService::NotifyExtensionLoaded(Extension* extension) {
@@ -515,6 +516,15 @@ void ExtensionsService::UpdateExtensionBlacklist(
}
}
+void ExtensionsService::SetLastPingDay(const std::string& extension_id,
+ const base::Time& time) {
+ extension_prefs_->SetLastPingDay(extension_id, time);
+}
+
+base::Time ExtensionsService::LastPingDay(const std::string& extension_id) {
+ return extension_prefs_->LastPingDay(extension_id);
+}
+
void ExtensionsService::CheckForExternalUpdates() {
// This installs or updates externally provided extensions.
// TODO(aa): Why pass this list into the provider, why not just filter it
diff --git a/chrome/browser/extensions/extensions_service.h b/chrome/browser/extensions/extensions_service.h
index 4b18c90..f71f044 100644
--- a/chrome/browser/extensions/extensions_service.h
+++ b/chrome/browser/extensions/extensions_service.h
@@ -51,6 +51,12 @@ class ExtensionUpdateService {
virtual void UpdateExtensionBlacklist(
const std::vector<std::string>& blacklist) = 0;
virtual bool HasInstalledExtensions() = 0;
+
+ // These set/get a server-provided time representing the start of the last day
+ // that we sent the 'ping' parameter during an update check.
+ virtual void SetLastPingDay(const std::string& extension_id,
+ const base::Time& time) = 0;
+ virtual base::Time LastPingDay(const std::string& extension_id) = 0;
};
// Manages installed and running Chromium extensions.
@@ -96,6 +102,11 @@ class ExtensionsService
return !(extensions_.empty() && disabled_extensions_.empty());
}
+ virtual void SetLastPingDay(const std::string& extension_id,
+ const base::Time& time);
+ virtual base::Time LastPingDay(const std::string& extension_id);
+
+
const FilePath& install_directory() const { return install_directory_; }
// Initialize and start all installed extensions.
diff --git a/chrome/browser/utility_process_host.h b/chrome/browser/utility_process_host.h
index 69fb270..3b5d58f 100644
--- a/chrome/browser/utility_process_host.h
+++ b/chrome/browser/utility_process_host.h
@@ -57,7 +57,7 @@ class UtilityProcessHost : public ChildProcessHost {
// Called when an update manifest xml file was successfully parsed.
virtual void OnParseUpdateManifestSucceeded(
- const UpdateManifest::ResultList& list) {}
+ const UpdateManifest::Results& results) {}
// Called when an update manifest xml file failed parsing. |error_message|
// contains details suitable for logging.
diff --git a/chrome/common/extensions/update_manifest.cc b/chrome/common/extensions/update_manifest.cc
index e3f1b4d..e461d7b 100644
--- a/chrome/common/extensions/update_manifest.cc
+++ b/chrome/common/extensions/update_manifest.cc
@@ -17,7 +17,9 @@ static const char* kExpectedGupdateProtocol = "2.0";
static const char* kExpectedGupdateXmlns =
"http://www.google.com/update2/response";
-UpdateManifest::UpdateManifest() {}
+UpdateManifest::UpdateManifest() {
+ results_.daystart_elapsed_seconds = kNoDaystart;
+}
UpdateManifest::~UpdateManifest() {}
@@ -135,6 +137,10 @@ static bool ParseSingleAppTag(xmlNode* app_node, xmlNs* xml_namespace,
}
xmlNode *updatecheck = updates[0];
+ if (GetAttribute(updatecheck, "status") == "noupdate") {
+ return true;
+ }
+
// Find the url to the crx file.
result->crx_url = GURL(GetAttribute(updatecheck, "codebase"));
if (!result->crx_url.is_valid()) {
@@ -180,7 +186,8 @@ static bool ParseSingleAppTag(xmlNode* app_node, xmlNs* xml_namespace,
bool UpdateManifest::Parse(const std::string& manifest_xml) {
- results_.resize(0);
+ results_.list.resize(0);
+ results_.daystart_elapsed_seconds = kNoDaystart;
if (manifest_xml.length() < 1) {
return false;
@@ -222,6 +229,17 @@ bool UpdateManifest::Parse(const std::string& manifest_xml) {
return false;
}
+ // Parse the first <daystart> if it's present.
+ std::vector<xmlNode*> daystarts = GetChildren(root, gupdate_ns, "daystart");
+ if (daystarts.size() > 0) {
+ xmlNode* first = daystarts[0];
+ std::string elapsed_seconds = GetAttribute(first, "elapsed_seconds");
+ int parsed_elapsed = kNoDaystart;
+ if (StringToInt(elapsed_seconds, &parsed_elapsed)) {
+ results_.daystart_elapsed_seconds = parsed_elapsed;
+ }
+ }
+
// Parse each of the <app> tags.
std::vector<xmlNode*> apps = GetChildren(root, gupdate_ns, "app");
for (unsigned int i = 0; i < apps.size(); i++) {
@@ -231,7 +249,7 @@ bool UpdateManifest::Parse(const std::string& manifest_xml) {
ParseError("%s", error.c_str());
return false;
}
- results_.push_back(current);
+ results_.list.push_back(current);
}
return true;
diff --git a/chrome/common/extensions/update_manifest.h b/chrome/common/extensions/update_manifest.h
index c1e5b2b..be0f643 100644
--- a/chrome/common/extensions/update_manifest.h
+++ b/chrome/common/extensions/update_manifest.h
@@ -23,6 +23,7 @@ class UpdateManifest {
//
// <?xml version='1.0' encoding='UTF-8'?>
// <gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>
+ // <daystart elapsed_seconds='300' />
// <app appid='12345'>
// <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'
// version='1.2.3.4' prodversionmin='2.0.143.0'
@@ -30,6 +31,9 @@ class UpdateManifest {
// </app>
// </gupdate>
//
+ // The <daystart> tag contains a "elapsed_seconds" attribute which refers to
+ // the server's notion of how many seconds it has been since midnight.
+ //
// The "appid" attribute of the <app> tag refers to the unique id of the
// extension. The "codebase" attribute of the <updatecheck> tag is the url to
// fetch the updated crx file, and the "prodversionmin" attribute refers to
@@ -44,7 +48,12 @@ class UpdateManifest {
GURL crx_url;
};
- typedef std::vector<Result> ResultList;
+ static const int kNoDaystart = -1;
+ struct Results {
+ std::vector<Result> list;
+ // This will be >= 0, or kNoDaystart if the <daystart> tag was not present.
+ int daystart_elapsed_seconds;
+ };
UpdateManifest();
~UpdateManifest();
@@ -55,16 +64,17 @@ class UpdateManifest {
// by calling errors().
bool Parse(const std::string& manifest_xml);
- const ResultList& results() { return results_; }
+ const Results& results() { return results_; }
const std::string& errors() { return errors_; }
private:
- ResultList results_;
-
+ Results results_;
std::string errors_;
// Helper function that adds parse error details to our errors_ string.
void ParseError(const char* details, ...);
+
+ DISALLOW_COPY_AND_ASSIGN(UpdateManifest);
};
#endif // CHROME_COMMON_EXTENSIONS_UPDATE_MANIFEST_H_
diff --git a/chrome/common/extensions/update_manifest_unittest.cc b/chrome/common/extensions/update_manifest_unittest.cc
index 7adc17f..fb7ee0e9 100644
--- a/chrome/common/extensions/update_manifest_unittest.cc
+++ b/chrome/common/extensions/update_manifest_unittest.cc
@@ -85,6 +85,25 @@ static const char* kSimilarTagnames =
" </app>"
"</gupdate>";
+// Includes a <daystart> tag.
+static const char* kWithDaystart =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>"
+" <daystart elapsed_seconds='456' />"
+" <app appid='12345'>"
+" <updatecheck codebase='http://example.com/extension_1.2.3.4.crx'"
+" version='1.2.3.4' prodversionmin='2.0.143.0' />"
+" </app>"
+"</gupdate>";
+
+// Indicates no updates available - this should not be a parse error.
+static const char* kNoUpdate =
+"<?xml version='1.0' encoding='UTF-8'?>"
+"<gupdate xmlns='http://www.google.com/update2/response' protocol='2.0'>"
+" <app appid='12345'>"
+" <updatecheck status='noupdate' />"
+" </app>"
+"</gupdate>";
TEST(ExtensionUpdateManifestTest, TestUpdateManifest) {
UpdateManifest parser;
@@ -98,8 +117,8 @@ TEST(ExtensionUpdateManifestTest, TestUpdateManifest) {
// Parse some valid XML, and check that all params came out as expected
EXPECT_TRUE(parser.Parse(kValidXml));
- EXPECT_FALSE(parser.results().empty());
- const UpdateManifest::Result* firstResult = &parser.results().at(0);
+ EXPECT_FALSE(parser.results().list.empty());
+ const UpdateManifest::Result* firstResult = &parser.results().list.at(0);
EXPECT_EQ(GURL("http://example.com/extension_1.2.3.4.crx"),
firstResult->crx_url);
@@ -113,7 +132,18 @@ TEST(ExtensionUpdateManifestTest, TestUpdateManifest) {
// Parse xml with hash value
EXPECT_TRUE(parser.Parse(valid_xml_with_hash));
- EXPECT_FALSE(parser.results().empty());
- firstResult = &parser.results().at(0);
+ EXPECT_FALSE(parser.results().list.empty());
+ firstResult = &parser.results().list.at(0);
EXPECT_EQ("1234", firstResult->package_hash);
+
+ EXPECT_TRUE(parser.Parse(kWithDaystart));
+ EXPECT_FALSE(parser.results().list.empty());
+ EXPECT_EQ(parser.results().daystart_elapsed_seconds, 456);
+
+ // Parse a no-update response.
+ EXPECT_TRUE(parser.Parse(kNoUpdate));
+ EXPECT_FALSE(parser.results().list.empty());
+ firstResult = &parser.results().list.at(0);
+ EXPECT_EQ(firstResult->extension_id, "12345");
+ EXPECT_EQ(firstResult->version, "");
}
diff --git a/chrome/common/utility_messages.h b/chrome/common/utility_messages.h
index eccdc14..867131f 100644
--- a/chrome/common/utility_messages.h
+++ b/chrome/common/utility_messages.h
@@ -50,6 +50,26 @@ struct ParamTraits<UpdateManifest::Result> {
}
};
+template<>
+struct ParamTraits<UpdateManifest::Results> {
+ typedef UpdateManifest::Results param_type;
+ static void Write(Message* m, const param_type& p) {
+ WriteParam(m, p.list);
+ WriteParam(m, p.daystart_elapsed_seconds);
+ }
+ static bool Read(const Message* m, void** iter, param_type* p) {
+ return ReadParam(m, iter, &p->list) &&
+ ReadParam(m, iter, &p->daystart_elapsed_seconds);
+ }
+ static void Log(const param_type& p, std::wstring* l) {
+ l->append(L"(");
+ LogParam(p.list, l);
+ l->append(L", ");
+ LogParam(p.daystart_elapsed_seconds, l);
+ l->append(L")");
+ }
+};
+
} // namespace IPC
#define MESSAGES_INTERNAL_FILE "chrome/common/utility_messages_internal.h"
diff --git a/chrome/common/utility_messages_internal.h b/chrome/common/utility_messages_internal.h
index 4947079..4933742 100644
--- a/chrome/common/utility_messages_internal.h
+++ b/chrome/common/utility_messages_internal.h
@@ -64,7 +64,7 @@ IPC_BEGIN_MESSAGES(UtilityHost)
// Reply when the utility process has succeeded in parsing an update manifest
// xml document.
IPC_MESSAGE_CONTROL1(UtilityHostMsg_ParseUpdateManifest_Succeeded,
- std::vector<UpdateManifest::Result> /* updates */)
+ UpdateManifest::Results /* updates */)
// Reply when an error occured parsing the update manifest. |error_message|
// is a description of what went wrong suitable for logging.