blob: ad67ea66949699674275acee9b6515494fe99d0c [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 "content/renderer/pepper/plugin_instance_throttler_impl.h"
#include <memory>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/logging.h"
#include "base/test/task_environment.h"
#include "content/public/common/content_switches.h"
#include "content/public/renderer/render_frame.h"
#include "skia/ext/platform_canvas.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/common/input/web_input_event.h"
#include "third_party/blink/public/common/input/web_mouse_event.h"
#include "third_party/blink/public/web/web_plugin_params.h"
#include "ui/events/base_event_utils.h"
#include "ui/gfx/canvas.h"
#include "url/gurl.h"
#include "url/origin.h"
using testing::_;
using testing::Return;
namespace content {
class PluginInstanceThrottlerImplTest
: public testing::Test,
public PluginInstanceThrottler::Observer {
protected:
const int kMaximumFramesToExamine =
PluginInstanceThrottlerImpl::kMaximumFramesToExamine;
PluginInstanceThrottlerImplTest() : change_callback_calls_(0) {}
~PluginInstanceThrottlerImplTest() override {
throttler_->RemoveObserver(this);
}
void SetUp() override {
throttler_.reset(
new PluginInstanceThrottlerImpl(RenderFrame::DONT_RECORD_DECISION));
throttler_->Initialize(nullptr,
url::Origin::Create(GURL("http://example.com")),
"Shockwave Flash", gfx::Size(100, 100));
throttler_->AddObserver(this);
}
PluginInstanceThrottlerImpl* throttler() {
DCHECK(throttler_.get());
return throttler_.get();
}
void DisablePowerSaverByRetroactiveWhitelist() {
throttler()->MarkPluginEssential(
PluginInstanceThrottlerImpl::UNTHROTTLE_METHOD_BY_WHITELIST);
}
int change_callback_calls() { return change_callback_calls_; }
void EngageThrottle() { throttler_->EngageThrottle(); }
void SendEventAndTest(blink::WebInputEvent::Type event_type,
bool expect_consumed,
bool expect_throttled,
int expect_change_callback_count) {
blink::WebMouseEvent event(event_type,
blink::WebInputEvent::Modifiers::kLeftButtonDown,
ui::EventTimeForNow());
EXPECT_EQ(expect_consumed, throttler()->ConsumeInputEvent(event));
EXPECT_EQ(expect_throttled, throttler()->IsThrottled());
EXPECT_EQ(expect_change_callback_count, change_callback_calls());
}
private:
// PluginInstanceThrottlerImpl::Observer
void OnThrottleStateChange() override { ++change_callback_calls_; }
std::unique_ptr<PluginInstanceThrottlerImpl> throttler_;
int change_callback_calls_;
base::test::SingleThreadTaskEnvironment task_environment_;
};
TEST_F(PluginInstanceThrottlerImplTest, ThrottleAndUnthrottleByClick) {
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
EngageThrottle();
EXPECT_TRUE(throttler()->IsThrottled());
EXPECT_EQ(1, change_callback_calls());
// MouseUp while throttled should be consumed and disengage throttling.
SendEventAndTest(blink::WebInputEvent::Type::kMouseUp, true, false, 2);
}
TEST_F(PluginInstanceThrottlerImplTest, ThrottleByKeyframe) {
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
SkBitmap boring_bitmap;
gfx::Canvas canvas(gfx::Size(20, 10), 1.0f, true);
canvas.FillRect(gfx::Rect(20, 10), SK_ColorBLACK);
canvas.FillRect(gfx::Rect(10, 10), SK_ColorWHITE);
SkBitmap interesting_bitmap = canvas.GetBitmap();
// Don't throttle for a boring frame.
throttler()->OnImageFlush(boring_bitmap);
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
// Throttle after an interesting frame.
throttler()->OnImageFlush(interesting_bitmap);
EXPECT_TRUE(throttler()->IsThrottled());
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());
// MouseUp before throttling engaged should not be consumed, but should
// prevent subsequent throttling from engaging.
SendEventAndTest(blink::WebInputEvent::Type::kMouseUp, false, false, 0);
EngageThrottle();
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
}
TEST_F(PluginInstanceThrottlerImplTest, FastWhitelisting) {
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
DisablePowerSaverByRetroactiveWhitelist();
EngageThrottle();
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
}
TEST_F(PluginInstanceThrottlerImplTest, SlowWhitelisting) {
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
EngageThrottle();
EXPECT_TRUE(throttler()->IsThrottled());
EXPECT_EQ(1, change_callback_calls());
DisablePowerSaverByRetroactiveWhitelist();
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(2, change_callback_calls());
}
TEST_F(PluginInstanceThrottlerImplTest, EventConsumption) {
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
EngageThrottle();
EXPECT_TRUE(throttler()->IsThrottled());
EXPECT_EQ(1, change_callback_calls());
// Consume but don't unthrottle on a variety of other events.
SendEventAndTest(blink::WebInputEvent::Type::kMouseDown, true, true, 1);
SendEventAndTest(blink::WebInputEvent::Type::kMouseWheel, true, true, 1);
SendEventAndTest(blink::WebInputEvent::Type::kMouseMove, true, true, 1);
SendEventAndTest(blink::WebInputEvent::Type::kKeyDown, true, true, 1);
SendEventAndTest(blink::WebInputEvent::Type::kKeyUp, true, true, 1);
// Consume and unthrottle on MouseUp
SendEventAndTest(blink::WebInputEvent::Type::kMouseUp, true, false, 2);
// Don't consume events after unthrottle.
SendEventAndTest(blink::WebInputEvent::Type::kMouseDown, false, false, 2);
SendEventAndTest(blink::WebInputEvent::Type::kMouseWheel, false, false, 2);
SendEventAndTest(blink::WebInputEvent::Type::kMouseMove, false, false, 2);
SendEventAndTest(blink::WebInputEvent::Type::kKeyDown, false, false, 2);
SendEventAndTest(blink::WebInputEvent::Type::kKeyUp, false, false, 2);
// Subsequent MouseUps should also not be consumed.
SendEventAndTest(blink::WebInputEvent::Type::kMouseUp, false, false, 2);
}
TEST_F(PluginInstanceThrottlerImplTest, ThrottleOnLeftClickOnly) {
EXPECT_FALSE(throttler()->IsThrottled());
EXPECT_EQ(0, change_callback_calls());
EngageThrottle();
EXPECT_TRUE(throttler()->IsThrottled());
EXPECT_EQ(1, change_callback_calls());
blink::WebMouseEvent event(blink::WebInputEvent::Type::kMouseUp,
blink::WebInputEvent::Modifiers::kRightButtonDown,
ui::EventTimeForNow());
EXPECT_FALSE(throttler()->ConsumeInputEvent(event));
EXPECT_TRUE(throttler()->IsThrottled());
event.SetModifiers(blink::WebInputEvent::Modifiers::kMiddleButtonDown);
EXPECT_TRUE(throttler()->ConsumeInputEvent(event));
EXPECT_TRUE(throttler()->IsThrottled());
event.SetModifiers(blink::WebInputEvent::Modifiers::kLeftButtonDown);
EXPECT_TRUE(throttler()->ConsumeInputEvent(event));
EXPECT_FALSE(throttler()->IsThrottled());
}
} // namespace content