From c476e6316a406234898bd0af07bd0a02ffb40f51 Mon Sep 17 00:00:00 2001 From: "joth@chromium.org" Date: Thu, 23 Jun 2011 11:18:04 +0000 Subject: Split out abstract interface from GeolocationPermissionContext This allows clear separation of chrome from content without requiring a link-time dependency from content onto the embedder to provide the permission context implementaiton. BUG=None TEST=No new tests. Review URL: http://codereview.chromium.org/7037005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90196 0039d316-1c4b-4281-b951-d872f2087c98 --- .../chrome_geolocation_permission_context.cc | 623 ++++++++++++++++++++ .../chrome_geolocation_permission_context.h | 62 ++ ...rome_geolocation_permission_context_unittest.cc | 478 ++++++++++++++++ .../geolocation/geolocation_permission_context.cc | 627 --------------------- .../geolocation_permission_context_unittest.cc | 476 ---------------- chrome/browser/profiles/profile_impl.cc | 5 +- chrome/chrome_browser.gypi | 3 +- chrome/chrome_tests.gypi | 2 +- chrome/test/testing_profile.cc | 4 +- 9 files changed, 1171 insertions(+), 1109 deletions(-) create mode 100644 chrome/browser/geolocation/chrome_geolocation_permission_context.cc create mode 100644 chrome/browser/geolocation/chrome_geolocation_permission_context.h create mode 100644 chrome/browser/geolocation/chrome_geolocation_permission_context_unittest.cc delete mode 100644 chrome/browser/geolocation/geolocation_permission_context.cc delete mode 100644 chrome/browser/geolocation/geolocation_permission_context_unittest.cc (limited to 'chrome') diff --git a/chrome/browser/geolocation/chrome_geolocation_permission_context.cc b/chrome/browser/geolocation/chrome_geolocation_permission_context.cc new file mode 100644 index 0000000..9b3bd60 --- /dev/null +++ b/chrome/browser/geolocation/chrome_geolocation_permission_context.cc @@ -0,0 +1,623 @@ +// 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 "chrome/browser/geolocation/chrome_geolocation_permission_context.h" + +#include +#include +#include + +#include "base/utf_string_conversions.h" +#include "chrome/browser/content_settings/tab_specific_content_settings.h" +#include "chrome/browser/extensions/extension_service.h" +#include "chrome/browser/geolocation/geolocation_content_settings_map.h" +#include "chrome/browser/google/google_util.h" +#include "chrome/browser/prefs/pref_service.h" +#include "chrome/browser/profiles/profile.h" +#include "chrome/browser/tab_contents/confirm_infobar_delegate.h" +#include "chrome/browser/tab_contents/tab_util.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/common/extensions/extension.h" +#include "chrome/common/pref_names.h" +#include "content/browser/browser_thread.h" +#include "content/browser/geolocation/geolocation_provider.h" +#include "content/browser/renderer_host/render_process_host.h" +#include "content/common/notification_registrar.h" +#include "content/common/notification_source.h" +#include "content/common/notification_type.h" +#include "grit/generated_resources.h" +#include "grit/locale_settings.h" +#include "grit/theme_resources.h" +#include "net/base/net_util.h" +#include "ui/base/l10n/l10n_util.h" +#include "ui/base/resource/resource_bundle.h" + +// GeolocationInfoBarQueueController ------------------------------------------ + +// This class controls the geolocation infobar queue per profile, and it's an +// internal class to GeolocationPermissionContext. +// An alternate approach would be to have this queue per tab, and use +// notifications to broadcast when permission is set / listen to notification to +// cancel pending requests. This may be specially useful if there are other +// things listening for such notifications. +// For the time being this class is self-contained and it doesn't seem pulling +// the notification infrastructure would simplify. +class GeolocationInfoBarQueueController : NotificationObserver { + public: + GeolocationInfoBarQueueController( + ChromeGeolocationPermissionContext* geolocation_permission_context, + Profile* profile); + ~GeolocationInfoBarQueueController(); + + // The InfoBar will be displayed immediately if the tab is not already + // displaying one, otherwise it'll be queued. + void CreateInfoBarRequest(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + const GURL& emebedder); + + // Cancels a specific infobar request. + void CancelInfoBarRequest(int render_process_id, + int render_view_id, + int bridge_id); + + // Called by the InfoBarDelegate to notify it's closed. It'll display a new + // InfoBar if there's any request pending for this tab. + void OnInfoBarClosed(int render_process_id, + int render_view_id, + int bridge_id); + + // Called by the InfoBarDelegate to notify permission has been set. + // It'll notify and dismiss any other pending InfoBar request for the same + // |requesting_frame| and embedder. + void OnPermissionSet(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + const GURL& embedder, + bool allowed); + + // NotificationObserver + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + private: + struct PendingInfoBarRequest; + class RequestEquals; + + typedef std::vector PendingInfoBarRequests; + + // Shows the first pending infobar for this tab. + void ShowQueuedInfoBar(int render_process_id, int render_view_id); + + // Cancels an InfoBar request and returns the next iterator position. + PendingInfoBarRequests::iterator CancelInfoBarRequestInternal( + PendingInfoBarRequests::iterator i); + + NotificationRegistrar registrar_; + + ChromeGeolocationPermissionContext* const geolocation_permission_context_; + Profile* const profile_; + PendingInfoBarRequests pending_infobar_requests_; +}; + + +// GeolocationConfirmInfoBarDelegate ------------------------------------------ + +namespace { + +class GeolocationConfirmInfoBarDelegate : public ConfirmInfoBarDelegate { + public: + GeolocationConfirmInfoBarDelegate( + TabContents* tab_contents, + GeolocationInfoBarQueueController* controller, + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame_url, + const std::string& display_languages); + + private: + virtual ~GeolocationConfirmInfoBarDelegate(); + + // ConfirmInfoBarDelegate: + virtual gfx::Image* GetIcon() const OVERRIDE; + virtual Type GetInfoBarType() const OVERRIDE; + virtual string16 GetMessageText() const OVERRIDE; + virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; + virtual bool Accept() OVERRIDE; + virtual bool Cancel() OVERRIDE; + virtual string16 GetLinkText() OVERRIDE; + virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE; + + TabContents* tab_contents_; + GeolocationInfoBarQueueController* controller_; + int render_process_id_; + int render_view_id_; + int bridge_id_; + GURL requesting_frame_url_; + std::string display_languages_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(GeolocationConfirmInfoBarDelegate); +}; + +GeolocationConfirmInfoBarDelegate::GeolocationConfirmInfoBarDelegate( + TabContents* tab_contents, + GeolocationInfoBarQueueController* controller, + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame_url, + const std::string& display_languages) + : ConfirmInfoBarDelegate(tab_contents), + tab_contents_(tab_contents), + controller_(controller), + render_process_id_(render_process_id), + render_view_id_(render_view_id), + bridge_id_(bridge_id), + requesting_frame_url_(requesting_frame_url), + display_languages_(display_languages) { +} + +GeolocationConfirmInfoBarDelegate::~GeolocationConfirmInfoBarDelegate() { + controller_->OnInfoBarClosed(render_process_id_, render_view_id_, + bridge_id_); +} + +gfx::Image* GeolocationConfirmInfoBarDelegate::GetIcon() const { + return &ResourceBundle::GetSharedInstance().GetNativeImageNamed( + IDR_GEOLOCATION_INFOBAR_ICON); +} + +InfoBarDelegate::Type + GeolocationConfirmInfoBarDelegate::GetInfoBarType() const { + return PAGE_ACTION_TYPE; +} + +string16 GeolocationConfirmInfoBarDelegate::GetMessageText() const { + return l10n_util::GetStringFUTF16(IDS_GEOLOCATION_INFOBAR_QUESTION, + net::FormatUrl(requesting_frame_url_.GetOrigin(), display_languages_)); +} + +string16 GeolocationConfirmInfoBarDelegate::GetButtonLabel( + InfoBarButton button) const { + return l10n_util::GetStringUTF16((button == BUTTON_OK) ? + IDS_GEOLOCATION_ALLOW_BUTTON : IDS_GEOLOCATION_DENY_BUTTON); +} + +bool GeolocationConfirmInfoBarDelegate::Accept() { + controller_->OnPermissionSet(render_process_id_, render_view_id_, bridge_id_, + requesting_frame_url_, tab_contents_->GetURL(), true); + return true; +} + +bool GeolocationConfirmInfoBarDelegate::Cancel() { + controller_->OnPermissionSet(render_process_id_, render_view_id_, bridge_id_, + requesting_frame_url_, tab_contents_->GetURL(), false); + return true; +} + +string16 GeolocationConfirmInfoBarDelegate::GetLinkText() { + return l10n_util::GetStringUTF16(IDS_LEARN_MORE); +} + +bool GeolocationConfirmInfoBarDelegate::LinkClicked( + WindowOpenDisposition disposition) { + const char kGeolocationLearnMoreUrl[] = +#if defined(OS_CHROMEOS) + "https://www.google.com/support/chromeos/bin/answer.py?answer=142065"; +#else + "https://www.google.com/support/chrome/bin/answer.py?answer=142065"; +#endif + + // Ignore the click disposition and always open in a new top level tab. + tab_contents_->OpenURL( + google_util::AppendGoogleLocaleParam(GURL(kGeolocationLearnMoreUrl)), + GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); + return false; // Do not dismiss the info bar. +} + +} // namespace + + +// GeolocationInfoBarQueueController::PendingInfoBarRequest ------------------- + +struct GeolocationInfoBarQueueController::PendingInfoBarRequest { + public: + PendingInfoBarRequest(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + const GURL& embedder); + + bool IsForTab(int p_render_process_id, int p_render_view_id) const; + bool IsForPair(const GURL& p_requesting_frame, + const GURL& p_embedder) const; + bool Equals(int p_render_process_id, + int p_render_view_id, + int p_bridge_id) const; + + int render_process_id; + int render_view_id; + int bridge_id; + GURL requesting_frame; + GURL embedder; + InfoBarDelegate* infobar_delegate; +}; + +GeolocationInfoBarQueueController::PendingInfoBarRequest::PendingInfoBarRequest( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + const GURL& embedder) + : render_process_id(render_process_id), + render_view_id(render_view_id), + bridge_id(bridge_id), + requesting_frame(requesting_frame), + embedder(embedder), + infobar_delegate(NULL) { +} + +bool GeolocationInfoBarQueueController::PendingInfoBarRequest::IsForTab( + int p_render_process_id, + int p_render_view_id) const { + return (render_process_id == p_render_process_id) && + (render_view_id == p_render_view_id); +} + +bool GeolocationInfoBarQueueController::PendingInfoBarRequest::IsForPair( + const GURL& p_requesting_frame, + const GURL& p_embedder) const { + return (requesting_frame == p_requesting_frame) && (embedder == p_embedder); +} + +bool GeolocationInfoBarQueueController::PendingInfoBarRequest::Equals( + int p_render_process_id, + int p_render_view_id, + int p_bridge_id) const { + return IsForTab(p_render_process_id, p_render_view_id) && + (bridge_id == p_bridge_id); +} + + +// GeolocationInfoBarQueueController::RequestEquals --------------------------- + +// Useful predicate for checking PendingInfoBarRequest equality. +class GeolocationInfoBarQueueController::RequestEquals + : public std::unary_function { + public: + RequestEquals(int render_process_id, int render_view_id, int bridge_id); + + bool operator()(const PendingInfoBarRequest& request) const; + + private: + int render_process_id_; + int render_view_id_; + int bridge_id_; +}; + +GeolocationInfoBarQueueController::RequestEquals::RequestEquals( + int render_process_id, + int render_view_id, + int bridge_id) + : render_process_id_(render_process_id), + render_view_id_(render_view_id), + bridge_id_(bridge_id) { +} + +bool GeolocationInfoBarQueueController::RequestEquals::operator()( + const PendingInfoBarRequest& request) const { + return request.Equals(render_process_id_, render_view_id_, bridge_id_); +} + + +// GeolocationInfoBarQueueController ------------------------------------------ + +GeolocationInfoBarQueueController::GeolocationInfoBarQueueController( + ChromeGeolocationPermissionContext* geolocation_permission_context, + Profile* profile) + : geolocation_permission_context_(geolocation_permission_context), + profile_(profile) { +} + +GeolocationInfoBarQueueController::~GeolocationInfoBarQueueController() { +} + +void GeolocationInfoBarQueueController::CreateInfoBarRequest( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + const GURL& embedder) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // We shouldn't get duplicate requests. + DCHECK(std::find_if(pending_infobar_requests_.begin(), + pending_infobar_requests_.end(), + RequestEquals(render_process_id, render_view_id, bridge_id)) == + pending_infobar_requests_.end()); + + pending_infobar_requests_.push_back(PendingInfoBarRequest(render_process_id, + render_view_id, bridge_id, requesting_frame, embedder)); + ShowQueuedInfoBar(render_process_id, render_view_id); +} + +void GeolocationInfoBarQueueController::CancelInfoBarRequest( + int render_process_id, + int render_view_id, + int bridge_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + PendingInfoBarRequests::iterator i = std::find_if( + pending_infobar_requests_.begin(), pending_infobar_requests_.end(), + RequestEquals(render_process_id, render_view_id, bridge_id)); + // TODO(pkasting): Can this conditional become a DCHECK()? + if (i != pending_infobar_requests_.end()) + CancelInfoBarRequestInternal(i); +} + +void GeolocationInfoBarQueueController::OnInfoBarClosed(int render_process_id, + int render_view_id, + int bridge_id) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + PendingInfoBarRequests::iterator i = std::find_if( + pending_infobar_requests_.begin(), pending_infobar_requests_.end(), + RequestEquals(render_process_id, render_view_id, bridge_id)); + if (i != pending_infobar_requests_.end()) + pending_infobar_requests_.erase(i); + + ShowQueuedInfoBar(render_process_id, render_view_id); +} + +void GeolocationInfoBarQueueController::OnPermissionSet( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + const GURL& embedder, + bool allowed) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + ContentSetting content_setting = + allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; + profile_->GetGeolocationContentSettingsMap()->SetContentSetting( + requesting_frame.GetOrigin(), embedder.GetOrigin(), content_setting); + + for (PendingInfoBarRequests::iterator i = pending_infobar_requests_.begin(); + i != pending_infobar_requests_.end(); ) { + if (i->IsForPair(requesting_frame, embedder)) { + // Cancel this request first, then notify listeners. TODO(pkasting): Why + // is this order important? + // NOTE: If the pending request had an infobar, TabContents will close it + // either synchronously or asynchronously, which will then pump the queue + // via OnInfoBarClosed(). + PendingInfoBarRequest copied_request = *i; + // Don't let CancelInfoBarRequestInternal() call RemoveInfoBar() on the + // delegate that's currently calling us. That delegate is in either + // Accept() or Cancel(), so its owning InfoBar will call RemoveInfoBar() + // later on in this callstack anyway; and if we do it here, and it causes + // the delegate to be deleted, our GURL& args will point to garbage and we + // may also cause other problems during stack unwinding. + if (i->Equals(render_process_id, render_view_id, bridge_id)) + i->infobar_delegate = NULL; + i = CancelInfoBarRequestInternal(i); + + geolocation_permission_context_->NotifyPermissionSet( + copied_request.render_process_id, copied_request.render_view_id, + copied_request.bridge_id, copied_request.requesting_frame, allowed); + } else { + ++i; + } + } +} + +void GeolocationInfoBarQueueController::Observe( + NotificationType type, const NotificationSource& source, + const NotificationDetails& details) { + registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, + source); + TabContents* tab_contents = Source(source).ptr(); + for (PendingInfoBarRequests::iterator i = pending_infobar_requests_.begin(); + i != pending_infobar_requests_.end();) { + if (i->infobar_delegate == NULL && + tab_contents == tab_util::GetTabContentsByID(i->render_process_id, + i->render_view_id)) { + i = pending_infobar_requests_.erase(i); + } else { + ++i; + } + } +} + +void GeolocationInfoBarQueueController::ShowQueuedInfoBar(int render_process_id, + int render_view_id) { + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_id, render_view_id); + TabContentsWrapper* wrapper = NULL; + if (tab_contents) + wrapper = TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); + for (PendingInfoBarRequests::iterator i = pending_infobar_requests_.begin(); + i != pending_infobar_requests_.end(); ) { + if (i->IsForTab(render_process_id, render_view_id)) { + if (!wrapper) { + i = pending_infobar_requests_.erase(i); + continue; + } + + if (!i->infobar_delegate) { + if (!registrar_.IsRegistered(this, + NotificationType::TAB_CONTENTS_DESTROYED, + Source(tab_contents))) { + registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, + Source(tab_contents)); + } + i->infobar_delegate = new GeolocationConfirmInfoBarDelegate( + tab_contents, this, render_process_id, render_view_id, i->bridge_id, + i->requesting_frame, + profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); + wrapper->AddInfoBar(i->infobar_delegate); + } + break; + } + ++i; + } +} + +GeolocationInfoBarQueueController::PendingInfoBarRequests::iterator + GeolocationInfoBarQueueController::CancelInfoBarRequestInternal( + PendingInfoBarRequests::iterator i) { + InfoBarDelegate* delegate = i->infobar_delegate; + if (!delegate) + return pending_infobar_requests_.erase(i); + + TabContents* tab_contents = + tab_util::GetTabContentsByID(i->render_process_id, i->render_view_id); + if (!tab_contents) + return pending_infobar_requests_.erase(i); + + // TabContents will destroy the InfoBar, which will remove from our vector + // asynchronously. + TabContentsWrapper* wrapper = + TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); + wrapper->RemoveInfoBar(i->infobar_delegate); + return ++i; +} + + +// GeolocationPermissionContext ----------------------------------------------- + +ChromeGeolocationPermissionContext::ChromeGeolocationPermissionContext( + Profile* profile) + : profile_(profile), + ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_infobar_queue_controller_( + new GeolocationInfoBarQueueController(this, profile))) { +} + +ChromeGeolocationPermissionContext::~ChromeGeolocationPermissionContext() { +} + +void ChromeGeolocationPermissionContext::RequestGeolocationPermission( + int render_process_id, int render_view_id, int bridge_id, + const GURL& requesting_frame) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod( + this, &ChromeGeolocationPermissionContext::RequestGeolocationPermission, + render_process_id, render_view_id, bridge_id, requesting_frame)); + return; + } + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + ExtensionService* extensions = profile_->GetExtensionService(); + if (extensions) { + const Extension* ext = extensions->GetExtensionByURL(requesting_frame); + if (!ext) + ext = extensions->GetExtensionByWebExtent(requesting_frame); + if (ext && ext->HasApiPermission(Extension::kGeolocationPermission)) { + ExtensionProcessManager* epm = profile_->GetExtensionProcessManager(); + RenderProcessHost* process = epm->GetExtensionProcess(requesting_frame); + if (process && process->id() == render_process_id) { + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, + requesting_frame, true); + return; + } + } + } + + TabContents* tab_contents = + tab_util::GetTabContentsByID(render_process_id, render_view_id); + if (!tab_contents) { + // The tab may have gone away, or the request may not be from a tab at all. + LOG(WARNING) << "Attempt to use geolocation tabless renderer: " + << render_process_id << "," << render_view_id << "," + << bridge_id << " (can't prompt user without a visible tab)"; + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, + requesting_frame, false); + return; + } + + GURL embedder = tab_contents->GetURL(); + if (!requesting_frame.is_valid() || !embedder.is_valid()) { + LOG(WARNING) << "Attempt to use geolocation from an invalid URL: " + << requesting_frame << "," << embedder + << " (geolocation is not supported in popups)"; + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, + requesting_frame, false); + return; + } + + ContentSetting content_setting = + profile_->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame, embedder); + if (content_setting == CONTENT_SETTING_BLOCK) { + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, + requesting_frame, false); + } else if (content_setting == CONTENT_SETTING_ALLOW) { + NotifyPermissionSet(render_process_id, render_view_id, bridge_id, + requesting_frame, true); + } else { // setting == ask. Prompt the user. + geolocation_infobar_queue_controller_->CreateInfoBarRequest( + render_process_id, render_view_id, bridge_id, requesting_frame, + embedder); + } +} + +void ChromeGeolocationPermissionContext::CancelGeolocationPermissionRequest( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame) { + CancelPendingInfoBarRequest(render_process_id, render_view_id, bridge_id); +} + +void ChromeGeolocationPermissionContext::NotifyPermissionSet( + int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + bool allowed) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + // TabContents may have gone away (or not exists for extension). + TabSpecificContentSettings* content_settings = + TabSpecificContentSettings::Get(render_process_id, render_view_id); + if (content_settings) { + content_settings->OnGeolocationPermissionSet(requesting_frame.GetOrigin(), + allowed); + } + + GeolocationPermissionContext::SetGeolocationPermissionResponse( + render_process_id, render_view_id, bridge_id, allowed); + + if (allowed) { + BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableMethod( + this, + &ChromeGeolocationPermissionContext::NotifyArbitratorPermissionGranted, + requesting_frame)); + } +} + +void ChromeGeolocationPermissionContext::NotifyArbitratorPermissionGranted( + const GURL& requesting_frame) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + GeolocationProvider::GetInstance()->OnPermissionGranted(requesting_frame); +} + +void ChromeGeolocationPermissionContext::CancelPendingInfoBarRequest( + int render_process_id, + int render_view_id, + int bridge_id) { + if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod( + this, &ChromeGeolocationPermissionContext::CancelPendingInfoBarRequest, + render_process_id, render_view_id, bridge_id)); + return; + } + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + geolocation_infobar_queue_controller_->CancelInfoBarRequest(render_process_id, + render_view_id, bridge_id); +} diff --git a/chrome/browser/geolocation/chrome_geolocation_permission_context.h b/chrome/browser/geolocation/chrome_geolocation_permission_context.h new file mode 100644 index 0000000..2195362 --- /dev/null +++ b/chrome/browser/geolocation/chrome_geolocation_permission_context.h @@ -0,0 +1,62 @@ +// 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 CHROME_BROWSER_GEOLOCATION_CHROME_GEOLOCATION_PERMISSION_CONTEXT_H_ +#define CHROME_BROWSER_GEOLOCATION_CHROME_GEOLOCATION_PERMISSION_CONTEXT_H_ +#pragma once + +#include "base/memory/scoped_ptr.h" +#include "content/browser/geolocation/geolocation_permission_context.h" + +class GeolocationInfoBarQueueController; +class Profile; + +// Chrome specific implementation of GeolocationPermissionContext; manages +// Geolocation permissions flow, and delegates UI handling via +// GeolocationInfoBarQueueController. +class ChromeGeolocationPermissionContext : public GeolocationPermissionContext { + public: + explicit ChromeGeolocationPermissionContext(Profile* profile); + + // Notifies whether or not the corresponding bridge is allowed to use + // geolocation via + // GeolocationPermissionContext::SetGeolocationPermissionResponse(). + void NotifyPermissionSet(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame, + bool allowed); + + // GeolocationPermissionContext + virtual void RequestGeolocationPermission(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame); + + virtual void CancelGeolocationPermissionRequest(int render_process_id, + int render_view_id, + int bridge_id, + const GURL& requesting_frame); + + private: + virtual ~ChromeGeolocationPermissionContext(); + + // Calls GeolocationArbitrator::OnPermissionGranted. + void NotifyArbitratorPermissionGranted(const GURL& requesting_frame); + + // Removes any pending InfoBar request. + void CancelPendingInfoBarRequest(int render_process_id, + int render_view_id, + int bridge_id); + + // This must only be accessed from the UI thread. + Profile* const profile_; + + scoped_ptr + geolocation_infobar_queue_controller_; + + DISALLOW_COPY_AND_ASSIGN(ChromeGeolocationPermissionContext); +}; + +#endif // CHROME_BROWSER_GEOLOCATION_CHROME_GEOLOCATION_PERMISSION_CONTEXT_H_ diff --git a/chrome/browser/geolocation/chrome_geolocation_permission_context_unittest.cc b/chrome/browser/geolocation/chrome_geolocation_permission_context_unittest.cc new file mode 100644 index 0000000..3d86695 --- /dev/null +++ b/chrome/browser/geolocation/chrome_geolocation_permission_context_unittest.cc @@ -0,0 +1,478 @@ +// 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 "chrome/browser/geolocation/chrome_geolocation_permission_context.h" + +#include + +#include "base/memory/scoped_vector.h" +#include "chrome/browser/content_settings/tab_specific_content_settings.h" +#include "chrome/browser/geolocation/geolocation_content_settings_map.h" +#include "chrome/browser/tab_contents/confirm_infobar_delegate.h" +#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" +#include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.h" +#include "chrome/test/testing_profile.h" +#include "content/browser/browser_thread.h" +#include "content/browser/geolocation/arbitrator_dependency_factories_for_test.h" +#include "content/browser/geolocation/location_arbitrator.h" +#include "content/browser/geolocation/location_provider.h" +#include "content/browser/geolocation/mock_location_provider.h" +#include "content/browser/renderer_host/mock_render_process_host.h" +#include "content/browser/tab_contents/test_tab_contents.h" +#include "content/common/geolocation_messages.h" +#include "content/common/notification_registrar.h" +#include "content/common/notification_service.h" +#include "testing/gtest/include/gtest/gtest.h" + +// ClosedDelegateTracker ------------------------------------------------------ + +namespace { + +// We need to track which infobars were closed. +class ClosedDelegateTracker : public NotificationObserver { + public: + ClosedDelegateTracker(); + virtual ~ClosedDelegateTracker(); + + // NotificationObserver: + virtual void Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details); + + size_t size() const { + return removed_infobar_delegates_.size(); + } + + bool Contains(InfoBarDelegate* delegate) const; + void Clear(); + + private: + NotificationRegistrar registrar_; + std::set removed_infobar_delegates_; +}; + +ClosedDelegateTracker::ClosedDelegateTracker() { + registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, + NotificationService::AllSources()); +} + +ClosedDelegateTracker::~ClosedDelegateTracker() { +} + +void ClosedDelegateTracker::Observe(NotificationType type, + const NotificationSource& source, + const NotificationDetails& details) { + DCHECK(type.value == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED); + removed_infobar_delegates_.insert( + Details >(details)->first); +} + +bool ClosedDelegateTracker::Contains(InfoBarDelegate* delegate) const { + return removed_infobar_delegates_.count(delegate) != 0; +} + +void ClosedDelegateTracker::Clear() { + removed_infobar_delegates_.clear(); +} + +} // namespace + + +// GeolocationPermissionContextTests ------------------------------------------ + +// This class sets up GeolocationArbitrator. +class GeolocationPermissionContextTests : public TabContentsWrapperTestHarness { + public: + GeolocationPermissionContextTests(); + + protected: + virtual ~GeolocationPermissionContextTests(); + + int process_id() { return contents()->render_view_host()->process()->id(); } + int process_id_for_tab(int tab) { + return extra_tabs_[tab]->tab_contents()->render_view_host()->process()-> + id(); + } + int render_id() { return contents()->render_view_host()->routing_id(); } + int render_id_for_tab(int tab) { + return extra_tabs_[tab]->tab_contents()->render_view_host()->routing_id(); + } + int bridge_id() const { return 42; } // Not relevant at this level. + + void CheckPermissionMessageSent(int bridge_id, bool allowed); + void CheckPermissionMessageSentForTab(int tab, int bridge_id, bool allowed); + void CheckPermissionMessageSentInternal(MockRenderProcessHost* process, + int bridge_id, + bool allowed); + void AddNewTab(const GURL& url); + void CheckTabContentsState(const GURL& requesting_frame, + ContentSetting expected_content_setting); + + scoped_refptr + geolocation_permission_context_; + ClosedDelegateTracker closed_delegate_tracker_; + ScopedVector extra_tabs_; + + private: + // TabContentsWrapperTestHarness: + virtual void SetUp(); + virtual void TearDown(); + + BrowserThread ui_thread_; + scoped_refptr dependency_factory_; +}; + +GeolocationPermissionContextTests::GeolocationPermissionContextTests() + : TabContentsWrapperTestHarness(), + ui_thread_(BrowserThread::UI, MessageLoop::current()), + dependency_factory_( + new GeolocationArbitratorDependencyFactoryWithLocationProvider( + &NewAutoSuccessMockNetworkLocationProvider)) { +} + +GeolocationPermissionContextTests::~GeolocationPermissionContextTests() { +} + +void GeolocationPermissionContextTests::CheckPermissionMessageSent( + int bridge_id, + bool allowed) { + CheckPermissionMessageSentInternal(process(), bridge_id, allowed); +} + +void GeolocationPermissionContextTests::CheckPermissionMessageSentForTab( + int tab, + int bridge_id, + bool allowed) { + CheckPermissionMessageSentInternal(static_cast( + extra_tabs_[tab]->tab_contents()->render_view_host()->process()), + bridge_id, allowed); +} + +void GeolocationPermissionContextTests::CheckPermissionMessageSentInternal( + MockRenderProcessHost* process, + int bridge_id, + bool allowed) { + MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); + MessageLoop::current()->Run(); + const IPC::Message* message = process->sink().GetFirstMessageMatching( + GeolocationMsg_PermissionSet::ID); + ASSERT_TRUE(message); + GeolocationMsg_PermissionSet::Param param; + GeolocationMsg_PermissionSet::Read(message, ¶m); + EXPECT_EQ(bridge_id, param.a); + EXPECT_EQ(allowed, param.b); + process->sink().ClearMessages(); +} + +void GeolocationPermissionContextTests::AddNewTab(const GURL& url) { + TabContents* new_tab = + new TabContents(profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); + new_tab->controller().LoadURL(url, GURL(), PageTransition::TYPED); + static_cast(new_tab->render_manager()->current_host())-> + SendNavigate(extra_tabs_.size() + 1, url); + extra_tabs_.push_back(new TabContentsWrapper(new_tab)); +} + +void GeolocationPermissionContextTests::CheckTabContentsState( + const GURL& requesting_frame, + ContentSetting expected_content_setting) { + TabSpecificContentSettings* content_settings = + contents_wrapper()->content_settings(); + const GeolocationSettingsState::StateMap& state_map = + content_settings->geolocation_settings_state().state_map(); + EXPECT_EQ(1U, state_map.count(requesting_frame.GetOrigin())); + EXPECT_EQ(0U, state_map.count(requesting_frame)); + GeolocationSettingsState::StateMap::const_iterator settings = + state_map.find(requesting_frame.GetOrigin()); + ASSERT_FALSE(settings == state_map.end()) + << "geolocation state not found " << requesting_frame; + EXPECT_EQ(expected_content_setting, settings->second); +} + +void GeolocationPermissionContextTests::SetUp() { + TabContentsWrapperTestHarness::SetUp(); + GeolocationArbitrator::SetDependencyFactoryForTest( + dependency_factory_.get()); + geolocation_permission_context_ = + new ChromeGeolocationPermissionContext(profile()); +} + +void GeolocationPermissionContextTests::TearDown() { + GeolocationArbitrator::SetDependencyFactoryForTest(NULL); + TabContentsWrapperTestHarness::TearDown(); +} + + +// Tests ---------------------------------------------------------------------- + +TEST_F(GeolocationPermissionContextTests, SinglePermission) { + GURL requesting_frame("http://www.example.com/geolocation"); + NavigateAndCommit(requesting_frame); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), requesting_frame); + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); +} + +TEST_F(GeolocationPermissionContextTests, QueuedPermission) { + GURL requesting_frame_0("http://www.example.com/geolocation"); + GURL requesting_frame_1("http://www.example-2.com/geolocation"); + EXPECT_EQ(CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_0, requesting_frame_0)); + EXPECT_EQ(CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_1, requesting_frame_0)); + + NavigateAndCommit(requesting_frame_0); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + // Request permission for two frames. + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), requesting_frame_0); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id() + 1, requesting_frame_1); + // Ensure only one infobar is created. + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + ConfirmInfoBarDelegate* infobar_0 = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_0); + string16 text_0 = infobar_0->GetMessageText(); + + // Accept the first frame. + infobar_0->Accept(); + CheckTabContentsState(requesting_frame_0, CONTENT_SETTING_ALLOW); + CheckPermissionMessageSent(bridge_id(), true); + + contents_wrapper()->RemoveInfoBar(infobar_0); + EXPECT_EQ(1U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); + closed_delegate_tracker_.Clear(); + infobar_0->InfoBarClosed(); + // Now we should have a new infobar for the second frame. + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + + ConfirmInfoBarDelegate* infobar_1 = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_1); + string16 text_1 = infobar_1->GetMessageText(); + EXPECT_NE(text_0, text_1); + + // Cancel (block) this frame. + infobar_1->Cancel(); + CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_BLOCK); + CheckPermissionMessageSent(bridge_id() + 1, false); + contents_wrapper()->RemoveInfoBar(infobar_1); + EXPECT_EQ(1U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_1)); + infobar_1->InfoBarClosed(); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + // Ensure the persisted permissions are ok. + EXPECT_EQ(CONTENT_SETTING_ALLOW, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_0, requesting_frame_0)); + EXPECT_EQ(CONTENT_SETTING_BLOCK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_1, requesting_frame_0)); +} + +TEST_F(GeolocationPermissionContextTests, CancelGeolocationPermissionRequest) { + GURL requesting_frame_0("http://www.example.com/geolocation"); + GURL requesting_frame_1("http://www.example-2.com/geolocation"); + EXPECT_EQ(CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_0, requesting_frame_0)); + EXPECT_EQ(CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_1, requesting_frame_0)); + + NavigateAndCommit(requesting_frame_0); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + // Request permission for two frames. + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), requesting_frame_0); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id() + 1, requesting_frame_1); + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + + ConfirmInfoBarDelegate* infobar_0 = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_0); + string16 text_0 = infobar_0->GetMessageText(); + + // Simulate the frame going away, ensure the infobar for this frame + // is removed and the next pending infobar is created. + geolocation_permission_context_->CancelGeolocationPermissionRequest( + process_id(), render_id(), bridge_id(), requesting_frame_0); + EXPECT_EQ(1U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); + closed_delegate_tracker_.Clear(); + infobar_0->InfoBarClosed(); + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + + ConfirmInfoBarDelegate* infobar_1 = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_1); + string16 text_1 = infobar_1->GetMessageText(); + EXPECT_NE(text_0, text_1); + + // Allow this frame. + infobar_1->Accept(); + CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_ALLOW); + CheckPermissionMessageSent(bridge_id() + 1, true); + contents_wrapper()->RemoveInfoBar(infobar_1); + EXPECT_EQ(1U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_1)); + infobar_1->InfoBarClosed(); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + // Ensure the persisted permissions are ok. + EXPECT_EQ(CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_0, requesting_frame_0)); + EXPECT_EQ(CONTENT_SETTING_ALLOW, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_1, requesting_frame_0)); +} + +TEST_F(GeolocationPermissionContextTests, InvalidURL) { + GURL invalid_embedder; + GURL requesting_frame("about:blank"); + NavigateAndCommit(invalid_embedder); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), requesting_frame); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + CheckPermissionMessageSent(bridge_id(), false); +} + +TEST_F(GeolocationPermissionContextTests, SameOriginMultipleTabs) { + GURL url_a("http://www.example.com/geolocation"); + GURL url_b("http://www.example-2.com/geolocation"); + NavigateAndCommit(url_a); + AddNewTab(url_b); + AddNewTab(url_a); + + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), url_a); + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + + geolocation_permission_context_->RequestGeolocationPermission( + process_id_for_tab(0), render_id_for_tab(0), bridge_id(), url_b); + EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); + + geolocation_permission_context_->RequestGeolocationPermission( + process_id_for_tab(1), render_id_for_tab(1), bridge_id(), url_a); + EXPECT_EQ(1U, extra_tabs_[1]->infobar_count()); + + ConfirmInfoBarDelegate* removed_infobar = + extra_tabs_[1]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + + // Accept the first tab. + ConfirmInfoBarDelegate* infobar_0 = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_0); + infobar_0->Accept(); + CheckPermissionMessageSent(bridge_id(), true); + contents_wrapper()->RemoveInfoBar(infobar_0); + EXPECT_EQ(2U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); + infobar_0->InfoBarClosed(); + // Now the infobar for the tab with the same origin should have gone. + EXPECT_EQ(0U, extra_tabs_[1]->infobar_count()); + CheckPermissionMessageSentForTab(1, bridge_id(), true); + EXPECT_TRUE(closed_delegate_tracker_.Contains(removed_infobar)); + // Destroy the infobar that has just been removed. + removed_infobar->InfoBarClosed(); + + // But the other tab should still have the info bar... + EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); + extra_tabs_.reset(); +} + +TEST_F(GeolocationPermissionContextTests, QueuedOriginMultipleTabs) { + GURL url_a("http://www.example.com/geolocation"); + GURL url_b("http://www.example-2.com/geolocation"); + NavigateAndCommit(url_a); + AddNewTab(url_a); + + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), url_a); + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + + geolocation_permission_context_->RequestGeolocationPermission( + process_id_for_tab(0), render_id_for_tab(0), bridge_id(), url_a); + EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); + + geolocation_permission_context_->RequestGeolocationPermission( + process_id_for_tab(0), render_id_for_tab(0), bridge_id() + 1, url_b); + EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); + + ConfirmInfoBarDelegate* removed_infobar = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + + // Accept the second tab. + ConfirmInfoBarDelegate* infobar_0 = + extra_tabs_[0]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_0); + infobar_0->Accept(); + CheckPermissionMessageSentForTab(0, bridge_id(), true); + extra_tabs_[0]->RemoveInfoBar(infobar_0); + EXPECT_EQ(2U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); + infobar_0->InfoBarClosed(); + // Now the infobar for the tab with the same origin should have gone. + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + CheckPermissionMessageSent(bridge_id(), true); + EXPECT_TRUE(closed_delegate_tracker_.Contains(removed_infobar)); + closed_delegate_tracker_.Clear(); + // Destroy the infobar that has just been removed. + removed_infobar->InfoBarClosed(); + + // And we should have the queued infobar displayed now. + EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); + + // Accept the second infobar. + ConfirmInfoBarDelegate* infobar_1 = + extra_tabs_[0]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_1); + infobar_1->Accept(); + CheckPermissionMessageSentForTab(0, bridge_id() + 1, true); + extra_tabs_[0]->RemoveInfoBar(infobar_1); + EXPECT_EQ(1U, closed_delegate_tracker_.size()); + EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_1)); + infobar_1->InfoBarClosed(); + + extra_tabs_.reset(); +} + +TEST_F(GeolocationPermissionContextTests, TabDestroyed) { + GURL requesting_frame_0("http://www.example.com/geolocation"); + GURL requesting_frame_1("http://www.example-2.com/geolocation"); + EXPECT_EQ( + CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_0, requesting_frame_0)); + EXPECT_EQ( + CONTENT_SETTING_ASK, + profile()->GetGeolocationContentSettingsMap()->GetContentSetting( + requesting_frame_1, requesting_frame_0)); + + NavigateAndCommit(requesting_frame_0); + EXPECT_EQ(0U, contents_wrapper()->infobar_count()); + // Request permission for two frames. + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id(), requesting_frame_0); + geolocation_permission_context_->RequestGeolocationPermission( + process_id(), render_id(), bridge_id() + 1, requesting_frame_1); + // Ensure only one infobar is created. + EXPECT_EQ(1U, contents_wrapper()->infobar_count()); + ConfirmInfoBarDelegate* infobar_0 = + contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_0); + string16 text_0 = infobar_0->GetMessageText(); + + // Delete the tab contents. + DeleteContents(); +} diff --git a/chrome/browser/geolocation/geolocation_permission_context.cc b/chrome/browser/geolocation/geolocation_permission_context.cc deleted file mode 100644 index 54ed24d..0000000 --- a/chrome/browser/geolocation/geolocation_permission_context.cc +++ /dev/null @@ -1,627 +0,0 @@ -// 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/geolocation/geolocation_permission_context.h" - -#include -#include -#include - -#include "base/utf_string_conversions.h" -#include "chrome/browser/content_settings/tab_specific_content_settings.h" -#include "chrome/browser/extensions/extension_service.h" -#include "chrome/browser/geolocation/geolocation_content_settings_map.h" -#include "chrome/browser/google/google_util.h" -#include "chrome/browser/prefs/pref_service.h" -#include "chrome/browser/profiles/profile.h" -#include "chrome/browser/tab_contents/confirm_infobar_delegate.h" -#include "chrome/browser/tab_contents/tab_util.h" -#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" -#include "chrome/common/extensions/extension.h" -#include "chrome/common/pref_names.h" -#include "content/browser/browser_thread.h" -#include "content/browser/geolocation/geolocation_provider.h" -#include "content/browser/renderer_host/render_process_host.h" -#include "content/browser/renderer_host/render_view_host.h" -#include "content/common/geolocation_messages.h" -#include "content/common/notification_registrar.h" -#include "content/common/notification_source.h" -#include "content/common/notification_type.h" -#include "grit/generated_resources.h" -#include "grit/locale_settings.h" -#include "grit/theme_resources.h" -#include "net/base/net_util.h" -#include "ui/base/l10n/l10n_util.h" -#include "ui/base/resource/resource_bundle.h" - -// GeolocationInfoBarQueueController ------------------------------------------ - -// This class controls the geolocation infobar queue per profile, and it's an -// internal class to GeolocationPermissionContext. -// An alternate approach would be to have this queue per tab, and use -// notifications to broadcast when permission is set / listen to notification to -// cancel pending requests. This may be specially useful if there are other -// things listening for such notifications. -// For the time being this class is self-contained and it doesn't seem pulling -// the notification infrastructure would simplify. -class GeolocationInfoBarQueueController : NotificationObserver { - public: - GeolocationInfoBarQueueController( - GeolocationPermissionContext* geolocation_permission_context, - Profile* profile); - ~GeolocationInfoBarQueueController(); - - // The InfoBar will be displayed immediately if the tab is not already - // displaying one, otherwise it'll be queued. - void CreateInfoBarRequest(int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - const GURL& emebedder); - - // Cancels a specific infobar request. - void CancelInfoBarRequest(int render_process_id, - int render_view_id, - int bridge_id); - - // Called by the InfoBarDelegate to notify it's closed. It'll display a new - // InfoBar if there's any request pending for this tab. - void OnInfoBarClosed(int render_process_id, - int render_view_id, - int bridge_id); - - // Called by the InfoBarDelegate to notify permission has been set. - // It'll notify and dismiss any other pending InfoBar request for the same - // |requesting_frame| and embedder. - void OnPermissionSet(int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - const GURL& embedder, - bool allowed); - - // NotificationObserver - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - private: - struct PendingInfoBarRequest; - class RequestEquals; - - typedef std::vector PendingInfoBarRequests; - - // Shows the first pending infobar for this tab. - void ShowQueuedInfoBar(int render_process_id, int render_view_id); - - // Cancels an InfoBar request and returns the next iterator position. - PendingInfoBarRequests::iterator CancelInfoBarRequestInternal( - PendingInfoBarRequests::iterator i); - - NotificationRegistrar registrar_; - - GeolocationPermissionContext* const geolocation_permission_context_; - Profile* const profile_; - PendingInfoBarRequests pending_infobar_requests_; -}; - - -// GeolocationConfirmInfoBarDelegate ------------------------------------------ - -namespace { - -class GeolocationConfirmInfoBarDelegate : public ConfirmInfoBarDelegate { - public: - GeolocationConfirmInfoBarDelegate( - TabContents* tab_contents, - GeolocationInfoBarQueueController* controller, - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame_url, - const std::string& display_languages); - - private: - virtual ~GeolocationConfirmInfoBarDelegate(); - - // ConfirmInfoBarDelegate: - virtual gfx::Image* GetIcon() const OVERRIDE; - virtual Type GetInfoBarType() const OVERRIDE; - virtual string16 GetMessageText() const OVERRIDE; - virtual string16 GetButtonLabel(InfoBarButton button) const OVERRIDE; - virtual bool Accept() OVERRIDE; - virtual bool Cancel() OVERRIDE; - virtual string16 GetLinkText() OVERRIDE; - virtual bool LinkClicked(WindowOpenDisposition disposition) OVERRIDE; - - TabContents* tab_contents_; - GeolocationInfoBarQueueController* controller_; - int render_process_id_; - int render_view_id_; - int bridge_id_; - GURL requesting_frame_url_; - std::string display_languages_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(GeolocationConfirmInfoBarDelegate); -}; - -GeolocationConfirmInfoBarDelegate::GeolocationConfirmInfoBarDelegate( - TabContents* tab_contents, - GeolocationInfoBarQueueController* controller, - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame_url, - const std::string& display_languages) - : ConfirmInfoBarDelegate(tab_contents), - tab_contents_(tab_contents), - controller_(controller), - render_process_id_(render_process_id), - render_view_id_(render_view_id), - bridge_id_(bridge_id), - requesting_frame_url_(requesting_frame_url), - display_languages_(display_languages) { -} - -GeolocationConfirmInfoBarDelegate::~GeolocationConfirmInfoBarDelegate() { - controller_->OnInfoBarClosed(render_process_id_, render_view_id_, - bridge_id_); -} - -gfx::Image* GeolocationConfirmInfoBarDelegate::GetIcon() const { - return &ResourceBundle::GetSharedInstance().GetNativeImageNamed( - IDR_GEOLOCATION_INFOBAR_ICON); -} - -InfoBarDelegate::Type - GeolocationConfirmInfoBarDelegate::GetInfoBarType() const { - return PAGE_ACTION_TYPE; -} - -string16 GeolocationConfirmInfoBarDelegate::GetMessageText() const { - return l10n_util::GetStringFUTF16(IDS_GEOLOCATION_INFOBAR_QUESTION, - net::FormatUrl(requesting_frame_url_.GetOrigin(), display_languages_)); -} - -string16 GeolocationConfirmInfoBarDelegate::GetButtonLabel( - InfoBarButton button) const { - return l10n_util::GetStringUTF16((button == BUTTON_OK) ? - IDS_GEOLOCATION_ALLOW_BUTTON : IDS_GEOLOCATION_DENY_BUTTON); -} - -bool GeolocationConfirmInfoBarDelegate::Accept() { - controller_->OnPermissionSet(render_process_id_, render_view_id_, bridge_id_, - requesting_frame_url_, tab_contents_->GetURL(), true); - return true; -} - -bool GeolocationConfirmInfoBarDelegate::Cancel() { - controller_->OnPermissionSet(render_process_id_, render_view_id_, bridge_id_, - requesting_frame_url_, tab_contents_->GetURL(), false); - return true; -} - -string16 GeolocationConfirmInfoBarDelegate::GetLinkText() { - return l10n_util::GetStringUTF16(IDS_LEARN_MORE); -} - -bool GeolocationConfirmInfoBarDelegate::LinkClicked( - WindowOpenDisposition disposition) { - const char kGeolocationLearnMoreUrl[] = -#if defined(OS_CHROMEOS) - "https://www.google.com/support/chromeos/bin/answer.py?answer=142065"; -#else - "https://www.google.com/support/chrome/bin/answer.py?answer=142065"; -#endif - - // Ignore the click disposition and always open in a new top level tab. - tab_contents_->OpenURL( - google_util::AppendGoogleLocaleParam(GURL(kGeolocationLearnMoreUrl)), - GURL(), NEW_FOREGROUND_TAB, PageTransition::LINK); - return false; // Do not dismiss the info bar. -} - -} // namespace - - -// GeolocationInfoBarQueueController::PendingInfoBarRequest ------------------- - -struct GeolocationInfoBarQueueController::PendingInfoBarRequest { - public: - PendingInfoBarRequest(int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - const GURL& embedder); - - bool IsForTab(int p_render_process_id, int p_render_view_id) const; - bool IsForPair(const GURL& p_requesting_frame, - const GURL& p_embedder) const; - bool Equals(int p_render_process_id, - int p_render_view_id, - int p_bridge_id) const; - - int render_process_id; - int render_view_id; - int bridge_id; - GURL requesting_frame; - GURL embedder; - InfoBarDelegate* infobar_delegate; -}; - -GeolocationInfoBarQueueController::PendingInfoBarRequest::PendingInfoBarRequest( - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - const GURL& embedder) - : render_process_id(render_process_id), - render_view_id(render_view_id), - bridge_id(bridge_id), - requesting_frame(requesting_frame), - embedder(embedder), - infobar_delegate(NULL) { -} - -bool GeolocationInfoBarQueueController::PendingInfoBarRequest::IsForTab( - int p_render_process_id, - int p_render_view_id) const { - return (render_process_id == p_render_process_id) && - (render_view_id == p_render_view_id); -} - -bool GeolocationInfoBarQueueController::PendingInfoBarRequest::IsForPair( - const GURL& p_requesting_frame, - const GURL& p_embedder) const { - return (requesting_frame == p_requesting_frame) && (embedder == p_embedder); -} - -bool GeolocationInfoBarQueueController::PendingInfoBarRequest::Equals( - int p_render_process_id, - int p_render_view_id, - int p_bridge_id) const { - return IsForTab(p_render_process_id, p_render_view_id) && - (bridge_id == p_bridge_id); -} - - -// GeolocationInfoBarQueueController::RequestEquals --------------------------- - -// Useful predicate for checking PendingInfoBarRequest equality. -class GeolocationInfoBarQueueController::RequestEquals - : public std::unary_function { - public: - RequestEquals(int render_process_id, int render_view_id, int bridge_id); - - bool operator()(const PendingInfoBarRequest& request) const; - - private: - int render_process_id_; - int render_view_id_; - int bridge_id_; -}; - -GeolocationInfoBarQueueController::RequestEquals::RequestEquals( - int render_process_id, - int render_view_id, - int bridge_id) - : render_process_id_(render_process_id), - render_view_id_(render_view_id), - bridge_id_(bridge_id) { -} - -bool GeolocationInfoBarQueueController::RequestEquals::operator()( - const PendingInfoBarRequest& request) const { - return request.Equals(render_process_id_, render_view_id_, bridge_id_); -} - - -// GeolocationInfoBarQueueController ------------------------------------------ - -GeolocationInfoBarQueueController::GeolocationInfoBarQueueController( - GeolocationPermissionContext* geolocation_permission_context, - Profile* profile) - : geolocation_permission_context_(geolocation_permission_context), - profile_(profile) { -} - -GeolocationInfoBarQueueController::~GeolocationInfoBarQueueController() { -} - -void GeolocationInfoBarQueueController::CreateInfoBarRequest( - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - const GURL& embedder) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // We shouldn't get duplicate requests. - DCHECK(std::find_if(pending_infobar_requests_.begin(), - pending_infobar_requests_.end(), - RequestEquals(render_process_id, render_view_id, bridge_id)) == - pending_infobar_requests_.end()); - - pending_infobar_requests_.push_back(PendingInfoBarRequest(render_process_id, - render_view_id, bridge_id, requesting_frame, embedder)); - ShowQueuedInfoBar(render_process_id, render_view_id); -} - -void GeolocationInfoBarQueueController::CancelInfoBarRequest( - int render_process_id, - int render_view_id, - int bridge_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - PendingInfoBarRequests::iterator i = std::find_if( - pending_infobar_requests_.begin(), pending_infobar_requests_.end(), - RequestEquals(render_process_id, render_view_id, bridge_id)); - // TODO(pkasting): Can this conditional become a DCHECK()? - if (i != pending_infobar_requests_.end()) - CancelInfoBarRequestInternal(i); -} - -void GeolocationInfoBarQueueController::OnInfoBarClosed(int render_process_id, - int render_view_id, - int bridge_id) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - PendingInfoBarRequests::iterator i = std::find_if( - pending_infobar_requests_.begin(), pending_infobar_requests_.end(), - RequestEquals(render_process_id, render_view_id, bridge_id)); - if (i != pending_infobar_requests_.end()) - pending_infobar_requests_.erase(i); - - ShowQueuedInfoBar(render_process_id, render_view_id); -} - -void GeolocationInfoBarQueueController::OnPermissionSet( - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - const GURL& embedder, - bool allowed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - ContentSetting content_setting = - allowed ? CONTENT_SETTING_ALLOW : CONTENT_SETTING_BLOCK; - profile_->GetGeolocationContentSettingsMap()->SetContentSetting( - requesting_frame.GetOrigin(), embedder.GetOrigin(), content_setting); - - for (PendingInfoBarRequests::iterator i = pending_infobar_requests_.begin(); - i != pending_infobar_requests_.end(); ) { - if (i->IsForPair(requesting_frame, embedder)) { - // Cancel this request first, then notify listeners. TODO(pkasting): Why - // is this order important? - // NOTE: If the pending request had an infobar, TabContents will close it - // either synchronously or asynchronously, which will then pump the queue - // via OnInfoBarClosed(). - PendingInfoBarRequest copied_request = *i; - // Don't let CancelInfoBarRequestInternal() call RemoveInfoBar() on the - // delegate that's currently calling us. That delegate is in either - // Accept() or Cancel(), so its owning InfoBar will call RemoveInfoBar() - // later on in this callstack anyway; and if we do it here, and it causes - // the delegate to be deleted, our GURL& args will point to garbage and we - // may also cause other problems during stack unwinding. - if (i->Equals(render_process_id, render_view_id, bridge_id)) - i->infobar_delegate = NULL; - i = CancelInfoBarRequestInternal(i); - - geolocation_permission_context_->NotifyPermissionSet( - copied_request.render_process_id, copied_request.render_view_id, - copied_request.bridge_id, copied_request.requesting_frame, allowed); - } else { - ++i; - } - } -} - -void GeolocationInfoBarQueueController::Observe( - NotificationType type, const NotificationSource& source, - const NotificationDetails& details) { - registrar_.Remove(this, NotificationType::TAB_CONTENTS_DESTROYED, - source); - TabContents* tab_contents = Source(source).ptr(); - for (PendingInfoBarRequests::iterator i = pending_infobar_requests_.begin(); - i != pending_infobar_requests_.end();) { - if (i->infobar_delegate == NULL && - tab_contents == tab_util::GetTabContentsByID(i->render_process_id, - i->render_view_id)) { - i = pending_infobar_requests_.erase(i); - } else { - ++i; - } - } -} - -void GeolocationInfoBarQueueController::ShowQueuedInfoBar(int render_process_id, - int render_view_id) { - TabContents* tab_contents = - tab_util::GetTabContentsByID(render_process_id, render_view_id); - TabContentsWrapper* wrapper = NULL; - if (tab_contents) - wrapper = TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); - for (PendingInfoBarRequests::iterator i = pending_infobar_requests_.begin(); - i != pending_infobar_requests_.end(); ) { - if (i->IsForTab(render_process_id, render_view_id)) { - if (!wrapper) { - i = pending_infobar_requests_.erase(i); - continue; - } - - if (!i->infobar_delegate) { - if (!registrar_.IsRegistered(this, - NotificationType::TAB_CONTENTS_DESTROYED, - Source(tab_contents))) { - registrar_.Add(this, NotificationType::TAB_CONTENTS_DESTROYED, - Source(tab_contents)); - } - i->infobar_delegate = new GeolocationConfirmInfoBarDelegate( - tab_contents, this, render_process_id, render_view_id, i->bridge_id, - i->requesting_frame, - profile_->GetPrefs()->GetString(prefs::kAcceptLanguages)); - wrapper->AddInfoBar(i->infobar_delegate); - } - break; - } - ++i; - } -} - -GeolocationInfoBarQueueController::PendingInfoBarRequests::iterator - GeolocationInfoBarQueueController::CancelInfoBarRequestInternal( - PendingInfoBarRequests::iterator i) { - InfoBarDelegate* delegate = i->infobar_delegate; - if (!delegate) - return pending_infobar_requests_.erase(i); - - TabContents* tab_contents = - tab_util::GetTabContentsByID(i->render_process_id, i->render_view_id); - if (!tab_contents) - return pending_infobar_requests_.erase(i); - - // TabContents will destroy the InfoBar, which will remove from our vector - // asynchronously. - TabContentsWrapper* wrapper = - TabContentsWrapper::GetCurrentWrapperForContents(tab_contents); - wrapper->RemoveInfoBar(i->infobar_delegate); - return ++i; -} - - -// GeolocationPermissionContext ----------------------------------------------- - -GeolocationPermissionContext::GeolocationPermissionContext( - Profile* profile) - : profile_(profile), - ALLOW_THIS_IN_INITIALIZER_LIST(geolocation_infobar_queue_controller_( - new GeolocationInfoBarQueueController(this, profile))) { -} - -GeolocationPermissionContext::~GeolocationPermissionContext() { -} - -void GeolocationPermissionContext::RequestGeolocationPermission( - int render_process_id, int render_view_id, int bridge_id, - const GURL& requesting_frame) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod( - this, &GeolocationPermissionContext::RequestGeolocationPermission, - render_process_id, render_view_id, bridge_id, requesting_frame)); - return; - } - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - ExtensionService* extensions = profile_->GetExtensionService(); - if (extensions) { - const Extension* ext = extensions->GetExtensionByURL(requesting_frame); - if (!ext) - ext = extensions->GetExtensionByWebExtent(requesting_frame); - if (ext && ext->HasApiPermission(Extension::kGeolocationPermission)) { - ExtensionProcessManager* epm = profile_->GetExtensionProcessManager(); - RenderProcessHost* process = epm->GetExtensionProcess(requesting_frame); - if (process && process->id() == render_process_id) { - NotifyPermissionSet(render_process_id, render_view_id, bridge_id, - requesting_frame, true); - return; - } - } - } - - TabContents* tab_contents = - tab_util::GetTabContentsByID(render_process_id, render_view_id); - if (!tab_contents) { - // The tab may have gone away, or the request may not be from a tab at all. - LOG(WARNING) << "Attempt to use geolocation tabless renderer: " - << render_process_id << "," << render_view_id << "," - << bridge_id << " (can't prompt user without a visible tab)"; - NotifyPermissionSet(render_process_id, render_view_id, bridge_id, - requesting_frame, false); - return; - } - - GURL embedder = tab_contents->GetURL(); - if (!requesting_frame.is_valid() || !embedder.is_valid()) { - LOG(WARNING) << "Attempt to use geolocation from an invalid URL: " - << requesting_frame << "," << embedder - << " (geolocation is not supported in popups)"; - NotifyPermissionSet(render_process_id, render_view_id, bridge_id, - requesting_frame, false); - return; - } - - ContentSetting content_setting = - profile_->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame, embedder); - if (content_setting == CONTENT_SETTING_BLOCK) { - NotifyPermissionSet(render_process_id, render_view_id, bridge_id, - requesting_frame, false); - } else if (content_setting == CONTENT_SETTING_ALLOW) { - NotifyPermissionSet(render_process_id, render_view_id, bridge_id, - requesting_frame, true); - } else { // setting == ask. Prompt the user. - geolocation_infobar_queue_controller_->CreateInfoBarRequest( - render_process_id, render_view_id, bridge_id, requesting_frame, - embedder); - } -} - -void GeolocationPermissionContext::CancelGeolocationPermissionRequest( - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame) { - CancelPendingInfoBarRequest(render_process_id, render_view_id, bridge_id); -} - -void GeolocationPermissionContext::NotifyPermissionSet( - int render_process_id, - int render_view_id, - int bridge_id, - const GURL& requesting_frame, - bool allowed) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - - // TabContents may have gone away (or not exists for extension). - TabSpecificContentSettings* content_settings = - TabSpecificContentSettings::Get(render_process_id, render_view_id); - if (content_settings) { - content_settings->OnGeolocationPermissionSet(requesting_frame.GetOrigin(), - allowed); - } - - RenderViewHost* r = RenderViewHost::FromID(render_process_id, render_view_id); - if (r) { - r->Send(new GeolocationMsg_PermissionSet( - render_view_id, bridge_id, allowed)); - } - - if (allowed) { - BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, NewRunnableMethod( - this, &GeolocationPermissionContext::NotifyArbitratorPermissionGranted, - requesting_frame)); - } -} - -void GeolocationPermissionContext::NotifyArbitratorPermissionGranted( - const GURL& requesting_frame) { - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); - GeolocationProvider::GetInstance()->OnPermissionGranted(requesting_frame); -} - -void GeolocationPermissionContext::CancelPendingInfoBarRequest( - int render_process_id, - int render_view_id, - int bridge_id) { - if (!BrowserThread::CurrentlyOn(BrowserThread::UI)) { - BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, NewRunnableMethod( - this, &GeolocationPermissionContext::CancelPendingInfoBarRequest, - render_process_id, render_view_id, bridge_id)); - return; - } - DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); - geolocation_infobar_queue_controller_->CancelInfoBarRequest(render_process_id, - render_view_id, bridge_id); -} diff --git a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc deleted file mode 100644 index 7d7ac53..0000000 --- a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc +++ /dev/null @@ -1,476 +0,0 @@ -// 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 - -#include "base/memory/scoped_vector.h" -#include "chrome/browser/content_settings/tab_specific_content_settings.h" -#include "chrome/browser/geolocation/geolocation_content_settings_map.h" -#include "chrome/browser/tab_contents/confirm_infobar_delegate.h" -#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h" -#include "chrome/browser/ui/tab_contents/test_tab_contents_wrapper.h" -#include "chrome/test/testing_profile.h" -#include "content/browser/browser_thread.h" -#include "content/browser/geolocation/arbitrator_dependency_factories_for_test.h" -#include "content/browser/geolocation/geolocation_permission_context.h" -#include "content/browser/geolocation/location_arbitrator.h" -#include "content/browser/geolocation/location_provider.h" -#include "content/browser/geolocation/mock_location_provider.h" -#include "content/browser/renderer_host/mock_render_process_host.h" -#include "content/browser/tab_contents/test_tab_contents.h" -#include "content/common/geolocation_messages.h" -#include "content/common/notification_registrar.h" -#include "content/common/notification_service.h" -#include "testing/gtest/include/gtest/gtest.h" - -// ClosedDelegateTracker ------------------------------------------------------ - -namespace { - -// We need to track which infobars were closed. -class ClosedDelegateTracker : public NotificationObserver { - public: - ClosedDelegateTracker(); - virtual ~ClosedDelegateTracker(); - - // NotificationObserver: - virtual void Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details); - - size_t size() const { - return removed_infobar_delegates_.size(); - } - - bool Contains(InfoBarDelegate* delegate) const; - void Clear(); - - private: - NotificationRegistrar registrar_; - std::set removed_infobar_delegates_; -}; - -ClosedDelegateTracker::ClosedDelegateTracker() { - registrar_.Add(this, NotificationType::TAB_CONTENTS_INFOBAR_REMOVED, - NotificationService::AllSources()); -} - -ClosedDelegateTracker::~ClosedDelegateTracker() { -} - -void ClosedDelegateTracker::Observe(NotificationType type, - const NotificationSource& source, - const NotificationDetails& details) { - DCHECK(type.value == NotificationType::TAB_CONTENTS_INFOBAR_REMOVED); - removed_infobar_delegates_.insert( - Details >(details)->first); -} - -bool ClosedDelegateTracker::Contains(InfoBarDelegate* delegate) const { - return removed_infobar_delegates_.count(delegate) != 0; -} - -void ClosedDelegateTracker::Clear() { - removed_infobar_delegates_.clear(); -} - -} // namespace - - -// GeolocationPermissionContextTests ------------------------------------------ - -// This class sets up GeolocationArbitrator. -class GeolocationPermissionContextTests : public TabContentsWrapperTestHarness { - public: - GeolocationPermissionContextTests(); - - protected: - virtual ~GeolocationPermissionContextTests(); - - int process_id() { return contents()->render_view_host()->process()->id(); } - int process_id_for_tab(int tab) { - return extra_tabs_[tab]->tab_contents()->render_view_host()->process()-> - id(); - } - int render_id() { return contents()->render_view_host()->routing_id(); } - int render_id_for_tab(int tab) { - return extra_tabs_[tab]->tab_contents()->render_view_host()->routing_id(); - } - int bridge_id() const { return 42; } // Not relevant at this level. - - void CheckPermissionMessageSent(int bridge_id, bool allowed); - void CheckPermissionMessageSentForTab(int tab, int bridge_id, bool allowed); - void CheckPermissionMessageSentInternal(MockRenderProcessHost* process, - int bridge_id, - bool allowed); - void AddNewTab(const GURL& url); - void CheckTabContentsState(const GURL& requesting_frame, - ContentSetting expected_content_setting); - - scoped_refptr geolocation_permission_context_; - ClosedDelegateTracker closed_delegate_tracker_; - ScopedVector extra_tabs_; - - private: - // TabContentsWrapperTestHarness: - virtual void SetUp(); - virtual void TearDown(); - - BrowserThread ui_thread_; - scoped_refptr dependency_factory_; -}; - -GeolocationPermissionContextTests::GeolocationPermissionContextTests() - : TabContentsWrapperTestHarness(), - ui_thread_(BrowserThread::UI, MessageLoop::current()), - dependency_factory_( - new GeolocationArbitratorDependencyFactoryWithLocationProvider( - &NewAutoSuccessMockNetworkLocationProvider)) { -} - -GeolocationPermissionContextTests::~GeolocationPermissionContextTests() { -} - -void GeolocationPermissionContextTests::CheckPermissionMessageSent( - int bridge_id, - bool allowed) { - CheckPermissionMessageSentInternal(process(), bridge_id, allowed); -} - -void GeolocationPermissionContextTests::CheckPermissionMessageSentForTab( - int tab, - int bridge_id, - bool allowed) { - CheckPermissionMessageSentInternal(static_cast( - extra_tabs_[tab]->tab_contents()->render_view_host()->process()), - bridge_id, allowed); -} - -void GeolocationPermissionContextTests::CheckPermissionMessageSentInternal( - MockRenderProcessHost* process, - int bridge_id, - bool allowed) { - MessageLoop::current()->PostTask(FROM_HERE, new MessageLoop::QuitTask()); - MessageLoop::current()->Run(); - const IPC::Message* message = process->sink().GetFirstMessageMatching( - GeolocationMsg_PermissionSet::ID); - ASSERT_TRUE(message); - GeolocationMsg_PermissionSet::Param param; - GeolocationMsg_PermissionSet::Read(message, ¶m); - EXPECT_EQ(bridge_id, param.a); - EXPECT_EQ(allowed, param.b); - process->sink().ClearMessages(); -} - -void GeolocationPermissionContextTests::AddNewTab(const GURL& url) { - TabContents* new_tab = - new TabContents(profile(), NULL, MSG_ROUTING_NONE, NULL, NULL); - new_tab->controller().LoadURL(url, GURL(), PageTransition::TYPED); - static_cast(new_tab->render_manager()->current_host())-> - SendNavigate(extra_tabs_.size() + 1, url); - extra_tabs_.push_back(new TabContentsWrapper(new_tab)); -} - -void GeolocationPermissionContextTests::CheckTabContentsState( - const GURL& requesting_frame, - ContentSetting expected_content_setting) { - TabSpecificContentSettings* content_settings = - contents_wrapper()->content_settings(); - const GeolocationSettingsState::StateMap& state_map = - content_settings->geolocation_settings_state().state_map(); - EXPECT_EQ(1U, state_map.count(requesting_frame.GetOrigin())); - EXPECT_EQ(0U, state_map.count(requesting_frame)); - GeolocationSettingsState::StateMap::const_iterator settings = - state_map.find(requesting_frame.GetOrigin()); - ASSERT_FALSE(settings == state_map.end()) - << "geolocation state not found " << requesting_frame; - EXPECT_EQ(expected_content_setting, settings->second); -} - -void GeolocationPermissionContextTests::SetUp() { - TabContentsWrapperTestHarness::SetUp(); - GeolocationArbitrator::SetDependencyFactoryForTest( - dependency_factory_.get()); - geolocation_permission_context_ = - new GeolocationPermissionContext(profile()); -} - -void GeolocationPermissionContextTests::TearDown() { - GeolocationArbitrator::SetDependencyFactoryForTest(NULL); - TabContentsWrapperTestHarness::TearDown(); -} - - -// Tests ---------------------------------------------------------------------- - -TEST_F(GeolocationPermissionContextTests, SinglePermission) { - GURL requesting_frame("http://www.example.com/geolocation"); - NavigateAndCommit(requesting_frame); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), requesting_frame); - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); -} - -TEST_F(GeolocationPermissionContextTests, QueuedPermission) { - GURL requesting_frame_0("http://www.example.com/geolocation"); - GURL requesting_frame_1("http://www.example-2.com/geolocation"); - EXPECT_EQ(CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_0, requesting_frame_0)); - EXPECT_EQ(CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_1, requesting_frame_0)); - - NavigateAndCommit(requesting_frame_0); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - // Request permission for two frames. - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), requesting_frame_0); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id() + 1, requesting_frame_1); - // Ensure only one infobar is created. - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - ConfirmInfoBarDelegate* infobar_0 = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_0); - string16 text_0 = infobar_0->GetMessageText(); - - // Accept the first frame. - infobar_0->Accept(); - CheckTabContentsState(requesting_frame_0, CONTENT_SETTING_ALLOW); - CheckPermissionMessageSent(bridge_id(), true); - - contents_wrapper()->RemoveInfoBar(infobar_0); - EXPECT_EQ(1U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); - closed_delegate_tracker_.Clear(); - infobar_0->InfoBarClosed(); - // Now we should have a new infobar for the second frame. - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - - ConfirmInfoBarDelegate* infobar_1 = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_1); - string16 text_1 = infobar_1->GetMessageText(); - EXPECT_NE(text_0, text_1); - - // Cancel (block) this frame. - infobar_1->Cancel(); - CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_BLOCK); - CheckPermissionMessageSent(bridge_id() + 1, false); - contents_wrapper()->RemoveInfoBar(infobar_1); - EXPECT_EQ(1U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_1)); - infobar_1->InfoBarClosed(); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - // Ensure the persisted permissions are ok. - EXPECT_EQ(CONTENT_SETTING_ALLOW, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_0, requesting_frame_0)); - EXPECT_EQ(CONTENT_SETTING_BLOCK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_1, requesting_frame_0)); -} - -TEST_F(GeolocationPermissionContextTests, CancelGeolocationPermissionRequest) { - GURL requesting_frame_0("http://www.example.com/geolocation"); - GURL requesting_frame_1("http://www.example-2.com/geolocation"); - EXPECT_EQ(CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_0, requesting_frame_0)); - EXPECT_EQ(CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_1, requesting_frame_0)); - - NavigateAndCommit(requesting_frame_0); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - // Request permission for two frames. - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), requesting_frame_0); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id() + 1, requesting_frame_1); - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - - ConfirmInfoBarDelegate* infobar_0 = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_0); - string16 text_0 = infobar_0->GetMessageText(); - - // Simulate the frame going away, ensure the infobar for this frame - // is removed and the next pending infobar is created. - geolocation_permission_context_->CancelGeolocationPermissionRequest( - process_id(), render_id(), bridge_id(), requesting_frame_0); - EXPECT_EQ(1U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); - closed_delegate_tracker_.Clear(); - infobar_0->InfoBarClosed(); - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - - ConfirmInfoBarDelegate* infobar_1 = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_1); - string16 text_1 = infobar_1->GetMessageText(); - EXPECT_NE(text_0, text_1); - - // Allow this frame. - infobar_1->Accept(); - CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_ALLOW); - CheckPermissionMessageSent(bridge_id() + 1, true); - contents_wrapper()->RemoveInfoBar(infobar_1); - EXPECT_EQ(1U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_1)); - infobar_1->InfoBarClosed(); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - // Ensure the persisted permissions are ok. - EXPECT_EQ(CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_0, requesting_frame_0)); - EXPECT_EQ(CONTENT_SETTING_ALLOW, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_1, requesting_frame_0)); -} - -TEST_F(GeolocationPermissionContextTests, InvalidURL) { - GURL invalid_embedder; - GURL requesting_frame("about:blank"); - NavigateAndCommit(invalid_embedder); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), requesting_frame); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - CheckPermissionMessageSent(bridge_id(), false); -} - -TEST_F(GeolocationPermissionContextTests, SameOriginMultipleTabs) { - GURL url_a("http://www.example.com/geolocation"); - GURL url_b("http://www.example-2.com/geolocation"); - NavigateAndCommit(url_a); - AddNewTab(url_b); - AddNewTab(url_a); - - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), url_a); - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - - geolocation_permission_context_->RequestGeolocationPermission( - process_id_for_tab(0), render_id_for_tab(0), bridge_id(), url_b); - EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); - - geolocation_permission_context_->RequestGeolocationPermission( - process_id_for_tab(1), render_id_for_tab(1), bridge_id(), url_a); - EXPECT_EQ(1U, extra_tabs_[1]->infobar_count()); - - ConfirmInfoBarDelegate* removed_infobar = - extra_tabs_[1]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - - // Accept the first tab. - ConfirmInfoBarDelegate* infobar_0 = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_0); - infobar_0->Accept(); - CheckPermissionMessageSent(bridge_id(), true); - contents_wrapper()->RemoveInfoBar(infobar_0); - EXPECT_EQ(2U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); - infobar_0->InfoBarClosed(); - // Now the infobar for the tab with the same origin should have gone. - EXPECT_EQ(0U, extra_tabs_[1]->infobar_count()); - CheckPermissionMessageSentForTab(1, bridge_id(), true); - EXPECT_TRUE(closed_delegate_tracker_.Contains(removed_infobar)); - // Destroy the infobar that has just been removed. - removed_infobar->InfoBarClosed(); - - // But the other tab should still have the info bar... - EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); - extra_tabs_.reset(); -} - -TEST_F(GeolocationPermissionContextTests, QueuedOriginMultipleTabs) { - GURL url_a("http://www.example.com/geolocation"); - GURL url_b("http://www.example-2.com/geolocation"); - NavigateAndCommit(url_a); - AddNewTab(url_a); - - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), url_a); - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - - geolocation_permission_context_->RequestGeolocationPermission( - process_id_for_tab(0), render_id_for_tab(0), bridge_id(), url_a); - EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); - - geolocation_permission_context_->RequestGeolocationPermission( - process_id_for_tab(0), render_id_for_tab(0), bridge_id() + 1, url_b); - EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); - - ConfirmInfoBarDelegate* removed_infobar = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - - // Accept the second tab. - ConfirmInfoBarDelegate* infobar_0 = - extra_tabs_[0]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_0); - infobar_0->Accept(); - CheckPermissionMessageSentForTab(0, bridge_id(), true); - extra_tabs_[0]->RemoveInfoBar(infobar_0); - EXPECT_EQ(2U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_0)); - infobar_0->InfoBarClosed(); - // Now the infobar for the tab with the same origin should have gone. - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - CheckPermissionMessageSent(bridge_id(), true); - EXPECT_TRUE(closed_delegate_tracker_.Contains(removed_infobar)); - closed_delegate_tracker_.Clear(); - // Destroy the infobar that has just been removed. - removed_infobar->InfoBarClosed(); - - // And we should have the queued infobar displayed now. - EXPECT_EQ(1U, extra_tabs_[0]->infobar_count()); - - // Accept the second infobar. - ConfirmInfoBarDelegate* infobar_1 = - extra_tabs_[0]->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_1); - infobar_1->Accept(); - CheckPermissionMessageSentForTab(0, bridge_id() + 1, true); - extra_tabs_[0]->RemoveInfoBar(infobar_1); - EXPECT_EQ(1U, closed_delegate_tracker_.size()); - EXPECT_TRUE(closed_delegate_tracker_.Contains(infobar_1)); - infobar_1->InfoBarClosed(); - - extra_tabs_.reset(); -} - -TEST_F(GeolocationPermissionContextTests, TabDestroyed) { - GURL requesting_frame_0("http://www.example.com/geolocation"); - GURL requesting_frame_1("http://www.example-2.com/geolocation"); - EXPECT_EQ( - CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_0, requesting_frame_0)); - EXPECT_EQ( - CONTENT_SETTING_ASK, - profile()->GetGeolocationContentSettingsMap()->GetContentSetting( - requesting_frame_1, requesting_frame_0)); - - NavigateAndCommit(requesting_frame_0); - EXPECT_EQ(0U, contents_wrapper()->infobar_count()); - // Request permission for two frames. - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id(), requesting_frame_0); - geolocation_permission_context_->RequestGeolocationPermission( - process_id(), render_id(), bridge_id() + 1, requesting_frame_1); - // Ensure only one infobar is created. - EXPECT_EQ(1U, contents_wrapper()->infobar_count()); - ConfirmInfoBarDelegate* infobar_0 = - contents_wrapper()->GetInfoBarDelegateAt(0)->AsConfirmInfoBarDelegate(); - ASSERT_TRUE(infobar_0); - string16 text_0 = infobar_0->GetMessageText(); - - // Delete the tab contents. - DeleteContents(); -} diff --git a/chrome/browser/profiles/profile_impl.cc b/chrome/browser/profiles/profile_impl.cc index 179ec61..8718711 100644 --- a/chrome/browser/profiles/profile_impl.cc +++ b/chrome/browser/profiles/profile_impl.cc @@ -37,6 +37,7 @@ #include "chrome/browser/extensions/extension_special_storage_policy.h" #include "chrome/browser/extensions/user_script_master.h" #include "chrome/browser/favicon/favicon_service.h" +#include "chrome/browser/geolocation/chrome_geolocation_permission_context.h" #include "chrome/browser/geolocation/geolocation_content_settings_map.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/top_sites.h" @@ -89,7 +90,6 @@ #include "content/browser/browser_thread.h" #include "content/browser/chrome_blob_storage_context.h" #include "content/browser/file_system/browser_file_system_helper.h" -#include "content/browser/geolocation/geolocation_permission_context.h" #include "content/browser/host_zoom_map.h" #include "content/browser/in_process_webkit/webkit_context.h" #include "content/browser/renderer_host/render_process_host.h" @@ -990,7 +990,8 @@ GeolocationContentSettingsMap* ProfileImpl::GetGeolocationContentSettingsMap() { GeolocationPermissionContext* ProfileImpl::GetGeolocationPermissionContext() { if (!geolocation_permission_context_.get()) - geolocation_permission_context_ = new GeolocationPermissionContext(this); + geolocation_permission_context_ = + new ChromeGeolocationPermissionContext(this); return geolocation_permission_context_.get(); } diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi index 5c6cec9..b76b461 100644 --- a/chrome/chrome_browser.gypi +++ b/chrome/chrome_browser.gypi @@ -1147,7 +1147,8 @@ 'browser/geolocation/geolocation_content_settings_map.h', 'browser/geolocation/geolocation_exceptions_table_model.cc', 'browser/geolocation/geolocation_exceptions_table_model.h', - 'browser/geolocation/geolocation_permission_context.cc', + 'browser/geolocation/chrome_geolocation_permission_context.cc', + 'browser/geolocation/chrome_geolocation_permission_contexth', 'browser/geolocation/geolocation_prefs.cc', 'browser/geolocation/geolocation_prefs.h', 'browser/geolocation/geolocation_settings_state.cc', diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi index 2c0a9c9..a3630f1 100644 --- a/chrome/chrome_tests.gypi +++ b/chrome/chrome_tests.gypi @@ -1419,7 +1419,7 @@ 'browser/first_run/first_run_unittest.cc', 'browser/geolocation/geolocation_content_settings_map_unittest.cc', 'browser/geolocation/geolocation_exceptions_table_model_unittest.cc', - 'browser/geolocation/geolocation_permission_context_unittest.cc', + 'browser/geolocation/chrome_geolocation_permission_context_unittest.cc', 'browser/geolocation/geolocation_settings_state_unittest.cc', 'browser/geolocation/wifi_data_provider_unittest_chromeos.cc', 'browser/global_keyboard_shortcuts_mac_unittest.mm', diff --git a/chrome/test/testing_profile.cc b/chrome/test/testing_profile.cc index 08dd691..d2249e6 100644 --- a/chrome/test/testing_profile.cc +++ b/chrome/test/testing_profile.cc @@ -22,6 +22,7 @@ #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/extension_special_storage_policy.h" #include "chrome/browser/favicon/favicon_service.h" +#include "chrome/browser/geolocation/chrome_geolocation_permission_context.h" #include "chrome/browser/geolocation/geolocation_content_settings_map.h" #include "chrome/browser/history/history.h" #include "chrome/browser/history/history_backend.h" @@ -50,7 +51,6 @@ #include "chrome/test/testing_pref_service.h" #include "chrome/test/ui_test_utils.h" #include "content/browser/browser_thread.h" -#include "content/browser/geolocation/geolocation_permission_context.h" #include "content/browser/in_process_webkit/webkit_context.h" #include "content/browser/mock_resource_context.h" #include "content/common/notification_service.h" @@ -628,7 +628,7 @@ GeolocationPermissionContext* TestingProfile::GetGeolocationPermissionContext() { if (!geolocation_permission_context_.get()) { geolocation_permission_context_ = - new GeolocationPermissionContext(this); + new ChromeGeolocationPermissionContext(this); } return geolocation_permission_context_.get(); } -- cgit v1.1