// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #ifndef CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_ #define CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_ #pragma once #include <deque> #include <map> #include <set> #include <string> #include <vector> #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_temp_dir.h" #include "base/memory/weak_ptr.h" #include "base/task.h" #include "base/time.h" #include "base/timer.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/common/extensions/update_manifest.h" #include "chrome/common/net/url_fetcher.h" #include "googleurl/src/gurl.h" class Extension; class ExtensionPrefs; class ExtensionUpdaterTest; class ExtensionUpdaterFileHandler; class PrefService; class Profile; class SafeManifestParser; // 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; // Each ping type is sent at most once per day. enum PingType { // Used for counting total installs of an extension/app/theme. ROLLCALL, // Used for counting number of active users of an app, where "active" means // the app was launched at least once since the last active ping. ACTIVE }; struct PingData { // The number of days it's been since our last rollcall or active ping, // respectively. These are calculated based on the start of day from the // server's perspective. int rollcall_days; int active_days; PingData() : rollcall_days(0), active_days(0) {} PingData(int rollcall, int active) : rollcall_days(rollcall), active_days(active) {} }; explicit ManifestFetchData(const GURL& update_url); ~ManifestFetchData(); // 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, const PingData& ping_data, const std::string& update_url_data); 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(const std::string& extension_id) const; // Returns true if a ping parameter for |type| was added to full_url for this // extension id. bool DidPing(std::string extension_id, PingType type) const; private: // The set of extension id's for this ManifestFetchData. std::set<std::string> extension_ids_; // The set of ping data we actually sent. std::map<std::string, PingData> pings_; // 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 building a set of ManifestFetchData objects from // extensions and pending extensions. class ManifestFetchesBuilder { public: ManifestFetchesBuilder(ExtensionServiceInterface* service, ExtensionPrefs* prefs); ~ManifestFetchesBuilder(); void AddExtension(const Extension& extension); void AddPendingExtension(const std::string& id, const PendingExtensionInfo& info); // Adds all recorded stats taken so far to histogram counts. void ReportStats() const; // Caller takes ownership of the returned ManifestFetchData // objects. Clears all recorded stats. std::vector<ManifestFetchData*> GetFetches(); private: struct URLStats { URLStats() : no_url_count(0), google_url_count(0), other_url_count(0), extension_count(0), theme_count(0), app_count(0), pending_count(0) {} int no_url_count, google_url_count, other_url_count; int extension_count, theme_count, app_count, pending_count; }; void AddExtensionData(Extension::Location location, const std::string& id, const Version& version, Extension::Type extension_type, GURL update_url, const std::string& update_url_data); ExtensionServiceInterface* const service_; ExtensionPrefs* const prefs_; // 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_; URLStats url_stats_; DISALLOW_COPY_AND_ASSIGN(ManifestFetchesBuilder); }; // A class for doing auto-updates of installed Extensions. Used like this: // // ExtensionUpdater* updater = new ExtensionUpdater(my_extensions_service, // pref_service, // update_frequency_secs); // updater->Start(); // .... // updater->Stop(); class ExtensionUpdater : public URLFetcher::Delegate { public: // Holds a pointer to the passed |service|, using it for querying installed // extensions and installing updated ones. The |frequency_seconds| parameter // controls how often update checks are scheduled. ExtensionUpdater(ExtensionServiceInterface* service, ExtensionPrefs* extension_prefs, PrefService* prefs, Profile* profile, int frequency_seconds); virtual ~ExtensionUpdater(); // Starts the updater running. Should be called at most once. void Start(); // Stops the updater running, cancelling any outstanding update manifest and // crx downloads. Does not cancel any in-progress installs. void Stop(); // Posts a task to do an update check. Does nothing if there is // already a pending task that has not yet run. void CheckSoon(); // Starts an update check right now, instead of waiting for the next // regularly scheduled check or a pending check from CheckSoon(). void CheckNow(); // Set blacklist checks on or off. void set_blacklist_checks_enabled(bool enabled) { blacklist_checks_enabled_ = enabled; } // Returns true iff CheckSoon() has been called but the update check // hasn't been performed yet. This is used mostly by tests; calling // code should just call CheckSoon(). bool WillCheckSoon() const; private: friend class ExtensionUpdaterTest; friend class ExtensionUpdaterFileHandler; friend class SafeManifestParser; // We need to keep track of some information associated with a url // when doing a fetch. struct ExtensionFetch { ExtensionFetch(); ExtensionFetch(const std::string& i, const GURL& u, const std::string& h, const std::string& v); ~ExtensionFetch(); std::string id; GURL url; std::string package_hash; std::string version; }; // These are needed for unit testing, to help identify the correct mock // URLFetcher objects. static const int kManifestFetcherId = 1; static const int kExtensionFetcherId = 2; static const char* kBlacklistAppID; // Does common work from constructors. void Init(); // Computes when to schedule the first update check. base::TimeDelta DetermineFirstCheckDelay(); // URLFetcher::Delegate interface. virtual void OnURLFetchComplete(const URLFetcher* source, const GURL& url, const net::URLRequestStatus& status, int response_code, const ResponseCookies& cookies, const std::string& data); // These do the actual work when a URL fetch completes. virtual void OnManifestFetchComplete(const GURL& url, const net::URLRequestStatus& status, int response_code, const std::string& data); virtual void OnCRXFetchComplete(const GURL& url, const net::URLRequestStatus& status, int response_code, const std::string& data); // Called when a crx file has been written into a temp file, and is ready // to be installed. void OnCRXFileWritten(const std::string& id, const FilePath& path, const GURL& download_url); // Called when we encountered an error writing a crx file to a temp file. void OnCRXFileWriteError(const std::string& id); // Verifies downloaded blacklist. Based on the blacklist, calls extension // service to unload blacklisted extensions and update pref. void ProcessBlacklist(const std::string& data); // Sets the timer to call TimerFired after roughly |target_delay| from now. // To help spread load evenly on servers, this method adds some random // jitter. It also saves the scheduled time so it can be reloaded on // browser restart. void ScheduleNextCheck(const base::TimeDelta& target_delay); // BaseTimer::ReceiverMethod callback. void TimerFired(); // Posted by CheckSoon(). void DoCheckSoon(); // 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. // If |results| is null, it means something went wrong when parsing it. void HandleManifestResults(const ManifestFetchData& fetch_data, const UpdateManifest::Results* results); // Determines the version of an existing extension. // Returns true on success and false on failures. bool GetExistingVersion(const std::string& id, std::string* version); // 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 ManifestFetchData& fetch_data, const UpdateManifest::Results& possible_updates); // Send a notification that update checks are starting. void NotifyStarted(); // Send a notification that an update was found for extension_id that we'll // attempt to download and install. void NotifyUpdateFound(const std::string& extension_id); // Send a notification if we're finished updating. void NotifyIfFinished(); // Adds a set of ids to in_progress_ids_. void AddToInProgress(const std::set<std::string>& ids); // Removes a set of ids from in_progress_ids_. void RemoveFromInProgress(const std::set<std::string>& ids); // Whether Start() has been called but not Stop(). bool alive_; base::WeakPtrFactory<ExtensionUpdater> weak_ptr_factory_; // Outstanding url fetch requests for manifests and updates. scoped_ptr<URLFetcher> manifest_fetcher_; scoped_ptr<URLFetcher> extension_fetcher_; // Pending manifests and extensions to be fetched when the appropriate fetcher // is available. 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_; // Pointer back to the service that owns this ExtensionUpdater. ExtensionServiceInterface* service_; base::OneShotTimer<ExtensionUpdater> timer_; int frequency_seconds_; ScopedRunnableMethodFactory<ExtensionUpdater> method_factory_; bool will_check_soon_; ExtensionPrefs* extension_prefs_; PrefService* prefs_; Profile* profile_; scoped_refptr<ExtensionUpdaterFileHandler> file_handler_; bool blacklist_checks_enabled_; // The ids of extensions that have in-progress update checks. std::set<std::string> in_progress_ids_; FRIEND_TEST(ExtensionUpdaterTest, TestStartUpdateCheckMemory); FRIEND_TEST(ExtensionUpdaterTest, TestAfterStopBehavior); DISALLOW_COPY_AND_ASSIGN(ExtensionUpdater); }; #endif // CHROME_BROWSER_EXTENSIONS_EXTENSION_UPDATER_H_