// Copyright (c) 2012 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/command_line.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop/message_loop.h" #include "base/strings/stringprintf.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/extensions/extension_action_manager.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/extensions/script_badge_controller.h" #include "chrome/browser/extensions/tab_helper.h" #include "chrome/browser/extensions/test_extension_system.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_version_info.h" #include "chrome/common/extensions/extension.h" #include "chrome/common/extensions/extension_builder.h" #include "chrome/common/extensions/feature_switch.h" #include "chrome/common/extensions/features/feature_channel.h" #include "chrome/common/extensions/value_builder.h" #include "chrome/test/base/chrome_render_view_host_test_harness.h" #include "chrome/test/base/testing_profile.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/navigation_entry.h" #include "content/public/browser/notification_registrar.h" #include "content/public/browser/notification_source.h" #include "content/public/browser/web_contents.h" #include "content/public/test/test_browser_thread.h" #include "testing/gmock/include/gmock/gmock.h" #if defined(OS_CHROMEOS) #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/browser/chromeos/settings/device_settings_service.h" #endif namespace extensions { namespace { class ScriptBadgeControllerTest : public ChromeRenderViewHostTestHarness { public: ScriptBadgeControllerTest() : feature_override_(FeatureSwitch::script_badges(), true), current_channel_(chrome::VersionInfo::CHANNEL_DEV) {} virtual void SetUp() OVERRIDE { // Note that this sets a PageActionController into the // extensions::TabHelper's location_bar_controller field. Do // not use that for testing. ChromeRenderViewHostTestHarness::SetUp(); #if defined OS_CHROMEOS test_user_manager_.reset(new chromeos::ScopedTestUserManager()); #endif Profile* profile = Profile::FromBrowserContext(web_contents()->GetBrowserContext()); TestExtensionSystem* extension_system = static_cast(ExtensionSystem::Get(profile)); // Create an ExtensionService so the ScriptBadgeController can find its // extensions. CommandLine command_line(CommandLine::NO_PROGRAM); extension_service_ = extension_system->CreateExtensionService( &command_line, base::FilePath(), false); TabHelper::CreateForWebContents(web_contents()); script_badge_controller_ = static_cast( TabHelper::FromWebContents(web_contents())->location_bar_controller()); } virtual void TearDown() OVERRIDE { #if defined OS_CHROMEOS test_user_manager_.reset(); #endif ChromeRenderViewHostTestHarness::TearDown(); } protected: // Creates a test extension and adds it to |extension_service_|. scoped_refptr AddTestExtension() { scoped_refptr extension = ExtensionBuilder() .SetManifest(DictionaryBuilder() .Set("name", "Extension with page action") .Set("version", "1.0.0") .Set("manifest_version", 2) .Set("permissions", ListBuilder() .Append("tabs")) .Set("page_action", DictionaryBuilder() .Set("default_title", "Hello"))) .Build(); extension_service_->AddExtension(extension.get()); return extension; } ExtensionAction* GetScriptBadge(const Extension& extension) { return ExtensionActionManager::Get(profile())->GetScriptBadge(extension); } ExtensionService* extension_service_; ScriptBadgeController* script_badge_controller_; private: FeatureSwitch::ScopedOverride feature_override_; ScopedCurrentChannel current_channel_; #if defined OS_CHROMEOS chromeos::ScopedTestDeviceSettingsService test_device_settings_service_; chromeos::ScopedTestCrosSettings test_cros_settings_; scoped_ptr test_user_manager_; #endif }; struct CountingNotificationObserver : public content::NotificationObserver { CountingNotificationObserver() : events(0) {} virtual void Observe(int type, const content::NotificationSource& source, const content::NotificationDetails& details) OVERRIDE { events++; } int events; }; TEST_F(ScriptBadgeControllerTest, ExecutionMakesBadgeVisible) { content::NotificationRegistrar notification_registrar; EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre()); scoped_refptr extension = AddTestExtension(); // Establish a page id. NavigateAndCommit(GURL("http://www.google.com")); CountingNotificationObserver location_bar_updated; Profile* profile = Profile::FromBrowserContext(web_contents()->GetBrowserContext()); notification_registrar.Add( &location_bar_updated, chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, content::Source(profile)); // Initially, no script badges. EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre()); TabHelper::ScriptExecutionObserver::ExecutingScriptsMap id_map; id_map[extension->id()] = std::set(); script_badge_controller_->OnScriptsExecuted( web_contents(), id_map, web_contents()->GetController().GetVisibleEntry()->GetPageID(), GURL(std::string())); EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre(GetScriptBadge(*extension.get()))); EXPECT_THAT(location_bar_updated.events, testing::Gt(0)); }; TEST_F(ScriptBadgeControllerTest, FragmentNavigation) { scoped_refptr extension = AddTestExtension(); Profile* profile = Profile::FromBrowserContext(web_contents()->GetBrowserContext()); // Establish a page id. NavigateAndCommit(GURL("http://www.google.com")); // Run script. Should be a notification and a script badge. { content::NotificationRegistrar notification_registrar; CountingNotificationObserver location_bar_updated; notification_registrar.Add( &location_bar_updated, chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, content::Source(profile)); TabHelper::ScriptExecutionObserver::ExecutingScriptsMap id_map; id_map[extension->id()] = std::set(); script_badge_controller_->OnScriptsExecuted( web_contents(), id_map, web_contents()->GetController().GetVisibleEntry()->GetPageID(), GURL(std::string())); EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre(GetScriptBadge(*extension.get()))); EXPECT_EQ(1, location_bar_updated.events); } // Navigate to a hash fragment. Shouldn't change. { content::NotificationRegistrar notification_registrar; CountingNotificationObserver location_bar_updated; notification_registrar.Add( &location_bar_updated, chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, content::Source(profile)); NavigateAndCommit(GURL("http://www.google.com#hash")); EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre(GetScriptBadge(*extension.get()))); EXPECT_EQ(0, location_bar_updated.events); } // Refreshing the page should reset the badges. { content::NotificationRegistrar notification_registrar; CountingNotificationObserver location_bar_updated; notification_registrar.Add( &location_bar_updated, chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, content::Source(profile)); Reload(); EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre()); } } TEST_F(ScriptBadgeControllerTest, GetAttentionMakesBadgeVisible) { content::NotificationRegistrar notification_registrar; scoped_refptr extension = ExtensionBuilder() .SetManifest(DictionaryBuilder() .Set("name", "Extension") .Set("version", "1.0.0") .Set("manifest_version", 2) .Set("permissions", ListBuilder() .Append("tabs"))) .Build(); extension_service_->AddExtension(extension.get()); // Establish a page id. NavigateAndCommit(GURL("http://www.google.com")); CountingNotificationObserver initial_badge_display; Profile* profile = Profile::FromBrowserContext(web_contents()->GetBrowserContext()); notification_registrar.Add( &initial_badge_display, chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, content::Source(profile)); // Initially, no script badges. EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre()); // Getting attention the first time should display the badge. script_badge_controller_->GetAttentionFor(extension->id()); EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre(GetScriptBadge(*extension.get()))); EXPECT_THAT(initial_badge_display.events, testing::Gt(0)); CountingNotificationObserver subsequent_get_attention_call; notification_registrar.Add( &subsequent_get_attention_call, chrome::NOTIFICATION_EXTENSION_LOCATION_BAR_UPDATED, content::Source(profile)); // Getting attention a second time should have no effect. script_badge_controller_->GetAttentionFor(extension->id()); EXPECT_THAT(script_badge_controller_->GetCurrentActions(), testing::ElementsAre(GetScriptBadge(*extension.get()))); EXPECT_EQ(0, subsequent_get_attention_call.events); }; } // namespace } // namespace extensions