1 // Copyright 2015 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 "base/command_line.h"
6 #include "base/strings/string_piece.h"
7 #include "base/strings/stringprintf.h"
8 #include "chrome/browser/ui/browser.h"
9 #include "chrome/browser/ui/tabs/tab_strip_model.h"
10 #include "chrome/common/chrome_switches.h"
11 #include "chrome/test/base/in_process_browser_test.h"
12 #include "chrome/test/base/ui_test_utils.h"
13 #include "components/ui/zoom/zoom_controller.h"
14 #include "content/public/common/content_switches.h"
15 #include "content/public/test/browser_test_utils.h"
16 #include "content/public/test/ppapi_test_utils.h"
17 #include "net/test/embedded_test_server/embedded_test_server.h"
18 #include "net/test/embedded_test_server/http_request.h"
19 #include "net/test/embedded_test_server/http_response.h"
20 #include "ppapi/shared_impl/ppapi_switches.h"
21 #include "third_party/WebKit/public/web/WebInputEvent.h"
22 #include "ui/base/window_open_disposition.h"
23 #include "ui/gfx/geometry/point.h"
27 std::string
RunTestScript(base::StringPiece test_script
,
28 content::WebContents
* contents
,
29 const std::string
& element_id
) {
30 std::string script
= base::StringPrintf(
31 "var plugin = window.document.getElementById('%s');"
32 "if (plugin === undefined ||"
33 " (plugin.nodeName !== 'OBJECT' && plugin.nodeName !== 'EMBED')) {"
34 " window.domAutomationController.send('error');"
38 element_id
.c_str(), test_script
.data());
41 content::ExecuteScriptAndExtractString(contents
, script
, &result
));
45 // This also tests that we have JavaScript access to the underlying plugin.
46 bool PluginLoaded(content::WebContents
* contents
, const char* element_id
) {
47 std::string result
= RunTestScript(
48 "if (plugin.postMessage === undefined) {"
49 " window.domAutomationController.send('poster_only');"
51 " window.domAutomationController.send('plugin_loaded');"
53 contents
, element_id
);
54 EXPECT_NE("error", result
);
55 return result
== "plugin_loaded";
58 // Also waits for the placeholder UI overlay to finish loading.
59 void VerifyPluginIsThrottled(content::WebContents
* contents
,
60 const char* element_id
) {
61 std::string result
= RunTestScript(
62 "function handleEvent(event) {"
63 " if (event.data.isPeripheral && event.data.isThrottled && "
64 " event.data.isHiddenForPlaceholder) {"
65 " window.domAutomationController.send('throttled');"
66 " plugin.removeEventListener('message', handleEvent);"
69 "plugin.addEventListener('message', handleEvent);"
70 "if (plugin.postMessage !== undefined) {"
71 " plugin.postMessage('getPowerSaverStatus');"
73 contents
, element_id
);
74 EXPECT_EQ("throttled", result
);
76 // Page should continue to have JavaScript access to all throttled plugins.
77 EXPECT_TRUE(PluginLoaded(contents
, element_id
));
80 void VerifyPluginMarkedEssential(content::WebContents
* contents
,
81 const char* element_id
) {
82 std::string result
= RunTestScript(
83 "function handleEvent(event) {"
84 " if (event.data.isPeripheral === false) {"
85 " window.domAutomationController.send('essential');"
86 " plugin.removeEventListener('message', handleEvent);"
89 "plugin.addEventListener('message', handleEvent);"
90 "if (plugin.postMessage !== undefined) {"
91 " plugin.postMessage('getPowerSaverStatus');"
93 contents
, element_id
);
94 EXPECT_EQ("essential", result
);
95 EXPECT_TRUE(PluginLoaded(contents
, element_id
));
98 scoped_ptr
<net::test_server::HttpResponse
> RespondWithHTML(
99 const std::string
& html
,
100 const net::test_server::HttpRequest
& request
) {
101 scoped_ptr
<net::test_server::BasicHttpResponse
> response(
102 new net::test_server::BasicHttpResponse());
103 response
->set_content_type("text/html");
104 response
->set_content(html
);
105 return response
.Pass();
110 class PluginPowerSaverBrowserTest
: public InProcessBrowserTest
{
112 void SetUpOnMainThread() override
{
113 InProcessBrowserTest::SetUpOnMainThread();
114 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady());
117 void SetUpCommandLine(base::CommandLine
* command_line
) override
{
118 command_line
->AppendSwitch(switches::kEnablePluginPowerSaver
);
119 command_line
->AppendSwitch(switches::kEnablePepperTesting
);
120 command_line
->AppendSwitch(switches::kEnablePluginPlaceholderTesting
);
121 command_line
->AppendSwitchASCII(
122 switches::kOverridePluginPowerSaverForTesting
, "ignore-list");
124 ASSERT_TRUE(ppapi::RegisterPowerSaverTestPlugin(command_line
));
128 void LoadHTML(const std::string
& html
) {
129 ASSERT_TRUE(embedded_test_server()->Started());
130 embedded_test_server()->RegisterRequestHandler(
131 base::Bind(&RespondWithHTML
, html
));
132 ui_test_utils::NavigateToURL(browser(), embedded_test_server()->base_url());
133 EXPECT_TRUE(content::WaitForRenderFrameReady(
134 GetActiveWebContents()->GetMainFrame()));
137 content::WebContents
* GetActiveWebContents() {
138 return browser()->tab_strip_model()->GetActiveWebContents();
141 // This sends a simulated click at |point| and waits for test plugin to send
142 // a status message indicating that it is essential. The test plugin sends a
143 // status message during:
144 // - Plugin creation, to handle a plugin freshly created from a poster.
145 // - Peripheral status change.
146 // - In response to the explicit 'getPowerSaverStatus' request, in case the
147 // test has missed the above two events.
148 void SimulateClickAndAwaitMarkedEssential(const char* element_id
,
149 const gfx::Point
& point
) {
150 // Waits for the placeholder to be ready to be clicked first.
151 std::string result
= RunTestScript(
152 "function handleEvent(event) {"
153 " if (event.data === 'placeholderLoaded') {"
154 " window.domAutomationController.send('ready');"
155 " plugin.removeEventListener('message', handleEvent);"
158 "plugin.addEventListener('message', handleEvent);"
159 "if (plugin.hasAttribute('placeholderLoaded')) {"
160 " window.domAutomationController.send('ready');"
161 " plugin.removeEventListener('message', handleEvent);"
163 GetActiveWebContents(), element_id
);
164 ASSERT_EQ("ready", result
);
166 content::SimulateMouseClickAt(GetActiveWebContents(), 0 /* modifiers */,
167 blink::WebMouseEvent::ButtonLeft
, point
);
169 VerifyPluginMarkedEssential(GetActiveWebContents(), element_id
);
173 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, SmallSameOrigin
) {
175 "<object id='plugin' data='fake.swf' "
176 " type='application/x-ppapi-tests' width='400' height='100'>"
178 VerifyPluginMarkedEssential(GetActiveWebContents(), "plugin");
181 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, SmallCrossOrigin
) {
183 "<object id='plugin' data='http://otherorigin.com/fake.swf' "
184 " type='application/x-ppapi-tests' width='400' height='100'>"
186 VerifyPluginIsThrottled(GetActiveWebContents(), "plugin");
188 SimulateClickAndAwaitMarkedEssential("plugin", gfx::Point(50, 50));
191 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, LargeCrossOrigin
) {
193 "<object id='large' data='http://otherorigin.com/fake.swf' "
194 " type='application/x-ppapi-tests' width='400' height='500'>"
196 "<object id='medium_16_9' data='http://otherorigin.com/fake.swf' "
197 " type='application/x-ppapi-tests' width='480' height='270'>"
199 VerifyPluginMarkedEssential(GetActiveWebContents(), "large");
200 VerifyPluginMarkedEssential(GetActiveWebContents(), "medium_16_9");
203 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
,
204 LargePluginsPeripheralWhenPosterSpecified
) {
206 "<object id='plugin_src' type='application/x-ppapi-tests' "
207 " width='400' height='500' poster='snapshot1x.png'></object>"
208 "<object id='plugin_srcset' type='application/x-ppapi-tests' "
209 " width='400' height='500' "
210 " poster='snapshot1x.png 1x, snapshot2x.png 2x'></object>"
211 "<object id='plugin_legacy_syntax' type='application/x-ppapi-tests' "
212 " width='400' height='500'>"
213 " <param name='poster' value='snapshot1x.png 1x, snapshot2x.png 2x'>"
215 "<embed id='plugin_embed_src' type='application/x-ppapi-tests' "
216 " width='400' height='500' poster='snapshot1x.png'></embed>"
217 "<embed id='plugin_embed_srcset' type='application/x-ppapi-tests' "
218 " width='400' height='500'"
219 " poster='snapshot1x.png 1x, snapshot2x.png 2x'></embed>");
221 EXPECT_FALSE(PluginLoaded(GetActiveWebContents(), "plugin_src"));
222 EXPECT_FALSE(PluginLoaded(GetActiveWebContents(), "plugin_srcset"));
223 EXPECT_FALSE(PluginLoaded(GetActiveWebContents(), "plugin_legacy_syntax"));
224 EXPECT_FALSE(PluginLoaded(GetActiveWebContents(), "plugin_embed_src"));
225 EXPECT_FALSE(PluginLoaded(GetActiveWebContents(), "plugin_embed_srcset"));
228 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
,
229 PluginMarkedEssentialAfterPosterClicked
) {
231 "<object id='plugin' type='application/x-ppapi-tests' "
232 " width='400' height='100' poster='snapshot1x.png'></object>");
233 EXPECT_FALSE(PluginLoaded(GetActiveWebContents(), "plugin"));
235 SimulateClickAndAwaitMarkedEssential("plugin", gfx::Point(50, 50));
238 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, OriginWhitelisting
) {
240 "<object id='plugin1' data='http://otherorigin.com/fake1.swf' "
241 " type='application/x-ppapi-tests' width='400' height='100'></object>"
242 "<object id='plugin2' data='http://otherorigin.com/fake2.swf' "
243 " type='application/x-ppapi-tests' width='400' height='500'>"
245 VerifyPluginMarkedEssential(GetActiveWebContents(), "plugin1");
246 VerifyPluginMarkedEssential(GetActiveWebContents(), "plugin2");
249 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, LargeCrossOriginObscured
) {
251 "<div id='container' "
252 " style='width: 400px; height: 100px; overflow: hidden;'>"
253 " <object id='plugin' data='http://otherorigin.com/fake.swf' "
254 " type='application/x-ppapi-tests' width='400' height='500'>"
257 VerifyPluginIsThrottled(GetActiveWebContents(), "plugin");
259 // Test that's unthrottled if it is unobscured.
261 "var container = window.document.getElementById('container');"
262 "container.setAttribute('style', 'width: 400px; height: 400px;');";
263 ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), script
));
264 VerifyPluginMarkedEssential(GetActiveWebContents(), "plugin");
267 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, ExpandingSmallPlugin
) {
269 "<object id='plugin' data='http://otherorigin.com/fake.swf' "
270 " type='application/x-ppapi-tests' width='400' height='80'></object>");
271 VerifyPluginIsThrottled(GetActiveWebContents(), "plugin");
273 std::string script
= "window.document.getElementById('plugin').height = 400;";
274 ASSERT_TRUE(content::ExecuteScript(GetActiveWebContents(), script
));
275 VerifyPluginMarkedEssential(GetActiveWebContents(), "plugin");
278 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, BackgroundTabPlugins
) {
280 "<object id='same_origin' data='fake.swf' "
281 " type='application/x-ppapi-tests'></object>"
282 "<object id='small_cross_origin' data='http://otherorigin.com/fake1.swf' "
283 " type='application/x-ppapi-tests' width='400' height='100'></object>";
284 embedded_test_server()->RegisterRequestHandler(
285 base::Bind(&RespondWithHTML
, html
));
286 ui_test_utils::NavigateToURLWithDisposition(
287 browser(), embedded_test_server()->base_url(), NEW_BACKGROUND_TAB
,
288 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION
);
290 ASSERT_EQ(2, browser()->tab_strip_model()->count());
291 content::WebContents
* background_contents
=
292 browser()->tab_strip_model()->GetWebContentsAt(1);
294 content::WaitForRenderFrameReady(background_contents
->GetMainFrame()));
296 EXPECT_FALSE(PluginLoaded(background_contents
, "same_origin"));
297 EXPECT_FALSE(PluginLoaded(background_contents
, "small_cross_origin"));
299 browser()->tab_strip_model()->SelectNextTab();
300 EXPECT_EQ(background_contents
, GetActiveWebContents());
302 VerifyPluginMarkedEssential(background_contents
, "same_origin");
303 VerifyPluginIsThrottled(background_contents
, "small_cross_origin");
306 IN_PROC_BROWSER_TEST_F(PluginPowerSaverBrowserTest
, ZoomIndependent
) {
307 ui_zoom::ZoomController::FromWebContents(GetActiveWebContents())
310 "<object id='plugin' data='http://otherorigin.com/fake.swf' "
311 " type='application/x-ppapi-tests' width='400' height='200'>"
313 VerifyPluginIsThrottled(GetActiveWebContents(), "plugin");