Home | History | Annotate | Download | only in background
      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