1 // Copyright 2013 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/browser/frame_host/navigation_entry_screenshot_manager.h"
7 #include "base/command_line.h"
8 #include "base/threading/worker_pool.h"
9 #include "content/browser/frame_host/navigation_controller_impl.h"
10 #include "content/browser/frame_host/navigation_entry_impl.h"
11 #include "content/browser/renderer_host/render_view_host_impl.h"
12 #include "content/public/browser/render_widget_host.h"
13 #include "content/public/browser/render_widget_host_view.h"
14 #include "content/public/common/content_switches.h"
15 #include "ui/gfx/codec/png_codec.h"
19 // Minimum delay between taking screenshots.
20 const int kMinScreenshotIntervalMS = 1000;
26 // Encodes an SkBitmap to PNG data in a worker thread.
27 class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
32 void EncodeScreenshot(const SkBitmap& bitmap, base::Closure callback) {
33 if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
34 base::Bind(&ScreenshotData::EncodeOnWorker,
43 scoped_refptr<base::RefCountedBytes> data() const { return data_; }
46 friend class base::RefCountedThreadSafe<ScreenshotData>;
47 virtual ~ScreenshotData() {
50 void EncodeOnWorker(const SkBitmap& bitmap) {
51 std::vector<unsigned char> data;
52 if (gfx::PNGCodec::EncodeBGRASkBitmap(bitmap, true, &data))
53 data_ = new base::RefCountedBytes(data);
56 scoped_refptr<base::RefCountedBytes> data_;
58 DISALLOW_COPY_AND_ASSIGN(ScreenshotData);
61 NavigationEntryScreenshotManager::NavigationEntryScreenshotManager(
62 NavigationControllerImpl* owner)
64 screenshot_factory_(this),
65 min_screenshot_interval_ms_(kMinScreenshotIntervalMS) {
68 NavigationEntryScreenshotManager::~NavigationEntryScreenshotManager() {
71 void NavigationEntryScreenshotManager::TakeScreenshot() {
72 static bool overscroll_enabled = CommandLine::ForCurrentProcess()->
73 GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
74 if (!overscroll_enabled)
77 NavigationEntryImpl* entry =
78 NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry());
82 RenderViewHost* render_view_host =
83 owner_->delegate()->GetRenderViewHost();
84 if (!static_cast<RenderViewHostImpl*>
85 (render_view_host)->overscroll_controller()) {
88 content::RenderWidgetHostView* view = render_view_host->GetView();
92 // Make sure screenshots aren't taken too frequently.
93 base::Time now = base::Time::Now();
94 if (now - last_screenshot_time_ <
95 base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) {
99 last_screenshot_time_ = now;
101 TakeScreenshotImpl(render_view_host, entry);
104 // Implemented here and not in NavigationEntry because this manager keeps track
105 // of the total number of screen shots across all entries.
106 void NavigationEntryScreenshotManager::ClearAllScreenshots() {
107 int count = owner_->GetEntryCount();
108 for (int i = 0; i < count; ++i) {
109 ClearScreenshot(NavigationEntryImpl::FromNavigationEntry(
110 owner_->GetEntryAtIndex(i)));
112 DCHECK_EQ(GetScreenshotCount(), 0);
115 void NavigationEntryScreenshotManager::TakeScreenshotImpl(
116 RenderViewHost* host,
117 NavigationEntryImpl* entry) {
118 DCHECK(host && host->GetView());
120 host->CopyFromBackingStore(gfx::Rect(),
121 host->GetView()->GetViewBounds().size(),
122 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotTaken,
123 screenshot_factory_.GetWeakPtr(),
124 entry->GetUniqueID()));
127 void NavigationEntryScreenshotManager::SetMinScreenshotIntervalMS(
129 DCHECK_GE(interval_ms, 0);
130 min_screenshot_interval_ms_ = interval_ms;
133 void NavigationEntryScreenshotManager::OnScreenshotTaken(int unique_id,
135 const SkBitmap& bitmap) {
136 NavigationEntryImpl* entry = NULL;
137 int entry_count = owner_->GetEntryCount();
138 for (int i = 0; i < entry_count; ++i) {
139 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
140 if (iter->GetUniqueID() == unique_id) {
141 entry = NavigationEntryImpl::FromNavigationEntry(iter);
147 LOG(ERROR) << "Invalid entry with unique id: " << unique_id;
151 if (!success || bitmap.empty() || bitmap.isNull()) {
152 if (!ClearScreenshot(entry))
153 OnScreenshotSet(entry);
157 scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
158 screenshot->EncodeScreenshot(
160 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete,
161 screenshot_factory_.GetWeakPtr(),
166 int NavigationEntryScreenshotManager::GetScreenshotCount() const {
167 int screenshot_count = 0;
168 int entry_count = owner_->GetEntryCount();
169 for (int i = 0; i < entry_count; ++i) {
170 NavigationEntryImpl* entry =
171 NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
172 if (entry->screenshot().get())
175 return screenshot_count;
178 void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
180 scoped_refptr<ScreenshotData> screenshot) {
181 NavigationEntryImpl* entry = NULL;
182 int entry_count = owner_->GetEntryCount();
183 for (int i = 0; i < entry_count; ++i) {
184 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
185 if (iter->GetUniqueID() == unique_id) {
186 entry = NavigationEntryImpl::FromNavigationEntry(iter);
192 entry->SetScreenshotPNGData(screenshot->data());
193 OnScreenshotSet(entry);
196 void NavigationEntryScreenshotManager::OnScreenshotSet(
197 NavigationEntryImpl* entry) {
198 if (entry->screenshot().get())
199 PurgeScreenshotsIfNecessary();
202 bool NavigationEntryScreenshotManager::ClearScreenshot(
203 NavigationEntryImpl* entry) {
204 if (!entry->screenshot().get())
207 entry->SetScreenshotPNGData(NULL);
211 void NavigationEntryScreenshotManager::PurgeScreenshotsIfNecessary() {
212 // Allow only a certain number of entries to keep screenshots.
213 const int kMaxScreenshots = 10;
214 int screenshot_count = GetScreenshotCount();
215 if (screenshot_count < kMaxScreenshots)
218 const int current = owner_->GetCurrentEntryIndex();
219 const int num_entries = owner_->GetEntryCount();
220 int available_slots = kMaxScreenshots;
221 if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
222 ->screenshot().get()) {
226 // Keep screenshots closer to the current navigation entry, and purge the ones
227 // that are farther away from it. So in each step, look at the entries at
228 // each offset on both the back and forward history, and start counting them
229 // to make sure that the correct number of screenshots are kept in memory.
230 // Note that it is possible for some entries to be missing screenshots (e.g.
231 // when taking the screenshot failed for some reason). So there may be a state
232 // where there are a lot of entries in the back history, but none of them has
233 // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots|
234 // entries in the forward history list.
235 int back = current - 1;
236 int forward = current + 1;
237 while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
239 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
240 owner_->GetEntryAtIndex(back));
241 if (entry->screenshot().get())
246 if (available_slots > 0 && forward < num_entries) {
247 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
248 owner_->GetEntryAtIndex(forward));
249 if (entry->screenshot().get())
255 // Purge any screenshot at |back| or lower indices, and |forward| or higher
257 while (screenshot_count > kMaxScreenshots && back >= 0) {
258 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
259 owner_->GetEntryAtIndex(back));
260 if (ClearScreenshot(entry))
265 while (screenshot_count > kMaxScreenshots && forward < num_entries) {
266 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
267 owner_->GetEntryAtIndex(forward));
268 if (ClearScreenshot(entry))
272 CHECK_GE(screenshot_count, 0);
273 CHECK_LE(screenshot_count, kMaxScreenshots);
276 } // namespace content