diff options
author | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-01 22:30:30 +0000 |
---|---|---|
committer | michaeln@google.com <michaeln@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-09-01 22:30:30 +0000 |
commit | 23f1ef1a445a53bcefc8ddab9f4184b1db7321c5 (patch) | |
tree | c9f17a33025ed5b5c5dee76a7b4fef9104e9a995 | |
parent | e143c823ce66af5a83a9a17b6f5938eb16e49392 (diff) | |
download | chromium_src-23f1ef1a445a53bcefc8ddab9f4184b1db7321c5.zip chromium_src-23f1ef1a445a53bcefc8ddab9f4184b1db7321c5.tar.gz chromium_src-23f1ef1a445a53bcefc8ddab9f4184b1db7321c5.tar.bz2 |
Plumb request interception into the appcache library for both chrome and test_shell.
AppCache library:
* Added AppCacheInterceptor, which is derived from URLRequest::Interceptor.
Chrome:
* Each UserProfile instantiates a ChromeAppCacheService, which is derived from an appcache library class.
* Each ChromeURLRequestContext associated with that profile has a reference to that instance.
* ResourceDispatcherHost pokes AppCacheInterceptor when initiating URLRequests and when returning the response head.
TestShell:
* Added SimpleAppCacheSystem which bundles together appcache lib components for use in a single process with an UI and IO thread.
* TestShellWebKit instantiates and initializes an instance of the above, aimed at at temp directory that will get cleaned up when the test run is over.
* SimpleResourceLoaderBridge pokes the system when initiating URLRequests and when returning the response head.
TEST=none, although many existing tests exercise this stuff
BUG=none
Review URL: http://codereview.chromium.org/173406
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@25099 0039d316-1c4b-4281-b951-d872f2087c98
31 files changed, 991 insertions, 92 deletions
diff --git a/chrome/browser/automation/automation_profile_impl.h b/chrome/browser/automation/automation_profile_impl.h index c0f2592..7a22fc3 100644 --- a/chrome/browser/automation/automation_profile_impl.h +++ b/chrome/browser/automation/automation_profile_impl.h @@ -45,6 +45,9 @@ class AutomationProfileImpl : public Profile { virtual Profile* GetOriginalProfile() { return original_profile_->GetOriginalProfile(); } + virtual ChromeAppCacheService* GetAppCacheService() { + return original_profile_->GetAppCacheService(); + } virtual VisitedLinkMaster* GetVisitedLinkMaster() { return original_profile_->GetVisitedLinkMaster(); } diff --git a/chrome/browser/net/chrome_url_request_context.cc b/chrome/browser/net/chrome_url_request_context.cc index 65671d4..0bc5572 100644 --- a/chrome/browser/net/chrome_url_request_context.cc +++ b/chrome/browser/net/chrome_url_request_context.cc @@ -354,6 +354,8 @@ ChromeURLRequestContext::ChromeURLRequestContext(Profile* profile) } ssl_config_service_ = profile->GetSSLConfigService(); + + appcache_service_ = profile->GetAppCacheService(); } ChromeURLRequestContext::ChromeURLRequestContext( @@ -378,6 +380,7 @@ ChromeURLRequestContext::ChromeURLRequestContext( blacklist_ = other->blacklist_; is_media_ = other->is_media_; is_off_the_record_ = other->is_off_the_record_; + appcache_service_ = other->appcache_service_; } // NotificationObserver implementation. diff --git a/chrome/browser/net/chrome_url_request_context.h b/chrome/browser/net/chrome_url_request_context.h index f4baea6..d0303d7 100644 --- a/chrome/browser/net/chrome_url_request_context.h +++ b/chrome/browser/net/chrome_url_request_context.h @@ -3,6 +3,7 @@ // found in the LICENSE file. #include "base/file_path.h" +#include "chrome/common/appcache/chrome_appcache_service.h" #include "chrome/common/net/cookie_monster_sqlite.h" #include "chrome/common/notification_registrar.h" #include "chrome/common/pref_service.h" @@ -65,6 +66,12 @@ class ChromeURLRequestContext : public URLRequestContext, return user_script_dir_path_; } + // Gets the appcache service to be used for requests in this context. + // May be NULL if requests for this context aren't subject to appcaching. + ChromeAppCacheService* appcache_service() const { + return appcache_service_.get(); + } + virtual const std::string& GetUserAgent(const GURL& url) const; virtual bool InterceptCookie(const URLRequest* request, std::string* cookie); @@ -118,6 +125,8 @@ class ChromeURLRequestContext : public URLRequestContext, // Path to the directory user scripts are stored in. FilePath user_script_dir_path_; + scoped_refptr<ChromeAppCacheService> appcache_service_; + scoped_ptr<SQLitePersistentCookieStore> cookie_db_; PrefService* prefs_; const Blacklist* blacklist_; diff --git a/chrome/browser/profile.cc b/chrome/browser/profile.cc index fb92549..1aa04e8 100644 --- a/chrome/browser/profile.cc +++ b/chrome/browser/profile.cc @@ -39,6 +39,7 @@ #include "chrome/browser/visitedlink_master.h" #include "chrome/browser/visitedlink_event_listener.h" #include "chrome/browser/webdata/web_data_service.h" +#include "chrome/common/appcache/chrome_appcache_service.h" #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_switches.h" @@ -115,6 +116,16 @@ static void CleanupRequestContext(ChromeURLRequestContext* context) { } } +static void CleanupAppCacheService(ChromeAppCacheService* service) { + if (service) { + MessageLoop* io_thread = ChromeThread::GetMessageLoop(ChromeThread::IO); + if (io_thread) + io_thread->ReleaseSoon(FROM_HERE, service); + else + service->Release(); + } +} + // static void Profile::RegisterUserPrefs(PrefService* prefs) { prefs->RegisterBooleanPref(prefs::kSearchSuggestEnabled, true); @@ -187,6 +198,7 @@ class OffTheRecordProfileImpl : public Profile, virtual ~OffTheRecordProfileImpl() { CleanupRequestContext(request_context_); CleanupRequestContext(extensions_request_context_); + CleanupAppCacheService(appcache_service_.release()); } virtual FilePath GetPath() { return profile_->GetPath(); } @@ -208,6 +220,14 @@ class OffTheRecordProfileImpl : public Profile, return profile_; } + virtual ChromeAppCacheService* GetAppCacheService() { + if (!appcache_service_.get()) { + appcache_service_ = new ChromeAppCacheService(); + appcache_service_->InitializeOnUIThread(GetPath(), true); + } + return appcache_service_.get(); + } + virtual VisitedLinkMaster* GetVisitedLinkMaster() { // We don't provide access to the VisitedLinkMaster when we're OffTheRecord // because we don't want to leak the sites that the user has visited before. @@ -484,6 +504,9 @@ class OffTheRecordProfileImpl : public Profile, ChromeURLRequestContext* extensions_request_context_; + // Use a seperate appcache service for OTR. + scoped_refptr<ChromeAppCacheService> appcache_service_; + // The download manager that only stores downloaded items in memory. scoped_refptr<DownloadManager> download_manager_; @@ -700,6 +723,7 @@ ProfileImpl::~ProfileImpl() { CleanupRequestContext(request_context_); CleanupRequestContext(media_request_context_); CleanupRequestContext(extensions_request_context_); + CleanupAppCacheService(appcache_service_.release()); // When the request contexts are gone, the blacklist wont be needed anymore. delete blacklist_; @@ -749,6 +773,14 @@ Profile* ProfileImpl::GetOriginalProfile() { return this; } +ChromeAppCacheService* ProfileImpl::GetAppCacheService() { + if (!appcache_service_.get()) { + appcache_service_ = new ChromeAppCacheService(); + appcache_service_->InitializeOnUIThread(GetPath(), false); + } + return appcache_service_.get(); +} + VisitedLinkMaster* ProfileImpl::GetVisitedLinkMaster() { if (!visited_link_master_.get()) { scoped_ptr<VisitedLinkMaster> visited_links( diff --git a/chrome/browser/profile.h b/chrome/browser/profile.h index 36679a7..83ae64c 100644 --- a/chrome/browser/profile.h +++ b/chrome/browser/profile.h @@ -25,6 +25,7 @@ class SSLConfigService; class Blacklist; class BookmarkModel; class BrowserThemeProvider; +class ChromeAppCacheService; class ChromeURLRequestContext; class DownloadManager; class Extension; @@ -117,6 +118,11 @@ class Profile { // profile is not off the record. virtual Profile* GetOriginalProfile() = 0; + // Retrieves a pointer to the AppCacheService for this profile. + // Chrome request contexts associated with this profile also have + // a reference to this instance. + virtual ChromeAppCacheService* GetAppCacheService() = 0; + // Retrieves a pointer to the VisitedLinkMaster associated with this // profile. The VisitedLinkMaster is lazily created the first time // that this method is called. @@ -359,6 +365,7 @@ class ProfileImpl : public Profile, virtual Profile* GetOffTheRecordProfile(); virtual void DestroyOffTheRecordProfile(); virtual Profile* GetOriginalProfile(); + virtual ChromeAppCacheService* GetAppCacheService(); virtual VisitedLinkMaster* GetVisitedLinkMaster(); virtual UserScriptMaster* GetUserScriptMaster(); virtual SSLHostState* GetSSLHostState(); @@ -444,6 +451,7 @@ class ProfileImpl : public Profile, FilePath path_; FilePath base_cache_path_; + scoped_refptr<ChromeAppCacheService> appcache_service_; scoped_ptr<VisitedLinkEventListener> visited_link_event_listener_; scoped_ptr<VisitedLinkMaster> visited_link_master_; scoped_refptr<ExtensionsService> extensions_service_; diff --git a/chrome/browser/renderer_host/resource_dispatcher_host.cc b/chrome/browser/renderer_host/resource_dispatcher_host.cc index 7d42929..669118b 100644 --- a/chrome/browser/renderer_host/resource_dispatcher_host.cc +++ b/chrome/browser/renderer_host/resource_dispatcher_host.cc @@ -54,6 +54,7 @@ #include "net/base/ssl_cert_request_info.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" +#include "webkit/appcache/appcache_interceptor.h" #include "webkit/appcache/appcache_interfaces.h" // TODO(port): Move these includes to the above section when porting is done. @@ -239,9 +240,11 @@ void PopulateResourceResponse(URLRequest* request, request->GetCharset(&response->response_head.charset); response->response_head.filter_policy = filter_policy; response->response_head.content_length = request->GetExpectedContentSize(); - response->response_head.appcache_id = appcache::kNoCacheId; - response->response_head.appcache_manifest_url = GURL(); request->GetMimeType(&response->response_head.mime_type); + appcache::AppCacheInterceptor::GetExtraResponseInfo( + request, + &response->response_head.appcache_id, + &response->response_head.appcache_manifest_url); } } // namespace @@ -294,6 +297,9 @@ void ResourceDispatcherHost::Initialize() { DCHECK(MessageLoop::current() == ui_loop_); download_file_manager_->Initialize(); safe_browsing_->Initialize(io_loop_); + io_loop_->PostTask( + FROM_HERE, + NewRunnableFunction(&appcache::AppCacheInterceptor::EnsureRegistered)); } void ResourceDispatcherHost::Shutdown() { @@ -582,6 +588,11 @@ void ResourceDispatcherHost::BeginRequest( chrome_browser_net::SetOriginProcessUniqueIDForRequest( request_data.origin_child_id, request); + // Have the appcache associate its extra info with the request. + appcache::AppCacheInterceptor::SetExtraRequestInfo( + request, context ? context->appcache_service() : NULL, child_id, + request_data.appcache_host_id, request_data.resource_type); + BeginRequestInternal(request); } diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index 61debd5..b211f67 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -156,7 +156,8 @@ ResourceMessageFilter::ResourceMessageFilter( profile_(profile), render_widget_helper_(render_widget_helper), audio_renderer_host_(audio_renderer_host), - appcache_dispatcher_host_(new AppCacheDispatcherHost), + appcache_dispatcher_host_( + new AppCacheDispatcherHost(profile->GetAppCacheService())), ALLOW_THIS_IN_INITIALIZER_LIST(dom_storage_dispatcher_host_( new DOMStorageDispatcherHost(this, profile->GetWebKitContext(), resource_dispatcher_host->webkit_thread()))), @@ -193,7 +194,7 @@ ResourceMessageFilter::~ResourceMessageFilter() { void ResourceMessageFilter::Init() { render_widget_helper_->Init(id(), resource_dispatcher_host_); - appcache_dispatcher_host_->Initialize(this); + appcache_dispatcher_host_->Initialize(this, id()); } // Called on the IPC thread: diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp index e4bbe6f..47a521b 100644 --- a/chrome/chrome.gyp +++ b/chrome/chrome.gyp @@ -423,6 +423,7 @@ 'common/appcache/appcache_dispatcher_host.h', 'common/appcache/appcache_frontend_proxy.cc', 'common/appcache/appcache_frontend_proxy.h', + 'common/appcache/chrome_appcache_service.h', 'common/automation_constants.cc', 'common/automation_constants.h', 'common/bindings_policy.h', diff --git a/chrome/common/appcache/appcache_backend_proxy.cc b/chrome/common/appcache/appcache_backend_proxy.cc index 14b81f1..25c77f7 100644 --- a/chrome/common/appcache/appcache_backend_proxy.cc +++ b/chrome/common/appcache/appcache_backend_proxy.cc @@ -34,20 +34,19 @@ void AppCacheBackendProxy::MarkAsForeignEntry( } appcache::Status AppCacheBackendProxy::GetStatus(int host_id) { - appcache::Status status; + appcache::Status status = appcache::UNCACHED; sender_->Send(new AppCacheMsg_GetStatus(host_id, &status)); return status; } bool AppCacheBackendProxy::StartUpdate(int host_id) { - bool result; + bool result = false; sender_->Send(new AppCacheMsg_StartUpdate(host_id, &result)); return result; } bool AppCacheBackendProxy::SwapCache(int host_id) { - bool result; + bool result = false; sender_->Send(new AppCacheMsg_SwapCache(host_id, &result)); return result; } - diff --git a/chrome/common/appcache/appcache_dispatcher_host.cc b/chrome/common/appcache/appcache_dispatcher_host.cc index b71f2d9..ee0265f 100644 --- a/chrome/common/appcache/appcache_dispatcher_host.cc +++ b/chrome/common/appcache/appcache_dispatcher_host.cc @@ -4,12 +4,28 @@ #include "chrome/common/appcache/appcache_dispatcher_host.h" +#include "chrome/common/appcache/chrome_appcache_service.h" #include "chrome/common/render_messages.h" -void AppCacheDispatcherHost::Initialize(IPC::Message::Sender* sender) { +AppCacheDispatcherHost::AppCacheDispatcherHost( + ChromeAppCacheService* appcache_service) + : appcache_service_(appcache_service) { +} + +void AppCacheDispatcherHost::Initialize(IPC::Message::Sender* sender, + int process_id) { DCHECK(sender); frontend_proxy_.set_sender(sender); - backend_impl_.Initialize(NULL, &frontend_proxy_); + if (appcache_service_.get()) { + backend_impl_.Initialize( + appcache_service_.get(), &frontend_proxy_, process_id); + get_status_callback_.reset( + NewCallback(this, &AppCacheDispatcherHost::GetStatusCallback)); + start_update_callback_.reset( + NewCallback(this, &AppCacheDispatcherHost::StartUpdateCallback)); + swap_cache_callback_.reset( + NewCallback(this, &AppCacheDispatcherHost::SwapCacheCallback)); + } } bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& msg, @@ -31,49 +47,78 @@ bool AppCacheDispatcherHost::OnMessageReceived(const IPC::Message& msg, } void AppCacheDispatcherHost::OnRegisterHost(int host_id) { - backend_impl_.RegisterHost(host_id); + if (appcache_service_.get()) + backend_impl_.RegisterHost(host_id); } void AppCacheDispatcherHost::OnUnregisterHost(int host_id) { - backend_impl_.UnregisterHost(host_id); + if (appcache_service_.get()) + backend_impl_.UnregisterHost(host_id); } void AppCacheDispatcherHost::OnSelectCache( int host_id, const GURL& document_url, int64 cache_document_was_loaded_from, const GURL& opt_manifest_url) { - backend_impl_.SelectCache(host_id, document_url, - cache_document_was_loaded_from, - opt_manifest_url); + if (appcache_service_.get()) + backend_impl_.SelectCache(host_id, document_url, + cache_document_was_loaded_from, + opt_manifest_url); + else + frontend_proxy_.OnCacheSelected( + host_id, appcache::kNoCacheId, appcache::UNCACHED); } void AppCacheDispatcherHost::OnMarkAsForeignEntry( int host_id, const GURL& document_url, int64 cache_document_was_loaded_from) { - backend_impl_.MarkAsForeignEntry(host_id, document_url, - cache_document_was_loaded_from); + if (appcache_service_.get()) + backend_impl_.MarkAsForeignEntry(host_id, document_url, + cache_document_was_loaded_from); } void AppCacheDispatcherHost::OnGetStatus(int host_id, IPC::Message* reply_msg) { - // TODO(michaeln): Handle the case where cache selection is not yet complete. - appcache::Status status = backend_impl_.GetStatus(host_id); - AppCacheMsg_GetStatus::WriteReplyParams(reply_msg, status); - frontend_proxy_.sender()->Send(reply_msg); + if (appcache_service_.get()) + backend_impl_.GetStatusWithCallback( + host_id, get_status_callback_.get(), reply_msg); + else + GetStatusCallback(appcache::UNCACHED, reply_msg); } void AppCacheDispatcherHost::OnStartUpdate(int host_id, IPC::Message* reply_msg) { - // TODO(michaeln): Handle the case where cache selection is not yet complete. - bool success = backend_impl_.StartUpdate(host_id); - AppCacheMsg_StartUpdate::WriteReplyParams(reply_msg, success); - frontend_proxy_.sender()->Send(reply_msg); + if (appcache_service_.get()) + backend_impl_.StartUpdateWithCallback( + host_id, start_update_callback_.get(), reply_msg); + else + StartUpdateCallback(false, reply_msg); } void AppCacheDispatcherHost::OnSwapCache(int host_id, IPC::Message* reply_msg) { - // TODO(michaeln): Handle the case where cache selection is not yet complete. - bool success = backend_impl_.SwapCache(host_id); - AppCacheMsg_SwapCache::WriteReplyParams(reply_msg, success); + if (appcache_service_.get()) + backend_impl_.SwapCacheWithCallback( + host_id, swap_cache_callback_.get(), reply_msg); + else + SwapCacheCallback(false, reply_msg); +} + +void AppCacheDispatcherHost::GetStatusCallback( + appcache::Status status, void* param) { + IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param); + AppCacheMsg_GetStatus::WriteReplyParams(reply_msg, status); + frontend_proxy_.sender()->Send(reply_msg); +} + +void AppCacheDispatcherHost::StartUpdateCallback(bool result, void* param) { + IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param); + AppCacheMsg_StartUpdate::WriteReplyParams(reply_msg, result); + frontend_proxy_.sender()->Send(reply_msg); +} + +void AppCacheDispatcherHost::SwapCacheCallback(bool result, void* param) { + IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param); + AppCacheMsg_SwapCache::WriteReplyParams(reply_msg, result); frontend_proxy_.sender()->Send(reply_msg); } diff --git a/chrome/common/appcache/appcache_dispatcher_host.h b/chrome/common/appcache/appcache_dispatcher_host.h index f141fdb..47f245a 100644 --- a/chrome/common/appcache/appcache_dispatcher_host.h +++ b/chrome/common/appcache/appcache_dispatcher_host.h @@ -6,19 +6,27 @@ #define CHROME_COMMON_APPCACHE_APPCACHE_DISPATCHER_HOST_H_ #include <vector> +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" #include "chrome/common/appcache/appcache_frontend_proxy.h" #include "ipc/ipc_message.h" #include "webkit/appcache/appcache_backend_impl.h" +class ChromeAppCacheService; + // Handles appcache related messages sent to the main browser process from // its child processes. There is a distinct host for each child process. // Messages are handled on the IO thread. The ResourceMessageFilter creates // an instance and delegates calls to it. class AppCacheDispatcherHost { public: - void Initialize(IPC::Message::Sender* sender); + explicit AppCacheDispatcherHost(ChromeAppCacheService* appcache_service); + + void Initialize(IPC::Message::Sender* sender, int process_id); bool OnMessageReceived(const IPC::Message& msg, bool* msg_is_ok); + int process_id() const { return backend_impl_.process_id(); } + // Note: needed to satisfy ipc message dispatching macros. bool Send(IPC::Message* msg) { return frontend_proxy_.sender()->Send(msg); @@ -37,8 +45,18 @@ class AppCacheDispatcherHost { void OnStartUpdate(int host_id, IPC::Message* reply_msg); void OnSwapCache(int host_id, IPC::Message* reply_msg); + void GetStatusCallback(appcache::Status status, void* param); + void StartUpdateCallback(bool result, void* param); + void SwapCacheCallback(bool result, void* param); + AppCacheFrontendProxy frontend_proxy_; appcache::AppCacheBackendImpl backend_impl_; + scoped_refptr<ChromeAppCacheService> appcache_service_; + scoped_ptr<appcache::GetStatusCallback> get_status_callback_; + scoped_ptr<appcache::StartUpdateCallback> start_update_callback_; + scoped_ptr<appcache::SwapCacheCallback> swap_cache_callback_; + + DISALLOW_COPY_AND_ASSIGN(AppCacheDispatcherHost); }; #endif // CHROME_COMMON_APPCACHE_APPCACHE_DISPATCHER_HOST_H_ diff --git a/chrome/common/appcache/appcache_frontend_proxy.cc b/chrome/common/appcache/appcache_frontend_proxy.cc index bf20baa8..08c8510 100644 --- a/chrome/common/appcache/appcache_frontend_proxy.cc +++ b/chrome/common/appcache/appcache_frontend_proxy.cc @@ -20,4 +20,3 @@ void AppCacheFrontendProxy::OnEventRaised(const std::vector<int>& host_ids, appcache::EventID event_id) { sender_->Send(new AppCacheMsg_EventRaised(host_ids, event_id)); } - diff --git a/chrome/common/appcache/chrome_appcache_service.h b/chrome/common/appcache/chrome_appcache_service.h new file mode 100644 index 0000000..63736da --- /dev/null +++ b/chrome/common/appcache/chrome_appcache_service.h @@ -0,0 +1,67 @@ +// 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. + +#ifndef CHROME_COMMON_APPCACHE_CHROME_APPCACHE_SERVICE_H_ +#define CHROME_COMMON_APPCACHE_CHROME_APPCACHE_SERVICE_H_ + +#include "base/file_path.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/task.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/common/chrome_constants.h" +#include "webkit/appcache/appcache_service.h" + +// An AppCacheService subclass used by the chrome. There is an instance +// associated with each Profile. This derivation adds refcounting semantics +// since a profile has multiple URLRequestContexts which refer to the same +// object, and those URLRequestContexts are refcounted independently of the +// owning profile. +// +// All methods, including the dtor, are expected to be called on the IO thread +// except for the ctor and the init method which are expected to be called on +// the UI thread. +class ChromeAppCacheService + : public base::RefCountedThreadSafe<ChromeAppCacheService>, + public appcache::AppCacheService { + public: + + explicit ChromeAppCacheService() + : was_initialized_with_io_thread_(false) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + } + + void InitializeOnUIThread(const FilePath& data_directory, + bool is_incognito) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + // Some test cases run without an IO thread. + MessageLoop* io_thread = ChromeThread::GetMessageLoop(ChromeThread::IO); + if (io_thread) { + was_initialized_with_io_thread_ = true; + io_thread->PostTask(FROM_HERE, + NewRunnableMethod(this, &ChromeAppCacheService::InitializeOnIOThread, + data_directory, is_incognito)); + } + } + + private: + friend class base::RefCountedThreadSafe<ChromeAppCacheService>; + + virtual ~ChromeAppCacheService() { + DCHECK(!was_initialized_with_io_thread_ || + ChromeThread::CurrentlyOn(ChromeThread::IO)); + } + + void InitializeOnIOThread(const FilePath& data_directory, + bool is_incognito) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + Initialize(is_incognito ? FilePath() + : data_directory.Append(chrome::kAppCacheDirname)); + } + + bool was_initialized_with_io_thread_; +}; + +#endif // CHROME_COMMON_APPCACHE_CHROME_APPCACHE_SERVICE_H_ diff --git a/chrome/common/chrome_constants.cc b/chrome/common/chrome_constants.cc index 6e578c5..d892a8f 100644 --- a/chrome/common/chrome_constants.cc +++ b/chrome/common/chrome_constants.cc @@ -64,6 +64,7 @@ const FilePath::CharType kCacheDirname[] = FPL("Cache"); const FilePath::CharType kMediaCacheDirname[] = FPL("Media Cache"); const FilePath::CharType kOffTheRecordMediaCacheDirname[] = FPL("Incognito Media Cache"); +const FilePath::CharType kAppCacheDirname[] = FPL("Application Cache"); const wchar_t kChromePluginDataDirname[] = L"Plugin Data"; const wchar_t kThemeImagesDirname[] = L"Cached Theme Images"; const FilePath::CharType kCookieFilename[] = FPL("Cookies"); diff --git a/chrome/common/chrome_constants.h b/chrome/common/chrome_constants.h index 9e76426..0f5ec52 100644 --- a/chrome/common/chrome_constants.h +++ b/chrome/common/chrome_constants.h @@ -30,6 +30,7 @@ extern const FilePath::CharType kArchivedHistoryFilename[]; extern const FilePath::CharType kCacheDirname[]; extern const FilePath::CharType kMediaCacheDirname[]; extern const FilePath::CharType kOffTheRecordMediaCacheDirname[]; +extern const FilePath::CharType kAppCacheDirname[]; extern const wchar_t kChromePluginDataDirname[]; extern const wchar_t kThemeImagesDirname[]; extern const FilePath::CharType kCookieFilename[]; diff --git a/chrome/test/testing_profile.h b/chrome/test/testing_profile.h index ff89b61..d786017 100644 --- a/chrome/test/testing_profile.h +++ b/chrome/test/testing_profile.h @@ -75,6 +75,9 @@ class TestingProfile : public Profile { virtual Profile* GetOriginalProfile() { return this; } + virtual ChromeAppCacheService* GetAppCacheService() { + return NULL; + } virtual VisitedLinkMaster* GetVisitedLinkMaster() { return NULL; } @@ -130,11 +133,9 @@ class TestingProfile : public Profile { virtual TemplateURLFetcher* GetTemplateURLFetcher() { return NULL; } - virtual ThumbnailStore* GetThumbnailStore() { return NULL; } - virtual DownloadManager* GetDownloadManager() { return NULL; } diff --git a/webkit/appcache/appcache_backend_impl.cc b/webkit/appcache/appcache_backend_impl.cc index 1c0cfc7..82b40ee 100644 --- a/webkit/appcache/appcache_backend_impl.cc +++ b/webkit/appcache/appcache_backend_impl.cc @@ -4,29 +4,34 @@ #include "webkit/appcache/appcache_backend_impl.h" -#include "base/logging.h" +#include "webkit/appcache/appcache_host.h" +#include "webkit/appcache/appcache_service.h" #include "webkit/appcache/web_application_cache_host_impl.h" namespace appcache { AppCacheBackendImpl::~AppCacheBackendImpl() { - // TODO(michaeln): if (service_) service_->UnregisterFrontend(frontend_); + if (service_) + service_->UnregisterBackend(this); } void AppCacheBackendImpl::Initialize(AppCacheService* service, - AppCacheFrontend* frontend) { - DCHECK(!service_ && !frontend_ && frontend); // DCHECK(service) + AppCacheFrontend* frontend, + int process_id) { + DCHECK(!service_ && !frontend_ && frontend && service); service_ = service; frontend_ = frontend; - // TODO(michaeln): service_->RegisterFrontend(frontend_); + process_id_ = process_id; + service_->RegisterBackend(this); } -void AppCacheBackendImpl::RegisterHost(int host_id) { - // TODO(michaeln): plumb to service +void AppCacheBackendImpl::RegisterHost(int id) { + DCHECK(hosts_.find(id) == hosts_.end()); + hosts_.insert(HostMap::value_type(id, AppCacheHost(id, frontend_))); } -void AppCacheBackendImpl::UnregisterHost(int host_id) { - // TODO(michaeln): plumb to service +void AppCacheBackendImpl::UnregisterHost(int id) { + hosts_.erase(id); } void AppCacheBackendImpl::SelectCache( @@ -34,7 +39,7 @@ void AppCacheBackendImpl::SelectCache( const GURL& document_url, const int64 cache_document_was_loaded_from, const GURL& manifest_url) { - // TODO(michaeln): plumb to service + // TODO(michaeln): write me frontend_->OnCacheSelected(host_id, kNoCacheId, UNCACHED); } @@ -42,22 +47,25 @@ void AppCacheBackendImpl::MarkAsForeignEntry( int host_id, const GURL& document_url, int64 cache_document_was_loaded_from) { - // TODO(michaeln): plumb to service + // TODO(michaeln): write me } -Status AppCacheBackendImpl::GetStatus(int host_id) { - // TODO(michaeln): plumb to service - return UNCACHED; +void AppCacheBackendImpl::GetStatusWithCallback( + int host_id, GetStatusCallback* callback, void* callback_param) { + // TODO(michaeln): write me + callback->Run(UNCACHED, callback_param); } -bool AppCacheBackendImpl::StartUpdate(int host_id) { - // TODO(michaeln): plumb to service - return false; +void AppCacheBackendImpl::StartUpdateWithCallback( + int host_id, StartUpdateCallback* callback, void* callback_param) { + // TODO(michaeln): write me + callback->Run(false, callback_param); } -bool AppCacheBackendImpl::SwapCache(int host_id) { - // TODO(michaeln): plumb to service - return false; +void AppCacheBackendImpl::SwapCacheWithCallback( + int host_id, SwapCacheCallback* callback, void* callback_param) { + // TODO(michaeln): write me + callback->Run(false, callback_param); } } // namespace appcache diff --git a/webkit/appcache/appcache_backend_impl.h b/webkit/appcache/appcache_backend_impl.h index 93a7083..abf4247 100644 --- a/webkit/appcache/appcache_backend_impl.h +++ b/webkit/appcache/appcache_backend_impl.h @@ -5,20 +5,42 @@ #ifndef WEBKIT_APPCACHE_APPCACHE_BACKEND_IMPL_H_ #define WEBKIT_APPCACHE_APPCACHE_BACKEND_IMPL_H_ +#include <map> + +#include "base/logging.h" +#include "base/task.h" +#include "webkit/appcache/appcache_host.h" #include "webkit/appcache/appcache_interfaces.h" namespace appcache { class AppCacheService; +typedef Callback2<Status, void*>::Type GetStatusCallback; +typedef Callback2<bool, void*>::Type StartUpdateCallback; +typedef Callback2<bool, void*>::Type SwapCacheCallback; + class AppCacheBackendImpl : public AppCacheBackend { public: - AppCacheBackendImpl() : service_(NULL), frontend_(NULL) {} + AppCacheBackendImpl() : service_(NULL), frontend_(NULL), process_id_(0) {} ~AppCacheBackendImpl(); void Initialize(AppCacheService* service, - AppCacheFrontend* frontend); + AppCacheFrontend* frontend, + int process_id); + + int process_id() const { return process_id_; } + + // Returns a pointer to a registered host. The backend retains ownership. + AppCacheHost* GetHost(int host_id) { + HostMap::iterator it = hosts_.find(host_id); + return (it != hosts_.end()) ? &(it->second) : NULL; + } + typedef std::map<int, AppCacheHost> HostMap; + const HostMap& hosts() { return hosts_; } + + // AppCacheBackend methods virtual void RegisterHost(int host_id); virtual void UnregisterHost(int host_id); virtual void SelectCache(int host_id, @@ -27,13 +49,25 @@ class AppCacheBackendImpl : public AppCacheBackend { const GURL& manifest_url); virtual void MarkAsForeignEntry(int host_id, const GURL& document_url, int64 cache_document_was_loaded_from); - virtual Status GetStatus(int host_id); - virtual bool StartUpdate(int host_id); - virtual bool SwapCache(int host_id); + + // We don't use the sync variants in the backend, would block the IO thread. + virtual Status GetStatus(int host_id) { NOTREACHED(); return UNCACHED; } + virtual bool StartUpdate(int host_id) { NOTREACHED(); return false; } + virtual bool SwapCache(int host_id) { NOTREACHED(); return false; } + + // Async variants of the sync methods defined in the backend interface. + void GetStatusWithCallback(int host_id, GetStatusCallback* callback, + void* callback_param); + void StartUpdateWithCallback(int host_id, StartUpdateCallback* callback, + void* callback_param); + void SwapCacheWithCallback(int host_id, SwapCacheCallback* callback, + void* callback_param); private: AppCacheService* service_; AppCacheFrontend* frontend_; + int process_id_; + HostMap hosts_; }; } // namespace diff --git a/webkit/appcache/appcache_host.h b/webkit/appcache/appcache_host.h index 439e042..bebc22d 100644 --- a/webkit/appcache/appcache_host.h +++ b/webkit/appcache/appcache_host.h @@ -24,6 +24,10 @@ class AppCacheHost { selected_cache_ = cache; } + bool is_selection_pending() { + return false; // TODO(michaeln) + } + private: // identifies the corresponding appcache host in the child process int host_id_; diff --git a/webkit/appcache/appcache_interceptor.cc b/webkit/appcache/appcache_interceptor.cc new file mode 100644 index 0000000..a66fea8 --- /dev/null +++ b/webkit/appcache/appcache_interceptor.cc @@ -0,0 +1,125 @@ +// 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 "webkit/appcache/appcache_interceptor.h" + +#include "webkit/appcache/appcache_backend_impl.h" +#include "webkit/appcache/appcache_host.h" +#include "webkit/appcache/appcache_interfaces.h" +#include "webkit/appcache/appcache_service.h" + +namespace appcache { + +// Extra info we associate with requests for use at MaybeIntercept time. This +// info is deleted when the URLRequest is deleted which occurs after the +// request is complete and all data has been read. +struct AppCacheInterceptor::ExtraInfo : public URLRequest::UserData { + // Inputs, extra request info + AppCacheService* service; + int process_id; + int host_id; + ResourceType::Type resource_type; + + // Outputs, extra response info + int64 cache_id; + GURL manifest_url; + + // The host associated with the request + // TODO(michaeln): Be careful with this data member, its not clear + // if a URLRequest can outlive the associated host. As we get further + // along, we'll need to notify reqeust waiting on cache selection to + // allow them to continue upon completion of selection. But we also need + // to handle navigating away from the page away prior to selection being + // complete. + AppCacheHost* host_; + + ExtraInfo(AppCacheService* service, int process_id, int host_id, + ResourceType::Type resource_type, AppCacheHost* host) + : service(service), process_id(process_id), host_id(host_id), + resource_type(resource_type), cache_id(kNoCacheId), host_(host) { + } + + static void SetInfo(URLRequest* request, ExtraInfo* info) { + request->SetUserData(instance(), info); // request takes ownership + } + + static ExtraInfo* GetInfo(URLRequest* request) { + return static_cast<ExtraInfo*>(request->GetUserData(instance())); + } +}; + +static bool IsMainRequest(ResourceType::Type type) { + // TODO(michaeln): SHARED_WORKER type? + return ResourceType::IsFrame(type); +} + +void AppCacheInterceptor::SetExtraRequestInfo( + URLRequest* request, AppCacheService* service, int process_id, + int host_id, ResourceType::Type resource_type) { + if (service && (host_id != kNoHostId)) { + AppCacheHost* host = service->GetBackend(process_id)->GetHost(host_id); + DCHECK(host); + if (IsMainRequest(resource_type) || host->selected_cache() || + host->is_selection_pending()) { + ExtraInfo* info = new ExtraInfo(service, process_id, + host_id, resource_type, host); + ExtraInfo::SetInfo(request, info); + } + } +} + +void AppCacheInterceptor::GetExtraResponseInfo(URLRequest* request, + int64* cache_id, + GURL* manifest_url) { + ExtraInfo* info = ExtraInfo::GetInfo(request); + if (info) { + // TODO(michaeln): If this is a main request and it was retrieved from + // an appcache, ensure that appcache survives the frame navigation. The + // AppCacheHost should hold reference to that cache to prevent it from + // being dropped from the in-memory collection of AppCaches. When cache + // selection occurs, that extra reference should be dropped. + *cache_id = info->cache_id; + *manifest_url = info->manifest_url; + } else { + DCHECK(*cache_id == kNoCacheId); + DCHECK(manifest_url->is_empty()); + } +} + +AppCacheInterceptor::AppCacheInterceptor() { + URLRequest::RegisterRequestInterceptor(this); +} + +AppCacheInterceptor::~AppCacheInterceptor() { + URLRequest::UnregisterRequestInterceptor(this); +} + +URLRequestJob* AppCacheInterceptor::MaybeIntercept(URLRequest* request) { + ExtraInfo* info = ExtraInfo::GetInfo(request); + if (!info) + return NULL; + // TODO(michaeln): write me + return NULL; +} + +URLRequestJob* AppCacheInterceptor::MaybeInterceptRedirect( + URLRequest* request, + const GURL& location) { + ExtraInfo* info = ExtraInfo::GetInfo(request); + if (!info) + return NULL; + // TODO(michaeln): write me + return NULL; +} + +URLRequestJob* AppCacheInterceptor::MaybeInterceptResponse( + URLRequest* request) { + ExtraInfo* info = ExtraInfo::GetInfo(request); + if (!info) + return NULL; + // TODO(michaeln): write me + return NULL; +} + +} // namespace appcache diff --git a/webkit/appcache/appcache_interceptor.h b/webkit/appcache/appcache_interceptor.h new file mode 100644 index 0000000..51b9a89 --- /dev/null +++ b/webkit/appcache/appcache_interceptor.h @@ -0,0 +1,60 @@ +// 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. + +#ifndef WEBKIT_APPCACHE_APPCACHE_INTERCEPTOR_H_ +#define WEBKIT_APPCACHE_APPCACHE_INTERCEPTOR_H_ + +#include "base/singleton.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request.h" +#include "webkit/glue/resource_type.h" + +namespace appcache { + +class AppCacheService; + +// An interceptor to hijack requests and potentially service them out of +// the appcache. +class AppCacheInterceptor : public URLRequest::Interceptor { + public: + // Registers a singleton instance with the net library. + // Should be called early in the IO thread prior to initiating requests. + static void EnsureRegistered() { + CHECK(instance()); + } + + // Must be called to make a request eligible for retrieval from an appcache. + static void SetExtraRequestInfo(URLRequest* request, + AppCacheService* service, + int process_id, + int host_id, + ResourceType::Type resource_type); + + // May be called after response headers are complete to retrieve extra + // info about the response. + static void GetExtraResponseInfo(URLRequest* request, + int64* cache_id, + GURL* manifest_url); + + protected: + // URLRequest::Interceptor overrides + virtual URLRequestJob* MaybeIntercept(URLRequest* request); + virtual URLRequestJob* MaybeInterceptResponse(URLRequest* request); + virtual URLRequestJob* MaybeInterceptRedirect(URLRequest* request, + const GURL& location); + + private: + friend struct DefaultSingletonTraits<AppCacheInterceptor>; + static AppCacheInterceptor* instance() { + return Singleton<AppCacheInterceptor>::get(); + } + struct ExtraInfo; + AppCacheInterceptor(); + virtual ~AppCacheInterceptor(); + DISALLOW_COPY_AND_ASSIGN(AppCacheInterceptor); +}; + +} // namespace appcache + +#endif // WEBKIT_APPCACHE_APPCACHE_INTERCEPTOR_H_ diff --git a/webkit/appcache/appcache_service.cc b/webkit/appcache/appcache_service.cc index 0d3579b..0fd5356 100644 --- a/webkit/appcache/appcache_service.cc +++ b/webkit/appcache/appcache_service.cc @@ -17,14 +17,21 @@ AppCacheService::~AppCacheService() { DCHECK(groups_.empty()); } -void AppCacheService::RegisterBackendImpl( +void AppCacheService::Initialize(const FilePath& cache_directory) { + // An empty cache directory indicates chrome incognito. + cache_directory_ = cache_directory; +} + +void AppCacheService::RegisterBackend( AppCacheBackendImpl* backend_impl) { - backends_.insert(backend_impl); + DCHECK(backends_.find(backend_impl->process_id()) == backends_.end()); + backends_.insert( + BackendMap::value_type(backend_impl->process_id(), backend_impl)); } -void AppCacheService::UnregisterBackendImpl( +void AppCacheService::UnregisterBackend( AppCacheBackendImpl* backend_impl) { - backends_.erase(backend_impl); + backends_.erase(backend_impl->process_id()); } void AppCacheService::AddCache(AppCache* cache) { diff --git a/webkit/appcache/appcache_service.h b/webkit/appcache/appcache_service.h index 3eaa43d..bc8bee8 100644 --- a/webkit/appcache/appcache_service.h +++ b/webkit/appcache/appcache_service.h @@ -10,6 +10,7 @@ #include <vector> #include "base/hash_tables.h" +#include "base/file_path.h" #include "googleurl/src/gurl.h" namespace appcache { @@ -24,17 +25,34 @@ class AppCacheService { public: virtual ~AppCacheService(); + void Initialize(const FilePath& cache_directory); + // TODO(jennb): API to set service settings, like file paths for storage // track which processes are using this appcache service - void RegisterBackendImpl(AppCacheBackendImpl* backend_impl); - void UnregisterBackendImpl(AppCacheBackendImpl* backend_impl); + void RegisterBackend(AppCacheBackendImpl* backend_impl); + void UnregisterBackend(AppCacheBackendImpl* backend_impl); void AddCache(AppCache* cache); void RemoveCache(AppCache* cache); void AddGroup(AppCacheGroup* group); void RemoveGroup(AppCacheGroup* group); + AppCacheBackendImpl* GetBackend(int id) { + BackendMap::iterator it = backends_.find(id); + return (it != backends_.end()) ? it->second : NULL; + } + + AppCache* GetCache(int64 id) { + CacheMap::iterator it = caches_.find(id); + return (it != caches_.end()) ? it->second : NULL; + } + + AppCacheGroup* GetGroup(const GURL& manifest_url) { + GroupMap::iterator it = groups_.find(manifest_url); + return (it != groups_.end()) ? it->second : NULL; + } + private: // In-memory representation of stored appcache data. Represents a subset // of the appcache database currently in use. @@ -44,9 +62,10 @@ class AppCacheService { GroupMap groups_; // Track current processes. One 'backend' per child process. - typedef std::set<AppCacheBackendImpl*> BackendSet; - BackendSet backends_; + typedef std::map<int, AppCacheBackendImpl*> BackendMap; + BackendMap backends_; + FilePath cache_directory_; // TODO(jennb): info about appcache storage // AppCacheDatabase db_; // DiskCache response_storage_; diff --git a/webkit/appcache/web_application_cache_host_impl.cc b/webkit/appcache/web_application_cache_host_impl.cc index 02a698f..d5d50fb 100644 --- a/webkit/appcache/web_application_cache_host_impl.cc +++ b/webkit/appcache/web_application_cache_host_impl.cc @@ -43,6 +43,7 @@ WebApplicationCacheHostImpl::WebApplicationCacheHostImpl( WebApplicationCacheHostImpl::~WebApplicationCacheHostImpl() { backend_->UnregisterHost(host_id_); + all_hosts.Remove(host_id_); } void WebApplicationCacheHostImpl::OnCacheSelected(int64 selected_cache_id, diff --git a/webkit/tools/test_shell/simple_appcache_system.cc b/webkit/tools/test_shell/simple_appcache_system.cc new file mode 100644 index 0000000..5ab7fe2 --- /dev/null +++ b/webkit/tools/test_shell/simple_appcache_system.cc @@ -0,0 +1,323 @@ +// 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 "webkit/tools/test_shell/simple_appcache_system.h" + +#include "base/lock.h" +#include "base/task.h" +#include "base/waitable_event.h" +#include "webkit/appcache/appcache_interceptor.h" +#include "webkit/appcache/web_application_cache_host_impl.h" +#include "webkit/tools/test_shell/simple_resource_loader_bridge.h" + +using WebKit::WebApplicationCacheHost; +using WebKit::WebApplicationCacheHostClient; +using appcache::WebApplicationCacheHostImpl; +using appcache::AppCacheBackendImpl; +using appcache::AppCacheInterceptor; + + +// SimpleFrontendProxy -------------------------------------------------------- +// Proxies method calls from the backend IO thread to the frontend UI thread. + +class SimpleFrontendProxy + : public base::RefCountedThreadSafe<SimpleFrontendProxy>, + public appcache::AppCacheFrontend { + public: + explicit SimpleFrontendProxy(SimpleAppCacheSystem* appcache_system) + : system_(appcache_system) { + } + + void clear_appcache_system() { system_ = NULL; } + + virtual void OnCacheSelected(int host_id, int64 cache_id , + appcache::Status status) { + if (!system_) + return; + if (system_->is_io_thread()) + system_->ui_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleFrontendProxy::OnCacheSelected, + host_id, cache_id, status)); + else if (system_->is_ui_thread()) + system_->frontend_impl_.OnCacheSelected(host_id, cache_id, status); + else + NOTREACHED(); + } + + virtual void OnStatusChanged(const std::vector<int>& host_ids, + appcache::Status status) { + if (!system_) + return; + if (system_->is_io_thread()) + system_->ui_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleFrontendProxy::OnStatusChanged, host_ids, status)); + else if (system_->is_ui_thread()) + system_->frontend_impl_.OnStatusChanged(host_ids, status); + else + NOTREACHED(); + } + + virtual void OnEventRaised(const std::vector<int>& host_ids, + appcache::EventID event_id) { + if (!system_) + return; + if (system_->is_io_thread()) + system_->ui_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleFrontendProxy::OnEventRaised, host_ids, event_id)); + else if (system_->is_ui_thread()) + system_->frontend_impl_.OnEventRaised(host_ids, event_id); + else + NOTREACHED(); + } + + private: + SimpleAppCacheSystem* system_; +}; + + +// SimpleBackendProxy -------------------------------------------------------- +// Proxies method calls from the frontend UI thread to the backend IO thread. + +class SimpleBackendProxy + : public base::RefCountedThreadSafe<SimpleBackendProxy>, + public appcache::AppCacheBackend { + public: + explicit SimpleBackendProxy(SimpleAppCacheSystem* appcache_system) + : system_(appcache_system), event_(true, false) { + get_status_callback_.reset( + NewCallback(this, &SimpleBackendProxy::GetStatusCallback)); + start_update_callback_.reset( + NewCallback(this, &SimpleBackendProxy::StartUpdateCallback)); + swap_cache_callback_.reset( + NewCallback(this, &SimpleBackendProxy::SwapCacheCallback)); + } + + virtual void RegisterHost(int host_id) { + if (system_->is_ui_thread()) { + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::RegisterHost, host_id)); + } else if (system_->is_io_thread()) { + system_->backend_impl_->RegisterHost(host_id); + } else { + NOTREACHED(); + } + } + + virtual void UnregisterHost(int host_id) { + if (system_->is_ui_thread()) { + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::UnregisterHost, host_id)); + } else if (system_->is_io_thread()) { + system_->backend_impl_->UnregisterHost(host_id); + } else { + NOTREACHED(); + } + } + + virtual void SelectCache(int host_id, + const GURL& document_url, + const int64 cache_document_was_loaded_from, + const GURL& manifest_url) { + if (system_->is_ui_thread()) { + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::SelectCache, host_id, document_url, + cache_document_was_loaded_from, manifest_url)); + } else if (system_->is_io_thread()) { + system_->backend_impl_->SelectCache(host_id, document_url, + cache_document_was_loaded_from, + manifest_url); + } else { + NOTREACHED(); + } + } + + virtual void MarkAsForeignEntry(int host_id, const GURL& document_url, + int64 cache_document_was_loaded_from) { + if (system_->is_ui_thread()) { + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::MarkAsForeignEntry, host_id, document_url, + cache_document_was_loaded_from)); + } else if (system_->is_io_thread()) { + system_->backend_impl_->MarkAsForeignEntry( + host_id, document_url, + cache_document_was_loaded_from); + } else { + NOTREACHED(); + } + } + + virtual appcache::Status GetStatus(int host_id) { + if (system_->is_ui_thread()) { + status_result_ = appcache::UNCACHED; + event_.Reset(); + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::GetStatus, host_id)); + event_.Wait(); + } else if (system_->is_io_thread()) { + system_->backend_impl_->GetStatusWithCallback( + host_id, get_status_callback_.get(), NULL); + } else { + NOTREACHED(); + } + return status_result_; + } + + virtual bool StartUpdate(int host_id) { + if (system_->is_ui_thread()) { + bool_result_ = false; + event_.Reset(); + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::StartUpdate, host_id)); + event_.Wait(); + } else if (system_->is_io_thread()) { + system_->backend_impl_->StartUpdateWithCallback( + host_id, start_update_callback_.get(), NULL); + } else { + NOTREACHED(); + } + return bool_result_; + } + + virtual bool SwapCache(int host_id) { + if (system_->is_ui_thread()) { + bool_result_ = false; + event_.Reset(); + system_->io_message_loop()->PostTask(FROM_HERE, NewRunnableMethod( + this, &SimpleBackendProxy::SwapCache, host_id)); + event_.Wait(); + } else if (system_->is_io_thread()) { + system_->backend_impl_->SwapCacheWithCallback( + host_id, swap_cache_callback_.get(), NULL); + } else { + NOTREACHED(); + } + return bool_result_; + } + + void GetStatusCallback(appcache::Status status, void* param) { + status_result_ = status; + event_.Signal(); + } + + void StartUpdateCallback(bool result, void* param) { + bool_result_ = result; + event_.Signal(); + } + + void SwapCacheCallback(bool result, void* param) { + bool_result_ = result; + event_.Signal(); + } + + void SignalEvent() { + event_.Signal(); + } + + private: + SimpleAppCacheSystem* system_; + base::WaitableEvent event_; + bool bool_result_; + appcache::Status status_result_; + scoped_ptr<appcache::GetStatusCallback> get_status_callback_; + scoped_ptr<appcache::StartUpdateCallback> start_update_callback_; + scoped_ptr<appcache::SwapCacheCallback> swap_cache_callback_; +}; + + +// SimpleAppCacheSystem -------------------------------------------------------- + +// This class only works for a single process browser. +static const int kSingleProcessId = 1; + +// A not so thread safe singleton, but should work for test_shell. +SimpleAppCacheSystem* SimpleAppCacheSystem::instance_ = NULL; + +SimpleAppCacheSystem::SimpleAppCacheSystem() + : io_message_loop_(NULL), ui_message_loop_(NULL), + ALLOW_THIS_IN_INITIALIZER_LIST( + backend_proxy_(new SimpleBackendProxy(this))), + ALLOW_THIS_IN_INITIALIZER_LIST( + frontend_proxy_(new SimpleFrontendProxy(this))), + backend_impl_(NULL), service_(NULL) { + DCHECK(!instance_); + instance_ = this; +} + +SimpleAppCacheSystem::~SimpleAppCacheSystem() { + DCHECK(!io_message_loop_ && !backend_impl_ && !service_); + frontend_proxy_->clear_appcache_system(); // in case a task is in transit + instance_ = NULL; +} + +void SimpleAppCacheSystem::InitOnUIThread( + const FilePath& cache_directory) { + DCHECK(!ui_message_loop_); + DCHECK(!cache_directory.empty()); + ui_message_loop_ = MessageLoop::current(); + cache_directory_ = cache_directory; +} + +void SimpleAppCacheSystem::InitOnIOThread() { + if (!is_initailized_on_ui_thread()) + return; + + DCHECK(!io_message_loop_); + io_message_loop_ = MessageLoop::current(); + io_message_loop_->AddDestructionObserver(this); + + // Recreate and initialize per each IO thread. + service_ = new appcache::AppCacheService(); + backend_impl_ = new appcache::AppCacheBackendImpl(); + service_->Initialize(cache_directory_); + backend_impl_->Initialize(service_, frontend_proxy_.get(), kSingleProcessId); + + AppCacheInterceptor::EnsureRegistered(); +} + +WebApplicationCacheHost* SimpleAppCacheSystem::CreateCacheHostForWebKit( + WebApplicationCacheHostClient* client) { + if (!is_initailized_on_ui_thread()) + return NULL; + + DCHECK(is_ui_thread()); + + // The IO thread needs to be running for this system to work. + SimpleResourceLoaderBridge::EnsureIOThread(); + + if (!is_initialized()) + return NULL; + return new WebApplicationCacheHostImpl(client, backend_proxy_.get()); +} + +void SimpleAppCacheSystem::SetExtraRequestBits( + URLRequest* request, int host_id, ResourceType::Type resource_type) { + if (is_initialized()) { + DCHECK(is_io_thread()); + AppCacheInterceptor::SetExtraRequestInfo( + request, service_, kSingleProcessId, host_id, resource_type); + } +} + +void SimpleAppCacheSystem::GetExtraResponseBits( + URLRequest* request, int64* cache_id, GURL* manifest_url) { + if (is_initialized()) { + DCHECK(is_io_thread()); + AppCacheInterceptor::GetExtraResponseInfo( + request, cache_id, manifest_url); + } +} + +void SimpleAppCacheSystem::WillDestroyCurrentMessageLoop() { + DCHECK(is_io_thread()); + DCHECK(backend_impl_->hosts().empty()); + + io_message_loop_ = NULL; + delete backend_impl_; + delete service_; + backend_impl_ = NULL; + service_ = NULL; + + // Just in case the main thread is waiting on it. + backend_proxy_->SignalEvent(); +} diff --git a/webkit/tools/test_shell/simple_appcache_system.h b/webkit/tools/test_shell/simple_appcache_system.h index 6f53524..184842f 100644 --- a/webkit/tools/test_shell/simple_appcache_system.h +++ b/webkit/tools/test_shell/simple_appcache_system.h @@ -5,21 +5,117 @@ #ifndef WEBKIT_TOOLS_TEST_SHELL_SIMPLE_APPCACHE_SYSTEM_H_ #define WEBKIT_TOOLS_TEST_SHELL_SIMPLE_APPCACHE_SYSTEM_H_ +#include "base/file_path.h" +#include "base/message_loop.h" #include "webkit/appcache/appcache_backend_impl.h" #include "webkit/appcache/appcache_frontend_impl.h" +#include "webkit/appcache/appcache_service.h" +#include "webkit/glue/resource_type.h" -class SimpleAppCacheSystem { +namespace WebKit { +class WebApplicationCacheHost; +class WebApplicationCacheHostClient; +} +class SimpleBackendProxy; +class SimpleFrontendProxy; +class URLRequest; + +// A class that composes the constituent parts of an appcache system +// together for use in a single process with two relavant threads, +// a UI thread on which webkit runs and an IO thread on which URLRequests +// are handled. This class conspires with SimpleResourceLoaderBridge to +// retrieve resources from the appcache. +class SimpleAppCacheSystem : public MessageLoop::DestructionObserver { public: - void Initialize() { - backend_impl_.Initialize(NULL, &frontend_impl_); + // Should be instanced somewhere in main(). If not instanced, the public + // static methods are all safe no-ops. + SimpleAppCacheSystem(); + virtual ~SimpleAppCacheSystem(); + + // One-time main UI thread initialization. + static void InitializeOnUIThread(const FilePath& cache_directory) { + if (instance_) + instance_->InitOnUIThread(cache_directory); } - appcache::AppCacheBackend* backend() { return &backend_impl_; } - appcache::AppCacheFrontend* frontend() { return &frontend_impl_; } + // Called by SimpleResourceLoaderBridge's IOThread class. + // Per IO thread initialization. Only one IO thread can exist + // at a time, but after IO thread termination a new one can be + // started on which this method should be called. The instance + // is assumed to outlive the IO thread. + static void InitializeOnIOThread() { + if (instance_) + instance_->InitOnIOThread(); + } + + // Called by TestShellWebKitInit to manufacture a 'host' for webcore. + static WebKit::WebApplicationCacheHost* CreateApplicationCacheHost( + WebKit::WebApplicationCacheHostClient* client) { + return instance_ ? instance_->CreateCacheHostForWebKit(client) : NULL; + } + + // Called by SimpleResourceLoaderBridge to hook into resource loads. + static void SetExtraRequestInfo(URLRequest* request, + int host_id, + ResourceType::Type resource_type) { + if (instance_) + instance_->SetExtraRequestBits(request, host_id, resource_type); + } + + // Called by SimpleResourceLoaderBridge extract extra response bits. + static void GetExtraResponseInfo(URLRequest* request, + int64* cache_id, + GURL* manifest_url) { + if (instance_) + instance_->GetExtraResponseBits(request, cache_id, manifest_url); + } private: - appcache::AppCacheBackendImpl backend_impl_; + friend class SimpleBackendProxy; + friend class SimpleFrontendProxy; + + // A low-tech singleton. + static SimpleAppCacheSystem* instance_; + + // Instance methods called by our static public methods + void InitOnUIThread(const FilePath& cache_directory); + void InitOnIOThread(); + WebKit::WebApplicationCacheHost* CreateCacheHostForWebKit( + WebKit::WebApplicationCacheHostClient* client); + void SetExtraRequestBits(URLRequest* request, + int host_id, + ResourceType::Type resource_type); + void GetExtraResponseBits(URLRequest* request, + int64* cache_id, + GURL* manifest_url); + + // Helpers + MessageLoop* io_message_loop() { return io_message_loop_; } + MessageLoop* ui_message_loop() { return ui_message_loop_; } + bool is_io_thread() { return MessageLoop::current() == io_message_loop_; } + bool is_ui_thread() { return MessageLoop::current() == ui_message_loop_; } + bool is_initialized() { + return io_message_loop_ && is_initailized_on_ui_thread(); + } + bool is_initailized_on_ui_thread() { + return ui_message_loop_ && !cache_directory_.empty(); + } + + // IOThread DestructionObserver + virtual void WillDestroyCurrentMessageLoop(); + + FilePath cache_directory_; + MessageLoop* io_message_loop_; + MessageLoop* ui_message_loop_; + scoped_refptr<SimpleBackendProxy> backend_proxy_; + scoped_refptr<SimpleFrontendProxy> frontend_proxy_; appcache::AppCacheFrontendImpl frontend_impl_; + + // Created and used only on the IO thread, these do + // not survive IO thread termination. If a new IO thread + // is started new instances will be created. + appcache::AppCacheBackendImpl* backend_impl_; + appcache::AppCacheService* service_; }; #endif // WEBKIT_TOOLS_TEST_SHELL_SIMPLE_APPCACHE_SYSTEM_H_ diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.cc b/webkit/tools/test_shell/simple_resource_loader_bridge.cc index c27f061..46126a0 100644 --- a/webkit/tools/test_shell/simple_resource_loader_bridge.cc +++ b/webkit/tools/test_shell/simple_resource_loader_bridge.cc @@ -48,6 +48,7 @@ #include "net/url_request/url_request.h" #include "webkit/appcache/appcache_interfaces.h" #include "webkit/glue/resource_loader_bridge.h" +#include "webkit/tools/test_shell/simple_appcache_system.h" #include "webkit/tools/test_shell/test_shell_request_context.h" using webkit_glue::ResourceLoaderBridge; @@ -71,6 +72,10 @@ class IOThread : public base::Thread { Stop(); } + virtual void Init() { + SimpleAppCacheSystem::InitializeOnIOThread(); + } + virtual void CleanUp() { if (request_context) { request_context->Release(); @@ -79,19 +84,6 @@ class IOThread : public base::Thread { } }; -bool EnsureIOThread() { - if (io_thread) - return true; - - if (!request_context) - SimpleResourceLoaderBridge::Init(NULL); - - io_thread = new IOThread(); - base::Thread::Options options; - options.message_loop_type = MessageLoop::TYPE_IO; - return io_thread->StartWithOptions(options); -} - //----------------------------------------------------------------------------- struct RequestParams { @@ -101,6 +93,7 @@ struct RequestParams { GURL referrer; std::string headers; int load_flags; + ResourceType::Type request_type; int appcache_host_id; scoped_refptr<net::UploadData> upload; }; @@ -214,6 +207,9 @@ class RequestProxy : public URLRequest::Delegate, request_->set_load_flags(params->load_flags); request_->set_upload(params->upload.get()); request_->set_context(request_context); + SimpleAppCacheSystem::SetExtraRequestInfo( + request_.get(), params->appcache_host_id, params->request_type); + request_->Start(); if (request_->has_upload() && @@ -376,11 +372,13 @@ class RequestProxy : public URLRequest::Delegate, info->request_time = request->request_time(); info->response_time = request->response_time(); info->headers = request->response_headers(); - info->appcache_id = appcache::kNoCacheId; - // TODO(michaeln): info->appcache_manifest_url = GURL(); request->GetMimeType(&info->mime_type); request->GetCharset(&info->charset); info->content_length = request->GetExpectedContentSize(); + SimpleAppCacheSystem::GetExtraResponseInfo( + request, + &info->appcache_id, + &info->appcache_manifest_url); } scoped_ptr<URLRequest> request_; @@ -469,6 +467,7 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { const GURL& referrer, const std::string& headers, int load_flags, + ResourceType::Type request_type, int appcache_host_id) : params_(new RequestParams), proxy_(NULL) { @@ -478,6 +477,7 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { params_->referrer = referrer; params_->headers = headers; params_->load_flags = load_flags; + params_->request_type = request_type; params_->appcache_host_id = appcache_host_id; } @@ -517,7 +517,7 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { virtual bool Start(Peer* peer) { DCHECK(!proxy_); - if (!EnsureIOThread()) + if (!SimpleResourceLoaderBridge::EnsureIOThread()) return false; proxy_ = new RequestProxy(); @@ -540,7 +540,7 @@ class ResourceLoaderBridgeImpl : public ResourceLoaderBridge { virtual void SyncLoad(SyncLoadResponse* response) { DCHECK(!proxy_); - if (!EnsureIOThread()) + if (!SimpleResourceLoaderBridge::EnsureIOThread()) return; // this may change as the result of a redirect @@ -616,7 +616,7 @@ ResourceLoaderBridge* ResourceLoaderBridge::Create( int routing_id) { return new ResourceLoaderBridgeImpl(method, url, first_party_for_cookies, referrer, headers, load_flags, - appcache_host_id); + request_type, appcache_host_id); } // Issue the proxy resolve request on the io thread, and wait @@ -696,3 +696,16 @@ std::string SimpleResourceLoaderBridge::GetCookies( return getter->GetResult(); } + +bool SimpleResourceLoaderBridge::EnsureIOThread() { + if (io_thread) + return true; + + if (!request_context) + SimpleResourceLoaderBridge::Init(NULL); + + io_thread = new IOThread(); + base::Thread::Options options; + options.message_loop_type = MessageLoop::TYPE_IO; + return io_thread->StartWithOptions(options); +} diff --git a/webkit/tools/test_shell/simple_resource_loader_bridge.h b/webkit/tools/test_shell/simple_resource_loader_bridge.h index 24df90c..460071b 100644 --- a/webkit/tools/test_shell/simple_resource_loader_bridge.h +++ b/webkit/tools/test_shell/simple_resource_loader_bridge.h @@ -33,6 +33,7 @@ class SimpleResourceLoaderBridge { const std::string& cookie); static std::string GetCookies(const GURL& url, const GURL& first_party_for_cookies); + static bool EnsureIOThread(); }; #endif // WEBKIT_TOOLS_TEST_SHELL_SIMPLE_RESOURCE_LOADER_BRIDGE_H__ diff --git a/webkit/tools/test_shell/test_shell.gyp b/webkit/tools/test_shell/test_shell.gyp index 73249ed..94d3863 100644 --- a/webkit/tools/test_shell/test_shell.gyp +++ b/webkit/tools/test_shell/test_shell.gyp @@ -63,6 +63,7 @@ 'mock_webclipboard_impl.cc', 'mock_webclipboard_impl.h', 'resource.h', + 'simple_appcache_system.cc', 'simple_appcache_system.h', 'simple_clipboard_impl.cc', 'simple_resource_loader_bridge.cc', diff --git a/webkit/tools/test_shell/test_shell_webkit_init.h b/webkit/tools/test_shell/test_shell_webkit_init.h index 2727d6b..354d949 100644 --- a/webkit/tools/test_shell/test_shell_webkit_init.h +++ b/webkit/tools/test_shell/test_shell_webkit_init.h @@ -7,6 +7,7 @@ #include "base/file_util.h" #include "base/path_service.h" +#include "base/scoped_temp_dir.h" #include "base/stats_counters.h" #include "base/string_util.h" #include "media/base/media.h" @@ -42,7 +43,6 @@ class TestShellWebKitInit : public webkit_glue::WebKitClientImpl { WebKit::enableV8SingleThreadMode(); WebKit::registerExtension(extensions_v8::GearsExtension::Get()); WebKit::registerExtension(extensions_v8::IntervalExtension::Get()); - appcache_system_.Initialize(); // Load libraries for media and enable the media player. FilePath module_path; @@ -50,6 +50,12 @@ class TestShellWebKitInit : public webkit_glue::WebKitClientImpl { media::InitializeMediaLibrary(module_path)) { WebKit::enableMediaPlayer(); } + + // Construct and initialize an appcache system for this scope. + // A new empty temp directory is created to house any cached + // content during the run. Upon exit that directory is deleted. + if (appcache_dir_.CreateUniqueTempDir()) + SimpleAppCacheSystem::InitializeOnUIThread(appcache_dir_.path()); } ~TestShellWebKitInit() { @@ -149,14 +155,14 @@ class TestShellWebKitInit : public webkit_glue::WebKitClientImpl { virtual WebKit::WebApplicationCacheHost* createApplicationCacheHost( WebKit::WebApplicationCacheHostClient* client) { - return new appcache::WebApplicationCacheHostImpl( - client, appcache_system_.backend()); + return SimpleAppCacheSystem::CreateApplicationCacheHost(client); } private: webkit_glue::SimpleWebMimeRegistryImpl mime_registry_; MockWebClipboardImpl mock_clipboard_; webkit_glue::WebClipboardImpl real_clipboard_; + ScopedTempDir appcache_dir_; SimpleAppCacheSystem appcache_system_; }; diff --git a/webkit/webkit.gyp b/webkit/webkit.gyp index 0a6fe18..397d56c 100644 --- a/webkit/webkit.gyp +++ b/webkit/webkit.gyp @@ -1258,6 +1258,8 @@ 'appcache/appcache_group.h', 'appcache/appcache_host.cc', 'appcache/appcache_host.h', + 'appcache/appcache_interceptor.cc', + 'appcache/appcache_interceptor.h', 'appcache/appcache_interfaces.cc', 'appcache/appcache_interfaces.h', 'appcache/appcache_service.cc', |