• 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/files/file_path.h"
8 #include "base/memory/singleton.h"
9 #include "base/path_service.h"
10 #include "base/pickle.h"
11 #include "content/public/common/content_constants.h"
12 #include "content/renderer/browser_plugin/browser_plugin.h"
13 #include "content/renderer/browser_plugin/browser_plugin_manager_factory.h"
14 #include "content/renderer/browser_plugin/mock_browser_plugin.h"
15 #include "content/renderer/browser_plugin/mock_browser_plugin_manager.h"
16 #include "content/renderer/render_thread_impl.h"
17 #include "content/renderer/renderer_webkitplatformsupport_impl.h"
18 #include "skia/ext/platform_canvas.h"
19 #include "third_party/WebKit/public/platform/WebCursorInfo.h"
20 #include "third_party/WebKit/public/web/WebInputEvent.h"
21 #include "third_party/WebKit/public/web/WebScriptSource.h"
22 
23 namespace {
24 const char kHTMLForBrowserPluginObject[] =
25     "<object id='browserplugin' width='640px' height='480px'"
26     " src='foo' type='%s'></object>"
27     "<script>document.querySelector('object').nonExistentAttribute;</script>";
28 
29 const char kHTMLForBrowserPluginWithAllAttributes[] =
30     "<object id='browserplugin' width='640' height='480' type='%s'"
31     " autosize maxheight='600' maxwidth='800' minheight='240'"
32     " minwidth='320' name='Jim' partition='someid' src='foo'>";
33 
34 const char kHTMLForSourcelessPluginObject[] =
35     "<object id='browserplugin' width='640px' height='480px' type='%s'>";
36 
37 const char kHTMLForPartitionedPluginObject[] =
38     "<object id='browserplugin' width='640px' height='480px'"
39     "  src='foo' type='%s' partition='someid'>";
40 
41 const char kHTMLForInvalidPartitionedPluginObject[] =
42     "<object id='browserplugin' width='640px' height='480px'"
43     "  type='%s' partition='persist:'>";
44 
45 const char kHTMLForPartitionedPersistedPluginObject[] =
46     "<object id='browserplugin' width='640px' height='480px'"
47     "  src='foo' type='%s' partition='persist:someid'>";
48 
GetHTMLForBrowserPluginObject()49 std::string GetHTMLForBrowserPluginObject() {
50   return base::StringPrintf(kHTMLForBrowserPluginObject,
51                             content::kBrowserPluginMimeType);
52 }
53 
54 }  // namespace
55 
56 namespace content {
57 
58 class TestContentRendererClient : public ContentRendererClient {
59  public:
TestContentRendererClient()60   TestContentRendererClient() : ContentRendererClient() {
61   }
~TestContentRendererClient()62   virtual ~TestContentRendererClient() {
63   }
AllowBrowserPlugin(blink::WebPluginContainer * container)64   virtual bool AllowBrowserPlugin(
65       blink::WebPluginContainer* container) OVERRIDE {
66     // Allow BrowserPlugin for tests.
67     return true;
68   }
69 };
70 
71 // Test factory for creating test instances of BrowserPluginManager.
72 class TestBrowserPluginManagerFactory : public BrowserPluginManagerFactory {
73  public:
CreateBrowserPluginManager(RenderViewImpl * render_view)74   virtual MockBrowserPluginManager* CreateBrowserPluginManager(
75       RenderViewImpl* render_view) OVERRIDE {
76     return new MockBrowserPluginManager(render_view);
77   }
78 
79   // Singleton getter.
GetInstance()80   static TestBrowserPluginManagerFactory* GetInstance() {
81     return Singleton<TestBrowserPluginManagerFactory>::get();
82   }
83 
84  protected:
TestBrowserPluginManagerFactory()85   TestBrowserPluginManagerFactory() {}
~TestBrowserPluginManagerFactory()86   virtual ~TestBrowserPluginManagerFactory() {}
87 
88  private:
89   // For Singleton.
90   friend struct DefaultSingletonTraits<TestBrowserPluginManagerFactory>;
91 
92   DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginManagerFactory);
93 };
94 
BrowserPluginTest()95 BrowserPluginTest::BrowserPluginTest() {}
96 
~BrowserPluginTest()97 BrowserPluginTest::~BrowserPluginTest() {}
98 
SetUp()99 void BrowserPluginTest::SetUp() {
100   test_content_renderer_client_.reset(new TestContentRendererClient);
101   SetRendererClientForTesting(test_content_renderer_client_.get());
102   BrowserPluginManager::set_factory_for_testing(
103       TestBrowserPluginManagerFactory::GetInstance());
104   content::RenderViewTest::SetUp();
105 }
106 
TearDown()107 void BrowserPluginTest::TearDown() {
108   BrowserPluginManager::set_factory_for_testing(
109       TestBrowserPluginManagerFactory::GetInstance());
110   content::RenderViewTest::TearDown();
111   test_content_renderer_client_.reset();
112 }
113 
ExecuteScriptAndReturnString(const std::string & script)114 std::string BrowserPluginTest::ExecuteScriptAndReturnString(
115     const std::string& script) {
116   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
117   v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
118       blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
119   if (value.IsEmpty() || !value->IsString())
120     return std::string();
121 
122   v8::Local<v8::String> v8_str = value->ToString();
123   int length = v8_str->Utf8Length() + 1;
124   scoped_ptr<char[]> str(new char[length]);
125   v8_str->WriteUtf8(str.get(), length);
126   return str.get();
127 }
128 
ExecuteScriptAndReturnInt(const std::string & script)129 int BrowserPluginTest::ExecuteScriptAndReturnInt(
130     const std::string& script) {
131   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
132   v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
133       blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
134   if (value.IsEmpty() || !value->IsInt32())
135     return 0;
136 
137   return value->Int32Value();
138 }
139 
140 // A return value of false means that a value was not present. The return value
141 // of the script is stored in |result|
ExecuteScriptAndReturnBool(const std::string & script,bool * result)142 bool BrowserPluginTest::ExecuteScriptAndReturnBool(
143     const std::string& script, bool* result) {
144   v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
145   v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
146       blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
147   if (value.IsEmpty() || !value->IsBoolean())
148     return false;
149 
150   *result = value->BooleanValue();
151   return true;
152 }
153 
GetCurrentPlugin()154 MockBrowserPlugin* BrowserPluginTest::GetCurrentPlugin() {
155   BrowserPluginHostMsg_Attach_Params params;
156   return GetCurrentPluginWithAttachParams(&params);
157 }
158 
GetCurrentPluginWithAttachParams(BrowserPluginHostMsg_Attach_Params * params)159 MockBrowserPlugin* BrowserPluginTest::GetCurrentPluginWithAttachParams(
160     BrowserPluginHostMsg_Attach_Params* params) {
161   int instance_id = 0;
162   const IPC::Message* msg =
163       browser_plugin_manager()->sink().GetUniqueMessageMatching(
164           BrowserPluginHostMsg_Attach::ID);
165   if (!msg)
166     return NULL;
167 
168   PickleIterator iter(*msg);
169   if (!iter.ReadInt(&instance_id))
170     return NULL;
171 
172   if (!IPC::ParamTraits<BrowserPluginHostMsg_Attach_Params>::Read(
173       msg, &iter, params))
174     return NULL;
175 
176   MockBrowserPlugin* browser_plugin = static_cast<MockBrowserPlugin*>(
177       browser_plugin_manager()->GetBrowserPlugin(instance_id));
178 
179   BrowserPluginMsg_Attach_ACK_Params attach_ack_params;
180   browser_plugin->OnAttachACK(instance_id, attach_ack_params);
181 
182   return browser_plugin;
183 }
184 
185 // This test verifies that an initial resize occurs when we instantiate the
186 // browser plugin. This test also verifies that the browser plugin is waiting
187 // for a BrowserPluginMsg_UpdateRect in response. We issue an UpdateRect, and
188 // we observe an UpdateRect_ACK, with the |pending_damage_buffer_| reset,
189 // indiciating that the BrowserPlugin is not waiting for any more UpdateRects to
190 // satisfy its resize request.
TEST_F(BrowserPluginTest,InitialResize)191 TEST_F(BrowserPluginTest, InitialResize) {
192   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
193   // Verify that the information in Attach is correct.
194   BrowserPluginHostMsg_Attach_Params params;
195   MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(&params);
196 
197   EXPECT_EQ(640, params.resize_guest_params.view_rect.width());
198   EXPECT_EQ(480, params.resize_guest_params.view_rect.height());
199   ASSERT_TRUE(browser_plugin);
200   // Now the browser plugin is expecting a UpdateRect resize.
201   int instance_id = browser_plugin->guest_instance_id();
202   EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get());
203 
204   // Send the BrowserPlugin an UpdateRect equal to its container size with
205   // the same damage buffer. That should clear |pending_damage_buffer_|.
206   BrowserPluginMsg_UpdateRect_Params update_rect_params;
207   update_rect_params.damage_buffer_sequence_id =
208       browser_plugin->damage_buffer_sequence_id_;
209   update_rect_params.view_size = gfx::Size(640, 480);
210   update_rect_params.scale_factor = 1.0f;
211   update_rect_params.is_resize_ack = true;
212   update_rect_params.needs_ack = true;
213   BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
214   browser_plugin->OnMessageReceived(msg);
215   EXPECT_FALSE(browser_plugin->pending_damage_buffer_.get());
216 }
217 
218 // This test verifies that all attributes (present at the time of writing) are
219 // parsed on initialization. However, this test does minimal checking of
220 // correct behavior.
TEST_F(BrowserPluginTest,ParseAllAttributes)221 TEST_F(BrowserPluginTest, ParseAllAttributes) {
222   std::string html = base::StringPrintf(kHTMLForBrowserPluginWithAllAttributes,
223                                         content::kBrowserPluginMimeType);
224   LoadHTML(html.c_str());
225   bool result;
226   bool has_value = ExecuteScriptAndReturnBool(
227       "document.getElementById('browserplugin').autosize", &result);
228   EXPECT_TRUE(has_value);
229   EXPECT_TRUE(result);
230   int maxHeight = ExecuteScriptAndReturnInt(
231       "document.getElementById('browserplugin').maxheight");
232   EXPECT_EQ(600, maxHeight);
233   int maxWidth = ExecuteScriptAndReturnInt(
234       "document.getElementById('browserplugin').maxwidth");
235   EXPECT_EQ(800, maxWidth);
236   int minHeight = ExecuteScriptAndReturnInt(
237       "document.getElementById('browserplugin').minheight");
238   EXPECT_EQ(240, minHeight);
239   int minWidth = ExecuteScriptAndReturnInt(
240       "document.getElementById('browserplugin').minwidth");
241   EXPECT_EQ(320, minWidth);
242   std::string name = ExecuteScriptAndReturnString(
243       "document.getElementById('browserplugin').name");
244   EXPECT_STREQ("Jim", name.c_str());
245   std::string partition = ExecuteScriptAndReturnString(
246       "document.getElementById('browserplugin').partition");
247   EXPECT_STREQ("someid", partition.c_str());
248   std::string src = ExecuteScriptAndReturnString(
249       "document.getElementById('browserplugin').src");
250   EXPECT_STREQ("foo", src.c_str());
251 }
252 
253 // Verify that the src attribute on the browser plugin works as expected.
TEST_F(BrowserPluginTest,SrcAttribute)254 TEST_F(BrowserPluginTest, SrcAttribute) {
255   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
256   // Verify that we're reporting the correct URL to navigate to based on the
257   // src attribute.
258   {
259     BrowserPluginHostMsg_Attach_Params params;
260     MockBrowserPlugin* browser_plugin =
261         GetCurrentPluginWithAttachParams(&params);
262     ASSERT_TRUE(browser_plugin);
263     EXPECT_EQ("foo", params.src);
264   }
265 
266   browser_plugin_manager()->sink().ClearMessages();
267   // Navigate to bar and observe the associated
268   // BrowserPluginHostMsg_NavigateGuest message.
269   // Verify that the src attribute is updated as well.
270   ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
271   {
272     // Verify that we do not get a Attach on subsequent navigations.
273     const IPC::Message* create_msg =
274         browser_plugin_manager()->sink().GetUniqueMessageMatching(
275             BrowserPluginHostMsg_Attach::ID);
276     ASSERT_FALSE(create_msg);
277 
278     const IPC::Message* msg =
279         browser_plugin_manager()->sink().GetUniqueMessageMatching(
280             BrowserPluginHostMsg_NavigateGuest::ID);
281     ASSERT_TRUE(msg);
282 
283     int instance_id = 0;
284     std::string src;
285     BrowserPluginHostMsg_NavigateGuest::Read(msg, &instance_id, &src);
286     EXPECT_EQ("bar", src);
287     std::string src_value =
288         ExecuteScriptAndReturnString(
289             "document.getElementById('browserplugin').src");
290     EXPECT_EQ("bar", src_value);
291   }
292 }
293 
TEST_F(BrowserPluginTest,ResizeFlowControl)294 TEST_F(BrowserPluginTest, ResizeFlowControl) {
295   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
296   MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
297   ASSERT_TRUE(browser_plugin);
298   int instance_id = browser_plugin->guest_instance_id();
299   EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get());
300   // Send an UpdateRect to the BrowserPlugin to make it use the pending damage
301   // buffer.
302   {
303     // We send a stale UpdateRect to the BrowserPlugin.
304     BrowserPluginMsg_UpdateRect_Params update_rect_params;
305     update_rect_params.view_size = gfx::Size(640, 480);
306     update_rect_params.scale_factor = 1.0f;
307     update_rect_params.is_resize_ack = true;
308     update_rect_params.needs_ack = true;
309     // By sending |damage_buffer_sequence_id| back to BrowserPlugin on
310     // UpdateRect, then the BrowserPlugin knows that the browser process has
311     // received and has begun to use the |pending_damage_buffer_|.
312     update_rect_params.damage_buffer_sequence_id =
313         browser_plugin->damage_buffer_sequence_id_;
314     BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
315     browser_plugin->OnMessageReceived(msg);
316     EXPECT_EQ(NULL, browser_plugin->pending_damage_buffer_.get());
317   }
318 
319   browser_plugin_manager()->sink().ClearMessages();
320 
321   // Resize the browser plugin three times.
322   ExecuteJavaScript("document.getElementById('browserplugin').width = '641px'");
323   ProcessPendingMessages();
324   ExecuteJavaScript("document.getElementById('browserplugin').width = '642px'");
325   ProcessPendingMessages();
326   ExecuteJavaScript("document.getElementById('browserplugin').width = '643px'");
327   ProcessPendingMessages();
328 
329   // Expect to see one resize messsage in the sink. BrowserPlugin will not issue
330   // subsequent resize requests until the first request is satisfied by the
331   // guest. The rest of the messages could be
332   // BrowserPluginHostMsg_UpdateGeometry msgs.
333   EXPECT_LE(1u, browser_plugin_manager()->sink().message_count());
334   for (size_t i = 0; i < browser_plugin_manager()->sink().message_count();
335        ++i) {
336     const IPC::Message* msg = browser_plugin_manager()->sink().GetMessageAt(i);
337     if (msg->type() != BrowserPluginHostMsg_ResizeGuest::ID)
338       EXPECT_EQ(msg->type(), BrowserPluginHostMsg_UpdateGeometry::ID);
339   }
340   const IPC::Message* msg =
341       browser_plugin_manager()->sink().GetUniqueMessageMatching(
342           BrowserPluginHostMsg_ResizeGuest::ID);
343   ASSERT_TRUE(msg);
344   BrowserPluginHostMsg_ResizeGuest_Params params;
345   BrowserPluginHostMsg_ResizeGuest::Read(msg, &instance_id, &params);
346   EXPECT_EQ(641, params.view_rect.width());
347   EXPECT_EQ(480, params.view_rect.height());
348   // This indicates that the BrowserPlugin has sent out a previous resize
349   // request but has not yet received an UpdateRect for that request.
350   EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get());
351 
352   {
353     // We send a stale UpdateRect to the BrowserPlugin.
354     BrowserPluginMsg_UpdateRect_Params update_rect_params;
355     update_rect_params.view_size = gfx::Size(641, 480);
356     update_rect_params.scale_factor = 1.0f;
357     update_rect_params.is_resize_ack = true;
358     update_rect_params.needs_ack = true;
359     update_rect_params.damage_buffer_sequence_id =
360         browser_plugin->damage_buffer_sequence_id_;
361     BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
362     browser_plugin->OnMessageReceived(msg);
363     // This tells us that the BrowserPlugin is still expecting another
364     // UpdateRect with the most recent size.
365     EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get());
366   }
367   // Send the BrowserPlugin another UpdateRect, but this time with a size
368   // that matches the size of the container.
369   {
370     BrowserPluginMsg_UpdateRect_Params update_rect_params;
371     update_rect_params.view_size = gfx::Size(643, 480);
372     update_rect_params.scale_factor = 1.0f;
373     update_rect_params.is_resize_ack = true;
374     update_rect_params.needs_ack = true;
375     update_rect_params.damage_buffer_sequence_id =
376         browser_plugin->damage_buffer_sequence_id_;
377     BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
378     browser_plugin->OnMessageReceived(msg);
379     // The BrowserPlugin has finally received an UpdateRect that satisifes
380     // its current size, and so it is happy.
381     EXPECT_FALSE(browser_plugin->pending_damage_buffer_.get());
382   }
383 }
384 
TEST_F(BrowserPluginTest,RemovePlugin)385 TEST_F(BrowserPluginTest, RemovePlugin) {
386   LoadHTML(GetHTMLForBrowserPluginObject().c_str());
387   EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
388       BrowserPluginHostMsg_PluginDestroyed::ID));
389   ExecuteJavaScript("x = document.getElementById('browserplugin'); "
390                     "x.parentNode.removeChild(x);");
391   ProcessPendingMessages();
392   EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
393       BrowserPluginHostMsg_PluginDestroyed::ID));
394 }
395 
396 // This test verifies that PluginDestroyed messages do not get sent from a
397 // BrowserPlugin that has never navigated.
TEST_F(BrowserPluginTest,RemovePluginBeforeNavigation)398 TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) {
399   std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
400                                         content::kBrowserPluginMimeType);
401   LoadHTML(html.c_str());
402   EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
403       BrowserPluginHostMsg_PluginDestroyed::ID));
404   ExecuteJavaScript("x = document.getElementById('browserplugin'); "
405                     "x.parentNode.removeChild(x);");
406   ProcessPendingMessages();
407   EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
408       BrowserPluginHostMsg_PluginDestroyed::ID));
409 }
410 
411 // Verify that the 'partition' attribute on the browser plugin is parsed
412 // correctly.
TEST_F(BrowserPluginTest,PartitionAttribute)413 TEST_F(BrowserPluginTest, PartitionAttribute) {
414   std::string html = base::StringPrintf(kHTMLForPartitionedPluginObject,
415                                         content::kBrowserPluginMimeType);
416   LoadHTML(html.c_str());
417   std::string partition_value = ExecuteScriptAndReturnString(
418       "document.getElementById('browserplugin').partition");
419   EXPECT_STREQ("someid", partition_value.c_str());
420 
421   html = base::StringPrintf(kHTMLForPartitionedPersistedPluginObject,
422                             content::kBrowserPluginMimeType);
423   LoadHTML(html.c_str());
424   partition_value = ExecuteScriptAndReturnString(
425       "document.getElementById('browserplugin').partition");
426   EXPECT_STREQ("persist:someid", partition_value.c_str());
427 
428   // Verify that once HTML has defined a source and partition, we cannot change
429   // the partition anymore.
430   ExecuteJavaScript(
431       "try {"
432       "  document.getElementById('browserplugin').partition = 'foo';"
433       "  document.title = 'success';"
434       "} catch (e) { document.title = e.message; }");
435   std::string title = ExecuteScriptAndReturnString("document.title");
436   EXPECT_STREQ(
437       "The object has already navigated, so its partition cannot be changed.",
438       title.c_str());
439 
440   // Load a browser tag without 'src' defined.
441   html = base::StringPrintf(kHTMLForSourcelessPluginObject,
442                             content::kBrowserPluginMimeType);
443   LoadHTML(html.c_str());
444 
445   // Ensure we don't parse just "persist:" string and return exception.
446   ExecuteJavaScript(
447       "try {"
448       "  document.getElementById('browserplugin').partition = 'persist:';"
449       "  document.title = 'success';"
450       "} catch (e) { document.title = e.message; }");
451   title = ExecuteScriptAndReturnString("document.title");
452   EXPECT_STREQ("Invalid partition attribute.", title.c_str());
453 }
454 
455 // This test verifies that BrowserPlugin enters an error state when the
456 // partition attribute is invalid.
TEST_F(BrowserPluginTest,InvalidPartition)457 TEST_F(BrowserPluginTest, InvalidPartition) {
458   std::string html = base::StringPrintf(kHTMLForInvalidPartitionedPluginObject,
459                                         content::kBrowserPluginMimeType);
460   LoadHTML(html.c_str());
461   // Attempt to navigate with an invalid partition.
462   {
463     ExecuteJavaScript(
464         "try {"
465         "  document.getElementById('browserplugin').src = 'bar';"
466         "  document.title = 'success';"
467         "} catch (e) { document.title = e.message; }");
468     std::string title = ExecuteScriptAndReturnString("document.title");
469     EXPECT_STREQ("Invalid partition attribute.", title.c_str());
470     // Verify that the 'src' attribute has not been updated.
471     EXPECT_EQ("", ExecuteScriptAndReturnString(
472         "document.getElementById('browserplugin').src"));
473   }
474 
475   // Verify that the BrowserPlugin accepts changes to its src attribue after
476   // setting the partition to a valid value.
477   ExecuteJavaScript(
478       "document.getElementById('browserplugin').partition = 'persist:foo'");
479   ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
480   EXPECT_EQ("bar", ExecuteScriptAndReturnString(
481       "document.getElementById('browserplugin').src"));
482   ProcessPendingMessages();
483   // Verify that the BrowserPlugin does not 'deadlock': it can recover from
484   // the partition ID error state.
485   {
486     ExecuteJavaScript(
487         "try {"
488         "  document.getElementById('browserplugin').partition = 'persist:1337';"
489         "  document.title = 'success';"
490         "} catch (e) { document.title = e.message; }");
491     std::string title = ExecuteScriptAndReturnString("document.title");
492     EXPECT_STREQ(
493         "The object has already navigated, so its partition cannot be changed.",
494         title.c_str());
495     ExecuteJavaScript("document.getElementById('browserplugin').src = '42'");
496     EXPECT_EQ("42", ExecuteScriptAndReturnString(
497         "document.getElementById('browserplugin').src"));
498   }
499 }
500 
501 // Test to verify that after the first navigation, the partition attribute
502 // cannot be modified.
TEST_F(BrowserPluginTest,ImmutableAttributesAfterNavigation)503 TEST_F(BrowserPluginTest, ImmutableAttributesAfterNavigation) {
504   std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
505                                         content::kBrowserPluginMimeType);
506   LoadHTML(html.c_str());
507 
508   ExecuteJavaScript(
509       "document.getElementById('browserplugin').partition = 'storage'");
510   std::string partition_value = ExecuteScriptAndReturnString(
511       "document.getElementById('browserplugin').partition");
512   EXPECT_STREQ("storage", partition_value.c_str());
513 
514   std::string src_value = ExecuteScriptAndReturnString(
515       "document.getElementById('browserplugin').src");
516   EXPECT_STREQ("", src_value.c_str());
517 
518   ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
519   ProcessPendingMessages();
520   {
521     BrowserPluginHostMsg_Attach_Params params;
522     MockBrowserPlugin* browser_plugin =
523         GetCurrentPluginWithAttachParams(&params);
524     ASSERT_TRUE(browser_plugin);
525 
526     EXPECT_STREQ("storage", params.storage_partition_id.c_str());
527     EXPECT_FALSE(params.persist_storage);
528     EXPECT_STREQ("bar", params.src.c_str());
529   }
530 
531   // Setting the partition should throw an exception and the value should not
532   // change.
533   ExecuteJavaScript(
534       "try {"
535       "  document.getElementById('browserplugin').partition = 'someid';"
536       "  document.title = 'success';"
537       "} catch (e) { document.title = e.message; }");
538 
539   std::string title = ExecuteScriptAndReturnString("document.title");
540   EXPECT_STREQ(
541       "The object has already navigated, so its partition cannot be changed.",
542       title.c_str());
543 
544   partition_value = ExecuteScriptAndReturnString(
545       "document.getElementById('browserplugin').partition");
546   EXPECT_STREQ("storage", partition_value.c_str());
547 }
548 
TEST_F(BrowserPluginTest,AutoSizeAttributes)549 TEST_F(BrowserPluginTest, AutoSizeAttributes) {
550   std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
551                                         content::kBrowserPluginMimeType);
552   LoadHTML(html.c_str());
553   const char* kSetAutoSizeParametersAndNavigate =
554     "var browserplugin = document.getElementById('browserplugin');"
555     "browserplugin.autosize = true;"
556     "browserplugin.minwidth = 42;"
557     "browserplugin.minheight = 43;"
558     "browserplugin.maxwidth = 1337;"
559     "browserplugin.maxheight = 1338;"
560     "browserplugin.src = 'foobar';";
561   const char* kDisableAutoSize =
562     "document.getElementById('browserplugin').removeAttribute('autosize');";
563 
564   int instance_id = 0;
565   // Set some autosize parameters before navigating then navigate.
566   // Verify that the BrowserPluginHostMsg_Attach message contains
567   // the correct autosize parameters.
568   ExecuteJavaScript(kSetAutoSizeParametersAndNavigate);
569   ProcessPendingMessages();
570 
571   BrowserPluginHostMsg_Attach_Params params;
572   MockBrowserPlugin* browser_plugin =
573       GetCurrentPluginWithAttachParams(&params);
574   ASSERT_TRUE(browser_plugin);
575 
576   EXPECT_TRUE(params.auto_size_params.enable);
577   EXPECT_EQ(42, params.auto_size_params.min_size.width());
578   EXPECT_EQ(43, params.auto_size_params.min_size.height());
579   EXPECT_EQ(1337, params.auto_size_params.max_size.width());
580   EXPECT_EQ(1338, params.auto_size_params.max_size.height());
581 
582   // Verify that we are waiting for the browser process to grab the new
583   // damage buffer.
584   EXPECT_TRUE(browser_plugin->pending_damage_buffer_.get());
585   // Disable autosize. AutoSize state will not be sent to the guest until
586   // the guest has responded to the last resize request.
587   ExecuteJavaScript(kDisableAutoSize);
588   ProcessPendingMessages();
589 
590   const IPC::Message* auto_size_msg =
591   browser_plugin_manager()->sink().GetUniqueMessageMatching(
592       BrowserPluginHostMsg_SetAutoSize::ID);
593   EXPECT_FALSE(auto_size_msg);
594 
595   // Send the BrowserPlugin an UpdateRect equal to its |max_size| with
596   // the same damage buffer.
597   BrowserPluginMsg_UpdateRect_Params update_rect_params;
598   update_rect_params.damage_buffer_sequence_id =
599       browser_plugin->damage_buffer_sequence_id_;
600   update_rect_params.view_size = gfx::Size(1337, 1338);
601   update_rect_params.scale_factor = 1.0f;
602   update_rect_params.is_resize_ack = true;
603   update_rect_params.needs_ack = true;
604   BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
605   browser_plugin->OnMessageReceived(msg);
606 
607   // Verify that the autosize state has been updated.
608   {
609     const IPC::Message* auto_size_msg =
610     browser_plugin_manager()->sink().GetUniqueMessageMatching(
611         BrowserPluginHostMsg_UpdateRect_ACK::ID);
612     ASSERT_TRUE(auto_size_msg);
613 
614     int instance_id = 0;
615     bool needs_ack = false;
616     BrowserPluginHostMsg_AutoSize_Params auto_size_params;
617     BrowserPluginHostMsg_ResizeGuest_Params resize_params;
618     BrowserPluginHostMsg_UpdateRect_ACK::Read(auto_size_msg,
619                                               &instance_id,
620                                               &needs_ack,
621                                               &auto_size_params,
622                                               &resize_params);
623     EXPECT_FALSE(auto_size_params.enable);
624     // These value are not populated (as an optimization) if autosize is
625     // disabled.
626     EXPECT_EQ(0, auto_size_params.min_size.width());
627     EXPECT_EQ(0, auto_size_params.min_size.height());
628     EXPECT_EQ(0, auto_size_params.max_size.width());
629     EXPECT_EQ(0, auto_size_params.max_size.height());
630   }
631 }
632 
633 }  // namespace content
634