// Copyright (c) 2009 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/ref_counted.h" #include "chrome/browser/browser.h" #include "chrome/browser/browser_list.h" #include "chrome/browser/renderer_host/render_view_host.h" #include "chrome/browser/extensions/autoupdate_interceptor.h" #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_host.h" #include "chrome/browser/extensions/extension_process_manager.h" #include "chrome/browser/extensions/extension_tabs_module.h" #include "chrome/browser/extensions/extensions_service.h" #include "chrome/browser/extensions/extension_updater.h" #include "chrome/browser/profile.h" #include "chrome/browser/renderer_host/site_instance.h" #include "chrome/browser/tab_contents/tab_contents.h" #if defined(TOOLKIT_VIEWS) #include "chrome/browser/views/extensions/extension_shelf.h" #endif #include "chrome/browser/views/frame/browser_view.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/extensions/extension_error_reporter.h" #include "chrome/common/notification_service.h" #include "chrome/common/url_constants.h" #include "chrome/test/ui_test_utils.h" #include "net/base/net_util.h" const std::wstring kSubscribePage = L"files/extensions/subscribe_page_action/subscribe.html"; const std::wstring kFeedPage = L"files/feeds/feed.html"; const std::wstring kNoFeedPage = L"files/feeds/no_feed.html"; const std::wstring kValidFeed0 = L"files/feeds/feed_script.xml"; const std::wstring kValidFeed1 = L"files/feeds/feed1.xml"; const std::wstring kValidFeed2 = L"files/feeds/feed2.xml"; const std::wstring kValidFeed3 = L"files/feeds/feed3.xml"; const std::wstring kValidFeed4 = L"files/feeds/feed4.xml"; const std::wstring kValidFeed5 = L"files/feeds/feed5.xml"; const std::wstring kInvalidFeed1 = L"files/feeds/feed_invalid1.xml"; const std::wstring kInvalidFeed2 = L"files/feeds/feed_invalid2.xml"; const std::wstring kLocalization = L"file/extensions/browsertest/title_localized_pa/simple.html"; const std::wstring kTestFile = L"file/extensions/test_file.html"; // Looks for an ExtensionHost whose URL has the given path component (including // leading slash). Also verifies that the expected number of hosts are loaded. static ExtensionHost* FindHostWithPath(ExtensionProcessManager* manager, const std::string& path, int expected_hosts) { ExtensionHost* host = NULL; int num_hosts = 0; for (ExtensionProcessManager::const_iterator iter = manager->begin(); iter != manager->end(); ++iter) { if ((*iter)->GetURL().path() == path) { EXPECT_FALSE(host); host = *iter; } num_hosts++; } EXPECT_EQ(expected_hosts, num_hosts); return host; } // Tests that toolstrips initializes properly and can run basic extension js. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, Toolstrip) { FilePath extension_test_data_dir = test_data_dir_.AppendASCII("good"). AppendASCII("Extensions").AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj"). AppendASCII("1.0.0.0"); ASSERT_TRUE(LoadExtension(extension_test_data_dir)); // At this point, there should be three ExtensionHosts loaded because this // extension has two toolstrips and one background page. Find the one that is // hosting toolstrip1.html. ExtensionProcessManager* manager = browser()->profile()->GetExtensionProcessManager(); ExtensionHost* host = FindHostWithPath(manager, "/toolstrip1.html", 3); // Tell it to run some JavaScript that tests that basic extension code works. bool result = false; ui_test_utils::ExecuteJavaScriptAndExtractBool( host->render_view_host(), L"", L"testTabsAPI()", &result); EXPECT_TRUE(result); #if defined(OS_WIN) // Test for compact language detection API. First navigate to a (static) html // file with a French sentence. Then, run the test API in toolstrip1.html to // actually call the language detection API through the existing extension, // and verify that the language returned is indeed French. FilePath language_url = extension_test_data_dir.AppendASCII( "french_sentence.html"); ui_test_utils::NavigateToURL( browser(), GURL(language_url.ToWStringHack())); ui_test_utils::ExecuteJavaScriptAndExtractBool( host->render_view_host(), L"", L"testTabsLanguageAPI()", &result); EXPECT_TRUE(result); #endif } #if defined(OS_WIN) // TODO(port) -- enable IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ExtensionViews) { FilePath extension_test_data_dir = test_data_dir_.AppendASCII("good"). AppendASCII("Extensions").AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj"). AppendASCII("1.0.0.0"); ASSERT_TRUE(LoadExtension(extension_test_data_dir)); // At this point, there should be three ExtensionHosts loaded because this // extension has two toolstrips and one background page. Find the one that is // hosting toolstrip1.html. ExtensionProcessManager* manager = browser()->profile()->GetExtensionProcessManager(); ExtensionHost* host = FindHostWithPath(manager, "/toolstrip1.html", 3); FilePath gettabs_url = extension_test_data_dir.AppendASCII( "test_gettabs.html"); ui_test_utils::NavigateToURL( browser(), GURL(gettabs_url.value())); bool result = false; ui_test_utils::ExecuteJavaScriptAndExtractBool( host->render_view_host(), L"", L"testgetToolstripsAPI()", &result); EXPECT_TRUE(result); result = false; ui_test_utils::ExecuteJavaScriptAndExtractBool( host->render_view_host(), L"", L"testgetBackgroundPageAPI()", &result); EXPECT_TRUE(result); ui_test_utils::NavigateToURL( browser(), GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/" "test_gettabs.html")); result = false; ui_test_utils::ExecuteJavaScriptAndExtractBool( host->render_view_host(), L"", L"testgetExtensionTabsAPI()", &result); EXPECT_TRUE(result); } #endif // defined(OS_WIN) #if defined(OS_WIN) // TODO(port) -- enable // Tests that the ExtensionShelf initializes properly, notices that // an extension loaded and has a view available, and then sets that up // properly. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, Shelf) { // When initialized, there are no extension views and the preferred height // should be zero. BrowserView* browser_view = static_cast(browser()->window()); ExtensionShelf* shelf = browser_view->extension_shelf(); ASSERT_TRUE(shelf); EXPECT_EQ(shelf->GetChildViewCount(), 0); EXPECT_EQ(shelf->GetPreferredSize().height(), 0); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("good").AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"))); // There should now be two extension views and preferred height of the view // should be non-zero. EXPECT_EQ(shelf->GetChildViewCount(), 2); EXPECT_NE(shelf->GetPreferredSize().height(), 0); } #endif // defined(OS_WIN) // Tests that installing and uninstalling extensions don't crash with an // incognito window open. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, Incognito) { // Open an incognito window to the extensions management page. We just // want to make sure that we don't crash while playing with extensions when // this guy is around. Browser::OpenURLOffTheRecord(browser()->profile(), GURL(chrome::kChromeUIExtensionsURL)); ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("good.crx"), 1)); UninstallExtension("ldnnhddmnhbkjipkidpdiheffobcpfmf"); } // Tests that we can load extension pages into the tab area and they can call // extension APIs. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TabContents) { ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("good").AppendASCII("Extensions") .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj") .AppendASCII("1.0.0.0"))); ui_test_utils::NavigateToURL( browser(), GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html")); bool result = false; ui_test_utils::ExecuteJavaScriptAndExtractBool( browser()->GetSelectedTabContents()->render_view_host(), L"", L"testTabsAPI()", &result); EXPECT_TRUE(result); // There was a bug where we would crash if we navigated to a page in the same // extension because no new render view was getting created, so we would not // do some setup. ui_test_utils::NavigateToURL( browser(), GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html")); result = false; ui_test_utils::ExecuteJavaScriptAndExtractBool( browser()->GetSelectedTabContents()->render_view_host(), L"", L"testTabsAPI()", &result); EXPECT_TRUE(result); } #if defined(OS_WIN) || defined(OS_LINUX) // Tests that we can load page actions in the Omnibox. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageAction) { HTTPTestServer* server = StartHTTPServer(); // This page action will not show an icon, since it doesn't specify one but // is included here to test for a crash (http://crbug.com/25562). ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("browsertest") .AppendASCII("crash_25562"))); ASSERT_TRUE(LoadExtension( test_data_dir_.AppendASCII("subscribe_page_action"))); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0)); // Navigate to the feed page. GURL feed_url = server->TestServerPageW(kFeedPage); ui_test_utils::NavigateToURL(browser(), feed_url); // We should now have one page action ready to go in the LocationBar. ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); // Navigate to a page with no feed. GURL no_feed = server->TestServerPageW(kNoFeedPage); ui_test_utils::NavigateToURL(browser(), no_feed); // Make sure the page action goes away. ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0)); } // Tests that the location bar forgets about unloaded page actions. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, UnloadPageAction) { HTTPTestServer* server = StartHTTPServer(); FilePath extension_path(test_data_dir_.AppendASCII("subscribe_page_action")); ASSERT_TRUE(LoadExtension(extension_path)); // Navigation prompts the location bar to load page actions. GURL feed_url = server->TestServerPageW(kFeedPage); ui_test_utils::NavigateToURL(browser(), feed_url); ASSERT_TRUE(WaitForPageActionCountChangeTo(1)); UnloadExtension(last_loaded_extension_id_); // Make sure the page action goes away when it's unloaded. ASSERT_TRUE(WaitForPageActionCountChangeTo(0)); } // Tests that tooltips of a browser action icon can be specified using UTF8. // See http://crbug.com/25349. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationBrowserAction) { FilePath extension_path(test_data_dir_.AppendASCII("browsertest") .AppendASCII("title_localized")); ASSERT_TRUE(LoadExtension(extension_path)); ExtensionsService* service = browser()->profile()->GetExtensionsService(); const ExtensionList* extensions = service->extensions(); ASSERT_EQ(1u, extensions->size()); Extension* extension = extensions->at(0); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n browser action").c_str(), extension->description().c_str()); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur is my name").c_str(), extension->name().c_str()); int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur").c_str(), extension->browser_action()->GetTitle(tab_id).c_str()); } // Tests that tooltips of a page action icon can be specified using UTF8. // See http://crbug.com/25349. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationPageAction) { HTTPTestServer* server = StartHTTPServer(); FilePath extension_path(test_data_dir_.AppendASCII("browsertest") .AppendASCII("title_localized_pa")); ASSERT_TRUE(LoadExtension(extension_path)); // Any navigation prompts the location bar to load the page action. GURL url = server->TestServerPageW(kLocalization); ui_test_utils::NavigateToURL(browser(), url); ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); ExtensionsService* service = browser()->profile()->GetExtensionsService(); const ExtensionList* extensions = service->extensions(); ASSERT_EQ(1u, extensions->size()); Extension* extension = extensions->at(0); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n page action").c_str(), extension->description().c_str()); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur is my name").c_str(), extension->name().c_str()); int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents()); EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur").c_str(), extension->page_action()->GetTitle(tab_id).c_str()); } #endif // defined(OS_WIN) || defined(OS_LINUX) GURL GetFeedUrl(HTTPTestServer* server, const std::wstring& feed_page) { static GURL base_url = server->TestServerPageW(kSubscribePage); GURL feed_url = server->TestServerPageW(feed_page); return GURL(base_url.spec() + std::string("?") + feed_url.spec() + std::string("&synchronous")); } static const wchar_t* jscript_feed_title = L"window.domAutomationController.send(" L" document.getElementById('title') ? " L" document.getElementById('title').textContent : " L" \"element 'title' not found\"" L");"; static const wchar_t* jscript_anchor = L"window.domAutomationController.send(" L" document.getElementById('anchor_0') ? " L" document.getElementById('anchor_0').textContent : " L" \"element 'anchor_0' not found\"" L");"; static const wchar_t* jscript_desc = L"window.domAutomationController.send(" L" document.getElementById('desc_0') ? " L" document.getElementById('desc_0').textContent : " L" \"element 'desc_0' not found\"" L");"; static const wchar_t* jscript_error = L"window.domAutomationController.send(" L" document.getElementById('error') ? " L" document.getElementById('error').textContent : " L" \"No error\"" L");"; void GetParsedFeedData(HTTPTestServer* server, const std::wstring& url, Browser* browser, const std::string& expected_feed_title, const std::string& expected_item_title, const std::string& expected_item_desc, const std::string& expected_error) { std::string feed_title; std::string item_title; std::string item_desc; std::string error; ui_test_utils::NavigateToURL(browser, GetFeedUrl(server, url)); ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( browser->GetSelectedTabContents()->render_view_host(), L"", // Title is on the main page, all the rest is in the IFRAME. jscript_feed_title, &feed_title)); ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( browser->GetSelectedTabContents()->render_view_host(), L"//html/body/div/iframe[1]", jscript_anchor, &item_title)); ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( browser->GetSelectedTabContents()->render_view_host(), L"//html/body/div/iframe[1]", jscript_desc, &item_desc)); ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractString( browser->GetSelectedTabContents()->render_view_host(), L"//html/body/div/iframe[1]", jscript_error, &error)); EXPECT_STREQ(expected_feed_title.c_str(), feed_title.c_str()); EXPECT_STREQ(expected_item_title.c_str(), item_title.c_str()); EXPECT_STREQ(expected_item_desc.c_str(), item_desc.c_str()); EXPECT_STREQ(expected_error.c_str(), error.c_str()); } IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed1) { HTTPTestServer* server = StartHTTPServer(); GetParsedFeedData(server, kValidFeed1, browser(), "Feed for 'MyFeedTitle'", "Title 1", "Desc", "No error"); } IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed2) { HTTPTestServer* server = StartHTTPServer(); GetParsedFeedData(server, kValidFeed2, browser(), "Feed for 'MyFeed2'", "My item title1", "This is a summary.", "No error"); } IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed3) { HTTPTestServer* server = StartHTTPServer(); GetParsedFeedData(server, kValidFeed3, browser(), "Feed for 'Google Code buglist rss feed'", "My dear title", "My dear content", "No error"); } IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed4) { HTTPTestServer* server = StartHTTPServer(); GetParsedFeedData(server, kValidFeed4, browser(), "Feed for 'Title chars