background_contents_service_unittest.cc revision a02191e04bc25c4935f804f2c080ae28663d096d
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <string> 6 7#include "base/basictypes.h" 8#include "base/command_line.h" 9#include "base/memory/scoped_ptr.h" 10#include "base/message_loop/message_loop.h" 11#include "base/prefs/pref_service.h" 12#include "base/prefs/scoped_user_pref_update.h" 13#include "base/run_loop.h" 14#include "base/strings/utf_string_conversions.h" 15#include "chrome/browser/background/background_contents_service.h" 16#include "chrome/browser/background/background_contents_service_factory.h" 17#include "chrome/browser/chrome_notification_types.h" 18#include "chrome/browser/tab_contents/background_contents.h" 19#include "chrome/browser/ui/browser_list.h" 20#include "chrome/common/extensions/extension_test_util.h" 21#include "chrome/common/pref_names.h" 22#include "chrome/test/base/browser_with_test_window_test.h" 23#include "chrome/test/base/testing_browser_process.h" 24#include "chrome/test/base/testing_profile.h" 25#include "chrome/test/base/testing_profile_manager.h" 26#include "content/public/browser/notification_service.h" 27#include "content/public/test/test_browser_thread.h" 28#include "extensions/common/extension.h" 29#include "testing/gtest/include/gtest/gtest.h" 30#include "testing/platform_test.h" 31#include "url/gurl.h" 32 33#if defined(ENABLE_NOTIFICATIONS) 34#include "chrome/browser/notifications/message_center_notification_manager.h" 35#include "chrome/browser/notifications/notification.h" 36#include "ui/message_center/fake_message_center_tray_delegate.h" 37#include "ui/message_center/message_center.h" 38#include "ui/message_center/message_center_observer.h" 39#endif 40 41class BackgroundContentsServiceTest : public testing::Test { 42 public: 43 BackgroundContentsServiceTest() {} 44 virtual ~BackgroundContentsServiceTest() {} 45 virtual void SetUp() { 46 command_line_.reset(new CommandLine(CommandLine::NO_PROGRAM)); 47 } 48 49 const base::DictionaryValue* GetPrefs(Profile* profile) { 50 return profile->GetPrefs()->GetDictionary( 51 prefs::kRegisteredBackgroundContents); 52 } 53 54 // Returns the stored pref URL for the passed app id. 55 std::string GetPrefURLForApp(Profile* profile, const base::string16& appid) { 56 const base::DictionaryValue* pref = GetPrefs(profile); 57 EXPECT_TRUE(pref->HasKey(base::UTF16ToUTF8(appid))); 58 const base::DictionaryValue* value; 59 pref->GetDictionaryWithoutPathExpansion(base::UTF16ToUTF8(appid), &value); 60 std::string url; 61 value->GetString("url", &url); 62 return url; 63 } 64 65 scoped_ptr<CommandLine> command_line_; 66}; 67 68class MockBackgroundContents : public BackgroundContents { 69 public: 70 explicit MockBackgroundContents(Profile* profile) 71 : appid_(base::ASCIIToUTF16("app_id")), 72 profile_(profile) { 73 } 74 MockBackgroundContents(Profile* profile, const std::string& id) 75 : appid_(base::ASCIIToUTF16(id)), 76 profile_(profile) { 77 } 78 79 void SendOpenedNotification(BackgroundContentsService* service) { 80 base::string16 frame_name = base::ASCIIToUTF16("background"); 81 BackgroundContentsOpenedDetails details = { 82 this, frame_name, appid_ }; 83 service->BackgroundContentsOpened(&details); 84 } 85 86 virtual void Navigate(GURL url) { 87 url_ = url; 88 content::NotificationService::current()->Notify( 89 chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED, 90 content::Source<Profile>(profile_), 91 content::Details<BackgroundContents>(this)); 92 } 93 virtual const GURL& GetURL() const OVERRIDE { return url_; } 94 95 void MockClose(Profile* profile) { 96 content::NotificationService::current()->Notify( 97 chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED, 98 content::Source<Profile>(profile), 99 content::Details<BackgroundContents>(this)); 100 delete this; 101 } 102 103 virtual ~MockBackgroundContents() { 104 content::NotificationService::current()->Notify( 105 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED, 106 content::Source<Profile>(profile_), 107 content::Details<BackgroundContents>(this)); 108 } 109 110 const base::string16& appid() { return appid_; } 111 112 private: 113 GURL url_; 114 115 // The ID of our parent application 116 base::string16 appid_; 117 118 // Parent profile 119 Profile* profile_; 120}; 121 122#if defined(ENABLE_NOTIFICATIONS) 123// Wait for the notification created. 124class NotificationWaiter : public message_center::MessageCenterObserver { 125 public: 126 explicit NotificationWaiter(const std::string& target_id) 127 : target_id_(target_id) {} 128 virtual ~NotificationWaiter() {} 129 130 void WaitForNotificationAdded() { 131 DCHECK(!run_loop_.running()); 132 message_center::MessageCenter* message_center = 133 message_center::MessageCenter::Get(); 134 if (message_center->HasNotification(target_id_)) 135 return; 136 137 message_center->AddObserver(this); 138 run_loop_.Run(); 139 message_center->RemoveObserver(this); 140 } 141 142 private: 143 // message_center::MessageCenterObserver overrides: 144 virtual void OnNotificationAdded( 145 const std::string& notification_id) OVERRIDE { 146 if (notification_id == target_id_) 147 run_loop_.Quit(); 148 } 149 150 std::string target_id_; 151 base::RunLoop run_loop_; 152 153 DISALLOW_COPY_AND_ASSIGN(NotificationWaiter); 154}; 155 156class BackgroundContentsServiceNotificationTest 157 : public BrowserWithTestWindowTest { 158 public: 159 BackgroundContentsServiceNotificationTest() {} 160 virtual ~BackgroundContentsServiceNotificationTest() {} 161 162 // Overridden from testing::Test 163 virtual void SetUp() { 164 BrowserWithTestWindowTest::SetUp(); 165 // In ChromeOS environment, BrowserWithTestWindowTest initializes 166 // MessageCenter. 167#if !defined(OS_CHROMEOS) 168 message_center::MessageCenter::Initialize(); 169#endif 170 profile_manager_.reset(new TestingProfileManager( 171 TestingBrowserProcess::GetGlobal())); 172 ASSERT_TRUE(profile_manager_->SetUp()); 173 MessageCenterNotificationManager* manager = 174 static_cast<MessageCenterNotificationManager*>( 175 g_browser_process->notification_ui_manager()); 176 manager->SetMessageCenterTrayDelegateForTest( 177 new message_center::FakeMessageCenterTrayDelegate( 178 message_center::MessageCenter::Get(), base::Closure())); 179 } 180 181 virtual void TearDown() { 182 g_browser_process->notification_ui_manager()->CancelAll(); 183 profile_manager_.reset(); 184#if !defined(OS_CHROMEOS) 185 message_center::MessageCenter::Shutdown(); 186#endif 187 BrowserWithTestWindowTest::TearDown(); 188 } 189 190 protected: 191 // Creates crash notification for the specified extension and returns 192 // the created one. 193 const Notification* CreateCrashNotification( 194 scoped_refptr<extensions::Extension> extension) { 195 std::string notification_id = 196 BackgroundContentsService::GetNotificationIdForExtensionForTesting( 197 extension->id()); 198 NotificationWaiter waiter(notification_id); 199 BackgroundContentsService::ShowBalloonForTesting( 200 extension.get(), profile()); 201 waiter.WaitForNotificationAdded(); 202 203 return g_browser_process->notification_ui_manager()->FindById( 204 notification_id); 205 } 206 207 private: 208 scoped_ptr<TestingProfileManager> profile_manager_; 209 210 DISALLOW_COPY_AND_ASSIGN(BackgroundContentsServiceNotificationTest); 211}; 212#endif // ENABLE_NOTIFICATIONS 213 214TEST_F(BackgroundContentsServiceTest, Create) { 215 // Check for creation and leaks. 216 TestingProfile profile; 217 BackgroundContentsService service(&profile, command_line_.get()); 218} 219 220TEST_F(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy) { 221 TestingProfile profile; 222 BackgroundContentsService service(&profile, command_line_.get()); 223 MockBackgroundContents* contents = new MockBackgroundContents(&profile); 224 EXPECT_FALSE(service.IsTracked(contents)); 225 contents->SendOpenedNotification(&service); 226 EXPECT_TRUE(service.IsTracked(contents)); 227 delete contents; 228 EXPECT_FALSE(service.IsTracked(contents)); 229} 230 231TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAdded) { 232 TestingProfile profile; 233 BackgroundContentsService service(&profile, command_line_.get()); 234 BackgroundContentsServiceFactory::GetInstance()-> 235 RegisterUserPrefsOnBrowserContextForTest(&profile); 236 GURL orig_url; 237 GURL url("http://a/"); 238 GURL url2("http://a/"); 239 { 240 scoped_ptr<MockBackgroundContents> contents( 241 new MockBackgroundContents(&profile)); 242 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 243 contents->SendOpenedNotification(&service); 244 245 contents->Navigate(url); 246 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 247 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 248 249 // Navigate the contents to a new url, should not change url. 250 contents->Navigate(url2); 251 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 252 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 253 } 254 // Contents are deleted, url should persist. 255 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 256} 257 258TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAddedAndClosed) { 259 TestingProfile profile; 260 BackgroundContentsService service(&profile, command_line_.get()); 261 BackgroundContentsServiceFactory::GetInstance()-> 262 RegisterUserPrefsOnBrowserContextForTest(&profile); 263 264 GURL url("http://a/"); 265 MockBackgroundContents* contents = new MockBackgroundContents(&profile); 266 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 267 contents->SendOpenedNotification(&service); 268 contents->Navigate(url); 269 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 270 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 271 272 // Fake a window closed by script. 273 contents->MockClose(&profile); 274 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 275} 276 277// Test what happens if a BackgroundContents shuts down (say, due to a renderer 278// crash) then is restarted. Should not persist URL twice. 279TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) { 280 TestingProfile profile; 281 BackgroundContentsService service(&profile, command_line_.get()); 282 BackgroundContentsServiceFactory::GetInstance()-> 283 RegisterUserPrefsOnBrowserContextForTest(&profile); 284 285 GURL url("http://a/"); 286 { 287 scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( 288 &profile, "appid")); 289 contents->SendOpenedNotification(&service); 290 contents->Navigate(url); 291 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 292 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 293 } 294 // Contents deleted, url should be persisted. 295 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 296 297 { 298 // Reopen the BackgroundContents to the same URL, we should not register the 299 // URL again. 300 scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( 301 &profile, "appid")); 302 contents->SendOpenedNotification(&service); 303 contents->Navigate(url); 304 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 305 } 306} 307 308// Ensures that BackgroundContentsService properly tracks the association 309// between a BackgroundContents and its parent extension, including 310// unregistering the BC when the extension is uninstalled. 311TEST_F(BackgroundContentsServiceTest, TestApplicationIDLinkage) { 312 TestingProfile profile; 313 BackgroundContentsService service(&profile, command_line_.get()); 314 BackgroundContentsServiceFactory::GetInstance()-> 315 RegisterUserPrefsOnBrowserContextForTest(&profile); 316 317 EXPECT_EQ(NULL, 318 service.GetAppBackgroundContents(base::ASCIIToUTF16("appid"))); 319 MockBackgroundContents* contents = new MockBackgroundContents(&profile, 320 "appid"); 321 scoped_ptr<MockBackgroundContents> contents2( 322 new MockBackgroundContents(&profile, "appid2")); 323 contents->SendOpenedNotification(&service); 324 EXPECT_EQ(contents, service.GetAppBackgroundContents(contents->appid())); 325 contents2->SendOpenedNotification(&service); 326 EXPECT_EQ(contents2.get(), service.GetAppBackgroundContents( 327 contents2->appid())); 328 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 329 330 // Navigate the contents, then make sure the one associated with the extension 331 // is unregistered. 332 GURL url("http://a/"); 333 GURL url2("http://b/"); 334 contents->Navigate(url); 335 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 336 contents2->Navigate(url2); 337 EXPECT_EQ(2U, GetPrefs(&profile)->size()); 338 service.ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16("appid")); 339 EXPECT_FALSE(service.IsTracked(contents)); 340 EXPECT_EQ(NULL, 341 service.GetAppBackgroundContents(base::ASCIIToUTF16("appid"))); 342 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 343 EXPECT_EQ(url2.spec(), GetPrefURLForApp(&profile, contents2->appid())); 344} 345 346#if defined(ENABLE_NOTIFICATIONS) 347TEST_F(BackgroundContentsServiceNotificationTest, TestShowBalloon) { 348 scoped_refptr<extensions::Extension> extension = 349 extension_test_util::LoadManifest("image_loading_tracker", "app.json"); 350 ASSERT_TRUE(extension.get()); 351 ASSERT_TRUE(extension->GetManifestData("icons")); 352 353 const Notification* notification = CreateCrashNotification(extension); 354 EXPECT_FALSE(notification->icon().IsEmpty()); 355} 356 357// Verify if a test notification can show the default extension icon for 358// a crash notification for an extension without icon. 359TEST_F(BackgroundContentsServiceNotificationTest, TestShowBalloonNoIcon) { 360 // Extension manifest file with no 'icon' field. 361 scoped_refptr<extensions::Extension> extension = 362 extension_test_util::LoadManifest("app", "manifest.json"); 363 ASSERT_TRUE(extension.get()); 364 ASSERT_FALSE(extension->GetManifestData("icons")); 365 366 const Notification* notification = CreateCrashNotification(extension); 367 EXPECT_FALSE(notification->icon().IsEmpty()); 368} 369#endif 370