Chromium Code Reviews| Index: content/browser/plugin_browsertest.cc |
| diff --git a/content/browser/plugin_browsertest.cc b/content/browser/plugin_browsertest.cc |
| index 9effe325deae04d183047fc23c8dd3888516fdd1..c3f6223ba8315a6f594bcf55df318aa086164193 100644 |
| --- a/content/browser/plugin_browsertest.cc |
| +++ b/content/browser/plugin_browsertest.cc |
| @@ -2,559 +2,426 @@ |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| +#include "content/browser/plugin_process_host.h" |
|
sky
2014/05/23 20:44:03
Are these changes expected?
ananta
2014/05/23 21:16:29
No. Updated patch. PTAL
|
| + |
| +#if defined(OS_WIN) |
| +#include <windows.h> |
| +#elif defined(OS_POSIX) |
| +#include <utility> // for pair<> |
| +#endif |
| + |
| +#include <vector> |
| + |
| +#include "base/base_switches.h" |
| +#include "base/bind.h" |
| #include "base/command_line.h" |
| -#include "base/file_util.h" |
| +#include "base/files/file_path.h" |
| +#include "base/logging.h" |
| +#include "base/metrics/histogram.h" |
| #include "base/path_service.h" |
| +#include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/strings/utf_string_conversions.h" |
| -#include "content/browser/loader/resource_dispatcher_host_impl.h" |
| +#include "content/browser/browser_child_process_host_impl.h" |
| +#include "content/browser/loader/resource_message_filter.h" |
| +#include "content/browser/gpu/gpu_data_manager_impl.h" |
| +#include "content/browser/plugin_service_impl.h" |
| +#include "content/common/child_process_host_impl.h" |
| +#include "content/common/plugin_process_messages.h" |
| +#include "content/common/resource_messages.h" |
| #include "content/public/browser/browser_thread.h" |
| -#include "content/public/browser/resource_dispatcher_host_delegate.h" |
| +#include "content/public/browser/content_browser_client.h" |
| +#include "content/public/browser/notification_types.h" |
| +#include "content/public/browser/plugin_service.h" |
| +#include "content/public/browser/resource_context.h" |
| #include "content/public/common/content_switches.h" |
| -#include "content/public/test/browser_test_utils.h" |
| -#include "content/public/test/content_browser_test.h" |
| -#include "content/public/test/content_browser_test_utils.h" |
| -#include "content/public/test/test_utils.h" |
| -#include "content/shell/browser/shell.h" |
| -#include "content/shell/common/shell_switches.h" |
| -#include "content/test/net/url_request_mock_http_job.h" |
| -#include "net/test/embedded_test_server/embedded_test_server.h" |
| -#include "net/url_request/url_request.h" |
| -#include "ui/gfx/rect.h" |
| +#include "content/public/common/process_type.h" |
| +#include "content/public/common/sandboxed_process_launcher_delegate.h" |
| +#include "ipc/ipc_switches.h" |
| +#include "net/url_request/url_request_context_getter.h" |
| +#include "ui/base/ui_base_switches.h" |
| +#include "ui/gfx/native_widget_types.h" |
| +#include "ui/gl/gl_switches.h" |
| -#if defined(OS_WIN) |
| -#include "base/win/registry.h" |
| +#if defined(OS_MACOSX) |
| +#include "base/mac/mac_util.h" |
| +#include "content/common/plugin_carbon_interpose_constants_mac.h" |
| +#include "ui/gfx/rect.h" |
| #endif |
| -// TODO(jschuh): Finish plugins on Win64. crbug.com/180861 |
| -#if defined(OS_WIN) && defined(ARCH_CPU_X86_64) |
| -#define MAYBE(x) DISABLED_##x |
| -#else |
| -#define MAYBE(x) x |
| +#if defined(OS_WIN) |
| +#include "base/win/windows_version.h" |
| +#include "content/common/plugin_constants_win.h" |
| +#include "ui/gfx/switches.h" |
| #endif |
| -using base::ASCIIToUTF16; |
| - |
| namespace content { |
| -namespace { |
| -void SetUrlRequestMock(const base::FilePath& path) { |
| - URLRequestMockHTTPJob::AddUrlHandler(path); |
| +#if defined(OS_WIN) |
| +void PluginProcessHost::OnPluginWindowDestroyed(HWND window, HWND parent) { |
| + // The window is destroyed at this point, we just care about its parent, which |
| + // is the intermediate window we created. |
| + std::set<HWND>::iterator window_index = |
| + plugin_parent_windows_set_.find(parent); |
| + if (window_index == plugin_parent_windows_set_.end()) |
| + return; |
| + |
| + plugin_parent_windows_set_.erase(window_index); |
| + PostMessage(parent, WM_CLOSE, 0, 0); |
| } |
| +void PluginProcessHost::AddWindow(HWND window) { |
| + plugin_parent_windows_set_.insert(window); |
| } |
| +#endif // defined(OS_WIN) |
| -class PluginTest : public ContentBrowserTest { |
| - protected: |
| - PluginTest() {} |
| +// NOTE: changes to this class need to be reviewed by the security team. |
| +class PluginSandboxedProcessLauncherDelegate |
| + : public SandboxedProcessLauncherDelegate { |
| + public: |
| + explicit PluginSandboxedProcessLauncherDelegate(ChildProcessHost* host) |
| +#if defined(OS_POSIX) |
| + : ipc_fd_(host->TakeClientFileDescriptor()) |
| +#endif // OS_POSIX |
| + {} |
| - virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { |
| - // Some NPAPI tests schedule garbage collection to force object tear-down. |
| - command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose_gc"); |
| + virtual ~PluginSandboxedProcessLauncherDelegate() {} |
| #if defined(OS_WIN) |
| - const testing::TestInfo* const test_info = |
| - testing::UnitTest::GetInstance()->current_test_info(); |
| - if (strcmp(test_info->name(), "MediaPlayerNew") == 0) { |
| - // The installer adds our process names to the registry key below. Since |
| - // the installer might not have run on this machine, add it manually. |
| - base::win::RegKey regkey; |
| - if (regkey.Open(HKEY_LOCAL_MACHINE, |
| - L"Software\\Microsoft\\MediaPlayer\\ShimInclusionList", |
| - KEY_WRITE) == ERROR_SUCCESS) { |
| - regkey.CreateKey(L"BROWSER_TESTS.EXE", KEY_READ); |
| - } |
| - } else if (strcmp(test_info->name(), "FlashSecurity") == 0) { |
| - command_line->AppendSwitchASCII(switches::kTestSandbox, |
| - "security_tests.dll"); |
| - } |
| -#elif defined(OS_MACOSX) |
| - base::FilePath plugin_dir; |
| - PathService::Get(base::DIR_MODULE, &plugin_dir); |
| - plugin_dir = plugin_dir.AppendASCII("plugins"); |
| - // The plugins directory isn't read by default on the Mac, so it needs to be |
| - // explicitly registered. |
| - command_line->AppendSwitchPath(switches::kExtraPluginDir, plugin_dir); |
| -#endif |
| - } |
| - |
| - virtual void SetUpOnMainThread() OVERRIDE { |
| - base::FilePath path = GetTestFilePath("", ""); |
| - BrowserThread::PostTask( |
| - BrowserThread::IO, FROM_HERE, base::Bind(&SetUrlRequestMock, path)); |
| - } |
| - |
| - static void LoadAndWaitInWindow(Shell* window, const GURL& url) { |
| - base::string16 expected_title(ASCIIToUTF16("OK")); |
| - TitleWatcher title_watcher(window->web_contents(), expected_title); |
| - title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); |
| - title_watcher.AlsoWaitForTitle(ASCIIToUTF16("plugin_not_found")); |
| - NavigateToURL(window, url); |
| - base::string16 title = title_watcher.WaitAndGetTitle(); |
| - if (title == ASCIIToUTF16("plugin_not_found")) { |
| - const testing::TestInfo* const test_info = |
| - testing::UnitTest::GetInstance()->current_test_info(); |
| - VLOG(0) << "PluginTest." << test_info->name() |
| - << " not running because plugin not installed."; |
| - } else { |
| - EXPECT_EQ(expected_title, title); |
| - } |
| - } |
| - |
| - void LoadAndWait(const GURL& url) { |
| - LoadAndWaitInWindow(shell(), url); |
| - } |
| - |
| - GURL GetURL(const char* filename) { |
| - return GetTestUrl("npapi", filename); |
| + virtual bool ShouldSandbox() OVERRIDE { |
| + return false; |
| } |
| - void NavigateAway() { |
| - GURL url = GetTestUrl("", "simple_page.html"); |
| - LoadAndWait(url); |
| +#elif defined(OS_POSIX) |
| + virtual int GetIpcFd() OVERRIDE { |
| + return ipc_fd_; |
| } |
| +#endif // OS_WIN |
| - void TestPlugin(const char* filename) { |
| - base::FilePath path = GetTestFilePath("plugin", filename); |
| - if (!base::PathExists(path)) { |
| - const testing::TestInfo* const test_info = |
| - testing::UnitTest::GetInstance()->current_test_info(); |
| - VLOG(0) << "PluginTest." << test_info->name() |
| - << " not running because test data wasn't found."; |
| - return; |
| - } |
| + private: |
| +#if defined(OS_POSIX) |
| + int ipc_fd_; |
| +#endif // OS_POSIX |
| - GURL url = GetTestUrl("plugin", filename); |
| - LoadAndWait(url); |
| - } |
| + DISALLOW_COPY_AND_ASSIGN(PluginSandboxedProcessLauncherDelegate); |
| }; |
| -// Make sure that navigating away from a plugin referenced by JS doesn't |
| -// crash. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, UnloadNoCrash) { |
| - LoadAndWait(GetURL("layout_test_plugin.html")); |
| - NavigateAway(); |
| -} |
| - |
| -// Tests if a plugin executing a self deleting script using NPN_GetURL |
| -// works without crashing or hanging |
| -// Flaky: http://crbug.com/59327 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginGetUrl)) { |
| - LoadAndWait(GetURL("self_delete_plugin_geturl.html")); |
| -} |
| - |
| -// Tests if a plugin executing a self deleting script using Invoke |
| -// works without crashing or hanging |
| -// Flaky. See http://crbug.com/30702 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInvoke)) { |
| - LoadAndWait(GetURL("self_delete_plugin_invoke.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectReleasedOnDestruction)) { |
| - NavigateToURL(shell(), GetURL("npobject_released_on_destruction.html")); |
| - NavigateAway(); |
| -} |
| - |
| -// Test that a dialog is properly created when a plugin throws an |
| -// exception. Should be run for in and out of process plugins, but |
| -// the more interesting case is out of process, where we must route |
| -// the exception to the correct renderer. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectSetException)) { |
| - LoadAndWait(GetURL("npobject_set_exception.html")); |
| -} |
| - |
| -#if defined(OS_WIN) |
| -// Tests if a plugin executing a self deleting script in the context of |
| -// a synchronous mouseup works correctly. |
| -// This was never ported to Mac. The only thing remaining is to make |
| -// SimulateMouseClick get to Mac plugins, currently it doesn't work. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, |
| - MAYBE(SelfDeletePluginInvokeInSynchronousMouseUp)) { |
| - NavigateToURL(shell(), GetURL("execute_script_delete_in_mouse_up.html")); |
| - |
| - base::string16 expected_title(ASCIIToUTF16("OK")); |
| - TitleWatcher title_watcher(shell()->web_contents(), expected_title); |
| - title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); |
| - SimulateMouseClick(shell()->web_contents(), 0, |
| - blink::WebMouseEvent::ButtonLeft); |
| - EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| -} |
| -#endif |
| - |
| -// Flaky, http://crbug.com/302274. |
| +PluginProcessHost::PluginProcessHost() |
| #if defined(OS_MACOSX) |
| -#define MAYBE_GetURLRequest404Response DISABLED_GetURLRequest404Response |
| -#else |
| -#define MAYBE_GetURLRequest404Response MAYBE(GetURLRequest404Response) |
| + : plugin_cursor_visible_(true) |
| #endif |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE_GetURLRequest404Response) { |
| - GURL url(URLRequestMockHTTPJob::GetMockUrl( |
| - base::FilePath().AppendASCII("npapi"). |
| - AppendASCII("plugin_url_request_404.html"))); |
| - LoadAndWait(url); |
| +{ |
| + process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_PLUGIN, this)); |
| } |
| -// Tests if a plugin executing a self deleting script using Invoke with |
| -// a modal dialog showing works without crashing or hanging |
| -// Disabled, flakily exceeds timeout, http://crbug.com/46257. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInvokeAlert)) { |
| - // Navigate asynchronously because if we waitd until it completes, there's a |
| - // race condition where the alert can come up before we start watching for it. |
| - shell()->LoadURL(GetURL("self_delete_plugin_invoke_alert.html")); |
| - |
| - base::string16 expected_title(ASCIIToUTF16("OK")); |
| - TitleWatcher title_watcher(shell()->web_contents(), expected_title); |
| - title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); |
| - |
| - WaitForAppModalDialog(shell()); |
| - |
| - EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| -} |
| - |
| -// Test passing arguments to a plugin. |
| -// crbug.com/306318 |
| -#if !defined(OS_LINUX) |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Arguments)) { |
| - LoadAndWait(GetURL("arguments.html")); |
| -} |
| +PluginProcessHost::~PluginProcessHost() { |
| +#if defined(OS_WIN) |
| + // We erase HWNDs from the plugin_parent_windows_set_ when we receive a |
| + // notification that the window is being destroyed. If we don't receive this |
| + // notification and the PluginProcessHost instance is being destroyed, it |
| + // means that the plugin process crashed. We paint a sad face in this case in |
| + // the renderer process. To ensure that the sad face shows up, and we don't |
| + // leak HWNDs, we should destroy existing plugin parent windows. |
| + std::set<HWND>::iterator window_index; |
| + for (window_index = plugin_parent_windows_set_.begin(); |
| + window_index != plugin_parent_windows_set_.end(); |
| + ++window_index) { |
| + PostMessage(*window_index, WM_CLOSE, 0, 0); |
| + } |
| +#elif defined(OS_MACOSX) |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + // If the plugin process crashed but had fullscreen windows open at the time, |
| + // make sure that the menu bar is visible. |
| + for (size_t i = 0; i < plugin_fullscreen_windows_set_.size(); ++i) { |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + base::Bind(base::mac::ReleaseFullScreen, |
| + base::mac::kFullScreenModeHideAll)); |
| + } |
| + // If the plugin hid the cursor, reset that. |
| + if (!plugin_cursor_visible_) { |
| + BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, |
| + base::Bind(base::mac::SetCursorVisibility, true)); |
| + } |
| #endif |
| - |
| -// Test invoking many plugins within a single page. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ManyPlugins)) { |
| - LoadAndWait(GetURL("many_plugins.html")); |
| -} |
| - |
| -// Test various calls to GetURL from a plugin. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetURL)) { |
| - LoadAndWait(GetURL("geturl.html")); |
| -} |
| - |
| -// Test various calls to GetURL for javascript URLs with |
| -// non NULL targets from a plugin. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetJavaScriptURL)) { |
| - LoadAndWait(GetURL("get_javascript_url.html")); |
| + // Cancel all pending and sent requests. |
| + CancelRequests(); |
| } |
| -// Test that calling GetURL with a javascript URL and target=_self |
| -// works properly when the plugin is embedded in a subframe. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetJavaScriptURL2)) { |
| - LoadAndWait(GetURL("get_javascript_url2.html")); |
| +bool PluginProcessHost::Send(IPC::Message* message) { |
| + return process_->Send(message); |
| } |
| -// Test is flaky on linux/cros/win builders. http://crbug.com/71904 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, GetURLRedirectNotification) { |
| - LoadAndWait(GetURL("geturl_redirect_notify.html")); |
| -} |
| +bool PluginProcessHost::Init(const WebPluginInfo& info) { |
| + info_ = info; |
| + process_->SetName(info_.name); |
| -// Tests that identity is preserved for NPObjects passed from a plugin |
| -// into JavaScript. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectIdentity)) { |
| - LoadAndWait(GetURL("npobject_identity.html")); |
| -} |
| + std::string channel_id = process_->GetHost()->CreateChannel(); |
| + if (channel_id.empty()) |
| + return false; |
| -// Tests that if an NPObject is proxies back to its original process, the |
| -// original pointer is returned and not a proxy. If this fails the plugin |
| -// will crash. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectProxy)) { |
| - LoadAndWait(GetURL("npobject_proxy.html")); |
| -} |
| + // Build command line for plugin. When we have a plugin launcher, we can't |
| + // allow "self" on linux and we need the real file path. |
| + const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); |
| + CommandLine::StringType plugin_launcher = |
| + browser_command_line.GetSwitchValueNative(switches::kPluginLauncher); |
| -#if defined(OS_WIN) || defined(OS_MACOSX) |
| -// Tests if a plugin executing a self deleting script in the context of |
| -// a synchronous paint event works correctly |
| -// http://crbug.com/44960 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, |
| - MAYBE(SelfDeletePluginInvokeInSynchronousPaint)) { |
| - LoadAndWait(GetURL("execute_script_delete_in_paint.html")); |
| -} |
| +#if defined(OS_MACOSX) |
| + // Run the plug-in process in a mode tolerant of heap execution without |
| + // explicit mprotect calls. Some plug-ins still rely on this quaint and |
| + // archaic "feature." See http://crbug.com/93551. |
| + int flags = ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION; |
| +#elif defined(OS_LINUX) |
| + int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : |
| + ChildProcessHost::CHILD_NORMAL; |
| +#else |
| + int flags = ChildProcessHost::CHILD_NORMAL; |
| #endif |
| -// Tests that if a plugin executes a self resizing script in the context of a |
| -// synchronous paint, the plugin doesn't use deallocated memory. |
| -// http://crbug.com/139462 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ResizeDuringPaint)) { |
| - LoadAndWait(GetURL("resize_during_paint.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInNewStream)) { |
| - LoadAndWait(GetURL("self_delete_plugin_stream.html")); |
| -} |
| - |
| -// On Mac this test asserts in plugin_host: http://crbug.com/95558 |
| -// On all platforms it flakes in ~URLRequestContext: http://crbug.com/310336 |
| -#if !defined(NDEBUG) |
| -IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_DeletePluginInDeallocate) { |
| - LoadAndWait(GetURL("plugin_delete_in_deallocate.html")); |
| -} |
| + base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); |
| + if (exe_path.empty()) |
| + return false; |
| + |
| + CommandLine* cmd_line = new CommandLine(exe_path); |
| + // Put the process type and plugin path first so they're easier to see |
| + // in process listings using native process management tools. |
| + cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kPluginProcess); |
| + cmd_line->AppendSwitchPath(switches::kPluginPath, info.path); |
| + |
| + // Propagate the following switches to the plugin command line (along with |
| + // any associated values) if present in the browser command line |
| + static const char* const kSwitchNames[] = { |
| + switches::kDisableBreakpad, |
| + switches::kDisableDirectNPAPIRequests, |
| + switches::kEnableStatsTable, |
| + switches::kFullMemoryCrashReport, |
| + switches::kLoggingLevel, |
| + switches::kLogPluginMessages, |
| + switches::kNoSandbox, |
| + switches::kPluginStartupDialog, |
| + switches::kTestSandbox, |
| + switches::kTraceStartup, |
| + switches::kUseGL, |
| +#if defined(OS_MACOSX) |
| + switches::kDisableCoreAnimationPlugins, |
| + switches::kEnableSandboxLogging, |
| #endif |
| + }; |
| -#if defined(OS_WIN) |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(VerifyPluginWindowRect)) { |
| - LoadAndWait(GetURL("verify_plugin_window_rect.html")); |
| -} |
| + cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, |
| + arraysize(kSwitchNames)); |
| -// Tests that creating a new instance of a plugin while another one is handling |
| -// a paint message doesn't cause deadlock. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(CreateInstanceInPaint)) { |
| - LoadAndWait(GetURL("create_instance_in_paint.html")); |
| -} |
| + GpuDataManagerImpl::GetInstance()->AppendPluginCommandLine(cmd_line); |
| -// Tests that putting up an alert in response to a paint doesn't deadlock. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_AlertInWindowMessage) { |
| - NavigateToURL(shell(), GetURL("alert_in_window_message.html")); |
| + // If specified, prepend a launcher program to the command line. |
| + if (!plugin_launcher.empty()) |
| + cmd_line->PrependWrapper(plugin_launcher); |
| - WaitForAppModalDialog(shell()); |
| - WaitForAppModalDialog(shell()); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(VerifyNPObjectLifetimeTest)) { |
| - LoadAndWait(GetURL("npobject_lifetime_test.html")); |
| -} |
| - |
| -// Tests that we don't crash or assert if NPP_New fails |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NewFails)) { |
| - LoadAndWait(GetURL("new_fails.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInNPNEvaluate)) { |
| - LoadAndWait(GetURL("execute_script_delete_in_npn_evaluate.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, |
| - MAYBE(SelfDeleteCreatePluginInNPNEvaluate)) { |
| - LoadAndWait(GetURL("npn_plugin_delete_create_in_evaluate.html")); |
| -} |
| - |
| -#endif // OS_WIN |
| + std::string locale = GetContentClient()->browser()->GetApplicationLocale(); |
| + if (!locale.empty()) { |
| + // Pass on the locale so the null plugin will use the right language in the |
| + // prompt to install the desired plugin. |
| + cmd_line->AppendSwitchASCII(switches::kLang, locale); |
| + } |
| -// If this flakes, reopen http://crbug.com/17645 |
| -// As of 6 July 2011, this test is flaky on Windows (perhaps due to timing out). |
| -#if !defined(OS_MACOSX) && !defined(OS_LINUX) |
| -// Disabled on Mac because the plugin side isn't implemented yet, see |
| -// "TODO(port)" in plugin_javascript_open_popup.cc. |
| -// Disabled on Linux because we don't support NPAPI any more. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(OpenPopupWindowWithPlugin)) { |
| - LoadAndWait(GetURL("get_javascript_open_popup_with_plugin.html")); |
| -} |
| + cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); |
| + |
| +#if defined(OS_POSIX) |
| + base::EnvironmentMap env; |
| +#if defined(OS_MACOSX) && !defined(__LP64__) |
| + if (browser_command_line.HasSwitch(switches::kEnableCarbonInterposing)) { |
| + std::string interpose_list = GetContentClient()->GetCarbonInterposePath(); |
| + if (!interpose_list.empty()) { |
| + // Add our interposing library for Carbon. This is stripped back out in |
| + // plugin_main.cc, so changes here should be reflected there. |
| + const char* existing_list = getenv(kDYLDInsertLibrariesKey); |
| + if (existing_list) { |
| + interpose_list.insert(0, ":"); |
| + interpose_list.insert(0, existing_list); |
| + } |
| + } |
| + env[kDYLDInsertLibrariesKey] = interpose_list; |
| + } |
| #endif |
| - |
| -// Test checking the privacy mode is off. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PrivateDisabled)) { |
| - LoadAndWait(GetURL("private.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ScheduleTimer)) { |
| - LoadAndWait(GetURL("schedule_timer.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PluginThreadAsyncCall)) { |
| - LoadAndWait(GetURL("plugin_thread_async_call.html")); |
| -} |
| - |
| -IN_PROC_BROWSER_TEST_F(PluginTest, PluginSingleRangeRequest) { |
| - LoadAndWait(GetURL("plugin_single_range_request.html")); |
| -} |
| - |
| -// Test checking the privacy mode is on. |
| -// If this flakes on Linux, use http://crbug.com/104380 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PrivateEnabled)) { |
| - GURL url = GetURL("private.html"); |
| - url = GURL(url.spec() + "?private"); |
| - LoadAndWaitInWindow(CreateOffTheRecordBrowser(), url); |
| -} |
| - |
| -#if defined(OS_WIN) || defined(OS_MACOSX) |
| -// Test a browser hang due to special case of multiple |
| -// plugin instances indulged in sync calls across renderer. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(MultipleInstancesSyncCalls)) { |
| - LoadAndWait(GetURL("multiple_instances_sync_calls.html")); |
| -} |
| #endif |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetURLRequestFailWrite)) { |
| - GURL url(URLRequestMockHTTPJob::GetMockUrl( |
| - base::FilePath().AppendASCII("npapi"). |
| - AppendASCII("plugin_url_request_fail_write.html"))); |
| - LoadAndWait(url); |
| -} |
| - |
| + process_->Launch( |
| + new PluginSandboxedProcessLauncherDelegate(process_->GetHost()), |
| + cmd_line); |
| + |
| + // The plugin needs to be shutdown gracefully, i.e. NP_Shutdown needs to be |
| + // called on the plugin. The plugin process exits when it receives the |
| + // OnChannelError notification indicating that the browser plugin channel has |
| + // been destroyed. |
| + process_->SetTerminateChildOnShutdown(false); |
| + |
| + ResourceMessageFilter::GetContextsCallback get_contexts_callback( |
| + base::Bind(&PluginProcessHost::GetContexts, |
| + base::Unretained(this))); |
| + |
| + // TODO(jam): right now we're passing NULL for appcache, blob storage, and |
| + // file system. If NPAPI plugins actually use this, we'll have to plumb them. |
| + ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( |
| + process_->GetData().id, PROCESS_TYPE_PLUGIN, NULL, NULL, NULL, NULL, |
| + get_contexts_callback); |
| + process_->AddFilter(resource_message_filter); |
| + return true; |
| +} |
| + |
| +void PluginProcessHost::ForceShutdown() { |
| + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
| + Send(new PluginProcessMsg_NotifyRenderersOfPendingShutdown()); |
| + process_->ForceShutdown(); |
| +} |
| + |
| +bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { |
| + bool handled = true; |
| + IPC_BEGIN_MESSAGE_MAP(PluginProcessHost, msg) |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelCreated, OnChannelCreated) |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelDestroyed, |
| + OnChannelDestroyed) |
| #if defined(OS_WIN) |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(EnsureScriptingWorksInDestroy)) { |
| - LoadAndWait(GetURL("ensure_scripting_works_in_destroy.html")); |
| -} |
| - |
| -// This test uses a Windows Event to signal to the plugin that it should crash |
| -// on NP_Initialize. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NoHangIfInitCrashes)) { |
| - HANDLE crash_event = CreateEvent(NULL, TRUE, FALSE, L"TestPluginCrashOnInit"); |
| - SetEvent(crash_event); |
| - LoadAndWait(GetURL("no_hang_if_init_crashes.html")); |
| - CloseHandle(crash_event); |
| -} |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginWindowDestroyed, |
| + OnPluginWindowDestroyed) |
| #endif |
| - |
| -// If this flakes on Mac, use http://crbug.com/111508 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PluginReferrerTest)) { |
| - GURL url(URLRequestMockHTTPJob::GetMockUrl( |
| - base::FilePath().AppendASCII("npapi"). |
| - AppendASCII("plugin_url_request_referrer_test.html"))); |
| - LoadAndWait(url); |
| -} |
| - |
| #if defined(OS_MACOSX) |
| -// Test is flaky, see http://crbug.com/134515. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_PluginConvertPointTest) { |
| - gfx::Rect bounds(50, 50, 400, 400); |
| - SetWindowBounds(shell()->window(), bounds); |
| - |
| - NavigateToURL(shell(), GetURL("convert_point.html")); |
| - |
| - base::string16 expected_title(ASCIIToUTF16("OK")); |
| - TitleWatcher title_watcher(shell()->web_contents(), expected_title); |
| - title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); |
| - // TODO(stuartmorgan): When the automation system supports sending clicks, |
| - // change the test to trigger on mouse-down rather than window focus. |
| - |
| - // TODO: is this code still needed? It was here when it used to run in |
| - // browser_tests. |
| - //static_cast<WebContentsDelegate*>(shell())-> |
| - // ActivateContents(shell()->web_contents()); |
| - EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); |
| -} |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSelectWindow, |
| + OnPluginSelectWindow) |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginShowWindow, |
| + OnPluginShowWindow) |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginHideWindow, |
| + OnPluginHideWindow) |
| + IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSetCursorVisibility, |
| + OnPluginSetCursorVisibility) |
| #endif |
| + IPC_MESSAGE_UNHANDLED(handled = false) |
| + IPC_END_MESSAGE_MAP() |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Flash)) { |
| - TestPlugin("flash.html"); |
| + return handled; |
| } |
| -#if defined(OS_WIN) |
| -// Windows only test |
| -IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_FlashSecurity) { |
| - TestPlugin("flash.html"); |
| -} |
| -#endif // defined(OS_WIN) |
| - |
| -#if defined(OS_WIN) |
| -// TODO(port) Port the following tests to platforms that have the required |
| -// plugins. |
| -// Flaky: http://crbug.com/55915 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Quicktime)) { |
| - TestPlugin("quicktime.html"); |
| -} |
| +void PluginProcessHost::OnChannelConnected(int32 peer_pid) { |
| + for (size_t i = 0; i < pending_requests_.size(); ++i) { |
| + RequestPluginChannel(pending_requests_[i]); |
| + } |
| -// Disabled - http://crbug.com/44662 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(MediaPlayerNew)) { |
| - TestPlugin("wmp_new.html"); |
| + pending_requests_.clear(); |
| } |
| -// Disabled - http://crbug.com/44673 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Real)) { |
| - TestPlugin("real.html"); |
| +void PluginProcessHost::OnChannelError() { |
| + CancelRequests(); |
| } |
| -// http://crbug.com/320041 |
| -#if (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) || \ |
| - (defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)) |
| -#define MAYBE_FlashOctetStream DISABLED_FlashOctetStream |
| -#else |
| -#define MAYBE_FlashOctetStream FlashOctetStream |
| -#endif |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE_FlashOctetStream) { |
| - TestPlugin("flash-octet-stream.html"); |
| +bool PluginProcessHost::CanShutdown() { |
| + return sent_requests_.empty(); |
| } |
| -#if defined(OS_WIN) |
| -// http://crbug.com/53926 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_FlashLayoutWhilePainting) { |
| -#else |
| -IN_PROC_BROWSER_TEST_F(PluginTest, FlashLayoutWhilePainting) { |
| -#endif |
| - TestPlugin("flash-layout-while-painting.html"); |
| +void PluginProcessHost::OnProcessCrashed(int exit_code) { |
| + PluginServiceImpl::GetInstance()->RegisterPluginCrash(info_.path); |
| } |
| -// http://crbug.com/8690 |
| -IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_Java) { |
| - TestPlugin("Java.html"); |
| -} |
| +void PluginProcessHost::CancelRequests() { |
| + for (size_t i = 0; i < pending_requests_.size(); ++i) |
| + pending_requests_[i]->OnError(); |
| + pending_requests_.clear(); |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Silverlight)) { |
| - TestPlugin("silverlight.html"); |
| + while (!sent_requests_.empty()) { |
| + Client* client = sent_requests_.front(); |
| + if (client) |
| + client->OnError(); |
| + sent_requests_.pop_front(); |
| + } |
| } |
| -#endif // defined(OS_WIN) |
| -class TestResourceDispatcherHostDelegate |
| - : public ResourceDispatcherHostDelegate { |
| - public: |
| - TestResourceDispatcherHostDelegate() : found_cookie_(false) {} |
| +void PluginProcessHost::OpenChannelToPlugin(Client* client) { |
| + BrowserThread::PostTask( |
| + BrowserThread::UI, FROM_HERE, |
| + base::Bind(&BrowserChildProcessHostImpl::NotifyProcessInstanceCreated, |
| + process_->GetData())); |
| + client->SetPluginInfo(info_); |
| + if (process_->GetHost()->IsChannelOpening()) { |
| + // The channel is already in the process of being opened. Put |
| + // this "open channel" request into a queue of requests that will |
| + // be run once the channel is open. |
| + pending_requests_.push_back(client); |
| + return; |
| + } |
| - bool found_cookie() { return found_cookie_; } |
| + // We already have an open channel, send a request right away to plugin. |
| + RequestPluginChannel(client); |
| +} |
| - void WaitForPluginRequest() { |
| - if (found_cookie_) |
| +void PluginProcessHost::CancelPendingRequest(Client* client) { |
| + std::vector<Client*>::iterator it = pending_requests_.begin(); |
| + while (it != pending_requests_.end()) { |
| + if (client == *it) { |
| + pending_requests_.erase(it); |
| return; |
| - |
| - runner_ = new MessageLoopRunner; |
| - runner_->Run(); |
| + } |
| + ++it; |
| } |
| + DCHECK(it != pending_requests_.end()); |
| +} |
| - private: |
| - // ResourceDispatcherHostDelegate implementation: |
| - virtual void OnResponseStarted( |
| - net::URLRequest* request, |
| - ResourceContext* resource_context, |
| - ResourceResponse* response, |
| - IPC::Sender* sender) OVERRIDE { |
| - // The URL below comes from plugin_geturl_test.cc. |
| - if (!EndsWith(request->url().spec(), |
| - "npapi/plugin_ref_target_page.html", |
| - true)) { |
| +void PluginProcessHost::CancelSentRequest(Client* client) { |
| + std::list<Client*>::iterator it = sent_requests_.begin(); |
| + while (it != sent_requests_.end()) { |
| + if (client == *it) { |
| + *it = NULL; |
| return; |
| } |
| - net::HttpRequestHeaders headers; |
| - bool found_cookie = false; |
| - if (request->GetFullRequestHeaders(&headers) && |
| - headers.ToString().find("Cookie: blah") != std::string::npos) { |
| - found_cookie = true; |
| - } |
| - BrowserThread::PostTask( |
| - BrowserThread::UI, |
| - FROM_HERE, |
| - base::Bind(&TestResourceDispatcherHostDelegate::GotCookie, |
| - base::Unretained(this), found_cookie)); |
| + ++it; |
| } |
| - |
| - void GotCookie(bool found_cookie) { |
| - found_cookie_ = found_cookie; |
| - if (runner_) |
| - runner_->QuitClosure().Run(); |
| + DCHECK(it != sent_requests_.end()); |
| +} |
| + |
| +void PluginProcessHost::RequestPluginChannel(Client* client) { |
| + // We can't send any sync messages from the browser because it might lead to |
| + // a hang. However this async messages must be answered right away by the |
| + // plugin process (i.e. unblocks a Send() call like a sync message) otherwise |
| + // a deadlock can occur if the plugin creation request from the renderer is |
| + // a result of a sync message by the plugin process. |
| + PluginProcessMsg_CreateChannel* msg = |
| + new PluginProcessMsg_CreateChannel( |
| + client->ID(), |
| + client->OffTheRecord()); |
| + msg->set_unblock(true); |
| + if (Send(msg)) { |
| + sent_requests_.push_back(client); |
| + client->OnSentPluginChannelRequest(); |
| + } else { |
| + client->OnError(); |
| } |
| +} |
| - scoped_refptr<MessageLoopRunner> runner_; |
| - bool found_cookie_; |
| +void PluginProcessHost::OnChannelCreated( |
| + const IPC::ChannelHandle& channel_handle) { |
| + Client* client = sent_requests_.front(); |
| - DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate); |
| -}; |
| + if (client) { |
| + if (!resource_context_map_.count(client->ID())) { |
| + ResourceContextEntry entry; |
| + entry.ref_count = 0; |
| + entry.resource_context = client->GetResourceContext(); |
| + resource_context_map_[client->ID()] = entry; |
| + } |
| + resource_context_map_[client->ID()].ref_count++; |
| + client->OnChannelOpened(channel_handle); |
| + } |
| + sent_requests_.pop_front(); |
| +} |
| + |
| +void PluginProcessHost::OnChannelDestroyed(int renderer_id) { |
| + resource_context_map_[renderer_id].ref_count--; |
| + if (!resource_context_map_[renderer_id].ref_count) |
| + resource_context_map_.erase(renderer_id); |
| +} |
| -// Ensure that cookies get sent with plugin requests. |
| -IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Cookies)) { |
| - // Create a new browser just to ensure that the plugin process' child_id is |
| - // not equal to its type (PROCESS_TYPE_PLUGIN), as that was the error which |
| - // caused this bug. |
| - NavigateToURL(CreateBrowser(), GURL("about:blank")); |
| - |
| - ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); |
| - GURL url(embedded_test_server()->GetURL("/npapi/cookies.html")); |
| - |
| - TestResourceDispatcherHostDelegate test_delegate; |
| - ResourceDispatcherHostDelegate* old_delegate = |
| - ResourceDispatcherHostImpl::Get()->delegate(); |
| - ResourceDispatcherHostImpl::Get()->SetDelegate(&test_delegate); |
| - LoadAndWait(url); |
| - test_delegate.WaitForPluginRequest(); |
| - ASSERT_TRUE(test_delegate.found_cookie()); |
| - ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate); |
| +void PluginProcessHost::GetContexts(const ResourceHostMsg_Request& request, |
| + ResourceContext** resource_context, |
| + net::URLRequestContext** request_context) { |
| + *resource_context = |
| + resource_context_map_[request.origin_pid].resource_context; |
| + *request_context = (*resource_context)->GetRequestContext(); |
| } |
| } // namespace content |