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/compiler_specific.h"
8#include "base/file_path.h"
9#include "base/sys_info.h"
10#include "base/utf_string_conversions.h"
11#include "chrome/app/chrome_command_ids.h"
12#include "chrome/browser/defaults.h"
13#include "chrome/browser/extensions/extension_browsertest.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/extension_tab_helper.h"
16#include "chrome/browser/profiles/profile.h"
17#include "chrome/browser/tabs/pinned_tab_codec.h"
18#include "chrome/browser/tabs/tab_strip_model.h"
19#include "chrome/browser/translate/translate_tab_helper.h"
20#include "chrome/browser/ui/app_modal_dialogs/app_modal_dialog.h"
21#include "chrome/browser/ui/app_modal_dialogs/js_modal_dialog.h"
22#include "chrome/browser/ui/app_modal_dialogs/native_app_modal_dialog.h"
23#include "chrome/browser/ui/browser.h"
24#include "chrome/browser/ui/browser_init.h"
25#include "chrome/browser/ui/browser_list.h"
26#include "chrome/browser/ui/browser_navigator.h"
27#include "chrome/browser/ui/browser_window.h"
28#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
29#include "chrome/common/chrome_switches.h"
30#include "chrome/common/extensions/extension.h"
31#include "chrome/common/url_constants.h"
32#include "chrome/test/in_process_browser_test.h"
33#include "chrome/test/ui_test_utils.h"
34#include "content/browser/renderer_host/render_process_host.h"
35#include "content/browser/renderer_host/render_view_host.h"
36#include "content/browser/tab_contents/tab_contents.h"
37#include "content/common/notification_source.h"
38#include "content/common/page_transition_types.h"
39#include "grit/chromium_strings.h"
40#include "grit/generated_resources.h"
41#include "net/base/mock_host_resolver.h"
42#include "net/test/test_server.h"
43#include "ui/base/l10n/l10n_util.h"
44
45#if defined(OS_WIN)
46#include "base/i18n/rtl.h"
47#include "chrome/browser/browser_process.h"
48#endif
49
50namespace {
51
52const char* kBeforeUnloadHTML =
53    "<html><head><title>beforeunload</title></head><body>"
54    "<script>window.onbeforeunload=function(e){return 'foo'}</script>"
55    "</body></html>";
56
57const char* kOpenNewBeforeUnloadPage =
58    "w=window.open(); w.onbeforeunload=function(e){return 'foo'};";
59
60const FilePath::CharType* kTitle1File = FILE_PATH_LITERAL("title1.html");
61const FilePath::CharType* kTitle2File = FILE_PATH_LITERAL("title2.html");
62
63const FilePath::CharType kDocRoot[] = FILE_PATH_LITERAL("chrome/test/data");
64
65// Given a page title, returns the expected window caption string.
66std::wstring WindowCaptionFromPageTitle(std::wstring page_title) {
67#if defined(OS_MACOSX) || defined(OS_CHROMEOS)
68  // On Mac or ChromeOS, we don't want to suffix the page title with
69  // the application name.
70  if (page_title.empty()) {
71    return UTF16ToWideHack(
72        l10n_util::GetStringUTF16(IDS_BROWSER_WINDOW_MAC_TAB_UNTITLED));
73  }
74  return page_title;
75#else
76  if (page_title.empty())
77    return UTF16ToWideHack(l10n_util::GetStringUTF16(IDS_PRODUCT_NAME));
78
79  return UTF16ToWideHack(
80      l10n_util::GetStringFUTF16(IDS_BROWSER_WINDOW_TITLE_FORMAT,
81                                 WideToUTF16Hack(page_title)));
82#endif
83}
84
85// Returns the number of active RenderProcessHosts.
86int CountRenderProcessHosts() {
87  int result = 0;
88  for (RenderProcessHost::iterator i(RenderProcessHost::AllHostsIterator());
89       !i.IsAtEnd(); i.Advance())
90    ++result;
91  return result;
92}
93
94class MockTabStripModelObserver : public TabStripModelObserver {
95 public:
96  MockTabStripModelObserver() : closing_count_(0) {}
97
98  virtual void TabClosingAt(TabStripModel* tab_strip_model,
99                            TabContentsWrapper* contents,
100                            int index) {
101    closing_count_++;
102  }
103
104  int closing_count() const { return closing_count_; }
105
106 private:
107  int closing_count_;
108
109  DISALLOW_COPY_AND_ASSIGN(MockTabStripModelObserver);
110};
111
112// Used by CloseWithAppMenuOpen. Invokes CloseWindow on the supplied browser.
113class CloseWindowTask : public Task {
114 public:
115  explicit CloseWindowTask(Browser* browser) : browser_(browser) {}
116
117  virtual void Run() {
118    browser_->CloseWindow();
119  }
120
121 private:
122  Browser* browser_;
123
124  DISALLOW_COPY_AND_ASSIGN(CloseWindowTask);
125};
126
127// Used by CloseWithAppMenuOpen. Posts a CloseWindowTask and shows the app menu.
128class RunCloseWithAppMenuTask : public Task {
129 public:
130  explicit RunCloseWithAppMenuTask(Browser* browser) : browser_(browser) {}
131
132  virtual void Run() {
133    // ShowAppMenu is modal under views. Schedule a task that closes the window.
134    MessageLoop::current()->PostTask(FROM_HERE, new CloseWindowTask(browser_));
135    browser_->ShowAppMenu();
136  }
137
138 private:
139  Browser* browser_;
140
141  DISALLOW_COPY_AND_ASSIGN(RunCloseWithAppMenuTask);
142};
143
144}  // namespace
145
146class BrowserTest : public ExtensionBrowserTest {
147 protected:
148  // In RTL locales wrap the page title with RTL embedding characters so that it
149  // matches the value returned by GetWindowTitle().
150  std::wstring LocaleWindowCaptionFromPageTitle(
151      const std::wstring& expected_title) {
152    std::wstring page_title = WindowCaptionFromPageTitle(expected_title);
153#if defined(OS_WIN)
154    std::string locale = g_browser_process->GetApplicationLocale();
155    if (base::i18n::GetTextDirectionForLocale(locale.c_str()) ==
156        base::i18n::RIGHT_TO_LEFT) {
157      base::i18n::WrapStringWithLTRFormatting(&page_title);
158    }
159
160    return page_title;
161#else
162    // Do we need to use the above code on POSIX as well?
163    return page_title;
164#endif
165  }
166
167  // Returns the app extension aptly named "App Test".
168  const Extension* GetExtension() {
169    const ExtensionList* extensions =
170        browser()->profile()->GetExtensionService()->extensions();
171    for (size_t i = 0; i < extensions->size(); ++i) {
172      if ((*extensions)[i]->name() == "App Test")
173        return (*extensions)[i];
174    }
175    NOTREACHED();
176    return NULL;
177  }
178};
179
180// Launch the app on a page with no title, check that the app title was set
181// correctly.
182IN_PROC_BROWSER_TEST_F(BrowserTest, NoTitle) {
183  ui_test_utils::NavigateToURL(browser(),
184      ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
185                                FilePath(kTitle1File)));
186  EXPECT_EQ(LocaleWindowCaptionFromPageTitle(L"title1.html"),
187            UTF16ToWideHack(browser()->GetWindowTitleForCurrentTab()));
188  string16 tab_title;
189  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
190  EXPECT_EQ(ASCIIToUTF16("title1.html"), tab_title);
191}
192
193// Launch the app, navigate to a page with a title, check that the app title
194// was set correctly.
195IN_PROC_BROWSER_TEST_F(BrowserTest, Title) {
196  ui_test_utils::NavigateToURL(browser(),
197      ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
198                                FilePath(kTitle2File)));
199  const std::wstring test_title(L"Title Of Awesomeness");
200  EXPECT_EQ(LocaleWindowCaptionFromPageTitle(test_title),
201            UTF16ToWideHack(browser()->GetWindowTitleForCurrentTab()));
202  string16 tab_title;
203  ASSERT_TRUE(ui_test_utils::GetCurrentTabTitle(browser(), &tab_title));
204  EXPECT_EQ(WideToUTF16(test_title), tab_title);
205}
206
207IN_PROC_BROWSER_TEST_F(BrowserTest, JavascriptAlertActivatesTab) {
208  GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
209                                     FilePath(kTitle1File)));
210  ui_test_utils::NavigateToURL(browser(), url);
211  AddTabAtIndex(0, url, PageTransition::TYPED);
212  EXPECT_EQ(2, browser()->tab_count());
213  EXPECT_EQ(0, browser()->active_index());
214  TabContents* second_tab = browser()->GetTabContentsAt(1);
215  ASSERT_TRUE(second_tab);
216  second_tab->render_view_host()->ExecuteJavascriptInWebFrame(
217      string16(),
218      ASCIIToUTF16("alert('Activate!');"));
219  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
220  alert->CloseModalDialog();
221  EXPECT_EQ(2, browser()->tab_count());
222  EXPECT_EQ(1, browser()->active_index());
223}
224
225
226
227#if defined(OS_WIN)
228// http://crbug.com/75274. On XP crashes inside
229// URLFetcher::Core::Registry::RemoveURLFetcherCore.
230#define MAYBE_ThirtyFourTabs FLAKY_ThirtyFourTabs
231#else
232#define MAYBE_ThirtyFourTabs ThirtyFourTabs
233#endif
234
235// Create 34 tabs and verify that a lot of processes have been created. The
236// exact number of processes depends on the amount of memory. Previously we
237// had a hard limit of 31 processes and this test is mainly directed at
238// verifying that we don't crash when we pass this limit.
239// Warning: this test can take >30 seconds when running on a slow (low
240// memory?) Mac builder.
241IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_ThirtyFourTabs) {
242  GURL url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
243                                     FilePath(kTitle2File)));
244
245  // There is one initial tab.
246  for (int ix = 0; ix != 33; ++ix)
247    browser()->AddSelectedTabWithURL(url, PageTransition::TYPED);
248  EXPECT_EQ(34, browser()->tab_count());
249
250  // See browser\renderer_host\render_process_host.cc for the algorithm to
251  // decide how many processes to create.
252  if (base::SysInfo::AmountOfPhysicalMemoryMB() >= 2048) {
253    EXPECT_GE(CountRenderProcessHosts(), 24);
254  } else {
255    EXPECT_LE(CountRenderProcessHosts(), 23);
256  }
257}
258
259// Test for crbug.com/22004.  Reloading a page with a before unload handler and
260// then canceling the dialog should not leave the throbber spinning.
261IN_PROC_BROWSER_TEST_F(BrowserTest, ReloadThenCancelBeforeUnload) {
262  GURL url(std::string("data:text/html,") + kBeforeUnloadHTML);
263  ui_test_utils::NavigateToURL(browser(), url);
264
265  // Navigate to another page, but click cancel in the dialog.  Make sure that
266  // the throbber stops spinning.
267  browser()->Reload(CURRENT_TAB);
268  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
269  alert->CloseModalDialog();
270  EXPECT_FALSE(browser()->GetSelectedTabContents()->is_loading());
271
272  // Clear the beforeunload handler so the test can easily exit.
273  browser()->GetSelectedTabContents()->render_view_host()->
274      ExecuteJavascriptInWebFrame(string16(),
275                                  ASCIIToUTF16("onbeforeunload=null;"));
276}
277
278// Crashy on mac.  http://crbug.com/38522
279#if defined(OS_MACOSX)
280#define MAYBE_SingleBeforeUnloadAfterWindowClose \
281        DISABLED_SingleBeforeUnloadAfterWindowClose
282#else
283#define MAYBE_SingleBeforeUnloadAfterWindowClose \
284        SingleBeforeUnloadAfterWindowClose
285#endif
286
287// Test for crbug.com/11647.  A page closed with window.close() should not have
288// two beforeunload dialogs shown.
289IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_SingleBeforeUnloadAfterWindowClose) {
290  browser()->GetSelectedTabContents()->render_view_host()->
291      ExecuteJavascriptInWebFrame(string16(),
292                                  ASCIIToUTF16(kOpenNewBeforeUnloadPage));
293
294  // Close the new window with JavaScript, which should show a single
295  // beforeunload dialog.  Then show another alert, to make it easy to verify
296  // that a second beforeunload dialog isn't shown.
297  browser()->GetTabContentsAt(0)->render_view_host()->
298      ExecuteJavascriptInWebFrame(string16(),
299                                  ASCIIToUTF16("w.close(); alert('bar');"));
300  AppModalDialog* alert = ui_test_utils::WaitForAppModalDialog();
301  alert->native_dialog()->AcceptAppModalDialog();
302
303  alert = ui_test_utils::WaitForAppModalDialog();
304  EXPECT_FALSE(static_cast<JavaScriptAppModalDialog*>(alert)->
305                   is_before_unload_dialog());
306  alert->native_dialog()->AcceptAppModalDialog();
307}
308
309// Test that get_process_idle_time() returns reasonable values when compared
310// with time deltas measured locally.
311IN_PROC_BROWSER_TEST_F(BrowserTest, RenderIdleTime) {
312  base::TimeTicks start = base::TimeTicks::Now();
313  ui_test_utils::NavigateToURL(browser(),
314      ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
315                                FilePath(kTitle1File)));
316  RenderProcessHost::iterator it(RenderProcessHost::AllHostsIterator());
317  for (; !it.IsAtEnd(); it.Advance()) {
318    base::TimeDelta renderer_td =
319        it.GetCurrentValue()->get_child_process_idle_time();
320    base::TimeDelta browser_td = base::TimeTicks::Now() - start;
321    EXPECT_TRUE(browser_td >= renderer_td);
322  }
323}
324
325// Test IDC_CREATE_SHORTCUTS command is enabled for url scheme file, ftp, http
326// and https and disabled for chrome://, about:// etc.
327// TODO(pinkerton): Disable app-mode in the model until we implement it
328// on the Mac. http://crbug.com/13148
329#if !defined(OS_MACOSX)
330IN_PROC_BROWSER_TEST_F(BrowserTest, CommandCreateAppShortcutFile) {
331  CommandUpdater* command_updater = browser()->command_updater();
332
333  static const FilePath::CharType* kEmptyFile = FILE_PATH_LITERAL("empty.html");
334  GURL file_url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
335                                          FilePath(kEmptyFile)));
336  ASSERT_TRUE(file_url.SchemeIs(chrome::kFileScheme));
337  ui_test_utils::NavigateToURL(browser(), file_url);
338  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
339}
340
341IN_PROC_BROWSER_TEST_F(BrowserTest, CommandCreateAppShortcutHttp) {
342  CommandUpdater* command_updater = browser()->command_updater();
343
344  ASSERT_TRUE(test_server()->Start());
345  GURL http_url(test_server()->GetURL(""));
346  ASSERT_TRUE(http_url.SchemeIs(chrome::kHttpScheme));
347  ui_test_utils::NavigateToURL(browser(), http_url);
348  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
349}
350
351IN_PROC_BROWSER_TEST_F(BrowserTest, CommandCreateAppShortcutHttps) {
352  CommandUpdater* command_updater = browser()->command_updater();
353
354  net::TestServer test_server(net::TestServer::TYPE_HTTPS, FilePath(kDocRoot));
355  ASSERT_TRUE(test_server.Start());
356  GURL https_url(test_server.GetURL("/"));
357  ASSERT_TRUE(https_url.SchemeIs(chrome::kHttpsScheme));
358  ui_test_utils::NavigateToURL(browser(), https_url);
359  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
360}
361
362IN_PROC_BROWSER_TEST_F(BrowserTest, CommandCreateAppShortcutFtp) {
363  CommandUpdater* command_updater = browser()->command_updater();
364
365  net::TestServer test_server(net::TestServer::TYPE_FTP, FilePath(kDocRoot));
366  ASSERT_TRUE(test_server.Start());
367  GURL ftp_url(test_server.GetURL(""));
368  ASSERT_TRUE(ftp_url.SchemeIs(chrome::kFtpScheme));
369  ui_test_utils::NavigateToURL(browser(), ftp_url);
370  EXPECT_TRUE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
371}
372
373IN_PROC_BROWSER_TEST_F(BrowserTest, CommandCreateAppShortcutInvalid) {
374  CommandUpdater* command_updater = browser()->command_updater();
375
376  // Urls that should not have shortcuts.
377  GURL new_tab_url(chrome::kChromeUINewTabURL);
378  ui_test_utils::NavigateToURL(browser(), new_tab_url);
379  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
380
381  GURL history_url(chrome::kChromeUIHistoryURL);
382  ui_test_utils::NavigateToURL(browser(), history_url);
383  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
384
385  GURL downloads_url(chrome::kChromeUIDownloadsURL);
386  ui_test_utils::NavigateToURL(browser(), downloads_url);
387  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
388
389  GURL blank_url(chrome::kAboutBlankURL);
390  ui_test_utils::NavigateToURL(browser(), blank_url);
391  EXPECT_FALSE(command_updater->IsCommandEnabled(IDC_CREATE_SHORTCUTS));
392}
393
394// Change a tab into an application window.
395// DISABLED: http://crbug.com/72310
396IN_PROC_BROWSER_TEST_F(BrowserTest, DISABLED_ConvertTabToAppShortcut) {
397  ASSERT_TRUE(test_server()->Start());
398  GURL http_url(test_server()->GetURL(""));
399  ASSERT_TRUE(http_url.SchemeIs(chrome::kHttpScheme));
400
401  ASSERT_EQ(1, browser()->tab_count());
402  TabContents* initial_tab = browser()->GetTabContentsAt(0);
403  TabContents* app_tab = browser()->AddSelectedTabWithURL(
404      http_url, PageTransition::TYPED)->tab_contents();
405  ASSERT_EQ(2, browser()->tab_count());
406  ASSERT_EQ(1u, BrowserList::GetBrowserCount(browser()->profile()));
407
408  // Normal tabs should accept load drops.
409  EXPECT_TRUE(initial_tab->GetMutableRendererPrefs()->can_accept_load_drops);
410  EXPECT_TRUE(app_tab->GetMutableRendererPrefs()->can_accept_load_drops);
411
412  // Turn |app_tab| into a tab in an app panel.
413  browser()->ConvertContentsToApplication(app_tab);
414
415  // The launch should have created a new browser.
416  ASSERT_EQ(2u, BrowserList::GetBrowserCount(browser()->profile()));
417
418  // Find the new browser.
419  Browser* app_browser = NULL;
420  for (BrowserList::const_iterator i = BrowserList::begin();
421       i != BrowserList::end() && !app_browser; ++i) {
422    if (*i != browser())
423      app_browser = *i;
424  }
425  ASSERT_TRUE(app_browser);
426
427  // Check that the tab contents is in the new browser, and not in the old.
428  ASSERT_EQ(1, browser()->tab_count());
429  ASSERT_EQ(initial_tab, browser()->GetTabContentsAt(0));
430
431  // Check that the appliaction browser has a single tab, and that tab contains
432  // the content that we app-ified.
433  ASSERT_EQ(1, app_browser->tab_count());
434  ASSERT_EQ(app_tab, app_browser->GetTabContentsAt(0));
435
436  // Normal tabs should accept load drops.
437  EXPECT_TRUE(initial_tab->GetMutableRendererPrefs()->can_accept_load_drops);
438
439  // The tab in an app window should not.
440  EXPECT_FALSE(app_tab->GetMutableRendererPrefs()->can_accept_load_drops);
441}
442
443#endif  // !defined(OS_MACOSX)
444
445// Test RenderView correctly send back favicon url for web page that redirects
446// to an anchor in javascript body.onload handler.
447IN_PROC_BROWSER_TEST_F(BrowserTest, FaviconOfOnloadRedirectToAnchorPage) {
448  ASSERT_TRUE(test_server()->Start());
449  GURL url(test_server()->GetURL("files/onload_redirect_to_anchor.html"));
450  GURL expected_favicon_url(test_server()->GetURL("files/test.png"));
451
452  ui_test_utils::NavigateToURL(browser(), url);
453
454  NavigationEntry* entry = browser()->GetSelectedTabContents()->
455      controller().GetActiveEntry();
456  EXPECT_EQ(expected_favicon_url.spec(), entry->favicon().url().spec());
457}
458
459// Test that an icon can be changed from JS.
460IN_PROC_BROWSER_TEST_F(BrowserTest, FaviconChange) {
461  static const FilePath::CharType* kFile =
462      FILE_PATH_LITERAL("onload_change_favicon.html");
463  GURL file_url(ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
464                                          FilePath(kFile)));
465  ASSERT_TRUE(file_url.SchemeIs(chrome::kFileScheme));
466  ui_test_utils::NavigateToURL(browser(), file_url);
467
468  NavigationEntry* entry = browser()->GetSelectedTabContents()->
469      controller().GetActiveEntry();
470  static const FilePath::CharType* kIcon =
471      FILE_PATH_LITERAL("test1.png");
472  GURL expected_favicon_url(
473      ui_test_utils::GetTestUrl(FilePath(FilePath::kCurrentDirectory),
474                                         FilePath(kIcon)));
475  EXPECT_EQ(expected_favicon_url.spec(), entry->favicon().url().spec());
476}
477
478// Makes sure TabClosing is sent when uninstalling an extension that is an app
479// tab.
480IN_PROC_BROWSER_TEST_F(BrowserTest, TabClosingWhenRemovingExtension) {
481  ASSERT_TRUE(test_server()->Start());
482  host_resolver()->AddRule("www.example.com", "127.0.0.1");
483  GURL url(test_server()->GetURL("empty.html"));
484  TabStripModel* model = browser()->tabstrip_model();
485
486  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
487
488  const Extension* extension_app = GetExtension();
489
490  ui_test_utils::NavigateToURL(browser(), url);
491
492  TabContentsWrapper* app_contents =
493      Browser::TabContentsFactory(browser()->profile(), NULL,
494                                  MSG_ROUTING_NONE, NULL, NULL);
495  app_contents->extension_tab_helper()->SetExtensionApp(extension_app);
496
497  model->AddTabContents(app_contents, 0, 0, TabStripModel::ADD_NONE);
498  model->SetTabPinned(0, true);
499  ui_test_utils::NavigateToURL(browser(), url);
500
501  MockTabStripModelObserver observer;
502  model->AddObserver(&observer);
503
504  // Uninstall the extension and make sure TabClosing is sent.
505  ExtensionService* service = browser()->profile()->GetExtensionService();
506  service->UninstallExtension(GetExtension()->id(), false, NULL);
507  EXPECT_EQ(1, observer.closing_count());
508
509  model->RemoveObserver(&observer);
510
511  // There should only be one tab now.
512  ASSERT_EQ(1, browser()->tab_count());
513}
514
515#if !defined(OS_MACOSX)
516// Open with --app-id=<id>, and see that an app window opens.
517IN_PROC_BROWSER_TEST_F(BrowserTest, AppIdSwitch) {
518  ASSERT_TRUE(test_server()->Start());
519
520  // Load an app.
521  host_resolver()->AddRule("www.example.com", "127.0.0.1");
522  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
523  const Extension* extension_app = GetExtension();
524
525  CommandLine command_line(CommandLine::NO_PROGRAM);
526  command_line.AppendSwitchASCII(switches::kAppId, extension_app->id());
527
528  BrowserInit::LaunchWithProfile launch(FilePath(), command_line);
529  ASSERT_TRUE(launch.OpenApplicationWindow(browser()->profile()));
530
531  // Check that the new browser has an app name.
532  // The launch should have created a new browser.
533  ASSERT_EQ(2u, BrowserList::GetBrowserCount(browser()->profile()));
534
535  // Find the new browser.
536  Browser* new_browser = NULL;
537  for (BrowserList::const_iterator i = BrowserList::begin();
538       i != BrowserList::end() && !new_browser; ++i) {
539    if (*i != browser())
540      new_browser = *i;
541  }
542  ASSERT_TRUE(new_browser);
543  ASSERT_TRUE(new_browser != browser());
544
545  // The browser's app_name should include the app's ID.
546  ASSERT_NE(
547      new_browser->app_name_.find(extension_app->id()),
548      std::string::npos) << new_browser->app_name_;
549
550}
551#endif
552
553#if defined(OS_WIN)
554// http://crbug.com/46198. On XP/Vista, the failure rate is 5 ~ 6%.
555#define MAYBE_PageLanguageDetection FLAKY_PageLanguageDetection
556#else
557#define MAYBE_PageLanguageDetection PageLanguageDetection
558#endif
559// Tests that the CLD (Compact Language Detection) works properly.
560IN_PROC_BROWSER_TEST_F(BrowserTest, MAYBE_PageLanguageDetection) {
561  ASSERT_TRUE(test_server()->Start());
562
563  TabContents* current_tab = browser()->GetSelectedTabContents();
564  TabContentsWrapper* wrapper = browser()->GetSelectedTabContentsWrapper();
565  TranslateTabHelper* helper = wrapper->translate_tab_helper();
566  Source<TabContents> source(current_tab);
567
568  // Navigate to a page in English.
569  ui_test_utils::WindowedNotificationObserverWithDetails<std::string>
570      en_language_detected_signal(NotificationType::TAB_LANGUAGE_DETERMINED,
571                                  source);
572  ui_test_utils::NavigateToURL(
573      browser(), GURL(test_server()->GetURL("files/english_page.html")));
574  EXPECT_TRUE(helper->language_state().original_language().empty());
575  en_language_detected_signal.Wait();
576  std::string lang;
577  EXPECT_TRUE(en_language_detected_signal.GetDetailsFor(
578        source.map_key(), &lang));
579  EXPECT_EQ("en", lang);
580  EXPECT_EQ("en", helper->language_state().original_language());
581
582  // Now navigate to a page in French.
583  ui_test_utils::WindowedNotificationObserverWithDetails<std::string>
584      fr_language_detected_signal(NotificationType::TAB_LANGUAGE_DETERMINED,
585                                  source);
586  ui_test_utils::NavigateToURL(
587      browser(), GURL(test_server()->GetURL("files/french_page.html")));
588  EXPECT_TRUE(helper->language_state().original_language().empty());
589  fr_language_detected_signal.Wait();
590  lang.clear();
591  EXPECT_TRUE(fr_language_detected_signal.GetDetailsFor(
592        source.map_key(), &lang));
593  EXPECT_EQ("fr", lang);
594  EXPECT_EQ("fr", helper->language_state().original_language());
595}
596
597// Chromeos defaults to restoring the last session, so this test isn't
598// applicable.
599#if !defined(OS_CHROMEOS)
600#if defined(OS_MACOSX)
601// Crashy, http://crbug.com/38522
602#define RestorePinnedTabs DISABLED_RestorePinnedTabs
603#endif
604// Makes sure pinned tabs are restored correctly on start.
605IN_PROC_BROWSER_TEST_F(BrowserTest, RestorePinnedTabs) {
606  ASSERT_TRUE(test_server()->Start());
607
608  // Add an pinned app tab.
609  host_resolver()->AddRule("www.example.com", "127.0.0.1");
610  GURL url(test_server()->GetURL("empty.html"));
611  TabStripModel* model = browser()->tabstrip_model();
612  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
613  const Extension* extension_app = GetExtension();
614  ui_test_utils::NavigateToURL(browser(), url);
615  TabContentsWrapper* app_contents =
616    Browser::TabContentsFactory(browser()->profile(), NULL,
617                                MSG_ROUTING_NONE, NULL, NULL);
618  app_contents->extension_tab_helper()->SetExtensionApp(extension_app);
619  model->AddTabContents(app_contents, 0, 0, TabStripModel::ADD_NONE);
620  model->SetTabPinned(0, true);
621  ui_test_utils::NavigateToURL(browser(), url);
622
623  // Add a non pinned tab.
624  browser()->NewTab();
625
626  // Add a pinned non-app tab.
627  browser()->NewTab();
628  ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
629  model->SetTabPinned(2, true);
630
631  // Write out the pinned tabs.
632  PinnedTabCodec::WritePinnedTabs(browser()->profile());
633
634  // Simulate launching again.
635  CommandLine dummy(CommandLine::NO_PROGRAM);
636  BrowserInit::LaunchWithProfile launch(FilePath(), dummy);
637  launch.profile_ = browser()->profile();
638  launch.ProcessStartupURLs(std::vector<GURL>());
639
640  // The launch should have created a new browser.
641  ASSERT_EQ(2u, BrowserList::GetBrowserCount(browser()->profile()));
642
643  // Find the new browser.
644  Browser* new_browser = NULL;
645  for (BrowserList::const_iterator i = BrowserList::begin();
646       i != BrowserList::end() && !new_browser; ++i) {
647    if (*i != browser())
648      new_browser = *i;
649  }
650  ASSERT_TRUE(new_browser);
651  ASSERT_TRUE(new_browser != browser());
652
653  // We should get back an additional tab for the app, and another for the
654  // default home page.
655  ASSERT_EQ(3, new_browser->tab_count());
656
657  // Make sure the state matches.
658  TabStripModel* new_model = new_browser->tabstrip_model();
659  EXPECT_TRUE(new_model->IsAppTab(0));
660  EXPECT_FALSE(new_model->IsAppTab(1));
661  EXPECT_FALSE(new_model->IsAppTab(2));
662
663  EXPECT_TRUE(new_model->IsTabPinned(0));
664  EXPECT_TRUE(new_model->IsTabPinned(1));
665  EXPECT_FALSE(new_model->IsTabPinned(2));
666
667  EXPECT_EQ(browser()->GetHomePage(),
668      new_model->GetTabContentsAt(2)->tab_contents()->GetURL());
669
670  EXPECT_TRUE(
671      new_model->GetTabContentsAt(0)->extension_tab_helper()->extension_app() ==
672          extension_app);
673}
674#endif  // !defined(OS_CHROMEOS)
675
676// This test verifies we don't crash when closing the last window and the app
677// menu is showing.
678IN_PROC_BROWSER_TEST_F(BrowserTest, CloseWithAppMenuOpen) {
679  if (browser_defaults::kBrowserAliveWithNoWindows)
680    return;
681
682  // We need a message loop running for menus on windows.
683  MessageLoop::current()->PostTask(FROM_HERE,
684                                   new RunCloseWithAppMenuTask(browser()));
685}
686
687#if !defined(OS_MACOSX)
688IN_PROC_BROWSER_TEST_F(BrowserTest, OpenAppWindowLikeNtp) {
689  ASSERT_TRUE(test_server()->Start());
690
691  // Load an app
692  host_resolver()->AddRule("www.example.com", "127.0.0.1");
693  ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app/")));
694  const Extension* extension_app = GetExtension();
695
696  // Launch it in a window, as AppLauncherHandler::HandleLaunchApp() would.
697  TabContents* app_window = Browser::OpenApplication(
698      browser()->profile(), extension_app, extension_misc::LAUNCH_WINDOW, NULL);
699  ASSERT_TRUE(app_window);
700
701  // Apps launched in a window from the NTP do not have extension_app set in
702  // tab contents.
703  TabContentsWrapper* wrapper =
704          TabContentsWrapper::GetCurrentWrapperForContents(app_window);
705  EXPECT_FALSE(wrapper->extension_tab_helper()->extension_app());
706  EXPECT_EQ(extension_app->GetFullLaunchURL(), app_window->GetURL());
707
708  // The launch should have created a new browser.
709  ASSERT_EQ(2u, BrowserList::GetBrowserCount(browser()->profile()));
710
711  // Find the new browser.
712  Browser* new_browser = NULL;
713  for (BrowserList::const_iterator i = BrowserList::begin();
714       i != BrowserList::end() && !new_browser; ++i) {
715    if (*i != browser())
716      new_browser = *i;
717  }
718  ASSERT_TRUE(new_browser);
719  ASSERT_TRUE(new_browser != browser());
720
721  EXPECT_EQ(Browser::TYPE_APP, new_browser->type());
722
723  // The browser's app name should include the extension's id.
724  std::string app_name = new_browser->app_name_;
725  EXPECT_NE(app_name.find(extension_app->id()), std::string::npos)
726      << "Name " << app_name << " should contain id "<< extension_app->id();
727}
728#endif  // !defined(OS_MACOSX)
729
730// TODO(ben): this test was never enabled. It has bit-rotted since being added.
731// It originally lived in browser_unittest.cc, but has been moved here to make
732// room for real browser unit tests.
733#if 0
734class BrowserTest2 : public InProcessBrowserTest {
735 public:
736  BrowserTest2() {
737    host_resolver_proc_ = new net::RuleBasedHostResolverProc(NULL);
738    // Avoid making external DNS lookups. In this test we don't need this
739    // to succeed.
740    host_resolver_proc_->AddSimulatedFailure("*.google.com");
741    scoped_host_resolver_proc_.Init(host_resolver_proc_.get());
742  }
743
744 private:
745  scoped_refptr<net::RuleBasedHostResolverProc> host_resolver_proc_;
746  net::ScopedDefaultHostResolverProc scoped_host_resolver_proc_;
747};
748
749IN_PROC_BROWSER_TEST_F(BrowserTest2, NoTabsInPopups) {
750  Browser::RegisterAppPrefs(L"Test");
751
752  // We start with a normal browser with one tab.
753  EXPECT_EQ(1, browser()->tab_count());
754
755  // Open a popup browser with a single blank foreground tab.
756  Browser* popup_browser = browser()->CreateForType(Browser::TYPE_POPUP,
757                                                    browser()->profile());
758  popup_browser->AddBlankTab(true);
759  EXPECT_EQ(1, popup_browser->tab_count());
760
761  // Now try opening another tab in the popup browser.
762  AddTabWithURLParams params1(url, PageTransition::TYPED);
763  popup_browser->AddTabWithURL(&params1);
764  EXPECT_EQ(popup_browser, params1.target);
765
766  // The popup should still only have one tab.
767  EXPECT_EQ(1, popup_browser->tab_count());
768
769  // The normal browser should now have two.
770  EXPECT_EQ(2, browser()->tab_count());
771
772  // Open an app frame browser with a single blank foreground tab.
773  Browser* app_browser =
774      browser()->CreateForApp(L"Test", browser()->profile(), false);
775  app_browser->AddBlankTab(true);
776  EXPECT_EQ(1, app_browser->tab_count());
777
778  // Now try opening another tab in the app browser.
779  AddTabWithURLParams params2(GURL(chrome::kAboutBlankURL),
780                              PageTransition::TYPED);
781  app_browser->AddTabWithURL(&params2);
782  EXPECT_EQ(app_browser, params2.target);
783
784  // The popup should still only have one tab.
785  EXPECT_EQ(1, app_browser->tab_count());
786
787  // The normal browser should now have three.
788  EXPECT_EQ(3, browser()->tab_count());
789
790  // Open an app frame popup browser with a single blank foreground tab.
791  Browser* app_popup_browser =
792      browser()->CreateForApp(L"Test", browser()->profile(), false);
793  app_popup_browser->AddBlankTab(true);
794  EXPECT_EQ(1, app_popup_browser->tab_count());
795
796  // Now try opening another tab in the app popup browser.
797  AddTabWithURLParams params3(GURL(chrome::kAboutBlankURL),
798                              PageTransition::TYPED);
799  app_popup_browser->AddTabWithURL(&params3);
800  EXPECT_EQ(app_popup_browser, params3.target);
801
802  // The popup should still only have one tab.
803  EXPECT_EQ(1, app_popup_browser->tab_count());
804
805  // The normal browser should now have four.
806  EXPECT_EQ(4, browser()->tab_count());
807
808  // Close the additional browsers.
809  popup_browser->CloseAllTabs();
810  app_browser->CloseAllTabs();
811  app_popup_browser->CloseAllTabs();
812}
813#endif
814