Pass FrameTreeNode (not RenderFrameHost) to NavigateToEntry.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_entry_screenshot_manager.cc
blob780f3c049caa1648980c237e2d919e89723b4b1c
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/overscroll_configuration.h"
13 #include "content/public/browser/render_widget_host.h"
14 #include "content/public/browser/render_widget_host_view.h"
15 #include "content/public/common/content_switches.h"
16 #include "ui/gfx/codec/png_codec.h"
18 namespace {
20 // Minimum delay between taking screenshots.
21 const int kMinScreenshotIntervalMS = 1000;
25 namespace content {
27 // Encodes the A8 SkBitmap to grayscale PNG in a worker thread.
28 class ScreenshotData : public base::RefCountedThreadSafe<ScreenshotData> {
29 public:
30 ScreenshotData() {
33 void EncodeScreenshot(const SkBitmap& bitmap, base::Closure callback) {
34 if (!base::WorkerPool::PostTaskAndReply(FROM_HERE,
35 base::Bind(&ScreenshotData::EncodeOnWorker,
36 this,
37 bitmap),
38 callback,
39 true)) {
40 callback.Run();
44 scoped_refptr<base::RefCountedBytes> data() const { return data_; }
46 private:
47 friend class base::RefCountedThreadSafe<ScreenshotData>;
48 virtual ~ScreenshotData() {
51 void EncodeOnWorker(const SkBitmap& bitmap) {
52 DCHECK_EQ(bitmap.colorType(), kAlpha_8_SkColorType);
53 // Encode the A8 bitmap to grayscale PNG treating alpha as color intensity.
54 std::vector<unsigned char> data;
55 if (gfx::PNGCodec::EncodeA8SkBitmap(bitmap, &data))
56 data_ = new base::RefCountedBytes(data);
59 scoped_refptr<base::RefCountedBytes> data_;
61 DISALLOW_COPY_AND_ASSIGN(ScreenshotData);
64 NavigationEntryScreenshotManager::NavigationEntryScreenshotManager(
65 NavigationControllerImpl* owner)
66 : owner_(owner),
67 min_screenshot_interval_ms_(kMinScreenshotIntervalMS),
68 screenshot_factory_(this) {
72 NavigationEntryScreenshotManager::~NavigationEntryScreenshotManager() {
75 void NavigationEntryScreenshotManager::TakeScreenshot() {
76 static bool overscroll_enabled = base::CommandLine::ForCurrentProcess()->
77 GetSwitchValueASCII(switches::kOverscrollHistoryNavigation) != "0";
78 if (!overscroll_enabled)
79 return;
81 NavigationEntryImpl* entry =
82 NavigationEntryImpl::FromNavigationEntry(owner_->GetLastCommittedEntry());
83 if (!entry)
84 return;
86 if (!owner_->delegate()->CanOverscrollContent())
87 return;
89 RenderViewHost* render_view_host =
90 owner_->delegate()->GetRenderViewHost();
91 content::RenderWidgetHostView* view = render_view_host->GetView();
92 if (!view)
93 return;
95 // Make sure screenshots aren't taken too frequently.
96 base::Time now = base::Time::Now();
97 if (now - last_screenshot_time_ <
98 base::TimeDelta::FromMilliseconds(min_screenshot_interval_ms_)) {
99 return;
102 last_screenshot_time_ = now;
104 TakeScreenshotImpl(render_view_host, entry);
107 // Implemented here and not in NavigationEntry because this manager keeps track
108 // of the total number of screen shots across all entries.
109 void NavigationEntryScreenshotManager::ClearAllScreenshots() {
110 int count = owner_->GetEntryCount();
111 for (int i = 0; i < count; ++i) {
112 ClearScreenshot(NavigationEntryImpl::FromNavigationEntry(
113 owner_->GetEntryAtIndex(i)));
115 DCHECK_EQ(GetScreenshotCount(), 0);
118 void NavigationEntryScreenshotManager::TakeScreenshotImpl(
119 RenderViewHost* host,
120 NavigationEntryImpl* entry) {
121 DCHECK(host && host->GetView());
122 DCHECK(entry);
123 host->CopyFromBackingStore(
124 gfx::Rect(),
125 host->GetView()->GetViewBounds().size(),
126 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotTaken,
127 screenshot_factory_.GetWeakPtr(),
128 entry->GetUniqueID()),
129 kAlpha_8_SkColorType);
132 void NavigationEntryScreenshotManager::SetMinScreenshotIntervalMS(
133 int interval_ms) {
134 DCHECK_GE(interval_ms, 0);
135 min_screenshot_interval_ms_ = interval_ms;
138 void NavigationEntryScreenshotManager::OnScreenshotTaken(
139 int unique_id,
140 const SkBitmap& bitmap,
141 ReadbackResponse response) {
142 NavigationEntryImpl* entry = NULL;
143 int entry_count = owner_->GetEntryCount();
144 for (int i = 0; i < entry_count; ++i) {
145 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
146 if (iter->GetUniqueID() == unique_id) {
147 entry = NavigationEntryImpl::FromNavigationEntry(iter);
148 break;
152 if (!entry) {
153 LOG(ERROR) << "Invalid entry with unique id: " << unique_id;
154 return;
157 if ((response != READBACK_SUCCESS) || bitmap.empty() || bitmap.isNull()) {
158 if (!ClearScreenshot(entry))
159 OnScreenshotSet(entry);
160 return;
163 scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
164 screenshot->EncodeScreenshot(
165 bitmap,
166 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete,
167 screenshot_factory_.GetWeakPtr(),
168 unique_id,
169 screenshot));
172 int NavigationEntryScreenshotManager::GetScreenshotCount() const {
173 int screenshot_count = 0;
174 int entry_count = owner_->GetEntryCount();
175 for (int i = 0; i < entry_count; ++i) {
176 NavigationEntryImpl* entry =
177 NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
178 if (entry->screenshot().get())
179 screenshot_count++;
181 return screenshot_count;
184 void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
185 int unique_id,
186 scoped_refptr<ScreenshotData> screenshot) {
187 NavigationEntryImpl* entry = NULL;
188 int entry_count = owner_->GetEntryCount();
189 for (int i = 0; i < entry_count; ++i) {
190 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
191 if (iter->GetUniqueID() == unique_id) {
192 entry = NavigationEntryImpl::FromNavigationEntry(iter);
193 break;
196 if (!entry)
197 return;
198 entry->SetScreenshotPNGData(screenshot->data());
199 OnScreenshotSet(entry);
202 void NavigationEntryScreenshotManager::OnScreenshotSet(
203 NavigationEntryImpl* entry) {
204 if (entry->screenshot().get())
205 PurgeScreenshotsIfNecessary();
208 bool NavigationEntryScreenshotManager::ClearScreenshot(
209 NavigationEntryImpl* entry) {
210 if (!entry->screenshot().get())
211 return false;
213 entry->SetScreenshotPNGData(NULL);
214 return true;
217 void NavigationEntryScreenshotManager::PurgeScreenshotsIfNecessary() {
218 // Allow only a certain number of entries to keep screenshots.
219 const int kMaxScreenshots = 10;
220 int screenshot_count = GetScreenshotCount();
221 if (screenshot_count < kMaxScreenshots)
222 return;
224 const int current = owner_->GetCurrentEntryIndex();
225 const int num_entries = owner_->GetEntryCount();
226 int available_slots = kMaxScreenshots;
227 if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
228 ->screenshot().get()) {
229 --available_slots;
232 // Keep screenshots closer to the current navigation entry, and purge the ones
233 // that are farther away from it. So in each step, look at the entries at
234 // each offset on both the back and forward history, and start counting them
235 // to make sure that the correct number of screenshots are kept in memory.
236 // Note that it is possible for some entries to be missing screenshots (e.g.
237 // when taking the screenshot failed for some reason). So there may be a state
238 // where there are a lot of entries in the back history, but none of them has
239 // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots|
240 // entries in the forward history list.
241 int back = current - 1;
242 int forward = current + 1;
243 while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
244 if (back >= 0) {
245 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
246 owner_->GetEntryAtIndex(back));
247 if (entry->screenshot().get())
248 --available_slots;
249 --back;
252 if (available_slots > 0 && forward < num_entries) {
253 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
254 owner_->GetEntryAtIndex(forward));
255 if (entry->screenshot().get())
256 --available_slots;
257 ++forward;
261 // Purge any screenshot at |back| or lower indices, and |forward| or higher
262 // indices.
263 while (screenshot_count > kMaxScreenshots && back >= 0) {
264 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
265 owner_->GetEntryAtIndex(back));
266 if (ClearScreenshot(entry))
267 --screenshot_count;
268 --back;
271 while (screenshot_count > kMaxScreenshots && forward < num_entries) {
272 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
273 owner_->GetEntryAtIndex(forward));
274 if (ClearScreenshot(entry))
275 --screenshot_count;
276 ++forward;
278 CHECK_GE(screenshot_count, 0);
279 CHECK_LE(screenshot_count, kMaxScreenshots);
282 } // namespace content