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 41 class 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 68 class 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. 124 class 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 135 message_center->AddObserver(this); 136 run_loop_.Run(); 137 message_center->RemoveObserver(this); 138 } 139 140 private: 141 // message_center::MessageCenterObserver overrides: 142 virtual void OnNotificationAdded( 143 const std::string& notification_id) OVERRIDE { 144 if (notification_id == target_id_) 145 run_loop_.Quit(); 146 } 147 148 virtual void OnNotificationUpdated( 149 const std::string& notification_id) OVERRIDE { 150 if (notification_id == target_id_) 151 run_loop_.Quit(); 152 } 153 154 std::string target_id_; 155 base::RunLoop run_loop_; 156 157 DISALLOW_COPY_AND_ASSIGN(NotificationWaiter); 158 }; 159 160 class BackgroundContentsServiceNotificationTest 161 : public BrowserWithTestWindowTest { 162 public: 163 BackgroundContentsServiceNotificationTest() {} 164 virtual ~BackgroundContentsServiceNotificationTest() {} 165 166 // Overridden from testing::Test 167 virtual void SetUp() { 168 BrowserWithTestWindowTest::SetUp(); 169 // In ChromeOS environment, BrowserWithTestWindowTest initializes 170 // MessageCenter. 171 #if !defined(OS_CHROMEOS) 172 message_center::MessageCenter::Initialize(); 173 #endif 174 profile_manager_.reset(new TestingProfileManager( 175 TestingBrowserProcess::GetGlobal())); 176 ASSERT_TRUE(profile_manager_->SetUp()); 177 MessageCenterNotificationManager* manager = 178 static_cast<MessageCenterNotificationManager*>( 179 g_browser_process->notification_ui_manager()); 180 manager->SetMessageCenterTrayDelegateForTest( 181 new message_center::FakeMessageCenterTrayDelegate( 182 message_center::MessageCenter::Get(), base::Closure())); 183 } 184 185 virtual void TearDown() { 186 g_browser_process->notification_ui_manager()->CancelAll(); 187 profile_manager_.reset(); 188 #if !defined(OS_CHROMEOS) 189 message_center::MessageCenter::Shutdown(); 190 #endif 191 BrowserWithTestWindowTest::TearDown(); 192 } 193 194 protected: 195 // Creates crash notification for the specified extension and returns 196 // the created one. 197 const Notification* CreateCrashNotification( 198 scoped_refptr<extensions::Extension> extension) { 199 std::string notification_id = 200 BackgroundContentsService::GetNotificationIdForExtensionForTesting( 201 extension->id()); 202 NotificationWaiter waiter(notification_id); 203 BackgroundContentsService::ShowBalloonForTesting( 204 extension.get(), profile()); 205 waiter.WaitForNotificationAdded(); 206 207 return g_browser_process->notification_ui_manager()->FindById( 208 notification_id); 209 } 210 211 private: 212 scoped_ptr<TestingProfileManager> profile_manager_; 213 214 DISALLOW_COPY_AND_ASSIGN(BackgroundContentsServiceNotificationTest); 215 }; 216 #endif // ENABLE_NOTIFICATIONS 217 218 TEST_F(BackgroundContentsServiceTest, Create) { 219 // Check for creation and leaks. 220 TestingProfile profile; 221 BackgroundContentsService service(&profile, command_line_.get()); 222 } 223 224 TEST_F(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy) { 225 TestingProfile profile; 226 BackgroundContentsService service(&profile, command_line_.get()); 227 MockBackgroundContents* contents = new MockBackgroundContents(&profile); 228 EXPECT_FALSE(service.IsTracked(contents)); 229 contents->SendOpenedNotification(&service); 230 EXPECT_TRUE(service.IsTracked(contents)); 231 delete contents; 232 EXPECT_FALSE(service.IsTracked(contents)); 233 } 234 235 TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAdded) { 236 TestingProfile profile; 237 BackgroundContentsService service(&profile, command_line_.get()); 238 BackgroundContentsServiceFactory::GetInstance()-> 239 RegisterUserPrefsOnBrowserContextForTest(&profile); 240 GURL orig_url; 241 GURL url("http://a/"); 242 GURL url2("http://a/"); 243 { 244 scoped_ptr<MockBackgroundContents> contents( 245 new MockBackgroundContents(&profile)); 246 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 247 contents->SendOpenedNotification(&service); 248 249 contents->Navigate(url); 250 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 251 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 252 253 // Navigate the contents to a new url, should not change url. 254 contents->Navigate(url2); 255 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 256 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 257 } 258 // Contents are deleted, url should persist. 259 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 260 } 261 262 TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAddedAndClosed) { 263 TestingProfile profile; 264 BackgroundContentsService service(&profile, command_line_.get()); 265 BackgroundContentsServiceFactory::GetInstance()-> 266 RegisterUserPrefsOnBrowserContextForTest(&profile); 267 268 GURL url("http://a/"); 269 MockBackgroundContents* contents = new MockBackgroundContents(&profile); 270 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 271 contents->SendOpenedNotification(&service); 272 contents->Navigate(url); 273 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 274 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 275 276 // Fake a window closed by script. 277 contents->MockClose(&profile); 278 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 279 } 280 281 // Test what happens if a BackgroundContents shuts down (say, due to a renderer 282 // crash) then is restarted. Should not persist URL twice. 283 TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) { 284 TestingProfile profile; 285 BackgroundContentsService service(&profile, command_line_.get()); 286 BackgroundContentsServiceFactory::GetInstance()-> 287 RegisterUserPrefsOnBrowserContextForTest(&profile); 288 289 GURL url("http://a/"); 290 { 291 scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( 292 &profile, "appid")); 293 contents->SendOpenedNotification(&service); 294 contents->Navigate(url); 295 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 296 EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid())); 297 } 298 // Contents deleted, url should be persisted. 299 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 300 301 { 302 // Reopen the BackgroundContents to the same URL, we should not register the 303 // URL again. 304 scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents( 305 &profile, "appid")); 306 contents->SendOpenedNotification(&service); 307 contents->Navigate(url); 308 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 309 } 310 } 311 312 // Ensures that BackgroundContentsService properly tracks the association 313 // between a BackgroundContents and its parent extension, including 314 // unregistering the BC when the extension is uninstalled. 315 TEST_F(BackgroundContentsServiceTest, TestApplicationIDLinkage) { 316 TestingProfile profile; 317 BackgroundContentsService service(&profile, command_line_.get()); 318 BackgroundContentsServiceFactory::GetInstance()-> 319 RegisterUserPrefsOnBrowserContextForTest(&profile); 320 321 EXPECT_EQ(NULL, 322 service.GetAppBackgroundContents(base::ASCIIToUTF16("appid"))); 323 MockBackgroundContents* contents = new MockBackgroundContents(&profile, 324 "appid"); 325 scoped_ptr<MockBackgroundContents> contents2( 326 new MockBackgroundContents(&profile, "appid2")); 327 contents->SendOpenedNotification(&service); 328 EXPECT_EQ(contents, service.GetAppBackgroundContents(contents->appid())); 329 contents2->SendOpenedNotification(&service); 330 EXPECT_EQ(contents2.get(), service.GetAppBackgroundContents( 331 contents2->appid())); 332 EXPECT_EQ(0U, GetPrefs(&profile)->size()); 333 334 // Navigate the contents, then make sure the one associated with the extension 335 // is unregistered. 336 GURL url("http://a/"); 337 GURL url2("http://b/"); 338 contents->Navigate(url); 339 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 340 contents2->Navigate(url2); 341 EXPECT_EQ(2U, GetPrefs(&profile)->size()); 342 service.ShutdownAssociatedBackgroundContents(base::ASCIIToUTF16("appid")); 343 EXPECT_FALSE(service.IsTracked(contents)); 344 EXPECT_EQ(NULL, 345 service.GetAppBackgroundContents(base::ASCIIToUTF16("appid"))); 346 EXPECT_EQ(1U, GetPrefs(&profile)->size()); 347 EXPECT_EQ(url2.spec(), GetPrefURLForApp(&profile, contents2->appid())); 348 } 349 350 #if defined(ENABLE_NOTIFICATIONS) 351 TEST_F(BackgroundContentsServiceNotificationTest, TestShowBalloon) { 352 scoped_refptr<extensions::Extension> extension = 353 extension_test_util::LoadManifest("image_loading_tracker", "app.json"); 354 ASSERT_TRUE(extension.get()); 355 ASSERT_TRUE(extension->GetManifestData("icons")); 356 357 const Notification* notification = CreateCrashNotification(extension); 358 EXPECT_FALSE(notification->icon().IsEmpty()); 359 } 360 361 // Verify if a test notification can show the default extension icon for 362 // a crash notification for an extension without icon. 363 TEST_F(BackgroundContentsServiceNotificationTest, TestShowBalloonNoIcon) { 364 // Extension manifest file with no 'icon' field. 365 scoped_refptr<extensions::Extension> extension = 366 extension_test_util::LoadManifest("app", "manifest.json"); 367 ASSERT_TRUE(extension.get()); 368 ASSERT_FALSE(extension->GetManifestData("icons")); 369 370 const Notification* notification = CreateCrashNotification(extension); 371 EXPECT_FALSE(notification->icon().IsEmpty()); 372 } 373 374 TEST_F(BackgroundContentsServiceNotificationTest, TestShowTwoBalloons) { 375 TestingProfile profile; 376 scoped_refptr<extensions::Extension> extension = 377 extension_test_util::LoadManifest("app", "manifest.json"); 378 ASSERT_TRUE(extension.get()); 379 CreateCrashNotification(extension); 380 CreateCrashNotification(extension); 381 382 message_center::MessageCenter* message_center = 383 message_center::MessageCenter::Get(); 384 message_center::NotificationList::Notifications notifications = 385 message_center->GetVisibleNotifications(); 386 ASSERT_EQ(1u, notifications.size()); 387 } 388 #endif 389