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"
9 #include "base/command_line.h"
10 #include "base/metrics/histogram.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "content/common/frame_messages.h"
13 #include "content/public/common/content_constants.h"
14 #include "content/public/common/content_switches.h"
15 #include "content/public/renderer/plugin_instance_throttler.h"
16 #include "content/public/renderer/render_frame.h"
17 #include "ppapi/shared_impl/ppapi_constants.h"
18 #include "third_party/WebKit/public/web/WebLocalFrame.h"
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_ESSENTIAL_UNKNOWN_SIZE
= 5,
33 HEURISTIC_DECISION_NUM_ITEMS
36 const char kPeripheralHeuristicHistogram
[] =
37 "Plugin.PowerSaver.PeripheralHeuristic";
39 // Plugin content below this size in height and width is considered "tiny".
40 // Tiny content is never peripheral, as tiny plugins often serve a critical
41 // purpose, and the user often cannot find and click to unthrottle it.
42 const int kPeripheralContentTinySize
= 5;
44 void RecordDecisionMetric(PeripheralHeuristicDecision decision
) {
45 UMA_HISTOGRAM_ENUMERATION(kPeripheralHeuristicHistogram
, decision
,
46 HEURISTIC_DECISION_NUM_ITEMS
);
51 PluginPowerSaverHelper::PeripheralPlugin::PeripheralPlugin(
52 const url::Origin
& content_origin
,
53 const base::Closure
& unthrottle_callback
)
54 : content_origin(content_origin
),
55 unthrottle_callback(unthrottle_callback
) {}
57 PluginPowerSaverHelper::PeripheralPlugin::~PeripheralPlugin() {
60 PluginPowerSaverHelper::PluginPowerSaverHelper(RenderFrame
* render_frame
)
61 : RenderFrameObserver(render_frame
)
62 , override_for_testing_(Normal
) {
63 base::CommandLine
& command_line
= *base::CommandLine::ForCurrentProcess();
64 std::string override_for_testing
= command_line
.GetSwitchValueASCII(
65 switches::kOverridePluginPowerSaverForTesting
);
66 if (override_for_testing
== "never")
67 override_for_testing_
= Never
;
68 else if (override_for_testing
== "ignore-list")
69 override_for_testing_
= IgnoreList
;
70 else if (override_for_testing
== "always")
71 override_for_testing_
= Always
;
74 PluginPowerSaverHelper::~PluginPowerSaverHelper() {
77 void PluginPowerSaverHelper::DidCommitProvisionalLoad(
78 bool is_new_navigation
,
79 bool is_same_page_navigation
) {
80 blink::WebFrame
* frame
= render_frame()->GetWebFrame();
81 // Only apply to top-level and new page navigation.
82 if (frame
->parent() || is_same_page_navigation
)
83 return; // Not a top-level navigation.
85 origin_whitelist_
.clear();
88 bool PluginPowerSaverHelper::OnMessageReceived(const IPC::Message
& message
) {
90 IPC_BEGIN_MESSAGE_MAP(PluginPowerSaverHelper
, message
)
91 IPC_MESSAGE_HANDLER(FrameMsg_UpdatePluginContentOriginWhitelist
,
92 OnUpdatePluginContentOriginWhitelist
)
93 IPC_MESSAGE_UNHANDLED(handled
= false)
98 void PluginPowerSaverHelper::OnUpdatePluginContentOriginWhitelist(
99 const std::set
<url::Origin
>& origin_whitelist
) {
100 origin_whitelist_
= origin_whitelist
;
102 // Check throttled plugin instances to see if any can be unthrottled.
103 auto it
= peripheral_plugins_
.begin();
104 while (it
!= peripheral_plugins_
.end()) {
105 if (origin_whitelist
.count(it
->content_origin
)) {
106 it
->unthrottle_callback
.Run();
107 it
= peripheral_plugins_
.erase(it
);
114 void PluginPowerSaverHelper::RegisterPeripheralPlugin(
115 const url::Origin
& content_origin
,
116 const base::Closure
& unthrottle_callback
) {
117 peripheral_plugins_
.push_back(
118 PeripheralPlugin(content_origin
, unthrottle_callback
));
121 bool PluginPowerSaverHelper::ShouldThrottleContent(
122 const url::Origin
& main_frame_origin
,
123 const url::Origin
& content_origin
,
124 const std::string
& plugin_module_name
,
127 bool* cross_origin_main_content
) const {
128 if (cross_origin_main_content
)
129 *cross_origin_main_content
= false;
131 // This feature has only been tested throughly with Flash thus far.
132 // It is also enabled for the Power Saver test plugin for browser tests.
133 if (override_for_testing_
== Always
) {
135 } else if (override_for_testing_
== Never
) {
137 } else if (override_for_testing_
== Normal
&&
138 plugin_module_name
!= content::kFlashPluginName
) {
142 if (main_frame_origin
.IsSameOriginWith(content_origin
)) {
143 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN
);
147 if (width
<= 0 || height
<= 0) {
148 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_UNKNOWN_SIZE
);
152 // Whitelisted plugin origins are also essential.
153 if (origin_whitelist_
.count(content_origin
)) {
154 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED
);
158 // Never mark tiny content as peripheral.
159 if (width
<= kPeripheralContentTinySize
&&
160 height
<= kPeripheralContentTinySize
) {
161 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_TINY
);
165 // Plugin content large in both dimensions are the "main attraction".
166 if (PluginInstanceThrottler::IsLargeContent(width
, height
)) {
167 RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG
);
168 if (cross_origin_main_content
)
169 *cross_origin_main_content
= true;
173 RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL
);
177 void PluginPowerSaverHelper::WhitelistContentOrigin(
178 const url::Origin
& content_origin
) {
179 if (origin_whitelist_
.insert(content_origin
).second
) {
180 Send(new FrameHostMsg_PluginContentOriginAllowed(
181 render_frame()->GetRoutingID(), content_origin
));
185 } // namespace content