cc: Make picture pile base thread safe.
[chromium-blink-merge.git] / content / browser / frame_host / navigation_entry_screenshot_manager.cc
blob162cfb790439c149272824da5ab282eff8a0d801
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(int unique_id,
139 bool success,
140 const SkBitmap& bitmap) {
141 NavigationEntryImpl* entry = NULL;
142 int entry_count = owner_->GetEntryCount();
143 for (int i = 0; i < entry_count; ++i) {
144 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
145 if (iter->GetUniqueID() == unique_id) {
146 entry = NavigationEntryImpl::FromNavigationEntry(iter);
147 break;
151 if (!entry) {
152 LOG(ERROR) << "Invalid entry with unique id: " << unique_id;
153 return;
156 if (!success || bitmap.empty() || bitmap.isNull()) {
157 if (!ClearScreenshot(entry))
158 OnScreenshotSet(entry);
159 return;
162 scoped_refptr<ScreenshotData> screenshot = new ScreenshotData();
163 screenshot->EncodeScreenshot(
164 bitmap,
165 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete,
166 screenshot_factory_.GetWeakPtr(),
167 unique_id,
168 screenshot));
171 int NavigationEntryScreenshotManager::GetScreenshotCount() const {
172 int screenshot_count = 0;
173 int entry_count = owner_->GetEntryCount();
174 for (int i = 0; i < entry_count; ++i) {
175 NavigationEntryImpl* entry =
176 NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(i));
177 if (entry->screenshot().get())
178 screenshot_count++;
180 return screenshot_count;
183 void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
184 int unique_id,
185 scoped_refptr<ScreenshotData> screenshot) {
186 NavigationEntryImpl* entry = NULL;
187 int entry_count = owner_->GetEntryCount();
188 for (int i = 0; i < entry_count; ++i) {
189 NavigationEntry* iter = owner_->GetEntryAtIndex(i);
190 if (iter->GetUniqueID() == unique_id) {
191 entry = NavigationEntryImpl::FromNavigationEntry(iter);
192 break;
195 if (!entry)
196 return;
197 entry->SetScreenshotPNGData(screenshot->data());
198 OnScreenshotSet(entry);
201 void NavigationEntryScreenshotManager::OnScreenshotSet(
202 NavigationEntryImpl* entry) {
203 if (entry->screenshot().get())
204 PurgeScreenshotsIfNecessary();
207 bool NavigationEntryScreenshotManager::ClearScreenshot(
208 NavigationEntryImpl* entry) {
209 if (!entry->screenshot().get())
210 return false;
212 entry->SetScreenshotPNGData(NULL);
213 return true;
216 void NavigationEntryScreenshotManager::PurgeScreenshotsIfNecessary() {
217 // Allow only a certain number of entries to keep screenshots.
218 const int kMaxScreenshots = 10;
219 int screenshot_count = GetScreenshotCount();
220 if (screenshot_count < kMaxScreenshots)
221 return;
223 const int current = owner_->GetCurrentEntryIndex();
224 const int num_entries = owner_->GetEntryCount();
225 int available_slots = kMaxScreenshots;
226 if (NavigationEntryImpl::FromNavigationEntry(owner_->GetEntryAtIndex(current))
227 ->screenshot().get()) {
228 --available_slots;
231 // Keep screenshots closer to the current navigation entry, and purge the ones
232 // that are farther away from it. So in each step, look at the entries at
233 // each offset on both the back and forward history, and start counting them
234 // to make sure that the correct number of screenshots are kept in memory.
235 // Note that it is possible for some entries to be missing screenshots (e.g.
236 // when taking the screenshot failed for some reason). So there may be a state
237 // where there are a lot of entries in the back history, but none of them has
238 // any screenshot. In such cases, keep the screenshots for |kMaxScreenshots|
239 // entries in the forward history list.
240 int back = current - 1;
241 int forward = current + 1;
242 while (available_slots > 0 && (back >= 0 || forward < num_entries)) {
243 if (back >= 0) {
244 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
245 owner_->GetEntryAtIndex(back));
246 if (entry->screenshot().get())
247 --available_slots;
248 --back;
251 if (available_slots > 0 && forward < num_entries) {
252 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
253 owner_->GetEntryAtIndex(forward));
254 if (entry->screenshot().get())
255 --available_slots;
256 ++forward;
260 // Purge any screenshot at |back| or lower indices, and |forward| or higher
261 // indices.
262 while (screenshot_count > kMaxScreenshots && back >= 0) {
263 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
264 owner_->GetEntryAtIndex(back));
265 if (ClearScreenshot(entry))
266 --screenshot_count;
267 --back;
270 while (screenshot_count > kMaxScreenshots && forward < num_entries) {
271 NavigationEntryImpl* entry = NavigationEntryImpl::FromNavigationEntry(
272 owner_->GetEntryAtIndex(forward));
273 if (ClearScreenshot(entry))
274 --screenshot_count;
275 ++forward;
277 CHECK_GE(screenshot_count, 0);
278 CHECK_LE(screenshot_count, kMaxScreenshots);
281 } // namespace content