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"
7 #include "base/metrics/histogram.h"
8 #include "base/strings/string_number_conversions.h"
9 #include "content/common/frame_messages.h"
10 #include "content/public/common/content_constants.h"
11 #include "content/public/renderer/render_frame.h"
12 #include "third_party/WebKit/public/web/WebDocument.h"
13 #include "third_party/WebKit/public/web/WebLocalFrame.h"
14 #include "third_party/WebKit/public/web/WebPluginParams.h"
15 #include "third_party/WebKit/public/web/WebView.h"
21 // Initial decision of the peripheral content decision.
22 // These numeric values are used in UMA logs; do not change them.
23 enum PeripheralHeuristicDecision
{
24 HEURISTIC_DECISION_PERIPHERAL
= 0,
25 HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN
= 1,
26 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG
= 2,
27 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED
= 3,
28 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY
= 4,
29 HEURISTIC_DECISION_NUM_ITEMS
32 const char kPeripheralHeuristicHistogram
[] =
33 "Plugin.PowerSaver.PeripheralHeuristic";
35 // Maximum dimensions plugin content may have while still being considered
36 // peripheral content. These match the sizes used by Safari.
37 const int kPeripheralContentMaxWidth
= 400;
38 const int kPeripheralContentMaxHeight
= 300;
40 // Plugin content below this size in height and width is considered "tiny".
41 // Tiny content is never peripheral, as tiny plugins often serve a critical
42 // purpose, and the user often cannot find and click to unthrottle it.
43 const int kPeripheralContentTinySize
= 5;
45 void RecordDecisionMetric(PeripheralHeuristicDecision decision
) {
46 UMA_HISTOGRAM_ENUMERATION(kPeripheralHeuristicHistogram
, decision
,
47 HEURISTIC_DECISION_NUM_ITEMS
);
52 PluginPowerSaverHelper::PeripheralPlugin::PeripheralPlugin(
53 const GURL
& content_origin
,
54 const base::Closure
& unthrottle_callback
)
55 : content_origin(content_origin
), unthrottle_callback(unthrottle_callback
) {
58 PluginPowerSaverHelper::PeripheralPlugin::~PeripheralPlugin() {
61 PluginPowerSaverHelper::PluginPowerSaverHelper(RenderFrame
* render_frame
)
62 : RenderFrameObserver(render_frame
) {
65 PluginPowerSaverHelper::~PluginPowerSaverHelper() {
68 void PluginPowerSaverHelper::DidCommitProvisionalLoad(
69 bool is_new_navigation
,
70 bool is_same_page_navigation
) {
71 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
72 // Only apply to top-level and new page navigation.
73 if (frame
->parent() || is_same_page_navigation
)
74 return; // Not a top-level navigation.
76 origin_whitelist_
.clear();
79 bool PluginPowerSaverHelper::OnMessageReceived(const IPC::Message
& message
) {
81 IPC_BEGIN_MESSAGE_MAP(PluginPowerSaverHelper
, message
)
82 IPC_MESSAGE_HANDLER(FrameMsg_UpdatePluginContentOriginWhitelist
,
83 OnUpdatePluginContentOriginWhitelist
)
84 IPC_MESSAGE_UNHANDLED(handled
= false)
89 void PluginPowerSaverHelper::OnUpdatePluginContentOriginWhitelist(
90 const std::set
<GURL
>& origin_whitelist
) {
91 origin_whitelist_
= origin_whitelist
;
93 // Check throttled plugin instances to see if any can be unthrottled.
94 auto it
= peripheral_plugins_
.begin();
95 while (it
!= peripheral_plugins_
.end()) {
96 if (origin_whitelist
.count(it
->content_origin
)) {
97 it
->unthrottle_callback
.Run();
98 it
= peripheral_plugins_
.erase(it
);
105 void PluginPowerSaverHelper::RegisterPeripheralPlugin(
106 const GURL
& content_origin
,
107 const base::Closure
& unthrottle_callback
) {
108 DCHECK_EQ(content_origin
.GetOrigin(), content_origin
);
109 peripheral_plugins_
.push_back(
110 PeripheralPlugin(content_origin
, unthrottle_callback
));
113 bool PluginPowerSaverHelper::ShouldThrottleContent(
114 const GURL
& content_origin
,
115 const std::string
& plugin_module_name
,
118 bool* cross_origin_main_content
) const {
119 DCHECK_EQ(content_origin
.GetOrigin(), content_origin
);
120 if (cross_origin_main_content
)
121 *cross_origin_main_content
= false;
123 // This feature has only been tested throughly with Flash thus far.
124 if (plugin_module_name
!= content::kFlashPluginName
)
127 if (width
<= 0 || height
<= 0)
130 // TODO(alexmos): Update this to use the origin of the RemoteFrame when 426512
131 // is fixed. For now, case 3 in the class level comment doesn't work in
132 // --site-per-process mode.
133 blink::WebFrame
* main_frame
=
134 render_frame()->GetWebFrame()->view()->mainFrame();
135 if (main_frame
->isWebRemoteFrame()) {
136 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL
);
140 // All same-origin plugin content is essential.
141 GURL main_frame_origin
= GURL(main_frame
->document().url()).GetOrigin();
142 if (content_origin
== main_frame_origin
) {
143 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN
);
147 // Whitelisted plugin origins are also essential.
148 if (origin_whitelist_
.count(content_origin
)) {
149 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED
);
153 // Never mark tiny content as peripheral.
154 if (width
<= kPeripheralContentTinySize
&&
155 height
<= kPeripheralContentTinySize
) {
156 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY
);
160 // Plugin content large in both dimensions are the "main attraction".
161 if (width
>= kPeripheralContentMaxWidth
&&
162 height
>= kPeripheralContentMaxHeight
) {
163 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG
);
164 if (cross_origin_main_content
)
165 *cross_origin_main_content
= true;
169 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL
);
173 void PluginPowerSaverHelper::WhitelistContentOrigin(
174 const GURL
& content_origin
) {
175 DCHECK_EQ(content_origin
.GetOrigin(), content_origin
);
176 if (origin_whitelist_
.insert(content_origin
).second
) {
177 Send(new FrameHostMsg_PluginContentOriginAllowed(
178 render_frame()->GetRoutingID(), content_origin
));
182 } // namespace content