diff options
author | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-19 12:11:28 +0000 |
---|---|---|
committer | bulach@chromium.org <bulach@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-02-19 12:11:28 +0000 |
commit | 58c321dd57a1fc00c2c51b0a8b6e547fdf32aa74 (patch) | |
tree | cdd273a38ba1b449952d5701e6ceb6f46142c073 | |
parent | 2375d294b30e5d0b0bc63d26a417203959ef32af (diff) | |
download | chromium_src-58c321dd57a1fc00c2c51b0a8b6e547fdf32aa74.zip chromium_src-58c321dd57a1fc00c2c51b0a8b6e547fdf32aa74.tar.gz chromium_src-58c321dd57a1fc00c2c51b0a8b6e547fdf32aa74.tar.bz2 |
Second try for:
http://src.chromium.org/viewvc/chrome?view=rev&revision=39374
Initial Geolocation implementation
Adds IPC plumbing.
Adds Infobar buttons for requesting permission
This change specifically:
ui_test_utils::WaitForAppModalDialog registers for listening to notifications too late, i.e., after the dialog had been triggered.
Exposes AppModalDialogObserver so that we can register, trigger the dialog, then wait for it.
Review URL: http://codereview.chromium.org/647048
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@39435 0039d316-1c4b-4281-b951-d872f2087c98
32 files changed, 1228 insertions, 40 deletions
diff --git a/chrome/app/generated_resources.grd b/chrome/app/generated_resources.grd index 502d4f8..22f82c4 100644 --- a/chrome/app/generated_resources.grd +++ b/chrome/app/generated_resources.grd @@ -6510,7 +6510,21 @@ Keep your key file in a safe place. You will need it to create new versions of y The following Privacy Blacklists prevented this content from showing: </message> - <!-- ProcessSingleton --> + <!-- Geolocation messages --> + <message name="IDS_GEOLOCATION_INFOBAR_QUESTION" desc="Question asked on the info bar whenever URL wants to access the user physical location"> + <ph name="URL"> + $1<ex>google.com</ex> + </ph> wants to track your physical location + </message> + <message name="IDS_GEOLOCATION_ALLOW_BUTTON" desc="A button in geolocation infobar for allowing access to geolocation for a given domain."> + Allow + </message> + <message name="IDS_GEOLOCATION_DENY_BUTTON" desc="A button in geolocation infobar for denying access to geolocation for a given domain."> + Deny + </message> + + + <!-- ProcessSingleton --> <message name="IDS_PROFILE_IN_USE_LINUX" desc="Message shown when the browser cannot start because the profile is in use on a different host."> The profile appears to be in use by process <ph name="PROCESS_ID">$1<ex>12345</ex></ph> on host <ph name="HOST_NAME">$2<ex>example.com</ex></ph>. If you are sure no other processes are using this profile, delete the file <ph name="LOCK_FILE">$3<ex>/home/user/.config/google-chrome/SingletonLock</ex></ph> and restart <ph name="PRODUCT_NAME">$4<ex>Google Chrome</ex></ph>. </message> diff --git a/chrome/app/theme/theme_resources.grd b/chrome/app/theme/theme_resources.grd index acf7d2f..64f075e 100644 --- a/chrome/app/theme/theme_resources.grd +++ b/chrome/app/theme/theme_resources.grd @@ -318,6 +318,10 @@ <include name="IDR_BALLOON_OPTIONS_ARROW" file="balloon_options_arrow.png" type="BINDATA" /> <include name="IDR_BALLOON_OPTIONS_ARROW_HOVER" file="balloon_options_arrow_hover.png" type="BINDATA" /> + <!-- Geolocation --> + <include name="IDR_GEOLOCATION_INFOBAR_ICON" file="geolocation_infobar_icon.png" type="BINDATA" /> + + <if expr="pp_ifdef('_google_chrome')"> <include name="IDR_ABOUT_BACKGROUND" file="google_chrome/about_background.png" type="BINDATA" /> <include name="IDR_ABOUT_BACKGROUND_RTL" file="google_chrome/about_background_rtl.png" type="BINDATA" /> diff --git a/chrome/browser/geolocation/geolocation_dispatcher_host.cc b/chrome/browser/geolocation/geolocation_dispatcher_host.cc new file mode 100644 index 0000000..1217224 --- /dev/null +++ b/chrome/browser/geolocation/geolocation_dispatcher_host.cc @@ -0,0 +1,154 @@ +// 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 "chrome/browser/geolocation/geolocation_dispatcher_host.h" + +#include "chrome/common/geoposition.h" +#include "chrome/browser/geolocation/geolocation_permission_context.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host_notification_task.h" +#include "chrome/browser/renderer_host/resource_message_filter.h" +#include "chrome/common/render_messages.h" + +GeolocationDispatcherHost::GeolocationDispatcherHost( + int resource_message_filter_process_id, + GeolocationPermissionContext* geolocation_permission_context) + : resource_message_filter_process_id_(resource_message_filter_process_id), + geolocation_permission_context_(geolocation_permission_context) { + // This is initialized by ResourceMessageFilter. Do not add any non-trivial + // initialization here, defer to OnRegisterBridge which is triggered whenever + // a javascript geolocation object is actually initialized. +} + +GeolocationDispatcherHost::~GeolocationDispatcherHost() { +} + +bool GeolocationDispatcherHost::OnMessageReceived( + const IPC::Message& msg, bool* msg_was_ok) { + *msg_was_ok = true; + bool handled = true; + IPC_BEGIN_MESSAGE_MAP_EX(GeolocationDispatcherHost, msg, *msg_was_ok) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_RegisterDispatcher, + OnRegisterDispatcher) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_UnregisterDispatcher, + OnUnregisterDispatcher) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_RequestPermission, + OnRequestPermission) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_StartUpdating, + OnStartUpdating) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_StopUpdating, + OnStopUpdating) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_Suspend, + OnSuspend) + IPC_MESSAGE_HANDLER(ViewHostMsg_Geolocation_Resume, + OnResume) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void GeolocationDispatcherHost::NotifyPositionUpdated( + const Geoposition& geoposition) { + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, &GeolocationDispatcherHost::NotifyPositionUpdated, + geoposition)); + return; + } + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + for (std::set<GeolocationServiceRenderId>::iterator geo = + geolocation_renderers_.begin(); + geo != geolocation_renderers_.end(); + ++geo) { + IPC::Message* message; + if (geoposition.error_code != Geoposition::ERROR_CODE_NONE) { + message = new ViewMsg_Geolocation_PositionUpdated( + geo->route_id, geoposition); + } else { + message = new ViewMsg_Geolocation_Error( + geo->route_id, geoposition.error_code, + WideToUTF8(geoposition.error_message)); + } + CallRenderViewHost( + geo->process_id, geo->route_id, &RenderViewHost::Send, message); + } +} + +void GeolocationDispatcherHost::OnRegisterDispatcher(int route_id) { + // TODO(bulach): is this the right way to get the RendererViewHost process id + // to be used by RenderViewHost::FromID? + RegisterDispatcher(resource_message_filter_process_id_, route_id); +} + +void GeolocationDispatcherHost::OnUnregisterDispatcher(int route_id) { + UnregisterDispatcher(resource_message_filter_process_id_, route_id); +} + +void GeolocationDispatcherHost::OnRequestPermission( + int route_id, int bridge_id, const GURL& origin) { + LOG(INFO) << "permission request"; + geolocation_permission_context_->RequestGeolocationPermission( + resource_message_filter_process_id_, route_id, bridge_id, origin); +} + +void GeolocationDispatcherHost::OnStartUpdating( + int route_id, int bridge_id, bool high_accuracy) { + LOG(INFO) << "start updating" << route_id; + // TODO(bulach): connect this with GeolocationServiceProvider. +} + +void GeolocationDispatcherHost::OnStopUpdating(int route_id, int bridge_id) { + LOG(INFO) << "stop updating" << route_id; + // TODO(bulach): connect this with GeolocationServiceProvider. +} + +void GeolocationDispatcherHost::OnSuspend(int route_id, int bridge_id) { + LOG(INFO) << "suspend" << route_id; + // TODO(bulach): connect this with GeolocationServiceProvider. +} + +void GeolocationDispatcherHost::OnResume(int route_id, int bridge_id) { + LOG(INFO) << "resume" << route_id; + // TODO(bulach): connect this with GeolocationServiceProvider. +} + +void GeolocationDispatcherHost::RegisterDispatcher( + int process_id, int route_id) { + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, &GeolocationDispatcherHost::RegisterDispatcher, process_id, + route_id)); + return; + } + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + + GeolocationServiceRenderId geolocation_render_id(process_id, route_id); + std::set<GeolocationServiceRenderId>::const_iterator existing = + geolocation_renderers_.find(geolocation_render_id); + DCHECK(existing == geolocation_renderers_.end()); + geolocation_renderers_.insert(geolocation_render_id); +} + +void GeolocationDispatcherHost::UnregisterDispatcher( + int process_id, int route_id) { + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, &GeolocationDispatcherHost::UnregisterDispatcher, process_id, + route_id)); + return; + } + GeolocationServiceRenderId geolocation_render_id(process_id, route_id); + std::set<GeolocationServiceRenderId>::iterator existing = + geolocation_renderers_.find(geolocation_render_id); + DCHECK(existing != geolocation_renderers_.end()); + geolocation_renderers_.erase(existing); +} diff --git a/chrome/browser/geolocation/geolocation_dispatcher_host.h b/chrome/browser/geolocation/geolocation_dispatcher_host.h new file mode 100644 index 0000000..8bd7eaf --- /dev/null +++ b/chrome/browser/geolocation/geolocation_dispatcher_host.h @@ -0,0 +1,77 @@ +// 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 CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_ +#define CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_ + +#include <set> + +#include "base/basictypes.h" +#include "base/ref_counted.h" +#include "ipc/ipc_message.h" + +class GeolocationPermissionContext; +struct Geoposition; +class GURL; +class ResourceMessageFilter; + +// GeolocationDispatcherHost is a delegate for Geolocation messages used by +// ResourceMessageFilter. +// It's the complement of GeolocationDispatcher (owned by RenderView). +class GeolocationDispatcherHost + : public base::RefCountedThreadSafe<GeolocationDispatcherHost> { + public: + GeolocationDispatcherHost( + int resource_message_filter_process_id, + GeolocationPermissionContext* geolocation_permission_context); + + // Called to possibly handle the incoming IPC message. Returns true if + // handled. Called in the browser process. + bool OnMessageReceived(const IPC::Message& msg, bool* msg_was_ok); + + // Tells the render view that a new geolocation position is available. + void NotifyPositionUpdated(const Geoposition& geoposition); + + private: + friend class base::RefCountedThreadSafe<GeolocationDispatcherHost>; + ~GeolocationDispatcherHost(); + + void OnRegisterDispatcher(int route_id); + void OnUnregisterDispatcher(int route_id); + void OnRequestPermission(int route_id, int bridge_id, const GURL& origin); + void OnStartUpdating(int route_id, int bridge_id, bool high_accuracy); + void OnStopUpdating(int route_id, int bridge_id); + void OnSuspend(int route_id, int bridge_id); + void OnResume(int route_id, int bridge_id); + + // Registers the bridge created in the renderer side. They'll delegate to the + // UI thread if not already in there. + void RegisterDispatcher(int process_id, int route_id); + void UnregisterDispatcher(int process_id, int route_id); + + int resource_message_filter_process_id_; + scoped_refptr<GeolocationPermissionContext> geolocation_permission_context_; + + struct GeolocationServiceRenderId { + int process_id; + int route_id; + GeolocationServiceRenderId(int process_id, int route_id) + : process_id(process_id), route_id(route_id) { + } + bool operator==(const GeolocationServiceRenderId& rhs) const { + return process_id == rhs.route_id && + route_id == rhs.route_id; + } + bool operator<(const GeolocationServiceRenderId& rhs) const { + return process_id < rhs.route_id && + route_id < rhs.route_id; + } + }; + // Only used on the UI thread. + std::set<GeolocationServiceRenderId> geolocation_renderers_; + + DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcherHost); +}; + +#endif // CHROME_BROWSER_GEOLOCATION_GEOLOCATION_DISPATCHER_HOST_H_ diff --git a/chrome/browser/geolocation/geolocation_permission_context.cc b/chrome/browser/geolocation/geolocation_permission_context.cc new file mode 100644 index 0000000..853e0a7 --- /dev/null +++ b/chrome/browser/geolocation/geolocation_permission_context.cc @@ -0,0 +1,291 @@ +// 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 "chrome/browser/geolocation/geolocation_permission_context.h" + +#include "app/l10n_util.h" +#include "app/resource_bundle.h" +#include "base/file_util.h" +#include "googleurl/src/gurl.h" +#include "chrome/browser/browser.h" +#include "chrome/browser/browser_list.h" +#include "chrome/browser/chrome_thread.h" +#include "chrome/browser/geolocation/geolocation_dispatcher_host.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/renderer_host/render_process_host.h" +#include "chrome/browser/renderer_host/render_view_host.h" +#include "chrome/browser/renderer_host/render_view_host_notification_task.h" +#include "chrome/browser/tab_contents/infobar_delegate.h" +#include "chrome/browser/tab_contents/tab_contents.h" +#include "chrome/common/json_value_serializer.h" +#include "chrome/common/render_messages.h" +#include "grit/generated_resources.h" +#include "grit/theme_resources.h" + +namespace { + +const FilePath::CharType kGeolocationPermissionPath[] = + FILE_PATH_LITERAL("Geolocation"); + +const wchar_t kAllowedDictionaryKey[] = L"allowed"; + +// This is the delegate used to display the confirmation info bar. +class GeolocationConfirmInfoBarDelegate : public ConfirmInfoBarDelegate { + public: + GeolocationConfirmInfoBarDelegate( + TabContents* tab_contents, GeolocationPermissionContext* context, + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin) + : ConfirmInfoBarDelegate(tab_contents), context_(context), + render_process_id_(render_process_id), render_view_id_(render_view_id), + bridge_id_(bridge_id), origin_(origin) { + } + + // ConfirmInfoBarDelegate + virtual Type GetInfoBarType() { return INFO_TYPE; } + virtual bool Accept() { return SetPermission(true); } + virtual bool Cancel() { return SetPermission(false); } + virtual int GetButtons() const { return BUTTON_OK | BUTTON_CANCEL; } + + virtual std::wstring GetButtonLabel(InfoBarButton button) const { + switch (button) { + case BUTTON_OK: + return l10n_util::GetString(IDS_GEOLOCATION_ALLOW_BUTTON); + case BUTTON_CANCEL: + return l10n_util::GetString(IDS_GEOLOCATION_DENY_BUTTON); + default: + // All buttons are labeled above. + NOTREACHED() << "Bad button id " << button; + return L""; + } + } + + virtual std::wstring GetMessageText() const { + return l10n_util::GetStringF( + IDS_GEOLOCATION_INFOBAR_QUESTION, UTF8ToWide(origin_.host())); + } + + virtual SkBitmap* GetIcon() const { + return ResourceBundle::GetSharedInstance().GetBitmapNamed( + IDR_GEOLOCATION_INFOBAR_ICON); + } + + private: + bool SetPermission(bool confirm) { + context_->SetPermission( + render_process_id_, render_view_id_, bridge_id_, origin_, confirm); + return true; + } + + scoped_refptr<GeolocationPermissionContext> context_; + int render_process_id_; + int render_view_id_; + int bridge_id_; + GURL origin_; +}; + +// TODO(bulach): use HostContentSettingsMap instead! +FilePath::StringType StdStringToFilePathString(const std::string& std_string) { +#if defined(OS_WIN) + return UTF8ToWide(std_string); +#else + return std_string; +#endif +} + +std::string GURLToCacheKey(const GURL& gurl) { + return gurl.host(); +} + +// Returns true if permission was successfully read from file, and *allowed +// be set accordingly. +// Returns false otherwise. +bool ReadPermissionFromFile( + const GURL& origin, const FilePath& permissions_path, bool* allowed) { + DCHECK(allowed); + *allowed = false; + // TODO(bulach): this is probably wrong! is there any utility to convert a URL + // to FilePath? + FilePath permission_file( + permissions_path.Append(StdStringToFilePathString( + GURLToCacheKey(origin)))); + if (!file_util::PathExists(permission_file)) + return false; + JSONFileValueSerializer serializer(permission_file); + scoped_ptr<Value> root_value(serializer.Deserialize(NULL)); + bool ret = root_value.get() && + root_value->GetType() == Value::TYPE_DICTIONARY; + DictionaryValue* dictionary = static_cast<DictionaryValue*>(root_value.get()); + return ret && + dictionary->GetBoolean(kAllowedDictionaryKey, allowed); +} + +void SavePermissionToFile( + const GURL& origin, const FilePath& permissions_path, bool allowed) { +#if 0 + if (!file_util::DirectoryExists(permissions_path)) + file_util::CreateDirectory(permissions_path); + // TODO(bulach): this is probably wrong! is there any utility to convert a URL + // to FilePath? + FilePath permission_file( + permissions_path.Append(StdStringToFilePathString( + GURLToCacheKey(origin)))); + DictionaryValue dictionary; + dictionary.SetBoolean(kAllowedDictionaryKey, allowed); + std::string permission_data; + JSONStringValueSerializer serializer(&permission_data); + serializer.Serialize(dictionary); + file_util::WriteFile( + permission_file, permission_data.c_str(), permission_data.length()); +#endif // if 0 +} + +} // namespace + +GeolocationPermissionContext::GeolocationPermissionContext( + Profile* profile) + : profile_(profile), + is_off_the_record_(profile->IsOffTheRecord()), + permissions_path_(profile->GetPath().Append(FilePath( + kGeolocationPermissionPath))) { +} + +GeolocationPermissionContext::~GeolocationPermissionContext() { +} + +void GeolocationPermissionContext::RequestGeolocationPermission( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin) { + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + std::map<std::string, bool>::const_iterator permission = + permissions_.find(GURLToCacheKey(origin)); + if (permission != permissions_.end()) { + NotifyPermissionSet( + render_process_id, render_view_id, bridge_id, permission->second); + } else { + HandlePermissionMemoryCacheMiss( + render_process_id, render_view_id, bridge_id, origin); + } +} + +void GeolocationPermissionContext::SetPermission( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin, bool allowed) { + SetPermissionMemoryCacheOnIOThread(origin, allowed); + SetPermissionOnFileThread(origin, allowed); + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, allowed); +} + +void GeolocationPermissionContext::HandlePermissionMemoryCacheMiss( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin) { + if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) { + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod( + this, + &GeolocationPermissionContext::HandlePermissionMemoryCacheMiss, + render_process_id, render_view_id, bridge_id, origin)); + return; + } + + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + // TODO(bulach): should we save a file per origin or have some smarter + // storage? Use HostContentSettingsMap instead. + bool allowed; + if (is_off_the_record_ || + !ReadPermissionFromFile(origin, permissions_path_, &allowed)) { + RequestPermissionFromUI( + render_process_id, render_view_id, bridge_id, origin); + } else { + SetPermissionMemoryCacheOnIOThread(origin, allowed); + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, allowed); + } +} + +void GeolocationPermissionContext::RequestPermissionFromUI( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin) { + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, &GeolocationPermissionContext::RequestPermissionFromUI, + render_process_id, render_view_id, bridge_id, origin)); + return; + } + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + + Browser* browser = BrowserList::GetLastActiveWithProfile(profile_); + for (int i = 0; i < browser->tab_count(); ++i) { + TabContents* tab_contents = browser->GetTabContentsAt(i); + RenderViewHost* render_view_host = tab_contents->render_view_host(); + if (render_view_host->process()->id() == render_process_id && + render_view_host->routing_id() == render_view_id && + tab_contents->GetURL().GetOrigin() == origin) { + tab_contents->AddInfoBar( + new GeolocationConfirmInfoBarDelegate( + tab_contents, this, render_process_id, render_view_id, + bridge_id, origin)); + break; + } + } +} + +void GeolocationPermissionContext::NotifyPermissionSet( + int render_process_id, int render_view_id, int bridge_id, bool allowed) { + if (!ChromeThread::CurrentlyOn(ChromeThread::UI)) { + ChromeThread::PostTask( + ChromeThread::UI, FROM_HERE, + NewRunnableMethod( + this, &GeolocationPermissionContext::NotifyPermissionSet, + render_process_id, render_view_id, bridge_id, allowed)); + return; + } + + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI)); + CallRenderViewHost( + render_process_id, render_view_id, + &RenderViewHost::Send, + new ViewMsg_Geolocation_PermissionSet( + render_view_id, bridge_id, allowed)); +} + +void GeolocationPermissionContext::SetPermissionMemoryCacheOnIOThread( + const GURL& origin, bool allowed) { + if (!ChromeThread::CurrentlyOn(ChromeThread::IO)) { + ChromeThread::PostTask( + ChromeThread::IO, FROM_HERE, + NewRunnableMethod( + this, + &GeolocationPermissionContext::SetPermissionMemoryCacheOnIOThread, + origin, allowed)); + return; + } + + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO)); + permissions_[GURLToCacheKey(origin)] = allowed; +} + +void GeolocationPermissionContext::SetPermissionOnFileThread( + const GURL& origin, bool allowed) { + if (is_off_the_record_) + return; + if (!ChromeThread::CurrentlyOn(ChromeThread::FILE)) { + ChromeThread::PostTask( + ChromeThread::FILE, FROM_HERE, + NewRunnableMethod( + this, &GeolocationPermissionContext::SetPermissionOnFileThread, + origin, allowed)); + return; + } + + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE)); + + // TODO(bulach): should we save a file per origin or have some smarter + // storage? Use HostContentSettingsMap instead. +#if 0 + SavePermissionToFile(origin, permissions_path_, allowed); +#endif +} diff --git a/chrome/browser/geolocation/geolocation_permission_context.h b/chrome/browser/geolocation/geolocation_permission_context.h new file mode 100644 index 0000000..c859648 --- /dev/null +++ b/chrome/browser/geolocation/geolocation_permission_context.h @@ -0,0 +1,88 @@ +// 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 CHROME_BROWSER_GEOLOCATION_GEOLOCATION_PERMISSION_CONTEXT_H_ +#define CHROME_BROWSER_GEOLOCATION_GEOLOCATION_PERMISSION_CONTEXT_H_ + +#include <map> +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/ref_counted.h" + +class GeolocationDispatcherHost; +class GURL; +class Profile; +class RenderViewHost; + +// GeolocationPermissionContext manages Geolocation permissions per origin. +// It keeps an in-memory cache of permissions, and if not available, loads +// from disk. If there's no data, it'll trigger the UI elements to ask the +// user for permission. +// Regardless of where the permission data came from, it always notifies the +// requesting render_view asynchronously via ViewMsg_Geolocation_PermissionSet. +class GeolocationPermissionContext + : public base::RefCountedThreadSafe<GeolocationPermissionContext> { + public: + explicit GeolocationPermissionContext(Profile* profile); + + // The render is requesting permission to use Geolocation. + // Response will be sent asynchronously as ViewMsg_Geolocation_PermissionSet. + // Must be called from the IO thread. + void RequestGeolocationPermission( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin); + + // Called once the user sets the geolocation permission. + // It'll update the internal state on different threads via + // SetPermissionMemoryCacheOnIOThread and SetPermissionOnFileThread. + void SetPermission( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin, bool allowed); + + private: + friend class base::RefCountedThreadSafe<GeolocationPermissionContext>; + ~GeolocationPermissionContext(); + + // This is initially called on the IO thread by the public API + // RequestGeolocationPermission when there's no data available in the + // in-memory cache. + // It forwards a call to the FILE thread which tries to load permission data + // from disk: + // - If available, it will call SetPermissionMemoryCacheOnIOThread() to write + // the in-memory cache in the IO thread, and NotifyPermissionSet to send the + // message to the corresponding render. + // - If not available, it'll delegate to RequestPermissionDataFromUI. + void HandlePermissionMemoryCacheMiss( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin); + + // Triggers the associated UI element to request permission. + void RequestPermissionFromUI( + int render_process_id, int render_view_id, int bridge_id, + const GURL& origin); + + // Notifies whether or not the corresponding render is allowed to use + // geolocation. + void NotifyPermissionSet( + int render_process_id, int render_view_id, int bridge_id, + bool allowed); + + // Sets permissions_ cache (if not on IO thread, will forward to it). + void SetPermissionMemoryCacheOnIOThread(const GURL& origin, bool allowed); + // Sets permissions file data (if not on FILE thread, will forward to it). + void SetPermissionOnFileThread(const GURL& origin, bool allowed); + + // This should only be accessed from the UI thread. + Profile* const profile_; + // Indicates whether profile_ is off the record. + bool const is_off_the_record_; + // The path where geolocation permission data is stored. + FilePath const permissions_path_; + // This should only be accessed from the UI thread. + std::map<std::string, bool> permissions_; + + DISALLOW_COPY_AND_ASSIGN(GeolocationPermissionContext); +}; + +#endif // CHROME_BROWSER_GEOLOCATION_GEOLOCATION_PERMISSION_CONTEXT_H_ diff --git a/chrome/browser/geolocation/location_arbitrator.cc b/chrome/browser/geolocation/location_arbitrator.cc index 0b5fc0af..c82fb1d 100644 --- a/chrome/browser/geolocation/location_arbitrator.cc +++ b/chrome/browser/geolocation/location_arbitrator.cc @@ -13,8 +13,8 @@ #include "base/string_util.h" #include "chrome/browser/net/url_request_context_getter.h" #include "chrome/browser/geolocation/access_token_store.h" -#include "chrome/browser/geolocation/geoposition.h" #include "chrome/browser/geolocation/location_provider.h" +#include "chrome/common/geoposition.h" #include "googleurl/src/gurl.h" namespace { @@ -109,7 +109,7 @@ void GeolocationArbitratorImpl::LocationUpdateAvailable( LocationProviderBase* provider) { DCHECK(CalledOnValidThread()); DCHECK(provider); - Position position; + Geoposition position; provider->GetPosition(&position); // TODO(joth): Arbitrate. for (DelegateMap::const_iterator it = observers_.begin(); diff --git a/chrome/browser/geolocation/location_arbitrator.h b/chrome/browser/geolocation/location_arbitrator.h index aaf61a0..b0c10bc9 100644 --- a/chrome/browser/geolocation/location_arbitrator.h +++ b/chrome/browser/geolocation/location_arbitrator.h @@ -7,7 +7,7 @@ class AccessTokenStoreFactory; class URLRequestContextGetter; -struct Position; +struct Geoposition; // This is the main API to the geolocaiton subsystem. Typically the application // will hold a single instance of this class, and can register multiple @@ -29,7 +29,7 @@ class GeolocationArbitrator { // This will be called whenever the 'best available' location is updated, // or when an error is encountered meaning no location data will be // available in the forseeable future. - virtual void OnLocationUpdate(const Position& position) = 0; + virtual void OnLocationUpdate(const Geoposition& position) = 0; protected: virtual ~Delegate() {} diff --git a/chrome/browser/geolocation/location_arbitrator_unittest.cc b/chrome/browser/geolocation/location_arbitrator_unittest.cc index 6e0c1d8..8d162f3 100644 --- a/chrome/browser/geolocation/location_arbitrator_unittest.cc +++ b/chrome/browser/geolocation/location_arbitrator_unittest.cc @@ -6,8 +6,8 @@ #include "base/scoped_ptr.h" #include "chrome/browser/geolocation/fake_access_token_store.h" -#include "chrome/browser/geolocation/geoposition.h" #include "chrome/browser/geolocation/location_provider.h" +#include "chrome/common/geoposition.h" #include "testing/gtest/include/gtest/gtest.h" namespace { @@ -31,11 +31,11 @@ class MockLocationProvider : public LocationProviderBase { ++started_count_; return true; } - virtual void GetPosition(Position *position) { + virtual void GetPosition(Geoposition *position) { *position = position_; } - Position position_; + Geoposition position_; int started_count_; static MockLocationProvider* instance_; @@ -49,18 +49,18 @@ class MockLocationObserver : public GeolocationArbitrator::Delegate { public: void InvalidateLastPosition() { last_position_.accuracy = -1; - last_position_.error_code = Position::ERROR_CODE_NONE; + last_position_.error_code = Geoposition::ERROR_CODE_NONE; ASSERT_FALSE(last_position_.IsInitialized()); } // Delegate - virtual void OnLocationUpdate(const Position& position) { + virtual void OnLocationUpdate(const Geoposition& position) { last_position_ = position; } - Position last_position_; + Geoposition last_position_; }; -void SetReferencePosition(Position* position) { +void SetReferencePosition(Geoposition* position) { position->latitude = 51.0; position->longitude = -0.1; position->accuracy = 400; diff --git a/chrome/browser/geolocation/location_provider.h b/chrome/browser/geolocation/location_provider.h index 8ff4acd..fbb49ad 100644 --- a/chrome/browser/geolocation/location_provider.h +++ b/chrome/browser/geolocation/location_provider.h @@ -20,6 +20,7 @@ class AccessTokenStore; class GURL; class URLRequestContextGetter; +struct Geoposition; struct Position; // The base class used by all location providers. @@ -64,7 +65,7 @@ class LocationProviderBase : public NonThreadSafe { // Returns false if the provider failed to start. virtual bool StartProvider() = 0; // Gets the current best position estimate. - virtual void GetPosition(Position* position) = 0; + virtual void GetPosition(Geoposition* position) = 0; // Provides a hint to the provider that new location data is needed as soon // as possible. Default implementation does nothing. virtual void UpdatePosition() {} diff --git a/chrome/browser/geolocation/network_location_provider.cc b/chrome/browser/geolocation/network_location_provider.cc index 8d0c69e..1b05018 100644 --- a/chrome/browser/geolocation/network_location_provider.cc +++ b/chrome/browser/geolocation/network_location_provider.cc @@ -32,7 +32,7 @@ class NetworkLocationProvider::PositionCache { // WiFi data. Returns true on success, false otherwise. bool CachePosition(const RadioData& radio_data, const WifiData& wifi_data, - const Position& position) { + const Geoposition& position) { // Check that we can generate a valid key for the device data. string16 key; if (!MakeKey(radio_data, wifi_data, &key)) { @@ -57,8 +57,8 @@ class NetworkLocationProvider::PositionCache { // Searches for a cached position response for the current set of cell ID and // WiFi data. Returns the cached position if available, NULL otherwise. - const Position *FindPosition(const RadioData &radio_data, - const WifiData &wifi_data) { + const Geoposition *FindPosition(const RadioData &radio_data, + const WifiData &wifi_data) { string16 key; if (!MakeKey(radio_data, wifi_data, &key)) { return NULL; @@ -96,7 +96,7 @@ class NetworkLocationProvider::PositionCache { // The cache of positions. This is stored using two maps. One map is keyed on // a string that represents a set of device data, the other is keyed on the // timestamp of the position. - typedef std::map<string16, Position> CacheMap; + typedef std::map<string16, Geoposition> CacheMap; CacheMap cache_; typedef std::map<int64, CacheMap::iterator> CacheTimesMap; CacheTimesMap cache_times_; @@ -141,7 +141,7 @@ NetworkLocationProvider::~NetworkLocationProvider() { } // LocationProviderBase implementation -void NetworkLocationProvider::GetPosition(Position *position) { +void NetworkLocationProvider::GetPosition(Geoposition *position) { DCHECK(position); AutoLock lock(position_mutex_); *position = position_; @@ -176,7 +176,7 @@ void NetworkLocationProvider::DeviceDataUpdateAvailable( // NetworkLocationRequest::ListenerInterface implementation. void NetworkLocationProvider::LocationResponseAvailable( - const Position& position, + const Geoposition& position, bool server_error, const string16& access_token) { DCHECK(CalledOnValidThread()); @@ -238,7 +238,7 @@ void NetworkLocationProvider::RequestPosition() { DCHECK(CalledOnValidThread()); delayed_start_task_.RevokeAll(); - const Position* cached_position; + const Geoposition* cached_position; { AutoLock lock(data_mutex_); cached_position = position_cache_->FindPosition(radio_data_, wifi_data_); diff --git a/chrome/browser/geolocation/network_location_provider.h b/chrome/browser/geolocation/network_location_provider.h index 9dfe59ac..2988841 100644 --- a/chrome/browser/geolocation/network_location_provider.h +++ b/chrome/browser/geolocation/network_location_provider.h @@ -12,9 +12,9 @@ #include "base/string16.h" #include "base/thread.h" #include "chrome/browser/geolocation/device_data_provider.h" -#include "chrome/browser/geolocation/geoposition.h" #include "chrome/browser/geolocation/location_provider.h" #include "chrome/browser/geolocation/network_location_request.h" +#include "chrome/common/geoposition.h" class URLFetcherProtectEntry; @@ -32,7 +32,7 @@ class NetworkLocationProvider // LocationProviderBase implementation virtual bool StartProvider(); - virtual void GetPosition(Position *position); + virtual void GetPosition(Geoposition *position); virtual void UpdatePosition(); private: @@ -50,7 +50,7 @@ class NetworkLocationProvider virtual void DeviceDataUpdateAvailable(WifiDataProvider* provider); // NetworkLocationRequest::ListenerInterface implementation. - virtual void LocationResponseAvailable(const Position& position, + virtual void LocationResponseAvailable(const Geoposition& position, bool server_error, const string16& access_token); @@ -74,7 +74,7 @@ class NetworkLocationProvider string16 access_token_; // The current best position estimate and its guarding mutex - Position position_; + Geoposition position_; Lock position_mutex_; bool is_new_data_available_; diff --git a/chrome/browser/geolocation/network_location_provider_unittest.cc b/chrome/browser/geolocation/network_location_provider_unittest.cc index f8af128..34e6b2d 100644 --- a/chrome/browser/geolocation/network_location_provider_unittest.cc +++ b/chrome/browser/geolocation/network_location_provider_unittest.cc @@ -298,7 +298,7 @@ TEST_F(GeolocationNetworkProviderTest, MultipleWifiScansComplete) { EXPECT_TRUE(access_token_store_->GetAccessToken(&token)); EXPECT_EQ(REFERENCE_ACCESS_TOKEN, UTF16ToUTF8(token)); - Position position; + Geoposition position; provider->GetPosition(&position); EXPECT_FALSE(position.IsValidFix()); diff --git a/chrome/browser/geolocation/network_location_request.cc b/chrome/browser/geolocation/network_location_request.cc index 65e3f37..ecf41d9 100644 --- a/chrome/browser/geolocation/network_location_request.cc +++ b/chrome/browser/geolocation/network_location_request.cc @@ -8,8 +8,8 @@ #include "base/json/json_writer.h" #include "base/string_util.h" #include "base/values.h" -#include "chrome/browser/geolocation/geoposition.h" #include "chrome/browser/net/url_request_context_getter.h" +#include "chrome/common/geoposition.h" #include "net/url_request/url_request_status.h" namespace { @@ -39,7 +39,7 @@ void GetLocationFromResponse(bool http_post_result, const std::string& response_body, int64 timestamp, const GURL& server_url, - Position* position, + Geoposition* position, string16* access_token); const char* RadioTypeToString(RadioType type); @@ -54,7 +54,7 @@ void AddInteger(const std::wstring& property_name, // Parses the server response body. Returns true if parsing was successful. bool ParseServerResponse(const std::string& response_body, int64 timestamp, - Position* position, + Geoposition* position, string16* access_token); void AddRadioData(const RadioData& radio_data, DictionaryValue* body_object); void AddWifiData(const WifiData& wifi_data, DictionaryValue* body_object); @@ -105,7 +105,7 @@ void NetworkLocationRequest::OnURLFetchComplete(const URLFetcher* source, DCHECK_EQ(url_fetcher_.get(), source); DCHECK(url_.possibly_invalid_spec() == url.possibly_invalid_spec()); - Position position; + Geoposition position; string16 access_token; GetLocationFromResponse(status.is_success(), response_code, data, timestamp_, url, &position, &access_token); @@ -162,8 +162,8 @@ bool FormRequestBody(const string16& host_name, void FormatPositionError(const GURL& server_url, const std::wstring& message, - Position* position) { - position->error_code = Position::ERROR_CODE_POSITION_UNAVAILABLE; + Geoposition* position) { + position->error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; position->error_message = L"Network location provider at '"; position->error_message += ASCIIToWide(server_url.possibly_invalid_spec()); position->error_message += L"' : "; @@ -178,7 +178,7 @@ void GetLocationFromResponse(bool http_post_result, const std::string& response_body, int64 timestamp, const GURL& server_url, - Position* position, + Geoposition* position, string16* access_token) { DCHECK(position); DCHECK(access_token); @@ -268,7 +268,7 @@ bool GetAsDouble(const DictionaryValue& object, bool ParseServerResponse(const std::string& response_body, int64 timestamp, - Position* position, + Geoposition* position, string16* access_token) { DCHECK(position); DCHECK(access_token); diff --git a/chrome/browser/geolocation/network_location_request.h b/chrome/browser/geolocation/network_location_request.h index da89cb1..97cf16c 100644 --- a/chrome/browser/geolocation/network_location_request.h +++ b/chrome/browser/geolocation/network_location_request.h @@ -14,6 +14,7 @@ class URLRequestContextGetter; class URLFetcher; +struct Geoposition; struct Position; // Takes a set of device data and sends it to a server to get a position fix. @@ -26,7 +27,7 @@ class NetworkLocationRequest : private URLFetcher::Delegate { // Updates the listener with a new position. server_error indicates whether // was a server or network error - either no response or a 500 error code. virtual void LocationResponseAvailable( - const Position& position, + const Geoposition& position, bool server_error, const string16& access_token) = 0; diff --git a/chrome/browser/renderer_host/render_view_host.h b/chrome/browser/renderer_host/render_view_host.h index 52453a2..7747066 100644 --- a/chrome/browser/renderer_host/render_view_host.h +++ b/chrome/browser/renderer_host/render_view_host.h @@ -611,7 +611,9 @@ class RenderViewHost : public RenderWidgetHost { void OnPageTranslated(int32 page_id, const std::string& original_lang, const std::string& translated_lang); + void OnContentBlocked(ContentSettingsType type); + private: friend class TestRenderViewHost; diff --git a/chrome/browser/renderer_host/resource_message_filter.cc b/chrome/browser/renderer_host/resource_message_filter.cc index e1405d3..9e9833c 100644 --- a/chrome/browser/renderer_host/resource_message_filter.cc +++ b/chrome/browser/renderer_host/resource_message_filter.cc @@ -19,6 +19,8 @@ #include "chrome/browser/download/download_file.h" #include "chrome/browser/extensions/extension_file_util.h" #include "chrome/browser/extensions/extension_message_service.h" +#include "chrome/browser/geolocation/geolocation_permission_context.h" +#include "chrome/browser/geolocation/geolocation_dispatcher_host.h" #include "chrome/browser/host_zoom_map.h" #include "chrome/browser/in_process_webkit/dom_storage_dispatcher_host.h" #include "chrome/browser/nacl_host/nacl_process_host.h" @@ -298,7 +300,10 @@ ResourceMessageFilter::ResourceMessageFilter( off_the_record_(profile->IsOffTheRecord()), next_route_id_callback_(NewCallbackWithReturnValue( render_widget_helper, &RenderWidgetHelper::GetNextRoutingID)), - ALLOW_THIS_IN_INITIALIZER_LIST(translation_service_(this)) { + ALLOW_THIS_IN_INITIALIZER_LIST(translation_service_(this)), + ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_dispatcher_host_( + new GeolocationDispatcherHost( + this->id(), new GeolocationPermissionContext(profile)))) { DCHECK(request_context_); DCHECK(media_request_context_); DCHECK(audio_renderer_host_.get()); @@ -388,7 +393,8 @@ bool ResourceMessageFilter::OnMessageReceived(const IPC::Message& msg) { audio_renderer_host_->OnMessageReceived(msg, &msg_is_ok) || db_dispatcher_host_->OnMessageReceived(msg, &msg_is_ok) || mp_dispatcher->OnMessageReceived( - msg, this, next_route_id_callback(), &msg_is_ok); + msg, this, next_route_id_callback(), &msg_is_ok) || + geolocation_dispatcher_host_->OnMessageReceived(msg, &msg_is_ok); if (!handled) { DCHECK(msg_is_ok); // It should have been marked handled if it wasn't OK. diff --git a/chrome/browser/renderer_host/resource_message_filter.h b/chrome/browser/renderer_host/resource_message_filter.h index f5068d9..347c3e5 100644 --- a/chrome/browser/renderer_host/resource_message_filter.h +++ b/chrome/browser/renderer_host/resource_message_filter.h @@ -39,6 +39,7 @@ class ChromeURLRequestContext; class DatabaseDispatcherHost; class DOMStorageDispatcherHost; class ExtensionMessageService; +class GeolocationDispatcherHost; class HostZoomMap; class NotificationsPrefsCache; class Profile; @@ -404,6 +405,9 @@ class ResourceMessageFilter : public IPC::ChannelProxy::MessageFilter, // Used to translate page contents from one language to another. TranslationService translation_service_; + // Used to handle geolocation-related messages. + scoped_refptr<GeolocationDispatcherHost> geolocation_dispatcher_host_; + DISALLOW_COPY_AND_ASSIGN(ResourceMessageFilter); }; diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index f4e6114..aafafd3 100755 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -932,10 +932,12 @@ 'browser/geolocation/device_data_provider.h', 'browser/geolocation/empty_device_data_provider.cc', 'browser/geolocation/empty_device_data_provider.h', + 'browser/geolocation/geolocation_dispatcher_host.cc', + 'browser/geolocation/geolocation_dispatcher_host.h', + 'browser/geolocation/geolocation_permission_context.cc', + 'browser/geolocation/geolocation_permission_context.h', 'browser/geolocation/geolocation_prefs.cc', - 'browser/geolocation/geolocation_prefs.h', - 'browser/geolocation/geoposition.cc', - 'browser/geolocation/geoposition.h', + 'browser/geolocation/geolocation_prefs.h', 'browser/geolocation/location_arbitrator.cc', 'browser/geolocation/location_arbitrator.h', 'browser/geolocation/location_provider.cc', diff --git a/chrome/chrome_common.gypi b/chrome/chrome_common.gypi index b61b098..8cfbcfc 100644 --- a/chrome/chrome_common.gypi +++ b/chrome/chrome_common.gypi @@ -44,6 +44,8 @@ 'common/debug_flags.h', 'common/devtools_messages.h', 'common/devtools_messages_internal.h', + 'common/geoposition.cc', + 'common/geoposition.h', 'common/gpu_messages.h', 'common/gpu_messages_internal.h', 'common/io_surface_support_mac.cc', diff --git a/chrome/chrome_renderer.gypi b/chrome/chrome_renderer.gypi index 77252ba..125c0fa 100755 --- a/chrome/chrome_renderer.gypi +++ b/chrome/chrome_renderer.gypi @@ -83,6 +83,8 @@ 'renderer/external_extension.h', 'renderer/form_manager.cc', 'renderer/form_manager.h', + 'renderer/geolocation_dispatcher.cc', + 'renderer/geolocation_dispatcher.h', 'renderer/localized_error.cc', 'renderer/localized_error.h', 'renderer/navigation_state.h', diff --git a/chrome/common/common_param_traits.cc b/chrome/common/common_param_traits.cc index e213f6d..62e57ff 100644 --- a/chrome/common/common_param_traits.cc +++ b/chrome/common/common_param_traits.cc @@ -245,5 +245,66 @@ void ParamTraits<webkit_glue::WebApplicationInfo>::Log( l->append(L"<WebApplicationInfo>"); } -} // namespace IPC +void ParamTraits<Geoposition::ErrorCode>::Write( + Message* m, const Geoposition::ErrorCode& p) { + int error_code = p; + WriteParam(m, error_code); +} + +bool ParamTraits<Geoposition::ErrorCode>::Read( + const Message* m, void** iter, Geoposition::ErrorCode* p) { + int error_code_param = 0; + bool ret = ReadParam(m, iter, &error_code_param); + *p = static_cast<Geoposition::ErrorCode>(error_code_param); + return ret; +} +void ParamTraits<Geoposition::ErrorCode>::Log( + const Geoposition::ErrorCode& p, std::wstring* l) { + int error_code = p; + l->append(StringPrintf(L"<Geoposition::ErrorCode>%d", error_code)); +} + +void ParamTraits<Geoposition>::Write(Message* m, const Geoposition& p) { + WriteParam(m, p.latitude); + WriteParam(m, p.longitude); + WriteParam(m, p.accuracy); + WriteParam(m, p.altitude); + WriteParam(m, p.altitude_accuracy); + WriteParam(m, p.speed); + WriteParam(m, p.heading); + WriteParam(m, p.timestamp); + WriteParam(m, p.error_code); + WriteParam(m, p.error_message); +} + +bool ParamTraits<Geoposition>::Read( + const Message* m, void** iter, Geoposition* p) { + bool ret = ReadParam(m, iter, &p->latitude); + ret = ret && ReadParam(m, iter, &p->longitude); + ret = ret && ReadParam(m, iter, &p->accuracy); + ret = ret && ReadParam(m, iter, &p->altitude); + ret = ret && ReadParam(m, iter, &p->altitude_accuracy); + ret = ret && ReadParam(m, iter, &p->speed); + ret = ret && ReadParam(m, iter, &p->heading); + ret = ret && ReadParam(m, iter, &p->timestamp); + ret = ret && ReadParam(m, iter, &p->error_code); + ret = ret && ReadParam(m, iter, &p->error_message); + return ret; +} + +void ParamTraits<Geoposition>::Log(const Geoposition& p, std::wstring* l) { + l->append( + StringPrintf( + L"<Geoposition>" + L"%.6f %.6f %.6f %.6f " + L"%.6f %.6f %.6f " + L"%lld ", + p.latitude, p.longitude, p.accuracy, p.altitude, + p.altitude_accuracy, p.speed, p.heading, + p.timestamp)); + l->append(p.error_message); + LogParam<Geoposition::ErrorCode>(p.error_code, l); +} + +} // namespace IPC diff --git a/chrome/common/common_param_traits.h b/chrome/common/common_param_traits.h index 83148a6..99b1896 100644 --- a/chrome/common/common_param_traits.h +++ b/chrome/common/common_param_traits.h @@ -15,6 +15,7 @@ #include "app/gfx/native_widget_types.h" #include "chrome/common/content_settings.h" +#include "chrome/common/geoposition.h" #include "chrome/common/page_zoom.h" #include "chrome/common/thumbnail_score.h" #include "chrome/common/transport_dib.h" @@ -392,6 +393,22 @@ struct ParamTraits<ThumbnailScore> { } }; +template <> +struct ParamTraits<Geoposition> { + typedef Geoposition param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::wstring* l); +}; + +template <> +struct ParamTraits<Geoposition::ErrorCode> { + typedef Geoposition::ErrorCode param_type; + static void Write(Message* m, const param_type& p); + static bool Read(const Message* m, void** iter, param_type* p); + static void Log(const param_type& p, std::wstring* l); +}; + } // namespace IPC #endif // CHROME_COMMON_COMMON_PARAM_TRAITS_H_ diff --git a/chrome/common/common_param_traits_unittest.cc b/chrome/common/common_param_traits_unittest.cc index f5db121..d888084 100644 --- a/chrome/common/common_param_traits_unittest.cc +++ b/chrome/common/common_param_traits_unittest.cc @@ -166,3 +166,42 @@ TEST(IPCMessageTest, DictionaryValue) { iter = NULL; EXPECT_FALSE(IPC::ReadParam(&bad_msg, &iter, &output)); } + +TEST(IPCMessageTest, Geoposition) { + Geoposition input; + input.latitude = 0.1; + input.longitude = 51.3; + input.accuracy = 13.7; + input.altitude = 42.24; + input.altitude_accuracy = 9.3; + input.speed = 55; + input.heading = 120; + input.timestamp = 1977; + input.error_code = Geoposition::ERROR_CODE_POSITION_UNAVAILABLE; + input.error_message = L"unittest error message for geoposition"; + + IPC::Message msg(1, 2, IPC::Message::PRIORITY_NORMAL); + IPC::WriteParam(&msg, input); + + Geoposition output; + void* iter = NULL; + EXPECT_TRUE(IPC::ReadParam(&msg, &iter, &output)); + EXPECT_EQ(input.altitude, output.altitude); + EXPECT_EQ(input.altitude_accuracy, output.altitude_accuracy); + EXPECT_EQ(input.latitude, output.latitude); + EXPECT_EQ(input.longitude, output.longitude); + EXPECT_EQ(input.accuracy, output.accuracy); + EXPECT_EQ(input.heading, output.heading); + EXPECT_EQ(input.speed, output.speed); + EXPECT_EQ(input.error_code, output.error_code); + EXPECT_EQ(input.error_message, output.error_message); + + std::wstring log_message; + IPC::LogParam(output, &log_message); + EXPECT_STREQ(L"<Geoposition>" + L"0.100000 51.300000 13.700000 42.240000 " + L"9.300000 55.000000 120.000000 " + L"1977 unittest error message for geoposition" + L"<Geoposition::ErrorCode>2", + log_message.c_str()); +} diff --git a/chrome/common/geoposition.cc b/chrome/common/geoposition.cc new file mode 100644 index 0000000..d4989ed --- /dev/null +++ b/chrome/common/geoposition.cc @@ -0,0 +1,67 @@ +// 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 "chrome/common/geoposition.h" + +namespace { +// Sentinel values to mark invalid data. (WebKit carries companion is_valid +// bools for this purpose; we may eventually follow that approach, but +// sentinels worked OK in the gears code this is based on.) +const double kBadLatitudeLongitude = 200; +// Lowest point on land is at approximately -400 meters. +const int kBadAltitude = -10000; +const int kBadAccuracy = -1; // Accuracy must be non-negative. +const int64 kBadTimestamp = kint64min; +const int kBadHeading = -1; // Heading must be non-negative. +const int kBadSpeed = -1; +} + +Geoposition::Geoposition() + : latitude(kBadLatitudeLongitude), + longitude(kBadLatitudeLongitude), + altitude(kBadAltitude), + accuracy(kBadAccuracy), + altitude_accuracy(kBadAccuracy), + heading(kBadHeading), + speed(kBadSpeed), + timestamp(kBadTimestamp), + error_code(ERROR_CODE_NONE) { +} + +bool Geoposition::is_valid_latlong() const { + return latitude >= -90.0 && latitude <= 90.0 && + longitude >= -180.0 && longitude <= 180.0; +} + +bool Geoposition::is_valid_altitude() const { + return altitude > kBadAltitude; +} + +bool Geoposition::is_valid_accuracy() const { + return accuracy >= 0.0; +} + +bool Geoposition::is_valid_altitude_accuracy() const { + return altitude_accuracy >= 0.0; +} + +bool Geoposition::is_valid_heading() const { + return heading >= 0 && heading <= 360; +} + +bool Geoposition::is_valid_speed() const { + return speed >= 0; +} + +bool Geoposition::is_valid_timestamp() const { + return timestamp != kBadTimestamp; +} + +bool Geoposition::IsValidFix() const { + return is_valid_latlong() && is_valid_accuracy() && is_valid_timestamp(); +} + +bool Geoposition::IsInitialized() const { + return error_code != ERROR_CODE_NONE || IsValidFix(); +} diff --git a/chrome/common/geoposition.h b/chrome/common/geoposition.h new file mode 100644 index 0000000..94c1dc9 --- /dev/null +++ b/chrome/common/geoposition.h @@ -0,0 +1,61 @@ +// 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 declares the Position structure, which is used to represent a +// position fix. Originally derived from +// http://gears.googlecode.com/svn/trunk/gears/geolocation/geolocation.h + +#ifndef CHROME_COMMON_GEOPOSITION_H_ +#define CHROME_COMMON_GEOPOSITION_H_ + +#include "base/string16.h" + +// The internal representation of a geo position. Some properties use different +// types when passed to JavaScript. +struct Geoposition { + public: + // Error codes for returning to JavaScript. These values are defined by the + // W3C spec. Note that Gears does not use all of these codes, but we need + // values for all of them to allow us to provide the constants on the error + // object. + enum ErrorCode { + ERROR_CODE_NONE = 0, // Chrome addition + ERROR_CODE_PERMISSION_DENIED = 1, + ERROR_CODE_POSITION_UNAVAILABLE = 2, + ERROR_CODE_TIMEOUT = 3, + }; + + Geoposition(); + + bool is_valid_latlong() const; + bool is_valid_altitude() const; + bool is_valid_accuracy() const; + bool is_valid_altitude_accuracy() const; + bool is_valid_heading() const; + bool is_valid_speed() const; + bool is_valid_timestamp() const; + + // A valid fix has a valid latitude, longitude, accuracy and timestamp. + bool IsValidFix() const; + + // A position is considered initialized if it has either a valid fix or + // an error code other than NONE. + bool IsInitialized() const; + + // These properties correspond to the JavaScript Position object. + double latitude; // In degrees + double longitude; // In degrees + double altitude; // In metres + double accuracy; // In metres + double altitude_accuracy; // In metres + double heading; // In degrees clockwise relative to the true north + double speed; // In meters per second + int64 timestamp; // Milliseconds since 1st Jan 1970 + + // These properties are returned to JavaScript as a PositionError object. + ErrorCode error_code; + std::wstring error_message; // Human-readable error message +}; + +#endif // CHROME_COMMON_GEOPOSITION_H_ diff --git a/chrome/common/render_messages_internal.h b/chrome/common/render_messages_internal.h index 208a280..a07dd2e 100644 --- a/chrome/common/render_messages_internal.h +++ b/chrome/common/render_messages_internal.h @@ -19,6 +19,7 @@ #include "base/values.h" #include "chrome/common/content_settings.h" #include "chrome/common/extensions/update_manifest.h" +#include "chrome/common/geoposition.h" #include "chrome/common/nacl_types.h" #include "chrome/common/notification_type.h" #include "chrome/common/page_zoom.h" @@ -899,6 +900,22 @@ IPC_BEGIN_MESSAGES(View) int /* error id of translation work */, std::vector<string16> /* the translated text chunks */) + // Reply in response to ViewHostMsg_Geolocation_RequestPermission. + IPC_MESSAGE_ROUTED2(ViewMsg_Geolocation_PermissionSet, + int /* bridge_id */, + bool /* is_allowed */) + + // Sent after ViewHostMsg_Geolocation_StartUpdating iff the user has granted + // permission and we have a position available. + IPC_MESSAGE_ROUTED1(ViewMsg_Geolocation_PositionUpdated, + Geoposition /* geoposition */) + + // Sent after ViewHostMsg_Geolocation_StartUpdating in case of error (such as + // permission denied, position unavailable, etc.). + IPC_MESSAGE_ROUTED2(ViewMsg_Geolocation_Error, + int /* code */, + std::string /* message */) + IPC_END_MESSAGES(View) @@ -2130,4 +2147,64 @@ IPC_BEGIN_MESSAGES(ViewHost) IPC_MESSAGE_CONTROL1(ViewHostMsg_TranslateText, ViewHostMsg_TranslateTextParam) + //--------------------------------------------------------------------------- + // Geolocation services messages + + // A GeolocationServiceBridgeImpl in the renderer process has been created. + // This is used to lazily initialize the host dispatchers and related + // Geolocation infrastructure in the browser process. + IPC_MESSAGE_CONTROL1(ViewHostMsg_Geolocation_RegisterDispatcher, + int /* route_id */) + + // A GeolocationServiceBridgeImpl has been destroyed. + // This is used to let the Geolocation infrastructure do its cleanup. + IPC_MESSAGE_CONTROL1(ViewHostMsg_Geolocation_UnregisterDispatcher, + int /* route_id */) + + // The |route_id| and |bridge_id| representing |URL| is requesting permission + // to access geolocation position. + // This will be replied by ViewMsg_Geolocation_PermissionSet. + IPC_MESSAGE_CONTROL3(ViewHostMsg_Geolocation_RequestPermission, + int /* route_id */, + int /* bridge_id */, + GURL /* URL of the page*/) + + // The |route_id| and |bridge_id| requests Geolocation service to start + // updating. + // This is an asynchronous call, and the browser process may eventually reply + // with the updated geoposition, or an error (access denied, location + // unavailable, etc.) + IPC_MESSAGE_CONTROL3(ViewHostMsg_Geolocation_StartUpdating, + int /* route_id */, + int /* bridge_id */, + bool /* high_accuracy */) + + // The |route_id| and |bridge_id| requests Geolocation service to stop + // updating. + // Note that the geolocation service may continue to fetch geolocation data + // for other origins. + IPC_MESSAGE_CONTROL2(ViewHostMsg_Geolocation_StopUpdating, + int /* route_id */, + int /* bridge_id */) + + // The |route_id| and |bridge_id| requests Geolocation service to suspend. + // Note that the geolocation service may continue to fetch geolocation data + // for other origins. + IPC_MESSAGE_CONTROL2(ViewHostMsg_Geolocation_Suspend, + int /* route_id */, + int /* bridge_id */) + + // The |route_id| and |bridge_id| requests Geolocation service to resume. + IPC_MESSAGE_CONTROL2(ViewHostMsg_Geolocation_Resume, + int /* route_id */, + int /* bridge_id */) + + // Sent to indicate whether this particular GeolocationServiceBridgeImpl + // (identified by |route_id| and |bridge_id|) is allowed to use geolocation + // services. + IPC_MESSAGE_CONTROL3(ViewHostMsg_Geolocation_PermissionSet, + int /* route_id */, + int /* bridge_id */, + bool /* is_allowed */) + IPC_END_MESSAGES(ViewHost) diff --git a/chrome/renderer/geolocation_dispatcher.cc b/chrome/renderer/geolocation_dispatcher.cc new file mode 100644 index 0000000..fedad06 --- /dev/null +++ b/chrome/renderer/geolocation_dispatcher.cc @@ -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. + +#include "chrome/renderer/geolocation_dispatcher.h" + +#include "base/command_line.h" +#include "chrome/common/chrome_switches.h" +#include "chrome/renderer/render_view.h" +#include "third_party/WebKit/WebKit/chromium/public/WebFrame.h" +#include "third_party/WebKit/WebKit/chromium/public/WebCString.h" + +using WebKit::WebFrame; + +GeolocationDispatcher::GeolocationDispatcher(RenderView* render_view) + : render_view_(render_view) { + render_view_->Send(new ViewHostMsg_Geolocation_RegisterDispatcher( + render_view_->routing_id())); +} + +GeolocationDispatcher::~GeolocationDispatcher() { + render_view_->Send(new ViewHostMsg_Geolocation_UnregisterDispatcher( + render_view_->routing_id())); +} + +bool GeolocationDispatcher::OnMessageReceived(const IPC::Message& message) { + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(GeolocationDispatcher, message) + IPC_MESSAGE_HANDLER(ViewMsg_Geolocation_PermissionSet, + OnGeolocationPermissionSet) + IPC_MESSAGE_HANDLER(ViewMsg_Geolocation_PositionUpdated, + OnGeolocationPositionUpdated) + IPC_MESSAGE_HANDLER(ViewMsg_Geolocation_Error, OnGeolocationError) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +} + +void GeolocationDispatcher::requestPermissionForFrame( + int bridge_id, const WebKit::WebURL& url) { + render_view_->Send(new ViewHostMsg_Geolocation_RequestPermission( + bridge_id, render_view_->routing_id(), GURL(url).GetOrigin())); +} + +void GeolocationDispatcher::startUpdating(int bridge_id, bool hasHighAccuracy) { + render_view_->Send(new ViewHostMsg_Geolocation_StartUpdating( + bridge_id, render_view_->routing_id(), hasHighAccuracy)); +} + +void GeolocationDispatcher::stopUpdating(int bridge_id) { + render_view_->Send(new ViewHostMsg_Geolocation_StopUpdating( + bridge_id, render_view_->routing_id())); +} + +void GeolocationDispatcher::suspend(int bridge_id) { + render_view_->Send(new ViewHostMsg_Geolocation_Suspend( + bridge_id, render_view_->routing_id())); +} + +void GeolocationDispatcher::resume(int bridge_id) { + render_view_->Send(new ViewHostMsg_Geolocation_Resume( + bridge_id, render_view_->routing_id())); +} + +int GeolocationDispatcher::attachBridge( + WebKit::WebGeolocationServiceBridge* bridge) { + return bridges_map_.Add(bridge); +} + +void GeolocationDispatcher::dettachBridge(int bridge_id) { + bridges_map_.Remove(bridge_id); +} + +void GeolocationDispatcher::OnGeolocationPermissionSet(int bridge_id, + bool allowed) { + WebKit::WebGeolocationServiceBridge* bridge = bridges_map_.Lookup(bridge_id); + if (bridge) { + bridge->setIsAllowed(allowed); + } +} + +void GeolocationDispatcher::OnGeolocationPositionUpdated( + const Geoposition& geoposition) { + for (IDMap<WebKit::WebGeolocationServiceBridge>::iterator it(&bridges_map_); + !it.IsAtEnd(); it.Advance()) { + it.GetCurrentValue()->setLastPosition( + geoposition.latitude, geoposition.longitude, + geoposition.is_valid_altitude(), geoposition.altitude, + geoposition.accuracy, + geoposition.is_valid_altitude_accuracy(), geoposition.altitude_accuracy, + geoposition.is_valid_heading(), geoposition.heading, + geoposition.is_valid_speed(), geoposition.speed, + geoposition.timestamp); + } +} + +void GeolocationDispatcher::OnGeolocationError(int code, + const std::string& message) { + for (IDMap<WebKit::WebGeolocationServiceBridge>::iterator it(&bridges_map_); + !it.IsAtEnd(); it.Advance()) { + it.GetCurrentValue()->setLastError( + code, WebKit::WebString::fromUTF8(message)); + } +} diff --git a/chrome/renderer/geolocation_dispatcher.h b/chrome/renderer/geolocation_dispatcher.h new file mode 100644 index 0000000..31fdab4c --- /dev/null +++ b/chrome/renderer/geolocation_dispatcher.h @@ -0,0 +1,57 @@ +// 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 CHROME_RENDERER_GEOLOCATION_DISPATCHER_H_ +#define CHROME_RENDERER_GEOLOCATION_DISPATCHER_H_ + +#include "base/basictypes.h" +#include "base/id_map.h" +#include "chrome/common/geoposition.h" +#include "ipc/ipc_message.h" +#include "googleurl/src/gurl.h" +#include "third_party/WebKit/WebKit/chromium/public/GeolocationServiceBridgeChromium.h" + +class GURL; +class RenderView; + +// GeolocationDispatcher is a delegate for Geolocation messages used by +// WebKit. +// It's the complement of GeolocationDispatcherHost (owned by RenderViewHost). +class GeolocationDispatcher : public WebKit::WebGeolocationServiceInterface { + public: + explicit GeolocationDispatcher(RenderView* render_view); + virtual ~GeolocationDispatcher(); + + // Called to possibly handle the incoming IPC message. Returns true if + // handled. Called in render thread. + bool OnMessageReceived(const IPC::Message& msg); + + // WebKit::GeolocationServiceInterfaceChromium. + void requestPermissionForFrame(int bridge_id, const WebKit::WebURL& url); + void startUpdating(int bridge_id, bool hasHighAccuracy); + void stopUpdating(int bridge_id); + void suspend(int bridge_id); + void resume(int bridge_id); + int attachBridge(WebKit::WebGeolocationServiceBridge* geolocation_service); + void dettachBridge(int bridge_id); + + private: + // Permission for using geolocation has been set. + void OnGeolocationPermissionSet(int bridge_id, bool is_allowed); + + // We have an updated geolocation position. + void OnGeolocationPositionUpdated(const Geoposition& geoposition); + + // An error has happened when fetching a geolocation position. + void OnGeolocationError(int code, const std::string& message); + + RenderView* render_view_; + + // The geolocation services attached to this dispatcher. + IDMap<WebKit::WebGeolocationServiceBridge> bridges_map_; + + DISALLOW_COPY_AND_ASSIGN(GeolocationDispatcher); +}; + +#endif // CHROME_RENDERER_GEOLOCATION_DISPATCHER_H_ diff --git a/chrome/renderer/render_view.cc b/chrome/renderer/render_view.cc index 14c57af..282d6ec 100644 --- a/chrome/renderer/render_view.cc +++ b/chrome/renderer/render_view.cc @@ -45,6 +45,7 @@ #include "chrome/renderer/extensions/event_bindings.h" #include "chrome/renderer/extensions/extension_process_bindings.h" #include "chrome/renderer/extensions/renderer_extension_bindings.h" +#include "chrome/renderer/geolocation_dispatcher.h" #include "chrome/renderer/localized_error.h" #include "chrome/renderer/media/audio_renderer_impl.h" #include "chrome/renderer/navigation_state.h" @@ -68,6 +69,7 @@ #include "skia/ext/bitmap_platform_device.h" #include "skia/ext/image_operations.h" #include "third_party/cld/encodings/compact_lang_det/win/cld_unicodetext.h" +#include "third_party/WebKit/WebKit/chromium/public/GeolocationServiceBridgeChromium.h" #include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityCache.h" #include "third_party/WebKit/WebKit/chromium/public/WebAccessibilityObject.h" #include "third_party/WebKit/WebKit/chromium/public/WebCString.h" @@ -482,6 +484,10 @@ void RenderView::OnMessageReceived(const IPC::Message& message) { return; if (notification_provider_->OnMessageReceived(message)) return; + if (geolocation_dispatcher_.get() && + geolocation_dispatcher_->OnMessageReceived(message)) { + return; + } IPC_BEGIN_MESSAGE_MAP(RenderView, message) IPC_MESSAGE_HANDLER(ViewMsg_CaptureThumbnail, SendThumbnail) @@ -4497,3 +4503,10 @@ void RenderView::GPUPluginBuffersSwapped(gfx::PluginWindowHandle window) { Send(new ViewHostMsg_GPUPluginBuffersSwapped(routing_id(), window)); } #endif + +WebKit::WebGeolocationServiceInterface* RenderView::getGeolocationService() { + if (!geolocation_dispatcher_.get()) + geolocation_dispatcher_.reset(new GeolocationDispatcher(this)); + return geolocation_dispatcher_.get(); +} + diff --git a/chrome/renderer/render_view.h b/chrome/renderer/render_view.h index 691ce8a..8f453b9 100644 --- a/chrome/renderer/render_view.h +++ b/chrome/renderer/render_view.h @@ -69,6 +69,7 @@ class DictionaryValue; class DevToolsAgent; class DevToolsClient; class FilePath; +class GeolocationDispatcher; class GURL; class ListValue; class NavigationState; @@ -89,6 +90,10 @@ struct FileUploadData; } namespace WebKit { +class WebGeolocationServiceInterfaceChromium; +} + +namespace WebKit { class WebAccessibilityCache; class WebDataSource; class WebDragData; @@ -259,6 +264,7 @@ class RenderView : public RenderWidget, virtual WebKit::WebNotificationPresenter* GetNotificationPresenter() { return notification_provider_.get(); } + virtual WebKit::WebGeolocationServiceInterface* getGeolocationService(); // Sets the content settings that back allowScripts(), allowImages(), and // allowPlugins(). @@ -1105,6 +1111,9 @@ class RenderView : public RenderWidget, std::set<WebPluginDelegateProxy*> plugin_delegates_; #endif + // The geolocation dispatcher attached to this view, lazily initialized. + scoped_ptr<GeolocationDispatcher> geolocation_dispatcher_; + DISALLOW_COPY_AND_ASSIGN(RenderView); }; diff --git a/chrome/test/data/geolocation/simple.html b/chrome/test/data/geolocation/simple.html new file mode 100644 index 0000000..fef9724 --- /dev/null +++ b/chrome/test/data/geolocation/simple.html @@ -0,0 +1,35 @@ +<html> + <head> + <script> + var last_position = 0; + var last_error = 0; + var watch_id = 0; + function geoSuccessCallback(position) { + last_position = position; + alert('geoSuccessCallback'); + } + function geoErrorCallback(error) { + last_error = error; + alert('geoErrorCallback'); + } + function geoStart() { + watch_id = navigator.geolocation.watchPosition( + geoSuccessCallback, geoErrorCallback, + {maximumAge:600000, timeout:100000, enableHighAccuracy:true}); + return watch_id; + } + function geoGetLastPositionLatitude() { + return "" + last_position.coords.latitude; + } + function geoGetLastPositionLongitude() { + return "" + last_position.coords.longitude; + } + function geoGetLastError() { + return "" + (last_error ? last_error.code : 0); + } + </script> + </head> + <body> + <input type="button" value="manual" onclick="geoStart()"/> + </body> +</html> |