diff options
author | dhnishi@chromium.org <dhnishi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 22:30:20 +0000 |
---|---|---|
committer | dhnishi@chromium.org <dhnishi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-07-17 22:30:20 +0000 |
commit | bfcf45666488555a142ba159c1557b62f8b886a5 (patch) | |
tree | 0cd0d027090b56057091b4b6eff33d3eaa046cb5 | |
parent | f313245afd6b6568fc7456c8ee71d4dd187f8214 (diff) | |
download | chromium_src-bfcf45666488555a142ba159c1557b62f8b886a5.zip chromium_src-bfcf45666488555a142ba159c1557b62f8b886a5.tar.gz chromium_src-bfcf45666488555a142ba159c1557b62f8b886a5.tar.bz2 |
Audit the last time the Geolocation and Notification content settings
have been used.
This will be used as part of a resource/permission manager which will allow users to more easily see and modify which permissions are being granted to which websites.
Design Doc: https://docs.google.com/document/d/1oQwmj3AU4QYhTyGrYEGr6zaZhHUfx-wqUgEcQGbUU-U/edit?usp=sharing
BUG=372607
Review URL: https://codereview.chromium.org/356543003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@283909 0039d316-1c4b-4281-b951-d872f2087c98
14 files changed, 548 insertions, 35 deletions
diff --git a/chrome/browser/chrome_content_browser_client.cc b/chrome/browser/chrome_content_browser_client.cc index b0716a9..53a1881 100644 --- a/chrome/browser/chrome_content_browser_client.cc +++ b/chrome/browser/chrome_content_browser_client.cc @@ -119,6 +119,7 @@ #include "content/public/browser/web_contents.h" #include "content/public/common/child_process_host.h" #include "content/public/common/content_descriptors.h" +#include "content/public/common/show_desktop_notification_params.h" #include "content/public/common/url_utils.h" #include "content/public/common/web_preferences.h" #include "extensions/browser/extension_host.h" @@ -2193,6 +2194,9 @@ void ChromeContentBrowserClient::ShowDesktopNotification( DesktopNotificationServiceFactory::GetForProfile(profile); service->ShowDesktopNotification( params, render_frame_host, delegate, cancel_callback); + + profile->GetHostContentSettingsMap()->UpdateLastUsage( + params.origin, params.origin, CONTENT_SETTINGS_TYPE_NOTIFICATIONS); #else NOTIMPLEMENTED(); #endif @@ -2230,6 +2234,16 @@ void ChromeContentBrowserClient::RequestMidiSysExPermission( user_gesture, result_callback); } +void ChromeContentBrowserClient::DidUseGeolocationPermission( + content::WebContents* web_contents, + const GURL& frame_url, + const GURL& main_frame_url) { + Profile::FromBrowserContext(web_contents->GetBrowserContext()) + ->GetHostContentSettingsMap() + ->UpdateLastUsage( + frame_url, main_frame_url, CONTENT_SETTINGS_TYPE_GEOLOCATION); +} + void ChromeContentBrowserClient::RequestProtectedMediaIdentifierPermission( content::WebContents* web_contents, const GURL& origin, diff --git a/chrome/browser/chrome_content_browser_client.h b/chrome/browser/chrome_content_browser_client.h index 1b9b1e8..ae1828a 100644 --- a/chrome/browser/chrome_content_browser_client.h +++ b/chrome/browser/chrome_content_browser_client.h @@ -12,13 +12,10 @@ #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" +#include "base/memory/scoped_ptr.h" #include "chrome/common/chrome_version_info.h" #include "content/public/browser/content_browser_client.h" -#if defined(OS_ANDROID) -#include "base/memory/scoped_ptr.h" -#endif - namespace base { class CommandLine; } @@ -213,6 +210,9 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { bool user_gesture, base::Callback<void(bool)> result_callback, base::Closure* cancel_callback) OVERRIDE; + virtual void DidUseGeolocationPermission(content::WebContents* web_contents, + const GURL& frame_url, + const GURL& main_frame_url) OVERRIDE; virtual void RequestProtectedMediaIdentifierPermission( content::WebContents* web_contents, const GURL& origin, @@ -295,6 +295,8 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { #endif private: + friend class DisableWebRtcEncryptionFlagTest; + #if defined(ENABLE_WEBRTC) // Copies disable WebRTC encryption switch depending on the channel. static void MaybeCopyDisableWebRtcEncryptionSwitch( @@ -304,24 +306,24 @@ class ChromeContentBrowserClient : public content::ContentBrowserClient { #endif void FileSystemAccessed( - const GURL& url, - const std::vector<std::pair<int, int> >& render_frames, - base::Callback<void(bool)> callback, - bool allow); + const GURL& url, + const std::vector<std::pair<int, int> >& render_frames, + base::Callback<void(bool)> callback, + bool allow); #if defined(ENABLE_EXTENSIONS) -void GuestPermissionRequestHelper( - const GURL& url, - const std::vector<std::pair<int, int> >& render_frames, - base::Callback<void(bool)> callback, - bool allow); + void GuestPermissionRequestHelper( + const GURL& url, + const std::vector<std::pair<int, int> >& render_frames, + base::Callback<void(bool)> callback, + bool allow); -static void RequestFileSystemPermissionOnUIThread( - int render_process_id, - int render_frame_id, - const GURL& url, - bool allowed_by_default, - const base::Callback<void(bool)>& callback); + static void RequestFileSystemPermissionOnUIThread( + int render_process_id, + int render_frame_id, + const GURL& url, + bool allowed_by_default, + const base::Callback<void(bool)>& callback); #endif #if defined(ENABLE_PLUGINS) @@ -344,9 +346,7 @@ static void RequestFileSystemPermissionOnUIThread( // created. It is used only the IO thread. prerender::PrerenderTracker* prerender_tracker_; - base::WeakPtrFactory<ChromeContentBrowserClient> weak_factory_; - - friend class DisableWebRtcEncryptionFlagTest; + base::WeakPtrFactory<ChromeContentBrowserClient> weak_factory_; DISALLOW_COPY_AND_ASSIGN(ChromeContentBrowserClient); }; diff --git a/chrome/browser/content_settings/content_settings_pref_provider.cc b/chrome/browser/content_settings/content_settings_pref_provider.cc index 0eaf685..fd5cf24 100644 --- a/chrome/browser/content_settings/content_settings_pref_provider.cc +++ b/chrome/browser/content_settings/content_settings_pref_provider.cc @@ -14,6 +14,8 @@ #include "base/metrics/histogram.h" #include "base/prefs/pref_service.h" #include "base/prefs/scoped_user_pref_update.h" +#include "base/time/clock.h" +#include "base/time/default_clock.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/content_settings_rule.h" #include "chrome/browser/content_settings/content_settings_utils.h" @@ -40,6 +42,7 @@ typedef std::map<std::string, std::string> StringMap; const char kPerPluginPrefName[] = "per_plugin"; const char kAudioKey[] = "audio"; const char kVideoKey[] = "video"; +const char kLastUsed[] = "last_used"; ContentSetting FixObsoleteCookiePromptMode(ContentSettingsType content_type, ContentSetting setting) { @@ -83,11 +86,11 @@ void PrefProvider::RegisterProfilePrefs( user_prefs::PrefRegistrySyncable::SYNCABLE_PREF); } -PrefProvider::PrefProvider(PrefService* prefs, - bool incognito) - : prefs_(prefs), - is_incognito_(incognito), - updating_preferences_(false) { +PrefProvider::PrefProvider(PrefService* prefs, bool incognito) + : prefs_(prefs), + clock_(new base::DefaultClock()), + is_incognito_(incognito), + updating_preferences_(false) { DCHECK(prefs_); // Verify preferences version. if (!prefs_->HasPrefPath(prefs::kContentSettingsVersion)) { @@ -290,6 +293,7 @@ void PrefProvider::UpdatePref( if (value == NULL) { settings_dictionary->RemoveWithoutPathExpansion(setting_path, NULL); + settings_dictionary->RemoveWithoutPathExpansion(kLastUsed, NULL); } else { settings_dictionary->SetWithoutPathExpansion( setting_path, value->DeepCopy()); @@ -566,6 +570,85 @@ void PrefProvider::ShutdownOnUIThread() { prefs_ = NULL; } +void PrefProvider::UpdateLastUsage( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type) { + // Don't write if in incognito. + if (is_incognito_) { + return; + } + + // Ensure that |lock_| is not held by this thread, since this function will + // send out notifications (by |~DictionaryPrefUpdate|). + AssertLockNotHeld(); + + base::AutoReset<bool> auto_reset(&updating_preferences_, true); + { + DictionaryPrefUpdate update(prefs_, prefs::kContentSettingsPatternPairs); + base::DictionaryValue* pattern_pairs_settings = update.Get(); + + std::string pattern_str( + CreatePatternString(primary_pattern, secondary_pattern)); + base::DictionaryValue* settings_dictionary = NULL; + bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion( + pattern_str, &settings_dictionary); + + if (!found) { + settings_dictionary = new base::DictionaryValue; + pattern_pairs_settings->SetWithoutPathExpansion(pattern_str, + settings_dictionary); + } + + base::DictionaryValue* last_used_dictionary = NULL; + found = settings_dictionary->GetDictionaryWithoutPathExpansion( + kLastUsed, &last_used_dictionary); + + if (!found) { + last_used_dictionary = new base::DictionaryValue; + settings_dictionary->SetWithoutPathExpansion(kLastUsed, + last_used_dictionary); + } + + std::string settings_path = GetTypeName(content_type); + last_used_dictionary->Set( + settings_path, new base::FundamentalValue(clock_->Now().ToDoubleT())); + } +} + +base::Time PrefProvider::GetLastUsage( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type) { + const base::DictionaryValue* pattern_pairs_settings = + prefs_->GetDictionary(prefs::kContentSettingsPatternPairs); + std::string pattern_str( + CreatePatternString(primary_pattern, secondary_pattern)); + + const base::DictionaryValue* settings_dictionary = NULL; + bool found = pattern_pairs_settings->GetDictionaryWithoutPathExpansion( + pattern_str, &settings_dictionary); + + if (!found) + return base::Time(); + + const base::DictionaryValue* last_used_dictionary = NULL; + found = settings_dictionary->GetDictionaryWithoutPathExpansion( + kLastUsed, &last_used_dictionary); + + if (!found) + return base::Time(); + + double last_used_time; + found = last_used_dictionary->GetDoubleWithoutPathExpansion( + GetTypeName(content_type), &last_used_time); + + if (!found) + return base::Time(); + + return base::Time::FromDoubleT(last_used_time); +} + void PrefProvider::AssertLockNotHeld() const { #if !defined(NDEBUG) // |Lock::Acquire()| will assert if the lock is held by this thread. @@ -574,4 +657,8 @@ void PrefProvider::AssertLockNotHeld() const { #endif } +void PrefProvider::SetClockForTesting(scoped_ptr<base::Clock> clock) { + clock_ = clock.Pass(); +} + } // namespace content_settings diff --git a/chrome/browser/content_settings/content_settings_pref_provider.h b/chrome/browser/content_settings/content_settings_pref_provider.h index 9d8a2bc..08a9d1b 100644 --- a/chrome/browser/content_settings/content_settings_pref_provider.h +++ b/chrome/browser/content_settings/content_settings_pref_provider.h @@ -19,6 +19,7 @@ class PrefService; namespace base { +class Clock; class DictionaryValue; } @@ -55,6 +56,18 @@ class PrefProvider : public ObservableProvider { virtual void ShutdownOnUIThread() OVERRIDE; + // Records the last time the given pattern has used a certain content setting. + void UpdateLastUsage(const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type); + + base::Time GetLastUsage(const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type); + + // Gains ownership of |clock|. + void SetClockForTesting(scoped_ptr<base::Clock> clock); + private: friend class DeadlockCheckerThread; // For testing. // Reads all content settings exceptions from the preference and load them @@ -90,6 +103,9 @@ class PrefProvider : public ObservableProvider { // Weak; owned by the Profile and reset in ShutdownOnUIThread. PrefService* prefs_; + // Can be set for testing. + scoped_ptr<base::Clock> clock_; + bool is_incognito_; PrefChangeRegistrar pref_change_registrar_; diff --git a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc index 395693a..a090372 100644 --- a/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc +++ b/chrome/browser/content_settings/content_settings_pref_provider_unittest.cc @@ -14,6 +14,7 @@ #include "base/prefs/pref_service.h" #include "base/prefs/scoped_user_pref_update.h" #include "base/prefs/testing_pref_store.h" +#include "base/test/simple_test_clock.h" #include "base/threading/platform_thread.h" #include "base/values.h" #include "chrome/browser/content_settings/content_settings_mock_observer.h" @@ -444,4 +445,39 @@ TEST_F(PrefProviderTest, Deadlock) { provider.ShutdownOnUIThread(); } +TEST_F(PrefProviderTest, LastUsage) { + TestingProfile testing_profile; + PrefProvider pref_content_settings_provider(testing_profile.GetPrefs(), + false); + base::SimpleTestClock* test_clock = new base::SimpleTestClock; + test_clock->SetNow(base::Time::Now()); + + pref_content_settings_provider.SetClockForTesting( + scoped_ptr<base::Clock>(test_clock)); + GURL host("http://example.com/"); + ContentSettingsPattern pattern = + ContentSettingsPattern::FromString("[*.]example.com"); + + base::Time no_usage = pref_content_settings_provider.GetLastUsage( + pattern, pattern, CONTENT_SETTINGS_TYPE_GEOLOCATION); + EXPECT_EQ(no_usage.ToDoubleT(), 0); + + pref_content_settings_provider.UpdateLastUsage( + pattern, pattern, CONTENT_SETTINGS_TYPE_GEOLOCATION); + base::Time first = pref_content_settings_provider.GetLastUsage( + pattern, pattern, CONTENT_SETTINGS_TYPE_GEOLOCATION); + + test_clock->Advance(base::TimeDelta::FromSeconds(10)); + + pref_content_settings_provider.UpdateLastUsage( + pattern, pattern, CONTENT_SETTINGS_TYPE_GEOLOCATION); + base::Time second = pref_content_settings_provider.GetLastUsage( + pattern, pattern, CONTENT_SETTINGS_TYPE_GEOLOCATION); + + base::TimeDelta delta = second - first; + EXPECT_EQ(delta.InSeconds(), 10); + + pref_content_settings_provider.ShutdownOnUIThread(); +} + } // namespace content_settings diff --git a/chrome/browser/content_settings/host_content_settings_map.cc b/chrome/browser/content_settings/host_content_settings_map.cc index 3dc620a..27427aef 100644 --- a/chrome/browser/content_settings/host_content_settings_map.cc +++ b/chrome/browser/content_settings/host_content_settings_map.cc @@ -12,6 +12,7 @@ #include "base/stl_util.h" #include "base/strings/string_util.h" #include "base/strings/utf_string_conversions.h" +#include "base/time/clock.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/content_settings_custom_extension_provider.h" #include "chrome/browser/content_settings/content_settings_default_provider.h" @@ -299,6 +300,13 @@ void HostContentSettingsMap::SetContentSetting( const std::string& resource_identifier, ContentSetting setting) { DCHECK(!ContentTypeHasCompoundValue(content_type)); + + if (setting == CONTENT_SETTING_ALLOW && + (content_type == CONTENT_SETTINGS_TYPE_GEOLOCATION || + content_type == CONTENT_SETTINGS_TYPE_NOTIFICATIONS)) { + UpdateLastUsageByPattern(primary_pattern, secondary_pattern, content_type); + } + base::Value* value = NULL; if (setting != CONTENT_SETTING_DEFAULT) value = base::Value::CreateIntegerValue(setting); @@ -309,6 +317,70 @@ void HostContentSettingsMap::SetContentSetting( value); } +ContentSetting HostContentSettingsMap::GetContentSettingAndMaybeUpdateLastUsage( + const GURL& primary_url, + const GURL& secondary_url, + ContentSettingsType content_type, + const std::string& resource_identifier) { + DCHECK_CURRENTLY_ON(BrowserThread::UI); + + ContentSetting setting = GetContentSetting( + primary_url, secondary_url, content_type, resource_identifier); + if (setting == CONTENT_SETTING_ALLOW) { + UpdateLastUsageByPattern( + ContentSettingsPattern::FromURLNoWildcard(primary_url), + ContentSettingsPattern::FromURLNoWildcard(secondary_url), + content_type); + } + return setting; +} + +void HostContentSettingsMap::UpdateLastUsage(const GURL& primary_url, + const GURL& secondary_url, + ContentSettingsType content_type) { + UpdateLastUsageByPattern( + ContentSettingsPattern::FromURLNoWildcard(primary_url), + ContentSettingsPattern::FromURLNoWildcard(secondary_url), + content_type); +} + +void HostContentSettingsMap::UpdateLastUsageByPattern( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type) { + UsedContentSettingsProviders(); + + GetPrefProvider()->UpdateLastUsage( + primary_pattern, secondary_pattern, content_type); +} + +base::Time HostContentSettingsMap::GetLastUsage( + const GURL& primary_url, + const GURL& secondary_url, + ContentSettingsType content_type) { + return GetLastUsageByPattern( + ContentSettingsPattern::FromURLNoWildcard(primary_url), + ContentSettingsPattern::FromURLNoWildcard(secondary_url), + content_type); +} + +base::Time HostContentSettingsMap::GetLastUsageByPattern( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type) { + UsedContentSettingsProviders(); + + return GetPrefProvider()->GetLastUsage( + primary_pattern, secondary_pattern, content_type); +} + +void HostContentSettingsMap::SetPrefClockForTesting( + scoped_ptr<base::Clock> clock) { + UsedContentSettingsProviders(); + + GetPrefProvider()->SetClockForTesting(clock.Pass()); +} + void HostContentSettingsMap::AddExceptionForURL( const GURL& primary_url, const GURL& secondary_url, @@ -639,3 +711,8 @@ HostContentSettingsMap::ProviderType NOTREACHED(); return DEFAULT_PROVIDER; } + +content_settings::PrefProvider* HostContentSettingsMap::GetPrefProvider() { + return static_cast<content_settings::PrefProvider*>( + content_settings_providers_[PREF_PROVIDER]); +} diff --git a/chrome/browser/content_settings/host_content_settings_map.h b/chrome/browser/content_settings/host_content_settings_map.h index 8d5681a..915c4a7 100644 --- a/chrome/browser/content_settings/host_content_settings_map.h +++ b/chrome/browser/content_settings/host_content_settings_map.h @@ -27,11 +27,13 @@ class GURL; class PrefService; namespace base { +class Clock; class Value; } namespace content_settings { class ProviderInterface; +class PrefProvider; } namespace user_prefs { @@ -199,6 +201,39 @@ class HostContentSettingsMap return is_off_the_record_; } + // Returns a single |ContentSetting| which applies to the given URLs, just as + // |GetContentSetting| does. If the setting is allowed, it also records the + // last usage to preferences. + // + // This should only be called on the UI thread, unlike |GetContentSetting|. + ContentSetting GetContentSettingAndMaybeUpdateLastUsage( + const GURL& primary_url, + const GURL& secondary_url, + ContentSettingsType content_type, + const std::string& resource_identifier); + + // Sets the last time that a given content type has been used for the pattern + // which matches the URLs to the current time. + void UpdateLastUsage(const GURL& primary_url, + const GURL& secondary_url, + ContentSettingsType content_type); + + // Returns the last time the pattern that matches the URL has requested + // permission for the |content_type| setting. + base::Time GetLastUsage(const GURL& primary_url, + const GURL& secondary_url, + ContentSettingsType content_type); + + // Returns the last time the pattern has requested permission for the + // |content_type| setting. + base::Time GetLastUsageByPattern( + const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type); + + // Passes ownership of |clock|. + void SetPrefClockForTesting(scoped_ptr<base::Clock> clock); + private: friend class base::RefCountedThreadSafe<HostContentSettingsMap>; friend class HostContentSettingsMapTest_NonDefaultSettings_Test; @@ -236,6 +271,14 @@ class HostContentSettingsMap // it is not being called too late. void UsedContentSettingsProviders() const; + // Convenience method for updating the last usage of a content type for a + // pattern. + void UpdateLastUsageByPattern(const ContentSettingsPattern& primary_pattern, + const ContentSettingsPattern& secondary_pattern, + ContentSettingsType content_type); + + content_settings::PrefProvider* GetPrefProvider(); + #ifndef NDEBUG // This starts as the thread ID of the thread that constructs this // object, and remains until used by a different thread, at which diff --git a/chrome/browser/geolocation/geolocation_browsertest.cc b/chrome/browser/geolocation/geolocation_browsertest.cc index 196f908..3c3b738 100644 --- a/chrome/browser/geolocation/geolocation_browsertest.cc +++ b/chrome/browser/geolocation/geolocation_browsertest.cc @@ -8,6 +8,8 @@ #include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/simple_test_clock.h" +#include "base/time/clock.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/content_settings_usages_state.h" #include "chrome/browser/content_settings/host_content_settings_map.h" @@ -769,3 +771,48 @@ IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, TabDestroyed) { current_browser()->tab_strip_model()->GetActiveWebContents(), script); EXPECT_EQ(result, true); } + +IN_PROC_BROWSER_TEST_F(GeolocationBrowserTest, LastUsageUpdated) { + ASSERT_TRUE(Initialize(INITIALIZATION_NONE)); + base::SimpleTestClock* clock_ = new base::SimpleTestClock(); + current_browser() + ->profile() + ->GetHostContentSettingsMap() + ->SetPrefClockForTesting(scoped_ptr<base::Clock>(clock_)); + clock_->SetNow(base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10)); + + // Setting the permission should trigger the last usage. + current_browser()->profile()->GetHostContentSettingsMap()->SetContentSetting( + ContentSettingsPattern::FromURLNoWildcard(current_url()), + ContentSettingsPattern::FromURLNoWildcard(current_url()), + CONTENT_SETTINGS_TYPE_GEOLOCATION, + std::string(), + CONTENT_SETTING_ALLOW); + + // Permission has been used at the starting time. + EXPECT_EQ(current_browser() + ->profile() + ->GetHostContentSettingsMap() + ->GetLastUsage(current_url().GetOrigin(), + current_url().GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION) + .ToDoubleT(), + 10); + + clock_->Advance(base::TimeDelta::FromSeconds(3)); + + // Watching should trigger the last usage update. + SetFrameHost(""); + AddGeolocationWatch(false); + CheckGeoposition(fake_latitude(), fake_longitude()); + + // Last usage has been updated. + EXPECT_EQ(current_browser() + ->profile() + ->GetHostContentSettingsMap() + ->GetLastUsage(current_url().GetOrigin(), + current_url().GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION) + .ToDoubleT(), + 13); +} diff --git a/chrome/browser/geolocation/geolocation_permission_context.cc b/chrome/browser/geolocation/geolocation_permission_context.cc index 37c53e8..1e634bd 100644 --- a/chrome/browser/geolocation/geolocation_permission_context.cc +++ b/chrome/browser/geolocation/geolocation_permission_context.cc @@ -209,9 +209,12 @@ void GeolocationPermissionContext::DecidePermission( DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); ContentSetting content_setting = - profile_->GetHostContentSettingsMap()->GetContentSetting( - requesting_frame, embedder, CONTENT_SETTINGS_TYPE_GEOLOCATION, - std::string()); + profile_->GetHostContentSettingsMap() + ->GetContentSettingAndMaybeUpdateLastUsage( + requesting_frame, + embedder, + CONTENT_SETTINGS_TYPE_GEOLOCATION, + std::string()); switch (content_setting) { case CONTENT_SETTING_BLOCK: PermissionDecided(id, requesting_frame, embedder, callback, false); diff --git a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc index f011ac3..b243ce6 100644 --- a/chrome/browser/geolocation/geolocation_permission_context_unittest.cc +++ b/chrome/browser/geolocation/geolocation_permission_context_unittest.cc @@ -13,6 +13,8 @@ #include "base/memory/scoped_vector.h" #include "base/run_loop.h" #include "base/synchronization/waitable_event.h" +#include "base/test/simple_test_clock.h" +#include "base/time/clock.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/content_settings/permission_request_id.h" @@ -696,3 +698,139 @@ TEST_F(GeolocationPermissionContextTests, InfoBarUsesCommittedEntry) { // Delete the tab contents. DeleteContents(); } + +TEST_F(GeolocationPermissionContextTests, LastUsageAudited) { + GURL requesting_frame("http://www.example.com/geolocation"); + NavigateAndCommit(requesting_frame); + + base::SimpleTestClock* test_clock = new base::SimpleTestClock; + test_clock->SetNow(base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(10)); + + HostContentSettingsMap* map = profile()->GetHostContentSettingsMap(); + map->SetPrefClockForTesting(scoped_ptr<base::Clock>(test_clock)); + + // The permission shouldn't have been used yet. + EXPECT_EQ(map->GetLastUsage(requesting_frame.GetOrigin(), + requesting_frame.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 0); + + EXPECT_EQ(0U, infobar_service()->infobar_count()); + RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame); + ASSERT_EQ(1U, infobar_service()->infobar_count()); + infobars::InfoBar* infobar = infobar_service()->infobar_at(0); + ConfirmInfoBarDelegate* infobar_delegate = + infobar->delegate()->AsConfirmInfoBarDelegate(); + ASSERT_TRUE(infobar_delegate); + infobar_delegate->Accept(); + CheckTabContentsState(requesting_frame, CONTENT_SETTING_ALLOW); + CheckPermissionMessageSent(0, true); + + // Permission has been used at the starting time. + EXPECT_EQ(map->GetLastUsage(requesting_frame.GetOrigin(), + requesting_frame.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 10); + + test_clock->Advance(base::TimeDelta::FromSeconds(3)); + RequestGeolocationPermission(web_contents(), RequestID(0), requesting_frame); + + // Permission has been used three seconds later. + EXPECT_EQ(map->GetLastUsage(requesting_frame.GetOrigin(), + requesting_frame.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 13); +} + +TEST_F(GeolocationPermissionContextTests, LastUsageAuditedMultipleFrames) { + base::SimpleTestClock* test_clock = new base::SimpleTestClock; + test_clock->SetNow(base::Time::UnixEpoch() + + base::TimeDelta::FromSeconds(10)); + + HostContentSettingsMap* map = profile()->GetHostContentSettingsMap(); + map->SetPrefClockForTesting(scoped_ptr<base::Clock>(test_clock)); + + GURL requesting_frame_0("http://www.example.com/geolocation"); + GURL requesting_frame_1("http://www.example-2.com/geolocation"); + + // The permission shouldn't have been used yet. + EXPECT_EQ(map->GetLastUsage(requesting_frame_0.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 0); + EXPECT_EQ(map->GetLastUsage(requesting_frame_1.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 0); + + NavigateAndCommit(requesting_frame_0); + EXPECT_EQ(0U, infobar_service()->infobar_count()); + + // Request permission for two frames. + RequestGeolocationPermission( + web_contents(), RequestID(0), requesting_frame_0); + RequestGeolocationPermission( + web_contents(), RequestID(1), requesting_frame_1); + + // Ensure only one infobar is created. + ASSERT_EQ(1U, infobar_service()->infobar_count()); + infobars::InfoBar* infobar_0 = infobar_service()->infobar_at(0); + ConfirmInfoBarDelegate* infobar_delegate_0 = + infobar_0->delegate()->AsConfirmInfoBarDelegate(); + + // Accept the first frame. + infobar_delegate_0->Accept(); + CheckTabContentsState(requesting_frame_0, CONTENT_SETTING_ALLOW); + CheckPermissionMessageSent(0, true); + infobar_service()->RemoveInfoBar(infobar_0); + + // Verify that accepting the first didn't accept because it's embedder + // in the other. + EXPECT_EQ(map->GetLastUsage(requesting_frame_0.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 10); + EXPECT_EQ(map->GetLastUsage(requesting_frame_1.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 0); + + ASSERT_EQ(1U, infobar_service()->infobar_count()); + infobars::InfoBar* infobar_1 = infobar_service()->infobar_at(0); + ConfirmInfoBarDelegate* infobar_delegate_1 = + infobar_1->delegate()->AsConfirmInfoBarDelegate(); + + test_clock->Advance(base::TimeDelta::FromSeconds(1)); + + // Allow the second frame. + infobar_delegate_1->Accept(); + CheckTabContentsState(requesting_frame_1, CONTENT_SETTING_ALLOW); + CheckPermissionMessageSent(1, true); + infobar_service()->RemoveInfoBar(infobar_1); + + // Verify that the times are different. + EXPECT_EQ(map->GetLastUsage(requesting_frame_0.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 10); + EXPECT_EQ(map->GetLastUsage(requesting_frame_1.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 11); + + test_clock->Advance(base::TimeDelta::FromSeconds(2)); + RequestGeolocationPermission( + web_contents(), RequestID(0), requesting_frame_0); + + // Verify that requesting permission in one frame doesn't update other where + // it is the embedder. + EXPECT_EQ(map->GetLastUsage(requesting_frame_0.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 13); + EXPECT_EQ(map->GetLastUsage(requesting_frame_1.GetOrigin(), + requesting_frame_0.GetOrigin(), + CONTENT_SETTINGS_TYPE_GEOLOCATION).ToDoubleT(), + 11); +} diff --git a/chrome/browser/notifications/desktop_notification_service.cc b/chrome/browser/notifications/desktop_notification_service.cc index 2d605f8..e0c1bb7 100644 --- a/chrome/browser/notifications/desktop_notification_service.cc +++ b/chrome/browser/notifications/desktop_notification_service.cc @@ -481,11 +481,12 @@ void DesktopNotificationService::ResetAllOrigins() { ContentSetting DesktopNotificationService::GetContentSetting( const GURL& origin) { - return profile_->GetHostContentSettingsMap()->GetContentSetting( - origin, - origin, - CONTENT_SETTINGS_TYPE_NOTIFICATIONS, - NO_RESOURCE_IDENTIFIER); + return profile_->GetHostContentSettingsMap() + ->GetContentSettingAndMaybeUpdateLastUsage( + origin, + origin, + CONTENT_SETTINGS_TYPE_NOTIFICATIONS, + NO_RESOURCE_IDENTIFIER); } void DesktopNotificationService::RequestPermission( diff --git a/chrome/browser/notifications/notification_browsertest.cc b/chrome/browser/notifications/notification_browsertest.cc index af06140..cacc5e8 100644 --- a/chrome/browser/notifications/notification_browsertest.cc +++ b/chrome/browser/notifications/notification_browsertest.cc @@ -13,8 +13,11 @@ #include "base/run_loop.h" #include "base/strings/stringprintf.h" #include "base/strings/utf_string_conversions.h" +#include "base/test/simple_test_clock.h" +#include "base/time/clock.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/chrome_notification_types.h" +#include "chrome/browser/content_settings/host_content_settings_map.h" #include "chrome/browser/infobars/infobar_service.h" #include "chrome/browser/notifications/desktop_notification_service.h" #include "chrome/browser/notifications/desktop_notification_service_factory.h" @@ -778,3 +781,37 @@ IN_PROC_BROWSER_TEST_F(NotificationsTest, MAYBE_TestNotificationReplacement) { EXPECT_EQ(base::ASCIIToUTF16("Body2"), (*notifications.rbegin())->message()); } + +IN_PROC_BROWSER_TEST_F(NotificationsTest, TestLastUsage) { + ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); + + HostContentSettingsMap* settings_map = + browser()->profile()->GetHostContentSettingsMap(); + base::SimpleTestClock* clock = new base::SimpleTestClock(); + settings_map->SetPrefClockForTesting(scoped_ptr<base::Clock>(clock)); + clock->SetNow(base::Time::UnixEpoch() + base::TimeDelta::FromSeconds(10)); + + // Creates a simple notification. + AllowAllOrigins(); + ui_test_utils::NavigateToURL(browser(), GetTestPageURL()); + + std::string result = CreateSimpleNotification(browser(), true); + EXPECT_NE("-1", result); + + EXPECT_EQ(settings_map->GetLastUsage(GetTestPageURL().GetOrigin(), + GetTestPageURL().GetOrigin(), + CONTENT_SETTINGS_TYPE_NOTIFICATIONS) + .ToDoubleT(), + 10); + + clock->Advance(base::TimeDelta::FromSeconds(3)); + + result = CreateSimpleNotification(browser(), true); + EXPECT_NE("-1", result); + + EXPECT_EQ(settings_map->GetLastUsage(GetTestPageURL().GetOrigin(), + GetTestPageURL().GetOrigin(), + CONTENT_SETTINGS_TYPE_NOTIFICATIONS) + .ToDoubleT(), + 13); +} diff --git a/content/browser/geolocation/geolocation_dispatcher_host.cc b/content/browser/geolocation/geolocation_dispatcher_host.cc index ad1f2fc..75abab5 100644 --- a/content/browser/geolocation/geolocation_dispatcher_host.cc +++ b/content/browser/geolocation/geolocation_dispatcher_host.cc @@ -130,6 +130,15 @@ void GeolocationDispatcherHost::OnLocationUpdate( for (std::map<RenderFrameHost*, bool>::iterator i = updating_frames_.begin(); i != updating_frames_.end(); ++i) { + RenderFrameHost* top_frame = i->first; + while (top_frame->GetParent()) { + top_frame = top_frame->GetParent(); + } + GetContentClient()->browser()->DidUseGeolocationPermission( + web_contents(), + i->first->GetLastCommittedURL().GetOrigin(), + top_frame->GetLastCommittedURL().GetOrigin()); + i->first->Send(new GeolocationMsg_PositionUpdated( i->first->GetRoutingID(), geoposition)); } diff --git a/content/public/browser/content_browser_client.h b/content/public/browser/content_browser_client.h index 356c724..68fa0d6 100644 --- a/content/public/browser/content_browser_client.h +++ b/content/public/browser/content_browser_client.h @@ -453,6 +453,11 @@ class CONTENT_EXPORT ContentBrowserClient { base::Callback<void(bool)> result_callback, base::Closure* cancel_callback); + // Invoked when the Geolocation API uses its permission. + virtual void DidUseGeolocationPermission(WebContents* web_contents, + const GURL& frame_url, + const GURL& main_frame_url) {} + // Requests a permission to use system exclusive messages in MIDI events. // |result_callback| will be invoked when the request is resolved. If // |cancel_callback| is non-null, it's set to a callback which can be used to |