OLD | NEW |
---|---|
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #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
| |
6 | |
7 #if defined(OS_WIN) | |
8 #include <windows.h> | |
9 #elif defined(OS_POSIX) | |
10 #include <utility> // for pair<> | |
11 #endif | |
12 | |
13 #include <vector> | |
14 | |
15 #include "base/base_switches.h" | |
16 #include "base/bind.h" | |
5 #include "base/command_line.h" | 17 #include "base/command_line.h" |
6 #include "base/file_util.h" | 18 #include "base/files/file_path.h" |
19 #include "base/logging.h" | |
20 #include "base/metrics/histogram.h" | |
7 #include "base/path_service.h" | 21 #include "base/path_service.h" |
22 #include "base/strings/string_number_conversions.h" | |
8 #include "base/strings/string_util.h" | 23 #include "base/strings/string_util.h" |
9 #include "base/strings/utf_string_conversions.h" | 24 #include "base/strings/utf_string_conversions.h" |
10 #include "content/browser/loader/resource_dispatcher_host_impl.h" | 25 #include "content/browser/browser_child_process_host_impl.h" |
26 #include "content/browser/loader/resource_message_filter.h" | |
27 #include "content/browser/gpu/gpu_data_manager_impl.h" | |
28 #include "content/browser/plugin_service_impl.h" | |
29 #include "content/common/child_process_host_impl.h" | |
30 #include "content/common/plugin_process_messages.h" | |
31 #include "content/common/resource_messages.h" | |
11 #include "content/public/browser/browser_thread.h" | 32 #include "content/public/browser/browser_thread.h" |
12 #include "content/public/browser/resource_dispatcher_host_delegate.h" | 33 #include "content/public/browser/content_browser_client.h" |
34 #include "content/public/browser/notification_types.h" | |
35 #include "content/public/browser/plugin_service.h" | |
36 #include "content/public/browser/resource_context.h" | |
13 #include "content/public/common/content_switches.h" | 37 #include "content/public/common/content_switches.h" |
14 #include "content/public/test/browser_test_utils.h" | 38 #include "content/public/common/process_type.h" |
15 #include "content/public/test/content_browser_test.h" | 39 #include "content/public/common/sandboxed_process_launcher_delegate.h" |
16 #include "content/public/test/content_browser_test_utils.h" | 40 #include "ipc/ipc_switches.h" |
17 #include "content/public/test/test_utils.h" | 41 #include "net/url_request/url_request_context_getter.h" |
18 #include "content/shell/browser/shell.h" | 42 #include "ui/base/ui_base_switches.h" |
19 #include "content/shell/common/shell_switches.h" | 43 #include "ui/gfx/native_widget_types.h" |
20 #include "content/test/net/url_request_mock_http_job.h" | 44 #include "ui/gl/gl_switches.h" |
21 #include "net/test/embedded_test_server/embedded_test_server.h" | 45 |
22 #include "net/url_request/url_request.h" | 46 #if defined(OS_MACOSX) |
47 #include "base/mac/mac_util.h" | |
48 #include "content/common/plugin_carbon_interpose_constants_mac.h" | |
23 #include "ui/gfx/rect.h" | 49 #include "ui/gfx/rect.h" |
24 | 50 #endif |
25 #if defined(OS_WIN) | 51 |
26 #include "base/win/registry.h" | 52 #if defined(OS_WIN) |
27 #endif | 53 #include "base/win/windows_version.h" |
28 | 54 #include "content/common/plugin_constants_win.h" |
29 // TODO(jschuh): Finish plugins on Win64. crbug.com/180861 | 55 #include "ui/gfx/switches.h" |
30 #if defined(OS_WIN) && defined(ARCH_CPU_X86_64) | 56 #endif |
31 #define MAYBE(x) DISABLED_##x | 57 |
58 namespace content { | |
59 | |
60 #if defined(OS_WIN) | |
61 void PluginProcessHost::OnPluginWindowDestroyed(HWND window, HWND parent) { | |
62 // The window is destroyed at this point, we just care about its parent, which | |
63 // is the intermediate window we created. | |
64 std::set<HWND>::iterator window_index = | |
65 plugin_parent_windows_set_.find(parent); | |
66 if (window_index == plugin_parent_windows_set_.end()) | |
67 return; | |
68 | |
69 plugin_parent_windows_set_.erase(window_index); | |
70 PostMessage(parent, WM_CLOSE, 0, 0); | |
71 } | |
72 | |
73 void PluginProcessHost::AddWindow(HWND window) { | |
74 plugin_parent_windows_set_.insert(window); | |
75 } | |
76 #endif // defined(OS_WIN) | |
77 | |
78 // NOTE: changes to this class need to be reviewed by the security team. | |
79 class PluginSandboxedProcessLauncherDelegate | |
80 : public SandboxedProcessLauncherDelegate { | |
81 public: | |
82 explicit PluginSandboxedProcessLauncherDelegate(ChildProcessHost* host) | |
83 #if defined(OS_POSIX) | |
84 : ipc_fd_(host->TakeClientFileDescriptor()) | |
85 #endif // OS_POSIX | |
86 {} | |
87 | |
88 virtual ~PluginSandboxedProcessLauncherDelegate() {} | |
89 | |
90 #if defined(OS_WIN) | |
91 virtual bool ShouldSandbox() OVERRIDE { | |
92 return false; | |
93 } | |
94 | |
95 #elif defined(OS_POSIX) | |
96 virtual int GetIpcFd() OVERRIDE { | |
97 return ipc_fd_; | |
98 } | |
99 #endif // OS_WIN | |
100 | |
101 private: | |
102 #if defined(OS_POSIX) | |
103 int ipc_fd_; | |
104 #endif // OS_POSIX | |
105 | |
106 DISALLOW_COPY_AND_ASSIGN(PluginSandboxedProcessLauncherDelegate); | |
107 }; | |
108 | |
109 PluginProcessHost::PluginProcessHost() | |
110 #if defined(OS_MACOSX) | |
111 : plugin_cursor_visible_(true) | |
112 #endif | |
113 { | |
114 process_.reset(new BrowserChildProcessHostImpl(PROCESS_TYPE_PLUGIN, this)); | |
115 } | |
116 | |
117 PluginProcessHost::~PluginProcessHost() { | |
118 #if defined(OS_WIN) | |
119 // We erase HWNDs from the plugin_parent_windows_set_ when we receive a | |
120 // notification that the window is being destroyed. If we don't receive this | |
121 // notification and the PluginProcessHost instance is being destroyed, it | |
122 // means that the plugin process crashed. We paint a sad face in this case in | |
123 // the renderer process. To ensure that the sad face shows up, and we don't | |
124 // leak HWNDs, we should destroy existing plugin parent windows. | |
125 std::set<HWND>::iterator window_index; | |
126 for (window_index = plugin_parent_windows_set_.begin(); | |
127 window_index != plugin_parent_windows_set_.end(); | |
128 ++window_index) { | |
129 PostMessage(*window_index, WM_CLOSE, 0, 0); | |
130 } | |
131 #elif defined(OS_MACOSX) | |
132 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
133 // If the plugin process crashed but had fullscreen windows open at the time, | |
134 // make sure that the menu bar is visible. | |
135 for (size_t i = 0; i < plugin_fullscreen_windows_set_.size(); ++i) { | |
136 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
137 base::Bind(base::mac::ReleaseFullScreen, | |
138 base::mac::kFullScreenModeHideAll)); | |
139 } | |
140 // If the plugin hid the cursor, reset that. | |
141 if (!plugin_cursor_visible_) { | |
142 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
143 base::Bind(base::mac::SetCursorVisibility, true)); | |
144 } | |
145 #endif | |
146 // Cancel all pending and sent requests. | |
147 CancelRequests(); | |
148 } | |
149 | |
150 bool PluginProcessHost::Send(IPC::Message* message) { | |
151 return process_->Send(message); | |
152 } | |
153 | |
154 bool PluginProcessHost::Init(const WebPluginInfo& info) { | |
155 info_ = info; | |
156 process_->SetName(info_.name); | |
157 | |
158 std::string channel_id = process_->GetHost()->CreateChannel(); | |
159 if (channel_id.empty()) | |
160 return false; | |
161 | |
162 // Build command line for plugin. When we have a plugin launcher, we can't | |
163 // allow "self" on linux and we need the real file path. | |
164 const CommandLine& browser_command_line = *CommandLine::ForCurrentProcess(); | |
165 CommandLine::StringType plugin_launcher = | |
166 browser_command_line.GetSwitchValueNative(switches::kPluginLauncher); | |
167 | |
168 #if defined(OS_MACOSX) | |
169 // Run the plug-in process in a mode tolerant of heap execution without | |
170 // explicit mprotect calls. Some plug-ins still rely on this quaint and | |
171 // archaic "feature." See http://crbug.com/93551. | |
172 int flags = ChildProcessHost::CHILD_ALLOW_HEAP_EXECUTION; | |
173 #elif defined(OS_LINUX) | |
174 int flags = plugin_launcher.empty() ? ChildProcessHost::CHILD_ALLOW_SELF : | |
175 ChildProcessHost::CHILD_NORMAL; | |
32 #else | 176 #else |
33 #define MAYBE(x) x | 177 int flags = ChildProcessHost::CHILD_NORMAL; |
34 #endif | 178 #endif |
35 | 179 |
36 using base::ASCIIToUTF16; | 180 base::FilePath exe_path = ChildProcessHost::GetChildPath(flags); |
37 | 181 if (exe_path.empty()) |
38 namespace content { | 182 return false; |
39 namespace { | 183 |
40 | 184 CommandLine* cmd_line = new CommandLine(exe_path); |
41 void SetUrlRequestMock(const base::FilePath& path) { | 185 // Put the process type and plugin path first so they're easier to see |
42 URLRequestMockHTTPJob::AddUrlHandler(path); | 186 // in process listings using native process management tools. |
43 } | 187 cmd_line->AppendSwitchASCII(switches::kProcessType, switches::kPluginProcess); |
44 | 188 cmd_line->AppendSwitchPath(switches::kPluginPath, info.path); |
45 } | 189 |
46 | 190 // Propagate the following switches to the plugin command line (along with |
47 class PluginTest : public ContentBrowserTest { | 191 // any associated values) if present in the browser command line |
48 protected: | 192 static const char* const kSwitchNames[] = { |
49 PluginTest() {} | 193 switches::kDisableBreakpad, |
50 | 194 switches::kDisableDirectNPAPIRequests, |
51 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { | 195 switches::kEnableStatsTable, |
52 // Some NPAPI tests schedule garbage collection to force object tear-down. | 196 switches::kFullMemoryCrashReport, |
53 command_line->AppendSwitchASCII(switches::kJavaScriptFlags, "--expose_gc"); | 197 switches::kLoggingLevel, |
54 | 198 switches::kLogPluginMessages, |
55 #if defined(OS_WIN) | 199 switches::kNoSandbox, |
56 const testing::TestInfo* const test_info = | 200 switches::kPluginStartupDialog, |
57 testing::UnitTest::GetInstance()->current_test_info(); | 201 switches::kTestSandbox, |
58 if (strcmp(test_info->name(), "MediaPlayerNew") == 0) { | 202 switches::kTraceStartup, |
59 // The installer adds our process names to the registry key below. Since | 203 switches::kUseGL, |
60 // the installer might not have run on this machine, add it manually. | 204 #if defined(OS_MACOSX) |
61 base::win::RegKey regkey; | 205 switches::kDisableCoreAnimationPlugins, |
62 if (regkey.Open(HKEY_LOCAL_MACHINE, | 206 switches::kEnableSandboxLogging, |
63 L"Software\\Microsoft\\MediaPlayer\\ShimInclusionList", | 207 #endif |
64 KEY_WRITE) == ERROR_SUCCESS) { | 208 }; |
65 regkey.CreateKey(L"BROWSER_TESTS.EXE", KEY_READ); | 209 |
210 cmd_line->CopySwitchesFrom(browser_command_line, kSwitchNames, | |
211 arraysize(kSwitchNames)); | |
212 | |
213 GpuDataManagerImpl::GetInstance()->AppendPluginCommandLine(cmd_line); | |
214 | |
215 // If specified, prepend a launcher program to the command line. | |
216 if (!plugin_launcher.empty()) | |
217 cmd_line->PrependWrapper(plugin_launcher); | |
218 | |
219 std::string locale = GetContentClient()->browser()->GetApplicationLocale(); | |
220 if (!locale.empty()) { | |
221 // Pass on the locale so the null plugin will use the right language in the | |
222 // prompt to install the desired plugin. | |
223 cmd_line->AppendSwitchASCII(switches::kLang, locale); | |
224 } | |
225 | |
226 cmd_line->AppendSwitchASCII(switches::kProcessChannelID, channel_id); | |
227 | |
228 #if defined(OS_POSIX) | |
229 base::EnvironmentMap env; | |
230 #if defined(OS_MACOSX) && !defined(__LP64__) | |
231 if (browser_command_line.HasSwitch(switches::kEnableCarbonInterposing)) { | |
232 std::string interpose_list = GetContentClient()->GetCarbonInterposePath(); | |
233 if (!interpose_list.empty()) { | |
234 // Add our interposing library for Carbon. This is stripped back out in | |
235 // plugin_main.cc, so changes here should be reflected there. | |
236 const char* existing_list = getenv(kDYLDInsertLibrariesKey); | |
237 if (existing_list) { | |
238 interpose_list.insert(0, ":"); | |
239 interpose_list.insert(0, existing_list); | |
66 } | 240 } |
67 } else if (strcmp(test_info->name(), "FlashSecurity") == 0) { | |
68 command_line->AppendSwitchASCII(switches::kTestSandbox, | |
69 "security_tests.dll"); | |
70 } | 241 } |
71 #elif defined(OS_MACOSX) | 242 env[kDYLDInsertLibrariesKey] = interpose_list; |
72 base::FilePath plugin_dir; | 243 } |
73 PathService::Get(base::DIR_MODULE, &plugin_dir); | 244 #endif |
74 plugin_dir = plugin_dir.AppendASCII("plugins"); | 245 #endif |
75 // The plugins directory isn't read by default on the Mac, so it needs to be | 246 |
76 // explicitly registered. | 247 process_->Launch( |
77 command_line->AppendSwitchPath(switches::kExtraPluginDir, plugin_dir); | 248 new PluginSandboxedProcessLauncherDelegate(process_->GetHost()), |
78 #endif | 249 cmd_line); |
79 } | 250 |
80 | 251 // The plugin needs to be shutdown gracefully, i.e. NP_Shutdown needs to be |
81 virtual void SetUpOnMainThread() OVERRIDE { | 252 // called on the plugin. The plugin process exits when it receives the |
82 base::FilePath path = GetTestFilePath("", ""); | 253 // OnChannelError notification indicating that the browser plugin channel has |
83 BrowserThread::PostTask( | 254 // been destroyed. |
84 BrowserThread::IO, FROM_HERE, base::Bind(&SetUrlRequestMock, path)); | 255 process_->SetTerminateChildOnShutdown(false); |
85 } | 256 |
86 | 257 ResourceMessageFilter::GetContextsCallback get_contexts_callback( |
87 static void LoadAndWaitInWindow(Shell* window, const GURL& url) { | 258 base::Bind(&PluginProcessHost::GetContexts, |
88 base::string16 expected_title(ASCIIToUTF16("OK")); | 259 base::Unretained(this))); |
89 TitleWatcher title_watcher(window->web_contents(), expected_title); | 260 |
90 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); | 261 // TODO(jam): right now we're passing NULL for appcache, blob storage, and |
91 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("plugin_not_found")); | 262 // file system. If NPAPI plugins actually use this, we'll have to plumb them. |
92 NavigateToURL(window, url); | 263 ResourceMessageFilter* resource_message_filter = new ResourceMessageFilter( |
93 base::string16 title = title_watcher.WaitAndGetTitle(); | 264 process_->GetData().id, PROCESS_TYPE_PLUGIN, NULL, NULL, NULL, NULL, |
94 if (title == ASCIIToUTF16("plugin_not_found")) { | 265 get_contexts_callback); |
95 const testing::TestInfo* const test_info = | 266 process_->AddFilter(resource_message_filter); |
96 testing::UnitTest::GetInstance()->current_test_info(); | 267 return true; |
97 VLOG(0) << "PluginTest." << test_info->name() | 268 } |
98 << " not running because plugin not installed."; | 269 |
99 } else { | 270 void PluginProcessHost::ForceShutdown() { |
100 EXPECT_EQ(expected_title, title); | 271 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); |
101 } | 272 Send(new PluginProcessMsg_NotifyRenderersOfPendingShutdown()); |
102 } | 273 process_->ForceShutdown(); |
103 | 274 } |
104 void LoadAndWait(const GURL& url) { | 275 |
105 LoadAndWaitInWindow(shell(), url); | 276 bool PluginProcessHost::OnMessageReceived(const IPC::Message& msg) { |
106 } | 277 bool handled = true; |
107 | 278 IPC_BEGIN_MESSAGE_MAP(PluginProcessHost, msg) |
108 GURL GetURL(const char* filename) { | 279 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelCreated, OnChannelCreated) |
109 return GetTestUrl("npapi", filename); | 280 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_ChannelDestroyed, |
110 } | 281 OnChannelDestroyed) |
111 | 282 #if defined(OS_WIN) |
112 void NavigateAway() { | 283 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginWindowDestroyed, |
113 GURL url = GetTestUrl("", "simple_page.html"); | 284 OnPluginWindowDestroyed) |
114 LoadAndWait(url); | 285 #endif |
115 } | 286 #if defined(OS_MACOSX) |
116 | 287 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSelectWindow, |
117 void TestPlugin(const char* filename) { | 288 OnPluginSelectWindow) |
118 base::FilePath path = GetTestFilePath("plugin", filename); | 289 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginShowWindow, |
119 if (!base::PathExists(path)) { | 290 OnPluginShowWindow) |
120 const testing::TestInfo* const test_info = | 291 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginHideWindow, |
121 testing::UnitTest::GetInstance()->current_test_info(); | 292 OnPluginHideWindow) |
122 VLOG(0) << "PluginTest." << test_info->name() | 293 IPC_MESSAGE_HANDLER(PluginProcessHostMsg_PluginSetCursorVisibility, |
123 << " not running because test data wasn't found."; | 294 OnPluginSetCursorVisibility) |
295 #endif | |
296 IPC_MESSAGE_UNHANDLED(handled = false) | |
297 IPC_END_MESSAGE_MAP() | |
298 | |
299 return handled; | |
300 } | |
301 | |
302 void PluginProcessHost::OnChannelConnected(int32 peer_pid) { | |
303 for (size_t i = 0; i < pending_requests_.size(); ++i) { | |
304 RequestPluginChannel(pending_requests_[i]); | |
305 } | |
306 | |
307 pending_requests_.clear(); | |
308 } | |
309 | |
310 void PluginProcessHost::OnChannelError() { | |
311 CancelRequests(); | |
312 } | |
313 | |
314 bool PluginProcessHost::CanShutdown() { | |
315 return sent_requests_.empty(); | |
316 } | |
317 | |
318 void PluginProcessHost::OnProcessCrashed(int exit_code) { | |
319 PluginServiceImpl::GetInstance()->RegisterPluginCrash(info_.path); | |
320 } | |
321 | |
322 void PluginProcessHost::CancelRequests() { | |
323 for (size_t i = 0; i < pending_requests_.size(); ++i) | |
324 pending_requests_[i]->OnError(); | |
325 pending_requests_.clear(); | |
326 | |
327 while (!sent_requests_.empty()) { | |
328 Client* client = sent_requests_.front(); | |
329 if (client) | |
330 client->OnError(); | |
331 sent_requests_.pop_front(); | |
332 } | |
333 } | |
334 | |
335 void PluginProcessHost::OpenChannelToPlugin(Client* client) { | |
336 BrowserThread::PostTask( | |
337 BrowserThread::UI, FROM_HERE, | |
338 base::Bind(&BrowserChildProcessHostImpl::NotifyProcessInstanceCreated, | |
339 process_->GetData())); | |
340 client->SetPluginInfo(info_); | |
341 if (process_->GetHost()->IsChannelOpening()) { | |
342 // The channel is already in the process of being opened. Put | |
343 // this "open channel" request into a queue of requests that will | |
344 // be run once the channel is open. | |
345 pending_requests_.push_back(client); | |
346 return; | |
347 } | |
348 | |
349 // We already have an open channel, send a request right away to plugin. | |
350 RequestPluginChannel(client); | |
351 } | |
352 | |
353 void PluginProcessHost::CancelPendingRequest(Client* client) { | |
354 std::vector<Client*>::iterator it = pending_requests_.begin(); | |
355 while (it != pending_requests_.end()) { | |
356 if (client == *it) { | |
357 pending_requests_.erase(it); | |
124 return; | 358 return; |
125 } | 359 } |
126 | 360 ++it; |
127 GURL url = GetTestUrl("plugin", filename); | 361 } |
128 LoadAndWait(url); | 362 DCHECK(it != pending_requests_.end()); |
129 } | 363 } |
130 }; | 364 |
131 | 365 void PluginProcessHost::CancelSentRequest(Client* client) { |
132 // Make sure that navigating away from a plugin referenced by JS doesn't | 366 std::list<Client*>::iterator it = sent_requests_.begin(); |
133 // crash. | 367 while (it != sent_requests_.end()) { |
134 IN_PROC_BROWSER_TEST_F(PluginTest, UnloadNoCrash) { | 368 if (client == *it) { |
135 LoadAndWait(GetURL("layout_test_plugin.html")); | 369 *it = NULL; |
136 NavigateAway(); | |
137 } | |
138 | |
139 // Tests if a plugin executing a self deleting script using NPN_GetURL | |
140 // works without crashing or hanging | |
141 // Flaky: http://crbug.com/59327 | |
142 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginGetUrl)) { | |
143 LoadAndWait(GetURL("self_delete_plugin_geturl.html")); | |
144 } | |
145 | |
146 // Tests if a plugin executing a self deleting script using Invoke | |
147 // works without crashing or hanging | |
148 // Flaky. See http://crbug.com/30702 | |
149 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInvoke)) { | |
150 LoadAndWait(GetURL("self_delete_plugin_invoke.html")); | |
151 } | |
152 | |
153 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectReleasedOnDestruction)) { | |
154 NavigateToURL(shell(), GetURL("npobject_released_on_destruction.html")); | |
155 NavigateAway(); | |
156 } | |
157 | |
158 // Test that a dialog is properly created when a plugin throws an | |
159 // exception. Should be run for in and out of process plugins, but | |
160 // the more interesting case is out of process, where we must route | |
161 // the exception to the correct renderer. | |
162 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectSetException)) { | |
163 LoadAndWait(GetURL("npobject_set_exception.html")); | |
164 } | |
165 | |
166 #if defined(OS_WIN) | |
167 // Tests if a plugin executing a self deleting script in the context of | |
168 // a synchronous mouseup works correctly. | |
169 // This was never ported to Mac. The only thing remaining is to make | |
170 // SimulateMouseClick get to Mac plugins, currently it doesn't work. | |
171 IN_PROC_BROWSER_TEST_F(PluginTest, | |
172 MAYBE(SelfDeletePluginInvokeInSynchronousMouseUp)) { | |
173 NavigateToURL(shell(), GetURL("execute_script_delete_in_mouse_up.html")); | |
174 | |
175 base::string16 expected_title(ASCIIToUTF16("OK")); | |
176 TitleWatcher title_watcher(shell()->web_contents(), expected_title); | |
177 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); | |
178 SimulateMouseClick(shell()->web_contents(), 0, | |
179 blink::WebMouseEvent::ButtonLeft); | |
180 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); | |
181 } | |
182 #endif | |
183 | |
184 // Flaky, http://crbug.com/302274. | |
185 #if defined(OS_MACOSX) | |
186 #define MAYBE_GetURLRequest404Response DISABLED_GetURLRequest404Response | |
187 #else | |
188 #define MAYBE_GetURLRequest404Response MAYBE(GetURLRequest404Response) | |
189 #endif | |
190 | |
191 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE_GetURLRequest404Response) { | |
192 GURL url(URLRequestMockHTTPJob::GetMockUrl( | |
193 base::FilePath().AppendASCII("npapi"). | |
194 AppendASCII("plugin_url_request_404.html"))); | |
195 LoadAndWait(url); | |
196 } | |
197 | |
198 // Tests if a plugin executing a self deleting script using Invoke with | |
199 // a modal dialog showing works without crashing or hanging | |
200 // Disabled, flakily exceeds timeout, http://crbug.com/46257. | |
201 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInvokeAlert)) { | |
202 // Navigate asynchronously because if we waitd until it completes, there's a | |
203 // race condition where the alert can come up before we start watching for it. | |
204 shell()->LoadURL(GetURL("self_delete_plugin_invoke_alert.html")); | |
205 | |
206 base::string16 expected_title(ASCIIToUTF16("OK")); | |
207 TitleWatcher title_watcher(shell()->web_contents(), expected_title); | |
208 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); | |
209 | |
210 WaitForAppModalDialog(shell()); | |
211 | |
212 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); | |
213 } | |
214 | |
215 // Test passing arguments to a plugin. | |
216 // crbug.com/306318 | |
217 #if !defined(OS_LINUX) | |
218 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Arguments)) { | |
219 LoadAndWait(GetURL("arguments.html")); | |
220 } | |
221 #endif | |
222 | |
223 // Test invoking many plugins within a single page. | |
224 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ManyPlugins)) { | |
225 LoadAndWait(GetURL("many_plugins.html")); | |
226 } | |
227 | |
228 // Test various calls to GetURL from a plugin. | |
229 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetURL)) { | |
230 LoadAndWait(GetURL("geturl.html")); | |
231 } | |
232 | |
233 // Test various calls to GetURL for javascript URLs with | |
234 // non NULL targets from a plugin. | |
235 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetJavaScriptURL)) { | |
236 LoadAndWait(GetURL("get_javascript_url.html")); | |
237 } | |
238 | |
239 // Test that calling GetURL with a javascript URL and target=_self | |
240 // works properly when the plugin is embedded in a subframe. | |
241 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetJavaScriptURL2)) { | |
242 LoadAndWait(GetURL("get_javascript_url2.html")); | |
243 } | |
244 | |
245 // Test is flaky on linux/cros/win builders. http://crbug.com/71904 | |
246 IN_PROC_BROWSER_TEST_F(PluginTest, GetURLRedirectNotification) { | |
247 LoadAndWait(GetURL("geturl_redirect_notify.html")); | |
248 } | |
249 | |
250 // Tests that identity is preserved for NPObjects passed from a plugin | |
251 // into JavaScript. | |
252 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectIdentity)) { | |
253 LoadAndWait(GetURL("npobject_identity.html")); | |
254 } | |
255 | |
256 // Tests that if an NPObject is proxies back to its original process, the | |
257 // original pointer is returned and not a proxy. If this fails the plugin | |
258 // will crash. | |
259 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NPObjectProxy)) { | |
260 LoadAndWait(GetURL("npobject_proxy.html")); | |
261 } | |
262 | |
263 #if defined(OS_WIN) || defined(OS_MACOSX) | |
264 // Tests if a plugin executing a self deleting script in the context of | |
265 // a synchronous paint event works correctly | |
266 // http://crbug.com/44960 | |
267 IN_PROC_BROWSER_TEST_F(PluginTest, | |
268 MAYBE(SelfDeletePluginInvokeInSynchronousPaint)) { | |
269 LoadAndWait(GetURL("execute_script_delete_in_paint.html")); | |
270 } | |
271 #endif | |
272 | |
273 // Tests that if a plugin executes a self resizing script in the context of a | |
274 // synchronous paint, the plugin doesn't use deallocated memory. | |
275 // http://crbug.com/139462 | |
276 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ResizeDuringPaint)) { | |
277 LoadAndWait(GetURL("resize_during_paint.html")); | |
278 } | |
279 | |
280 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInNewStream)) { | |
281 LoadAndWait(GetURL("self_delete_plugin_stream.html")); | |
282 } | |
283 | |
284 // On Mac this test asserts in plugin_host: http://crbug.com/95558 | |
285 // On all platforms it flakes in ~URLRequestContext: http://crbug.com/310336 | |
286 #if !defined(NDEBUG) | |
287 IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_DeletePluginInDeallocate) { | |
288 LoadAndWait(GetURL("plugin_delete_in_deallocate.html")); | |
289 } | |
290 #endif | |
291 | |
292 #if defined(OS_WIN) | |
293 | |
294 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(VerifyPluginWindowRect)) { | |
295 LoadAndWait(GetURL("verify_plugin_window_rect.html")); | |
296 } | |
297 | |
298 // Tests that creating a new instance of a plugin while another one is handling | |
299 // a paint message doesn't cause deadlock. | |
300 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(CreateInstanceInPaint)) { | |
301 LoadAndWait(GetURL("create_instance_in_paint.html")); | |
302 } | |
303 | |
304 // Tests that putting up an alert in response to a paint doesn't deadlock. | |
305 IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_AlertInWindowMessage) { | |
306 NavigateToURL(shell(), GetURL("alert_in_window_message.html")); | |
307 | |
308 WaitForAppModalDialog(shell()); | |
309 WaitForAppModalDialog(shell()); | |
310 } | |
311 | |
312 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(VerifyNPObjectLifetimeTest)) { | |
313 LoadAndWait(GetURL("npobject_lifetime_test.html")); | |
314 } | |
315 | |
316 // Tests that we don't crash or assert if NPP_New fails | |
317 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NewFails)) { | |
318 LoadAndWait(GetURL("new_fails.html")); | |
319 } | |
320 | |
321 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(SelfDeletePluginInNPNEvaluate)) { | |
322 LoadAndWait(GetURL("execute_script_delete_in_npn_evaluate.html")); | |
323 } | |
324 | |
325 IN_PROC_BROWSER_TEST_F(PluginTest, | |
326 MAYBE(SelfDeleteCreatePluginInNPNEvaluate)) { | |
327 LoadAndWait(GetURL("npn_plugin_delete_create_in_evaluate.html")); | |
328 } | |
329 | |
330 #endif // OS_WIN | |
331 | |
332 // If this flakes, reopen http://crbug.com/17645 | |
333 // As of 6 July 2011, this test is flaky on Windows (perhaps due to timing out). | |
334 #if !defined(OS_MACOSX) && !defined(OS_LINUX) | |
335 // Disabled on Mac because the plugin side isn't implemented yet, see | |
336 // "TODO(port)" in plugin_javascript_open_popup.cc. | |
337 // Disabled on Linux because we don't support NPAPI any more. | |
338 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(OpenPopupWindowWithPlugin)) { | |
339 LoadAndWait(GetURL("get_javascript_open_popup_with_plugin.html")); | |
340 } | |
341 #endif | |
342 | |
343 // Test checking the privacy mode is off. | |
344 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PrivateDisabled)) { | |
345 LoadAndWait(GetURL("private.html")); | |
346 } | |
347 | |
348 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(ScheduleTimer)) { | |
349 LoadAndWait(GetURL("schedule_timer.html")); | |
350 } | |
351 | |
352 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PluginThreadAsyncCall)) { | |
353 LoadAndWait(GetURL("plugin_thread_async_call.html")); | |
354 } | |
355 | |
356 IN_PROC_BROWSER_TEST_F(PluginTest, PluginSingleRangeRequest) { | |
357 LoadAndWait(GetURL("plugin_single_range_request.html")); | |
358 } | |
359 | |
360 // Test checking the privacy mode is on. | |
361 // If this flakes on Linux, use http://crbug.com/104380 | |
362 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PrivateEnabled)) { | |
363 GURL url = GetURL("private.html"); | |
364 url = GURL(url.spec() + "?private"); | |
365 LoadAndWaitInWindow(CreateOffTheRecordBrowser(), url); | |
366 } | |
367 | |
368 #if defined(OS_WIN) || defined(OS_MACOSX) | |
369 // Test a browser hang due to special case of multiple | |
370 // plugin instances indulged in sync calls across renderer. | |
371 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(MultipleInstancesSyncCalls)) { | |
372 LoadAndWait(GetURL("multiple_instances_sync_calls.html")); | |
373 } | |
374 #endif | |
375 | |
376 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(GetURLRequestFailWrite)) { | |
377 GURL url(URLRequestMockHTTPJob::GetMockUrl( | |
378 base::FilePath().AppendASCII("npapi"). | |
379 AppendASCII("plugin_url_request_fail_write.html"))); | |
380 LoadAndWait(url); | |
381 } | |
382 | |
383 #if defined(OS_WIN) | |
384 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(EnsureScriptingWorksInDestroy)) { | |
385 LoadAndWait(GetURL("ensure_scripting_works_in_destroy.html")); | |
386 } | |
387 | |
388 // This test uses a Windows Event to signal to the plugin that it should crash | |
389 // on NP_Initialize. | |
390 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(NoHangIfInitCrashes)) { | |
391 HANDLE crash_event = CreateEvent(NULL, TRUE, FALSE, L"TestPluginCrashOnInit"); | |
392 SetEvent(crash_event); | |
393 LoadAndWait(GetURL("no_hang_if_init_crashes.html")); | |
394 CloseHandle(crash_event); | |
395 } | |
396 #endif | |
397 | |
398 // If this flakes on Mac, use http://crbug.com/111508 | |
399 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(PluginReferrerTest)) { | |
400 GURL url(URLRequestMockHTTPJob::GetMockUrl( | |
401 base::FilePath().AppendASCII("npapi"). | |
402 AppendASCII("plugin_url_request_referrer_test.html"))); | |
403 LoadAndWait(url); | |
404 } | |
405 | |
406 #if defined(OS_MACOSX) | |
407 // Test is flaky, see http://crbug.com/134515. | |
408 IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_PluginConvertPointTest) { | |
409 gfx::Rect bounds(50, 50, 400, 400); | |
410 SetWindowBounds(shell()->window(), bounds); | |
411 | |
412 NavigateToURL(shell(), GetURL("convert_point.html")); | |
413 | |
414 base::string16 expected_title(ASCIIToUTF16("OK")); | |
415 TitleWatcher title_watcher(shell()->web_contents(), expected_title); | |
416 title_watcher.AlsoWaitForTitle(ASCIIToUTF16("FAIL")); | |
417 // TODO(stuartmorgan): When the automation system supports sending clicks, | |
418 // change the test to trigger on mouse-down rather than window focus. | |
419 | |
420 // TODO: is this code still needed? It was here when it used to run in | |
421 // browser_tests. | |
422 //static_cast<WebContentsDelegate*>(shell())-> | |
423 // ActivateContents(shell()->web_contents()); | |
424 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle()); | |
425 } | |
426 #endif | |
427 | |
428 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Flash)) { | |
429 TestPlugin("flash.html"); | |
430 } | |
431 | |
432 #if defined(OS_WIN) | |
433 // Windows only test | |
434 IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_FlashSecurity) { | |
435 TestPlugin("flash.html"); | |
436 } | |
437 #endif // defined(OS_WIN) | |
438 | |
439 #if defined(OS_WIN) | |
440 // TODO(port) Port the following tests to platforms that have the required | |
441 // plugins. | |
442 // Flaky: http://crbug.com/55915 | |
443 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Quicktime)) { | |
444 TestPlugin("quicktime.html"); | |
445 } | |
446 | |
447 // Disabled - http://crbug.com/44662 | |
448 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(MediaPlayerNew)) { | |
449 TestPlugin("wmp_new.html"); | |
450 } | |
451 | |
452 // Disabled - http://crbug.com/44673 | |
453 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Real)) { | |
454 TestPlugin("real.html"); | |
455 } | |
456 | |
457 // http://crbug.com/320041 | |
458 #if (defined(OS_WIN) && defined(ARCH_CPU_X86_64)) || \ | |
459 (defined(GOOGLE_CHROME_BUILD) && defined(OS_WIN)) | |
460 #define MAYBE_FlashOctetStream DISABLED_FlashOctetStream | |
461 #else | |
462 #define MAYBE_FlashOctetStream FlashOctetStream | |
463 #endif | |
464 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE_FlashOctetStream) { | |
465 TestPlugin("flash-octet-stream.html"); | |
466 } | |
467 | |
468 #if defined(OS_WIN) | |
469 // http://crbug.com/53926 | |
470 IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_FlashLayoutWhilePainting) { | |
471 #else | |
472 IN_PROC_BROWSER_TEST_F(PluginTest, FlashLayoutWhilePainting) { | |
473 #endif | |
474 TestPlugin("flash-layout-while-painting.html"); | |
475 } | |
476 | |
477 // http://crbug.com/8690 | |
478 IN_PROC_BROWSER_TEST_F(PluginTest, DISABLED_Java) { | |
479 TestPlugin("Java.html"); | |
480 } | |
481 | |
482 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Silverlight)) { | |
483 TestPlugin("silverlight.html"); | |
484 } | |
485 #endif // defined(OS_WIN) | |
486 | |
487 class TestResourceDispatcherHostDelegate | |
488 : public ResourceDispatcherHostDelegate { | |
489 public: | |
490 TestResourceDispatcherHostDelegate() : found_cookie_(false) {} | |
491 | |
492 bool found_cookie() { return found_cookie_; } | |
493 | |
494 void WaitForPluginRequest() { | |
495 if (found_cookie_) | |
496 return; | |
497 | |
498 runner_ = new MessageLoopRunner; | |
499 runner_->Run(); | |
500 } | |
501 | |
502 private: | |
503 // ResourceDispatcherHostDelegate implementation: | |
504 virtual void OnResponseStarted( | |
505 net::URLRequest* request, | |
506 ResourceContext* resource_context, | |
507 ResourceResponse* response, | |
508 IPC::Sender* sender) OVERRIDE { | |
509 // The URL below comes from plugin_geturl_test.cc. | |
510 if (!EndsWith(request->url().spec(), | |
511 "npapi/plugin_ref_target_page.html", | |
512 true)) { | |
513 return; | 370 return; |
514 } | 371 } |
515 net::HttpRequestHeaders headers; | 372 ++it; |
516 bool found_cookie = false; | 373 } |
517 if (request->GetFullRequestHeaders(&headers) && | 374 DCHECK(it != sent_requests_.end()); |
518 headers.ToString().find("Cookie: blah") != std::string::npos) { | 375 } |
519 found_cookie = true; | 376 |
377 void PluginProcessHost::RequestPluginChannel(Client* client) { | |
378 // We can't send any sync messages from the browser because it might lead to | |
379 // a hang. However this async messages must be answered right away by the | |
380 // plugin process (i.e. unblocks a Send() call like a sync message) otherwise | |
381 // a deadlock can occur if the plugin creation request from the renderer is | |
382 // a result of a sync message by the plugin process. | |
383 PluginProcessMsg_CreateChannel* msg = | |
384 new PluginProcessMsg_CreateChannel( | |
385 client->ID(), | |
386 client->OffTheRecord()); | |
387 msg->set_unblock(true); | |
388 if (Send(msg)) { | |
389 sent_requests_.push_back(client); | |
390 client->OnSentPluginChannelRequest(); | |
391 } else { | |
392 client->OnError(); | |
393 } | |
394 } | |
395 | |
396 void PluginProcessHost::OnChannelCreated( | |
397 const IPC::ChannelHandle& channel_handle) { | |
398 Client* client = sent_requests_.front(); | |
399 | |
400 if (client) { | |
401 if (!resource_context_map_.count(client->ID())) { | |
402 ResourceContextEntry entry; | |
403 entry.ref_count = 0; | |
404 entry.resource_context = client->GetResourceContext(); | |
405 resource_context_map_[client->ID()] = entry; | |
520 } | 406 } |
521 BrowserThread::PostTask( | 407 resource_context_map_[client->ID()].ref_count++; |
522 BrowserThread::UI, | 408 client->OnChannelOpened(channel_handle); |
523 FROM_HERE, | 409 } |
524 base::Bind(&TestResourceDispatcherHostDelegate::GotCookie, | 410 sent_requests_.pop_front(); |
525 base::Unretained(this), found_cookie)); | 411 } |
526 } | 412 |
527 | 413 void PluginProcessHost::OnChannelDestroyed(int renderer_id) { |
528 void GotCookie(bool found_cookie) { | 414 resource_context_map_[renderer_id].ref_count--; |
529 found_cookie_ = found_cookie; | 415 if (!resource_context_map_[renderer_id].ref_count) |
530 if (runner_) | 416 resource_context_map_.erase(renderer_id); |
531 runner_->QuitClosure().Run(); | 417 } |
532 } | 418 |
533 | 419 void PluginProcessHost::GetContexts(const ResourceHostMsg_Request& request, |
534 scoped_refptr<MessageLoopRunner> runner_; | 420 ResourceContext** resource_context, |
535 bool found_cookie_; | 421 net::URLRequestContext** request_context) { |
536 | 422 *resource_context = |
537 DISALLOW_COPY_AND_ASSIGN(TestResourceDispatcherHostDelegate); | 423 resource_context_map_[request.origin_pid].resource_context; |
538 }; | 424 *request_context = (*resource_context)->GetRequestContext(); |
539 | |
540 // Ensure that cookies get sent with plugin requests. | |
541 IN_PROC_BROWSER_TEST_F(PluginTest, MAYBE(Cookies)) { | |
542 // Create a new browser just to ensure that the plugin process' child_id is | |
543 // not equal to its type (PROCESS_TYPE_PLUGIN), as that was the error which | |
544 // caused this bug. | |
545 NavigateToURL(CreateBrowser(), GURL("about:blank")); | |
546 | |
547 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); | |
548 GURL url(embedded_test_server()->GetURL("/npapi/cookies.html")); | |
549 | |
550 TestResourceDispatcherHostDelegate test_delegate; | |
551 ResourceDispatcherHostDelegate* old_delegate = | |
552 ResourceDispatcherHostImpl::Get()->delegate(); | |
553 ResourceDispatcherHostImpl::Get()->SetDelegate(&test_delegate); | |
554 LoadAndWait(url); | |
555 test_delegate.WaitForPluginRequest(); | |
556 ASSERT_TRUE(test_delegate.found_cookie()); | |
557 ResourceDispatcherHostImpl::Get()->SetDelegate(old_delegate); | |
558 } | 425 } |
559 | 426 |
560 } // namespace content | 427 } // namespace content |
OLD | NEW |