blob: 9d82e683010808f6b43153c700f38e891b490584 [file] [log] [blame]
// Copyright (c) 2009 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.
#include <deque>
#include <map>
#include <set>
#include <string>
#include <vector>
#include "base/gtest_prod_util.h"
#include "base/ref_counted.h"
#include "base/task.h"
#include "googleurl/src/gurl.h"
#include "net/base/completion_callback.h"
#include "net/url_request/url_request.h"
#include "webkit/appcache/appcache.h"
#include "webkit/appcache/appcache_host.h"
#include "webkit/appcache/appcache_interfaces.h"
#include "webkit/appcache/appcache_response.h"
#include "webkit/appcache/appcache_storage.h"
namespace appcache {
class UpdateJobInfo;
class HostNotifier;
// Application cache Update algorithm and state.
class AppCacheUpdateJob : public URLRequest::Delegate,
public AppCacheStorage::Delegate,
public AppCacheHost::Observer {
AppCacheUpdateJob(AppCacheService* service, AppCacheGroup* group);
// Triggers the update process or adds more info if this update is already
// in progress.
void StartUpdate(AppCacheHost* host, const GURL& new_master_resource);
friend class ScopedRunnableMethodFactory<AppCacheUpdateJob>;
friend class AppCacheUpdateJobTest;
friend class UpdateJobInfo;
// Master entries have multiple hosts, for example, the same page is opened
// in different tabs.
typedef std::vector<AppCacheHost*> PendingHosts;
typedef std::map<GURL, PendingHosts> PendingMasters;
typedef std::map<GURL, URLRequest*> PendingUrlFetches;
typedef std::map<int64, GURL> LoadingResponses;
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 {
enum InternalUpdateState {
// Every state after this comment indicates the update is terminating.
enum StoredState {
struct UrlToFetch {
GURL url;
bool storage_checked;
scoped_refptr<AppCacheResponseInfo> existing_response_info;
UrlToFetch(const GURL& url, bool checked, AppCacheResponseInfo* info)
: url(url), storage_checked(checked), existing_response_info(info) {}
UpdateJobInfo* GetUpdateJobInfo(URLRequest* request);
// Methods for URLRequest::Delegate.
void OnResponseStarted(URLRequest* request);
void OnReadCompleted(URLRequest* request, int bytes_read);
void OnReceivedRedirect(URLRequest* request,
const GURL& new_url,
bool* defer_redirect);
// TODO(jennb): any other delegate callbacks to handle? certificate?
// Methods for AppCacheStorage::Delegate.
void OnResponseInfoLoaded(AppCacheResponseInfo* response_info,
int64 response_id);
void OnGroupAndNewestCacheStored(AppCacheGroup* group, AppCache* newest_cache,
bool success, bool would_exceed_quota);
void OnGroupMadeObsolete(AppCacheGroup* group, bool success);
// Methods for AppCacheHost::Observer.
void OnCacheSelectionComplete(AppCacheHost* host) {} // N/A
void OnDestructionImminent(AppCacheHost* host);
void CheckPolicy();
void OnPolicyCheckComplete(int rv);
void HandleCacheFailure(const std::string& error_message);
void FetchManifest(bool is_first_fetch);
// Add extra conditional HTTP headers to the request based on the
// currently cached response headers.
void AddConditionalHeaders(URLRequest* request,
const net::HttpResponseInfo* info);
void OnResponseCompleted(URLRequest* request);
// Retries a 503 request with retry-after header of 0.
// Returns true if request should be retried and deletes original request.
bool RetryRequest(URLRequest* request);
void ReadResponseData(URLRequest* request);
// Returns false if response data is processed asynchronously, in which
// case ReadResponseData will be invoked when it is safe to continue
// reading more response data from the request.
bool ConsumeResponseData(URLRequest* request,
UpdateJobInfo* info,
int bytes_read);
void OnWriteResponseComplete(int result, URLRequest* request,
UpdateJobInfo* info);
void HandleManifestFetchCompleted(URLRequest* request);
void ContinueHandleManifestFetchCompleted(bool changed);
void HandleUrlFetchCompleted(URLRequest* request);
void HandleMasterEntryFetchCompleted(URLRequest* request);
void HandleManifestRefetchCompleted(URLRequest* request);
void OnManifestInfoWriteComplete(int result);
void OnManifestDataWriteComplete(int result);
void StoreGroupAndCache();
void NotifySingleHost(AppCacheHost* host, EventID event_id);
void NotifyAllAssociatedHosts(EventID event_id);
void NotifyAllProgress(const GURL& url);
void NotifyAllFinalProgress();
void NotifyAllError(const std::string& error_message);
void AddAllAssociatedHostsToNotifier(HostNotifier* notifier);
// Checks if manifest is byte for byte identical with the manifest
// in the newest application cache.
void CheckIfManifestChanged();
void OnManifestDataReadComplete(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 Manifest& 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 std::string& error_message);
// 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();
// Deletes this object after letting the stack unwind.
void DeleteSoon();
bool IsTerminating() { return internal_state_ >= REFETCH_MANIFEST ||
stored_state_ != UNSTORED; }
// This factory will be used to schedule invocations of various methods.
ScopedRunnableMethodFactory<AppCacheUpdateJob> method_factory_;
GURL manifest_url_; // here for easier access
AppCacheService* service_;
scoped_refptr<AppCache> inprogress_cache_;
AppCacheGroup* group_;
UpdateType update_type_;
InternalUpdateState internal_state_;
PendingMasters pending_master_entries_;
size_t master_entries_completed_;
// 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_;
PendingUrlFetches master_entry_fetches_;
// URLs of files to fetch along with their flags.
AppCache::EntryMap url_file_list_;
size_t url_fetches_completed_;
// Helper container to track which urls have not been fetched yet. URLs are
// removed when the fetch is initiated. Flag indicates whether an attempt
// to load the URL from storage has already been tried and failed.
std::deque<UrlToFetch> urls_to_fetch_;
// Helper container to track which urls are being loaded from response
// storage.
LoadingResponses loading_responses_;
// Keep track of pending URL requests so we can cancel them if necessary.
URLRequest* manifest_url_request_;
PendingUrlFetches pending_url_fetches_;
// Temporary storage of manifest response data for parsing and comparison.
std::string manifest_data_;
std::string manifest_refetch_data_;
scoped_ptr<net::HttpResponseInfo> manifest_response_info_;
scoped_ptr<AppCacheResponseWriter> manifest_response_writer_;
scoped_refptr<net::IOBuffer> read_manifest_buffer_;
std::string loaded_manifest_data_;
scoped_ptr<AppCacheResponseReader> manifest_response_reader_;
// 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> 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> duplicate_response_ids_;
// Whether we've stored the resulting group/cache yet.
StoredState stored_state_;
net::CompletionCallbackImpl<AppCacheUpdateJob> manifest_info_write_callback_;
net::CompletionCallbackImpl<AppCacheUpdateJob> manifest_data_write_callback_;
net::CompletionCallbackImpl<AppCacheUpdateJob> manifest_data_read_callback_;
scoped_refptr<net::CancelableCompletionCallback<AppCacheUpdateJob> >
FRIEND_TEST_ALL_PREFIXES(AppCacheGroupTest, QueueUpdate);
} // namespace appcache