• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&params);
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(&params);
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, &param);
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(&params);
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, &param);
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