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_instance_throttler_impl.h"
7 #include "base/metrics/histogram.h"
8 #include "base/time/time.h"
9 #include "content/public/common/content_constants.h"
10 #include "content/public/renderer/render_thread.h"
11 #include "content/renderer/render_frame_impl.h"
12 #include "third_party/WebKit/public/platform/WebRect.h"
13 #include "third_party/WebKit/public/web/WebInputEvent.h"
14 #include "third_party/WebKit/public/web/WebPluginParams.h"
15 #include "ui/gfx/color_utils.h"
22 // Threshold for 'boring' score to accept a frame as good enough to be a
23 // representative keyframe. Units are the ratio of all pixels that are within
24 // the most common luma bin. The same threshold is used for history thumbnails.
25 const double kAcceptableFrameMaximumBoringness
= 0.94;
27 const int kMinimumConsecutiveInterestingFrames
= 4;
32 const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine
= 150;
35 scoped_ptr
<PluginInstanceThrottler
> PluginInstanceThrottler::Create(
36 bool power_saver_enabled
) {
37 return make_scoped_ptr(new PluginInstanceThrottlerImpl(power_saver_enabled
));
41 void PluginInstanceThrottler::RecordUnthrottleMethodMetric(
42 PluginInstanceThrottlerImpl::PowerSaverUnthrottleMethod method
) {
43 UMA_HISTOGRAM_ENUMERATION(
44 "Plugin.PowerSaver.Unthrottle", method
,
45 PluginInstanceThrottler::UNTHROTTLE_METHOD_NUM_ITEMS
);
48 PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl(
49 bool power_saver_enabled
)
50 : state_(power_saver_enabled
? THROTTLER_STATE_AWAITING_KEYFRAME
51 : THROTTLER_STATE_POWER_SAVER_DISABLED
),
52 is_hidden_for_placeholder_(false),
54 consecutive_interesting_frames_(0),
59 PluginInstanceThrottlerImpl::~PluginInstanceThrottlerImpl() {
60 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnThrottlerDestroyed());
61 if (state_
!= THROTTLER_STATE_MARKED_ESSENTIAL
)
62 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_NEVER
);
65 void PluginInstanceThrottlerImpl::AddObserver(Observer
* observer
) {
66 observer_list_
.AddObserver(observer
);
69 void PluginInstanceThrottlerImpl::RemoveObserver(Observer
* observer
) {
70 observer_list_
.RemoveObserver(observer
);
73 bool PluginInstanceThrottlerImpl::IsThrottled() const {
74 return state_
== THROTTLER_STATE_PLUGIN_THROTTLED
;
77 bool PluginInstanceThrottlerImpl::IsHiddenForPlaceholder() const {
78 return is_hidden_for_placeholder_
;
81 void PluginInstanceThrottlerImpl::MarkPluginEssential(
82 PowerSaverUnthrottleMethod method
) {
83 if (state_
== THROTTLER_STATE_MARKED_ESSENTIAL
)
86 bool was_throttled
= IsThrottled();
87 state_
= THROTTLER_STATE_MARKED_ESSENTIAL
;
88 RecordUnthrottleMethodMetric(method
);
91 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnThrottleStateChange());
94 void PluginInstanceThrottlerImpl::SetHiddenForPlaceholder(bool hidden
) {
95 is_hidden_for_placeholder_
= hidden
;
96 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnHiddenForPlaceholder(hidden
));
99 blink::WebPlugin
* PluginInstanceThrottlerImpl::GetWebPlugin() const {
104 void PluginInstanceThrottlerImpl::SetWebPlugin(blink::WebPlugin
* web_plugin
) {
105 DCHECK(!web_plugin_
);
106 web_plugin_
= web_plugin
;
109 void PluginInstanceThrottlerImpl::Initialize(
110 RenderFrameImpl
* frame
,
111 const GURL
& content_origin
,
112 const std::string
& plugin_module_name
,
113 const blink::WebRect
& bounds
) {
114 // |frame| may be nullptr in tests.
116 PluginPowerSaverHelper
* helper
= frame
->plugin_power_saver_helper();
117 bool cross_origin_main_content
= false;
118 if (!helper
->ShouldThrottleContent(content_origin
, plugin_module_name
,
119 bounds
.width
, bounds
.height
,
120 &cross_origin_main_content
)) {
121 state_
= THROTTLER_STATE_MARKED_ESSENTIAL
;
123 if (cross_origin_main_content
)
124 helper
->WhitelistContentOrigin(content_origin
);
129 // To collect UMAs, register peripheral content even if power saver mode
131 helper
->RegisterPeripheralPlugin(
133 base::Bind(&PluginInstanceThrottlerImpl::MarkPluginEssential
,
134 weak_factory_
.GetWeakPtr(), UNTHROTTLE_METHOD_BY_WHITELIST
));
138 void PluginInstanceThrottlerImpl::OnImageFlush(const SkBitmap
* bitmap
) {
139 DCHECK(needs_representative_keyframe());
145 double boring_score
= color_utils::CalculateBoringScore(*bitmap
);
146 if (boring_score
<= kAcceptableFrameMaximumBoringness
)
147 ++consecutive_interesting_frames_
;
149 consecutive_interesting_frames_
= 0;
151 if (frames_examined_
>= kMaximumFramesToExamine
||
152 consecutive_interesting_frames_
>= kMinimumConsecutiveInterestingFrames
) {
153 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnKeyframeExtracted(bitmap
));
158 bool PluginInstanceThrottlerImpl::ConsumeInputEvent(
159 const blink::WebInputEvent
& event
) {
160 // Always allow right-clicks through so users may verify it's a plug-in.
161 // TODO(tommycli): We should instead show a custom context menu (probably
162 // using PluginPlaceholder) so users aren't confused and try to click the
163 // Flash-internal 'Play' menu item. This is a stopgap solution.
164 if (event
.modifiers
& blink::WebInputEvent::Modifiers::RightButtonDown
)
167 if (state_
!= THROTTLER_STATE_MARKED_ESSENTIAL
&&
168 event
.type
== blink::WebInputEvent::MouseUp
&&
169 (event
.modifiers
& blink::WebInputEvent::LeftButtonDown
)) {
170 bool was_throttled
= IsThrottled();
171 MarkPluginEssential(UNTHROTTLE_METHOD_BY_CLICK
);
172 return was_throttled
;
175 return IsThrottled();
178 void PluginInstanceThrottlerImpl::EngageThrottle() {
179 if (state_
!= THROTTLER_STATE_AWAITING_KEYFRAME
)
182 state_
= THROTTLER_STATE_PLUGIN_THROTTLED
;
183 FOR_EACH_OBSERVER(Observer
, observer_list_
, OnThrottleStateChange());
186 } // namespace content