diff options
author | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 23:37:35 +0000 |
---|---|---|
committer | jam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-02-24 23:37:35 +0000 |
commit | 1eaa4f7c5ad1d08be49cea5b2c3c6cd64b5e2e1c (patch) | |
tree | 7e91b52608eac0a555856908c588b529caa58965 /content/browser | |
parent | fbf3ff838fb1dbe7493c4df4c0a619f6c406682d (diff) | |
download | chromium_src-1eaa4f7c5ad1d08be49cea5b2c3c6cd64b5e2e1c.zip chromium_src-1eaa4f7c5ad1d08be49cea5b2c3c6cd64b5e2e1c.tar.gz chromium_src-1eaa4f7c5ad1d08be49cea5b2c3c6cd64b5e2e1c.tar.bz2 |
Move appcache/file_sytem/device_orientation subdirectories of chrome\browser to content\browser.
TBR=avi
Review URL: http://codereview.chromium.org/6586001
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@75990 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'content/browser')
27 files changed, 2872 insertions, 0 deletions
diff --git a/content/browser/OWNERS b/content/browser/OWNERS new file mode 100644 index 0000000..63f6c51 --- /dev/null +++ b/content/browser/OWNERS @@ -0,0 +1 @@ +avi@chromium.org
diff --git a/content/browser/appcache/appcache_dispatcher_host.cc b/content/browser/appcache/appcache_dispatcher_host.cc new file mode 100644 index 0000000..a634f4a --- /dev/null +++ b/content/browser/appcache/appcache_dispatcher_host.cc @@ -0,0 +1,237 @@ +// Copyright (c) 2011 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 "content/browser/appcache/appcache_dispatcher_host.h" + +#include "base/callback.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" +#include "content/browser/appcache/chrome_appcache_service.h" +#include "chrome/common/render_messages.h" + +AppCacheDispatcherHost::AppCacheDispatcherHost( + net::URLRequestContext* request_context, + int process_id) + : ALLOW_THIS_IN_INITIALIZER_LIST(frontend_proxy_(this)), + request_context_(request_context), + process_id_(process_id) { + DCHECK(request_context_.get()); +} + +AppCacheDispatcherHost::AppCacheDispatcherHost( + URLRequestContextGetter* request_context_getter, + int process_id) + : ALLOW_THIS_IN_INITIALIZER_LIST(frontend_proxy_(this)), + request_context_getter_(request_context_getter), + process_id_(process_id) { + DCHECK(request_context_getter_.get()); +} + +AppCacheDispatcherHost::~AppCacheDispatcherHost() {} + +void AppCacheDispatcherHost::OnChannelConnected(int32 peer_pid) { + BrowserMessageFilter::OnChannelConnected(peer_pid); + + DCHECK(request_context_.get() || request_context_getter_.get()); + + // Get the AppCacheService (it can only be accessed from IO thread). + net::URLRequestContext* context = request_context_.get(); + if (!context) + context = request_context_getter_->GetURLRequestContext(); + appcache_service_ = + static_cast<ChromeURLRequestContext*>(context)->appcache_service(); + request_context_ = NULL; + request_context_getter_ = NULL; + + 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& message, + bool* message_was_ok) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(AppCacheDispatcherHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(AppCacheMsg_RegisterHost, OnRegisterHost) + IPC_MESSAGE_HANDLER(AppCacheMsg_UnregisterHost, OnUnregisterHost) + IPC_MESSAGE_HANDLER(AppCacheMsg_GetResourceList, OnGetResourceList) + IPC_MESSAGE_HANDLER(AppCacheMsg_SelectCache, OnSelectCache) + IPC_MESSAGE_HANDLER(AppCacheMsg_SelectCacheForWorker, + OnSelectCacheForWorker) + IPC_MESSAGE_HANDLER(AppCacheMsg_SelectCacheForSharedWorker, + OnSelectCacheForSharedWorker) + IPC_MESSAGE_HANDLER(AppCacheMsg_MarkAsForeignEntry, OnMarkAsForeignEntry) + IPC_MESSAGE_HANDLER_DELAY_REPLY(AppCacheMsg_GetStatus, OnGetStatus) + IPC_MESSAGE_HANDLER_DELAY_REPLY(AppCacheMsg_StartUpdate, OnStartUpdate) + IPC_MESSAGE_HANDLER_DELAY_REPLY(AppCacheMsg_SwapCache, OnSwapCache) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + + return handled; +} + +void AppCacheDispatcherHost::BadMessageReceived() { + UserMetrics::RecordAction(UserMetricsAction("BadMessageTerminate_ACDH")); + BrowserMessageFilter::BadMessageReceived(); +} + +void AppCacheDispatcherHost::OnRegisterHost(int host_id) { + if (appcache_service_.get()) { + if (!backend_impl_.RegisterHost(host_id)) { + BadMessageReceived(); + } + } +} + +void AppCacheDispatcherHost::OnUnregisterHost(int host_id) { + if (appcache_service_.get()) { + if (!backend_impl_.UnregisterHost(host_id)) { + BadMessageReceived(); + } + } +} + +void AppCacheDispatcherHost::OnSelectCache( + int host_id, const GURL& document_url, + int64 cache_document_was_loaded_from, + const GURL& opt_manifest_url) { + if (appcache_service_.get()) { + if (!backend_impl_.SelectCache(host_id, document_url, + cache_document_was_loaded_from, + opt_manifest_url)) { + BadMessageReceived(); + } + } else { + frontend_proxy_.OnCacheSelected(host_id, appcache::AppCacheInfo()); + } +} + +void AppCacheDispatcherHost::OnSelectCacheForWorker( + int host_id, int parent_process_id, int parent_host_id) { + if (appcache_service_.get()) { + if (!backend_impl_.SelectCacheForWorker( + host_id, parent_process_id, parent_host_id)) { + BadMessageReceived(); + } + } else { + frontend_proxy_.OnCacheSelected(host_id, appcache::AppCacheInfo()); + } +} + +void AppCacheDispatcherHost::OnSelectCacheForSharedWorker( + int host_id, int64 appcache_id) { + if (appcache_service_.get()) { + if (!backend_impl_.SelectCacheForSharedWorker(host_id, appcache_id)) + BadMessageReceived(); + } else { + frontend_proxy_.OnCacheSelected(host_id, appcache::AppCacheInfo()); + } +} + +void AppCacheDispatcherHost::OnMarkAsForeignEntry( + int host_id, const GURL& document_url, + int64 cache_document_was_loaded_from) { + if (appcache_service_.get()) { + if (!backend_impl_.MarkAsForeignEntry(host_id, document_url, + cache_document_was_loaded_from)) { + BadMessageReceived(); + } + } +} + +void AppCacheDispatcherHost::OnGetResourceList( + int host_id, std::vector<appcache::AppCacheResourceInfo>* params) { + if (appcache_service_.get()) + backend_impl_.GetResourceList(host_id, params); +} + +void AppCacheDispatcherHost::OnGetStatus(int host_id, + IPC::Message* reply_msg) { + if (pending_reply_msg_.get()) { + BadMessageReceived(); + delete reply_msg; + return; + } + + pending_reply_msg_.reset(reply_msg); + if (appcache_service_.get()) { + if (!backend_impl_.GetStatusWithCallback( + host_id, get_status_callback_.get(), reply_msg)) { + BadMessageReceived(); + } + return; + } + + GetStatusCallback(appcache::UNCACHED, reply_msg); +} + +void AppCacheDispatcherHost::OnStartUpdate(int host_id, + IPC::Message* reply_msg) { + if (pending_reply_msg_.get()) { + BadMessageReceived(); + delete reply_msg; + return; + } + + pending_reply_msg_.reset(reply_msg); + if (appcache_service_.get()) { + if (!backend_impl_.StartUpdateWithCallback( + host_id, start_update_callback_.get(), reply_msg)) { + BadMessageReceived(); + } + return; + } + + StartUpdateCallback(false, reply_msg); +} + +void AppCacheDispatcherHost::OnSwapCache(int host_id, + IPC::Message* reply_msg) { + if (pending_reply_msg_.get()) { + BadMessageReceived(); + delete reply_msg; + return; + } + + pending_reply_msg_.reset(reply_msg); + if (appcache_service_.get()) { + if (!backend_impl_.SwapCacheWithCallback( + host_id, swap_cache_callback_.get(), reply_msg)) { + BadMessageReceived(); + } + return; + } + + SwapCacheCallback(false, reply_msg); +} + +void AppCacheDispatcherHost::GetStatusCallback( + appcache::Status status, void* param) { + IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param); + DCHECK_EQ(reply_msg, pending_reply_msg_.get()); + AppCacheMsg_GetStatus::WriteReplyParams(reply_msg, status); + Send(pending_reply_msg_.release()); +} + +void AppCacheDispatcherHost::StartUpdateCallback(bool result, void* param) { + IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param); + DCHECK_EQ(reply_msg, pending_reply_msg_.get()); + AppCacheMsg_StartUpdate::WriteReplyParams(reply_msg, result); + Send(pending_reply_msg_.release()); +} + +void AppCacheDispatcherHost::SwapCacheCallback(bool result, void* param) { + IPC::Message* reply_msg = reinterpret_cast<IPC::Message*>(param); + DCHECK_EQ(reply_msg, pending_reply_msg_.get()); + AppCacheMsg_SwapCache::WriteReplyParams(reply_msg, result); + Send(pending_reply_msg_.release()); +} diff --git a/content/browser/appcache/appcache_dispatcher_host.h b/content/browser/appcache/appcache_dispatcher_host.h new file mode 100644 index 0000000..e6b9f34 --- /dev/null +++ b/content/browser/appcache/appcache_dispatcher_host.h @@ -0,0 +1,96 @@ +// Copyright (c) 2011 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_DISPATCHER_HOST_H_ +#define CONTENT_BROWSER_APPCACHE_APPCACHE_DISPATCHER_HOST_H_ +#pragma once + +#include <vector> + +#include "base/process.h" +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "content/browser/appcache/appcache_frontend_proxy.h" +#include "content/browser/browser_message_filter.h" +#include "content/browser/renderer_host/resource_dispatcher_host.h" +#include "webkit/appcache/appcache_backend_impl.h" + +class ChromeAppCacheService; +class URLRequestContextGetter; + +namespace net { +class URLRequestContext; +} // namespace net + +// 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 BrowserRenderProcessHost and +// WorkerProcessHost create an instance and delegates calls to it. +class AppCacheDispatcherHost : public BrowserMessageFilter { + public: + // Constructor for use on the IO thread. + AppCacheDispatcherHost(net::URLRequestContext* request_context, + int process_id); + + // Constructor for use on the UI thread. + AppCacheDispatcherHost(URLRequestContextGetter* request_context_getter, + int process_id); + + ~AppCacheDispatcherHost(); + + // BrowserIOMessageFilter implementation + virtual void OnChannelConnected(int32 peer_pid); + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok); + + private: + // BrowserMessageFilter override. + virtual void BadMessageReceived(); + + // IPC message handlers + void OnRegisterHost(int host_id); + void OnUnregisterHost(int host_id); + void OnSelectCache(int host_id, const GURL& document_url, + int64 cache_document_was_loaded_from, + const GURL& opt_manifest_url); + void OnSelectCacheForWorker(int host_id, int parent_process_id, + int parent_host_id); + void OnSelectCacheForSharedWorker(int host_id, int64 appcache_id); + void OnMarkAsForeignEntry(int host_id, const GURL& document_url, + int64 cache_document_was_loaded_from); + void OnGetStatus(int host_id, IPC::Message* reply_msg); + void OnStartUpdate(int host_id, IPC::Message* reply_msg); + void OnSwapCache(int host_id, IPC::Message* reply_msg); + void OnGetResourceList( + int host_id, + std::vector<appcache::AppCacheResourceInfo>* resource_infos); + void GetStatusCallback(appcache::Status status, void* param); + void StartUpdateCallback(bool result, void* param); + void SwapCacheCallback(bool result, void* param); + + // This is only valid once Initialize() has been called. This MUST be defined + // before backend_impl_ since the latter maintains a (non-refcounted) pointer + // to it. + scoped_refptr<ChromeAppCacheService> appcache_service_; + + AppCacheFrontendProxy frontend_proxy_; + appcache::AppCacheBackendImpl backend_impl_; + + // Temporary until OnChannelConnected() can be called from the IO thread, + // which will extract the AppCacheService from the net::URLRequestContext. + scoped_refptr<net::URLRequestContext> request_context_; + scoped_refptr<URLRequestContextGetter> request_context_getter_; + + scoped_ptr<appcache::GetStatusCallback> get_status_callback_; + scoped_ptr<appcache::StartUpdateCallback> start_update_callback_; + scoped_ptr<appcache::SwapCacheCallback> swap_cache_callback_; + scoped_ptr<IPC::Message> pending_reply_msg_; + + // The corresponding ChildProcessHost object's id(). + int process_id_; + + DISALLOW_COPY_AND_ASSIGN(AppCacheDispatcherHost); +}; + +#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_DISPATCHER_HOST_H_ diff --git a/content/browser/appcache/appcache_frontend_proxy.cc b/content/browser/appcache/appcache_frontend_proxy.cc new file mode 100644 index 0000000..a38d029 --- /dev/null +++ b/content/browser/appcache/appcache_frontend_proxy.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2010 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 "content/browser/appcache/appcache_frontend_proxy.h" + +#include "chrome/common/render_messages.h" + +AppCacheFrontendProxy::AppCacheFrontendProxy(IPC::Message::Sender* sender) + : sender_(sender) { +} + +void AppCacheFrontendProxy::OnCacheSelected( + int host_id, const appcache::AppCacheInfo& info) { + sender_->Send(new AppCacheMsg_CacheSelected(host_id, info)); +} + +void AppCacheFrontendProxy::OnStatusChanged(const std::vector<int>& host_ids, + appcache::Status status) { + sender_->Send(new AppCacheMsg_StatusChanged(host_ids, status)); +} + +void AppCacheFrontendProxy::OnEventRaised(const std::vector<int>& host_ids, + appcache::EventID event_id) { + DCHECK_NE(event_id, appcache::PROGRESS_EVENT); // See OnProgressEventRaised. + sender_->Send(new AppCacheMsg_EventRaised(host_ids, event_id)); +} + +void AppCacheFrontendProxy::OnProgressEventRaised( + const std::vector<int>& host_ids, + const GURL& url, int num_total, int num_complete) { + sender_->Send(new AppCacheMsg_ProgressEventRaised( + host_ids, url, num_total, num_complete)); +} + +void AppCacheFrontendProxy::OnErrorEventRaised( + const std::vector<int>& host_ids, + const std::string& message) { + sender_->Send(new AppCacheMsg_ErrorEventRaised( + host_ids, message)); +} + +void AppCacheFrontendProxy::OnLogMessage(int host_id, + appcache::LogLevel log_level, + const std::string& message) { + sender_->Send(new AppCacheMsg_LogMessage(host_id, log_level, message)); +} + +void AppCacheFrontendProxy::OnContentBlocked(int host_id, + const GURL& manifest_url) { + sender_->Send(new AppCacheMsg_ContentBlocked(host_id, manifest_url)); +} diff --git a/content/browser/appcache/appcache_frontend_proxy.h b/content/browser/appcache/appcache_frontend_proxy.h new file mode 100644 index 0000000..f16a423 --- /dev/null +++ b/content/browser/appcache/appcache_frontend_proxy.h @@ -0,0 +1,40 @@ +// Copyright (c) 2010 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_FRONTEND_PROXY_H_ +#define CONTENT_BROWSER_APPCACHE_APPCACHE_FRONTEND_PROXY_H_ +#pragma once + +#include <string> +#include <vector> + +#include "ipc/ipc_message.h" +#include "webkit/appcache/appcache_interfaces.h" + +// Sends appcache related messages to a child process. +class AppCacheFrontendProxy : public appcache::AppCacheFrontend { + public: + explicit AppCacheFrontendProxy(IPC::Message::Sender* sender); + + // AppCacheFrontend methods + virtual void OnCacheSelected(int host_id, const appcache::AppCacheInfo& info); + virtual void OnStatusChanged(const std::vector<int>& host_ids, + appcache::Status status); + virtual void OnEventRaised(const std::vector<int>& host_ids, + appcache::EventID event_id); + virtual void OnProgressEventRaised(const std::vector<int>& host_ids, + const GURL& url, + int num_total, int num_complete); + virtual void OnErrorEventRaised(const std::vector<int>& host_ids, + const std::string& message); + virtual void OnLogMessage(int host_id, appcache::LogLevel log_level, + const std::string& message); + virtual void OnContentBlocked(int host_id, + const GURL& manifest_url); + + private: + IPC::Message::Sender* sender_; +}; + +#endif // CONTENT_BROWSER_APPCACHE_APPCACHE_FRONTEND_PROXY_H_ diff --git a/content/browser/appcache/appcache_ui_test.cc b/content/browser/appcache/appcache_ui_test.cc new file mode 100644 index 0000000..58ca32b --- /dev/null +++ b/content/browser/appcache/appcache_ui_test.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2010 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 "base/file_path.h" +#include "chrome/test/ui/ui_layout_test.h" + +class AppCacheUITest : public UILayoutTest { + public: + void RunAppCacheTests(const char* tests[], int num_tests) { + FilePath http_test_dir; + http_test_dir = http_test_dir.AppendASCII("http"); + http_test_dir = http_test_dir.AppendASCII("tests"); + + FilePath appcache_test_dir; + appcache_test_dir = appcache_test_dir.AppendASCII("appcache"); + InitializeForLayoutTest(http_test_dir, appcache_test_dir, kHttpPort); + + StartHttpServer(new_http_root_dir_); + for (int i = 0; i < num_tests; ++i) + RunLayoutTest(tests[i], kHttpPort); + StopHttpServer(); + } + + protected: + virtual ~AppCacheUITest() {} +}; + +// Flaky: http://crbug.com/54717 +// The tests that don't depend on PHP should be less flaky. +TEST_F(AppCacheUITest, FLAKY_AppCacheLayoutTests_NoPHP) { + static const char* kNoPHPTests[] = { + "404-manifest.html", + "404-resource.html", + "cyrillic-uri.html", + "deferred-events-delete-while-raising.html", + "deferred-events.html", + "destroyed-frame.html", + "detached-iframe.html", + "different-origin-manifest.html", + "different-scheme.html", + "empty-manifest.html", + "foreign-iframe-main.html", + "insert-html-element-with-manifest.html", + "insert-html-element-with-manifest-2.html", + "manifest-containing-itself.html", + "manifest-parsing.html", + "manifest-with-empty-file.html", + "progress-counter.html", + "reload.html", + "simple.html", + "top-frame-1.html", + "top-frame-2.html", + "top-frame-3.html", + "top-frame-4.html", + "whitelist-wildcard.html", + "wrong-content-type.html", + "wrong-signature-2.html", + "wrong-signature.html", + "xhr-foreign-resource.html", + }; + + // This test is racey. + // https://bugs.webkit.org/show_bug.cgi?id=49104 + // "foreign-fallback.html" + + RunAppCacheTests(kNoPHPTests, arraysize(kNoPHPTests)); +} + +// Flaky: http://crbug.com/54717 +// Lighty/PHP is not reliable enough on windows. +TEST_F(AppCacheUITest, FLAKY_AppCacheLayoutTests_PHP) { + static const char* kPHPTests[] = { + "auth.html", + "fallback.html", + "main-resource-hash.html", + "manifest-redirect.html", + "manifest-redirect-2.html", + "navigating-away-while-cache-attempt-in-progress.html", + "non-html.xhtml", + "offline-access.html", + "online-whitelist.html", + "remove-cache.html", + "resource-redirect.html", + "resource-redirect-2.html", + "update-cache.html", + }; + + // This tests loads a data url which calls notifyDone, this just + // doesn't work with the layoutTestController in this test harness. + // "fail-on-update.html", + + // Flaky for reasons i don't yet see? + // "fail-on-update2.html", + + // TODO(michaeln): investigate these more closely + // "crash-when-navigating-away-then-back.html", + // "credential-url.html", + // "different-https-origin-resource-main.html", + // "idempotent-update.html", not sure this is a valid test + // "local-content.html", + // "max-size.html", we use a different quota scheme + + RunAppCacheTests(kPHPTests, arraysize(kPHPTests)); +} diff --git a/content/browser/appcache/chrome_appcache_service.cc b/content/browser/appcache/chrome_appcache_service.cc new file mode 100644 index 0000000..68c7ece --- /dev/null +++ b/content/browser/appcache/chrome_appcache_service.cc @@ -0,0 +1,147 @@ +// Copyright (c) 2011 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 "content/browser/appcache/chrome_appcache_service.h" + +#include "base/file_path.h" +#include "base/file_util.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/common/notification_service.h" +#include "net/base/net_errors.h" +#include "webkit/appcache/appcache_thread.h" + +static bool has_initialized_thread_ids; + +namespace { + +// Used to defer deleting of local storage until the destructor has finished. +void DeleteLocalStateOnIOThread(FilePath cache_path) { + // Post the actual deletion to the DB thread to ensure it happens after the + // database file has been closed. + BrowserThread::PostTask( + BrowserThread::DB, FROM_HERE, + NewRunnableFunction<bool(*)(const FilePath&, bool), FilePath, bool>( + &file_util::Delete, cache_path, true)); +} + +} // namespace + +// ---------------------------------------------------------------------------- + +ChromeAppCacheService::ChromeAppCacheService() + : clear_local_state_on_exit_(false) { +} + +void ChromeAppCacheService::InitializeOnIOThread( + const FilePath& profile_path, bool is_incognito, + scoped_refptr<HostContentSettingsMap> content_settings_map, + bool clear_local_state_on_exit) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!has_initialized_thread_ids) { + has_initialized_thread_ids = true; + appcache::AppCacheThread::Init(BrowserThread::DB, BrowserThread::IO); + } + + host_contents_settings_map_ = content_settings_map; + registrar_.Add( + this, NotificationType::PURGE_MEMORY, NotificationService::AllSources()); + SetClearLocalStateOnExit(clear_local_state_on_exit); + if (!is_incognito) + cache_path_ = profile_path.Append(chrome::kAppCacheDirname); + + // Init our base class. + Initialize(cache_path_, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE)); + set_appcache_policy(this); +} + +ChromeAppCacheService::~ChromeAppCacheService() { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (clear_local_state_on_exit_ && !cache_path_.empty()) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableFunction(DeleteLocalStateOnIOThread, cache_path_)); + } +} + +void ChromeAppCacheService::SetOriginQuotaInMemory( + const GURL& origin, int64 quota) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (storage()) + storage()->SetOriginQuotaInMemory(origin, quota); +} + +void ChromeAppCacheService::ResetOriginQuotaInMemory(const GURL& origin) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + if (storage()) + storage()->ResetOriginQuotaInMemory(origin); +} + +void ChromeAppCacheService::SetClearLocalStateOnExit(bool clear_local_state) { + if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(this, + &ChromeAppCacheService::SetClearLocalStateOnExit, + clear_local_state)); + return; + } + clear_local_state_on_exit_ = clear_local_state; +} + +bool ChromeAppCacheService::CanLoadAppCache(const GURL& manifest_url) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ContentSetting setting = host_contents_settings_map_->GetContentSetting( + manifest_url, CONTENT_SETTINGS_TYPE_COOKIES, ""); + DCHECK(setting != CONTENT_SETTING_DEFAULT); + // We don't prompt for read access. + return setting != CONTENT_SETTING_BLOCK; +} + +int ChromeAppCacheService::CanCreateAppCache( + const GURL& manifest_url, net::CompletionCallback* callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + ContentSetting setting = host_contents_settings_map_->GetContentSetting( + manifest_url, CONTENT_SETTINGS_TYPE_COOKIES, ""); + DCHECK(setting != CONTENT_SETTING_DEFAULT); + return (setting != CONTENT_SETTING_BLOCK) ? net::OK : + net::ERR_ACCESS_DENIED; +} + +void ChromeAppCacheService::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(type == NotificationType::PURGE_MEMORY); + PurgeMemory(); +} + +// ---------------------------------------------------------------------------- + +static BrowserThread::ID ToBrowserThreadID(int id) { + DCHECK(has_initialized_thread_ids); + DCHECK(id == BrowserThread::DB || id == BrowserThread::IO); + return static_cast<BrowserThread::ID>(id); +} + +namespace appcache { + +// An impl of AppCacheThread we need to provide to the appcache lib. + +bool AppCacheThread::PostTask( + int id, + const tracked_objects::Location& from_here, + Task* task) { + return BrowserThread::PostTask(ToBrowserThreadID(id), from_here, task); +} + +bool AppCacheThread::CurrentlyOn(int id) { + return BrowserThread::CurrentlyOn(ToBrowserThreadID(id)); +} + +} // namespace appcache diff --git a/content/browser/appcache/chrome_appcache_service.h b/content/browser/appcache/chrome_appcache_service.h new file mode 100644 index 0000000..8081122 --- /dev/null +++ b/content/browser/appcache/chrome_appcache_service.h @@ -0,0 +1,72 @@ +// Copyright (c) 2011 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_CHROME_APPCACHE_SERVICE_H_ +#define CONTENT_BROWSER_APPCACHE_CHROME_APPCACHE_SERVICE_H_ +#pragma once + +#include "base/ref_counted.h" +#include "chrome/browser/content_settings/host_content_settings_map.h" +#include "chrome/common/notification_registrar.h" +#include "content/browser/browser_thread.h" +#include "webkit/appcache/appcache_policy.h" +#include "webkit/appcache/appcache_service.h" + +class ChromeURLRequestContext; +class FilePath; + +// 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, except the ctor, are expected to be called on +// the IO thread (unless specifically called out in doc comments). +class ChromeAppCacheService + : public base::RefCountedThreadSafe<ChromeAppCacheService, + BrowserThread::DeleteOnIOThread>, + public appcache::AppCacheService, + public appcache::AppCachePolicy, + public NotificationObserver { + public: + ChromeAppCacheService(); + + void InitializeOnIOThread( + const FilePath& profile_path, bool is_incognito, + scoped_refptr<HostContentSettingsMap> content_settings_map, + bool clear_local_state_on_exit); + + // Helpers used by the extension service to grant and revoke + // unlimited storage to app extensions. + void SetOriginQuotaInMemory(const GURL& origin, int64 quota); + void ResetOriginQuotaInMemory(const GURL& origin); + + void SetClearLocalStateOnExit(bool clear_local_state); + + private: + friend class BrowserThread; + friend class DeleteTask<ChromeAppCacheService>; + + virtual ~ChromeAppCacheService(); + + // AppCachePolicy overrides + virtual bool CanLoadAppCache(const GURL& manifest_url); + virtual int CanCreateAppCache(const GURL& manifest_url, + net::CompletionCallback* callback); + + // NotificationObserver override + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + scoped_refptr<HostContentSettingsMap> host_contents_settings_map_; + NotificationRegistrar registrar_; + bool clear_local_state_on_exit_; + FilePath cache_path_; + + DISALLOW_COPY_AND_ASSIGN(ChromeAppCacheService); +}; + +#endif // CONTENT_BROWSER_APPCACHE_CHROME_APPCACHE_SERVICE_H_ diff --git a/content/browser/appcache/chrome_appcache_service_unittest.cc b/content/browser/appcache/chrome_appcache_service_unittest.cc new file mode 100644 index 0000000..0c5c512 --- /dev/null +++ b/content/browser/appcache/chrome_appcache_service_unittest.cc @@ -0,0 +1,101 @@ +// Copyright (c) 2011 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 "base/file_util.h" +#include "base/message_loop.h" +#include "base/ref_counted.h" +#include "base/scoped_temp_dir.h" +#include "chrome/common/chrome_constants.h" +#include "chrome/test/testing_browser_process.h" +#include "chrome/test/testing_browser_process_test.h" +#include "chrome/test/thread_test_helper.h" +#include "content/browser/appcache/chrome_appcache_service.h" +#include "content/browser/browser_thread.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webkit/appcache/appcache_storage_impl.h" + +namespace appcache { + +class ChromeAppCacheServiceTest : public TestingBrowserProcessTest { + public: + ChromeAppCacheServiceTest() + : message_loop_(MessageLoop::TYPE_IO), + db_thread_(BrowserThread::DB, &message_loop_), + file_thread_(BrowserThread::FILE, &message_loop_), + cache_thread_(BrowserThread::CACHE, &message_loop_), + io_thread_(BrowserThread::IO, &message_loop_) { + } + + protected: + MessageLoop message_loop_; + ScopedTempDir temp_dir_; + + private: + BrowserThread db_thread_; + BrowserThread file_thread_; + BrowserThread cache_thread_; + BrowserThread io_thread_; +}; + +TEST_F(ChromeAppCacheServiceTest, KeepOnDestruction) { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + FilePath appcache_path = temp_dir_.path().Append(chrome::kAppCacheDirname); + scoped_refptr<ChromeAppCacheService> appcache_service = + new ChromeAppCacheService; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(appcache_service.get(), + &ChromeAppCacheService::InitializeOnIOThread, + temp_dir_.path(), false, + scoped_refptr<HostContentSettingsMap>(NULL), + false)); + // Make the steps needed to initialize the storage of AppCache data. + message_loop_.RunAllPending(); + appcache::AppCacheStorageImpl* storage = + static_cast<appcache::AppCacheStorageImpl*>(appcache_service->storage()); + ASSERT_TRUE(storage->database_->db_connection()); + ASSERT_EQ(1, storage->NewCacheId()); + storage->disk_cache(); + message_loop_.RunAllPending(); + + ASSERT_TRUE(file_util::PathExists(appcache_path)); + ASSERT_TRUE(file_util::PathExists(appcache_path.AppendASCII("Index"))); + + appcache_service = NULL; + message_loop_.RunAllPending(); + + ASSERT_TRUE(file_util::PathExists(appcache_path)); +} + +TEST_F(ChromeAppCacheServiceTest, RemoveOnDestruction) { + ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); + FilePath appcache_path = temp_dir_.path().Append(chrome::kAppCacheDirname); + scoped_refptr<ChromeAppCacheService> appcache_service = + new ChromeAppCacheService; + BrowserThread::PostTask( + BrowserThread::IO, FROM_HERE, + NewRunnableMethod(appcache_service.get(), + &ChromeAppCacheService::InitializeOnIOThread, + temp_dir_.path(), false, + scoped_refptr<HostContentSettingsMap>(NULL), + true)); + // Make the steps needed to initialize the storage of AppCache data. + message_loop_.RunAllPending(); + appcache::AppCacheStorageImpl* storage = + static_cast<appcache::AppCacheStorageImpl*>(appcache_service->storage()); + ASSERT_TRUE(storage->database_->db_connection()); + ASSERT_EQ(1, storage->NewCacheId()); + storage->disk_cache(); + message_loop_.RunAllPending(); + + ASSERT_TRUE(file_util::PathExists(appcache_path)); + ASSERT_TRUE(file_util::PathExists(appcache_path.AppendASCII("Index"))); + + appcache_service = NULL; + message_loop_.RunAllPending(); + + ASSERT_FALSE(file_util::PathExists(appcache_path)); +} + +} // namespace appcache diff --git a/content/browser/appcache/view_appcache_internals_job_factory.cc b/content/browser/appcache/view_appcache_internals_job_factory.cc new file mode 100644 index 0000000..4a06999 --- /dev/null +++ b/content/browser/appcache/view_appcache_internals_job_factory.cc @@ -0,0 +1,28 @@ +// Copyright (c) 2011 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 "content/browser/appcache/view_appcache_internals_job_factory.h" + +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/common/url_constants.h" +#include "net/url_request/url_request.h" +#include "webkit/appcache/appcache_service.h" +#include "webkit/appcache/view_appcache_internals_job.h" + +// static. +bool ViewAppCacheInternalsJobFactory::IsSupportedURL(const GURL& url) { + return StartsWithASCII(url.spec(), + chrome::kAppCacheViewInternalsURL, + true /*case_sensitive*/); +} + +// static. +net::URLRequestJob* ViewAppCacheInternalsJobFactory::CreateJobForRequest( + net::URLRequest* request) { + net::URLRequestContext* context = request->context(); + ChromeURLRequestContext* chrome_request_context = + reinterpret_cast<ChromeURLRequestContext*>(context); + return new appcache::ViewAppCacheInternalsJob( + request, chrome_request_context->appcache_service()); +} diff --git a/content/browser/appcache/view_appcache_internals_job_factory.h b/content/browser/appcache/view_appcache_internals_job_factory.h new file mode 100644 index 0000000..d488b01 --- /dev/null +++ b/content/browser/appcache/view_appcache_internals_job_factory.h @@ -0,0 +1,22 @@ +// Copyright (c) 2010 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_VIEW_APPCACHE_INTERNALS_JOB_FACTORY_H_ +#define CONTENT_BROWSER_APPCACHE_VIEW_APPCACHE_INTERNALS_JOB_FACTORY_H_ +#pragma once + +namespace net { +class URLRequest; +class URLRequestJob; +} // namespace net + +class GURL; + +class ViewAppCacheInternalsJobFactory { + public: + static bool IsSupportedURL(const GURL& url); + static net::URLRequestJob* CreateJobForRequest(net::URLRequest* request); +}; + +#endif // CONTENT_BROWSER_APPCACHE_VIEW_APPCACHE_INTERNALS_JOB_FACTORY_H_ diff --git a/content/browser/device_orientation/accelerometer_mac.cc b/content/browser/device_orientation/accelerometer_mac.cc new file mode 100644 index 0000000..f642212 --- /dev/null +++ b/content/browser/device_orientation/accelerometer_mac.cc @@ -0,0 +1,412 @@ +// Copyright (c) 2011 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. +// +// This file is based on the SMSLib library. +// +// SMSLib Sudden Motion Sensor Access Library +// Copyright (c) 2010 Suitable Systems +// All rights reserved. +// +// Developed by: Daniel Griscom +// Suitable Systems +// http://www.suitable.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal with the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the names of Suitable Systems nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +// +// For more information about SMSLib, see +// <http://www.suitable.com/tools/smslib.html> +// or contact +// Daniel Griscom +// Suitable Systems +// 1 Centre Street, Suite 204 +// Wakefield, MA 01880 +// (781) 665-0053 + +#include "content/browser/device_orientation/accelerometer_mac.h" + +#include <math.h> +#include <sys/sysctl.h> + +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "content/browser/device_orientation/orientation.h" + +namespace device_orientation { + +struct AccelerometerMac::GenericMacbookSensor { + // Name of device to be read. + const char* service_name; + + // Number of bytes of the axis data. + int axis_size; + + // Default calibration value for zero g. + float zero_g; + + // Default calibration value for one g (negative when axis is inverted). + float one_g; + + // Kernel function index. + unsigned int function; + + // Size of the sensor record to be sent/received. + unsigned int record_size; +}; + +struct AccelerometerMac::AxisData { + // Location of the first byte representing the axis in the sensor data. + int index; + + // Axis inversion flag. The value changes often between models. + bool inverted; +}; + +// Sudden Motion Sensor descriptor. +struct AccelerometerMac::SensorDescriptor { + // Prefix of model to be tested. + const char* model_name; + + // Axis-specific data (x,y,z order). + AxisData axis[3]; +}; + +// Typical sensor parameters in MacBook models. +const AccelerometerMac::GenericMacbookSensor + AccelerometerMac::kGenericSensor = { + "SMCMotionSensor", 2, + 0, 251, + 5, 40 +}; + +// Supported sensor descriptors. Add entries here to enhance compatibility. +// All non-tested entries from SMSLib have been removed. +const AccelerometerMac::SensorDescriptor + AccelerometerMac::kSupportedSensors[] = { + // Tested by tommyw on a 13" MacBook. + { "MacBook1,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by S.Selz. (via avi) on a 13" MacBook. + { "MacBook2,1", { { 0, true }, { 2, false }, { 4, true } } }, + + // Tested by verhees on a 13" MacBook. + { "MacBook3,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by adlr on a 13" MacBook. + { "MacBook4,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by tommyw on a 13" MacBook. + { "MacBook6,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by avi on a 13" MacBook. + { "MacBook7,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by crc on a 13" MacBook Air. + { "MacBookAir1,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by sfiera, pjw on a 13" MacBook Air. + { "MacBookAir2,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Note: MacBookAir3,1 (11" MacBook Air) and MacBookAir3,2 (13" MacBook Air) + // have no accelerometer sensors. + + // Tested by crc on a 15" MacBook Pro. + { "MacBookPro1,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by L.V. (via avi) on a 17" MacBook Pro. + { "MacBookPro2,1", { { 0, true }, { 2, false }, { 4, true } } }, + + // Tested by leandrogracia on a 15" MacBook Pro. + { "MacBookPro2,2", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by leandrogracia on a 15" MacBook Pro. + // TODO(avi): this model name was also used for the 17" version; verify that + // these parameters are also valid for that model. + { "MacBookPro3,1", { { 0, false }, { 2, true }, { 4, true } } }, + + // Tested by leandrogracia on a 15" MacBook Pro. + // Tested by Eric Shapiro (via avi) on a 17" MacBook Pro. + { "MacBookPro4,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by leandrogracia on a 15" MacBook Pro. + { "MacBookPro5,1", { { 0, false }, { 2, false }, { 4, false } } }, + + // Tested by S.Selz. (via avi) on a 17" MacBook Pro. + { "MacBookPro5,2", { { 0, false }, { 2, false }, { 4, false } } }, + + // Tested by dmaclach on a 15" MacBook Pro. + { "MacBookPro5,3", { { 2, false }, { 0, false }, { 4, true } } }, + + // Tested by leandrogracia on a 15" MacBook Pro. + { "MacBookPro5,4", { { 0, false }, { 2, false }, { 4, false } } }, + + // Tested by leandrogracia on a 13" MacBook Pro. + { "MacBookPro5,5", { { 0, true }, { 2, true }, { 4, false } } }, + + // Tested by khom, leadpipe on a 17" MacBook Pro. + { "MacBookPro6,1", { { 0, false }, { 2, false }, { 4, false } } }, + + // Tested by leandrogracia on a 15" MacBook Pro. + { "MacBookPro6,2", { { 0, true }, { 2, false }, { 4, true } } }, + + // Tested by leandrogracia on a 13" MacBook Pro. + { "MacBookPro7,1", { { 0, true }, { 2, true }, { 4, false } } }, + + // Generic MacBook accelerometer sensor data, used for for both future models + // and past models for which there has been no testing. Note that this generic + // configuration may well have problems with inverted axes. + // TODO(avi): Find these past models and test on them; test on future models. + // MacBook5,1 + // MacBook5,2 + // MacBookPro1,2 + // MacBookPro3,1 (17" to compare to 15") + { "", { { 0, true }, { 2, true }, { 4, false } } } +}; + +// Create a AccelerometerMac object and return NULL if no valid sensor found. +DataFetcher* AccelerometerMac::Create() { + scoped_ptr<AccelerometerMac> accelerometer(new AccelerometerMac); + return accelerometer->Init() ? accelerometer.release() : NULL; +} + +AccelerometerMac::~AccelerometerMac() { + IOServiceClose(io_connection_); +} + +AccelerometerMac::AccelerometerMac() + : sensor_(NULL), + io_connection_(0) { +} + +// Retrieve per-axis accelerometer values. +// +// Axes and angles are defined according to the W3C DeviceOrientation Draft. +// See here: http://dev.w3.org/geo/api/spec-source-orientation.html +// +// Note: only beta and gamma angles are provided. Alpha is set to zero. +// +// Returns false in case of error or non-properly initialized object. +// +bool AccelerometerMac::GetOrientation(Orientation* orientation) { + DCHECK(sensor_); + + // Reset output record memory buffer. + std::fill(output_record_.begin(), output_record_.end(), 0x00); + + // Read record data from memory. + const size_t kInputSize = kGenericSensor.record_size; + size_t output_size = kGenericSensor.record_size; + + if (IOConnectCallStructMethod(io_connection_, kGenericSensor.function, + static_cast<const char *>(&input_record_[0]), kInputSize, + &output_record_[0], &output_size) != KERN_SUCCESS) { + return false; + } + + // Calculate per-axis calibrated values. + float axis_value[3]; + + for (int i = 0; i < 3; ++i) { + int sensor_value = 0; + int size = kGenericSensor.axis_size; + int index = sensor_->axis[i].index; + + // Important Note: Little endian is assumed as this code is Mac-only + // and PowerPC is currently not supported. + memcpy(&sensor_value, &output_record_[index], size); + + sensor_value = ExtendSign(sensor_value, size); + + // Correct value using the current calibration. + axis_value[i] = static_cast<float>(sensor_value - kGenericSensor.zero_g) / + kGenericSensor.one_g; + + // Make sure we reject any NaN or infinite values. + if (!isfinite(axis_value[i])) + return false; + + // Clamp value to the [-1, 1] range. + if (axis_value[i] < -1.0) + axis_value[i] = -1.0; + else if (axis_value[i] > 1.0) + axis_value[i] = 1.0; + + // Apply axis inversion. + if (sensor_->axis[i].inverted) + axis_value[i] = -axis_value[i]; + } + + // Transform the accelerometer values to W3C draft angles. + // + // Accelerometer values are just dot products of the sensor axes + // by the gravity vector 'g' with the result for the z axis inverted. + // + // To understand this transformation calculate the 3rd row of the z-x-y + // Euler angles rotation matrix (because of the 'g' vector, only 3rd row + // affects to the result). Note that z-x-y matrix means R = Ry * Rx * Rz. + // Then, assume alpha = 0 and you get this: + // + // x_acc = sin(gamma) + // y_acc = - cos(gamma) * sin(beta) + // z_acc = cos(beta) * cos(gamma) + // + // After that the rest is just a bit of trigonometry. + // + // Also note that alpha can't be provided but it's assumed to be always zero. + // This is necessary in order to provide enough information to solve + // the equations. + // + const double kRad2deg = 180.0 / M_PI; + + orientation->alpha_ = 0.0; + orientation->beta_ = kRad2deg * atan2(-axis_value[1], axis_value[2]); + orientation->gamma_ = kRad2deg * asin(axis_value[0]); + + // Make sure that the interval boundaries comply with the specification. At + // this point, beta is [-180, 180] and gamma is [-90, 90], but the spec has + // the upper bound open on both. + if (orientation->beta_ == 180.0) { + orientation->beta_ = -180.0; // -180 == 180 (upside-down) + } + if (orientation->gamma_ == 90.0) { + static double just_less_than_90 = nextafter(90, 0); + orientation->gamma_ = just_less_than_90; + } + + // At this point, DCHECKing is paranoia. Never hurts. + DCHECK_GE(orientation->beta_, -180.0); + DCHECK_LT(orientation->beta_, 180.0); + DCHECK_GE(orientation->gamma_, -90.0); + DCHECK_LT(orientation->gamma_, 90.0); + + orientation->can_provide_alpha_ = false; + orientation->can_provide_beta_ = true; + orientation->can_provide_gamma_ = true; + + return true; +} + +// Probe the local hardware looking for a supported sensor device +// and initialize an I/O connection to it. +bool AccelerometerMac::Init() { + // Allocate local variables for model name string (size from SMSLib). + static const int kNameSize = 32; + char local_model[kNameSize]; + + // Request model name to the kernel. + size_t name_size = kNameSize; + int params[2] = { CTL_HW, HW_MODEL }; + if (sysctl(params, 2, local_model, &name_size, NULL, 0) != 0) + return NULL; + + const SensorDescriptor* sensor_candidate = NULL; + + // Look for the current model in the supported sensor list. + const int kNumSensors = arraysize(kSupportedSensors); + + for (int i = 0; i < kNumSensors; ++i) { + // Check if the supported sensor model name is a prefix + // of the local hardware model (empty names are accepted). + const char* p1 = kSupportedSensors[i].model_name; + for (const char* p2 = local_model; *p1 != '\0' && *p1 == *p2; ++p1, ++p2) + continue; + if (*p1 != '\0') + continue; + + // Local hardware found in the supported sensor list. + sensor_candidate = &kSupportedSensors[i]; + + // Get a dictionary of the services matching to the one in the sensor. + CFMutableDictionaryRef dict = + IOServiceMatching(kGenericSensor.service_name); + if (dict == NULL) + continue; + + // Get an iterator for the matching services. + io_iterator_t device_iterator; + if (IOServiceGetMatchingServices(kIOMasterPortDefault, dict, + &device_iterator) != KERN_SUCCESS) { + continue; + } + + // Get the first device in the list. + io_object_t device = IOIteratorNext(device_iterator); + IOObjectRelease(device_iterator); + if (device == 0) + continue; + + // Try to open device. + kern_return_t result; + result = IOServiceOpen(device, mach_task_self(), 0, &io_connection_); + IOObjectRelease(device); + if (result != KERN_SUCCESS || io_connection_ == 0) + return false; + + // Local sensor service confirmed by IOKit. + sensor_ = sensor_candidate; + break; + } + + if (sensor_ == NULL) + return false; + + // Allocate and initialize input/output records. + input_record_.resize(kGenericSensor.record_size, 0x01); + output_record_.resize(kGenericSensor.record_size, 0x00); + + // Try to retrieve the current orientation. + Orientation test_orientation; + return GetOrientation(&test_orientation); +} + +// Extend the sign of an integer of less than 32 bits to a 32-bit integer. +int AccelerometerMac::ExtendSign(int value, size_t size) { + switch (size) { + case 1: + if (value & 0x00000080) + return value | 0xffffff00; + break; + + case 2: + if (value & 0x00008000) + return value | 0xffff0000; + break; + + case 3: + if (value & 0x00800000) + return value | 0xff000000; + break; + + default: + LOG(FATAL) << "Invalid integer size for sign extension: " << size; + } + + return value; +} + +} // namespace device_orientation diff --git a/content/browser/device_orientation/accelerometer_mac.h b/content/browser/device_orientation/accelerometer_mac.h new file mode 100644 index 0000000..d4480c8 --- /dev/null +++ b/content/browser/device_orientation/accelerometer_mac.h @@ -0,0 +1,104 @@ +// Copyright (c) 2010 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. +// +// This file is based on the SMSLib library. +// +// SMSLib Sudden Motion Sensor Access Library +// Copyright (c) 2010 Suitable Systems +// All rights reserved. +// +// Developed by: Daniel Griscom +// Suitable Systems +// http://www.suitable.com +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal with the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// - Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimers. +// +// - Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimers in the +// documentation and/or other materials provided with the distribution. +// +// - Neither the names of Suitable Systems nor the names of its +// contributors may be used to endorse or promote products derived from +// this Software without specific prior written permission. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +// IN NO EVENT SHALL THE CONTRIBUTORS OR COPYRIGHT HOLDERS BE LIABLE FOR +// ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +// TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +// SOFTWARE OR THE USE OR OTHER DEALINGS WITH THE SOFTWARE. +// +// For more information about SMSLib, see +// <http://www.suitable.com/tools/smslib.html> +// or contact +// Daniel Griscom +// Suitable Systems +// 1 Centre Street, Suite 204 +// Wakefield, MA 01880 +// (781) 665-0053 + +#ifndef CONTENT_BROWSER_DEVICE_ORIENTATION_ACCELEROMETER_MAC_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_ACCELEROMETER_MAC_H_ +#pragma once + +#include <vector> + +#include <IOKit/IOKitLib.h> + +#include "content/browser/device_orientation/data_fetcher.h" + +namespace device_orientation { + +// Provides an easy-to-use interface to retrieve data +// from the MacBook family accelerometer. +class AccelerometerMac : public DataFetcher { + public: + static DataFetcher* Create(); + + // Implement DataFetcher. + virtual bool GetOrientation(Orientation* orientation); + + virtual ~AccelerometerMac(); + + private: + AccelerometerMac(); + bool Init(); + + // Extend the sign of an integer of size bytes to a 32-bit one. + static int ExtendSign(int value, size_t size); + + struct GenericMacbookSensor; + struct SensorDescriptor; + struct AxisData; + + // Generic sensor data and list of supported sensors. + static const GenericMacbookSensor kGenericSensor; + static const SensorDescriptor kSupportedSensors[]; + + // Local sensor if supported, else NULL. + const SensorDescriptor* sensor_; + + // IOKit connection to the local sensor. + io_connect_t io_connection_; + + // Input memory used for sensor I/O. + std::vector<char> input_record_; + + // Output memory used for sensor I/O. + std::vector<char> output_record_; +}; + +} // namespace device_orientation + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_ACCELEROMETER_MAC_H_ diff --git a/content/browser/device_orientation/data_fetcher.h b/content/browser/device_orientation/data_fetcher.h new file mode 100644 index 0000000..6f6bd99 --- /dev/null +++ b/content/browser/device_orientation/data_fetcher.h @@ -0,0 +1,20 @@ +// Copyright (c) 2010 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_DEVICE_ORIENTATION_DATA_FETCHER_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_ + +namespace device_orientation { + +class Orientation; + +class DataFetcher { + public: + virtual ~DataFetcher() {} + virtual bool GetOrientation(Orientation*) = 0; +}; + +} // namespace device_orientation + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_DATA_FETCHER_H_ diff --git a/content/browser/device_orientation/device_orientation_browsertest.cc b/content/browser/device_orientation/device_orientation_browsertest.cc new file mode 100644 index 0000000..7646cae --- /dev/null +++ b/content/browser/device_orientation/device_orientation_browsertest.cc @@ -0,0 +1,70 @@ +// Copyright (c) 2010 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 "base/command_line.h" +#include "base/file_path.h" +#include "base/ref_counted.h" +#include "chrome/browser/ui/browser.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/test/in_process_browser_test.h" +#include "chrome/test/ui_test_utils.h" +#include "content/browser/device_orientation/orientation.h" +#include "content/browser/device_orientation/provider.h" +#include "content/browser/tab_contents/tab_contents.h" + +namespace device_orientation { + +class MockProvider : public Provider { + public: + explicit MockProvider(const Orientation& orientation) + : orientation_(orientation), + added_observer_(false), + removed_observer_(false) {} + + virtual void AddObserver(Observer* observer) { + added_observer_ = true; + observer->OnOrientationUpdate(orientation_); + } + virtual void RemoveObserver(Observer* observer) { + removed_observer_ = true; + } + Orientation orientation_; + bool added_observer_; + bool removed_observer_; +}; + +class DeviceOrientationBrowserTest : public InProcessBrowserTest { + public: + // From InProcessBrowserTest. + virtual void SetUpCommandLine(CommandLine* command_line) { + EXPECT_TRUE(!command_line->HasSwitch(switches::kDisableDeviceOrientation)); + } + + GURL testUrl(const FilePath::CharType* filename) { + const FilePath kTestDir(FILE_PATH_LITERAL("device_orientation")); + return ui_test_utils::GetTestUrl(kTestDir, FilePath(filename)); + } +}; + +IN_PROC_BROWSER_TEST_F(DeviceOrientationBrowserTest, BasicTest) { + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + scoped_refptr<MockProvider> provider(new MockProvider(kTestOrientation)); + Provider::SetInstanceForTests(provider.get()); + + // The test page will register an event handler for orientation events, + // expects to get an event with kTestOrientation orientation, + // then removes the event handler and navigates to #pass. + GURL test_url = testUrl(FILE_PATH_LITERAL("device_orientation_test.html")); + ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(browser(), + test_url, + 2); + + // Check that the page got the event it expected and that the provider + // saw requests for adding and removing an observer. + EXPECT_EQ("pass", browser()->GetSelectedTabContents()->GetURL().ref()); + EXPECT_TRUE(provider->added_observer_); + EXPECT_TRUE(provider->removed_observer_); +} + +} // namespace device_orientation diff --git a/content/browser/device_orientation/message_filter.cc b/content/browser/device_orientation/message_filter.cc new file mode 100644 index 0000000..4cb7501 --- /dev/null +++ b/content/browser/device_orientation/message_filter.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2010 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 "content/browser/device_orientation/message_filter.h" + +#include "base/scoped_ptr.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/render_messages_params.h" +#include "content/browser/browser_thread.h" +#include "content/browser/device_orientation/orientation.h" +#include "content/browser/device_orientation/provider.h" +#include "content/browser/renderer_host/render_view_host.h" +#include "content/browser/renderer_host/render_view_host_notification_task.h" + +namespace device_orientation { + +MessageFilter::MessageFilter() : provider_(NULL) { +} + +MessageFilter::~MessageFilter() { +} + +class MessageFilter::ObserverDelegate + : public base::RefCounted<ObserverDelegate>, public Provider::Observer { + public: + // Create ObserverDelegate that observes provider and forwards updates to + // render_view_id in process_id. + // Will stop observing provider when destructed. + ObserverDelegate(Provider* provider, + int render_view_id, + IPC::Message::Sender* sender); + + // From Provider::Observer. + virtual void OnOrientationUpdate(const Orientation& orientation); + + private: + friend class base::RefCounted<ObserverDelegate>; + virtual ~ObserverDelegate(); + + scoped_refptr<Provider> provider_; + int render_view_id_; + IPC::Message::Sender* sender_; // Weak pointer. + + DISALLOW_COPY_AND_ASSIGN(ObserverDelegate); +}; + +MessageFilter::ObserverDelegate::ObserverDelegate(Provider* provider, + int render_view_id, + IPC::Message::Sender* sender) + : provider_(provider), + render_view_id_(render_view_id), + sender_(sender) { + provider_->AddObserver(this); +} + +MessageFilter::ObserverDelegate::~ObserverDelegate() { + provider_->RemoveObserver(this); +} + +void MessageFilter::ObserverDelegate::OnOrientationUpdate( + const Orientation& orientation) { + ViewMsg_DeviceOrientationUpdated_Params params; + params.can_provide_alpha = orientation.can_provide_alpha_; + params.alpha = orientation.alpha_; + params.can_provide_beta = orientation.can_provide_beta_; + params.beta = orientation.beta_; + params.can_provide_gamma = orientation.can_provide_gamma_; + params.gamma = orientation.gamma_; + + sender_->Send(new ViewMsg_DeviceOrientationUpdated(render_view_id_, params)); +} + +bool MessageFilter::OnMessageReceived(const IPC::Message& message, + bool* message_was_ok) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(MessageFilter, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_DeviceOrientation_StartUpdating, + OnStartUpdating) + IPC_MESSAGE_HANDLER(ViewHostMsg_DeviceOrientation_StopUpdating, + OnStopUpdating) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void MessageFilter::OnStartUpdating(int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (!provider_) + provider_ = Provider::GetInstance(); + + observers_map_[render_view_id] = new ObserverDelegate(provider_, + render_view_id, + this); +} + +void MessageFilter::OnStopUpdating(int render_view_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + observers_map_.erase(render_view_id); +} + +} // namespace device_orientation diff --git a/content/browser/device_orientation/message_filter.h b/content/browser/device_orientation/message_filter.h new file mode 100644 index 0000000..606d8bf --- /dev/null +++ b/content/browser/device_orientation/message_filter.h @@ -0,0 +1,44 @@ +// Copyright (c) 2010 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_DEVICE_ORIENTATION_MESSAGE_FILTER_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_MESSAGE_FILTER_H_ + +#include <map> + +#include "content/browser/browser_message_filter.h" +#include "content/browser/device_orientation/provider.h" + +namespace device_orientation { + +class Orientation; + +class MessageFilter : public BrowserMessageFilter { + public: + MessageFilter(); + + // BrowserMessageFilter implementation. + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok); + + private: + virtual ~MessageFilter(); + + void OnStartUpdating(int render_view_id); + void OnStopUpdating(int render_view_id); + + // Helper class that observes a Provider and forwards updates to a RenderView. + class ObserverDelegate; + + // map from render_view_id to ObserverDelegate. + std::map<int, scoped_refptr<ObserverDelegate> > observers_map_; + + scoped_refptr<Provider> provider_; + + DISALLOW_COPY_AND_ASSIGN(MessageFilter); +}; + +} // namespace device_orientation + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_MESSAGE_FILTER_H_ diff --git a/content/browser/device_orientation/orientation.h b/content/browser/device_orientation/orientation.h new file mode 100644 index 0000000..3f15d66 --- /dev/null +++ b/content/browser/device_orientation/orientation.h @@ -0,0 +1,53 @@ +// Copyright (c) 2010 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_DEVICE_ORIENTATION_ORIENTATION_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_ORIENTATION_H_ + +namespace device_orientation { +class Orientation { + public: + // alpha, beta and gamma are the rotations around the axes as specified in + // http://dev.w3.org/geo/api/spec-source-orientation.html + // + // can_provide_{alpha,beta,gamma} is true if data can be provided for that + // variable. + + Orientation(bool can_provide_alpha, double alpha, + bool can_provide_beta, double beta, + bool can_provide_gamma, double gamma) + : alpha_(alpha), + beta_(beta), + gamma_(gamma), + can_provide_alpha_(can_provide_alpha), + can_provide_beta_(can_provide_beta), + can_provide_gamma_(can_provide_gamma) { + } + + Orientation() + : alpha_(0), + beta_(0), + gamma_(0), + can_provide_alpha_(false), + can_provide_beta_(false), + can_provide_gamma_(false) { + } + + static Orientation Empty() { return Orientation(); } + + bool IsEmpty() const { + return !can_provide_alpha_ && !can_provide_beta_ && !can_provide_gamma_; + } + + double alpha_; + double beta_; + double gamma_; + bool can_provide_alpha_; + bool can_provide_beta_; + bool can_provide_gamma_; +}; + +} // namespace device_orientation + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_ORIENTATION_H_ diff --git a/content/browser/device_orientation/provider.cc b/content/browser/device_orientation/provider.cc new file mode 100644 index 0000000..e10da2b --- /dev/null +++ b/content/browser/device_orientation/provider.cc @@ -0,0 +1,52 @@ +// Copyright (c) 2010 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 "content/browser/device_orientation/provider.h" + +#include "base/logging.h" +#include "content/browser/browser_thread.h" +#include "content/browser/device_orientation/data_fetcher.h" +#include "content/browser/device_orientation/provider_impl.h" + +#if defined(OS_MACOSX) +#include "content/browser/device_orientation/accelerometer_mac.h" +#endif + +namespace device_orientation { + +Provider* Provider::GetInstance() { + if (!instance_) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + const ProviderImpl::DataFetcherFactory default_factories[] = { +#if defined(OS_MACOSX) + AccelerometerMac::Create, +#endif + NULL + }; + + instance_ = new ProviderImpl(default_factories); + } + return instance_; +} + +void Provider::SetInstanceForTests(Provider* provider) { + DCHECK(!instance_); + instance_ = provider; +} + +Provider* Provider::GetInstanceForTests() { + return instance_; +} + +Provider::Provider() { +} + +Provider::~Provider() { + DCHECK(instance_ == this); + instance_ = NULL; +} + +Provider* Provider::instance_ = NULL; + +} // namespace device_orientation diff --git a/content/browser/device_orientation/provider.h b/content/browser/device_orientation/provider.h new file mode 100644 index 0000000..ab44a89 --- /dev/null +++ b/content/browser/device_orientation/provider.h @@ -0,0 +1,56 @@ +// Copyright (c) 2010 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_DEVICE_ORIENTATION_PROVIDER_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_PROVIDER_H_ + +#include "base/ref_counted.h" + +namespace device_orientation { + +class Orientation; + +class Provider : public base::RefCountedThreadSafe<Provider> { + public: + class Observer { + public: + // Called when the orientation changes. + // An Observer must not synchronously call Provider::RemoveObserver + // or Provider::AddObserver when this is called. + virtual void OnOrientationUpdate(const Orientation& orientation) = 0; + }; + + // Returns a pointer to the singleton instance of this class. + // The caller should store the returned pointer in a scoped_refptr. + // The Provider instance is lazily constructed when GetInstance() is called, + // and destructed when the last scoped_refptr referring to it is destructed. + static Provider* GetInstance(); + + // Inject a mock Provider for testing. Only a weak pointer to the injected + // object will be held by Provider, i.e. it does not itself contribute to the + // injected object's reference count. + static void SetInstanceForTests(Provider* provider); + + // Get the current instance. Used for testing. + static Provider* GetInstanceForTests(); + + // Note: AddObserver may call back synchronously to the observer with + // orientation data. + virtual void AddObserver(Observer* observer) = 0; + virtual void RemoveObserver(Observer* observer) = 0; + + protected: + Provider(); + virtual ~Provider(); + + private: + friend class base::RefCountedThreadSafe<Provider>; + static Provider* instance_; + + DISALLOW_COPY_AND_ASSIGN(Provider); +}; + +} // namespace device_orientation + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_PROVIDER_H_ diff --git a/content/browser/device_orientation/provider_impl.cc b/content/browser/device_orientation/provider_impl.cc new file mode 100644 index 0000000..27c155f --- /dev/null +++ b/content/browser/device_orientation/provider_impl.cc @@ -0,0 +1,203 @@ +// Copyright (c) 2010 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 <cmath> +#include <set> +#include <vector> + +#include "base/logging.h" +#include "base/message_loop.h" +#include "base/task.h" +#include "base/threading/thread.h" +#include "base/threading/thread_restrictions.h" +#include "content/browser/device_orientation/orientation.h" +#include "content/browser/device_orientation/provider_impl.h" + +namespace device_orientation { + +ProviderImpl::ProviderImpl(const DataFetcherFactory factories[]) + : creator_loop_(MessageLoop::current()), + ALLOW_THIS_IN_INITIALIZER_LIST(do_poll_method_factory_(this)) { + for (const DataFetcherFactory* fp = factories; *fp; ++fp) + factories_.push_back(*fp); +} + +ProviderImpl::~ProviderImpl() { +} + +void ProviderImpl::AddObserver(Observer* observer) { + DCHECK(MessageLoop::current() == creator_loop_); + + observers_.insert(observer); + if (observers_.size() == 1) + Start(); + else + observer->OnOrientationUpdate(last_notification_); +} + +void ProviderImpl::RemoveObserver(Observer* observer) { + DCHECK(MessageLoop::current() == creator_loop_); + + observers_.erase(observer); + if (observers_.empty()) + Stop(); +} + +void ProviderImpl::Start() { + DCHECK(MessageLoop::current() == creator_loop_); + DCHECK(!polling_thread_.get()); + + polling_thread_.reset(new base::Thread("Device orientation polling thread")); + if (!polling_thread_->Start()) { + LOG(ERROR) << "Failed to start device orientation polling thread"; + polling_thread_.reset(); + return; + } + ScheduleInitializePollingThread(); +} + +void ProviderImpl::Stop() { + DCHECK(MessageLoop::current() == creator_loop_); + + // TODO(hans): Don't join the thread. See crbug.com/72286. + base::ThreadRestrictions::ScopedAllowIO allow_io; + + polling_thread_.reset(); + data_fetcher_.reset(); +} + +void ProviderImpl::DoInitializePollingThread( + std::vector<DataFetcherFactory> factories) { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + typedef std::vector<DataFetcherFactory>::const_iterator Iterator; + for (Iterator i = factories_.begin(), e = factories_.end(); i != e; ++i) { + DataFetcherFactory factory = *i; + scoped_ptr<DataFetcher> fetcher(factory()); + Orientation orientation; + + if (fetcher.get() && fetcher->GetOrientation(&orientation)) { + // Pass ownership of fetcher to provider_. + data_fetcher_.swap(fetcher); + last_orientation_ = orientation; + + // Notify observers. + ScheduleDoNotify(orientation); + + // Start polling. + ScheduleDoPoll(); + return; + } + } + + // When no orientation data can be provided. + ScheduleDoNotify(Orientation::Empty()); +} + +void ProviderImpl::ScheduleInitializePollingThread() { + DCHECK(MessageLoop::current() == creator_loop_); + + Task* task = NewRunnableMethod(this, + &ProviderImpl::DoInitializePollingThread, + factories_); + MessageLoop* polling_loop = polling_thread_->message_loop(); + polling_loop->PostTask(FROM_HERE, task); +} + +void ProviderImpl::DoNotify(const Orientation& orientation) { + DCHECK(MessageLoop::current() == creator_loop_); + + last_notification_ = orientation; + + typedef std::set<Observer*>::const_iterator Iterator; + for (Iterator i = observers_.begin(), e = observers_.end(); i != e; ++i) + (*i)->OnOrientationUpdate(orientation); + + if (orientation.IsEmpty()) { + // Notify observers about failure to provide data exactly once. + observers_.clear(); + Stop(); + } +} + +void ProviderImpl::ScheduleDoNotify(const Orientation& orientation) { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + Task* task = NewRunnableMethod(this, &ProviderImpl::DoNotify, orientation); + creator_loop_->PostTask(FROM_HERE, task); +} + +void ProviderImpl::DoPoll() { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + Orientation orientation; + if (!data_fetcher_->GetOrientation(&orientation)) { + LOG(ERROR) << "Failed to poll device orientation data fetcher."; + + ScheduleDoNotify(Orientation::Empty()); + return; + } + + if (SignificantlyDifferent(orientation, last_orientation_)) { + last_orientation_ = orientation; + ScheduleDoNotify(orientation); + } + + ScheduleDoPoll(); +} + +void ProviderImpl::ScheduleDoPoll() { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + + Task* task = do_poll_method_factory_.NewRunnableMethod(&ProviderImpl::DoPoll); + MessageLoop* polling_loop = polling_thread_->message_loop(); + polling_loop->PostDelayedTask(FROM_HERE, task, SamplingIntervalMs()); +} + +namespace { + +bool IsElementSignificantlyDifferent(bool can_provide_element1, + bool can_provide_element2, + double element1, + double element2) { + const double kThreshold = 0.1; + + if (can_provide_element1 != can_provide_element2) + return true; + if (can_provide_element1 && + std::fabs(element1 - element2) >= kThreshold) + return true; + return false; +} +} // namespace + +// Returns true if two orientations are considered different enough that +// observers should be notified of the new orientation. +bool ProviderImpl::SignificantlyDifferent(const Orientation& o1, + const Orientation& o2) { + return IsElementSignificantlyDifferent(o1.can_provide_alpha_, + o2.can_provide_alpha_, + o1.alpha_, + o2.alpha_) || + IsElementSignificantlyDifferent(o1.can_provide_beta_, + o2.can_provide_beta_, + o1.beta_, + o2.beta_) || + IsElementSignificantlyDifferent(o1.can_provide_gamma_, + o2.can_provide_gamma_, + o1.gamma_, + o2.gamma_); +} + +int ProviderImpl::SamplingIntervalMs() const { + DCHECK(MessageLoop::current() == polling_thread_->message_loop()); + DCHECK(data_fetcher_.get()); + + // TODO(erg): There used to be unused code here, that called a default + // implementation on the DataFetcherInterface that was never defined. I'm + // removing unused methods from headers. + return kDesiredSamplingIntervalMs; +} + +} // namespace device_orientation diff --git a/content/browser/device_orientation/provider_impl.h b/content/browser/device_orientation/provider_impl.h new file mode 100644 index 0000000..e8e2b99 --- /dev/null +++ b/content/browser/device_orientation/provider_impl.h @@ -0,0 +1,85 @@ +// Copyright (c) 2010 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_DEVICE_ORIENTATION_PROVIDER_IMPL_H_ +#define CONTENT_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_ + +#include <set> +#include <vector> + +#include "base/scoped_ptr.h" +#include "base/task.h" +#include "content/browser/device_orientation/data_fetcher.h" +#include "content/browser/device_orientation/orientation.h" +#include "content/browser/device_orientation/provider.h" + +class MessageLoop; + +namespace base { +class Thread; +} + +namespace device_orientation { + +class ProviderImpl : public Provider { + public: + typedef DataFetcher* (*DataFetcherFactory)(); + + // Create a ProviderImpl that uses the NULL-terminated factories array to find + // a DataFetcher that can provide orientation data. + ProviderImpl(const DataFetcherFactory factories[]); + + // From Provider. + virtual void AddObserver(Observer* observer); + virtual void RemoveObserver(Observer* observer); + + private: + virtual ~ProviderImpl(); + + // Starts or Stops the provider. Called from creator_loop_. + void Start(); + void Stop(); + + // Method for finding a suitable DataFetcher and starting the polling. + // Runs on the polling_thread_. + void DoInitializePollingThread(std::vector<DataFetcherFactory> factories); + void ScheduleInitializePollingThread(); + + // Method for polling a DataFetcher. Runs on the polling_thread_. + void DoPoll(); + void ScheduleDoPoll(); + + // Method for notifying observers of an orientation update. + // Runs on the creator_thread_. + void DoNotify(const Orientation& orientation); + void ScheduleDoNotify(const Orientation& orientation); + + static bool SignificantlyDifferent(const Orientation& orientation1, + const Orientation& orientation2); + + enum { kDesiredSamplingIntervalMs = 100 }; + int SamplingIntervalMs() const; + + // The Message Loop on which this object was created. + // Typically the I/O loop, but may be something else during testing. + MessageLoop* creator_loop_; + + // Members below are only to be used from the creator_loop_. + std::vector<DataFetcherFactory> factories_; + std::set<Observer*> observers_; + Orientation last_notification_; + + // When polling_thread_ is running, members below are only to be used + // from that thread. + scoped_ptr<DataFetcher> data_fetcher_; + Orientation last_orientation_; + ScopedRunnableMethodFactory<ProviderImpl> do_poll_method_factory_; + + // Polling is done on this background thread. + scoped_ptr<base::Thread> polling_thread_; +}; + +} // namespace device_orientation + +#endif // CONTENT_BROWSER_DEVICE_ORIENTATION_PROVIDER_IMPL_H_ diff --git a/content/browser/device_orientation/provider_unittest.cc b/content/browser/device_orientation/provider_unittest.cc new file mode 100644 index 0000000..facfc0c --- /dev/null +++ b/content/browser/device_orientation/provider_unittest.cc @@ -0,0 +1,373 @@ +// Copyright (c) 2010 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 <queue> + +#include "base/message_loop.h" +#include "base/synchronization/lock.h" +#include "base/task.h" +#include "content/browser/device_orientation/data_fetcher.h" +#include "content/browser/device_orientation/orientation.h" +#include "content/browser/device_orientation/provider.h" +#include "content/browser/device_orientation/provider_impl.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace device_orientation { +namespace { + +// Class for checking expectations on orientation updates from the Provider. +class UpdateChecker : public Provider::Observer { + public: + explicit UpdateChecker(int* expectations_count_ptr) + : expectations_count_ptr_(expectations_count_ptr) { + } + + // From Provider::Observer. + virtual void OnOrientationUpdate(const Orientation& orientation) { + ASSERT_FALSE(expectations_queue_.empty()); + + Orientation expected = expectations_queue_.front(); + expectations_queue_.pop(); + + EXPECT_EQ(expected.can_provide_alpha_, orientation.can_provide_alpha_); + EXPECT_EQ(expected.can_provide_beta_, orientation.can_provide_beta_); + EXPECT_EQ(expected.can_provide_gamma_, orientation.can_provide_gamma_); + if (expected.can_provide_alpha_) + EXPECT_EQ(expected.alpha_, orientation.alpha_); + if (expected.can_provide_beta_) + EXPECT_EQ(expected.beta_, orientation.beta_); + if (expected.can_provide_gamma_) + EXPECT_EQ(expected.gamma_, orientation.gamma_); + + --(*expectations_count_ptr_); + + if (*expectations_count_ptr_ == 0) { + MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + } + } + + void AddExpectation(const Orientation& orientation) { + expectations_queue_.push(orientation); + ++(*expectations_count_ptr_); + } + + private: + // Set up by the test fixture, which then blocks while it is accessed + // from OnOrientationUpdate which is executed on the test fixture's + // message_loop_. + int* expectations_count_ptr_; + std::queue<Orientation> expectations_queue_; +}; + +// Class for injecting test orientation data into the Provider. +class MockOrientationFactory : public base::RefCounted<MockOrientationFactory> { + public: + MockOrientationFactory() { + EXPECT_FALSE(instance_); + instance_ = this; + } + + ~MockOrientationFactory() { + instance_ = NULL; + } + + static DataFetcher* CreateDataFetcher() { + EXPECT_TRUE(instance_); + return new MockDataFetcher(instance_); + } + + void SetOrientation(const Orientation& orientation) { + base::AutoLock auto_lock(lock_); + orientation_ = orientation; + } + + private: + // Owned by ProviderImpl. Holds a reference back to MockOrientationFactory. + class MockDataFetcher : public DataFetcher { + public: + explicit MockDataFetcher(MockOrientationFactory* orientation_factory) + : orientation_factory_(orientation_factory) { } + + // From DataFetcher. Called by the Provider. + virtual bool GetOrientation(Orientation* orientation) { + base::AutoLock auto_lock(orientation_factory_->lock_); + *orientation = orientation_factory_->orientation_; + return true; + } + + private: + scoped_refptr<MockOrientationFactory> orientation_factory_; + }; + + static MockOrientationFactory* instance_; + Orientation orientation_; + base::Lock lock_; +}; + +MockOrientationFactory* MockOrientationFactory::instance_; + +// Mock DataFetcher that always fails to provide any orientation data. +class FailingDataFetcher : public DataFetcher { + public: + // Factory method; passed to and called by the ProviderImpl. + static DataFetcher* Create() { + return new FailingDataFetcher(); + } + + // From DataFetcher. + virtual bool GetOrientation(Orientation* orientation) { + return false; + } + + private: + FailingDataFetcher() {} +}; + +class DeviceOrientationProviderTest : public testing::Test { + public: + DeviceOrientationProviderTest() + : pending_expectations_(0) { + } + + virtual void TearDown() { + provider_ = NULL; + + // Make sure it is really gone. + EXPECT_FALSE(Provider::GetInstanceForTests()); + + // Clean up in any case, so as to not break subsequent test. + Provider::SetInstanceForTests(NULL); + } + + // Initialize the test fixture with a ProviderImpl that uses the + // DataFetcherFactories in the null-terminated factories array. + void Init(ProviderImpl::DataFetcherFactory* factories) { + provider_ = new ProviderImpl(factories); + Provider::SetInstanceForTests(provider_); + } + + // Initialize the test fixture with a ProviderImpl that uses the + // DataFetcherFactory factory. + void Init(ProviderImpl::DataFetcherFactory factory) { + ProviderImpl::DataFetcherFactory factories[] = { factory, NULL }; + Init(factories); + } + + protected: + // Number of pending expectations. + int pending_expectations_; + + // Provider instance under test. + scoped_refptr<Provider> provider_; + + // Message loop for the test thread. + MessageLoop message_loop_; +}; + +TEST_F(DeviceOrientationProviderTest, FailingTest) { + Init(FailingDataFetcher::Create); + + scoped_ptr<UpdateChecker> checker_a( + new UpdateChecker(&pending_expectations_)); + scoped_ptr<UpdateChecker> checker_b( + new UpdateChecker(&pending_expectations_)); + + checker_a->AddExpectation(Orientation::Empty()); + provider_->AddObserver(checker_a.get()); + MessageLoop::current()->Run(); + + checker_b->AddExpectation(Orientation::Empty()); + provider_->AddObserver(checker_b.get()); + MessageLoop::current()->Run(); +} + +TEST_F(DeviceOrientationProviderTest, ProviderIsSingleton) { + Init(FailingDataFetcher::Create); + + scoped_refptr<Provider> provider_a(Provider::GetInstance()); + scoped_refptr<Provider> provider_b(Provider::GetInstance()); + + EXPECT_EQ(provider_a.get(), provider_b.get()); +} + +TEST_F(DeviceOrientationProviderTest, BasicPushTest) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + + scoped_ptr<UpdateChecker> checker(new UpdateChecker(&pending_expectations_)); + checker->AddExpectation(kTestOrientation); + orientation_factory->SetOrientation(kTestOrientation); + provider_->AddObserver(checker.get()); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker.get()); +} + +TEST_F(DeviceOrientationProviderTest, MultipleObserversPushTest) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + const Orientation kTestOrientations[] = { + Orientation(true, 1, true, 2, true, 3), + Orientation(true, 4, true, 5, true, 6), + Orientation(true, 7, true, 8, true, 9)}; + + scoped_ptr<UpdateChecker> checker_a( + new UpdateChecker(&pending_expectations_)); + scoped_ptr<UpdateChecker> checker_b( + new UpdateChecker(&pending_expectations_)); + scoped_ptr<UpdateChecker> checker_c( + new UpdateChecker(&pending_expectations_)); + + checker_a->AddExpectation(kTestOrientations[0]); + orientation_factory->SetOrientation(kTestOrientations[0]); + provider_->AddObserver(checker_a.get()); + MessageLoop::current()->Run(); + + checker_a->AddExpectation(kTestOrientations[1]); + checker_b->AddExpectation(kTestOrientations[0]); + checker_b->AddExpectation(kTestOrientations[1]); + orientation_factory->SetOrientation(kTestOrientations[1]); + provider_->AddObserver(checker_b.get()); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker_a.get()); + checker_b->AddExpectation(kTestOrientations[2]); + checker_c->AddExpectation(kTestOrientations[1]); + checker_c->AddExpectation(kTestOrientations[2]); + orientation_factory->SetOrientation(kTestOrientations[2]); + provider_->AddObserver(checker_c.get()); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker_b.get()); + provider_->RemoveObserver(checker_c.get()); +} + +TEST_F(DeviceOrientationProviderTest, ObserverNotRemoved) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + const Orientation kTestOrientation2(true, 4, true, 5, true, 6); + + scoped_ptr<UpdateChecker> checker(new UpdateChecker(&pending_expectations_)); + checker->AddExpectation(kTestOrientation); + orientation_factory->SetOrientation(kTestOrientation); + provider_->AddObserver(checker.get()); + MessageLoop::current()->Run(); + + checker->AddExpectation(kTestOrientation2); + orientation_factory->SetOrientation(kTestOrientation2); + MessageLoop::current()->Run(); + + // Note that checker is not removed. This should not be a problem. +} + +TEST_F(DeviceOrientationProviderTest, StartFailing) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker( + &pending_expectations_)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker( + &pending_expectations_)); + + orientation_factory->SetOrientation(kTestOrientation); + checker_a->AddExpectation(kTestOrientation); + provider_->AddObserver(checker_a.get()); + MessageLoop::current()->Run(); + + checker_a->AddExpectation(Orientation::Empty()); + orientation_factory->SetOrientation(Orientation::Empty()); + MessageLoop::current()->Run(); + + checker_b->AddExpectation(Orientation::Empty()); + provider_->AddObserver(checker_b.get()); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker_a.get()); + provider_->RemoveObserver(checker_b.get()); +} + +TEST_F(DeviceOrientationProviderTest, StartStopStart) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + const Orientation kTestOrientation(true, 1, true, 2, true, 3); + const Orientation kTestOrientation2(true, 4, true, 5, true, 6); + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker( + &pending_expectations_)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker( + &pending_expectations_)); + + checker_a->AddExpectation(kTestOrientation); + orientation_factory->SetOrientation(kTestOrientation); + provider_->AddObserver(checker_a.get()); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker_a.get()); // This stops the Provider. + + checker_b->AddExpectation(kTestOrientation2); + orientation_factory->SetOrientation(kTestOrientation2); + provider_->AddObserver(checker_b.get()); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker_b.get()); +} + +TEST_F(DeviceOrientationProviderTest, SignificantlyDifferent) { + scoped_refptr<MockOrientationFactory> orientation_factory( + new MockOrientationFactory()); + Init(MockOrientationFactory::CreateDataFetcher); + + // Values that should be well below or above the implementation's + // significane threshold. + const double kInsignificantDifference = 1e-6; + const double kSignificantDifference = 30; + const double kAlpha = 4, kBeta = 5, kGamma = 6; + + const Orientation first_orientation(true, kAlpha, true, kBeta, true, kGamma); + + const Orientation second_orientation(true, kAlpha + kInsignificantDifference, + true, kBeta + kInsignificantDifference, + true, kGamma + kInsignificantDifference); + + const Orientation third_orientation(true, kAlpha + kSignificantDifference, + true, kBeta + kSignificantDifference, + true, kGamma + kSignificantDifference); + + scoped_ptr<UpdateChecker> checker_a(new UpdateChecker( + &pending_expectations_)); + scoped_ptr<UpdateChecker> checker_b(new UpdateChecker( + &pending_expectations_)); + + + orientation_factory->SetOrientation(first_orientation); + checker_a->AddExpectation(first_orientation); + provider_->AddObserver(checker_a.get()); + MessageLoop::current()->Run(); + + // The observers should not see this insignificantly different orientation. + orientation_factory->SetOrientation(second_orientation); + checker_b->AddExpectation(first_orientation); + provider_->AddObserver(checker_b.get()); + MessageLoop::current()->Run(); + + orientation_factory->SetOrientation(third_orientation); + checker_a->AddExpectation(third_orientation); + checker_b->AddExpectation(third_orientation); + MessageLoop::current()->Run(); + + provider_->RemoveObserver(checker_a.get()); + provider_->RemoveObserver(checker_b.get()); +} + +} // namespace + +} // namespace device_orientation diff --git a/content/browser/file_system/browser_file_system_helper.cc b/content/browser/file_system/browser_file_system_helper.cc new file mode 100644 index 0000000..81666f0 --- /dev/null +++ b/content/browser/file_system/browser_file_system_helper.cc @@ -0,0 +1,23 @@ +// Copyright (c) 2010 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 "content/browser/file_system/browser_file_system_helper.h" + +#include "base/file_path.h" +#include "base/command_line.h" +#include "chrome/common/chrome_switches.h" +#include "content/browser/browser_thread.h" + +scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext( + const FilePath& profile_path, bool is_incognito) { + return new fileapi::FileSystemContext( + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO), + profile_path, + is_incognito, + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kAllowFileAccessFromFiles), + CommandLine::ForCurrentProcess()->HasSwitch( + switches::kUnlimitedQuotaForFiles)); +} diff --git a/content/browser/file_system/browser_file_system_helper.h b/content/browser/file_system/browser_file_system_helper.h new file mode 100644 index 0000000..55c6e3a --- /dev/null +++ b/content/browser/file_system/browser_file_system_helper.h @@ -0,0 +1,17 @@ +// Copyright (c) 2010 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_FILE_SYSTEM_BROWSER_FILE_SYSTEM_HELPER_H_ +#define CONTENT_BROWSER_FILE_SYSTEM_BROWSER_FILE_SYSTEM_HELPER_H_ + +#include "base/ref_counted.h" +#include "base/scoped_ptr.h" +#include "webkit/fileapi/file_system_context.h" + +// Helper method that returns FileSystemContext constructed for +// the browser process. +scoped_refptr<fileapi::FileSystemContext> CreateFileSystemContext( + const FilePath& profile_path, bool is_incognito); + +#endif // CONTENT_BROWSER_FILE_SYSTEM_BROWSER_FILE_SYSTEM_HELPER_H_ diff --git a/content/browser/file_system/file_system_dispatcher_host.cc b/content/browser/file_system/file_system_dispatcher_host.cc new file mode 100644 index 0000000..df4f5d8 --- /dev/null +++ b/content/browser/file_system/file_system_dispatcher_host.cc @@ -0,0 +1,250 @@ +// Copyright (c) 2010 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 "content/browser/file_system/file_system_dispatcher_host.h" + +#include <string> +#include <vector> + +#include "base/file_path.h" +#include "base/threading/thread.h" +#include "base/time.h" +#include "chrome/browser/content_settings/host_content_settings_map.h" +#include "chrome/browser/net/chrome_url_request_context.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/renderer_host/browser_render_process_host.h" +#include "chrome/common/net/url_request_context_getter.h" +#include "chrome/common/render_messages.h" +#include "chrome/common/render_messages_params.h" +#include "googleurl/src/gurl.h" +#include "net/url_request/url_request_context.h" +#include "webkit/fileapi/file_system_callback_dispatcher.h" +#include "webkit/fileapi/file_system_context.h" +#include "webkit/fileapi/file_system_operation.h" +#include "webkit/fileapi/file_system_operation.h" +#include "webkit/fileapi/file_system_path_manager.h" +#include "webkit/fileapi/file_system_quota_manager.h" + +using fileapi::FileSystemCallbackDispatcher; +using fileapi::FileSystemOperation; +using fileapi::FileSystemQuotaManager; + +class BrowserFileSystemCallbackDispatcher + : public FileSystemCallbackDispatcher { + public: + BrowserFileSystemCallbackDispatcher( + FileSystemDispatcherHost* dispatcher_host, int request_id) + : dispatcher_host_(dispatcher_host), + request_id_(request_id) { + DCHECK(dispatcher_host_); + } + + virtual ~BrowserFileSystemCallbackDispatcher() { + dispatcher_host_->UnregisterOperation(request_id_); + } + + virtual void DidSucceed() { + dispatcher_host_->Send(new ViewMsg_FileSystem_DidSucceed(request_id_)); + } + + virtual void DidReadMetadata(const base::PlatformFileInfo& info) { + dispatcher_host_->Send(new ViewMsg_FileSystem_DidReadMetadata( + request_id_, info)); + } + + virtual void DidReadDirectory( + const std::vector<base::FileUtilProxy::Entry>& entries, bool has_more) { + dispatcher_host_->Send(new ViewMsg_FileSystem_DidReadDirectory( + request_id_, entries, has_more)); + } + + virtual void DidOpenFileSystem(const std::string& name, + const FilePath& path) { + dispatcher_host_->Send( + new ViewMsg_OpenFileSystemRequest_Complete( + request_id_, !path.empty(), name, path)); + } + + virtual void DidFail(base::PlatformFileError error_code) { + dispatcher_host_->Send(new ViewMsg_FileSystem_DidFail( + request_id_, error_code)); + } + + virtual void DidWrite(int64 bytes, bool complete) { + dispatcher_host_->Send(new ViewMsg_FileSystem_DidWrite( + request_id_, bytes, complete)); + } + + private: + scoped_refptr<FileSystemDispatcherHost> dispatcher_host_; + int request_id_; +}; + +FileSystemDispatcherHost::FileSystemDispatcherHost(Profile* profile) + : context_(profile->GetFileSystemContext()), + host_content_settings_map_(profile->GetHostContentSettingsMap()), + request_context_getter_(profile->GetRequestContext()) { +} + +FileSystemDispatcherHost::FileSystemDispatcherHost( + ChromeURLRequestContext* context) + : context_(context->file_system_context()), + host_content_settings_map_(context->host_content_settings_map()), + request_context_(context) { +} + +FileSystemDispatcherHost::~FileSystemDispatcherHost() { +} + +void FileSystemDispatcherHost::OnChannelConnected(int32 peer_pid) { + BrowserMessageFilter::OnChannelConnected(peer_pid); + + if (request_context_getter_.get()) { + DCHECK(!request_context_.get()); + request_context_ = request_context_getter_->GetURLRequestContext(); + } + DCHECK(request_context_.get()); +} + +bool FileSystemDispatcherHost::OnMessageReceived( + const IPC::Message& message, bool* message_was_ok) { + *message_was_ok = true; + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(FileSystemDispatcherHost, message, *message_was_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_OpenFileSystemRequest, OnOpenFileSystem) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Move, OnMove) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Copy, OnCopy) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Remove, OnRemove) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_ReadMetadata, OnReadMetadata) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Create, OnCreate) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Exists, OnExists) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_ReadDirectory, OnReadDirectory) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Write, OnWrite) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_Truncate, OnTruncate) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_TouchFile, OnTouchFile) + IPC_MESSAGE_HANDLER(ViewHostMsg_FileSystem_CancelWrite, OnCancel) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP_EX() + return handled; +} + +void FileSystemDispatcherHost::OnOpenFileSystem( + int request_id, const GURL& origin_url, fileapi::FileSystemType type, + int64 requested_size, bool create) { + ContentSetting content_setting = + host_content_settings_map_->GetContentSetting( + origin_url, CONTENT_SETTINGS_TYPE_COOKIES, ""); + DCHECK((content_setting == CONTENT_SETTING_ALLOW) || + (content_setting == CONTENT_SETTING_BLOCK) || + (content_setting == CONTENT_SETTING_SESSION_ONLY)); + if (content_setting == CONTENT_SETTING_BLOCK) { + // TODO(kinuko): Need to notify the UI thread to indicate that + // there's a blocked content. + Send(new ViewMsg_OpenFileSystemRequest_Complete( + request_id, false, std::string(), FilePath())); + return; + } + + GetNewOperation(request_id)->OpenFileSystem(origin_url, type, create); +} + +void FileSystemDispatcherHost::OnMove( + int request_id, const FilePath& src_path, const FilePath& dest_path) { + GetNewOperation(request_id)->Move(src_path, dest_path); +} + +void FileSystemDispatcherHost::OnCopy( + int request_id, const FilePath& src_path, const FilePath& dest_path) { + GetNewOperation(request_id)->Copy(src_path, dest_path); +} + +void FileSystemDispatcherHost::OnRemove( + int request_id, const FilePath& path, bool recursive) { + GetNewOperation(request_id)->Remove(path, recursive); +} + +void FileSystemDispatcherHost::OnReadMetadata( + int request_id, const FilePath& path) { + GetNewOperation(request_id)->GetMetadata(path); +} + +void FileSystemDispatcherHost::OnCreate( + int request_id, const FilePath& path, bool exclusive, + bool is_directory, bool recursive) { + if (is_directory) + GetNewOperation(request_id)->CreateDirectory(path, exclusive, recursive); + else + GetNewOperation(request_id)->CreateFile(path, exclusive); +} + +void FileSystemDispatcherHost::OnExists( + int request_id, const FilePath& path, bool is_directory) { + if (is_directory) + GetNewOperation(request_id)->DirectoryExists(path); + else + GetNewOperation(request_id)->FileExists(path); +} + +void FileSystemDispatcherHost::OnReadDirectory( + int request_id, const FilePath& path) { + GetNewOperation(request_id)->ReadDirectory(path); +} + +void FileSystemDispatcherHost::OnWrite( + int request_id, + const FilePath& path, + const GURL& blob_url, + int64 offset) { + GetNewOperation(request_id)->Write( + request_context_, path, blob_url, offset); +} + +void FileSystemDispatcherHost::OnTruncate( + int request_id, + const FilePath& path, + int64 length) { + GetNewOperation(request_id)->Truncate(path, length); +} + +void FileSystemDispatcherHost::OnTouchFile( + int request_id, + const FilePath& path, + const base::Time& last_access_time, + const base::Time& last_modified_time) { + GetNewOperation(request_id)->TouchFile( + path, last_access_time, last_modified_time); +} + +void FileSystemDispatcherHost::OnCancel( + int request_id, + int request_id_to_cancel) { + FileSystemOperation* write = operations_.Lookup( + request_id_to_cancel); + if (write) { + // The cancel will eventually send both the write failure and the cancel + // success. + write->Cancel(GetNewOperation(request_id)); + } else { + // The write already finished; report that we failed to stop it. + Send(new ViewMsg_FileSystem_DidFail( + request_id, base::PLATFORM_FILE_ERROR_INVALID_OPERATION)); + } +} + +FileSystemOperation* FileSystemDispatcherHost::GetNewOperation( + int request_id) { + BrowserFileSystemCallbackDispatcher* dispatcher = + new BrowserFileSystemCallbackDispatcher(this, request_id); + FileSystemOperation* operation = new FileSystemOperation( + dispatcher, + BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE), + context_); + operations_.AddWithID(operation, request_id); + return operation; +} + +void FileSystemDispatcherHost::UnregisterOperation(int request_id) { + DCHECK(operations_.Lookup(request_id)); + operations_.Remove(request_id); +} diff --git a/content/browser/file_system/file_system_dispatcher_host.h b/content/browser/file_system/file_system_dispatcher_host.h new file mode 100644 index 0000000..728720e --- /dev/null +++ b/content/browser/file_system/file_system_dispatcher_host.h @@ -0,0 +1,104 @@ +// Copyright (c) 2011 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_FILE_SYSTEM_FILE_SYSTEM_DISPATCHER_HOST_H_ +#define CONTENT_BROWSER_FILE_SYSTEM_FILE_SYSTEM_DISPATCHER_HOST_H_ + +#include <set> + +#include "base/basictypes.h" +#include "base/id_map.h" +#include "content/browser/browser_message_filter.h" +#include "webkit/fileapi/file_system_types.h" + +namespace base { +class Time; +} + +class ChromeURLRequestContext; +class FilePath; +class GURL; +class HostContentSettingsMap; +class Profile; +class Receiver; +class RenderMessageFilter; +class URLRequestContextGetter; + +namespace net { +class URLRequestContext; +} // namespace net + +namespace fileapi { +class FileSystemContext; +class FileSystemOperation; +} + +class FileSystemDispatcherHost : public BrowserMessageFilter { + public: + // Used by the renderer. + explicit FileSystemDispatcherHost(Profile* profile); + // Used by the worker, since it has the context handy already. + explicit FileSystemDispatcherHost(ChromeURLRequestContext* context); + ~FileSystemDispatcherHost(); + + // BrowserMessageFilter implementation. + virtual void OnChannelConnected(int32 peer_pid); + virtual bool OnMessageReceived(const IPC::Message& message, + bool* message_was_ok); + + + void OnOpenFileSystem(int request_id, + const GURL& origin_url, + fileapi::FileSystemType type, + int64 requested_size, + bool create); + void OnMove(int request_id, + const FilePath& src_path, + const FilePath& dest_path); + void OnCopy(int request_id, + const FilePath& src_path, + const FilePath& dest_path); + void OnRemove(int request_id, const FilePath& path, bool recursive); + void OnReadMetadata(int request_id, const FilePath& path); + void OnCreate(int request_id, + const FilePath& path, + bool exclusive, + bool is_directory, + bool recursive); + void OnExists(int request_id, const FilePath& path, bool is_directory); + void OnReadDirectory(int request_id, const FilePath& path); + void OnWrite(int request_id, + const FilePath& path, + const GURL& blob_url, + int64 offset); + void OnTruncate(int request_id, const FilePath& path, int64 length); + void OnTouchFile(int request_id, + const FilePath& path, + const base::Time& last_access_time, + const base::Time& last_modified_time); + void OnCancel(int request_id, int request_to_cancel); + void UnregisterOperation(int request_id); + + private: + // Creates a new FileSystemOperation. + fileapi::FileSystemOperation* GetNewOperation(int request_id); + + scoped_refptr<fileapi::FileSystemContext> context_; + + // Used to look up permissions. + scoped_refptr<HostContentSettingsMap> host_content_settings_map_; + + // Keeps ongoing file system operations. + typedef IDMap<fileapi::FileSystemOperation> OperationsMap; + OperationsMap operations_; + + // This holds the URLRequestContextGetter until Init() can be called from the + // IO thread, which will extract the net::URLRequestContext from it. + scoped_refptr<URLRequestContextGetter> request_context_getter_; + scoped_refptr<net::URLRequestContext> request_context_; + + DISALLOW_COPY_AND_ASSIGN(FileSystemDispatcherHost); +}; + +#endif // CONTENT_BROWSER_FILE_SYSTEM_FILE_SYSTEM_DISPATCHER_HOST_H_ |