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/prefs/pref_service.h"
11 #include "base/prefs/scoped_user_pref_update.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/background/background_contents_service.h"
14 #include "chrome/browser/background/background_contents_service_factory.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/tab_contents/background_contents.h"
17 #include "chrome/browser/ui/browser_list.h"
18 #include "chrome/common/pref_names.h"
19 #include "chrome/test/base/testing_browser_process.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "content/public/browser/notification_service.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "testing/platform_test.h"
24 #include "url/gurl.h"
25
26 class BackgroundContentsServiceTest : public testing::Test {
27 public:
BackgroundContentsServiceTest()28 BackgroundContentsServiceTest() {}
~BackgroundContentsServiceTest()29 virtual ~BackgroundContentsServiceTest() {}
SetUp()30 virtual void SetUp() {
31 command_line_.reset(new CommandLine(CommandLine::NO_PROGRAM));
32 }
33
GetPrefs(Profile * profile)34 const DictionaryValue* GetPrefs(Profile* profile) {
35 return profile->GetPrefs()->GetDictionary(
36 prefs::kRegisteredBackgroundContents);
37 }
38
39 // Returns the stored pref URL for the passed app id.
GetPrefURLForApp(Profile * profile,const base::string16 & appid)40 std::string GetPrefURLForApp(Profile* profile, const base::string16& appid) {
41 const DictionaryValue* pref = GetPrefs(profile);
42 EXPECT_TRUE(pref->HasKey(UTF16ToUTF8(appid)));
43 const DictionaryValue* value;
44 pref->GetDictionaryWithoutPathExpansion(UTF16ToUTF8(appid), &value);
45 std::string url;
46 value->GetString("url", &url);
47 return url;
48 }
49
50 scoped_ptr<CommandLine> command_line_;
51 };
52
53 class MockBackgroundContents : public BackgroundContents {
54 public:
MockBackgroundContents(Profile * profile)55 explicit MockBackgroundContents(Profile* profile)
56 : appid_(ASCIIToUTF16("app_id")),
57 profile_(profile) {
58 }
MockBackgroundContents(Profile * profile,const std::string & id)59 MockBackgroundContents(Profile* profile, const std::string& id)
60 : appid_(ASCIIToUTF16(id)),
61 profile_(profile) {
62 }
63
SendOpenedNotification(BackgroundContentsService * service)64 void SendOpenedNotification(BackgroundContentsService* service) {
65 base::string16 frame_name = ASCIIToUTF16("background");
66 BackgroundContentsOpenedDetails details = {
67 this, frame_name, appid_ };
68 service->BackgroundContentsOpened(&details);
69 }
70
Navigate(GURL url)71 virtual void Navigate(GURL url) {
72 url_ = url;
73 content::NotificationService::current()->Notify(
74 chrome::NOTIFICATION_BACKGROUND_CONTENTS_NAVIGATED,
75 content::Source<Profile>(profile_),
76 content::Details<BackgroundContents>(this));
77 }
GetURL() const78 virtual const GURL& GetURL() const OVERRIDE { return url_; }
79
MockClose(Profile * profile)80 void MockClose(Profile* profile) {
81 content::NotificationService::current()->Notify(
82 chrome::NOTIFICATION_BACKGROUND_CONTENTS_CLOSED,
83 content::Source<Profile>(profile),
84 content::Details<BackgroundContents>(this));
85 delete this;
86 }
87
~MockBackgroundContents()88 virtual ~MockBackgroundContents() {
89 content::NotificationService::current()->Notify(
90 chrome::NOTIFICATION_BACKGROUND_CONTENTS_DELETED,
91 content::Source<Profile>(profile_),
92 content::Details<BackgroundContents>(this));
93 }
94
appid()95 const base::string16& appid() { return appid_; }
96
97 private:
98 GURL url_;
99
100 // The ID of our parent application
101 base::string16 appid_;
102
103 // Parent profile
104 Profile* profile_;
105 };
106
TEST_F(BackgroundContentsServiceTest,Create)107 TEST_F(BackgroundContentsServiceTest, Create) {
108 // Check for creation and leaks.
109 TestingProfile profile;
110 BackgroundContentsService service(&profile, command_line_.get());
111 }
112
TEST_F(BackgroundContentsServiceTest,BackgroundContentsCreateDestroy)113 TEST_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
TEST_F(BackgroundContentsServiceTest,BackgroundContentsUrlAdded)124 TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAdded) {
125 TestingProfile profile;
126 BackgroundContentsService service(&profile, command_line_.get());
127 BackgroundContentsServiceFactory::GetInstance()->
128 RegisterUserPrefsOnBrowserContextForTest(&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
TEST_F(BackgroundContentsServiceTest,BackgroundContentsUrlAddedAndClosed)151 TEST_F(BackgroundContentsServiceTest, BackgroundContentsUrlAddedAndClosed) {
152 TestingProfile profile;
153 BackgroundContentsService service(&profile, command_line_.get());
154 BackgroundContentsServiceFactory::GetInstance()->
155 RegisterUserPrefsOnBrowserContextForTest(&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.
TEST_F(BackgroundContentsServiceTest,RestartBackgroundContents)172 TEST_F(BackgroundContentsServiceTest, RestartBackgroundContents) {
173 TestingProfile profile;
174 BackgroundContentsService service(&profile, command_line_.get());
175 BackgroundContentsServiceFactory::GetInstance()->
176 RegisterUserPrefsOnBrowserContextForTest(&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.
TEST_F(BackgroundContentsServiceTest,TestApplicationIDLinkage)204 TEST_F(BackgroundContentsServiceTest, TestApplicationIDLinkage) {
205 TestingProfile profile;
206 BackgroundContentsService service(&profile, command_line_.get());
207 BackgroundContentsServiceFactory::GetInstance()->
208 RegisterUserPrefsOnBrowserContextForTest(&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