Process Alt-Svc headers.
[chromium-blink-merge.git] / content / renderer / pepper / plugin_instance_throttler_impl.cc
blob7bf11133c0c71a10d1db7bf9b9576087d3c4b613
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/WebPluginParams.h"
19 #include "ui/gfx/color_utils.h"
20 #include "url/gurl.h"
22 namespace content {
24 namespace {
26 // Cross-origin plugin content must have a width and height both exceeding
27 // these minimums to be considered "large", and thus not peripheral.
28 const int kLargeContentMinWidth = 398;
29 const int kLargeContentMinHeight = 298;
31 // Mark some 16:9 aspect ratio plugins as essential (not peripheral). This is to
32 // mark as "large" some medium sized video content that meets a minimum area
33 // requirement, even if it is below the max width/height above.
34 const double kEssentialVideoAspectRatio = 16.0 / 9.0;
35 const double kAspectRatioEpsilon = 0.01;
36 const int kEssentialVideoMinimumArea = 120000;
38 // Threshold for 'boring' score to accept a frame as good enough to be a
39 // representative keyframe. Units are the ratio of all pixels that are within
40 // the most common luma bin. The same threshold is used for history thumbnails.
41 const double kAcceptableFrameMaximumBoringness = 0.94;
43 // When plugin audio is throttled, the plugin will sometimes stop generating
44 // video frames. We use this timeout to prevent waiting forever for a good
45 // poster image. Chosen arbitrarily.
46 const int kAudioThrottledFrameTimeoutMilliseconds = 500;
48 } // namespace
50 // static
51 const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine = 150;
53 // static
54 scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create() {
55 return make_scoped_ptr(new PluginInstanceThrottlerImpl);
58 // static
59 void PluginInstanceThrottler::RecordUnthrottleMethodMetric(
60 PluginInstanceThrottlerImpl::PowerSaverUnthrottleMethod method) {
61 UMA_HISTOGRAM_ENUMERATION(
62 "Plugin.PowerSaver.Unthrottle", method,
63 PluginInstanceThrottler::UNTHROTTLE_METHOD_NUM_ITEMS);
66 // static
67 bool PluginInstanceThrottler::IsLargeContent(int width, int height) {
68 if (width >= kLargeContentMinWidth && height >= kLargeContentMinHeight)
69 return true;
71 double aspect_ratio = static_cast<double>(width) / height;
72 if (std::abs(aspect_ratio - kEssentialVideoAspectRatio) <
73 kAspectRatioEpsilon &&
74 width * height >= kEssentialVideoMinimumArea) {
75 return true;
78 return false;
81 PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl()
82 : state_(THROTTLER_STATE_AWAITING_KEYFRAME),
83 is_hidden_for_placeholder_(false),
84 web_plugin_(nullptr),
85 frames_examined_(0),
86 audio_throttled_(false),
87 audio_throttled_frame_timeout_(
88 FROM_HERE,
89 base::TimeDelta::FromMilliseconds(
90 kAudioThrottledFrameTimeoutMilliseconds),
91 this,
92 &PluginInstanceThrottlerImpl::EngageThrottle),
93 weak_factory_(this) {
96 PluginInstanceThrottlerImpl::~PluginInstanceThrottlerImpl() {
97 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottlerDestroyed());
98 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL)
99 RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_NEVER);
102 void PluginInstanceThrottlerImpl::AddObserver(Observer* observer) {
103 observer_list_.AddObserver(observer);
106 void PluginInstanceThrottlerImpl::RemoveObserver(Observer* observer) {
107 observer_list_.RemoveObserver(observer);
110 bool PluginInstanceThrottlerImpl::IsThrottled() const {
111 return state_ == THROTTLER_STATE_PLUGIN_THROTTLED;
114 bool PluginInstanceThrottlerImpl::IsHiddenForPlaceholder() const {
115 return is_hidden_for_placeholder_;
118 void PluginInstanceThrottlerImpl::MarkPluginEssential(
119 PowerSaverUnthrottleMethod method) {
120 if (state_ == THROTTLER_STATE_MARKED_ESSENTIAL)
121 return;
123 bool was_throttled = IsThrottled();
124 state_ = THROTTLER_STATE_MARKED_ESSENTIAL;
125 RecordUnthrottleMethodMetric(method);
127 FOR_EACH_OBSERVER(Observer, observer_list_, OnPeripheralStateChange());
129 if (was_throttled)
130 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange());
133 void PluginInstanceThrottlerImpl::SetHiddenForPlaceholder(bool hidden) {
134 is_hidden_for_placeholder_ = hidden;
135 FOR_EACH_OBSERVER(Observer, observer_list_, OnHiddenForPlaceholder(hidden));
138 PepperWebPluginImpl* PluginInstanceThrottlerImpl::GetWebPlugin() const {
139 DCHECK(web_plugin_);
140 return web_plugin_;
143 const gfx::Size& PluginInstanceThrottlerImpl::GetSize() const {
144 return unobscured_size_;
147 void PluginInstanceThrottlerImpl::NotifyAudioThrottled() {
148 audio_throttled_ = true;
149 audio_throttled_frame_timeout_.Reset();
152 void PluginInstanceThrottlerImpl::SetWebPlugin(
153 PepperWebPluginImpl* web_plugin) {
154 DCHECK(!web_plugin_);
155 web_plugin_ = web_plugin;
158 void PluginInstanceThrottlerImpl::Initialize(
159 RenderFrameImpl* frame,
160 const GURL& content_origin,
161 const std::string& plugin_module_name,
162 const gfx::Size& unobscured_size) {
163 DCHECK(unobscured_size_.IsEmpty());
164 unobscured_size_ = unobscured_size;
166 // |frame| may be nullptr in tests.
167 if (frame) {
168 PluginPowerSaverHelper* helper = frame->plugin_power_saver_helper();
169 bool cross_origin_main_content = false;
170 if (!helper->ShouldThrottleContent(content_origin, plugin_module_name,
171 unobscured_size.width(),
172 unobscured_size.height(),
173 &cross_origin_main_content)) {
174 DCHECK_NE(THROTTLER_STATE_MARKED_ESSENTIAL, state_);
175 state_ = THROTTLER_STATE_MARKED_ESSENTIAL;
176 FOR_EACH_OBSERVER(Observer, observer_list_, OnPeripheralStateChange());
178 if (cross_origin_main_content)
179 helper->WhitelistContentOrigin(content_origin);
181 return;
184 // To collect UMAs, register peripheral content even if power saver mode
185 // is disabled.
186 helper->RegisterPeripheralPlugin(
187 content_origin,
188 base::Bind(&PluginInstanceThrottlerImpl::MarkPluginEssential,
189 weak_factory_.GetWeakPtr(), UNTHROTTLE_METHOD_BY_WHITELIST));
193 void PluginInstanceThrottlerImpl::OnImageFlush(const SkBitmap* bitmap) {
194 DCHECK(needs_representative_keyframe());
195 if (!bitmap)
196 return;
198 ++frames_examined_;
200 // Does not make a copy, just takes a reference to the underlying pixel data.
201 last_received_frame_ = *bitmap;
203 if (audio_throttled_)
204 audio_throttled_frame_timeout_.Reset();
206 double boring_score = color_utils::CalculateBoringScore(*bitmap);
207 if (boring_score <= kAcceptableFrameMaximumBoringness ||
208 frames_examined_ >= kMaximumFramesToExamine) {
209 EngageThrottle();
213 bool PluginInstanceThrottlerImpl::ConsumeInputEvent(
214 const blink::WebInputEvent& event) {
215 // Always allow right-clicks through so users may verify it's a plugin.
216 // TODO(tommycli): We should instead show a custom context menu (probably
217 // using PluginPlaceholder) so users aren't confused and try to click the
218 // Flash-internal 'Play' menu item. This is a stopgap solution.
219 if (event.modifiers & blink::WebInputEvent::Modifiers::RightButtonDown)
220 return false;
222 if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL &&
223 event.type == blink::WebInputEvent::MouseUp &&
224 (event.modifiers & blink::WebInputEvent::LeftButtonDown)) {
225 bool was_throttled = IsThrottled();
226 MarkPluginEssential(UNTHROTTLE_METHOD_BY_CLICK);
227 return was_throttled;
230 return IsThrottled();
233 void PluginInstanceThrottlerImpl::EngageThrottle() {
234 if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME)
235 return;
237 if (!last_received_frame_.empty()) {
238 FOR_EACH_OBSERVER(Observer, observer_list_,
239 OnKeyframeExtracted(&last_received_frame_));
241 // Release our reference to the underlying pixel data.
242 last_received_frame_.reset();
245 state_ = THROTTLER_STATE_PLUGIN_THROTTLED;
246 FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange());
249 } // namespace content