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 "base/command_line.h"
6#include "base/files/file_path.h"
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/bookmarks/bookmark_model.h"
9#include "chrome/browser/bookmarks/bookmark_model_factory.h"
10#include "chrome/browser/bookmarks/bookmark_utils.h"
11#include "chrome/browser/chrome_notification_types.h"
12#include "chrome/browser/extensions/browser_action_test_util.h"
13#include "chrome/browser/extensions/extension_apitest.h"
14#include "chrome/browser/extensions/extension_host.h"
15#include "chrome/browser/extensions/extension_service.h"
16#include "chrome/browser/extensions/extension_system.h"
17#include "chrome/browser/extensions/extension_test_message_listener.h"
18#include "chrome/browser/extensions/lazy_background_page_test_util.h"
19#include "chrome/browser/profiles/profile.h"
20#include "chrome/browser/ui/browser.h"
21#include "chrome/browser/ui/browser_window.h"
22#include "chrome/browser/ui/omnibox/location_bar.h"
23#include "chrome/browser/ui/tabs/tab_strip_model.h"
24#include "chrome/common/chrome_switches.h"
25#include "chrome/common/extensions/extension.h"
26#include "chrome/common/url_constants.h"
27#include "chrome/test/base/ui_test_utils.h"
28#include "content/public/browser/notification_service.h"
29#include "content/public/browser/web_contents.h"
30#include "content/public/test/browser_test_utils.h"
31#include "net/dns/mock_host_resolver.h"
32#include "net/test/embedded_test_server/embedded_test_server.h"
33#include "url/gurl.h"
34
35using extensions::Extension;
36
37namespace {
38
39// This unfortunate bit of silliness is necessary when loading an extension in
40// incognito. The goal is to load the extension, enable incognito, then wait
41// for both background pages to load and close. The problem is that enabling
42// incognito involves reloading the extension - and the background pages may
43// have already loaded once before then. So we wait until the extension is
44// unloaded before listening to the background page notifications.
45class LoadedIncognitoObserver : public content::NotificationObserver {
46 public:
47  explicit LoadedIncognitoObserver(Profile* profile) : profile_(profile) {
48    registrar_.Add(this, chrome::NOTIFICATION_EXTENSION_UNLOADED,
49                   content::Source<Profile>(profile));
50  }
51
52  void Wait() {
53    ASSERT_TRUE(original_complete_.get());
54    original_complete_->Wait();
55    incognito_complete_->Wait();
56  }
57
58 private:
59
60  virtual void Observe(
61      int type,
62      const content::NotificationSource& source,
63      const content::NotificationDetails& details) OVERRIDE {
64    original_complete_.reset(new LazyBackgroundObserver(profile_));
65    incognito_complete_.reset(
66        new LazyBackgroundObserver(profile_->GetOffTheRecordProfile()));
67  }
68
69  Profile* profile_;
70  content::NotificationRegistrar registrar_;
71  scoped_ptr<LazyBackgroundObserver> original_complete_;
72  scoped_ptr<LazyBackgroundObserver> incognito_complete_;
73};
74
75}  // namespace
76
77class LazyBackgroundPageApiTest : public ExtensionApiTest {
78 public:
79  virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
80    ExtensionApiTest::SetUpCommandLine(command_line);
81    // Set shorter delays to prevent test timeouts.
82    command_line->AppendSwitchASCII(switches::kEventPageIdleTime, "1");
83    command_line->AppendSwitchASCII(switches::kEventPageSuspendingTime, "1");
84  }
85
86  // Loads the extension, which temporarily starts the lazy background page
87  // to dispatch the onInstalled event. We wait until it shuts down again.
88  const Extension* LoadExtensionAndWait(const std::string& test_name) {
89    LazyBackgroundObserver page_complete;
90    base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
91        AppendASCII(test_name);
92    const Extension* extension = LoadExtension(extdir);
93    if (extension)
94      page_complete.Wait();
95    return extension;
96  }
97};
98
99IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BrowserActionCreateTab) {
100  ASSERT_TRUE(LoadExtensionAndWait("browser_action_create_tab"));
101
102  // Lazy Background Page doesn't exist yet.
103  ExtensionProcessManager* pm =
104      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
105  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
106  int num_tabs_before = browser()->tab_strip_model()->count();
107
108  // Observe background page being created and closed after
109  // the browser action is clicked.
110  LazyBackgroundObserver page_complete;
111  BrowserActionTestUtil(browser()).Press(0);
112  page_complete.Wait();
113
114  // Background page created a new tab before it closed.
115  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
116  EXPECT_EQ(num_tabs_before + 1, browser()->tab_strip_model()->count());
117  EXPECT_EQ(std::string(chrome::kChromeUIExtensionsURL),
118            browser()->tab_strip_model()->GetActiveWebContents()->
119                GetURL().spec());
120}
121
122IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest,
123                       BrowserActionCreateTabAfterCallback) {
124  ASSERT_TRUE(LoadExtensionAndWait("browser_action_with_callback"));
125
126  // Lazy Background Page doesn't exist yet.
127  ExtensionProcessManager* pm =
128      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
129  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
130  int num_tabs_before = browser()->tab_strip_model()->count();
131
132  // Observe background page being created and closed after
133  // the browser action is clicked.
134  LazyBackgroundObserver page_complete;
135  BrowserActionTestUtil(browser()).Press(0);
136  page_complete.Wait();
137
138  // Background page is closed after creating a new tab.
139  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
140  EXPECT_EQ(num_tabs_before + 1, browser()->tab_strip_model()->count());
141}
142
143IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, BroadcastEvent) {
144  ASSERT_TRUE(StartEmbeddedTestServer());
145
146  const Extension* extension = LoadExtensionAndWait("broadcast_event");
147  ASSERT_TRUE(extension);
148
149  // Lazy Background Page doesn't exist yet.
150  ExtensionProcessManager* pm =
151      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
152  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
153  int num_page_actions = browser()->window()->GetLocationBar()->
154      GetLocationBarForTesting()->PageActionVisibleCount();
155
156  // Open a tab to a URL that will trigger the page action to show.
157  LazyBackgroundObserver page_complete;
158  content::WindowedNotificationObserver page_action_changed(
159        chrome::NOTIFICATION_EXTENSION_PAGE_ACTION_VISIBILITY_CHANGED,
160        content::NotificationService::AllSources());
161  ui_test_utils::NavigateToURL(
162      browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
163  page_complete.Wait();
164
165  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
166
167  // Page action is shown.
168  page_action_changed.Wait();
169  EXPECT_EQ(num_page_actions + 1,
170            browser()->window()->GetLocationBar()->
171                GetLocationBarForTesting()->PageActionVisibleCount());
172}
173
174IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Filters) {
175  const Extension* extension = LoadExtensionAndWait("filters");
176  ASSERT_TRUE(extension);
177
178  // Lazy Background Page doesn't exist yet.
179  ExtensionProcessManager* pm =
180      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
181  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
182
183  // Open a tab to a URL that will fire a webNavigation event.
184  LazyBackgroundObserver page_complete;
185  ui_test_utils::NavigateToURL(
186      browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
187  page_complete.Wait();
188}
189
190// Tests that the lazy background page receives the onInstalled event and shuts
191// down.
192IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnInstalled) {
193  ResultCatcher catcher;
194  ASSERT_TRUE(LoadExtensionAndWait("on_installed"));
195  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
196
197  // Lazy Background Page has been shut down.
198  ExtensionProcessManager* pm =
199      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
200  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
201}
202
203// Tests that the lazy background page stays alive until all visible views are
204// closed.
205IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForView) {
206  LazyBackgroundObserver page_complete;
207  ResultCatcher catcher;
208  base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
209      AppendASCII("wait_for_view");
210  const Extension* extension = LoadExtension(extdir);
211  ASSERT_TRUE(extension);
212  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
213
214  // The extension should've opened a new tab to an extension page.
215  EXPECT_EQ(extension->GetResourceURL("extension_page.html").spec(),
216            browser()->tab_strip_model()->GetActiveWebContents()->
217                GetURL().spec());
218
219  // Lazy Background Page still exists, because the extension created a new tab
220  // to an extension page.
221  ExtensionProcessManager* pm =
222      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
223  EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
224
225  // Close the new tab.
226  browser()->tab_strip_model()->CloseWebContentsAt(
227      browser()->tab_strip_model()->active_index(), TabStripModel::CLOSE_NONE);
228  page_complete.Wait();
229
230  // Lazy Background Page has been shut down.
231  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
232}
233
234// Tests that the lazy background page stays alive until all network requests
235// are complete.
236IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, WaitForRequest) {
237  host_resolver()->AddRule("*", "127.0.0.1");
238  ASSERT_TRUE(StartEmbeddedTestServer());
239
240  LazyBackgroundObserver page_complete;
241  ResultCatcher catcher;
242  base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
243      AppendASCII("wait_for_request");
244  const Extension* extension = LoadExtension(extdir);
245  ASSERT_TRUE(extension);
246  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
247
248  // Lazy Background Page still exists, because the extension started a request.
249  ExtensionProcessManager* pm =
250      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
251  extensions::ExtensionHost* host =
252      pm->GetBackgroundHostForExtension(last_loaded_extension_id_);
253  ASSERT_TRUE(host);
254
255  // Abort the request.
256  bool result = false;
257  EXPECT_TRUE(content::ExecuteScriptAndExtractBool(
258      host->render_view_host(), "abortRequest()", &result));
259  EXPECT_TRUE(result);
260  page_complete.Wait();
261
262  // Lazy Background Page has been shut down.
263  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
264}
265
266// Tests that the lazy background page stays alive until all visible views are
267// closed.
268// http://crbug.com/175778; test fails frequently on OS X
269#if defined(OS_MACOSX)
270#define MAYBE_WaitForNTP DISABLED_WaitForNTP
271#else
272#define MAYBE_WaitForNTP WaitForNTP
273#endif
274IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_WaitForNTP) {
275  LazyBackgroundObserver lazybg;
276  ResultCatcher catcher;
277  base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
278      AppendASCII("wait_for_ntp");
279  const Extension* extension = LoadExtension(extdir);
280  ASSERT_TRUE(extension);
281  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
282
283  // The extension should've opened a new tab to an extension page.
284  EXPECT_EQ(std::string(chrome::kChromeUINewTabURL),
285            browser()->tab_strip_model()->GetActiveWebContents()->
286                GetURL().spec());
287
288  // Lazy Background Page still exists, because the extension created a new tab
289  // to an extension page.
290  ExtensionProcessManager* pm =
291      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
292  EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
293
294  // Navigate away from the NTP, which should close the event page.
295  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
296  lazybg.Wait();
297
298  // Lazy Background Page has been shut down.
299  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
300}
301
302// See crbug.com/248437
303#if defined(OS_WIN)
304#define MAYBE_IncognitoSplitMode DISABLED_IncognitoSplitMode
305#else
306#define MAYBE_IncognitoSplitMode IncognitoSplitMode
307#endif
308
309// Tests that an incognito split mode extension gets 2 lazy background pages,
310// and they each load and unload at the proper times.
311IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, MAYBE_IncognitoSplitMode) {
312  // Open incognito window.
313  Browser* incognito_browser = ui_test_utils::OpenURLOffTheRecord(
314      browser()->profile(), GURL("about:blank"));
315
316  // Load the extension with incognito enabled.
317  {
318    LoadedIncognitoObserver loaded(browser()->profile());
319    base::FilePath extdir = test_data_dir_.AppendASCII("lazy_background_page").
320        AppendASCII("incognito_split");
321    ASSERT_TRUE(LoadExtensionIncognito(extdir));
322    loaded.Wait();
323  }
324
325  // Lazy Background Page doesn't exist yet.
326  ExtensionProcessManager* pm =
327      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
328  ExtensionProcessManager* pmi =
329      extensions::ExtensionSystem::Get(incognito_browser->profile())->
330          process_manager();
331  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
332  EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id_));
333
334  // Trigger a browserAction event in the original profile and ensure only
335  // the original event page received it (since the event is scoped to the
336  // profile).
337  {
338    ExtensionTestMessageListener listener("waiting", false);
339    ExtensionTestMessageListener listener_incognito("waiting_incognito", false);
340
341    LazyBackgroundObserver page_complete(browser()->profile());
342    BrowserActionTestUtil(browser()).Press(0);
343    page_complete.Wait();
344
345    // Only the original event page received the message.
346    EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
347    EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id_));
348    EXPECT_TRUE(listener.was_satisfied());
349    EXPECT_FALSE(listener_incognito.was_satisfied());
350  }
351
352  // Trigger a bookmark created event and ensure both pages receive it.
353  {
354    ExtensionTestMessageListener listener("waiting", false);
355    ExtensionTestMessageListener listener_incognito("waiting_incognito", false);
356
357    LazyBackgroundObserver page_complete(browser()->profile()),
358                           page2_complete(incognito_browser->profile());
359    BookmarkModel* bookmark_model =
360        BookmarkModelFactory::GetForProfile(browser()->profile());
361    ui_test_utils::WaitForBookmarkModelToLoad(bookmark_model);
362    const BookmarkNode* parent = bookmark_model->bookmark_bar_node();
363    bookmark_model->AddURL(
364        parent, 0, ASCIIToUTF16("Title"), GURL("about:blank"));
365    page_complete.Wait();
366    page2_complete.Wait();
367
368    // Both pages received the message.
369    EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
370    EXPECT_FALSE(pmi->GetBackgroundHostForExtension(last_loaded_extension_id_));
371    EXPECT_TRUE(listener.was_satisfied());
372    EXPECT_TRUE(listener_incognito.was_satisfied());
373  }
374}
375
376// Tests that messages from the content script activate the lazy background
377// page, and keep it alive until all channels are closed.
378IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, Messaging) {
379  ASSERT_TRUE(StartEmbeddedTestServer());
380  ASSERT_TRUE(LoadExtensionAndWait("messaging"));
381
382  // Lazy Background Page doesn't exist yet.
383  ExtensionProcessManager* pm =
384      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
385  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
386  EXPECT_EQ(1, browser()->tab_strip_model()->count());
387
388  // Navigate to a page that opens a message channel to the background page.
389  ResultCatcher catcher;
390  LazyBackgroundObserver lazybg;
391  ui_test_utils::NavigateToURL(
392      browser(), embedded_test_server()->GetURL("/extensions/test_file.html"));
393  lazybg.WaitUntilLoaded();
394
395  // Background page got the content script's message and is still loaded
396  // until we close the channel.
397  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
398  EXPECT_TRUE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
399
400  // Navigate away, closing the message channel and therefore the background
401  // page.
402  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
403  lazybg.WaitUntilClosed();
404
405  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
406}
407
408// Tests that the lazy background page receives the unload event when we
409// close it, and that it can execute simple API calls that don't require an
410// asynchronous response.
411IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, OnUnload) {
412  ASSERT_TRUE(LoadExtensionAndWait("on_unload"));
413
414  // Lazy Background Page has been shut down.
415  ExtensionProcessManager* pm =
416      extensions::ExtensionSystem::Get(browser()->profile())->process_manager();
417  EXPECT_FALSE(pm->GetBackgroundHostForExtension(last_loaded_extension_id_));
418
419  // The browser action has a new title.
420  BrowserActionTestUtil browser_action(browser());
421  ASSERT_EQ(1, browser_action.NumberOfBrowserActions());
422  EXPECT_EQ("Success", browser_action.GetTooltip(0));
423}
424
425// Tests that both a regular page and an event page will receive events when
426// the event page is not loaded.
427IN_PROC_BROWSER_TEST_F(LazyBackgroundPageApiTest, EventDispatchToTab) {
428  ResultCatcher catcher;
429  catcher.RestrictToProfile(browser()->profile());
430
431  const extensions::Extension* extension =
432      LoadExtensionAndWait("event_dispatch_to_tab");
433
434  ExtensionTestMessageListener page_ready("ready", true);
435  GURL page_url = extension->GetResourceURL("page.html");
436  ui_test_utils::NavigateToURL(browser(), page_url);
437  EXPECT_TRUE(page_ready.WaitUntilSatisfied());
438
439  // After the event is sent below, wait for the event page to have received
440  // the event before proceeding with the test.  This allows the regular page
441  // to test that the event page received the event, which makes the pass/fail
442  // logic simpler.
443  ExtensionTestMessageListener event_page_ready("ready", true);
444
445  // Send an event by making a bookmark.
446  BookmarkModel* bookmark_model =
447      BookmarkModelFactory::GetForProfile(browser()->profile());
448  ui_test_utils::WaitForBookmarkModelToLoad(bookmark_model);
449  bookmark_utils::AddIfNotBookmarked(
450      bookmark_model,
451      GURL("http://www.google.com"),
452      UTF8ToUTF16("Google"));
453
454  EXPECT_TRUE(event_page_ready.WaitUntilSatisfied());
455
456  page_ready.Reply("go");
457
458  EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
459}
460
461// TODO: background page with timer.
462// TODO: background page that interacts with popup.
463