Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / renderer / pepper / plugin_power_saver_helper.cc
blob2891980fe7f49c4afeeeaf95401667698a6f4155
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"
17 namespace content {
19 namespace {
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);
50 } // namespace
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) {
80 bool handled = true;
81 IPC_BEGIN_MESSAGE_MAP(PluginPowerSaverHelper, message)
82 IPC_MESSAGE_HANDLER(FrameMsg_UpdatePluginContentOriginWhitelist,
83 OnUpdatePluginContentOriginWhitelist)
84 IPC_MESSAGE_UNHANDLED(handled = false)
85 IPC_END_MESSAGE_MAP()
86 return handled;
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);
99 } else {
100 ++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,
116 int width,
117 int height,
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)
125 return false;
127 if (width <= 0 || height <= 0)
128 return false;
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);
137 return true;
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);
144 return false;
147 // Whitelisted plugin origins are also essential.
148 if (origin_whitelist_.count(content_origin)) {
149 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED);
150 return false;
153 // Never mark tiny content as peripheral.
154 if (width <= kPeripheralContentTinySize &&
155 height <= kPeripheralContentTinySize) {
156 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY);
157 return false;
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;
166 return false;
169 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL);
170 return true;
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