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