Roll src/third_party/WebKit 9d2dfea:3aea697 (svn 201972:201973)
[chromium-blink-merge.git] / content / renderer / pepper / plugin_instance_throttler_impl.cc
blob00eecbb80dc94f9a8ba0b3b53f54bb5d2b5f3471
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 <cmath>
9 #include "base/metrics/histogram.h"
10 #include "base/time/time.h"
11 #include "content/public/common/content_constants.h"
12 #include "content/public/renderer/render_thread.h"
13 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
14 #include "content/renderer/render_frame_impl.h"
15 #include "ppapi/shared_impl/ppapi_constants.h"
16 #include "third_party/WebKit/public/platform/WebRect.h"
17 #include "third_party/WebKit/public/web/WebInputEvent.h"
18 #include "third_party/WebKit/public/web/WebPluginContainer.h"
19 #include "third_party/WebKit/public/web/WebPluginParams.h"
20 #include "ui/gfx/color_utils.h"
21 #include "url/gurl.h"
23 namespace content {
25 namespace {
27 // Cross-origin plugin content must have a width and height both exceeding
28 // these minimums to be considered "large", and thus not peripheral.
29 const int kLargeContentMinWidth = 398;
30 const int kLargeContentMinHeight = 298;
32 // Mark some 16:9 aspect ratio plugins as essential (not peripheral). This is to
33 // mark as "large" some medium sized video content that meets a minimum area
34 // requirement, even if it is below the max width/height above.
35 const double kEssentialVideoAspectRatio = 16.0 / 9.0;
36 const double kAspectRatioEpsilon = 0.01;
37 const int kEssentialVideoMinimumArea = 120000;
39 // Threshold for 'boring' score to accept a frame as good enough to be a
40 // representative keyframe. Units are the ratio of all pixels that are within
41 // the most common luma bin. The same threshold is used for history thumbnails.
42 const double kAcceptableFrameMaximumBoringness = 0.94;
44 // When plugin audio is throttled, the plugin will sometimes stop generating
45 // video frames. We use this timeout to prevent waiting forever for a good
46 // poster image. Chosen arbitrarily.
47 const int kAudioThrottledFrameTimeoutMilliseconds = 500;
49 } // namespace
51 // static
52 const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine = 150;
54 // static
55 scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create() {
56 return make_scoped_ptr(new PluginInstanceThrottlerImpl);
59 // static
60 void PluginInstanceThrottler::RecordUnthrottleMethodMetric(
61 PluginInstanceThrottlerImpl::PowerSaverUnthrottleMethod method) {
62 UMA_HISTOGRAM_ENUMERATION(
63 "Plugin.PowerSaver.Unthrottle", method,
64 PluginInstanceThrottler::UNTHROTTLE_METHOD_NUM_ITEMS);
67 // static
68 bool PluginInstanceThrottler::IsLargeContent(int width, int height) {
69 if (width >= kLargeContentMinWidth && height >= kLargeContentMinHeight)
70 return true;
72 double aspect_ratio = static_cast<double>(width) / height;
73 if (std::abs(aspect_ratio - kEssentialVideoAspectRatio) <
74 kAspectRatioEpsilon &&
75 width * height >= kEssentialVideoMinimumArea) {
76 return true;
79 return false;
82 PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl()
83 : state_(THROTTLER_STATE_AWAITING_KEYFRAME),
84 is_hidden_for_placeholder_(false),
85 web_plugin_(nullptr),
86 frames_examined_(0),
87 audio_throttled_(false),
88 audio_throttled_frame_timeout_(
89 FROM_HERE,
90 base::TimeDelta::FromMilliseconds(
91 kAudioThrottledFrameTimeoutMilliseconds),
92 this,
93 &PluginInstanceThrottlerImpl::EngageThrottle),
94 weak_factory_(this) {
97 PluginInstanceThrottlerImpl::~PluginInstanceThrottlerImpl() {
98 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottlerDestroyed());
99 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL)
100 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_NEVER);
103 void PluginInstanceThrottlerImpl::AddObserver(Observer* observer) {
104 observer_list_.AddObserver(observer);
107 void PluginInstanceThrottlerImpl::RemoveObserver(Observer* observer) {
108 observer_list_.RemoveObserver(observer);
111 bool PluginInstanceThrottlerImpl::IsThrottled() const {
112 return state_ == THROTTLER_STATE_PLUGIN_THROTTLED;
115 bool PluginInstanceThrottlerImpl::IsHiddenForPlaceholder() const {
116 return is_hidden_for_placeholder_;
119 void PluginInstanceThrottlerImpl::MarkPluginEssential(
120 PowerSaverUnthrottleMethod method) {
121 if (state_ == THROTTLER_STATE_MARKED_ESSENTIAL)
122 return;
124 bool was_throttled = IsThrottled();
125 state_ = THROTTLER_STATE_MARKED_ESSENTIAL;
126 RecordUnthrottleMethodMetric(method);
128 FOR_EACH_OBSERVER(Observer, observer_list_, OnPeripheralStateChange());
130 if (was_throttled)
131 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange());
134 void PluginInstanceThrottlerImpl::SetHiddenForPlaceholder(bool hidden) {
135 is_hidden_for_placeholder_ = hidden;
136 FOR_EACH_OBSERVER(Observer, observer_list_, OnHiddenForPlaceholder(hidden));
139 PepperWebPluginImpl* PluginInstanceThrottlerImpl::GetWebPlugin() const {
140 DCHECK(web_plugin_);
141 return web_plugin_;
144 const gfx::Size& PluginInstanceThrottlerImpl::GetSize() const {
145 return unobscured_size_;
148 void PluginInstanceThrottlerImpl::NotifyAudioThrottled() {
149 audio_throttled_ = true;
150 audio_throttled_frame_timeout_.Reset();
153 void PluginInstanceThrottlerImpl::SetWebPlugin(
154 PepperWebPluginImpl* web_plugin) {
155 DCHECK(!web_plugin_);
156 web_plugin_ = web_plugin;
159 void PluginInstanceThrottlerImpl::Initialize(
160 RenderFrameImpl* frame,
161 const GURL& content_origin,
162 const std::string& plugin_module_name,
163 const gfx::Size& unobscured_size) {
164 DCHECK(unobscured_size_.IsEmpty());
165 unobscured_size_ = unobscured_size;
167 // |frame| may be nullptr in tests.
168 if (frame) {
169 PluginPowerSaverHelper* helper = frame->plugin_power_saver_helper();
170 bool cross_origin_main_content = false;
171 float zoom_factor = GetWebPlugin()->container()->pageZoomFactor();
172 if (!helper->ShouldThrottleContent(
173 content_origin, plugin_module_name,
174 roundf(unobscured_size.width() / zoom_factor),
175 roundf(unobscured_size.height() / zoom_factor),
176 &cross_origin_main_content)) {
177 DCHECK_NE(THROTTLER_STATE_MARKED_ESSENTIAL, state_);
178 state_ = THROTTLER_STATE_MARKED_ESSENTIAL;
179 FOR_EACH_OBSERVER(Observer, observer_list_, OnPeripheralStateChange());
181 if (cross_origin_main_content)
182 helper->WhitelistContentOrigin(content_origin);
184 return;
187 // To collect UMAs, register peripheral content even if power saver mode
188 // is disabled.
189 helper->RegisterPeripheralPlugin(
190 content_origin,
191 base::Bind(&PluginInstanceThrottlerImpl::MarkPluginEssential,
192 weak_factory_.GetWeakPtr(), UNTHROTTLE_METHOD_BY_WHITELIST));
196 void PluginInstanceThrottlerImpl::OnImageFlush(const SkBitmap* bitmap) {
197 DCHECK(needs_representative_keyframe());
198 if (!bitmap)
199 return;
201 ++frames_examined_;
203 // Does not make a copy, just takes a reference to the underlying pixel data.
204 last_received_frame_ = *bitmap;
206 if (audio_throttled_)
207 audio_throttled_frame_timeout_.Reset();
209 double boring_score = color_utils::CalculateBoringScore(*bitmap);
210 if (boring_score <= kAcceptableFrameMaximumBoringness ||
211 frames_examined_ >= kMaximumFramesToExamine) {
212 EngageThrottle();
216 bool PluginInstanceThrottlerImpl::ConsumeInputEvent(
217 const blink::WebInputEvent& event) {
218 // Always allow right-clicks through so users may verify it's a plugin.
219 // TODO(tommycli): We should instead show a custom context menu (probably
220 // using PluginPlaceholder) so users aren't confused and try to click the
221 // Flash-internal 'Play' menu item. This is a stopgap solution.
222 if (event.modifiers & blink::WebInputEvent::Modifiers::RightButtonDown)
223 return false;
225 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL &&
226 event.type == blink::WebInputEvent::MouseUp &&
227 (event.modifiers & blink::WebInputEvent::LeftButtonDown)) {
228 bool was_throttled = IsThrottled();
229 MarkPluginEssential(UNTHROTTLE_METHOD_BY_CLICK);
230 return was_throttled;
233 return IsThrottled();
236 void PluginInstanceThrottlerImpl::EngageThrottle() {
237 if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME)
238 return;
240 if (!last_received_frame_.empty()) {
241 FOR_EACH_OBSERVER(Observer, observer_list_,
242 OnKeyframeExtracted(&last_received_frame_));
244 // Release our reference to the underlying pixel data.
245 last_received_frame_.reset();
248 state_ = THROTTLER_STATE_PLUGIN_THROTTLED;
249 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange());
252 } // namespace content