diff options
16 files changed, 249 insertions, 336 deletions
diff --git a/chrome/renderer/chrome_content_renderer_client.cc b/chrome/renderer/chrome_content_renderer_client.cc index 8ee88ff..d38acae 100644 --- a/chrome/renderer/chrome_content_renderer_client.cc +++ b/chrome/renderer/chrome_content_renderer_client.cc @@ -155,7 +155,6 @@ using autofill::PasswordGenerationAgent; using base::ASCIIToUTF16; using base::UserMetricsAction; using content::PluginInstanceThrottler; -using content::PluginPowerSaverMode; using content::RenderFrame; using content::RenderThread; using content::WebPluginInfo; @@ -269,6 +268,21 @@ void IsGuestViewApiAvailableToScriptContext( } #endif +#if defined(ENABLE_PLUGINS) +GURL GetPluginInstancePosterImage(const blink::WebPluginParams& params, + const GURL& page_base_url) { + DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size()); + + for (size_t i = 0; i < params.attributeNames.size(); ++i) { + if (params.attributeNames[i].utf8() == "poster" && + !params.attributeValues[i].isEmpty()) { + return page_base_url.Resolve(params.attributeValues[i].utf8()); + } + } + return GURL(); +} +#endif + } // namespace ChromeContentRendererClient::ChromeContentRendererClient() { @@ -796,34 +810,17 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin( } #endif // !defined(DISABLE_NACL) && defined(ENABLE_EXTENSIONS) - scoped_ptr<content::PluginInstanceThrottler> throttler; #if defined(ENABLE_PLUGINS) - PluginPowerSaverMode power_saver_mode = - PluginPowerSaverMode::POWER_SAVER_MODE_ESSENTIAL; - bool show_poster = false; - GURL poster_url; - bool cross_origin_main_content = false; - bool blocked_for_background_tab = - render_frame->IsHidden() && + bool power_saver_enabled = status_value == - ChromeViewHostMsg_GetPluginInfo_Status::kPlayImportantContent; - if (render_frame->ShouldThrottleContent(params, frame->document().url(), - &poster_url, - &cross_origin_main_content)) { - // TODO(tommycli): Apply throttler behavior to all plugins. - if (info.name == base::ASCIIToUTF16(content::kFlashPluginName) && - status_value == ChromeViewHostMsg_GetPluginInfo_Status:: - kPlayImportantContent) { - power_saver_mode = - PluginPowerSaverMode::POWER_SAVER_MODE_PERIPHERAL_THROTTLED; - show_poster = poster_url.is_valid(); - } else { - power_saver_mode = - PluginPowerSaverMode::POWER_SAVER_MODE_PERIPHERAL_UNTHROTTLED; - } + ChromeViewHostMsg_GetPluginInfo_Status::kPlayImportantContent; + bool blocked_for_background_tab = + render_frame->IsHidden() && power_saver_enabled; - throttler = content::PluginInstanceThrottler::Get(render_frame, url, - power_saver_mode); + GURL poster_url; + if (power_saver_enabled) { + poster_url = + GetPluginInstancePosterImage(params, frame->document().url()); } // Delay loading plugins if prerendering. @@ -832,41 +829,40 @@ WebPlugin* ChromeContentRendererClient::CreatePlugin( // reduce the chance of future regressions. bool is_prerendering = prerender::PrerenderHelper::IsPrerendering(render_frame); - if (blocked_for_background_tab || is_prerendering || show_poster) { + if (blocked_for_background_tab || is_prerendering || + poster_url.is_valid()) { placeholder = ChromePluginPlaceholder::CreateBlockedPlugin( render_frame, frame, params, info, identifier, group_name, - show_poster ? IDR_PLUGIN_POSTER_HTML : IDR_BLOCKED_PLUGIN_HTML, + poster_url.is_valid() ? IDR_PLUGIN_POSTER_HTML + : IDR_BLOCKED_PLUGIN_HTML, l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name), poster_url); placeholder->set_blocked_for_background_tab( blocked_for_background_tab); placeholder->set_blocked_for_prerendering(is_prerendering); - placeholder->set_power_saver_mode(power_saver_mode); + placeholder->set_power_saver_enabled(power_saver_enabled); placeholder->set_allow_loading(true); break; - } else if (cross_origin_main_content) { - GURL content_origin = GURL(params.url).GetOrigin(); - render_frame->WhitelistContentOrigin(content_origin); } - if (power_saver_mode == - PluginPowerSaverMode::POWER_SAVER_MODE_PERIPHERAL_THROTTLED) { - content::PluginInstanceThrottler* throttler_raw = throttler.get(); - blink::WebPlugin* plugin = - render_frame->CreatePlugin(frame, info, params, throttler.Pass()); + scoped_ptr<content::PluginInstanceThrottler> throttler = + PluginInstanceThrottler::Create(power_saver_enabled); + content::PluginInstanceThrottler* throttler_raw = throttler.get(); + blink::WebPlugin* plugin = + render_frame->CreatePlugin(frame, info, params, throttler.Pass()); + if (power_saver_enabled) { // PluginPreroller manages its own lifetime. new PluginPreroller( render_frame, frame, params, info, identifier, group_name, l10n_util::GetStringFUTF16(IDS_PLUGIN_BLOCKED, group_name), plugin, throttler_raw); - - return plugin; } + return plugin; +#else // !defined(ENABLE_PLUGINS) + return render_frame->CreatePlugin(frame, info, params, nullptr); #endif // defined(ENABLE_PLUGINS) - return render_frame->CreatePlugin(frame, info, params, - throttler.Pass()); } case ChromeViewHostMsg_GetPluginInfo_Status::kNPAPINotSupported: { RenderThread::Get()->RecordAction( diff --git a/chrome/renderer/plugins/chrome_plugin_placeholder.cc b/chrome/renderer/plugins/chrome_plugin_placeholder.cc index 4a1720f..dcbeca8 100644 --- a/chrome/renderer/plugins/chrome_plugin_placeholder.cc +++ b/chrome/renderer/plugins/chrome_plugin_placeholder.cc @@ -303,7 +303,7 @@ void ChromePluginPlaceholder::OnMenuAction(int request_id, unsigned action) { case chrome::MENU_COMMAND_PLUGIN_RUN: { RenderThread::Get()->RecordAction(UserMetricsAction("Plugin_Load_Menu")); #if defined(ENABLE_PLUGINS) - DisablePowerSaverForInstance( + MarkPluginEssential( content::PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK); #endif LoadPlugin(); diff --git a/chrome/renderer/plugins/plugin_preroller.cc b/chrome/renderer/plugins/plugin_preroller.cc index 34d830f..fecfb6d 100644 --- a/chrome/renderer/plugins/plugin_preroller.cc +++ b/chrome/renderer/plugins/plugin_preroller.cc @@ -66,8 +66,7 @@ void PluginPreroller::OnThrottleStateChange() { render_frame(), frame_, params_, info_, identifier_, name_, IDR_PLUGIN_POSTER_HTML, message_, keyframe_data_url_); placeholder->SetPremadePlugin(plugin_, throttler_); - placeholder->set_power_saver_mode( - content::PluginPowerSaverMode::POWER_SAVER_MODE_PERIPHERAL_THROTTLED); + placeholder->set_power_saver_enabled(true); placeholder->set_allow_loading(true); blink::WebPluginContainer* container = plugin_->container(); diff --git a/components/plugins/renderer/loadable_plugin_placeholder.cc b/components/plugins/renderer/loadable_plugin_placeholder.cc index 89c8258..5b33949 100644 --- a/components/plugins/renderer/loadable_plugin_placeholder.cc +++ b/components/plugins/renderer/loadable_plugin_placeholder.cc @@ -32,7 +32,6 @@ using blink::WebPluginParams; using blink::WebScriptSource; using blink::WebURLRequest; using content::PluginInstanceThrottler; -using content::PluginPowerSaverMode; using content::RenderThread; namespace plugins { @@ -44,7 +43,7 @@ void LoadablePluginPlaceholder::BlockForPowerSaverPoster() { render_frame()->RegisterPeripheralPlugin( GURL(GetPluginParams().url).GetOrigin(), - base::Bind(&LoadablePluginPlaceholder::DisablePowerSaverForInstance, + base::Bind(&LoadablePluginPlaceholder::MarkPluginEssential, weak_factory_.GetWeakPtr(), PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_WHITELIST)); } @@ -77,7 +76,8 @@ LoadablePluginPlaceholder::LoadablePluginPlaceholder( is_blocked_for_background_tab_(false), is_blocked_for_prerendering_(false), is_blocked_for_power_saver_poster_(false), - power_saver_mode_(PluginPowerSaverMode::POWER_SAVER_MODE_ESSENTIAL), + power_saver_enabled_(false), + plugin_marked_essential_(false), premade_plugin_(nullptr), premade_throttler_(nullptr), allow_loading_(false), @@ -92,8 +92,8 @@ LoadablePluginPlaceholder::~LoadablePluginPlaceholder() { DCHECK(!premade_plugin_); DCHECK(!premade_throttler_); - if (!placeholder_was_replaced_ && !is_blocked_for_prerendering_ && - power_saver_mode_ != PluginPowerSaverMode::POWER_SAVER_MODE_ESSENTIAL) { + if (!plugin_marked_essential_ && !placeholder_was_replaced_ && + !is_blocked_for_prerendering_ && is_blocked_for_power_saver_poster_) { PluginInstanceThrottler::RecordUnthrottleMethodMetric( PluginInstanceThrottler::UNTHROTTLE_METHOD_NEVER); } @@ -101,20 +101,19 @@ LoadablePluginPlaceholder::~LoadablePluginPlaceholder() { } #if defined(ENABLE_PLUGINS) -void LoadablePluginPlaceholder::DisablePowerSaverForInstance( +void LoadablePluginPlaceholder::MarkPluginEssential( PluginInstanceThrottler::PowerSaverUnthrottleMethod method) { - if (power_saver_mode_ == PluginPowerSaverMode::POWER_SAVER_MODE_ESSENTIAL) + if (plugin_marked_essential_) return; - power_saver_mode_ = PluginPowerSaverMode::POWER_SAVER_MODE_ESSENTIAL; + plugin_marked_essential_ = true; if (premade_throttler_) { premade_throttler_->MarkPluginEssential(method); - } else { - PluginInstanceThrottler::RecordUnthrottleMethodMetric(method); } if (is_blocked_for_power_saver_poster_) { is_blocked_for_power_saver_poster_ = false; + PluginInstanceThrottler::RecordUnthrottleMethodMetric(method); if (!LoadingBlocked()) LoadPlugin(); } @@ -307,8 +306,10 @@ void LoadablePluginPlaceholder::LoadPlugin() { // reduce the chance of future regressions. scoped_ptr<PluginInstanceThrottler> throttler; #if defined(ENABLE_PLUGINS) - throttler = PluginInstanceThrottler::Get( - render_frame(), GetPluginParams().url, power_saver_mode_); + // If the plugin has already been marked essential in its placeholder form, + // we shouldn't create a new throttler and start the process all over again. + if (!plugin_marked_essential_) + throttler = PluginInstanceThrottler::Create(power_saver_enabled_); #endif WebPlugin* plugin = render_frame()->CreatePlugin( GetFrame(), plugin_info_, GetPluginParams(), throttler.Pass()); @@ -322,8 +323,7 @@ void LoadablePluginPlaceholder::LoadCallback() { #if defined(ENABLE_PLUGINS) // If the user specifically clicks on the plug-in content's placeholder, // disable power saver throttling for this instance. - DisablePowerSaverForInstance( - PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK); + MarkPluginEssential(PluginInstanceThrottler::UNTHROTTLE_METHOD_BY_CLICK); #endif LoadPlugin(); } diff --git a/components/plugins/renderer/loadable_plugin_placeholder.h b/components/plugins/renderer/loadable_plugin_placeholder.h index 401ec7d..ee24663 100644 --- a/components/plugins/renderer/loadable_plugin_placeholder.h +++ b/components/plugins/renderer/loadable_plugin_placeholder.h @@ -26,8 +26,8 @@ class LoadablePluginPlaceholder } #if defined(ENABLE_PLUGINS) - void set_power_saver_mode(content::PluginPowerSaverMode power_saver_mode) { - power_saver_mode_ = power_saver_mode; + void set_power_saver_enabled(bool power_saver_enabled) { + power_saver_enabled_ = power_saver_enabled; } // Defer loading of plug-in, and instead show the Power Saver poster image. @@ -50,7 +50,7 @@ class LoadablePluginPlaceholder ~LoadablePluginPlaceholder() override; #if defined(ENABLE_PLUGINS) - void DisablePowerSaverForInstance( + void MarkPluginEssential( content::PluginInstanceThrottler::PowerSaverUnthrottleMethod method); #endif @@ -117,7 +117,10 @@ class LoadablePluginPlaceholder bool is_blocked_for_power_saver_poster_; // This is independent of deferred plugin load due to a Power Saver poster. - content::PluginPowerSaverMode power_saver_mode_; + bool power_saver_enabled_; + + // True if the plugin has been marked essential. + bool plugin_marked_essential_; // When we load, uses this premade plugin instead of creating a new one. blink::WebPlugin* premade_plugin_; diff --git a/content/public/renderer/plugin_instance_throttler.h b/content/public/renderer/plugin_instance_throttler.h index 376e6ab..8243a21 100644 --- a/content/public/renderer/plugin_instance_throttler.h +++ b/content/public/renderer/plugin_instance_throttler.h @@ -9,25 +9,15 @@ #include "base/memory/scoped_ptr.h" #include "content/common/content_export.h" +namespace blink { +struct WebPluginParams; +} + class GURL; class SkBitmap; namespace content { -class RenderFrame; - -// Plugin instances are "peripheral" if they are heuristically determined to be -// not "essential" to the web page content. See comments on -// RenderFrame::ShouldThrottleContent for details on these heuristics. -enum PluginPowerSaverMode { - // Plugin content is main content, and therefore never throttled. - POWER_SAVER_MODE_ESSENTIAL = 0, - // Plugin content is peripheral, but throttling is disabled. - POWER_SAVER_MODE_PERIPHERAL_UNTHROTTLED = 1, - // Plugin content is peripheral, and throttling is enabled. - POWER_SAVER_MODE_PERIPHERAL_THROTTLED = 2 -}; - // This class manages the metric collection, throttling, and unthrottling of a // single peripheral plugin instance. If the Power Saver feature is disabled, // the plugin instance will never actually be throttled, but still collects @@ -73,11 +63,7 @@ class CONTENT_EXPORT PluginInstanceThrottler { virtual void OnThrottlerDestroyed() {} }; - // Returns a nullptr if no throttler needed based on |power_saver_mode|. - static scoped_ptr<PluginInstanceThrottler> Get( - RenderFrame* frame, - const GURL& plugin_url, - PluginPowerSaverMode power_saver_mode); + static scoped_ptr<PluginInstanceThrottler> Create(bool power_saver_enabled); static void RecordUnthrottleMethodMetric(PowerSaverUnthrottleMethod method); diff --git a/content/public/renderer/render_frame.h b/content/public/renderer/render_frame.h index 6b32dd5..875e87d 100644 --- a/content/public/renderer/render_frame.h +++ b/content/public/renderer/render_frame.h @@ -120,34 +120,6 @@ class CONTENT_EXPORT RenderFrame : public IPC::Listener, virtual void RegisterPeripheralPlugin( const GURL& content_origin, const base::Closure& unthrottle_callback) = 0; - - // Returns true if this plugin should have power saver enabled. - // - // Power Saver is enabled for plugin content that are cross-origin and - // heuristically determined to be not essential to the web page content. - // - // Plugin content is defined to be cross-origin when the plugin source's - // origin differs from the top level frame's origin. For example: - // - Cross-origin: a.com -> b.com/plugin.swf - // - Cross-origin: a.com -> b.com/iframe.html -> b.com/plugin.swf - // - Same-origin: a.com -> b.com/iframe-to-a.html -> a.com/plugin.swf - // - // |page_frame_url| is the URL of the frame containing the plugin, which may - // be different from the URL of the top level document. - // - // |poster_image| may be NULL. It is set to the absolute URL of the poster - // image if it exists and this method returns true. Otherwise, an empty GURL. - // - // |cross_origin_main_content| may be NULL. It is set to true if the - // plugin content is cross-origin but still the "main attraction" of the page. - virtual bool ShouldThrottleContent(const blink::WebPluginParams& params, - const GURL& page_frame_url, - GURL* poster_image, - bool* cross_origin_main_content) const = 0; - - // Whitelists a |content_origin| so its content will never be throttled in - // this RenderFrame. Whitelist is cleared by top level navigation. - virtual void WhitelistContentOrigin(const GURL& content_origin) = 0; #endif // Returns true if this frame is a FTP directory listing. diff --git a/content/renderer/pepper/pepper_plugin_instance_impl.cc b/content/renderer/pepper/pepper_plugin_instance_impl.cc index 6f5dafe..4d4dec4 100644 --- a/content/renderer/pepper/pepper_plugin_instance_impl.cc +++ b/content/renderer/pepper/pepper_plugin_instance_impl.cc @@ -816,6 +816,9 @@ bool PepperPluginInstanceImpl::Initialize( if (throttler) { throttler_ = throttler.Pass(); throttler_->AddObserver(this); + throttler_->Initialize(render_frame_, plugin_url_.GetOrigin(), + module()->name(), + container()->element().boundsInViewportSpace()); } message_channel_ = MessageChannel::Create(this, &message_channel_object_); diff --git a/content/renderer/pepper/plugin_instance_throttler_impl.cc b/content/renderer/pepper/plugin_instance_throttler_impl.cc index f1fe7c4..432dfaa 100644 --- a/content/renderer/pepper/plugin_instance_throttler_impl.cc +++ b/content/renderer/pepper/plugin_instance_throttler_impl.cc @@ -7,9 +7,11 @@ #include "base/metrics/histogram.h" #include "base/time/time.h" #include "content/public/common/content_constants.h" -#include "content/public/renderer/render_frame.h" #include "content/public/renderer/render_thread.h" +#include "content/renderer/render_frame_impl.h" +#include "third_party/WebKit/public/platform/WebRect.h" #include "third_party/WebKit/public/web/WebInputEvent.h" +#include "third_party/WebKit/public/web/WebPluginParams.h" #include "ui/gfx/color_utils.h" #include "url/gurl.h" @@ -17,10 +19,6 @@ namespace content { namespace { -// When we give up waiting for a suitable preview frame, and simply suspend -// the plugin where it's at. In milliseconds. -const int kThrottleTimeout = 5000; - // Threshold for 'boring' score to accept a frame as good enough to be a // representative keyframe. Units are the ratio of all pixels that are within // the most common luma bin. The same threshold is used for history thumbnails. @@ -31,18 +29,12 @@ const int kMinimumConsecutiveInterestingFrames = 4; } // namespace // static -scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Get( - RenderFrame* frame, - const GURL& plugin_url, - PluginPowerSaverMode power_saver_mode) { - if (power_saver_mode == PluginPowerSaverMode::POWER_SAVER_MODE_ESSENTIAL) - return nullptr; - - bool power_saver_enabled = - power_saver_mode == - PluginPowerSaverMode::POWER_SAVER_MODE_PERIPHERAL_THROTTLED; - return make_scoped_ptr( - new PluginInstanceThrottlerImpl(frame, plugin_url, power_saver_enabled)); +const int PluginInstanceThrottlerImpl::kMaximumFramesToExamine = 150; + +// static +scoped_ptr<PluginInstanceThrottler> PluginInstanceThrottler::Create( + bool power_saver_enabled) { + return make_scoped_ptr(new PluginInstanceThrottlerImpl(power_saver_enabled)); } // static @@ -54,35 +46,18 @@ void PluginInstanceThrottler::RecordUnthrottleMethodMetric( } PluginInstanceThrottlerImpl::PluginInstanceThrottlerImpl( - RenderFrame* frame, - const GURL& plugin_url, bool power_saver_enabled) - : state_(power_saver_enabled ? POWER_SAVER_ENABLED_AWAITING_KEYFRAME - : POWER_SAVER_DISABLED), + : state_(power_saver_enabled ? THROTTLER_STATE_AWAITING_KEYFRAME + : THROTTLER_STATE_POWER_SAVER_DISABLED), is_hidden_for_placeholder_(false), consecutive_interesting_frames_(0), - keyframe_extraction_timed_out_(false), + frames_examined_(0), weak_factory_(this) { - // To collect UMAs, register peripheral content even if power saver disabled. - if (frame) { - frame->RegisterPeripheralPlugin( - plugin_url.GetOrigin(), - base::Bind(&PluginInstanceThrottlerImpl::MarkPluginEssential, - weak_factory_.GetWeakPtr(), UNTHROTTLE_METHOD_BY_WHITELIST)); - } - - if (power_saver_enabled) { - base::MessageLoop::current()->PostDelayedTask( - FROM_HERE, - base::Bind(&PluginInstanceThrottlerImpl::TimeoutKeyframeExtraction, - weak_factory_.GetWeakPtr()), - base::TimeDelta::FromMilliseconds(kThrottleTimeout)); - } } PluginInstanceThrottlerImpl::~PluginInstanceThrottlerImpl() { FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottlerDestroyed()); - if (state_ != PLUGIN_INSTANCE_MARKED_ESSENTIAL) + if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL) RecordUnthrottleMethodMetric(UNTHROTTLE_METHOD_NEVER); } @@ -95,7 +70,7 @@ void PluginInstanceThrottlerImpl::RemoveObserver(Observer* observer) { } bool PluginInstanceThrottlerImpl::IsThrottled() const { - return state_ == POWER_SAVER_ENABLED_PLUGIN_THROTTLED; + return state_ == THROTTLER_STATE_PLUGIN_THROTTLED; } bool PluginInstanceThrottlerImpl::IsHiddenForPlaceholder() const { @@ -104,11 +79,11 @@ bool PluginInstanceThrottlerImpl::IsHiddenForPlaceholder() const { void PluginInstanceThrottlerImpl::MarkPluginEssential( PowerSaverUnthrottleMethod method) { - if (state_ == PLUGIN_INSTANCE_MARKED_ESSENTIAL) + if (state_ == THROTTLER_STATE_MARKED_ESSENTIAL) return; bool was_throttled = IsThrottled(); - state_ = PLUGIN_INSTANCE_MARKED_ESSENTIAL; + state_ = THROTTLER_STATE_MARKED_ESSENTIAL; RecordUnthrottleMethodMetric(method); if (was_throttled) @@ -120,18 +95,49 @@ void PluginInstanceThrottlerImpl::SetHiddenForPlaceholder(bool hidden) { FOR_EACH_OBSERVER(Observer, observer_list_, OnHiddenForPlaceholder(hidden)); } +void PluginInstanceThrottlerImpl::Initialize( + RenderFrameImpl* frame, + const GURL& content_origin, + const std::string& plugin_module_name, + const blink::WebRect& bounds) { + // |frame| may be nullptr in tests. + if (frame) { + PluginPowerSaverHelper* helper = frame->plugin_power_saver_helper(); + bool cross_origin_main_content = false; + if (!helper->ShouldThrottleContent(content_origin, plugin_module_name, + bounds.width, bounds.height, + &cross_origin_main_content)) { + state_ = THROTTLER_STATE_MARKED_ESSENTIAL; + + if (cross_origin_main_content) + helper->WhitelistContentOrigin(content_origin); + + return; + } + + // To collect UMAs, register peripheral content even if power saver mode + // is disabled. + helper->RegisterPeripheralPlugin( + content_origin, + base::Bind(&PluginInstanceThrottlerImpl::MarkPluginEssential, + weak_factory_.GetWeakPtr(), UNTHROTTLE_METHOD_BY_WHITELIST)); + } +} + void PluginInstanceThrottlerImpl::OnImageFlush(const SkBitmap* bitmap) { DCHECK(needs_representative_keyframe()); if (!bitmap) return; + ++frames_examined_; + double boring_score = color_utils::CalculateBoringScore(*bitmap); if (boring_score <= kAcceptableFrameMaximumBoringness) ++consecutive_interesting_frames_; else consecutive_interesting_frames_ = 0; - if (keyframe_extraction_timed_out_ || + if (frames_examined_ >= kMaximumFramesToExamine || consecutive_interesting_frames_ >= kMinimumConsecutiveInterestingFrames) { FOR_EACH_OBSERVER(Observer, observer_list_, OnKeyframeExtracted(bitmap)); EngageThrottle(); @@ -147,7 +153,7 @@ bool PluginInstanceThrottlerImpl::ConsumeInputEvent( if (event.modifiers & blink::WebInputEvent::Modifiers::RightButtonDown) return false; - if (state_ != PLUGIN_INSTANCE_MARKED_ESSENTIAL && + if (state_ != THROTTLER_STATE_MARKED_ESSENTIAL && event.type == blink::WebInputEvent::MouseUp && (event.modifiers & blink::WebInputEvent::LeftButtonDown)) { bool was_throttled = IsThrottled(); @@ -159,15 +165,11 @@ bool PluginInstanceThrottlerImpl::ConsumeInputEvent( } void PluginInstanceThrottlerImpl::EngageThrottle() { - if (state_ != POWER_SAVER_ENABLED_AWAITING_KEYFRAME) + if (state_ != THROTTLER_STATE_AWAITING_KEYFRAME) return; - state_ = POWER_SAVER_ENABLED_PLUGIN_THROTTLED; + state_ = THROTTLER_STATE_PLUGIN_THROTTLED; FOR_EACH_OBSERVER(Observer, observer_list_, OnThrottleStateChange()); } -void PluginInstanceThrottlerImpl::TimeoutKeyframeExtraction() { - keyframe_extraction_timed_out_ = true; -} - } // namespace content diff --git a/content/renderer/pepper/plugin_instance_throttler_impl.h b/content/renderer/pepper/plugin_instance_throttler_impl.h index c8cbb8b..226e19b 100644 --- a/content/renderer/pepper/plugin_instance_throttler_impl.h +++ b/content/renderer/pepper/plugin_instance_throttler_impl.h @@ -11,22 +11,20 @@ #include "content/common/content_export.h" #include "content/public/renderer/plugin_instance_throttler.h" #include "ppapi/shared_impl/ppb_view_shared.h" -#include "third_party/WebKit/public/platform/WebRect.h" namespace blink { class WebInputEvent; +struct WebRect; } -class SkBitmap; - namespace content { +class RenderFrameImpl; + class CONTENT_EXPORT PluginInstanceThrottlerImpl : public PluginInstanceThrottler { public: - PluginInstanceThrottlerImpl(RenderFrame* frame, - const GURL& plugin_url, - bool power_saver_enabled); + PluginInstanceThrottlerImpl(bool power_saver_enabled); ~PluginInstanceThrottlerImpl() override; @@ -39,14 +37,20 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl void SetHiddenForPlaceholder(bool hidden) override; bool needs_representative_keyframe() const { - return state_ == POWER_SAVER_ENABLED_AWAITING_KEYFRAME; + return state_ == THROTTLER_STATE_AWAITING_KEYFRAME; } bool power_saver_enabled() const { - return state_ == POWER_SAVER_ENABLED_AWAITING_KEYFRAME || - state_ == POWER_SAVER_ENABLED_PLUGIN_THROTTLED; + return state_ == THROTTLER_STATE_AWAITING_KEYFRAME || + state_ == THROTTLER_STATE_PLUGIN_THROTTLED; } + // Throttler needs to be initialized with the real plugin's view bounds. + void Initialize(RenderFrameImpl* frame, + const GURL& content_origin, + const std::string& plugin_module_name, + const blink::WebRect& bounds); + // Called when the plugin flushes it's graphics context. Supplies the // throttler with a candidate to use as the representative keyframe. void OnImageFlush(const SkBitmap* bitmap); @@ -57,32 +61,33 @@ class CONTENT_EXPORT PluginInstanceThrottlerImpl private: friend class PluginInstanceThrottlerImplTest; - enum State { - // Initial state if Power Saver is disabled. We are just collecting metrics. - POWER_SAVER_DISABLED, - // Initial state if Power Saver is enabled. Waiting for a keyframe. - POWER_SAVER_ENABLED_AWAITING_KEYFRAME, - // We've chosen a keyframe and the plug-in is throttled. - POWER_SAVER_ENABLED_PLUGIN_THROTTLED, - // Plugin instance is no longer considered peripheral. This can happen from - // a user click, whitelisting, or some other reason. We can end up in this - // state regardless of whether power saver is enabled. - PLUGIN_INSTANCE_MARKED_ESSENTIAL + enum ThrottlerState { + // Power saver is disabled, but the plugin instance is still peripheral. + THROTTLER_STATE_POWER_SAVER_DISABLED, + // Plugin has been found to be peripheral, Plugin Power Saver is enabled, + // and throttler is awaiting a representative keyframe. + THROTTLER_STATE_AWAITING_KEYFRAME, + // A representative keyframe has been chosen and the plugin is throttled. + THROTTLER_STATE_PLUGIN_THROTTLED, + // Plugin instance has been marked essential. + THROTTLER_STATE_MARKED_ESSENTIAL, }; - void EngageThrottle(); + // Maximum number of frames to examine for a suitable keyframe. After that, we + // simply suspend the plugin where it's at. Chosen arbitrarily. + static const int kMaximumFramesToExamine; - void TimeoutKeyframeExtraction(); + void EngageThrottle(); - State state_; + ThrottlerState state_; bool is_hidden_for_placeholder_; // Number of consecutive interesting frames we've encountered. int consecutive_interesting_frames_; - // If true, take the next frame as the keyframe regardless of interestingness. - bool keyframe_extraction_timed_out_; + // Number of frames we've examined to find a keyframe. + int frames_examined_; ObserverList<Observer> observer_list_; diff --git a/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc b/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc index fc284b0..0b3e398 100644 --- a/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc +++ b/content/renderer/pepper/plugin_instance_throttler_impl_unittest.cc @@ -27,6 +27,9 @@ class PluginInstanceThrottlerImplTest : public testing::Test, public PluginInstanceThrottler::Observer { protected: + const int kMaximumFramesToExamine = + PluginInstanceThrottlerImpl::kMaximumFramesToExamine; + PluginInstanceThrottlerImplTest() : change_callback_calls_(0) {} ~PluginInstanceThrottlerImplTest() override { throttler_->RemoveObserver(this); @@ -36,8 +39,10 @@ class PluginInstanceThrottlerImplTest blink::WebRect rect; rect.width = 100; rect.height = 100; - throttler_.reset(new PluginInstanceThrottlerImpl( - nullptr, GURL("http://example.com"), true /* power_saver_enabled */)); + throttler_.reset( + new PluginInstanceThrottlerImpl(true /* power_saver_enabled */)); + throttler_->Initialize(nullptr, GURL("http://example.com"), + "Shockwave Flash", rect); throttler_->AddObserver(this); } @@ -125,6 +130,19 @@ TEST_F(PluginInstanceThrottlerImplTest, ThrottleByKeyframe) { EXPECT_EQ(1, change_callback_calls()); } +TEST_F(PluginInstanceThrottlerImplTest, MaximumKeyframesAnalyzed) { + EXPECT_FALSE(throttler()->IsThrottled()); + EXPECT_EQ(0, change_callback_calls()); + + SkBitmap boring_bitmap; + + // Throttle after tons of boring bitmaps. + for (int i = 0; i < kMaximumFramesToExamine; ++i) { + throttler()->OnImageFlush(&boring_bitmap); + } + EXPECT_TRUE(throttler()->IsThrottled()); + EXPECT_EQ(1, change_callback_calls()); +} TEST_F(PluginInstanceThrottlerImplTest, IgnoreThrottlingAfterMouseUp) { EXPECT_FALSE(throttler()->IsThrottled()); EXPECT_EQ(0, change_callback_calls()); diff --git a/content/renderer/pepper/plugin_power_saver_helper.cc b/content/renderer/pepper/plugin_power_saver_helper.cc index b804ee6..e4ad0b4 100644 --- a/content/renderer/pepper/plugin_power_saver_helper.cc +++ b/content/renderer/pepper/plugin_power_saver_helper.cc @@ -7,6 +7,7 @@ #include "base/metrics/histogram.h" #include "base/strings/string_number_conversions.h" #include "content/common/frame_messages.h" +#include "content/public/common/content_constants.h" #include "content/public/renderer/document_state.h" #include "content/public/renderer/navigation_state.h" #include "content/public/renderer/render_frame.h" @@ -19,8 +20,6 @@ namespace content { namespace { -const char kPosterParamName[] = "poster"; - // Initial decision of the peripheral content decision. // These numeric values are used in UMA logs; do not change them. enum PeripheralHeuristicDecision { @@ -50,45 +49,6 @@ void RecordDecisionMetric(PeripheralHeuristicDecision decision) { HEURISTIC_DECISION_NUM_ITEMS); } -const char kWebPluginParamHeight[] = "height"; -const char kWebPluginParamWidth[] = "width"; - -// Returns true if valid non-negative height and width extracted. -// When this returns false, |width| and |height| are set to undefined values. -bool ExtractDimensions(const blink::WebPluginParams& params, - int* width, - int* height) { - DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size()); - DCHECK(width); - DCHECK(height); - bool width_extracted = false; - bool height_extracted = false; - for (size_t i = 0; i < params.attributeNames.size(); ++i) { - if (params.attributeNames[i].utf8() == kWebPluginParamWidth) { - width_extracted = - base::StringToInt(params.attributeValues[i].utf8(), width); - } else if (params.attributeNames[i].utf8() == kWebPluginParamHeight) { - height_extracted = - base::StringToInt(params.attributeValues[i].utf8(), height); - } - } - return width_extracted && height_extracted && *width >= 0 && *height >= 0; -} - -GURL GetPluginInstancePosterImage(const blink::WebPluginParams& params, - const GURL& page_base_url) { - DCHECK_EQ(params.attributeNames.size(), params.attributeValues.size()); - - for (size_t i = 0; i < params.attributeNames.size(); ++i) { - if (params.attributeNames[i] == kPosterParamName) { - std::string poster_value(params.attributeValues[i].utf8()); - if (!poster_value.empty()) - return page_base_url.Resolve(poster_value); - } - } - return GURL(); -} - } // namespace PluginPowerSaverHelper::PeripheralPlugin::PeripheralPlugin( @@ -148,25 +108,26 @@ void PluginPowerSaverHelper::OnUpdatePluginContentOriginWhitelist( void PluginPowerSaverHelper::RegisterPeripheralPlugin( const GURL& content_origin, const base::Closure& unthrottle_callback) { + DCHECK_EQ(content_origin.GetOrigin(), content_origin); peripheral_plugins_.push_back( PeripheralPlugin(content_origin, unthrottle_callback)); } bool PluginPowerSaverHelper::ShouldThrottleContent( - const blink::WebPluginParams& params, - const GURL& plugin_frame_url, - GURL* poster_image, + const GURL& content_origin, + const std::string& plugin_module_name, + int width, + int height, bool* cross_origin_main_content) const { - if (poster_image) - *poster_image = GURL(); + DCHECK_EQ(content_origin.GetOrigin(), content_origin); if (cross_origin_main_content) *cross_origin_main_content = false; - GURL content_origin = GURL(params.url).GetOrigin(); + // This feature has only been tested throughly with Flash thus far. + if (plugin_module_name != content::kFlashPluginName) + return false; - int width = 0; - int height = 0; - if (!ExtractDimensions(params, &width, &height)) + if (width <= 0 || height <= 0) return false; // TODO(alexmos): Update this to use the origin of the RemoteFrame when 426512 @@ -176,8 +137,6 @@ bool PluginPowerSaverHelper::ShouldThrottleContent( render_frame()->GetWebFrame()->view()->mainFrame(); if (main_frame->isWebRemoteFrame()) { RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL); - if (poster_image) - *poster_image = GetPluginInstancePosterImage(params, plugin_frame_url); return true; } @@ -211,8 +170,6 @@ bool PluginPowerSaverHelper::ShouldThrottleContent( } RecordDecisionMetric(HEURISTIC_DECISION_PERIPHERAL); - if (poster_image) - *poster_image = GetPluginInstancePosterImage(params, plugin_frame_url); return true; } diff --git a/content/renderer/pepper/plugin_power_saver_helper.h b/content/renderer/pepper/plugin_power_saver_helper.h index 67a9471..bc88cf5 100644 --- a/content/renderer/pepper/plugin_power_saver_helper.h +++ b/content/renderer/pepper/plugin_power_saver_helper.h @@ -15,6 +15,7 @@ namespace blink { struct WebPluginParams; +struct WebRect; } namespace content { @@ -27,10 +28,34 @@ class CONTENT_EXPORT PluginPowerSaverHelper : public RenderFrameObserver { // See RenderFrame for documentation. void RegisterPeripheralPlugin(const GURL& content_origin, const base::Closure& unthrottle_callback); - bool ShouldThrottleContent(const blink::WebPluginParams& params, - const GURL& plugin_frame_url, - GURL* poster_image, + + // Returns true if this plugin should have power saver enabled. + // + // Power Saver is enabled for plugin content that are cross-origin and + // heuristically determined to be not essential to the web page content. + // + // Plugin content is defined to be cross-origin when the plugin source's + // origin differs from the top level frame's origin. For example: + // - Cross-origin: a.com -> b.com/plugin.swf + // - Cross-origin: a.com -> b.com/iframe.html -> b.com/plugin.swf + // - Same-origin: a.com -> b.com/iframe-to-a.html -> a.com/plugin.swf + // + // |page_frame_url| is the URL of the frame containing the plugin, which may + // be different from the URL of the top level document. + // + // |poster_image| may be NULL. It is set to the absolute URL of the poster + // image if it exists and this method returns true. Otherwise, an empty GURL. + // + // |cross_origin_main_content| may be NULL. It is set to true if the + // plugin content is cross-origin but still the "main attraction" of the page. + bool ShouldThrottleContent(const GURL& content_origin, + const std::string& plugin_module_name, + int width, + int height, bool* cross_origin_main_content) const; + + // Whitelists a |content_origin| so its content will never be throttled in + // this RenderFrame. Whitelist is cleared by top level navigation. void WhitelistContentOrigin(const GURL& content_origin); private: diff --git a/content/renderer/pepper/plugin_power_saver_helper_browsertest.cc b/content/renderer/pepper/plugin_power_saver_helper_browsertest.cc index 60b40f0..7444d1c 100644 --- a/content/renderer/pepper/plugin_power_saver_helper_browsertest.cc +++ b/content/renderer/pepper/plugin_power_saver_helper_browsertest.cc @@ -5,6 +5,7 @@ #include "base/run_loop.h" #include "content/common/frame_messages.h" #include "content/common/view_message_enums.h" +#include "content/public/common/content_constants.h" #include "content/public/test/render_view_test.h" #include "content/renderer/pepper/plugin_power_saver_helper.h" #include "content/renderer/render_frame_impl.h" @@ -25,6 +26,10 @@ class PluginPowerSaverHelperTest : public RenderViewTest { return static_cast<RenderFrameImpl*>(view_->GetMainRenderFrame()); } + PluginPowerSaverHelper* helper() { + return frame()->plugin_power_saver_helper(); + } + void SetUp() override { RenderViewTest::SetUp(); sink_ = &render_thread_->sink(); @@ -60,103 +65,62 @@ class PluginPowerSaverHelperTest : public RenderViewTest { DISALLOW_COPY_AND_ASSIGN(PluginPowerSaverHelperTest); }; -TEST_F(PluginPowerSaverHelperTest, PosterImage) { - size_t size = 3; - blink::WebVector<blink::WebString> names(size); - blink::WebVector<blink::WebString> values(size); - - blink::WebPluginParams params; - params.url = GURL("http://b.com/foo.swf"); - - params.attributeNames.swap(names); - params.attributeValues.swap(values); - - params.attributeNames[0] = "poster"; - params.attributeNames[1] = "height"; - params.attributeNames[2] = "width"; - params.attributeValues[0] = "poster.jpg"; - params.attributeValues[1] = "100"; - params.attributeValues[2] = "100"; - - GURL poster_result; - - EXPECT_TRUE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", "poster.jpg", "100", "100"), - GURL("http://a.com/page.html"), &poster_result, nullptr)); - EXPECT_EQ(GURL("http://a.com/poster.jpg"), poster_result); - - // Ignore empty poster paramaters. - EXPECT_TRUE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "100", "100"), - GURL("http://a.com/page.html"), &poster_result, nullptr)); - EXPECT_EQ(GURL(), poster_result); - - // Ignore poster parameter when plugin is big (shouldn't be throttled). - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", "poster.jpg", "500", "500"), - GURL("http://a.com/page.html"), &poster_result, nullptr)); - EXPECT_EQ(GURL(), poster_result); -} - TEST_F(PluginPowerSaverHelperTest, AllowSameOrigin) { - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams(std::string(), std::string(), "100", "100"), GURL(), nullptr, - nullptr)); - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams(std::string(), std::string(), "1000", "1000"), GURL(), nullptr, - nullptr)); + EXPECT_FALSE(helper()->ShouldThrottleContent(GURL(), kFlashPluginName, 100, + 100, nullptr)); + EXPECT_FALSE(helper()->ShouldThrottleContent(GURL(), kFlashPluginName, 1000, + 1000, nullptr)); } TEST_F(PluginPowerSaverHelperTest, DisallowCrossOriginUnlessLarge) { bool cross_origin_main_content = false; - EXPECT_TRUE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "100", "100"), GURL(), - nullptr, &cross_origin_main_content)); + EXPECT_TRUE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 100, 100, + &cross_origin_main_content)); EXPECT_FALSE(cross_origin_main_content); - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "1000", "1000"), GURL(), - nullptr, &cross_origin_main_content)); + EXPECT_FALSE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 1000, 1000, + &cross_origin_main_content)); EXPECT_TRUE(cross_origin_main_content); } TEST_F(PluginPowerSaverHelperTest, AlwaysAllowTinyContent) { bool cross_origin_main_content = false; - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams(std::string(), std::string(), "1", "1"), GURL(), nullptr, - &cross_origin_main_content)); + EXPECT_FALSE( + helper()->ShouldThrottleContent(GURL(), kFlashPluginName, 1, 1, nullptr)); EXPECT_FALSE(cross_origin_main_content); - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "1", "1"), GURL(), - nullptr, &cross_origin_main_content)); + EXPECT_FALSE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 1, 1, + &cross_origin_main_content)); EXPECT_FALSE(cross_origin_main_content); - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "5", "5"), GURL(), - nullptr, &cross_origin_main_content)); + EXPECT_FALSE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 5, 5, + &cross_origin_main_content)); EXPECT_FALSE(cross_origin_main_content); - EXPECT_TRUE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "10", "10"), GURL(), - nullptr, &cross_origin_main_content)); + EXPECT_TRUE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 10, 10, + &cross_origin_main_content)); EXPECT_FALSE(cross_origin_main_content); } TEST_F(PluginPowerSaverHelperTest, TemporaryOriginWhitelist) { bool cross_origin_main_content = false; - EXPECT_TRUE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "100", "100"), GURL(), - nullptr, &cross_origin_main_content)); + EXPECT_TRUE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 100, 100, + &cross_origin_main_content)); EXPECT_FALSE(cross_origin_main_content); // Clear out other messages so we find just the plugin power saver IPCs. sink_->ClearMessages(); - frame()->WhitelistContentOrigin(GURL("http://b.com")); - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "100", "100"), GURL(), - nullptr, &cross_origin_main_content)); + helper()->WhitelistContentOrigin(GURL("http://b.com")); + EXPECT_FALSE(helper()->ShouldThrottleContent(GURL("http://b.com"), + kFlashPluginName, 100, 100, + &cross_origin_main_content)); EXPECT_FALSE(cross_origin_main_content); // Test that we've sent an IPC to the browser. @@ -183,17 +147,15 @@ TEST_F(PluginPowerSaverHelperTest, UnthrottleOnExPostFactoWhitelist) { } TEST_F(PluginPowerSaverHelperTest, ClearWhitelistOnNavigate) { - frame()->WhitelistContentOrigin(GURL("http://b.com")); + helper()->WhitelistContentOrigin(GURL("http://b.com")); - EXPECT_FALSE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "100", "100"), GURL(), - nullptr, nullptr)); + EXPECT_FALSE(helper()->ShouldThrottleContent( + GURL("http://b.com"), kFlashPluginName, 100, 100, nullptr)); LoadHTML("<html></html>"); - EXPECT_TRUE(frame()->ShouldThrottleContent( - MakeParams("http://b.com/foo.swf", std::string(), "100", "100"), GURL(), - nullptr, nullptr)); + EXPECT_TRUE(helper()->ShouldThrottleContent( + GURL("http://b.com"), kFlashPluginName, 100, 100, nullptr)); } } // namespace content diff --git a/content/renderer/render_frame_impl.cc b/content/renderer/render_frame_impl.cc index 6827da7..e27310c 100644 --- a/content/renderer/render_frame_impl.cc +++ b/content/renderer/render_frame_impl.cc @@ -1765,19 +1765,6 @@ void RenderFrameImpl::RegisterPeripheralPlugin( return plugin_power_saver_helper_->RegisterPeripheralPlugin( content_origin, unthrottle_callback); } - -bool RenderFrameImpl::ShouldThrottleContent( - const blink::WebPluginParams& params, - const GURL& page_frame_url, - GURL* poster_image, - bool* cross_origin_main_content) const { - return plugin_power_saver_helper_->ShouldThrottleContent( - params, page_frame_url, poster_image, cross_origin_main_content); -} - -void RenderFrameImpl::WhitelistContentOrigin(const GURL& content_origin) { - return plugin_power_saver_helper_->WhitelistContentOrigin(content_origin); -} #endif // defined(ENABLE_PLUGINS) bool RenderFrameImpl::IsFTPDirectoryListing() { diff --git a/content/renderer/render_frame_impl.h b/content/renderer/render_frame_impl.h index 7c56df7..9c53107 100644 --- a/content/renderer/render_frame_impl.h +++ b/content/renderer/render_frame_impl.h @@ -315,11 +315,9 @@ class CONTENT_EXPORT RenderFrameImpl void RegisterPeripheralPlugin( const GURL& content_origin, const base::Closure& unthrottle_callback) override; - bool ShouldThrottleContent(const blink::WebPluginParams& params, - const GURL& page_frame_url, - GURL* poster_image, - bool* cross_origin_main_content) const override; - void WhitelistContentOrigin(const GURL& content_origin) override; + PluginPowerSaverHelper* plugin_power_saver_helper() { + return plugin_power_saver_helper_; + } #endif bool IsFTPDirectoryListing() override; void AttachGuest(int element_instance_id) override; |