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