Implement HasPermission() method in PermissionService.
[chromium-blink-merge.git] / content / renderer / pepper / plugin_power_saver_helper.cc
blobb804ee6ad9718ee4a0e28cc522558c1ebefff7b0
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/renderer/document_state.h"
11 #include "content/public/renderer/navigation_state.h"
12 #include "content/public/renderer/render_frame.h"
13 #include "third_party/WebKit/public/web/WebDocument.h"
14 #include "third_party/WebKit/public/web/WebLocalFrame.h"
15 #include "third_party/WebKit/public/web/WebPluginParams.h"
16 #include "third_party/WebKit/public/web/WebView.h"
18 namespace content {
20 namespace {
22 const char kPosterParamName[] = "poster";
24 // Initial decision of the peripheral content decision.
25 // These numeric values are used in UMA logs; do not change them.
26 enum PeripheralHeuristicDecision {
27 HEURISTIC_DECISION_PERIPHERAL = 0,
28 HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN = 1,
29 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG = 2,
30 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED = 3,
31 HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY = 4,
32 HEURISTIC_DECISION_NUM_ITEMS
35 const char kPeripheralHeuristicHistogram[] =
36 "Plugin.PowerSaver.PeripheralHeuristic";
38 // Maximum dimensions plug-in content may have while still being considered
39 // peripheral content. These match the sizes used by Safari.
40 const int kPeripheralContentMaxWidth = 400;
41 const int kPeripheralContentMaxHeight = 300;
43 // Plug-in content below this size in height and width is considered "tiny".
44 // Tiny content is never peripheral, as tiny plug-ins often serve a critical
45 // purpose, and the user often cannot find and click to unthrottle it.
46 const int kPeripheralContentTinySize = 5;
48 void RecordDecisionMetric(PeripheralHeuristicDecision decision) {
49 UMA_HISTOGRAM_ENUMERATION(kPeripheralHeuristicHistogram, decision,
50 HEURISTIC_DECISION_NUM_ITEMS);
53 const char kWebPluginParamHeight[] = "height";
54 const char kWebPluginParamWidth[] = "width";
56 // Returns true if valid non-negative height and width extracted.
57 // When this returns false, |width| and |height| are set to undefined values.
58 bool ExtractDimensions(const blink::WebPluginParams& params,
59 int* width,
60 int* height) {
61 DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
62 DCHECK(width);
63 DCHECK(height);
64 bool width_extracted = false;
65 bool height_extracted = false;
66 for (size_t i = 0; i < params.attributeNames.size(); ++i) {
67 if (params.attributeNames[i].utf8() == kWebPluginParamWidth) {
68 width_extracted =
69 base::StringToInt(params.attributeValues[i].utf8(), width);
70 } else if (params.attributeNames[i].utf8() == kWebPluginParamHeight) {
71 height_extracted =
72 base::StringToInt(params.attributeValues[i].utf8(), height);
75 return width_extracted && height_extracted && *width >= 0 && *height >= 0;
78 GURL GetPluginInstancePosterImage(const blink::WebPluginParams& params,
79 const GURL& page_base_url) {
80 DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size());
82 for (size_t i = 0; i < params.attributeNames.size(); ++i) {
83 if (params.attributeNames[i] == kPosterParamName) {
84 std::string poster_value(params.attributeValues[i].utf8());
85 if (!poster_value.empty())
86 return page_base_url.Resolve(poster_value);
89 return GURL();
92 } // namespace
94 PluginPowerSaverHelper::PeripheralPlugin::PeripheralPlugin(
95 const GURL& content_origin,
96 const base::Closure& unthrottle_callback)
97 : content_origin(content_origin), unthrottle_callback(unthrottle_callback) {
100 PluginPowerSaverHelper::PeripheralPlugin::~PeripheralPlugin() {
103 PluginPowerSaverHelper::PluginPowerSaverHelper(RenderFrame* render_frame)
104 : RenderFrameObserver(render_frame) {
107 PluginPowerSaverHelper::~PluginPowerSaverHelper() {
110 void PluginPowerSaverHelper::DidCommitProvisionalLoad(bool is_new_navigation) {
111 blink::WebFrame* frame = render_frame()->GetWebFrame();
112 if (frame->parent())
113 return; // Not a top-level navigation.
115 DocumentState* document_state =
116 DocumentState::FromDataSource(frame->dataSource());
117 NavigationState* navigation_state = document_state->navigation_state();
118 if (!navigation_state->was_within_same_page())
119 origin_whitelist_.clear();
122 bool PluginPowerSaverHelper::OnMessageReceived(const IPC::Message& message) {
123 bool handled = true;
124 IPC_BEGIN_MESSAGE_MAP(PluginPowerSaverHelper, message)
125 IPC_MESSAGE_HANDLER(FrameMsg_UpdatePluginContentOriginWhitelist,
126 OnUpdatePluginContentOriginWhitelist)
127 IPC_MESSAGE_UNHANDLED(handled = false)
128 IPC_END_MESSAGE_MAP()
129 return handled;
132 void PluginPowerSaverHelper::OnUpdatePluginContentOriginWhitelist(
133 const std::set<GURL>& origin_whitelist) {
134 origin_whitelist_ = origin_whitelist;
136 // Check throttled plugin instances to see if any can be unthrottled.
137 auto it = peripheral_plugins_.begin();
138 while (it != peripheral_plugins_.end()) {
139 if (origin_whitelist.count(it->content_origin)) {
140 it->unthrottle_callback.Run();
141 it = peripheral_plugins_.erase(it);
142 } else {
143 ++it;
148 void PluginPowerSaverHelper::RegisterPeripheralPlugin(
149 const GURL& content_origin,
150 const base::Closure& unthrottle_callback) {
151 peripheral_plugins_.push_back(
152 PeripheralPlugin(content_origin, unthrottle_callback));
155 bool PluginPowerSaverHelper::ShouldThrottleContent(
156 const blink::WebPluginParams& params,
157 const GURL& plugin_frame_url,
158 GURL* poster_image,
159 bool* cross_origin_main_content) const {
160 if (poster_image)
161 *poster_image = GURL();
162 if (cross_origin_main_content)
163 *cross_origin_main_content = false;
165 GURL content_origin = GURL(params.url).GetOrigin();
167 int width = 0;
168 int height = 0;
169 if (!ExtractDimensions(params, &width, &height))
170 return false;
172 // TODO(alexmos): Update this to use the origin of the RemoteFrame when 426512
173 // is fixed. For now, case 3 in the class level comment doesn't work in
174 // --site-per-process mode.
175 blink::WebFrame* main_frame =
176 render_frame()->GetWebFrame()->view()->mainFrame();
177 if (main_frame->isWebRemoteFrame()) {
178 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL);
179 if (poster_image)
180 *poster_image = GetPluginInstancePosterImage(params, plugin_frame_url);
181 return true;
184 // All same-origin plugin content is essential.
185 GURL main_frame_origin = GURL(main_frame->document().url()).GetOrigin();
186 if (content_origin == main_frame_origin) {
187 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN);
188 return false;
191 // Whitelisted plugin origins are also essential.
192 if (origin_whitelist_.count(content_origin)) {
193 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED);
194 return false;
197 // Never mark tiny content as peripheral.
198 if (width <= kPeripheralContentTinySize &&
199 height <= kPeripheralContentTinySize) {
200 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY);
201 return false;
204 // Plugin content large in both dimensions are the "main attraction".
205 if (width >= kPeripheralContentMaxWidth &&
206 height >= kPeripheralContentMaxHeight) {
207 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG);
208 if (cross_origin_main_content)
209 *cross_origin_main_content = true;
210 return false;
213 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL);
214 if (poster_image)
215 *poster_image = GetPluginInstancePosterImage(params, plugin_frame_url);
216 return true;
219 void PluginPowerSaverHelper::WhitelistContentOrigin(
220 const GURL& content_origin) {
221 DCHECK_EQ(content_origin.GetOrigin(), content_origin);
222 if (origin_whitelist_.insert(content_origin).second) {
223 Send(new FrameHostMsg_PluginContentOriginAllowed(
224 render_frame()->GetRoutingID(), content_origin));
228 } // namespace content