// Copyright (c) 2012 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 EXTENSIONS_BROWSER_UPDATER_EXTENSION_DOWNLOADER_H_ #define EXTENSIONS_BROWSER_UPDATER_EXTENSION_DOWNLOADER_H_ #include #include #include #include #include #include #include "base/compiler_specific.h" #include "base/files/file_path.h" #include "base/macros.h" #include "base/memory/weak_ptr.h" #include "base/optional.h" #include "base/version.h" #include "extensions/browser/updater/extension_downloader_delegate.h" #include "extensions/browser/updater/manifest_fetch_data.h" #include "extensions/browser/updater/request_queue.h" #include "extensions/browser/updater/safe_manifest_parser.h" #include "extensions/common/extension.h" #include "extensions/common/extension_id.h" #include "google_apis/gaia/google_service_auth_error.h" #include "mojo/public/cpp/bindings/remote.h" #include "net/http/http_request_headers.h" #include "services/network/public/mojom/url_loader_factory.mojom.h" #include "url/gurl.h" namespace crx_file { enum class VerifierFormat; } namespace signin { class PrimaryAccountAccessTokenFetcher; class IdentityManager; struct AccessTokenInfo; } // namespace signin namespace network { class SharedURLLoaderFactory; class SimpleURLLoader; struct ResourceRequest; } // namespace network namespace extensions { using ManifestInvalidFailureDataList = std::vector< std::pair>; struct UpdateDetails { UpdateDetails(const std::string& id, const base::Version& version); ~UpdateDetails(); std::string id; base::Version version; }; class ExtensionCache; class ExtensionDownloaderTestDelegate; class ExtensionUpdaterTest; // A class that checks for updates of a given list of extensions, and downloads // the crx file when updates are found. It uses a |ExtensionDownloaderDelegate| // that takes ownership of the downloaded crx files, and handles events during // the update check. class ExtensionDownloader { public: // A closure which constructs a new ExtensionDownloader to be owned by the // caller. using Factory = base::RepeatingCallback( ExtensionDownloaderDelegate* delegate)>; // |delegate| is stored as a raw pointer and must outlive the // ExtensionDownloader. ExtensionDownloader( ExtensionDownloaderDelegate* delegate, scoped_refptr url_loader_factory, crx_file::VerifierFormat crx_format_requirement, const base::FilePath& profile_path = base::FilePath()); ~ExtensionDownloader(); // Adds |extension| to the list of extensions to check for updates. // Returns false if the |extension| can't be updated due to invalid details. // In that case, no callbacks will be performed on the |delegate_|. // The |request_id| is passed on as is to the various |delegate_| callbacks. // This is used for example by ExtensionUpdater to keep track of when // potentially concurrent update checks complete. |fetch_priority| // parameter notifies the downloader the priority of this extension update // (either foreground or background). bool AddExtension(const Extension& extension, int request_id, ManifestFetchData::FetchPriority fetch_priority); // Check AddPendingExtensionWithVersion with the version set as "0.0.0.0". bool AddPendingExtension(const std::string& id, const GURL& update_url, Manifest::Location install_source, bool is_corrupt_reinstall, int request_id, ManifestFetchData::FetchPriority fetch_priority); // Adds extension |id| to the list of extensions to check for updates. // Returns false if the |id| can't be updated due to invalid details. // In that case, no callbacks will be performed on the |delegate_|. // The |request_id| is passed on as is to the various |delegate_| callbacks. // This is used for example by ExtensionUpdater to keep track of when // potentially concurrent update checks complete. The |is_corrupt_reinstall| // parameter is used to indicate in the request that we detected corruption in // the local copy of the extension and we want to perform a reinstall of it. // |fetch_priority| parameter notifies the downloader the priority of this // extension update (either foreground or background). The |version| // parameter specifies the version of the downloaded crx file, // equals to 0.0.0.0 if there is no crx file. bool AddPendingExtensionWithVersion( const std::string& id, const GURL& update_url, Manifest::Location install_source, bool is_corrupt_reinstall, int request_id, ManifestFetchData::FetchPriority fetch_priority, base::Version version); // Schedules a fetch of the manifest of all the extensions added with // AddExtension() and AddPendingExtension(). void StartAllPending(ExtensionCache* cache); // Sets the IdentityManager instance to be used for OAuth2 authentication on // protected Webstore downloads. The IdentityManager instance must be valid to // use for the lifetime of this object. void SetIdentityManager(signin::IdentityManager* identity_manager); void set_brand_code(const std::string& brand_code) { brand_code_ = brand_code; } void set_manifest_query_params(const std::string& params) { manifest_query_params_ = params; } void set_ping_enabled_domain(const std::string& domain) { ping_enabled_domain_ = domain; } // Set backoff policy for manifest queue for testing with less initial delay // so the tests do not timeout on retries. void SetBackoffPolicyForTesting( const net::BackoffEntry::Policy* backoff_policy); // Sets a test delegate to use by any instances of this class. The |delegate| // should outlive all instances. static void set_test_delegate(ExtensionDownloaderTestDelegate* delegate); // 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 int kMaxRetries = 10; // Names of the header fields used for traffic management for extension // updater. static const char kUpdateInteractivityHeader[]; static const char kUpdateAppIdHeader[]; static const char kUpdateUpdaterHeader[]; // Header values for foreground/background update requests. static const char kUpdateInteractivityForeground[]; static const char kUpdateInteractivityBackground[]; private: friend class ExtensionDownloaderTest; friend class ExtensionUpdaterTest; // These counters are bumped as extensions are added to be fetched. They // are then recorded as UMA metrics when all the extensions have been added. struct URLStats { URLStats() : no_url_count(0), google_url_count(0), other_url_count(0), extension_count(0), theme_count(0), app_count(0), platform_app_count(0), pending_count(0) {} int no_url_count, google_url_count, other_url_count; int extension_count, theme_count, app_count, platform_app_count, pending_count; }; // We need to keep track of some information associated with a url // when doing a fetch. struct ExtensionFetch { ExtensionFetch(); ExtensionFetch(const std::string& id, const GURL& url, const std::string& package_hash, const std::string& version, const std::set& request_ids, ManifestFetchData::FetchPriority fetch_priority); ~ExtensionFetch(); ExtensionId id; GURL url; std::string package_hash; base::Version version; std::set request_ids; ManifestFetchData::FetchPriority fetch_priority; enum CredentialsMode { CREDENTIALS_NONE = 0, CREDENTIALS_OAUTH2_TOKEN, CREDENTIALS_COOKIES, }; // Indicates the type of credentials to include with this fetch. CredentialsMode credentials; // Counts the number of times OAuth2 authentication has been attempted for // this fetch. int oauth2_attempt_count; }; // Parameters for special cases that aren't used for most requests. struct ExtraParams { // Additional data to be passed up in the update request. std::string update_url_data; // Indicates whether this extension is being reinstalled due to corruption. bool is_corrupt_reinstall; ExtraParams(); }; // We limit the number of extensions grouped together in one batch to avoid // running into the limits on the length of http GET requests, this represents // the key for grouping these extensions. struct FetchDataGroupKey { FetchDataGroupKey(); FetchDataGroupKey(const FetchDataGroupKey& other); FetchDataGroupKey(const int request_id, const GURL& update_url, const bool is_force_installed); ~FetchDataGroupKey(); bool operator<(const FetchDataGroupKey& other) const; int request_id{0}; GURL update_url; // The extensions in current ManifestFetchData are all force installed // (Manifest::Location::EXTERNAL_POLICY_DOWNLOAD) or not. In a // ManifestFetchData we would have either all the extensions as force // installed or we would none extensions as force installed. bool is_force_installed{false}; }; enum class UpdateAvailability { kAvailable, kNoUpdate, kBadUpdateSpecification, }; // Helper for AddExtension() and AddPendingExtension(). bool AddExtensionData(const std::string& id, const base::Version& version, Manifest::Type extension_type, Manifest::Location extension_location, const GURL& extension_update_url, const ExtraParams& extra, int request_id, ManifestFetchData::FetchPriority fetch_priority); // Adds all recorded stats taken so far to histogram counts. void ReportStats() const; // Begins an update check. void StartUpdateCheck(std::unique_ptr fetch_data); // Returns the URLLoaderFactory instance to be used, depending on whether // the URL being handled is file:// or not. network::mojom::URLLoaderFactory* GetURLLoaderFactoryToUse(const GURL& url); // Called by RequestQueue when a new manifest load request is started. void CreateManifestLoader(); // Retries the active request with some backoff delay. void RetryManifestFetchRequest(); // Reports failures if we failed to fetch the manifest or the fetched manifest // was invalid. void ReportManifestFetchFailure( ManifestFetchData* fetch_data, ExtensionDownloaderDelegate::Error error, const ExtensionDownloaderDelegate::FailureData& data); // Tries fetching the extension from cache if manifest fetch is failed for // force installed extensions, and notifies the failure reason for remaining // extensions. void TryFetchingExtensionsFromCache( ManifestFetchData* fetch_data, ExtensionDownloaderDelegate::Error error, const int net_error, const int response_code, const base::Optional& manifest_invalid_errors); // Makes a retry attempt, reports failure by calling // AddFailureDataOnManifestFetchFailed when fetching of update manifest // failed. void RetryRequestOrHandleFailureOnManifestFetchFailure( const network::SimpleURLLoader* loader, const int response_code); // Handles the result of a manifest fetch. void OnManifestLoadComplete(std::unique_ptr response_body); // 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(std::unique_ptr fetch_data, std::unique_ptr results, const base::Optional& error); // This function partition extension IDs stored in |fetch_data| into 3 sets: // update/no update/error using the update information from // |possible_updates| and the extension system. When the function returns: // - |to_update| stores entries from |possible_updates| that will be updated. // - |no_updates| stores the set of extension IDs that will not be updated. // - |errors| stores the entries of extension IDs along with the error that // occurred in the process // determining updates. For example, a common error is |possible_updates| // doesn't have any update information for some extensions in |fetch_data|. void DetermineUpdates(const ManifestFetchData& fetch_data, const UpdateManifestResults& possible_updates, std::vector* to_update, std::set* no_updates, ManifestInvalidFailureDataList* errors); // Checks whether extension is presented in cache. If yes, return path to its // cached CRX, base::nullopt otherwise. |manifest_fetch_failed| flag indicates // whether the lookup in cache is performed after the manifest is fetched or // due to failure while fetching or parsing manifest. base::Optional GetCachedExtension( const ExtensionFetch& fetch_data, bool manifest_fetch_failed); // Begins (or queues up) download of an updated extension. |info| represents // additional information about the extension update from the info field in // the update manifest. void FetchUpdatedExtension(std::unique_ptr fetch_data, base::Optional info); // Called by RequestQueue when a new extension load request is started. void CreateExtensionLoader(); void StartExtensionLoader(); // Handles the result of a crx fetch. void OnExtensionLoadComplete(base::FilePath crx_path); void NotifyExtensionManifestUpdateCheckStatus( std::vector results); void NotifyExtensionsManifestInvalidFailure( const ManifestInvalidFailureDataList& errors, const std::set& request_ids); // Invokes OnExtensionDownloadStageChanged() on the |delegate_| for each // extension in the set, with |stage| as the current stage. Make a copy of // arguments because there is no guarantee that callback won't indirectly // change source of IDs. void NotifyExtensionsDownloadStageChanged( ExtensionIdSet extension_ids, ExtensionDownloaderDelegate::Stage stage); // Calls NotifyExtensionsDownloadFailedWithFailureData with empty failure // data. void NotifyExtensionsDownloadFailed(ExtensionIdSet id_set, std::set request_ids, ExtensionDownloaderDelegate::Error error); // Invokes OnExtensionDownloadFailed() on the |delegate_| for each extension // in the set, with |error| as the reason for failure, and failure data. Make // a copy of arguments because there is no guarantee that callback won't // indirectly change source of IDs. void NotifyExtensionsDownloadFailedWithFailureData( ExtensionIdSet extension_ids, std::set request_ids, ExtensionDownloaderDelegate::Error error, const ExtensionDownloaderDelegate::FailureData& data); // Send a notification that an update was found for |id| that we'll // attempt to download. void NotifyUpdateFound(const std::string& id, const std::string& version); // Do real work of StartAllPending. If .crx cache is used, this function // is called when cache is ready. void DoStartAllPending(); // Notify delegate and remove ping results. void NotifyDelegateDownloadFinished( std::unique_ptr fetch_data, bool from_cache, const base::FilePath& crx_path, bool file_ownership_passed); // Cached extension installation completed. If it was not successful, we will // try to download it from the web store using already fetched manifest. void CacheInstallDone(std::unique_ptr fetch_data, bool installed); // Potentially updates an ExtensionFetch's authentication state and returns // |true| if the fetch should be retried. Returns |false| if the failure was // not related to authentication, leaving the ExtensionFetch data unmodified. bool IterateFetchCredentialsAfterFailure(ExtensionFetch* fetch, int response_code); void OnAccessTokenFetchComplete(GoogleServiceAuthError error, signin::AccessTokenInfo token_info); ManifestFetchData* CreateManifestFetchData( const GURL& update_url, int request_id, ManifestFetchData::FetchPriority fetch_priority); // This function helps obtain an update (if any) from |possible_updates|. // |possible_indices| is an array of indices of |possible_updates| which // the function would check to find an update. // If the return value is |kAvailable|, |update_index_out| will store the // index of the update in |possible_updates|. UpdateAvailability GetUpdateAvailability( const std::string& extension_id, const std::vector& possible_candidates, UpdateManifestResult** update_result_out) const; // The delegate that receives the crx files downloaded by the // ExtensionDownloader, and that fills in optional ping and update url data. ExtensionDownloaderDelegate* delegate_; // The URL loader factory to use for the SimpleURLLoaders. scoped_refptr url_loader_factory_; // The URL loader factory exclusively used to load file:// URLs. mojo::Remote file_url_loader_factory_; // The profile path used to load file:// URLs. It can be invalid. base::FilePath profile_path_for_url_loader_factory_; // Collects UMA samples that are reported when ReportStats() is called. URLStats url_stats_; // 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 update_url. std::map>> fetches_preparing_; // Outstanding url loader requests for manifests and updates. std::unique_ptr manifest_loader_; std::unique_ptr extension_loader_; std::unique_ptr extension_loader_resource_request_; // Pending manifests and extensions to be fetched when the appropriate fetcher // is available. RequestQueue manifests_queue_; RequestQueue extensions_queue_; // Maps an extension-id to its PingResult data. std::map ping_results_; // Cache for .crx files. ExtensionCache* extension_cache_; // May be used to fetch access tokens for protected download requests. May be // null. If non-null, guaranteed to outlive this object. signin::IdentityManager* identity_manager_; // A Webstore download-scoped access token for the |identity_provider_|'s // active account, if any. std::string access_token_; // A pending access token fetcher. std::unique_ptr access_token_fetcher_; // Brand code to include with manifest fetch queries if sending ping data. std::string brand_code_; // Baseline parameters to include with manifest fetch queries. std::string manifest_query_params_; // Domain to enable ping data. Ping data will be sent with manifest fetches // to update URLs which match this domain. Defaults to empty (no domain). std::string ping_enabled_domain_; net::HttpRequestHeaders last_extension_loader_resource_request_headers_for_testing_; int last_extension_loader_load_flags_for_testing_ = 0; crx_file::VerifierFormat crx_format_requirement_; // Used to create WeakPtrs to |this|. base::WeakPtrFactory weak_ptr_factory_{this}; DISALLOW_COPY_AND_ASSIGN(ExtensionDownloader); }; } // namespace extensions #endif // EXTENSIONS_BROWSER_UPDATER_EXTENSION_DOWNLOADER_H_