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"
20 // Minimum delay between taking screenshots.
21 const int kMinScreenshotIntervalMS
= 1000;
27 // Encodes the A8 SkBitmap to grayscale PNG in a worker thread.
28 class ScreenshotData
: public base::RefCountedThreadSafe
<ScreenshotData
> {
33 void EncodeScreenshot(const SkBitmap
& bitmap
, base::Closure callback
) {
34 if (!base::WorkerPool::PostTaskAndReply(FROM_HERE
,
35 base::Bind(&ScreenshotData::EncodeOnWorker
,
44 scoped_refptr
<base::RefCountedBytes
> data() const { return data_
; }
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
)
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
)
81 NavigationEntryImpl
* entry
=
82 NavigationEntryImpl::FromNavigationEntry(owner_
->GetLastCommittedEntry());
86 if (!owner_
->delegate()->CanOverscrollContent())
89 RenderViewHost
* render_view_host
=
90 owner_
->delegate()->GetRenderViewHost();
91 content::RenderWidgetHostView
* view
= render_view_host
->GetView();
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_
)) {
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());
123 host
->CopyFromBackingStore(
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(
134 DCHECK_GE(interval_ms
, 0);
135 min_screenshot_interval_ms_
= interval_ms
;
138 void NavigationEntryScreenshotManager::OnScreenshotTaken(
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
);
153 LOG(ERROR
) << "Invalid entry with unique id: " << unique_id
;
157 if ((response
!= READBACK_SUCCESS
) || bitmap
.empty() || bitmap
.isNull()) {
158 if (!ClearScreenshot(entry
))
159 OnScreenshotSet(entry
);
163 scoped_refptr
<ScreenshotData
> screenshot
= new ScreenshotData();
164 screenshot
->EncodeScreenshot(
166 base::Bind(&NavigationEntryScreenshotManager::OnScreenshotEncodeComplete
,
167 screenshot_factory_
.GetWeakPtr(),
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())
181 return screenshot_count
;
184 void NavigationEntryScreenshotManager::OnScreenshotEncodeComplete(
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
);
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())
213 entry
->SetScreenshotPNGData(NULL
);
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
)
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()) {
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
)) {
245 NavigationEntryImpl
* entry
= NavigationEntryImpl::FromNavigationEntry(
246 owner_
->GetEntryAtIndex(back
));
247 if (entry
->screenshot().get())
252 if (available_slots
> 0 && forward
< num_entries
) {
253 NavigationEntryImpl
* entry
= NavigationEntryImpl::FromNavigationEntry(
254 owner_
->GetEntryAtIndex(forward
));
255 if (entry
->screenshot().get())
261 // Purge any screenshot at |back| or lower indices, and |forward| or higher
263 while (screenshot_count
> kMaxScreenshots
&& back
>= 0) {
264 NavigationEntryImpl
* entry
= NavigationEntryImpl::FromNavigationEntry(
265 owner_
->GetEntryAtIndex(back
));
266 if (ClearScreenshot(entry
))
271 while (screenshot_count
> kMaxScreenshots
&& forward
< num_entries
) {
272 NavigationEntryImpl
* entry
= NavigationEntryImpl::FromNavigationEntry(
273 owner_
->GetEntryAtIndex(forward
));
274 if (ClearScreenshot(entry
))
278 CHECK_GE(screenshot_count
, 0);
279 CHECK_LE(screenshot_count
, kMaxScreenshots
);
282 } // namespace content