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/pepper_plugin_instance_throttler.h"
7 #include "base/command_line.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/metrics/histogram.h"
10 #include "base/metrics/sparse_histogram.h"
11 #include "base/time/time.h"
12 #include "content/public/common/content_constants.h"
13 #include "content/public/common/content_switches.h"
14 #include "content/renderer/pepper/plugin_power_saver_helper_impl.h"
15 #include "content/renderer/render_thread_impl.h"
16 #include "third_party/WebKit/public/web/WebInputEvent.h"
17 #include "ui/gfx/color_utils.h"
23 static const int kInfiniteRatio
= 99999;
25 #define UMA_HISTOGRAM_ASPECT_RATIO(name, width, height) \
26 UMA_HISTOGRAM_SPARSE_SLOWLY( \
27 name, (height) ? ((width)*100) / (height) : kInfiniteRatio);
29 // Histogram tracking prevalence of tiny Flash instances. Units in pixels.
30 enum PluginFlashTinyContentSize
{
31 TINY_CONTENT_SIZE_1_1
= 0,
32 TINY_CONTENT_SIZE_5_5
= 1,
33 TINY_CONTENT_SIZE_10_10
= 2,
34 TINY_CONTENT_SIZE_LARGE
= 3,
35 TINY_CONTENT_SIZE_NUM_ITEMS
38 // How the throttled power saver is unthrottled, if ever.
39 // These numeric values are used in UMA logs; do not change them.
40 enum PowerSaverUnthrottleMethod
{
41 UNTHROTTLE_METHOD_NEVER
= 0,
42 UNTHROTTLE_METHOD_BY_CLICK
= 1,
43 UNTHROTTLE_METHOD_BY_WHITELIST
= 2,
44 UNTHROTTLE_METHOD_NUM_ITEMS
47 const char kFlashClickSizeAspectRatioHistogram
[] =
48 "Plugin.Flash.ClickSize.AspectRatio";
49 const char kFlashClickSizeHeightHistogram
[] = "Plugin.Flash.ClickSize.Height";
50 const char kFlashClickSizeWidthHistogram
[] = "Plugin.Flash.ClickSize.Width";
51 const char kFlashTinyContentSizeHistogram
[] = "Plugin.Flash.TinyContentSize";
52 const char kPowerSaverUnthrottleHistogram
[] = "Plugin.PowerSaver.Unthrottle";
54 // Record size metrics for all Flash instances.
55 void RecordFlashSizeMetric(int width
, int height
) {
56 PluginFlashTinyContentSize size
= TINY_CONTENT_SIZE_LARGE
;
58 if (width
<= 1 && height
<= 1)
59 size
= TINY_CONTENT_SIZE_1_1
;
60 else if (width
<= 5 && height
<= 5)
61 size
= TINY_CONTENT_SIZE_5_5
;
62 else if (width
<= 10 && height
<= 10)
63 size
= TINY_CONTENT_SIZE_10_10
;
65 UMA_HISTOGRAM_ENUMERATION(kFlashTinyContentSizeHistogram
, size
,
66 TINY_CONTENT_SIZE_NUM_ITEMS
);
69 void RecordUnthrottleMethodMetric(PowerSaverUnthrottleMethod method
) {
70 UMA_HISTOGRAM_ENUMERATION(kPowerSaverUnthrottleHistogram
, method
,
71 UNTHROTTLE_METHOD_NUM_ITEMS
);
74 // Records size metrics for Flash instances that are clicked.
75 void RecordFlashClickSizeMetric(int width
, int height
) {
76 base::HistogramBase
* width_histogram
= base::LinearHistogram::FactoryGet(
77 kFlashClickSizeWidthHistogram
,
80 100, // number of buckets.
81 base::HistogramBase::kUmaTargetedHistogramFlag
);
82 width_histogram
->Add(width
);
84 base::HistogramBase
* height_histogram
= base::LinearHistogram::FactoryGet(
85 kFlashClickSizeHeightHistogram
,
87 400, // maximum height
88 100, // number of buckets.
89 base::HistogramBase::kUmaTargetedHistogramFlag
);
90 height_histogram
->Add(height
);
92 UMA_HISTOGRAM_ASPECT_RATIO(kFlashClickSizeAspectRatioHistogram
, width
,
96 // When we give up waiting for a suitable preview frame, and simply suspend
97 // the plugin where it's at. In milliseconds.
98 const int kThrottleTimeout
= 5000;
100 // Threshold for 'boring' score to accept a frame as good enough to be a
101 // representative keyframe. Units are the ratio of all pixels that are within
102 // the most common luma bin. The same threshold is used for history thumbnails.
103 const double kAcceptableFrameMaximumBoringness
= 0.94;
105 const int kMinimumConsecutiveInterestingFrames
= 4;
109 PepperPluginInstanceThrottler::PepperPluginInstanceThrottler(
110 PluginPowerSaverHelperImpl
* power_saver_helper
,
111 const blink::WebRect
& bounds
,
112 const std::string
& module_name
,
113 const GURL
& plugin_url
,
114 const base::Closure
& throttle_change_callback
)
116 throttle_change_callback_(throttle_change_callback
),
117 is_flash_plugin_(module_name
== kFlashPluginName
),
118 needs_representative_keyframe_(false),
119 consecutive_interesting_frames_(0),
120 has_been_clicked_(false),
121 power_saver_enabled_(false),
122 is_peripheral_content_(false),
123 plugin_throttled_(false),
124 weak_factory_(this) {
125 GURL content_origin
= plugin_url
.GetOrigin();
127 if (is_flash_plugin_
&& RenderThread::Get()) {
128 RenderThread::Get()->RecordAction(
129 base::UserMetricsAction("Flash.PluginInstanceCreated"));
130 RecordFlashSizeMetric(bounds
.width
, bounds
.height
);
133 bool is_main_attraction
= false;
134 is_peripheral_content_
=
136 power_saver_helper
->ShouldThrottleContent(
137 content_origin
, bounds
.width
, bounds
.height
, &is_main_attraction
);
139 power_saver_enabled_
= is_peripheral_content_
&&
140 base::CommandLine::ForCurrentProcess()->HasSwitch(
141 switches::kEnablePluginPowerSaver
);
143 if (is_peripheral_content_
) {
144 // To collect UMAs, register peripheral content even if we don't throttle.
145 power_saver_helper
->RegisterPeripheralPlugin(
146 content_origin
, base::Bind(&PepperPluginInstanceThrottler::
147 DisablePowerSaverByRetroactiveWhitelist
,
148 weak_factory_
.GetWeakPtr()));
150 if (power_saver_enabled_
) {
151 needs_representative_keyframe_
= true;
152 base::MessageLoop::current()->PostDelayedTask(
154 base::Bind(&PepperPluginInstanceThrottler::SetPluginThrottled
,
155 weak_factory_
.GetWeakPtr(), true /* throttled */),
156 base::TimeDelta::FromMilliseconds(kThrottleTimeout
));
158 } else if (is_main_attraction
) {
159 power_saver_helper
->WhitelistContentOrigin(content_origin
);
163 PepperPluginInstanceThrottler::~PepperPluginInstanceThrottler() {
166 void PepperPluginInstanceThrottler::OnImageFlush(const SkBitmap
* bitmap
) {
167 if (!needs_representative_keyframe_
|| !bitmap
)
170 double boring_score
= color_utils::CalculateBoringScore(*bitmap
);
171 if (boring_score
<= kAcceptableFrameMaximumBoringness
)
172 ++consecutive_interesting_frames_
;
174 consecutive_interesting_frames_
= 0;
176 if (consecutive_interesting_frames_
>= kMinimumConsecutiveInterestingFrames
)
177 SetPluginThrottled(true);
180 bool PepperPluginInstanceThrottler::ConsumeInputEvent(
181 const blink::WebInputEvent
& event
) {
182 if (!has_been_clicked_
&& is_flash_plugin_
&&
183 event
.type
== blink::WebInputEvent::MouseDown
) {
184 has_been_clicked_
= true;
185 RecordFlashClickSizeMetric(bounds_
.width
, bounds_
.height
);
188 if (event
.type
== blink::WebInputEvent::MouseUp
&& is_peripheral_content_
) {
189 is_peripheral_content_
= false;
190 power_saver_enabled_
= false;
192 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_BY_CLICK
);
194 if (plugin_throttled_
) {
195 SetPluginThrottled(false /* throttled */);
200 return plugin_throttled_
;
203 void PepperPluginInstanceThrottler::SetPluginThrottled(bool throttled
) {
204 // Do not throttle if we've already disabled power saver.
205 if (!power_saver_enabled_
&& throttled
)
208 // Once we change the throttle state, we will never need the snapshot again.
209 needs_representative_keyframe_
= false;
211 plugin_throttled_
= throttled
;
212 throttle_change_callback_
.Run();
215 void PepperPluginInstanceThrottler::DisablePowerSaverByRetroactiveWhitelist() {
216 if (!is_peripheral_content_
)
219 is_peripheral_content_
= false;
220 power_saver_enabled_
= false;
221 SetPluginThrottled(false);
223 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_BY_WHITELIST
);
226 } // namespace content