1 // Copyright 2014 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/pepper/plugin_power_saver_helper.h"
9 #include "base/command_line.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "content/common/frame_messages.h"
13 #include "content/public/common/content_constants.h"
14 #include "content/public/common/content_switches.h"
15 #include "content/public/renderer/plugin_instance_throttler.h"
16 #include "content/public/renderer/render_frame.h"
17 #include "ppapi/shared_impl/ppapi_constants.h"
18 #include "third_party/WebKit/public/web/WebDocument.h"
19 #include "third_party/WebKit/public/web/WebLocalFrame.h"
20 #include "third_party/WebKit/public/web/WebPluginParams.h"
21 #include "third_party/WebKit/public/web/WebView.h"
27 // Initial decision of the peripheral content decision.
28 // These numeric values are used in UMA logs; do not change them.
29 enum PeripheralHeuristicDecision
{
30 HEURISTIC_DECISION_PERIPHERAL
= 0,
31 HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN
= 1,
32 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG
= 2,
33 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED
= 3,
34 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY
= 4,
35 HEURISTIC_DECISION_NUM_ITEMS
38 const char kPeripheralHeuristicHistogram
[] =
39 "Plugin.PowerSaver.PeripheralHeuristic";
41 // Plugin content below this size in height and width is considered "tiny".
42 // Tiny content is never peripheral, as tiny plugins often serve a critical
43 // purpose, and the user often cannot find and click to unthrottle it.
44 const int kPeripheralContentTinySize
= 5;
46 void RecordDecisionMetric(PeripheralHeuristicDecision decision
) {
47 UMA_HISTOGRAM_ENUMERATION(kPeripheralHeuristicHistogram
, decision
,
48 HEURISTIC_DECISION_NUM_ITEMS
);
53 PluginPowerSaverHelper::PeripheralPlugin::PeripheralPlugin(
54 const GURL
& content_origin
,
55 const base::Closure
& unthrottle_callback
)
56 : content_origin(content_origin
), unthrottle_callback(unthrottle_callback
) {
59 PluginPowerSaverHelper::PeripheralPlugin::~PeripheralPlugin() {
62 PluginPowerSaverHelper::PluginPowerSaverHelper(RenderFrame
* render_frame
)
63 : RenderFrameObserver(render_frame
)
64 , override_for_testing_(Normal
) {
65 base::CommandLine
& command_line
= *base::CommandLine::ForCurrentProcess();
66 std::string override_for_testing
= command_line
.GetSwitchValueASCII(
67 switches::kOverridePluginPowerSaverForTesting
);
68 if (override_for_testing
== "never")
69 override_for_testing_
= Never
;
70 else if (override_for_testing
== "ignore-list")
71 override_for_testing_
= IgnoreList
;
72 else if (override_for_testing
== "always")
73 override_for_testing_
= Always
;
76 PluginPowerSaverHelper::~PluginPowerSaverHelper() {
79 void PluginPowerSaverHelper::DidCommitProvisionalLoad(
80 bool is_new_navigation
,
81 bool is_same_page_navigation
) {
82 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
83 // Only apply to top-level and new page navigation.
84 if (frame
->parent() || is_same_page_navigation
)
85 return; // Not a top-level navigation.
87 origin_whitelist_
.clear();
90 bool PluginPowerSaverHelper::OnMessageReceived(const IPC::Message
& message
) {
92 IPC_BEGIN_MESSAGE_MAP(PluginPowerSaverHelper
, message
)
93 IPC_MESSAGE_HANDLER(FrameMsg_UpdatePluginContentOriginWhitelist
,
94 OnUpdatePluginContentOriginWhitelist
)
95 IPC_MESSAGE_UNHANDLED(handled
= false)
100 void PluginPowerSaverHelper::OnUpdatePluginContentOriginWhitelist(
101 const std::set
<GURL
>& origin_whitelist
) {
102 origin_whitelist_
= origin_whitelist
;
104 // Check throttled plugin instances to see if any can be unthrottled.
105 auto it
= peripheral_plugins_
.begin();
106 while (it
!= peripheral_plugins_
.end()) {
107 if (origin_whitelist
.count(it
->content_origin
)) {
108 it
->unthrottle_callback
.Run();
109 it
= peripheral_plugins_
.erase(it
);
116 void PluginPowerSaverHelper::RegisterPeripheralPlugin(
117 const GURL
& content_origin
,
118 const base::Closure
& unthrottle_callback
) {
119 DCHECK_EQ(content_origin
.GetOrigin(), content_origin
);
120 peripheral_plugins_
.push_back(
121 PeripheralPlugin(content_origin
, unthrottle_callback
));
124 bool PluginPowerSaverHelper::ShouldThrottleContent(
125 const GURL
& content_origin
,
126 const std::string
& plugin_module_name
,
129 bool* cross_origin_main_content
) const {
130 DCHECK_EQ(content_origin
.GetOrigin(), content_origin
);
131 if (cross_origin_main_content
)
132 *cross_origin_main_content
= false;
134 // This feature has only been tested throughly with Flash thus far.
135 // It is also enabled for the Power Saver test plugin for browser tests.
136 if (override_for_testing_
== Always
) {
138 } else if (override_for_testing_
== Never
) {
140 } else if (override_for_testing_
== Normal
&&
141 plugin_module_name
!= content::kFlashPluginName
) {
145 if (width
<= 0 || height
<= 0)
148 // TODO(alexmos): Update this to use the origin of the RemoteFrame when 426512
149 // is fixed. For now, case 3 in the class level comment doesn't work in
150 // --site-per-process mode.
151 blink::WebFrame
* main_frame
=
152 render_frame()->GetWebFrame()->view()->mainFrame();
153 if (main_frame
->isWebRemoteFrame()) {
154 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL
);
158 // All same-origin plugin content is essential.
159 GURL main_frame_origin
= GURL(main_frame
->document().url()).GetOrigin();
160 if (content_origin
== main_frame_origin
) {
161 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN
);
165 // Whitelisted plugin origins are also essential.
166 if (origin_whitelist_
.count(content_origin
)) {
167 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED
);
171 // Never mark tiny content as peripheral.
172 if (width
<= kPeripheralContentTinySize
&&
173 height
<= kPeripheralContentTinySize
) {
174 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY
);
178 // Plugin content large in both dimensions are the "main attraction".
179 if (PluginInstanceThrottler::IsLargeContent(width
, height
)) {
180 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG
);
181 if (cross_origin_main_content
)
182 *cross_origin_main_content
= true;
186 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL
);
190 void PluginPowerSaverHelper::WhitelistContentOrigin(
191 const GURL
& content_origin
) {
192 DCHECK_EQ(content_origin
.GetOrigin(), content_origin
);
193 if (origin_whitelist_
.insert(content_origin
).second
) {
194 Send(new FrameHostMsg_PluginContentOriginAllowed(
195 render_frame()->GetRoutingID(), content_origin
));
199 } // namespace content