| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "content/browser/plugin_service.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/command_line.h" | |
| 9 #include "base/path_service.h" | |
| 10 #include "chrome/browser/ui/browser.h" | |
| 11 #include "chrome/test/base/in_process_browser_test.h" | |
| 12 #include "chrome/test/base/testing_profile.h" | |
| 13 #include "chrome/test/base/ui_test_utils.h" | |
| 14 #include "content/browser/resource_context.h" | |
| 15 #include "content/public/common/content_switches.h" | |
| 16 #include "content/test/test_browser_thread.h" | |
| 17 #include "testing/gmock/include/gmock/gmock.h" | |
| 18 #include "webkit/plugins/npapi/plugin_list.h" | |
| 19 | |
| 20 using content::BrowserThread; | |
| 21 | |
| 22 namespace { | |
| 23 | |
| 24 const char kNPAPITestPluginMimeType[] = "application/vnd.npapi-test"; | |
| 25 | |
| 26 void OpenChannel(PluginProcessHost::Client* client) { | |
| 27 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 28 // Start opening the channel | |
| 29 PluginService::GetInstance()->OpenChannelToNpapiPlugin( | |
| 30 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client); | |
| 31 } | |
| 32 | |
| 33 // Mock up of the Client and the Listener classes that would supply the | |
| 34 // communication channel with the plugin. | |
| 35 class MockPluginProcessHostClient : public PluginProcessHost::Client, | |
| 36 public IPC::Channel::Listener { | |
| 37 public: | |
| 38 MockPluginProcessHostClient(const content::ResourceContext& context) | |
| 39 : context_(context), | |
| 40 channel_(NULL), | |
| 41 set_plugin_info_called_(false) { | |
| 42 } | |
| 43 | |
| 44 virtual ~MockPluginProcessHostClient() { | |
| 45 if (channel_) | |
| 46 BrowserThread::DeleteSoon(BrowserThread::IO, FROM_HERE, channel_); | |
| 47 } | |
| 48 | |
| 49 // Client implementation. | |
| 50 virtual int ID() OVERRIDE { return 42; } | |
| 51 virtual bool OffTheRecord() OVERRIDE { return false; } | |
| 52 virtual const content::ResourceContext& GetResourceContext() OVERRIDE { | |
| 53 return context_; | |
| 54 } | |
| 55 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE {} | |
| 56 virtual void OnSentPluginChannelRequest() OVERRIDE {} | |
| 57 | |
| 58 virtual void OnChannelOpened(const IPC::ChannelHandle& handle) OVERRIDE { | |
| 59 ASSERT_TRUE(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 60 ASSERT_TRUE(set_plugin_info_called_); | |
| 61 ASSERT_TRUE(!channel_); | |
| 62 channel_ = new IPC::Channel(handle, IPC::Channel::MODE_CLIENT, this); | |
| 63 ASSERT_TRUE(channel_->Connect()); | |
| 64 } | |
| 65 | |
| 66 void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE { | |
| 67 ASSERT_TRUE(info.mime_types.size()); | |
| 68 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type); | |
| 69 set_plugin_info_called_ = true; | |
| 70 } | |
| 71 | |
| 72 MOCK_METHOD0(OnError, void()); | |
| 73 | |
| 74 // Listener implementation. | |
| 75 MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message& message)); | |
| 76 void OnChannelConnected(int32 peer_pid) OVERRIDE { | |
| 77 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 78 MessageLoop::QuitClosure()); | |
| 79 } | |
| 80 MOCK_METHOD0(OnChannelError, void()); | |
| 81 MOCK_METHOD0(OnChannelDenied, void()); | |
| 82 MOCK_METHOD0(OnChannelListenError, void()); | |
| 83 | |
| 84 private: | |
| 85 const content::ResourceContext& context_; | |
| 86 IPC::Channel* channel_; | |
| 87 bool set_plugin_info_called_; | |
| 88 DISALLOW_COPY_AND_ASSIGN(MockPluginProcessHostClient); | |
| 89 }; | |
| 90 | |
| 91 class PluginServiceTest : public InProcessBrowserTest { | |
| 92 public: | |
| 93 PluginServiceTest() : InProcessBrowserTest() { } | |
| 94 | |
| 95 virtual void SetUpCommandLine(CommandLine* command_line) { | |
| 96 #ifdef OS_MACOSX | |
| 97 FilePath browser_directory; | |
| 98 PathService::Get(base::DIR_MODULE, &browser_directory); | |
| 99 command_line->AppendSwitchPath(switches::kExtraPluginDir, | |
| 100 browser_directory.AppendASCII("plugins")); | |
| 101 #endif | |
| 102 } | |
| 103 }; | |
| 104 | |
| 105 // Try to open a channel to the test plugin. Minimal plugin process spawning | |
| 106 // test for the PluginService interface. | |
| 107 IN_PROC_BROWSER_TEST_F(PluginServiceTest, OpenChannelToPlugin) { | |
| 108 ::testing::StrictMock<MockPluginProcessHostClient> mock_client( | |
| 109 browser()->profile()->GetResourceContext()); | |
| 110 BrowserThread::PostTask( | |
| 111 BrowserThread::IO, FROM_HERE, | |
| 112 base::Bind(&OpenChannel, &mock_client)); | |
| 113 ui_test_utils::RunMessageLoop(); | |
| 114 } | |
| 115 | |
| 116 // A strict mock that fails if any of the methods are called. They shouldn't be | |
| 117 // called since the request should get canceled before then. | |
| 118 class MockCanceledPluginServiceClient : public PluginProcessHost::Client { | |
| 119 public: | |
| 120 MockCanceledPluginServiceClient(const content::ResourceContext& context) | |
| 121 : context_(context), | |
| 122 get_resource_context_called_(false) { | |
| 123 } | |
| 124 | |
| 125 virtual ~MockCanceledPluginServiceClient() {} | |
| 126 | |
| 127 // Client implementation. | |
| 128 MOCK_METHOD0(ID, int()); | |
| 129 virtual const content::ResourceContext& GetResourceContext() OVERRIDE { | |
| 130 get_resource_context_called_ = true; | |
| 131 return context_; | |
| 132 } | |
| 133 MOCK_METHOD0(OffTheRecord, bool()); | |
| 134 MOCK_METHOD1(OnFoundPluginProcessHost, void(PluginProcessHost* host)); | |
| 135 MOCK_METHOD0(OnSentPluginChannelRequest, void()); | |
| 136 MOCK_METHOD1(OnChannelOpened, void(const IPC::ChannelHandle& handle)); | |
| 137 MOCK_METHOD1(SetPluginInfo, void(const webkit::WebPluginInfo& info)); | |
| 138 MOCK_METHOD0(OnError, void()); | |
| 139 | |
| 140 bool get_resource_context_called() const { | |
| 141 return get_resource_context_called_; | |
| 142 } | |
| 143 | |
| 144 private: | |
| 145 const content::ResourceContext& context_; | |
| 146 bool get_resource_context_called_; | |
| 147 | |
| 148 DISALLOW_COPY_AND_ASSIGN(MockCanceledPluginServiceClient); | |
| 149 }; | |
| 150 | |
| 151 void DoNothing() {} | |
| 152 | |
| 153 void QuitUIMessageLoopFromIOThread() { | |
| 154 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 155 MessageLoop::QuitClosure()); | |
| 156 } | |
| 157 | |
| 158 void OpenChannelAndThenCancel(PluginProcessHost::Client* client) { | |
| 159 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 160 // Start opening the channel | |
| 161 PluginService::GetInstance()->OpenChannelToNpapiPlugin( | |
| 162 0, 0, GURL(), GURL(), kNPAPITestPluginMimeType, client); | |
| 163 // Immediately cancel it. This is guaranteed to work since PluginService needs | |
| 164 // to consult its filter on the FILE thread. | |
| 165 PluginService::GetInstance()->CancelOpenChannelToNpapiPlugin(client); | |
| 166 // Before we terminate the test, add a roundtrip through the FILE thread to | |
| 167 // make sure that it's had a chance to post back to the IO thread. Then signal | |
| 168 // the UI thread to stop and exit the test. | |
| 169 BrowserThread::PostTaskAndReply( | |
| 170 BrowserThread::FILE, FROM_HERE, | |
| 171 base::Bind(&DoNothing), | |
| 172 base::Bind(&QuitUIMessageLoopFromIOThread)); | |
| 173 } | |
| 174 | |
| 175 // Should not attempt to open a channel, since it should be canceled early on. | |
| 176 IN_PROC_BROWSER_TEST_F(PluginServiceTest, CancelOpenChannelToPluginService) { | |
| 177 ::testing::StrictMock<MockCanceledPluginServiceClient> mock_client( | |
| 178 browser()->profile()->GetResourceContext()); | |
| 179 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE, | |
| 180 base::Bind(OpenChannelAndThenCancel, &mock_client)); | |
| 181 ui_test_utils::RunMessageLoop(); | |
| 182 EXPECT_TRUE(mock_client.get_resource_context_called()); | |
| 183 } | |
| 184 | |
| 185 class MockCanceledBeforeSentPluginProcessHostClient | |
| 186 : public MockCanceledPluginServiceClient { | |
| 187 public: | |
| 188 MockCanceledBeforeSentPluginProcessHostClient( | |
| 189 const content::ResourceContext& context) | |
| 190 : MockCanceledPluginServiceClient(context), | |
| 191 set_plugin_info_called_(false), | |
| 192 on_found_plugin_process_host_called_(false), | |
| 193 host_(NULL) {} | |
| 194 | |
| 195 virtual ~MockCanceledBeforeSentPluginProcessHostClient() {} | |
| 196 | |
| 197 // Client implementation. | |
| 198 virtual void SetPluginInfo(const webkit::WebPluginInfo& info) OVERRIDE { | |
| 199 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 200 ASSERT_TRUE(info.mime_types.size()); | |
| 201 ASSERT_EQ(kNPAPITestPluginMimeType, info.mime_types[0].mime_type); | |
| 202 set_plugin_info_called_ = true; | |
| 203 } | |
| 204 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE { | |
| 205 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 206 set_on_found_plugin_process_host_called(); | |
| 207 set_host(host); | |
| 208 // This gets called right before we request the plugin<=>renderer channel, | |
| 209 // so we have to post a task to cancel it. | |
| 210 MessageLoop::current()->PostTask( | |
| 211 FROM_HERE, | |
| 212 base::Bind(&PluginProcessHost::CancelPendingRequest, | |
| 213 base::Unretained(host), this)); | |
| 214 MessageLoop::current()->PostTask( | |
| 215 FROM_HERE, | |
| 216 base::Bind(&QuitUIMessageLoopFromIOThread)); | |
| 217 } | |
| 218 | |
| 219 bool set_plugin_info_called() const { | |
| 220 return set_plugin_info_called_; | |
| 221 } | |
| 222 | |
| 223 bool on_found_plugin_process_host_called() const { | |
| 224 return on_found_plugin_process_host_called_; | |
| 225 } | |
| 226 | |
| 227 protected: | |
| 228 void set_on_found_plugin_process_host_called() { | |
| 229 on_found_plugin_process_host_called_ = true; | |
| 230 } | |
| 231 void set_host(PluginProcessHost* host) { | |
| 232 host_ = host; | |
| 233 } | |
| 234 | |
| 235 PluginProcessHost* host() const { return host_; } | |
| 236 | |
| 237 private: | |
| 238 bool set_plugin_info_called_; | |
| 239 bool on_found_plugin_process_host_called_; | |
| 240 PluginProcessHost* host_; | |
| 241 | |
| 242 DISALLOW_COPY_AND_ASSIGN(MockCanceledBeforeSentPluginProcessHostClient); | |
| 243 }; | |
| 244 | |
| 245 IN_PROC_BROWSER_TEST_F( | |
| 246 PluginServiceTest, CancelBeforeSentOpenChannelToPluginProcessHost) { | |
| 247 ::testing::StrictMock<MockCanceledBeforeSentPluginProcessHostClient> | |
| 248 mock_client(browser()->profile()->GetResourceContext()); | |
| 249 BrowserThread::PostTask( | |
| 250 BrowserThread::IO, FROM_HERE, | |
| 251 base::Bind(&OpenChannel, &mock_client)); | |
| 252 ui_test_utils::RunMessageLoop(); | |
| 253 EXPECT_TRUE(mock_client.get_resource_context_called()); | |
| 254 EXPECT_TRUE(mock_client.set_plugin_info_called()); | |
| 255 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called()); | |
| 256 } | |
| 257 | |
| 258 class MockCanceledAfterSentPluginProcessHostClient | |
| 259 : public MockCanceledBeforeSentPluginProcessHostClient { | |
| 260 public: | |
| 261 MockCanceledAfterSentPluginProcessHostClient( | |
| 262 const content::ResourceContext& context) | |
| 263 : MockCanceledBeforeSentPluginProcessHostClient(context), | |
| 264 on_sent_plugin_channel_request_called_(false) {} | |
| 265 virtual ~MockCanceledAfterSentPluginProcessHostClient() {} | |
| 266 | |
| 267 // Client implementation. | |
| 268 | |
| 269 virtual int ID() OVERRIDE { return 42; } | |
| 270 virtual bool OffTheRecord() OVERRIDE { return false; } | |
| 271 | |
| 272 // We override this guy again since we don't want to cancel yet. | |
| 273 virtual void OnFoundPluginProcessHost(PluginProcessHost* host) OVERRIDE { | |
| 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); | |
| 275 set_on_found_plugin_process_host_called(); | |
| 276 set_host(host); | |
| 277 } | |
| 278 | |
| 279 virtual void OnSentPluginChannelRequest() OVERRIDE { | |
| 280 on_sent_plugin_channel_request_called_ = true; | |
| 281 host()->CancelSentRequest(this); | |
| 282 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, | |
| 283 MessageLoop::QuitClosure()); | |
| 284 } | |
| 285 | |
| 286 bool on_sent_plugin_channel_request_called() const { | |
| 287 return on_sent_plugin_channel_request_called_; | |
| 288 } | |
| 289 | |
| 290 private: | |
| 291 bool on_sent_plugin_channel_request_called_; | |
| 292 | |
| 293 DISALLOW_COPY_AND_ASSIGN(MockCanceledAfterSentPluginProcessHostClient); | |
| 294 }; | |
| 295 | |
| 296 // Should not attempt to open a channel, since it should be canceled early on. | |
| 297 IN_PROC_BROWSER_TEST_F( | |
| 298 PluginServiceTest, CancelAfterSentOpenChannelToPluginProcessHost) { | |
| 299 ::testing::StrictMock<MockCanceledAfterSentPluginProcessHostClient> | |
| 300 mock_client(browser()->profile()->GetResourceContext()); | |
| 301 BrowserThread::PostTask( | |
| 302 BrowserThread::IO, FROM_HERE, | |
| 303 base::Bind(&OpenChannel, &mock_client)); | |
| 304 ui_test_utils::RunMessageLoop(); | |
| 305 EXPECT_TRUE(mock_client.get_resource_context_called()); | |
| 306 EXPECT_TRUE(mock_client.set_plugin_info_called()); | |
| 307 EXPECT_TRUE(mock_client.on_found_plugin_process_host_called()); | |
| 308 EXPECT_TRUE(mock_client.on_sent_plugin_channel_request_called()); | |
| 309 } | |
| 310 | |
| 311 } // namespace | |
| OLD | NEW |