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
3 // found in the LICENSE file.
4
5 #include "content/renderer/browser_plugin/browser_plugin_browsertest.h"
6
7 #include "base/debug/leak_annotations.h"
8 #include "base/files/file_path.h"
9 #include "base/memory/singleton.h"
10 #include "base/path_service.h"
11 #include "base/pickle.h"
12 #include "content/public/common/content_constants.h"
13 #include "content/public/renderer/content_renderer_client.h"
14 #include "content/renderer/browser_plugin/browser_plugin.h"
15 #include "content/renderer/browser_plugin/browser_plugin_manager_factory.h"
16 #include "content/renderer/browser_plugin/mock_browser_plugin.h"
17 #include "content/renderer/browser_plugin/mock_browser_plugin_manager.h"
18 #include "content/renderer/render_thread_impl.h"
19 #include "content/renderer/renderer_webkitplatformsupport_impl.h"
20 #include "skia/ext/platform_canvas.h"
21 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
22 #include "third_party/WebKit/public/web/WebInputEvent.h"
23 #include "third_party/WebKit/public/web/WebLocalFrame.h"
24 #include "third_party/WebKit/public/web/WebScriptSource.h"
25
26 namespace content {
27
28 namespace {
29 const char kHTMLForBrowserPluginObject[] =
30 "<object id='browserplugin' width='640px' height='480px'"
31 " src='foo' type='%s'></object>"
32 "<script>document.querySelector('object').nonExistentAttribute;</script>";
33
34 const char kHTMLForBrowserPluginWithAllAttributes[] =
35 "<object id='browserplugin' width='640' height='480' type='%s'"
36 " autosize maxheight='600' maxwidth='800' minheight='240'"
37 " minwidth='320' name='Jim' partition='someid' src='foo'>";
38
39 const char kHTMLForSourcelessPluginObject[] =
40 "<object id='browserplugin' width='640px' height='480px' type='%s'>";
41
GetHTMLForBrowserPluginObject()42 std::string GetHTMLForBrowserPluginObject() {
43 return base::StringPrintf(kHTMLForBrowserPluginObject,
44 kBrowserPluginMimeType);
45 }
46
47 } // namespace
48
49 // Test factory for creating test instances of BrowserPluginManager.
50 class TestBrowserPluginManagerFactory : public BrowserPluginManagerFactory {
51 public:
CreateBrowserPluginManager(RenderViewImpl * render_view)52 virtual MockBrowserPluginManager* CreateBrowserPluginManager(
53 RenderViewImpl* render_view) OVERRIDE {
54 return new MockBrowserPluginManager(render_view);
55 }
56
57 // Singleton getter.
GetInstance()58 static TestBrowserPluginManagerFactory* GetInstance() {
59 return Singleton<TestBrowserPluginManagerFactory>::get();
60 }
61
62 protected:
TestBrowserPluginManagerFactory()63 TestBrowserPluginManagerFactory() {}
~TestBrowserPluginManagerFactory()64 virtual ~TestBrowserPluginManagerFactory() {}
65
66 private:
67 // For Singleton.
68 friend struct DefaultSingletonTraits<TestBrowserPluginManagerFactory>;
69
70 DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginManagerFactory);
71 };
72
BrowserPluginTest()73 BrowserPluginTest::BrowserPluginTest() {}
74
~BrowserPluginTest()75 BrowserPluginTest::~BrowserPluginTest() {}
76
SetUp()77 void BrowserPluginTest::SetUp() {
78 BrowserPluginManager::set_factory_for_testing(
79 TestBrowserPluginManagerFactory::GetInstance());
80 content::RenderViewTest::SetUp();
81 }
82
TearDown()83 void BrowserPluginTest::TearDown() {
84 BrowserPluginManager::set_factory_for_testing(
85 TestBrowserPluginManagerFactory::GetInstance());
86 #if defined(LEAK_SANITIZER)
87 // Do this before shutting down V8 in RenderViewTest::TearDown().
88 // http://crbug.com/328552
89 __lsan_do_leak_check();
90 #endif
91 RenderViewTest::TearDown();
92 }
93
ExecuteScriptAndReturnString(const std::string & script)94 std::string BrowserPluginTest::ExecuteScriptAndReturnString(
95 const std::string& script) {
96 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
97 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
98 blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
99 if (value.IsEmpty() || !value->IsString())
100 return std::string();
101
102 v8::Local<v8::String> v8_str = value->ToString();
103 int length = v8_str->Utf8Length() + 1;
104 scoped_ptr<char[]> str(new char[length]);
105 v8_str->WriteUtf8(str.get(), length);
106 return str.get();
107 }
108
ExecuteScriptAndReturnInt(const std::string & script)109 int BrowserPluginTest::ExecuteScriptAndReturnInt(
110 const std::string& script) {
111 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
112 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
113 blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
114 if (value.IsEmpty() || !value->IsInt32())
115 return 0;
116
117 return value->Int32Value();
118 }
119
120 // A return value of false means that a value was not present. The return value
121 // of the script is stored in |result|
ExecuteScriptAndReturnBool(const std::string & script,bool * result)122 bool BrowserPluginTest::ExecuteScriptAndReturnBool(
123 const std::string& script, bool* result) {
124 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
125 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
126 blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
127 if (value.IsEmpty() || !value->IsBoolean())
128 return false;
129
130 *result = value->BooleanValue();
131 return true;
132 }
133
GetCurrentPlugin()134 MockBrowserPlugin* BrowserPluginTest::GetCurrentPlugin() {
135 BrowserPluginHostMsg_Attach_Params params;
136 return GetCurrentPluginWithAttachParams(¶ms);
137 }
138
GetCurrentPluginWithAttachParams(BrowserPluginHostMsg_Attach_Params * params)139 MockBrowserPlugin* BrowserPluginTest::GetCurrentPluginWithAttachParams(
140 BrowserPluginHostMsg_Attach_Params* params) {
141 MockBrowserPlugin* browser_plugin = static_cast<MockBrowserPluginManager*>(
142 browser_plugin_manager())->last_plugin();
143 if (!browser_plugin)
144 return NULL;
145 browser_plugin_manager()->AllocateInstanceID(browser_plugin);
146
147 int instance_id = 0;
148 const IPC::Message* msg =
149 browser_plugin_manager()->sink().GetUniqueMessageMatching(
150 BrowserPluginHostMsg_Attach::ID);
151 if (!msg)
152 return NULL;
153
154 PickleIterator iter(*msg);
155 if (!iter.ReadInt(&instance_id))
156 return NULL;
157
158 if (!IPC::ParamTraits<BrowserPluginHostMsg_Attach_Params>::Read(
159 msg, &iter, params)) {
160 return NULL;
161 }
162
163 browser_plugin->OnAttachACK(instance_id);
164 return browser_plugin;
165 }
166
167 // This test verifies that an initial resize occurs when we instantiate the
168 // browser plugin.
TEST_F(BrowserPluginTest,InitialResize)169 TEST_F(BrowserPluginTest, InitialResize) {
170 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
171 // Verify that the information in Attach is correct.
172 BrowserPluginHostMsg_Attach_Params params;
173 MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(¶ms);
174
175 EXPECT_EQ(640, params.resize_guest_params.view_size.width());
176 EXPECT_EQ(480, params.resize_guest_params.view_size.height());
177 ASSERT_TRUE(browser_plugin);
178 }
179
180 // This test verifies that all attributes (present at the time of writing) are
181 // parsed on initialization. However, this test does minimal checking of
182 // correct behavior.
TEST_F(BrowserPluginTest,ParseAllAttributes)183 TEST_F(BrowserPluginTest, ParseAllAttributes) {
184 std::string html = base::StringPrintf(kHTMLForBrowserPluginWithAllAttributes,
185 kBrowserPluginMimeType);
186 LoadHTML(html.c_str());
187 bool result;
188 bool has_value = ExecuteScriptAndReturnBool(
189 "document.getElementById('browserplugin').autosize", &result);
190 EXPECT_TRUE(has_value);
191 EXPECT_TRUE(result);
192 int maxHeight = ExecuteScriptAndReturnInt(
193 "document.getElementById('browserplugin').maxheight");
194 EXPECT_EQ(600, maxHeight);
195 int maxWidth = ExecuteScriptAndReturnInt(
196 "document.getElementById('browserplugin').maxwidth");
197 EXPECT_EQ(800, maxWidth);
198 int minHeight = ExecuteScriptAndReturnInt(
199 "document.getElementById('browserplugin').minheight");
200 EXPECT_EQ(240, minHeight);
201 int minWidth = ExecuteScriptAndReturnInt(
202 "document.getElementById('browserplugin').minwidth");
203 EXPECT_EQ(320, minWidth);
204 }
205
TEST_F(BrowserPluginTest,ResizeFlowControl)206 TEST_F(BrowserPluginTest, ResizeFlowControl) {
207 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
208 MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
209 ASSERT_TRUE(browser_plugin);
210 int instance_id = browser_plugin->guest_instance_id();
211 // Send an UpdateRect to the BrowserPlugin to make sure the browser sees a
212 // resize related (SetAutoSize) message.
213 {
214 // We send a stale UpdateRect to the BrowserPlugin.
215 BrowserPluginMsg_UpdateRect_Params update_rect_params;
216 update_rect_params.view_size = gfx::Size(640, 480);
217 update_rect_params.scale_factor = 1.0f;
218 update_rect_params.is_resize_ack = true;
219 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
220 browser_plugin->OnMessageReceived(msg);
221 }
222
223 browser_plugin_manager()->sink().ClearMessages();
224
225 // Resize the browser plugin three times.
226
227 ExecuteJavaScript("document.getElementById('browserplugin').width = '641px'");
228 GetMainFrame()->view()->layout();
229 ProcessPendingMessages();
230
231 ExecuteJavaScript("document.getElementById('browserplugin').width = '642px'");
232 GetMainFrame()->view()->layout();
233 ProcessPendingMessages();
234
235 ExecuteJavaScript("document.getElementById('browserplugin').width = '643px'");
236 GetMainFrame()->view()->layout();
237 ProcessPendingMessages();
238
239 // Expect to see one resize messsage in the sink. BrowserPlugin will not issue
240 // subsequent resize requests until the first request is satisfied by the
241 // guest. The rest of the messages could be
242 // BrowserPluginHostMsg_UpdateGeometry msgs.
243 EXPECT_LE(1u, browser_plugin_manager()->sink().message_count());
244 for (size_t i = 0; i < browser_plugin_manager()->sink().message_count();
245 ++i) {
246 const IPC::Message* msg = browser_plugin_manager()->sink().GetMessageAt(i);
247 if (msg->type() != BrowserPluginHostMsg_ResizeGuest::ID)
248 EXPECT_EQ(msg->type(), BrowserPluginHostMsg_UpdateGeometry::ID);
249 }
250 const IPC::Message* msg =
251 browser_plugin_manager()->sink().GetUniqueMessageMatching(
252 BrowserPluginHostMsg_ResizeGuest::ID);
253 ASSERT_TRUE(msg);
254 BrowserPluginHostMsg_ResizeGuest::Param param;
255 BrowserPluginHostMsg_ResizeGuest::Read(msg, ¶m);
256 instance_id = param.a;
257 BrowserPluginHostMsg_ResizeGuest_Params params = param.b;
258 EXPECT_EQ(641, params.view_size.width());
259 EXPECT_EQ(480, params.view_size.height());
260
261 {
262 // We send a stale UpdateRect to the BrowserPlugin.
263 BrowserPluginMsg_UpdateRect_Params update_rect_params;
264 update_rect_params.view_size = gfx::Size(641, 480);
265 update_rect_params.scale_factor = 1.0f;
266 update_rect_params.is_resize_ack = true;
267 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
268 browser_plugin->OnMessageReceived(msg);
269 }
270 // Send the BrowserPlugin another UpdateRect, but this time with a size
271 // that matches the size of the container.
272 {
273 BrowserPluginMsg_UpdateRect_Params update_rect_params;
274 update_rect_params.view_size = gfx::Size(643, 480);
275 update_rect_params.scale_factor = 1.0f;
276 update_rect_params.is_resize_ack = true;
277 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
278 browser_plugin->OnMessageReceived(msg);
279 }
280 }
281
TEST_F(BrowserPluginTest,RemovePlugin)282 TEST_F(BrowserPluginTest, RemovePlugin) {
283 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
284 MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
285 ASSERT_TRUE(browser_plugin);
286
287 EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
288 BrowserPluginHostMsg_PluginDestroyed::ID));
289 ExecuteJavaScript("x = document.getElementById('browserplugin'); "
290 "x.parentNode.removeChild(x);");
291 ProcessPendingMessages();
292 EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
293 BrowserPluginHostMsg_PluginDestroyed::ID));
294 }
295
296 // This test verifies that PluginDestroyed messages do not get sent from a
297 // BrowserPlugin that has never navigated.
TEST_F(BrowserPluginTest,RemovePluginBeforeNavigation)298 TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) {
299 std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
300 kBrowserPluginMimeType);
301 LoadHTML(html.c_str());
302 EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
303 BrowserPluginHostMsg_PluginDestroyed::ID));
304 ExecuteJavaScript("x = document.getElementById('browserplugin'); "
305 "x.parentNode.removeChild(x);");
306 ProcessPendingMessages();
307 EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
308 BrowserPluginHostMsg_PluginDestroyed::ID));
309 }
310
311 // Verify that the 'partition' attribute on the browser plugin is parsed
312 // correctly.
TEST_F(BrowserPluginTest,AutoSizeAttributes)313 TEST_F(BrowserPluginTest, AutoSizeAttributes) {
314 std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
315 kBrowserPluginMimeType);
316 LoadHTML(html.c_str());
317 const char* kSetAutoSizeParametersAndNavigate =
318 "var browserplugin = document.getElementById('browserplugin');"
319 "browserplugin.autosize = true;"
320 "browserplugin.minwidth = 42;"
321 "browserplugin.minheight = 43;"
322 "browserplugin.maxwidth = 1337;"
323 "browserplugin.maxheight = 1338;"
324 "browserplugin.src = 'foobar';";
325 const char* kDisableAutoSize =
326 "document.getElementById('browserplugin').removeAttribute('autosize');";
327
328 int instance_id = 0;
329 // Set some autosize parameters before navigating then navigate.
330 // Verify that the BrowserPluginHostMsg_Attach message contains
331 // the correct autosize parameters.
332 ExecuteJavaScript(kSetAutoSizeParametersAndNavigate);
333 ProcessPendingMessages();
334
335 BrowserPluginHostMsg_Attach_Params params;
336 MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(¶ms);
337 ASSERT_TRUE(browser_plugin);
338
339 EXPECT_TRUE(params.auto_size_params.enable);
340 EXPECT_EQ(42, params.auto_size_params.min_size.width());
341 EXPECT_EQ(43, params.auto_size_params.min_size.height());
342 EXPECT_EQ(1337, params.auto_size_params.max_size.width());
343 EXPECT_EQ(1338, params.auto_size_params.max_size.height());
344
345 // Disable autosize. AutoSize state will not be sent to the guest until
346 // the guest has responded to the last resize request.
347 ExecuteJavaScript(kDisableAutoSize);
348 ProcessPendingMessages();
349
350 const IPC::Message* auto_size_msg =
351 browser_plugin_manager()->sink().GetUniqueMessageMatching(
352 BrowserPluginHostMsg_SetAutoSize::ID);
353 EXPECT_FALSE(auto_size_msg);
354
355 // Send the BrowserPlugin an UpdateRect equal to its |max_size|.
356 BrowserPluginMsg_UpdateRect_Params update_rect_params;
357 update_rect_params.view_size = gfx::Size(1337, 1338);
358 update_rect_params.scale_factor = 1.0f;
359 update_rect_params.is_resize_ack = true;
360 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
361 browser_plugin->OnMessageReceived(msg);
362
363 // Verify that the autosize state has been updated.
364 {
365 const IPC::Message* auto_size_msg =
366 browser_plugin_manager()->sink().GetUniqueMessageMatching(
367 BrowserPluginHostMsg_SetAutoSize::ID);
368 ASSERT_TRUE(auto_size_msg);
369
370 BrowserPluginHostMsg_SetAutoSize::Param param;
371 BrowserPluginHostMsg_SetAutoSize::Read(auto_size_msg, ¶m);
372 BrowserPluginHostMsg_AutoSize_Params auto_size_params = param.b;
373 BrowserPluginHostMsg_ResizeGuest_Params resize_params = param.c;
374 EXPECT_FALSE(auto_size_params.enable);
375 // These value are not populated (as an optimization) if autosize is
376 // disabled.
377 EXPECT_EQ(0, auto_size_params.min_size.width());
378 EXPECT_EQ(0, auto_size_params.min_size.height());
379 EXPECT_EQ(0, auto_size_params.max_size.width());
380 EXPECT_EQ(0, auto_size_params.max_size.height());
381 }
382 }
383
384 } // namespace content
385