This source file includes following definitions.
- EncodeScreenshot
- data
- EncodeOnWorker
- min_screenshot_interval_ms_
- TakeScreenshot
- ClearAllScreenshots
- TakeScreenshotImpl
- SetMinScreenshotIntervalMS
- OnScreenshotTaken
- GetScreenshotCount
- OnScreenshotEncodeComplete
- OnScreenshotSet
- ClearScreenshot
- PurgeScreenshotsIfNecessary
#include "content/browser/frame_host/navigation_entry_screenshot_manager.h"
#include "base/command_line.h"
#include "base/threading/worker_pool.h"
#include "content/browser/frame_host/navigation_controller_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/public/browser/render_widget_host.h"
#include "content/public/browser/render_widget_host_view.h"
#include "content/public/common/content_switches.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkPaint.h"
#include "third_party/skia/include/effects/SkLumaColorFilter.h"
#include "ui/gfx/codec/png_codec.h"
namespace {
const int kMinScreenshotIntervalMS = 1000;
}
namespace content {
class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
public:
ScreenshotData() {
}
void EncodeScreenshot(const SkBitmap& bitmap, base::Closure callback) {
if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
base::Bind(&ScreenshotData::EncodeOnWorker,
this,
bitmap),
callback,
true)) {
callback.Run();
}
}
scoped_refptr<base::RefCountedBytes> data() const { return data_; }
private:
friend class base::RefCountedThreadSafe<ScreenshotData>;
virtual ~ScreenshotData() {
}
void EncodeOnWorker(const SkBitmap& bitmap) {
std::vector<unsigned char> data;
SkBitmap a8Bitmap;
a8Bitmap.setConfig(SkBitmap::kA8_Config,
bitmap.width(),
bitmap.height(),
0);
a8Bitmap.allocPixels();
SkCanvas canvas(a8Bitmap);
SkPaint paint;
SkColorFilter* filter = SkLumaColorFilter::Create();
paint.setColorFilter(filter);
filter->unref();
canvas.drawBitmap(bitmap, SK_Scalar1, SK_Scalar1, &paint);
if (gfx::PNGCodec::EncodeA8SkBitmap(a8Bitmap, &data))
data_ = new base::RefCountedBytes(data);
}
scoped_refptr<base::RefCountedBytes> data_;
DISALLOW_COPY_AND_ASSIGN(ScreenshotData);
};
NavigationEntryScreenshotManager::NavigationEntryScreenshotManager(
NavigationControllerImpl* owner)
: owner_(owner),
screenshot_factory_(this),
min_screenshot_interval_ms_(kMinScreenshotIntervalMS) {
}
NavigationEntryScreenshotManager::~NavigationEntryScreenshotManager() {
}
void NavigationEntryScreenshotManager::TakeScreenshot() {
static bool overscroll_enabled = CommandLine::ForCurrentProcess()->
GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
if (!overscroll_enabled)
return;
NavigationEntryImpl* entry =
NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry());
if (!entry)
return;
RenderViewHost* render_view_host =
owner_->delegate()->GetRenderViewHost();
if (!static_cast<RenderViewHostImpl*>
(render_view_host)->overscroll_controller()) {
return;
}
content::RenderWidgetHostView* view = render_view_host->GetView();
if (!view)
return;
base::Time now = base::Time::Now();
if (now - last_screenshot_time_ <
base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) {
return;
}
last_screenshot_time_ = now;
TakeScreenshotImpl(render_view_host, entry);
}
void NavigationEntryScreenshotManager::ClearAllScreenshots() {
int count = owner_->GetEntryCount();
for (int i = 0; i < count; ++i) {
ClearScreenshot(NavigationEntryImpl::FromNavigationEntry(
owner_->GetEntryAtIndex(i)));
}
DCHECK_EQ(GetScreenshotCount(), 0);
}
void NavigationEntryScreenshotManager::TakeScreenshotImpl(
RenderViewHost* host,
NavigationEntryImpl* entry) {
DCHECK(host && host->GetView());
DCHECK(entry);
SkBitmap::Config preferred_format = host->PreferredReadbackFormat();
host->CopyFromBackingStore(
gfx::Rect(),
host->GetView()->GetViewBounds().size(),
base::Bind(&NavigationEntryScreenshotManager::OnScreenshotTaken,
screenshot_factory_.GetWeakPtr(),
entry->GetUniqueID()),
preferred_format);
}
void NavigationEntryScreenshotManager::SetMinScreenshotIntervalMS(
int interval_ms) {
DCHECK_GE(interval_ms, 0);
min_screenshot_interval_ms_ = interval_ms;
}
void NavigationEntryScreenshotManager::OnScreenshotTaken(int unique_id,
bool success,
const SkBitmap& bitmap) {
NavigationEntryImpl* entry = NULL;
int entry_count = owner_->GetEntryCount();
for (int i = 0; i < entry_count; ++i) {
NavigationEntry* iter = owner_->GetEntryAtIndex(i);
if (iter->GetUniqueID() == unique_id) {
entry = NavigationEntryImpl::FromNavigationEntry(iter);
break;
}
}
if (!entry) {
LOG(ERROR) << "Invalid entry with unique id: " << unique_id;
return;
}
if (!success || bitmap.empty() || bitmap.isNull()) {
if (!ClearScreenshot(entry))
OnScreenshotSet(entry);
return;
}
scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
screenshot->EncodeScreenshot(
bitmap,
base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete,
screenshot_factory_.GetWeakPtr(),
unique_id,
screenshot));
}
int NavigationEntryScreenshotManager::GetScreenshotCount() const {
int screenshot_count = 0;
int entry_count = owner_->GetEntryCount();
for (int i = 0; i < entry_count; ++i) {
NavigationEntryImpl* entry =
NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
if (entry->screenshot().get())
screenshot_count++;
}
return screenshot_count;
}
void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
int unique_id,
scoped_refptr<ScreenshotData> screenshot) {
NavigationEntryImpl* entry = NULL;
int entry_count = owner_->GetEntryCount();
for (int i = 0; i < entry_count; ++i) {
NavigationEntry* iter = owner_->GetEntryAtIndex(i);
if (iter->GetUniqueID() == unique_id) {
entry = NavigationEntryImpl::FromNavigationEntry(iter);
break;
}
}
if (!entry)
return;
entry->SetScreenshotPNGData(screenshot->data());
OnScreenshotSet(entry);
}
void NavigationEntryScreenshotManager::OnScreenshotSet(
NavigationEntryImpl* entry) {
if (entry->screenshot().get())
PurgeScreenshotsIfNecessary();
}
bool NavigationEntryScreenshotManager::ClearScreenshot(
NavigationEntryImpl* entry) {
if (!entry->screenshot().get())
return false;
entry->SetScreenshotPNGData(NULL);
return true;
}
void NavigationEntryScreenshotManager::PurgeScreenshotsIfNecessary() {
const int kMaxScreenshots = 10;
int screenshot_count = GetScreenshotCount();
if (screenshot_count < kMaxScreenshots)
return;
const int current = owner_->GetCurrentEntryIndex();
const int num_entries = owner_->GetEntryCount();
int available_slots = kMaxScreenshots;
if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
->screenshot().get()) {
--available_slots;
}
int back = current - 1;
int forward = current + 1;
while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
if (back >= 0) {
NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
owner_->GetEntryAtIndex(back));
if (entry->screenshot().get())
--available_slots;
--back;
}
if (available_slots > 0 && forward < num_entries) {
NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
owner_->GetEntryAtIndex(forward));
if (entry->screenshot().get())
--available_slots;
++forward;
}
}
while (screenshot_count > kMaxScreenshots && back >= 0) {
NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
owner_->GetEntryAtIndex(back));
if (ClearScreenshot(entry))
--screenshot_count;
--back;
}
while (screenshot_count > kMaxScreenshots && forward < num_entries) {
NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
owner_->GetEntryAtIndex(forward));
if (ClearScreenshot(entry))
--screenshot_count;
++forward;
}
CHECK_GE(screenshot_count, 0);
CHECK_LE(screenshot_count, kMaxScreenshots);
}
}