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.
5 #include "content/renderer/browser_plugin/browser_plugin_browsertest.h"
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"
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>";
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'>";
39 const char kHTMLForSourcelessPluginObject[] =
40 "<object id='browserplugin' width='640px' height='480px' type='%s'>";
42 const char kHTMLForPartitionedPluginObject[] =
43 "<object id='browserplugin' width='640px' height='480px'"
44 " src='foo' type='%s' partition='someid'>";
46 const char kHTMLForInvalidPartitionedPluginObject[] =
47 "<object id='browserplugin' width='640px' height='480px'"
48 " type='%s' partition='persist:'>";
50 const char kHTMLForPartitionedPersistedPluginObject[] =
51 "<object id='browserplugin' width='640px' height='480px'"
52 " src='foo' type='%s' partition='persist:someid'>";
54 std::string GetHTMLForBrowserPluginObject() {
55 return base::StringPrintf(kHTMLForBrowserPluginObject,
56 kBrowserPluginMimeType);
61 class TestContentRendererClient : public ContentRendererClient {
63 TestContentRendererClient() : ContentRendererClient() {
65 virtual ~TestContentRendererClient() {
67 virtual bool AllowBrowserPlugin(
68 blink::WebPluginContainer* container) OVERRIDE {
69 // Allow BrowserPlugin for tests.
74 // Test factory for creating test instances of BrowserPluginManager.
75 class TestBrowserPluginManagerFactory : public BrowserPluginManagerFactory {
77 virtual MockBrowserPluginManager* CreateBrowserPluginManager(
78 RenderViewImpl* render_view) OVERRIDE {
79 return new MockBrowserPluginManager(render_view);
83 static TestBrowserPluginManagerFactory* GetInstance() {
84 return Singleton<TestBrowserPluginManagerFactory>::get();
88 TestBrowserPluginManagerFactory() {}
89 virtual ~TestBrowserPluginManagerFactory() {}
93 friend struct DefaultSingletonTraits<TestBrowserPluginManagerFactory>;
95 DISALLOW_COPY_AND_ASSIGN(TestBrowserPluginManagerFactory);
98 BrowserPluginTest::BrowserPluginTest() {}
100 BrowserPluginTest::~BrowserPluginTest() {}
102 void BrowserPluginTest::SetUp() {
103 BrowserPluginManager::set_factory_for_testing(
104 TestBrowserPluginManagerFactory::GetInstance());
105 content::RenderViewTest::SetUp();
108 void BrowserPluginTest::TearDown() {
109 BrowserPluginManager::set_factory_for_testing(
110 TestBrowserPluginManagerFactory::GetInstance());
111 #if defined(LEAK_SANITIZER)
112 // Do this before shutting down V8 in RenderViewTest::TearDown().
113 // http://crbug.com/328552
114 __lsan_do_leak_check();
116 RenderViewTest::TearDown();
119 ContentRendererClient* BrowserPluginTest::CreateContentRendererClient() {
120 return new TestContentRendererClient;
123 std::string BrowserPluginTest::ExecuteScriptAndReturnString(
124 const std::string& script) {
125 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
126 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
127 blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
128 if (value.IsEmpty() || !value->IsString())
129 return std::string();
131 v8::Local<v8::String> v8_str = value->ToString();
132 int length = v8_str->Utf8Length() + 1;
133 scoped_ptr<char[]> str(new char[length]);
134 v8_str->WriteUtf8(str.get(), length);
138 int BrowserPluginTest::ExecuteScriptAndReturnInt(
139 const std::string& script) {
140 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
141 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
142 blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
143 if (value.IsEmpty() || !value->IsInt32())
146 return value->Int32Value();
149 // A return value of false means that a value was not present. The return value
150 // of the script is stored in |result|
151 bool BrowserPluginTest::ExecuteScriptAndReturnBool(
152 const std::string& script, bool* result) {
153 v8::HandleScope handle_scope(v8::Isolate::GetCurrent());
154 v8::Handle<v8::Value> value = GetMainFrame()->executeScriptAndReturnValue(
155 blink::WebScriptSource(blink::WebString::fromUTF8(script.c_str())));
156 if (value.IsEmpty() || !value->IsBoolean())
159 *result = value->BooleanValue();
163 MockBrowserPlugin* BrowserPluginTest::GetCurrentPlugin() {
164 BrowserPluginHostMsg_Attach_Params params;
165 return GetCurrentPluginWithAttachParams(¶ms);
168 MockBrowserPlugin* BrowserPluginTest::GetCurrentPluginWithAttachParams(
169 BrowserPluginHostMsg_Attach_Params* params) {
171 const IPC::Message* msg =
172 browser_plugin_manager()->sink().GetUniqueMessageMatching(
173 BrowserPluginHostMsg_Attach::ID);
177 PickleIterator iter(*msg);
178 if (!iter.ReadInt(&instance_id))
181 if (!IPC::ParamTraits<BrowserPluginHostMsg_Attach_Params>::Read(
185 MockBrowserPlugin* browser_plugin = static_cast<MockBrowserPlugin*>(
186 browser_plugin_manager()->GetBrowserPlugin(instance_id));
188 BrowserPluginMsg_Attach_ACK_Params attach_ack_params;
189 browser_plugin->OnAttachACK(instance_id, attach_ack_params);
191 return browser_plugin;
194 // This test verifies that an initial resize occurs when we instantiate the
196 TEST_F(BrowserPluginTest, InitialResize) {
197 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
198 // Verify that the information in Attach is correct.
199 BrowserPluginHostMsg_Attach_Params params;
200 MockBrowserPlugin* browser_plugin = GetCurrentPluginWithAttachParams(¶ms);
202 EXPECT_EQ(640, params.resize_guest_params.view_rect.width());
203 EXPECT_EQ(480, params.resize_guest_params.view_rect.height());
204 ASSERT_TRUE(browser_plugin);
207 // This test verifies that all attributes (present at the time of writing) are
208 // parsed on initialization. However, this test does minimal checking of
210 TEST_F(BrowserPluginTest, ParseAllAttributes) {
211 std::string html = base::StringPrintf(kHTMLForBrowserPluginWithAllAttributes,
212 kBrowserPluginMimeType);
213 LoadHTML(html.c_str());
215 bool has_value = ExecuteScriptAndReturnBool(
216 "document.getElementById('browserplugin').autosize", &result);
217 EXPECT_TRUE(has_value);
219 int maxHeight = ExecuteScriptAndReturnInt(
220 "document.getElementById('browserplugin').maxheight");
221 EXPECT_EQ(600, maxHeight);
222 int maxWidth = ExecuteScriptAndReturnInt(
223 "document.getElementById('browserplugin').maxwidth");
224 EXPECT_EQ(800, maxWidth);
225 int minHeight = ExecuteScriptAndReturnInt(
226 "document.getElementById('browserplugin').minheight");
227 EXPECT_EQ(240, minHeight);
228 int minWidth = ExecuteScriptAndReturnInt(
229 "document.getElementById('browserplugin').minwidth");
230 EXPECT_EQ(320, minWidth);
231 std::string name = ExecuteScriptAndReturnString(
232 "document.getElementById('browserplugin').name");
233 EXPECT_STREQ("Jim", name.c_str());
234 std::string partition = ExecuteScriptAndReturnString(
235 "document.getElementById('browserplugin').partition");
236 EXPECT_STREQ("someid", partition.c_str());
237 std::string src = ExecuteScriptAndReturnString(
238 "document.getElementById('browserplugin').src");
239 EXPECT_STREQ("foo", src.c_str());
242 // Verify that the src attribute on the browser plugin works as expected.
243 TEST_F(BrowserPluginTest, SrcAttribute) {
244 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
245 // Verify that we're reporting the correct URL to navigate to based on the
248 BrowserPluginHostMsg_Attach_Params params;
249 MockBrowserPlugin* browser_plugin =
250 GetCurrentPluginWithAttachParams(¶ms);
251 ASSERT_TRUE(browser_plugin);
252 EXPECT_EQ("foo", params.src);
255 browser_plugin_manager()->sink().ClearMessages();
256 // Navigate to bar and observe the associated
257 // BrowserPluginHostMsg_NavigateGuest message.
258 // Verify that the src attribute is updated as well.
259 ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
261 // Verify that we do not get a Attach on subsequent navigations.
262 const IPC::Message* create_msg =
263 browser_plugin_manager()->sink().GetUniqueMessageMatching(
264 BrowserPluginHostMsg_Attach::ID);
265 ASSERT_FALSE(create_msg);
267 const IPC::Message* msg =
268 browser_plugin_manager()->sink().GetUniqueMessageMatching(
269 BrowserPluginHostMsg_NavigateGuest::ID);
274 BrowserPluginHostMsg_NavigateGuest::Read(msg, &instance_id, &src);
275 EXPECT_EQ("bar", src);
276 std::string src_value =
277 ExecuteScriptAndReturnString(
278 "document.getElementById('browserplugin').src");
279 EXPECT_EQ("bar", src_value);
283 TEST_F(BrowserPluginTest, ResizeFlowControl) {
284 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
285 MockBrowserPlugin* browser_plugin = GetCurrentPlugin();
286 ASSERT_TRUE(browser_plugin);
287 int instance_id = browser_plugin->guest_instance_id();
288 // Send an UpdateRect to the BrowserPlugin to make sure the browser sees a
289 // resize related (SetAutoSize) message.
291 // We send a stale UpdateRect to the BrowserPlugin.
292 BrowserPluginMsg_UpdateRect_Params update_rect_params;
293 update_rect_params.view_size = gfx::Size(640, 480);
294 update_rect_params.scale_factor = 1.0f;
295 update_rect_params.is_resize_ack = true;
296 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
297 browser_plugin->OnMessageReceived(msg);
300 browser_plugin_manager()->sink().ClearMessages();
302 // Resize the browser plugin three times.
304 ExecuteJavaScript("document.getElementById('browserplugin').width = '641px'");
305 GetMainFrame()->view()->layout();
306 ProcessPendingMessages();
308 ExecuteJavaScript("document.getElementById('browserplugin').width = '642px'");
309 GetMainFrame()->view()->layout();
310 ProcessPendingMessages();
312 ExecuteJavaScript("document.getElementById('browserplugin').width = '643px'");
313 GetMainFrame()->view()->layout();
314 ProcessPendingMessages();
316 // Expect to see one resize messsage in the sink. BrowserPlugin will not issue
317 // subsequent resize requests until the first request is satisfied by the
318 // guest. The rest of the messages could be
319 // BrowserPluginHostMsg_UpdateGeometry msgs.
320 EXPECT_LE(1u, browser_plugin_manager()->sink().message_count());
321 for (size_t i = 0; i < browser_plugin_manager()->sink().message_count();
323 const IPC::Message* msg = browser_plugin_manager()->sink().GetMessageAt(i);
324 if (msg->type() != BrowserPluginHostMsg_ResizeGuest::ID)
325 EXPECT_EQ(msg->type(), BrowserPluginHostMsg_UpdateGeometry::ID);
327 const IPC::Message* msg =
328 browser_plugin_manager()->sink().GetUniqueMessageMatching(
329 BrowserPluginHostMsg_ResizeGuest::ID);
331 BrowserPluginHostMsg_ResizeGuest_Params params;
332 BrowserPluginHostMsg_ResizeGuest::Read(msg, &instance_id, ¶ms);
333 EXPECT_EQ(641, params.view_rect.width());
334 EXPECT_EQ(480, params.view_rect.height());
337 // We send a stale UpdateRect to the BrowserPlugin.
338 BrowserPluginMsg_UpdateRect_Params update_rect_params;
339 update_rect_params.view_size = gfx::Size(641, 480);
340 update_rect_params.scale_factor = 1.0f;
341 update_rect_params.is_resize_ack = true;
342 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
343 browser_plugin->OnMessageReceived(msg);
345 // Send the BrowserPlugin another UpdateRect, but this time with a size
346 // that matches the size of the container.
348 BrowserPluginMsg_UpdateRect_Params update_rect_params;
349 update_rect_params.view_size = gfx::Size(643, 480);
350 update_rect_params.scale_factor = 1.0f;
351 update_rect_params.is_resize_ack = true;
352 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
353 browser_plugin->OnMessageReceived(msg);
357 TEST_F(BrowserPluginTest, RemovePlugin) {
358 LoadHTML(GetHTMLForBrowserPluginObject().c_str());
359 EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
360 BrowserPluginHostMsg_PluginDestroyed::ID));
361 ExecuteJavaScript("x = document.getElementById('browserplugin'); "
362 "x.parentNode.removeChild(x);");
363 ProcessPendingMessages();
364 EXPECT_TRUE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
365 BrowserPluginHostMsg_PluginDestroyed::ID));
368 // This test verifies that PluginDestroyed messages do not get sent from a
369 // BrowserPlugin that has never navigated.
370 TEST_F(BrowserPluginTest, RemovePluginBeforeNavigation) {
371 std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
372 kBrowserPluginMimeType);
373 LoadHTML(html.c_str());
374 EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
375 BrowserPluginHostMsg_PluginDestroyed::ID));
376 ExecuteJavaScript("x = document.getElementById('browserplugin'); "
377 "x.parentNode.removeChild(x);");
378 ProcessPendingMessages();
379 EXPECT_FALSE(browser_plugin_manager()->sink().GetUniqueMessageMatching(
380 BrowserPluginHostMsg_PluginDestroyed::ID));
383 // Verify that the 'partition' attribute on the browser plugin is parsed
385 TEST_F(BrowserPluginTest, PartitionAttribute) {
386 std::string html = base::StringPrintf(kHTMLForPartitionedPluginObject,
387 kBrowserPluginMimeType);
388 LoadHTML(html.c_str());
389 std::string partition_value = ExecuteScriptAndReturnString(
390 "document.getElementById('browserplugin').partition");
391 EXPECT_STREQ("someid", partition_value.c_str());
393 html = base::StringPrintf(kHTMLForPartitionedPersistedPluginObject,
394 kBrowserPluginMimeType);
395 LoadHTML(html.c_str());
396 partition_value = ExecuteScriptAndReturnString(
397 "document.getElementById('browserplugin').partition");
398 EXPECT_STREQ("persist:someid", partition_value.c_str());
400 // Verify that once HTML has defined a source and partition, we cannot change
401 // the partition anymore.
404 " document.getElementById('browserplugin').partition = 'foo';"
405 " document.title = 'success';"
406 "} catch (e) { document.title = e.message; }");
407 std::string title = ExecuteScriptAndReturnString("document.title");
409 "The object has already navigated, so its partition cannot be changed.",
412 // Load a browser tag without 'src' defined.
413 html = base::StringPrintf(kHTMLForSourcelessPluginObject,
414 kBrowserPluginMimeType);
415 LoadHTML(html.c_str());
417 // Ensure we don't parse just "persist:" string and return exception.
420 " document.getElementById('browserplugin').partition = 'persist:';"
421 " document.title = 'success';"
422 "} catch (e) { document.title = e.message; }");
423 title = ExecuteScriptAndReturnString("document.title");
424 EXPECT_STREQ("Invalid partition attribute.", title.c_str());
427 // This test verifies that BrowserPlugin enters an error state when the
428 // partition attribute is invalid.
429 TEST_F(BrowserPluginTest, InvalidPartition) {
430 std::string html = base::StringPrintf(kHTMLForInvalidPartitionedPluginObject,
431 kBrowserPluginMimeType);
432 LoadHTML(html.c_str());
433 // Attempt to navigate with an invalid partition.
437 " document.getElementById('browserplugin').src = 'bar';"
438 " document.title = 'success';"
439 "} catch (e) { document.title = e.message; }");
440 std::string title = ExecuteScriptAndReturnString("document.title");
441 EXPECT_STREQ("Invalid partition attribute.", title.c_str());
442 // Verify that the 'src' attribute has not been updated.
443 EXPECT_EQ("", ExecuteScriptAndReturnString(
444 "document.getElementById('browserplugin').src"));
447 // Verify that the BrowserPlugin accepts changes to its src attribue after
448 // setting the partition to a valid value.
450 "document.getElementById('browserplugin').partition = 'persist:foo'");
451 ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
452 EXPECT_EQ("bar", ExecuteScriptAndReturnString(
453 "document.getElementById('browserplugin').src"));
454 ProcessPendingMessages();
455 // Verify that the BrowserPlugin does not 'deadlock': it can recover from
456 // the partition ID error state.
460 " document.getElementById('browserplugin').partition = 'persist:1337';"
461 " document.title = 'success';"
462 "} catch (e) { document.title = e.message; }");
463 std::string title = ExecuteScriptAndReturnString("document.title");
465 "The object has already navigated, so its partition cannot be changed.",
467 ExecuteJavaScript("document.getElementById('browserplugin').src = '42'");
468 EXPECT_EQ("42", ExecuteScriptAndReturnString(
469 "document.getElementById('browserplugin').src"));
473 // Test to verify that after the first navigation, the partition attribute
474 // cannot be modified.
475 TEST_F(BrowserPluginTest, ImmutableAttributesAfterNavigation) {
476 std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
477 kBrowserPluginMimeType);
478 LoadHTML(html.c_str());
481 "document.getElementById('browserplugin').partition = 'storage'");
482 std::string partition_value = ExecuteScriptAndReturnString(
483 "document.getElementById('browserplugin').partition");
484 EXPECT_STREQ("storage", partition_value.c_str());
486 std::string src_value = ExecuteScriptAndReturnString(
487 "document.getElementById('browserplugin').src");
488 EXPECT_STREQ("", src_value.c_str());
490 ExecuteJavaScript("document.getElementById('browserplugin').src = 'bar'");
491 ProcessPendingMessages();
493 BrowserPluginHostMsg_Attach_Params params;
494 MockBrowserPlugin* browser_plugin =
495 GetCurrentPluginWithAttachParams(¶ms);
496 ASSERT_TRUE(browser_plugin);
498 EXPECT_STREQ("storage", params.storage_partition_id.c_str());
499 EXPECT_FALSE(params.persist_storage);
500 EXPECT_STREQ("bar", params.src.c_str());
503 // Setting the partition should throw an exception and the value should not
507 " document.getElementById('browserplugin').partition = 'someid';"
508 " document.title = 'success';"
509 "} catch (e) { document.title = e.message; }");
511 std::string title = ExecuteScriptAndReturnString("document.title");
513 "The object has already navigated, so its partition cannot be changed.",
516 partition_value = ExecuteScriptAndReturnString(
517 "document.getElementById('browserplugin').partition");
518 EXPECT_STREQ("storage", partition_value.c_str());
521 TEST_F(BrowserPluginTest, AutoSizeAttributes) {
522 std::string html = base::StringPrintf(kHTMLForSourcelessPluginObject,
523 kBrowserPluginMimeType);
524 LoadHTML(html.c_str());
525 const char* kSetAutoSizeParametersAndNavigate =
526 "var browserplugin = document.getElementById('browserplugin');"
527 "browserplugin.autosize = true;"
528 "browserplugin.minwidth = 42;"
529 "browserplugin.minheight = 43;"
530 "browserplugin.maxwidth = 1337;"
531 "browserplugin.maxheight = 1338;"
532 "browserplugin.src = 'foobar';";
533 const char* kDisableAutoSize =
534 "document.getElementById('browserplugin').removeAttribute('autosize');";
537 // Set some autosize parameters before navigating then navigate.
538 // Verify that the BrowserPluginHostMsg_Attach message contains
539 // the correct autosize parameters.
540 ExecuteJavaScript(kSetAutoSizeParametersAndNavigate);
541 ProcessPendingMessages();
543 BrowserPluginHostMsg_Attach_Params params;
544 MockBrowserPlugin* browser_plugin =
545 GetCurrentPluginWithAttachParams(¶ms);
546 ASSERT_TRUE(browser_plugin);
548 EXPECT_TRUE(params.auto_size_params.enable);
549 EXPECT_EQ(42, params.auto_size_params.min_size.width());
550 EXPECT_EQ(43, params.auto_size_params.min_size.height());
551 EXPECT_EQ(1337, params.auto_size_params.max_size.width());
552 EXPECT_EQ(1338, params.auto_size_params.max_size.height());
554 // Disable autosize. AutoSize state will not be sent to the guest until
555 // the guest has responded to the last resize request.
556 ExecuteJavaScript(kDisableAutoSize);
557 ProcessPendingMessages();
559 const IPC::Message* auto_size_msg =
560 browser_plugin_manager()->sink().GetUniqueMessageMatching(
561 BrowserPluginHostMsg_SetAutoSize::ID);
562 EXPECT_FALSE(auto_size_msg);
564 // Send the BrowserPlugin an UpdateRect equal to its |max_size|.
565 BrowserPluginMsg_UpdateRect_Params update_rect_params;
566 update_rect_params.view_size = gfx::Size(1337, 1338);
567 update_rect_params.scale_factor = 1.0f;
568 update_rect_params.is_resize_ack = true;
569 BrowserPluginMsg_UpdateRect msg(instance_id, update_rect_params);
570 browser_plugin->OnMessageReceived(msg);
572 // Verify that the autosize state has been updated.
574 const IPC::Message* auto_size_msg =
575 browser_plugin_manager()->sink().GetUniqueMessageMatching(
576 BrowserPluginHostMsg_SetAutoSize::ID);
577 ASSERT_TRUE(auto_size_msg);
580 BrowserPluginHostMsg_AutoSize_Params auto_size_params;
581 BrowserPluginHostMsg_ResizeGuest_Params resize_params;
582 BrowserPluginHostMsg_SetAutoSize::Read(auto_size_msg,
586 EXPECT_FALSE(auto_size_params.enable);
587 // These value are not populated (as an optimization) if autosize is
589 EXPECT_EQ(0, auto_size_params.min_size.width());
590 EXPECT_EQ(0, auto_size_params.min_size.height());
591 EXPECT_EQ(0, auto_size_params.max_size.width());
592 EXPECT_EQ(0, auto_size_params.max_size.height());
596 } // namespace content