summaryrefslogtreecommitdiffstats
path: root/content/browser
diff options
context:
space:
mode:
authorjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-24 23:37:35 +0000
committerjam@chromium.org <jam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-02-24 23:37:35 +0000
commit1eaa4f7c5ad1d08be49cea5b2c3c6cd64b5e2e1c (patch)
tree7e91b52608eac0a555856908c588b529caa58965 /content/browser
parentfbf3ff838fb1dbe7493c4df4c0a619f6c406682d (diff)
downloadchromium_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')
-rw-r--r--content/browser/OWNERS1
-rw-r--r--content/browser/appcache/appcache_dispatcher_host.cc237
-rw-r--r--content/browser/appcache/appcache_dispatcher_host.h96
-rw-r--r--content/browser/appcache/appcache_frontend_proxy.cc52
-rw-r--r--content/browser/appcache/appcache_frontend_proxy.h40
-rw-r--r--content/browser/appcache/appcache_ui_test.cc105
-rw-r--r--content/browser/appcache/chrome_appcache_service.cc147
-rw-r--r--content/browser/appcache/chrome_appcache_service.h72
-rw-r--r--content/browser/appcache/chrome_appcache_service_unittest.cc101
-rw-r--r--content/browser/appcache/view_appcache_internals_job_factory.cc28
-rw-r--r--content/browser/appcache/view_appcache_internals_job_factory.h22
-rw-r--r--content/browser/device_orientation/accelerometer_mac.cc412
-rw-r--r--content/browser/device_orientation/accelerometer_mac.h104
-rw-r--r--content/browser/device_orientation/data_fetcher.h20
-rw-r--r--content/browser/device_orientation/device_orientation_browsertest.cc70
-rw-r--r--content/browser/device_orientation/message_filter.cc105
-rw-r--r--content/browser/device_orientation/message_filter.h44
-rw-r--r--content/browser/device_orientation/orientation.h53
-rw-r--r--content/browser/device_orientation/provider.cc52
-rw-r--r--content/browser/device_orientation/provider.h56
-rw-r--r--content/browser/device_orientation/provider_impl.cc203
-rw-r--r--content/browser/device_orientation/provider_impl.h85
-rw-r--r--content/browser/device_orientation/provider_unittest.cc373
-rw-r--r--content/browser/file_system/browser_file_system_helper.cc23
-rw-r--r--content/browser/file_system/browser_file_system_helper.h17
-rw-r--r--content/browser/file_system/file_system_dispatcher_host.cc250
-rw-r--r--content/browser/file_system/file_system_dispatcher_host.h104
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_