| // 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 CONTENT_BROWSER_APPCACHE_APPCACHE_UPDATE_JOB_H_ |
| #define CONTENT_BROWSER_APPCACHE_APPCACHE_UPDATE_JOB_H_ |
| |
| #include <stddef.h> |
| #include <stdint.h> |
| |
| #include <map> |
| #include <memory> |
| #include <set> |
| #include <string> |
| #include <vector> |
| |
| #include "base/containers/circular_deque.h" |
| #include "base/feature_list.h" |
| #include "base/gtest_prod_util.h" |
| #include "base/macros.h" |
| #include "base/memory/ref_counted.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/time/time.h" |
| #include "content/browser/appcache/appcache.h" |
| #include "content/browser/appcache/appcache_host.h" |
| #include "content/browser/appcache/appcache_service_impl.h" |
| #include "content/browser/appcache/appcache_storage.h" |
| #include "content/browser/appcache/appcache_update_job_state.h" |
| #include "content/browser/appcache/appcache_update_metrics_recorder.h" |
| #include "content/common/appcache_interfaces.h" |
| #include "content/common/content_export.h" |
| #include "net/http/http_response_headers.h" |
| #include "net/url_request/url_request.h" |
| #include "third_party/blink/public/mojom/appcache/appcache.mojom.h" |
| #include "url/gurl.h" |
| |
| namespace content { |
| FORWARD_DECLARE_TEST(AppCacheGroupTest, QueueUpdate); |
| class AppCacheGroupTest; |
| |
| namespace appcache_update_job_unittest { |
| class AppCacheUpdateJobTest; |
| } |
| |
| class AppCacheResponseInfo; |
| class HostNotifier; |
| |
| CONTENT_EXPORT extern const base::Feature kAppCacheUpdateResourceOn304Feature; |
| |
| // Application cache Update algorithm and state. |
| class CONTENT_EXPORT AppCacheUpdateJob |
| : public AppCacheStorage::Delegate, |
| public AppCacheHost::Observer, |
| public AppCacheServiceImpl::Observer { |
| public: |
| // Used for uma stats only for now, so new values are append only. |
| enum ResultType { |
| UPDATE_OK, DB_ERROR, DISKCACHE_ERROR, QUOTA_ERROR, REDIRECT_ERROR, |
| MANIFEST_ERROR, NETWORK_ERROR, SERVER_ERROR, CANCELLED_ERROR, |
| SECURITY_ERROR, NUM_UPDATE_JOB_RESULT_TYPES |
| }; |
| |
| AppCacheUpdateJob(AppCacheServiceImpl* service, AppCacheGroup* group); |
| ~AppCacheUpdateJob() override; |
| |
| // Triggers the update process or adds more info if this update is already |
| // in progress. |
| void StartUpdate(AppCacheHost* host, const GURL& new_master_resource); |
| |
| private: |
| friend class content::AppCacheGroupTest; |
| friend class content::appcache_update_job_unittest::AppCacheUpdateJobTest; |
| |
| class CacheCopier; |
| class URLFetcher; |
| class UpdateRequestBase; |
| class UpdateURLLoaderRequest; |
| class UpdateURLRequest; |
| |
| static const int kRerunDelayMs = 1000; |
| |
| // TODO(michaeln): Rework the set of states vs update types vs stored states. |
| // The NO_UPDATE state is really more of an update type. For all update types |
| // storing the results is relevant. |
| |
| enum UpdateType { |
| UNKNOWN_TYPE, |
| UPGRADE_ATTEMPT, |
| CACHE_ATTEMPT, |
| }; |
| |
| enum StoredState { |
| UNSTORED, |
| STORING, |
| STORED, |
| }; |
| |
| struct UrlToFetch { |
| UrlToFetch(const GURL& url, bool checked, AppCacheResponseInfo* info); |
| UrlToFetch(const UrlToFetch& other); |
| ~UrlToFetch(); |
| |
| GURL url; |
| |
| // If true, we attempted fetching the URL from storage. The fetch failed, |
| // because this instance indicates the URL still needs to be fetched. |
| bool storage_checked; |
| // Cached entry with a matching URL. The entry is expired or uncacheable, |
| // but it can serve as the basis for a conditional HTTP request. |
| scoped_refptr<AppCacheResponseInfo> existing_response_info; |
| }; |
| |
| std::unique_ptr<AppCacheResponseWriter> CreateResponseWriter(); |
| |
| // Methods for AppCacheStorage::Delegate. |
| void OnResponseInfoLoaded(AppCacheResponseInfo* response_info, |
| int64_t response_id) override; |
| void OnGroupAndNewestCacheStored(AppCacheGroup* group, |
| AppCache* newest_cache, |
| bool success, |
| bool would_exceed_quota) override; |
| void OnGroupMadeObsolete(AppCacheGroup* group, |
| bool success, |
| int response_code) override; |
| |
| // Methods for AppCacheHost::Observer. |
| void OnCacheSelectionComplete(AppCacheHost* host) override {} // N/A |
| void OnDestructionImminent(AppCacheHost* host) override; |
| |
| // Methods for AppCacheServiceImpl::Observer. |
| void OnServiceReinitialized(AppCacheStorageReference* old_storage) override; |
| |
| void HandleCacheFailure(const blink::mojom::AppCacheErrorDetails& details, |
| ResultType result, |
| const GURL& failed_resource_url); |
| |
| // Retrieves the cache's manifest. |
| // |
| // Called when a page referencing this job's manifest is loaded. This can |
| // result in a new cache getting created, or in an existing cache receiving a |
| // new master entry. |
| void FetchManifest(); |
| void HandleManifestFetchCompleted(URLFetcher* url_fetcher, int net_error); |
| void HandleFetchedManifestChanged(base::Time token_expires); |
| void HandleFetchedManifestIsUnchanged(); |
| |
| void HandleResourceFetchCompleted(URLFetcher* url_fetcher, int net_error); |
| void ContinueHandleResourceFetchCompleted(const GURL& url, |
| URLFetcher* entry_fetcher); |
| void HandleNewMasterEntryFetchCompleted(URLFetcher* url_fetcher, |
| int net_error); |
| |
| void RefetchManifest(); |
| void HandleManifestRefetchCompleted(URLFetcher* url_fetcher, int net_error); |
| |
| void OnManifestInfoWriteComplete(base::Time token_expires, int result); |
| void OnManifestDataWriteComplete(base::Time token_expires, int result); |
| |
| void StoreGroupAndCache(); |
| |
| void NotifySingleHost(AppCacheHost* host, |
| blink::mojom::AppCacheEventID event_id); |
| void NotifyAllAssociatedHosts(blink::mojom::AppCacheEventID event_id); |
| void NotifyAllProgress(const GURL& url); |
| void NotifyAllFinalProgress(); |
| void NotifyAllError(const blink::mojom::AppCacheErrorDetails& detals); |
| void LogConsoleMessageToAll(const std::string& message); |
| void AddAllAssociatedHostsToNotifier(HostNotifier* notifier); |
| |
| // Checks if manifest is byte for byte identical with the manifest |
| // in the newest application cache. |
| void CheckIfManifestChanged(base::Time token_expires); |
| void OnManifestDataReadComplete(base::Time token_expires, int result); |
| |
| // Used to read a manifest from the cache in case of a 304 Not Modified HTTP |
| // response. |
| void ReadManifestFromCacheAndContinue(); |
| void OnManifestFromCacheDataReadComplete(int result); |
| |
| // Creates the list of files that may need to be fetched and initiates |
| // fetches. Section 6.9.4 steps 12-17 |
| void BuildUrlFileList(const AppCacheManifest& manifest); |
| void AddUrlToFileList(const GURL& url, int type); |
| void FetchUrls(); |
| void CancelAllUrlFetches(); |
| bool ShouldSkipUrlFetch(const AppCacheEntry& entry); |
| |
| // If entry already exists in the cache currently being updated, merge |
| // the entry type information with the existing entry. |
| // Returns true if entry exists in cache currently being updated. |
| bool AlreadyFetchedEntry(const GURL& url, int entry_type); |
| |
| // TODO(jennb): Delete when update no longer fetches master entries directly. |
| // Creates the list of master entries that need to be fetched and initiates |
| // fetches. |
| void AddMasterEntryToFetchList(AppCacheHost* host, const GURL& url, |
| bool is_new); |
| void FetchMasterEntries(); |
| void CancelAllMasterEntryFetches( |
| const blink::mojom::AppCacheErrorDetails& details); |
| |
| // Asynchronously loads the entry from the newest complete cache if the |
| // HTTP caching semantics allow. |
| // Returns false if immediately obvious that data cannot be loaded from |
| // newest complete cache. |
| bool MaybeLoadFromNewestCache(const GURL& url, AppCacheEntry& entry); |
| void LoadFromNewestCacheFailed(const GURL& url, |
| AppCacheResponseInfo* newest_response_info); |
| |
| // Does nothing if update process is still waiting for pending master |
| // entries or URL fetches to complete downloading. Otherwise, completes |
| // the update process. |
| void MaybeCompleteUpdate(); |
| |
| // Schedules a rerun of the entire update with the same parameters as |
| // this update job after a short delay. |
| void ScheduleUpdateRetry(int delay_ms); |
| |
| void Cancel(); |
| void ClearPendingMasterEntries(); |
| void DiscardInprogressCache(); |
| void DiscardDuplicateResponses(); |
| bool IsFinished() const; |
| |
| void MadeProgress() { last_progress_time_ = base::Time::Now(); } |
| |
| // Deletes this object after letting the stack unwind. |
| void DeleteSoon(); |
| |
| bool IsTerminating() { |
| return internal_state_ >= AppCacheUpdateJobState::REFETCH_MANIFEST || |
| stored_state_ != UNSTORED; |
| } |
| |
| AppCache* inprogress_cache() { return inprogress_cache_.get(); } |
| |
| AppCacheServiceImpl* service_; |
| const GURL manifest_url_; // here for easier access |
| |
| // Stores the manifest parser version for the group before an update begins. |
| int64_t cached_manifest_parser_version_; |
| // Stores the manifest parser version determined during the fetch phase. |
| int64_t fetched_manifest_parser_version_; |
| |
| // Stores the manifest scope for the group before an update begins. |
| std::string cached_manifest_scope_; |
| // Stores the manifest scope determined during the fetch phase. |
| std::string fetched_manifest_scope_; |
| // Stores the manifest scope determined during the refetch phase. |
| std::string refetched_manifest_scope_; |
| |
| // If true, AppCache resource fetches that return a 304 response will have |
| // their cache entry copied and updated based on the 304 response. |
| const bool update_resource_on_304_enabled_; |
| |
| // Defined prior to refs to AppCaches and Groups because destruction |
| // order matters, the disabled_storage_reference_ must outlive those |
| // objects. |
| scoped_refptr<AppCacheStorageReference> disabled_storage_reference_; |
| |
| scoped_refptr<AppCache> inprogress_cache_; |
| |
| AppCacheGroup* group_; |
| |
| UpdateType update_type_; |
| AppCacheUpdateJobState internal_state_; |
| base::Time last_progress_time_; |
| bool doing_full_update_check_; |
| |
| // Master entries have multiple hosts, for example, the same page is opened |
| // in different tabs. |
| std::map<GURL, std::vector<AppCacheHost*>> pending_master_entries_; |
| |
| size_t master_entries_completed_; |
| std::set<GURL> failed_master_entries_; |
| |
| // TODO(jennb): Delete when update no longer fetches master entries directly. |
| // Helper containers to track which pending master entries have yet to be |
| // fetched and which are currently being fetched. Master entries that |
| // are listed in the manifest may be fetched as a regular URL instead of |
| // as a separate master entry fetch to optimize against duplicate fetches. |
| std::set<GURL> master_entries_to_fetch_; |
| std::map<GURL, std::unique_ptr<URLFetcher>> master_entry_fetches_; |
| |
| // URLs of files to fetch along with their flags. |
| std::map<GURL, AppCacheEntry> url_file_list_; |
| size_t url_fetches_completed_; |
| |
| // URLs that have not been fetched yet. |
| // |
| // URLs are removed from this set right as they are about to be fetched. |
| base::circular_deque<UrlToFetch> urls_to_fetch_; |
| |
| // Helper container to track which urls are being loaded from response |
| // storage. |
| std::map<int64_t, GURL> loading_responses_; |
| |
| // Keep track of pending URL requests so we can cancel them if necessary. |
| std::unique_ptr<URLFetcher> manifest_fetcher_; |
| std::map<GURL, std::unique_ptr<URLFetcher>> pending_url_fetches_; |
| |
| // Temporary storage of manifest response data for parsing and comparison. |
| std::string manifest_data_; |
| std::unique_ptr<net::HttpResponseInfo> manifest_response_info_; |
| std::unique_ptr<AppCacheResponseWriter> manifest_response_writer_; |
| scoped_refptr<net::IOBuffer> read_manifest_buffer_; |
| base::Time read_manifest_token_expires_; |
| std::string loaded_manifest_data_; |
| std::unique_ptr<AppCacheResponseReader> manifest_response_reader_; |
| bool manifest_has_valid_mime_type_; |
| |
| // New master entries added to the cache by this job, used to cleanup |
| // in error conditions. |
| std::vector<GURL> added_master_entries_; |
| |
| // Response ids stored by this update job, used to cleanup in |
| // error conditions. |
| std::vector<int64_t> stored_response_ids_; |
| |
| // In some cases we fetch the same resource multiple times, and then |
| // have to delete the duplicates upon successful update. These ids |
| // are also in the stored_response_ids_ collection so we only schedule |
| // these for deletion on success. |
| // TODO(michaeln): Rework when we no longer fetches master entries directly. |
| std::vector<int64_t> duplicate_response_ids_; |
| |
| // Whether we've stored the resulting group/cache yet. |
| StoredState stored_state_; |
| |
| // Used to track behavior and conditions found during update for submission |
| // to UMA. |
| AppCacheUpdateMetricsRecorder update_metrics_; |
| |
| // |cache_copier_by_url_| owns all running cache copies, indexed by |url|. |
| std::map<GURL, std::unique_ptr<CacheCopier>> cache_copier_by_url_; |
| |
| AppCacheStorage* storage_; |
| base::WeakPtrFactory<AppCacheUpdateJob> weak_factory_{this}; |
| |
| FRIEND_TEST_ALL_PREFIXES(content::AppCacheGroupTest, QueueUpdate); |
| |
| DISALLOW_COPY_AND_ASSIGN(AppCacheUpdateJob); |
| }; |
| |
| } // namespace content |
| |
| #endif // CONTENT_BROWSER_APPCACHE_APPCACHE_UPDATE_JOB_H_ |