blob: 3c60c16d2db8c0f8284fcdf971d85ce340cc6a67 [file] [log] [blame]
// Copyright 2014 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/callback.h"
#include "base/metrics/histogram.h"
#include "content/common/frame_messages.h"
#include "content/public/renderer/document_state.h"
#include "content/public/renderer/navigation_state.h"
#include "content/public/renderer/render_frame.h"
#include "content/renderer/pepper/plugin_power_saver_helper.h"
#include "third_party/WebKit/public/web/WebDocument.h"
#include "third_party/WebKit/public/web/WebLocalFrame.h"
#include "third_party/WebKit/public/web/WebView.h"
namespace content {
namespace {
// Initial decision of the peripheral content decision.
// These numeric values are used in UMA logs; do not change them.
enum PeripheralHeuristicDecision {
HEURISTIC_DECISION_PERIPHERAL = 0,
HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN = 1,
HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG = 2,
HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED = 3,
HEURISTIC_DECISION_NUM_ITEMS
};
const char kPeripheralHeuristicHistogram[] =
"Plugin.PowerSaver.PeripheralHeuristic";
// Maximum dimensions plug-in content may have while still being considered
// peripheral content. These match the sizes used by Safari.
const int kPeripheralContentMaxWidth = 400;
const int kPeripheralContentMaxHeight = 300;
void RecordDecisionMetric(PeripheralHeuristicDecision decision) {
UMA_HISTOGRAM_ENUMERATION(kPeripheralHeuristicHistogram, decision,
HEURISTIC_DECISION_NUM_ITEMS);
}
} // namespace
PluginPowerSaverHelper::PeripheralPlugin::PeripheralPlugin(
const GURL& content_origin,
const base::Closure& unthrottle_callback)
: content_origin(content_origin), unthrottle_callback(unthrottle_callback) {
}
PluginPowerSaverHelper::PeripheralPlugin::~PeripheralPlugin() {
}
PluginPowerSaverHelper::PluginPowerSaverHelper(RenderFrame* render_frame)
: RenderFrameObserver(render_frame) {
}
PluginPowerSaverHelper::~PluginPowerSaverHelper() {
}
void PluginPowerSaverHelper::DidCommitProvisionalLoad(bool is_new_navigation) {
blink::WebFrame* frame = render_frame()->GetWebFrame();
if (frame->parent())
return; // Not a top-level navigation.
DocumentState* document_state =
DocumentState::FromDataSource(frame->dataSource());
NavigationState* navigation_state = document_state->navigation_state();
if (!navigation_state->was_within_same_page())
origin_whitelist_.clear();
}
bool PluginPowerSaverHelper::OnMessageReceived(const IPC::Message& message) {
bool handled = true;
IPC_BEGIN_MESSAGE_MAP(PluginPowerSaverHelper, message)
IPC_MESSAGE_HANDLER(FrameMsg_UpdatePluginContentOriginWhitelist,
OnUpdatePluginContentOriginWhitelist)
IPC_MESSAGE_UNHANDLED(handled = false)
IPC_END_MESSAGE_MAP()
return handled;
}
void PluginPowerSaverHelper::OnUpdatePluginContentOriginWhitelist(
const std::set<GURL>& origin_whitelist) {
origin_whitelist_ = origin_whitelist;
// Check throttled plugin instances to see if any can be unthrottled.
auto it = peripheral_plugins_.begin();
while (it != peripheral_plugins_.end()) {
if (origin_whitelist.count(it->content_origin)) {
it->unthrottle_callback.Run();
it = peripheral_plugins_.erase(it);
} else {
++it;
}
}
}
bool PluginPowerSaverHelper::ShouldThrottleContent(const GURL& content_origin,
int width,
int height,
bool* cross_origin) const {
DCHECK(cross_origin);
*cross_origin = true;
// TODO(alexmos): Update this to use the origin of the RemoteFrame when 426512
// is fixed. For now, case 3 in the class level comment doesn't work in
// --site-per-process mode.
blink::WebFrame* main_frame =
render_frame()->GetWebFrame()->view()->mainFrame();
if (main_frame->isWebRemoteFrame()) {
RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL);
return true;
}
// All same-origin plugin content is essential.
GURL main_frame_origin = GURL(main_frame->document().url()).GetOrigin();
if (content_origin == main_frame_origin) {
RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_SAME_ORIGIN);
*cross_origin = false;
return false;
}
// Whitelisted plugin origins are also essential.
if (origin_whitelist_.count(content_origin)) {
RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_WHITELISTED);
return false;
}
// Cross-origin plugin content is peripheral if smaller than a maximum size.
bool content_is_small = width < kPeripheralContentMaxWidth ||
height < kPeripheralContentMaxHeight;
if (content_is_small)
RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL);
else
RecordDecisionMetric(HEURISTIC_DECISION_ESSENTIAL_CROSS_ORIGIN_BIG);
return content_is_small;
}
void PluginPowerSaverHelper::RegisterPeripheralPlugin(
const GURL& content_origin,
const base::Closure& unthrottle_callback) {
peripheral_plugins_.push_back(
PeripheralPlugin(content_origin, unthrottle_callback));
}
void PluginPowerSaverHelper::WhitelistContentOrigin(
const GURL& content_origin) {
if (origin_whitelist_.insert(content_origin).second) {
Send(new FrameHostMsg_PluginContentOriginAllowed(
render_frame()->GetRoutingID(), content_origin));
}
}
} // namespace content