| // Copyright (c) 2012 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 <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/files/file_util.h" |
| #include "base/macros.h" |
| #include "base/run_loop.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "base/threading/thread_restrictions.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/extensions/api/webstore_private/webstore_private_api.h" |
| #include "chrome/browser/extensions/extension_apitest.h" |
| #include "chrome/browser/extensions/extension_function_test_utils.h" |
| #include "chrome/browser/extensions/extension_install_prompt.h" |
| #include "chrome/browser/extensions/extension_service.h" |
| #include "chrome/browser/extensions/webstore_installer.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| #include "chrome/browser/ui/tabs/tab_strip_model.h" |
| #include "chrome/common/buildflags.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension_test_util.h" |
| #include "chrome/test/base/ui_test_utils.h" |
| #include "content/public/browser/gpu_data_manager.h" |
| #include "content/public/browser/notification_observer.h" |
| #include "content/public/browser/notification_registrar.h" |
| #include "content/public/browser/render_frame_host.h" |
| #include "content/public/test/browser_test_utils.h" |
| #include "content/public/test/test_navigation_observer.h" |
| #include "extensions/browser/api/management/management_api.h" |
| #include "extensions/browser/extension_dialog_auto_confirm.h" |
| #include "extensions/browser/extension_system.h" |
| #include "extensions/browser/install/extension_install_ui.h" |
| #include "gpu/config/gpu_feature_type.h" |
| #include "net/dns/mock_host_resolver.h" |
| #include "net/test/embedded_test_server/embedded_test_server.h" |
| #include "ui/gl/gl_switches.h" |
| |
| #if BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| #include "chrome/browser/supervised_user/supervised_user_constants.h" |
| |
| #if defined(OS_CHROMEOS) |
| #include "chromeos/constants/chromeos_switches.h" |
| #endif |
| |
| #endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| |
| namespace utils = extension_function_test_utils; |
| |
| namespace extensions { |
| |
| namespace { |
| |
| class WebstoreInstallListener : public WebstoreInstaller::Delegate { |
| public: |
| WebstoreInstallListener() |
| : received_failure_(false), received_success_(false), waiting_(false) {} |
| |
| void OnExtensionInstallSuccess(const std::string& id) override { |
| received_success_ = true; |
| id_ = id; |
| |
| if (waiting_) { |
| waiting_ = false; |
| base::RunLoop::QuitCurrentWhenIdleDeprecated(); |
| } |
| } |
| |
| void OnExtensionInstallFailure( |
| const std::string& id, |
| const std::string& error, |
| WebstoreInstaller::FailureReason reason) override { |
| received_failure_ = true; |
| id_ = id; |
| error_ = error; |
| |
| if (waiting_) { |
| waiting_ = false; |
| base::RunLoop::QuitCurrentWhenIdleDeprecated(); |
| } |
| } |
| |
| void Wait() { |
| if (received_success_ || received_failure_) |
| return; |
| |
| waiting_ = true; |
| content::RunMessageLoop(); |
| } |
| bool received_success() const { return received_success_; } |
| const std::string& id() const { return id_; } |
| |
| private: |
| bool received_failure_; |
| bool received_success_; |
| bool waiting_; |
| std::string id_; |
| std::string error_; |
| }; |
| |
| } // namespace |
| |
| // A base class for tests below. |
| class ExtensionWebstorePrivateApiTest : public ExtensionApiTest { |
| public: |
| ExtensionWebstorePrivateApiTest() {} |
| ~ExtensionWebstorePrivateApiTest() override {} |
| |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| ExtensionApiTest::SetUpCommandLine(command_line); |
| command_line->AppendSwitchASCII( |
| switches::kAppsGalleryURL, |
| "http://www.example.com/extensions/api_test"); |
| } |
| |
| void SetUpOnMainThread() override { |
| ExtensionApiTest::SetUpOnMainThread(); |
| |
| // Start up the test server and get us ready for calling the install |
| // API functions. |
| host_resolver()->AddRule("www.example.com", "127.0.0.1"); |
| ASSERT_TRUE(StartEmbeddedTestServer()); |
| extensions::ExtensionInstallUI::set_disable_ui_for_tests(); |
| |
| auto_confirm_install_.reset( |
| new ScopedTestDialogAutoConfirm(ScopedTestDialogAutoConfirm::ACCEPT)); |
| |
| ASSERT_TRUE(webstore_install_dir_.CreateUniqueTempDir()); |
| webstore_install_dir_copy_ = webstore_install_dir_.GetPath(); |
| WebstoreInstaller::SetDownloadDirectoryForTests( |
| &webstore_install_dir_copy_); |
| } |
| |
| protected: |
| // Returns a test server URL, but with host 'www.example.com' so it matches |
| // the web store app's extent that we set up via command line flags. |
| GURL DoGetTestServerURL(const std::string& path) { |
| GURL url = embedded_test_server()->GetURL(path); |
| |
| // Replace the host with 'www.example.com' so it matches the web store |
| // app's extent. |
| GURL::Replacements replace_host; |
| replace_host.SetHostStr("www.example.com"); |
| |
| return url.ReplaceComponents(replace_host); |
| } |
| |
| virtual GURL GetTestServerURL(const std::string& path) { |
| return DoGetTestServerURL( |
| std::string("/extensions/api_test/webstore_private/") + path); |
| } |
| |
| // Navigates to |page| and runs the Extension API test there. Any downloads |
| // of extensions will return the contents of |crx_file|. |
| bool RunInstallTest(const std::string& page, const std::string& crx_file) { |
| #if defined(OS_WIN) && !defined(NDEBUG) |
| // See http://crbug.com/177163 for details. |
| return true; |
| #else |
| const GURL crx_url = GetTestServerURL(crx_file); |
| extension_test_util::SetGalleryUpdateURL(crx_url); |
| |
| GURL page_url = GetTestServerURL(page); |
| return RunPageTest(page_url.spec()); |
| #endif |
| } |
| |
| content::WebContents* GetWebContents() { |
| return browser()->tab_strip_model()->GetActiveWebContents(); |
| } |
| |
| ExtensionService* service() { |
| return ExtensionSystem::Get(browser()->profile())->extension_service(); |
| } |
| |
| private: |
| base::ScopedTempDir webstore_install_dir_; |
| // WebstoreInstaller needs a reference to a FilePath when setting the download |
| // directory for testing. |
| base::FilePath webstore_install_dir_copy_; |
| |
| std::unique_ptr<ScopedTestDialogAutoConfirm> auto_confirm_install_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ExtensionWebstorePrivateApiTest); |
| }; |
| |
| // Test cases for webstore origin frame blocking. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, |
| FrameWebstorePageBlocked) { |
| GURL url = embedded_test_server()->GetURL( |
| "/extensions/api_test/webstore_private/noframe.html"); |
| content::WebContents* web_contents = GetWebContents(); |
| ui_test_utils::NavigateToURL(browser(), url); |
| |
| // Try to load the same URL, but with the current Chrome web store origin in |
| // an iframe (i.e. http://www.example.com) |
| content::TestNavigationObserver observer(web_contents); |
| ASSERT_TRUE(content::ExecuteScript(web_contents, "dropFrame()")); |
| WaitForLoadStop(web_contents); |
| content::RenderFrameHost* subframe = |
| content::ChildFrameAt(web_contents->GetMainFrame(), 0); |
| ASSERT_TRUE(subframe); |
| |
| // The subframe load should fail due to XFO. |
| GURL iframe_url = embedded_test_server()->GetURL( |
| "www.example.com", "/extensions/api_test/webstore_private/noframe.html"); |
| EXPECT_EQ(iframe_url, subframe->GetLastCommittedURL()); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE, observer.last_net_error_code()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, FrameErrorPageBlocked) { |
| GURL url = embedded_test_server()->GetURL( |
| "/extensions/api_test/webstore_private/noframe2.html"); |
| content::WebContents* web_contents = GetWebContents(); |
| ui_test_utils::NavigateToURL(browser(), url); |
| |
| // Try to load the same URL, but with the current Chrome web store origin in |
| // an iframe (i.e. http://www.example.com) |
| content::TestNavigationObserver observer(web_contents); |
| ASSERT_TRUE(content::ExecuteScript(web_contents, "dropFrame()")); |
| WaitForLoadStop(web_contents); |
| content::RenderFrameHost* subframe = |
| content::ChildFrameAt(web_contents->GetMainFrame(), 0); |
| ASSERT_TRUE(subframe); |
| |
| // The subframe load should fail due to XFO. |
| GURL iframe_url = embedded_test_server()->GetURL( |
| "www.example.com", |
| "/nonesuch/extensions/api_test/webstore_private/noframe2.html "); |
| EXPECT_EQ(iframe_url, subframe->GetLastCommittedURL()); |
| EXPECT_FALSE(observer.last_navigation_succeeded()); |
| EXPECT_EQ(net::ERR_BLOCKED_BY_RESPONSE, observer.last_net_error_code()); |
| } |
| |
| // Test cases where the user accepts the install confirmation dialog. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallAccepted) { |
| ASSERT_TRUE(RunInstallTest("accepted.html", "extension.crx")); |
| } |
| |
| // Test having the default download directory missing. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, MissingDownloadDir) { |
| // Set a non-existent directory as the download path. |
| base::ScopedAllowBlockingForTesting allow_blocking; |
| base::ScopedTempDir temp_dir; |
| EXPECT_TRUE(temp_dir.CreateUniqueTempDir()); |
| base::FilePath missing_directory = temp_dir.Take(); |
| EXPECT_TRUE(base::DeleteFile(missing_directory, true)); |
| WebstoreInstaller::SetDownloadDirectoryForTests(&missing_directory); |
| |
| // Now run the install test, which should succeed. |
| ASSERT_TRUE(RunInstallTest("accepted.html", "extension.crx")); |
| |
| // Cleanup. |
| if (base::DirectoryExists(missing_directory)) |
| EXPECT_TRUE(base::DeleteFile(missing_directory, true)); |
| } |
| |
| // Tests passing a localized name. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallLocalized) { |
| ASSERT_TRUE(RunInstallTest("localized.html", "localized_extension.crx")); |
| } |
| |
| // Now test the case where the user cancels the confirmation dialog. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, InstallCancelled) { |
| ScopedTestDialogAutoConfirm auto_cancel(ScopedTestDialogAutoConfirm::CANCEL); |
| ASSERT_TRUE(RunInstallTest("cancelled.html", "extension.crx")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, IncorrectManifest1) { |
| ASSERT_TRUE(RunInstallTest("incorrect_manifest1.html", "extension.crx")); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, IncorrectManifest2) { |
| ASSERT_TRUE(RunInstallTest("incorrect_manifest2.html", "extension.crx")); |
| } |
| |
| // Disabled: http://crbug.com/174399 and http://crbug.com/177163 |
| #if defined(OS_WIN) && (defined(USE_AURA) || !defined(NDEBUG)) |
| #define MAYBE_AppInstallBubble DISABLED_AppInstallBubble |
| #else |
| #define MAYBE_AppInstallBubble AppInstallBubble |
| #endif |
| |
| // Tests that we can request an app installed bubble (instead of the default |
| // UI when an app is installed). |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, |
| MAYBE_AppInstallBubble) { |
| WebstoreInstallListener listener; |
| WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener); |
| ASSERT_TRUE(RunInstallTest("app_install_bubble.html", "app.crx")); |
| listener.Wait(); |
| ASSERT_TRUE(listener.received_success()); |
| ASSERT_EQ("iladmdjkfniedhfhcfoefgojhgaiaccc", listener.id()); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, IsInIncognitoMode) { |
| GURL page_url = GetTestServerURL("incognito.html"); |
| ASSERT_TRUE( |
| RunPageTest(page_url.spec(), ExtensionApiTest::kFlagUseIncognito)); |
| } |
| |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, IsNotInIncognitoMode) { |
| GURL page_url = GetTestServerURL("not_incognito.html"); |
| ASSERT_TRUE(RunPageTest(page_url.spec())); |
| } |
| |
| // Fails often on Windows dbg bots. http://crbug.com/177163. |
| #if defined(OS_WIN) |
| #define MAYBE_IconUrl DISABLED_IconUrl |
| #else |
| #define MAYBE_IconUrl IconUrl |
| #endif // defined(OS_WIN) |
| // Tests using the iconUrl parameter to the install function. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, MAYBE_IconUrl) { |
| ASSERT_TRUE(RunInstallTest("icon_url.html", "extension.crx")); |
| } |
| |
| // http://crbug.com/177163 |
| #if defined(OS_WIN) && !defined(NDEBUG) |
| #define MAYBE_BeginInstall DISABLED_BeginInstall |
| #else |
| #define MAYBE_BeginInstall BeginInstall |
| #endif |
| // Tests that the Approvals are properly created in beginInstall. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, MAYBE_BeginInstall) { |
| std::string appId = "iladmdjkfniedhfhcfoefgojhgaiaccc"; |
| std::string extensionId = "enfkhcelefdadlmkffamgdlgplcionje"; |
| ASSERT_TRUE(RunInstallTest("begin_install.html", "extension.crx")); |
| |
| std::unique_ptr<WebstoreInstaller::Approval> approval = |
| WebstorePrivateApi::PopApprovalForTesting(browser()->profile(), appId); |
| EXPECT_EQ(appId, approval->extension_id); |
| EXPECT_TRUE(approval->use_app_installed_bubble); |
| EXPECT_FALSE(approval->skip_post_install_ui); |
| EXPECT_EQ("2", approval->authuser); |
| EXPECT_EQ(browser()->profile(), approval->profile); |
| |
| approval = WebstorePrivateApi::PopApprovalForTesting( |
| browser()->profile(), extensionId); |
| EXPECT_EQ(extensionId, approval->extension_id); |
| EXPECT_FALSE(approval->use_app_installed_bubble); |
| EXPECT_FALSE(approval->skip_post_install_ui); |
| EXPECT_TRUE(approval->authuser.empty()); |
| EXPECT_EQ(browser()->profile(), approval->profile); |
| } |
| |
| // http://crbug.com/177163 |
| #if defined(OS_WIN) && !defined(NDEBUG) |
| #define MAYBE_InstallTheme DISABLED_InstallTheme |
| #else |
| #define MAYBE_InstallTheme InstallTheme |
| #endif |
| // Tests that themes are installed without an install prompt. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, MAYBE_InstallTheme) { |
| WebstoreInstallListener listener; |
| WebstorePrivateApi::SetWebstoreInstallerDelegateForTesting(&listener); |
| ASSERT_TRUE(RunInstallTest("theme.html", "../../theme.crx")); |
| listener.Wait(); |
| ASSERT_TRUE(listener.received_success()); |
| ASSERT_EQ("idlfhncioikpdnlhnmcjogambnefbbfp", listener.id()); |
| } |
| |
| // Tests that an error is properly reported when an empty crx is returned. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTest, EmptyCrx) { |
| ASSERT_TRUE(RunInstallTest("empty.html", "empty.crx")); |
| } |
| |
| #if BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| class ExtensionWebstorePrivateApiTestChild |
| : public ExtensionWebstorePrivateApiTest { |
| public: |
| void SetUpCommandLine(base::CommandLine* command_line) override { |
| ExtensionWebstorePrivateApiTest::SetUpCommandLine(command_line); |
| command_line->AppendSwitchASCII(switches::kSupervisedUserId, |
| supervised_users::kChildAccountSUID); |
| #if defined(OS_CHROMEOS) |
| command_line->AppendSwitchASCII( |
| chromeos::switches::kLoginUser, |
| "supervised_user@locally-managed.localhost"); |
| command_line->AppendSwitchASCII(chromeos::switches::kLoginProfile, "hash"); |
| #endif |
| } |
| }; |
| |
| // Tests that extension installation is blocked for child accounts, and |
| // attempting to do so produces a special error code. |
| // Note: This will have to be updated when we enable child-initiated installs. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateApiTestChild, InstallBlocked) { |
| ASSERT_TRUE(RunInstallTest("begin_install_fail_child.html", "extension.crx")); |
| } |
| |
| #endif // BUILDFLAG(ENABLE_SUPERVISED_USERS) |
| |
| class ExtensionWebstoreGetWebGLStatusTest : public InProcessBrowserTest { |
| protected: |
| void RunTest(bool webgl_allowed) { |
| // If Gpu access is disallowed then WebGL will not be available. |
| if (!content::GpuDataManager::GetInstance()->GpuAccessAllowed(NULL)) |
| webgl_allowed = false; |
| |
| static const char kEmptyArgs[] = "[]"; |
| static const char kWebGLStatusAllowed[] = "webgl_allowed"; |
| static const char kWebGLStatusBlocked[] = "webgl_blocked"; |
| scoped_refptr<WebstorePrivateGetWebGLStatusFunction> function = |
| new WebstorePrivateGetWebGLStatusFunction(); |
| std::unique_ptr<base::Value> result(utils::RunFunctionAndReturnSingleResult( |
| function.get(), kEmptyArgs, browser())); |
| ASSERT_TRUE(result); |
| EXPECT_EQ(base::Value::Type::STRING, result->type()); |
| std::string webgl_status; |
| EXPECT_TRUE(result->GetAsString(&webgl_status)); |
| EXPECT_STREQ(webgl_allowed ? kWebGLStatusAllowed : kWebGLStatusBlocked, |
| webgl_status.c_str()); |
| } |
| }; |
| |
| // Tests getWebGLStatus function when WebGL is allowed. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstoreGetWebGLStatusTest, Allowed) { |
| bool webgl_allowed = true; |
| RunTest(webgl_allowed); |
| } |
| |
| // Tests getWebGLStatus function when WebGL is blacklisted. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstoreGetWebGLStatusTest, Blocked) { |
| content::GpuDataManager::GetInstance()->BlacklistWebGLForTesting(); |
| |
| bool webgl_allowed = false; |
| RunTest(webgl_allowed); |
| } |
| |
| class ExtensionWebstorePrivateGetReferrerChainApiTest |
| : public ExtensionWebstorePrivateApiTest { |
| public: |
| void SetUpOnMainThread() override { |
| host_resolver()->AddRule("redirect1.com", "127.0.0.1"); |
| host_resolver()->AddRule("redirect2.com", "127.0.0.1"); |
| |
| ExtensionWebstorePrivateApiTest::SetUpOnMainThread(); |
| } |
| |
| GURL GetTestServerURLWithReferrers(const std::string& path) { |
| // Hand craft a url that will cause the test server to issue redirects. |
| const std::vector<std::string> redirects = {"redirect1.com", |
| "redirect2.com"}; |
| net::HostPortPair host_port = embedded_test_server()->host_port_pair(); |
| std::string redirect_chain; |
| for (const auto& redirect : redirects) { |
| std::string redirect_url = base::StringPrintf( |
| "http://%s:%d/server-redirect?", redirect.c_str(), host_port.port()); |
| redirect_chain += redirect_url; |
| } |
| |
| return GURL(redirect_chain + GetTestServerURL(path).spec()); |
| } |
| }; |
| |
| // Tests that the GetReferrerChain API returns the redirect information. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateGetReferrerChainApiTest, |
| GetReferrerChain) { |
| GURL page_url = GetTestServerURLWithReferrers("referrer_chain.html"); |
| ASSERT_TRUE(RunPageTest(page_url.spec())); |
| } |
| |
| // Tests that the GetReferrerChain API returns an empty string for profiles |
| // opted out of SafeBrowsing. |
| IN_PROC_BROWSER_TEST_F(ExtensionWebstorePrivateGetReferrerChainApiTest, |
| GetReferrerChainForNonSafeBrowsingUser) { |
| PrefService* pref_service = browser()->profile()->GetPrefs(); |
| EXPECT_TRUE(pref_service->GetBoolean(prefs::kSafeBrowsingEnabled)); |
| // Disable SafeBrowsing. |
| pref_service->SetBoolean(prefs::kSafeBrowsingEnabled, false); |
| |
| GURL page_url = GetTestServerURLWithReferrers("empty_referrer_chain.html"); |
| ASSERT_TRUE(RunPageTest(page_url.spec())); |
| } |
| |
| } // namespace extensions |