summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
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.