// Copyright 2012 The Chromium Authors // 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/memory/raw_ptr.h" #include "base/memory/weak_ptr.h" #include "base/version.h" #include "extensions/browser/updater/extension_downloader_delegate.h" #include "extensions/browser/updater/extension_downloader_task.h" #include "extensions/browser/updater/extension_downloader_types.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_id.h" #include "extensions/common/mojom/manifest.mojom-shared.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 "third_party/abseil-cpp/absl/types/optional.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 { struct DownloadFailure { DownloadFailure(ExtensionDownloaderDelegate::Error error, ExtensionDownloaderDelegate::FailureData failure_data); DownloadFailure(DownloadFailure&& other); ~DownloadFailure(); ExtensionDownloaderDelegate::Error error; ExtensionDownloaderDelegate::FailureData failure_data; }; 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(const ExtensionDownloader&) = delete; ExtensionDownloader& operator=(const ExtensionDownloader&) = delete; ~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_|. See // ExtensionDownloaderTask's description for more details and available // parameter. bool AddPendingExtension(ExtensionDownloaderTask task); // 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); bool HasActiveManifestRequestForTesting(); ManifestFetchData* GetActiveManifestFetchForTesting(); // 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 ExtensionDownloaderTestHelper; 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(ExtensionDownloaderTask task, const GURL& url, const std::string& package_hash, const std::string& version, DownloadFetchPriority fetch_priority); ~ExtensionFetch(); // Collects request ids from associated tasks. std::set GetRequestIds() const; ExtensionId id; GURL url; std::string package_hash; base::Version version; DownloadFetchPriority fetch_priority; std::vector associated_tasks; 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; }; // 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 // (mojom::ManifestLocation::kExternalPolicyDownload) 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, }; // Updates |url_stats_| for a single extension download requests. void UpdateURLStats(const GURL& update_url, Manifest::Type extension_type); // Helper for AddExtension() and AddPendingExtension(). bool AddExtensionData(ExtensionDownloaderTask task); // 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( RequestQueue::Request request, int network_error_code, int response_code); // 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. Removes found extensions from // |fetch_data|. Return true if all extensions were found. bool TryFetchingExtensionsFromCache(ManifestFetchData* fetch_data); // Makes a retry attempt, reports failure by calling // AddFailureDataOnManifestFetchFailed when fetching of update manifest // failed. void RetryRequestOrHandleFailureOnManifestFetchFailure( RequestQueue::Request request, const network::SimpleURLLoader& loader, const int response_code); // Handles the result of a manifest fetch. void OnManifestLoadComplete(std::unique_ptr loader, 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 absl::optional& error); // This function partition extensions from given |tasks| into two sets: // 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. // - |errors| stores the entries of extension IDs along with the error that // occurred in the process (no update available is considered an error from // ExtensionDownloader's perspective). // For example, a common error is |possible_updates| doesn't have any update // information for some extensions. void DetermineUpdates( std::vector tasks, const UpdateManifestResults& possible_updates, std::vector>* to_update, std::vector>* errors); // Checks whether extension is presented in cache. If yes, return path to its // cached CRX, absl::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. absl::optional GetCachedExtension( const ExtensionId& id, const std::string& package_hash, const base::Version& expected_version, 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, absl::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); // 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); // Invokes OnExtensionDownloadFailed() on the |delegate_| for each extension // in the list, which also provides the reason for the failure. Make a copy // of arguments because there is no guarantee that callback won't indirectly // change source of them. void NotifyExtensionsDownloadFailedWithList( std::vector> errors, std::set request_ids); // 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); // Helper method to populate lists of manifest fetch requests. void AddToFetches(std::map>>& fetches_preparing, ExtensionDownloaderTask task); // 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, DownloadFetchPriority 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. raw_ptr 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_; // List of update requests added to the downloader but not started yet. std::vector pending_tasks_; // Outstanding url loader requests for manifests and updates. 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. raw_ptr extension_cache_; // May be used to fetch access tokens for protected download requests. May be // null. If non-null, guaranteed to outlive this object. raw_ptr 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}; }; } // namespace extensions #endif // EXTENSIONS_BROWSER_UPDATER_EXTENSION_DOWNLOADER_H_