background_contents_service_unittest.cc revision eb525c5499e34cc9c4b825d6d9e75bb07cc06ace
1cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)// found in the LICENSE file.
4cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
5cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include <string>
6cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
7cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/basictypes.h"
8cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/command_line.h"
9cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
10cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/prefs/pref_service.h"
11cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
12cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/background/background_contents_service.h"
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/background/background_contents_service_factory.h"
14cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/prefs/scoped_user_pref_update.h"
15cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/tab_contents/background_contents.h"
16cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/browser/ui/browser_list.h"
17cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/common/chrome_notification_types.h"
18cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/common/pref_names.h"
19cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/test/base/testing_browser_process.h"
20cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "chrome/test/base/testing_profile.h"
21cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "content/public/browser/notification_service.h"
22cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "testing/gtest/include/gtest/gtest.h"
23cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "testing/platform_test.h"
24cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)#include "url/gurl.h"
25cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
26cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class BackgroundContentsServiceTest : public testing::Test {
27cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public:
28cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  BackgroundContentsServiceTest() {}
29cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual ~BackgroundContentsServiceTest() {}
30cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual void SetUp() {
31cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    command_line_.reset(new CommandLine(CommandLine::NO_PROGRAM));
32cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
33cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
34cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  const DictionaryValue* GetPrefs(Profile* profile) {
35cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return profile->GetPrefs()->GetDictionary(
36cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        prefs::kRegisteredBackgroundContents);
37cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
38cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
39cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  // Returns the stored pref URL for the passed app id.
40cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  std::string GetPrefURLForApp(Profile* profile, const string16& appid) {
41cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const DictionaryValue* pref = GetPrefs(profile);
42cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    EXPECT_TRUE(pref->HasKey(UTF16ToUTF8(appid)));
43cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    const DictionaryValue* value;
44cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    pref->GetDictionaryWithoutPathExpansion(UTF16ToUTF8(appid), &value);
45cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    std::string url;
46cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    value->GetString("url", &url);
47cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    return url;
48cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
49cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
50cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  scoped_ptr<CommandLine> command_line_;
51cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)};
52cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
53cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)class MockBackgroundContents : public BackgroundContents {
54cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles) public:
55cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  explicit MockBackgroundContents(Profile* profile)
56cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)      : appid_(ASCIIToUTF16("app_id")),
57cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        profile_(profile) {
5846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  }
5946d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)  MockBackgroundContents(Profile* profile, const std::string& id)
6046d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)      : appid_(ASCIIToUTF16(id)),
6146d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        profile_(profile) {
62cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
63cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
64cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  void SendOpenedNotification(BackgroundContentsService* service) {
65cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    string16 frame_name = ASCIIToUTF16("background");
66cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    BackgroundContentsOpenedDetails details = {
67cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        this, frame_name, appid_ };
68cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    service->BackgroundContentsOpened(&details);
69cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
70cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
71cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual void Navigate(GURL url) {
72cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    url_ = url;
73cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::NotificationService::current()->Notify(
74cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
75cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        content::Source<Profile>(profile_),
76cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        content::Details<BackgroundContents>(this));
77cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
78cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  virtual const GURL& GetURL() const OVERRIDE { return url_; }
79cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
80cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  void MockClose(Profile* profile) {
81cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    content::NotificationService::current()->Notify(
82cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED,
83cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        content::Source<Profile>(profile),
84cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        content::Details<BackgroundContents>(this));
85cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)    delete this;
86cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
881320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  virtual ~MockBackgroundContents() {
891320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    content::NotificationService::current()->Notify(
90cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
91cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        content::Source<Profile>(profile_),
92cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)        content::Details<BackgroundContents>(this));
93cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)  }
94cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)
95  const string16& appid() { return appid_; }
96
97 private:
98  GURL url_;
99
100  // The ID of our parent application
101  string16 appid_;
102
103  // Parent profile
104  Profile* profile_;
105};
106
107TEST_F(BackgroundContentsServiceTest, Create) {
108  // Check for creation and leaks.
109  TestingProfile profile;
110  BackgroundContentsService service(&profile, command_line_.get());
111}
112
113TEST_F(BackgroundContentsServiceTest, BackgroundContentsCreateDestroy) {
114  TestingProfile profile;
115  BackgroundContentsService service(&profile, command_line_.get());
116  MockBackgroundContents* contents = new MockBackgroundContents(&profile);
117  EXPECT_FALSE(service.IsTracked(contents));
118  contents->SendOpenedNotification(&service);
119  EXPECT_TRUE(service.IsTracked(contents));
120  delete contents;
121  EXPECT_FALSE(service.IsTracked(contents));
122}
123
124TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAdded) {
125  TestingProfile profile;
126  BackgroundContentsService service(&profile, command_line_.get());
127  BackgroundContentsServiceFactory::GetInstance()->
128      RegisterUserPrefsOnBrowserContext(&profile);
129  GURL orig_url;
130  GURL url("http://a/");
131  GURL url2("http://a/");
132  {
133    scoped_ptr<MockBackgroundContents> contents(
134        new MockBackgroundContents(&profile));
135    EXPECT_EQ(0U, GetPrefs(&profile)->size());
136    contents->SendOpenedNotification(&service);
137
138    contents->Navigate(url);
139    EXPECT_EQ(1U, GetPrefs(&profile)->size());
140    EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid()));
141
142    // Navigate the contents to a new url, should not change url.
143    contents->Navigate(url2);
144    EXPECT_EQ(1U, GetPrefs(&profile)->size());
145    EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid()));
146  }
147  // Contents are deleted, url should persist.
148  EXPECT_EQ(1U, GetPrefs(&profile)->size());
149}
150
151TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAddedAndClosed) {
152  TestingProfile profile;
153  BackgroundContentsService service(&profile, command_line_.get());
154  BackgroundContentsServiceFactory::GetInstance()->
155      RegisterUserPrefsOnBrowserContext(&profile);
156
157  GURL url("http://a/");
158  MockBackgroundContents* contents = new MockBackgroundContents(&profile);
159  EXPECT_EQ(0U, GetPrefs(&profile)->size());
160  contents->SendOpenedNotification(&service);
161  contents->Navigate(url);
162  EXPECT_EQ(1U, GetPrefs(&profile)->size());
163  EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid()));
164
165  // Fake a window closed by script.
166  contents->MockClose(&profile);
167  EXPECT_EQ(0U, GetPrefs(&profile)->size());
168}
169
170// Test what happens if a BackgroundContents shuts down (say, due to a renderer
171// crash) then is restarted. Should not persist URL twice.
172TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) {
173  TestingProfile profile;
174  BackgroundContentsService service(&profile, command_line_.get());
175  BackgroundContentsServiceFactory::GetInstance()->
176      RegisterUserPrefsOnBrowserContext(&profile);
177
178  GURL url("http://a/");
179  {
180    scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents(
181        &profile, "appid"));
182    contents->SendOpenedNotification(&service);
183    contents->Navigate(url);
184    EXPECT_EQ(1U, GetPrefs(&profile)->size());
185    EXPECT_EQ(url.spec(), GetPrefURLForApp(&profile, contents->appid()));
186  }
187  // Contents deleted, url should be persisted.
188  EXPECT_EQ(1U, GetPrefs(&profile)->size());
189
190  {
191    // Reopen the BackgroundContents to the same URL, we should not register the
192    // URL again.
193    scoped_ptr<MockBackgroundContents> contents(new MockBackgroundContents(
194        &profile, "appid"));
195    contents->SendOpenedNotification(&service);
196    contents->Navigate(url);
197    EXPECT_EQ(1U, GetPrefs(&profile)->size());
198  }
199}
200
201// Ensures that BackgroundContentsService properly tracks the association
202// between a BackgroundContents and its parent extension, including
203// unregistering the BC when the extension is uninstalled.
204TEST_F(BackgroundContentsServiceTest, TestApplicationIDLinkage) {
205  TestingProfile profile;
206  BackgroundContentsService service(&profile, command_line_.get());
207  BackgroundContentsServiceFactory::GetInstance()->
208      RegisterUserPrefsOnBrowserContext(&profile);
209
210  EXPECT_EQ(NULL, service.GetAppBackgroundContents(ASCIIToUTF16("appid")));
211  MockBackgroundContents* contents = new MockBackgroundContents(&profile,
212                                                                "appid");
213  scoped_ptr<MockBackgroundContents> contents2(
214      new MockBackgroundContents(&profile, "appid2"));
215  contents->SendOpenedNotification(&service);
216  EXPECT_EQ(contents, service.GetAppBackgroundContents(contents->appid()));
217  contents2->SendOpenedNotification(&service);
218  EXPECT_EQ(contents2.get(), service.GetAppBackgroundContents(
219      contents2->appid()));
220  EXPECT_EQ(0U, GetPrefs(&profile)->size());
221
222  // Navigate the contents, then make sure the one associated with the extension
223  // is unregistered.
224  GURL url("http://a/");
225  GURL url2("http://b/");
226  contents->Navigate(url);
227  EXPECT_EQ(1U, GetPrefs(&profile)->size());
228  contents2->Navigate(url2);
229  EXPECT_EQ(2U, GetPrefs(&profile)->size());
230  service.ShutdownAssociatedBackgroundContents(ASCIIToUTF16("appid"));
231  EXPECT_FALSE(service.IsTracked(contents));
232  EXPECT_EQ(NULL, service.GetAppBackgroundContents(ASCIIToUTF16("appid")));
233  EXPECT_EQ(1U, GetPrefs(&profile)->size());
234  EXPECT_EQ(url2.spec(), GetPrefURLForApp(&profile, contents2->appid()));
235}
236